From 3ac6daa9e1c5d7dae2a3cd1c6a388174b462f3e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 4 Jun 2025 16:47:31 +0100 Subject: [PATCH 0001/1794] ui: fix setting client_endian field defaults MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a VNC client sends a "set pixel format" message, the 'client_endian' field will get initialized, however, it is valid to omit this message if the client wants to use the server's native pixel format. In the latter scenario nothing is initializing the 'client_endian' field, so it remains set to 0, matching neither G_LITTLE_ENDIAN nor G_BIG_ENDIAN. This then results in pixel format conversion routines taking the wrong code paths. This problem existed before the 'client_be' flag was changed into the 'client_endian' value, but the lack of initialization meant it semantically defaulted to little endian, so only big endian systems would potentially be exposed to incorrect pixel translation. The 'virt-viewer' / 'remote-viewer' apps always send a "set pixel format" message so aren't exposed to any problems, but the classical 'vncviewer' app will show the problem easily. Fixes: 7ed96710e82c385c6cfc3d064eec7dde20f0f3fd Reported-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Signed-off-by: Daniel P. Berrangé --- ui/vnc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/vnc.c b/ui/vnc.c index e9c30aad62c25..a16be468b9096 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2329,6 +2329,7 @@ static void pixel_format_message (VncState *vs) { char pad[3] = { 0, 0, 0 }; vs->client_pf = qemu_default_pixelformat(32); + vs->client_endian = G_BYTE_ORDER; vnc_write_u8(vs, vs->client_pf.bits_per_pixel); /* bits-per-pixel */ vnc_write_u8(vs, vs->client_pf.depth); /* depth */ From 8fc3d63d685751734fb9c8c0284dc44a36a8e053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 4 Jun 2025 17:14:38 +0100 Subject: [PATCH 0002/1794] ui: add trace events for all client messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This lets us see the full flow of RFB messages received from the client. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Signed-off-by: Daniel P. Berrangé --- ui/trace-events | 14 +++++++++++++ ui/vnc.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/ui/trace-events b/ui/trace-events index 3da0d5e2800db..3eba9ca3a825e 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -48,13 +48,27 @@ vnc_msg_server_ext_desktop_resize(void *state, void *ioc, int width, int height, vnc_msg_client_audio_enable(void *state, void *ioc) "VNC client msg audio enable state=%p ioc=%p" vnc_msg_client_audio_disable(void *state, void *ioc) "VNC client msg audio disable state=%p ioc=%p" vnc_msg_client_audio_format(void *state, void *ioc, int fmt, int channels, int freq) "VNC client msg audio format state=%p ioc=%p fmt=%d channels=%d freq=%d" +vnc_msg_client_cut_text(void *state, void *ioc, int len) "VNC client msg cut text state=%p ioc=%p len=%u" +vnc_msg_client_cut_text_ext(void *state, void *ioc, int len, int flags) "VNC client msg cut text state=%p ioc=%p len=%u flags=%u" +vnc_msg_client_ext_key_event(void *state, void *ioc, int down, int sym, int keycode) "VNC client msg ext key event state=%p ioc=%p down=%u sym=%u keycode=%u" +vnc_msg_client_framebuffer_update_request(void *state, void *ioc, int incremental, int x, int y, int w, int h) "VNC client msg framebuffer update request state=%p ioc=%p incremental=%u x=%u y=%u w=%u h=%u" +vnc_msg_client_key_event(void *state, void *ioc, int down, int sym) "VNC client msg key event state=%p ioc=%p down=%u sym=%u" +vnc_msg_client_pointer_event(void *state, void *ioc, int button_mask, int x, int y) "VNC client msg pointer event state=%p ioc=%p button_mask=%u x=%u y=%u" vnc_msg_client_set_desktop_size(void *state, void *ioc, int width, int height, int screens) "VNC client msg set desktop size state=%p ioc=%p size=%dx%d screens=%d" +vnc_msg_client_set_encodings(void *state, void *ioc, int limit) "VNC client msg set encodings state=%p ioc=%p limit=%u" +vnc_msg_client_set_pixel_format(void *state, void *ioc, int bpp, int big_endian, int true_color) "VNC client msg set pixel format state=%p ioc=%p bpp=%u big_endian=%u true_color=%u" +vnc_msg_client_set_pixel_format_rgb(void *state, void *ioc, int red_max, int green_max, int blue_max, int red_shift, int green_shift, int blue_shift) "VNC client msg set pixel format RGB state=%p ioc=%p red_max=%u green_max=%u blue_max=%u red_shift=%u green_shift=%u blue_shift=%u" +vnc_msg_client_xvp(void *state, void *ioc, int version, int action) "VNC client msg XVP state=%p ioc=%p version=%u action=%u" vnc_client_eof(void *state, void *ioc) "VNC client EOF state=%p ioc=%p" vnc_client_io_error(void *state, void *ioc, const char *msg) "VNC client I/O error state=%p ioc=%p errmsg=%s" vnc_client_connect(void *state, void *ioc) "VNC client connect state=%p ioc=%p" vnc_client_disconnect_start(void *state, void *ioc) "VNC client disconnect start state=%p ioc=%p" vnc_client_disconnect_finish(void *state, void *ioc) "VNC client disconnect finish state=%p ioc=%p" vnc_client_io_wrap(void *state, void *ioc, const char *type) "VNC client I/O wrap state=%p ioc=%p type=%s" +vnc_client_pixel_format(void *state, void *ioc, int bpp, int depth, int endian) "VNC client pixel format state=%p ioc=%p bpp=%u depth=%u endian=%u" +vnc_client_pixel_format_red(void *state, void *ioc, int max, int bits, int shift, int mask) "VNC client pixel format red state=%p ioc=%p max=%u bits=%u shift=%u mask=%u" +vnc_client_pixel_format_green(void *state, void *ioc, int max, int bits, int shift, int mask) "VNC client pixel format green state=%p ioc=%p max=%u bits=%u shift=%u mask=%u" +vnc_client_pixel_format_blue(void *state, void *ioc, int max, int bits, int shift, int mask) "VNC client pixel format blue state=%p ioc=%p max=%u bits=%u shift=%u mask=%u" vnc_client_throttle_threshold(void *state, void *ioc, size_t oldoffset, size_t offset, int client_width, int client_height, int bytes_per_pixel, void *audio_cap) "VNC client throttle threshold state=%p ioc=%p oldoffset=%zu newoffset=%zu width=%d height=%d bpp=%d audio=%p" vnc_client_throttle_incremental(void *state, void *ioc, int job_update, size_t offset) "VNC client throttle incremental state=%p ioc=%p job-update=%d offset=%zu" vnc_client_throttle_forced(void *state, void *ioc, int job_update, size_t offset) "VNC client throttle forced state=%p ioc=%p job-update=%d offset=%zu" diff --git a/ui/vnc.c b/ui/vnc.c index a16be468b9096..c309882ddb920 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2314,6 +2314,25 @@ static void set_pixel_format(VncState *vs, int bits_per_pixel, vs->client_pf.bytes_per_pixel = bits_per_pixel / 8; vs->client_pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel; vs->client_endian = big_endian_flag ? G_BIG_ENDIAN : G_LITTLE_ENDIAN; + trace_vnc_client_pixel_format(vs, vs->ioc, + vs->client_pf.bits_per_pixel, + vs->client_pf.depth, + vs->client_endian); + trace_vnc_client_pixel_format_red(vs, vs->ioc, + vs->client_pf.rmax, + vs->client_pf.rbits, + vs->client_pf.rshift, + vs->client_pf.rmask); + trace_vnc_client_pixel_format_green(vs, vs->ioc, + vs->client_pf.gmax, + vs->client_pf.gbits, + vs->client_pf.gshift, + vs->client_pf.gmask); + trace_vnc_client_pixel_format_blue(vs, vs->ioc, + vs->client_pf.bmax, + vs->client_pf.bbits, + vs->client_pf.bshift, + vs->client_pf.bmask); if (!true_color_flag) { send_color_map(vs); @@ -2388,6 +2407,17 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 1) return 20; + trace_vnc_msg_client_set_pixel_format(vs, vs->ioc, + read_u8(data, 4), + read_u8(data, 6), + read_u8(data, 7)); + trace_vnc_msg_client_set_pixel_format_rgb(vs, vs->ioc, + read_u16(data, 8), + read_u16(data, 10), + read_u16(data, 12), + read_u8(data, 14), + read_u8(data, 15), + read_u8(data, 16)); set_pixel_format(vs, read_u8(data, 4), read_u8(data, 6), read_u8(data, 7), read_u16(data, 8), read_u16(data, 10), @@ -2410,12 +2440,19 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) memcpy(data + 4 + (i * 4), &val, sizeof(val)); } + trace_vnc_msg_client_set_encodings(vs, vs->ioc, limit); set_encodings(vs, (int32_t *)(data + 4), limit); break; case VNC_MSG_CLIENT_FRAMEBUFFER_UPDATE_REQUEST: if (len == 1) return 10; + trace_vnc_msg_client_framebuffer_update_request(vs, vs->ioc, + read_u8(data, 1), + read_u16(data, 2), + read_u16(data, 4), + read_u16(data, 6), + read_u16(data, 8)); framebuffer_update_request(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4), read_u16(data, 6), read_u16(data, 8)); @@ -2424,12 +2461,19 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 1) return 8; + trace_vnc_msg_client_key_event(vs, vs->ioc, + read_u8(data, 1), + read_u32(data, 4)); key_event(vs, read_u8(data, 1), read_u32(data, 4)); break; case VNC_MSG_CLIENT_POINTER_EVENT: if (len == 1) return 6; + trace_vnc_msg_client_pointer_event(vs, vs->ioc, + read_u8(data, 1), + read_u16(data, 2), + read_u16(data, 4)); pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4)); break; case VNC_MSG_CLIENT_CUT_TEXT: @@ -2461,9 +2505,12 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) vnc_client_error(vs); break; } + trace_vnc_msg_client_cut_text_ext(vs, vs->ioc, + dlen, read_u32(data, 8)); vnc_client_cut_text_ext(vs, dlen, read_u32(data, 8), data + 12); break; } + trace_vnc_msg_client_cut_text(vs, vs->ioc, read_u32(data, 4)); vnc_client_cut_text(vs, read_u32(data, 4), data + 8); break; case VNC_MSG_CLIENT_XVP: @@ -2478,6 +2525,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 4) { uint8_t version = read_u8(data, 2); uint8_t action = read_u8(data, 3); + trace_vnc_msg_client_xvp(vs, vs->ioc, version, action); if (version != 1) { error_report("vnc: xvp client message version %d != 1", @@ -2511,6 +2559,10 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 2) return 12; + trace_vnc_msg_client_ext_key_event(vs, vs->ioc, + read_u16(data, 2), + read_u32(data, 4), + read_u32(data, 8)); ext_key_event(vs, read_u16(data, 2), read_u32(data, 4), read_u32(data, 8)); break; From 2183ab62512c6253293e83cce3970b0b42e65630 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 10 Jul 2025 15:44:17 +0100 Subject: [PATCH 0003/1794] crypto/x509-utils: Check for error from gnutls_x509_crt_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity notes that in qcrypto_get_x509_cert_fingerprint() we call gnutls_x509_crt_init() but don't check for an error return. Add the missing check. Coverity: CID 1593155 Fixes: 10a1d34fc0d ("crypto: Introduce x509 utils") Signed-off-by: Peter Maydell Reviewed-by: Daniel P. Berrangé Signed-off-by: Daniel P. Berrangé --- crypto/x509-utils.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crypto/x509-utils.c b/crypto/x509-utils.c index 8bad00a51b867..39bb6d4d8c3f7 100644 --- a/crypto/x509-utils.c +++ b/crypto/x509-utils.c @@ -46,7 +46,11 @@ int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size, return -1; } - gnutls_x509_crt_init(&crt); + if (gnutls_x509_crt_init(&crt) < 0) { + error_setg(errp, "Unable to initialize certificate: %s", + gnutls_strerror(ret)); + return -1; + } if (gnutls_x509_crt_import(crt, &datum, GNUTLS_X509_FMT_PEM) != 0) { error_setg(errp, "Failed to import certificate"); From 350785d41d8bb0b799dd16ea04a7232dc8d6093a Mon Sep 17 00:00:00 2001 From: Shaoqin Huang Date: Thu, 17 Jul 2025 06:09:39 -0400 Subject: [PATCH 0004/1794] ramfb: Add property to control if load the romfile Currently the ramfb device loads the vgabios-ramfb.bin unconditionally, but only the x86 need the vgabios-ramfb.bin, this can cause that when use the release package on arm64 it can't find the vgabios-ramfb.bin. Because only seabios will use the vgabios-ramfb.bin, load the rom logic is x86-specific. For other !x86 platforms, the edk2 ships an EFI driver for ramfb, so they don't need to load the romfile. So add a new property use-legacy-x86-rom in both ramfb and vfio_pci device, because the vfio display also use the ramfb_setup() to load the vgabios-ramfb.bin file. After have this property, the machine type can set the compatibility to not load the vgabios-ramfb.bin if the arch doesn't need it. For now the default value is true but it will be turned off by default in subsequent patch when compats get properly handled. Reviewed-by: Zhao Liu Reviewed-by: Eric Auger Signed-off-by: Shaoqin Huang Message-ID: <20250717100941.2230408-2-shahuang@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/display/ramfb-standalone.c | 5 ++++- hw/display/ramfb-stubs.c | 2 +- hw/display/ramfb.c | 6 ++++-- hw/vfio/display.c | 4 ++-- hw/vfio/pci.c | 2 ++ hw/vfio/pci.h | 1 + include/hw/display/ramfb.h | 2 +- 7 files changed, 15 insertions(+), 7 deletions(-) diff --git a/hw/display/ramfb-standalone.c b/hw/display/ramfb-standalone.c index 08f2d5db4eca4..82d8c69f89039 100644 --- a/hw/display/ramfb-standalone.c +++ b/hw/display/ramfb-standalone.c @@ -17,6 +17,7 @@ struct RAMFBStandaloneState { QemuConsole *con; RAMFBState *state; bool migrate; + bool use_legacy_x86_rom; }; static void display_update_wrapper(void *dev) @@ -39,7 +40,7 @@ static void ramfb_realizefn(DeviceState *dev, Error **errp) RAMFBStandaloneState *ramfb = RAMFB(dev); ramfb->con = graphic_console_init(dev, 0, &wrapper_ops, dev); - ramfb->state = ramfb_setup(errp); + ramfb->state = ramfb_setup(ramfb->use_legacy_x86_rom, errp); } static bool migrate_needed(void *opaque) @@ -62,6 +63,8 @@ static const VMStateDescription ramfb_dev_vmstate = { static const Property ramfb_properties[] = { DEFINE_PROP_BOOL("x-migrate", RAMFBStandaloneState, migrate, true), + DEFINE_PROP_BOOL("use-legacy-x86-rom", RAMFBStandaloneState, + use_legacy_x86_rom, true), }; static void ramfb_class_initfn(ObjectClass *klass, const void *data) diff --git a/hw/display/ramfb-stubs.c b/hw/display/ramfb-stubs.c index cf64733b10cda..b83551357bb3f 100644 --- a/hw/display/ramfb-stubs.c +++ b/hw/display/ramfb-stubs.c @@ -8,7 +8,7 @@ void ramfb_display_update(QemuConsole *con, RAMFBState *s) { } -RAMFBState *ramfb_setup(Error **errp) +RAMFBState *ramfb_setup(bool romfile, Error **errp) { error_setg(errp, "ramfb support not available"); return NULL; diff --git a/hw/display/ramfb.c b/hw/display/ramfb.c index 8c0f907673da4..9a17d97d076fb 100644 --- a/hw/display/ramfb.c +++ b/hw/display/ramfb.c @@ -135,7 +135,7 @@ const VMStateDescription ramfb_vmstate = { } }; -RAMFBState *ramfb_setup(Error **errp) +RAMFBState *ramfb_setup(bool romfile, Error **errp) { FWCfgState *fw_cfg = fw_cfg_find(); RAMFBState *s; @@ -147,7 +147,9 @@ RAMFBState *ramfb_setup(Error **errp) s = g_new0(RAMFBState, 1); - rom_add_vga("vgabios-ramfb.bin"); + if (romfile) { + rom_add_vga("vgabios-ramfb.bin"); + } fw_cfg_add_file_callback(fw_cfg, "etc/ramfb", NULL, ramfb_fw_cfg_write, s, &s->cfg, sizeof(s->cfg), false); diff --git a/hw/vfio/display.c b/hw/vfio/display.c index 9c6f5aa265dd7..faacd9019a558 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -365,7 +365,7 @@ static bool vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) &vfio_display_dmabuf_ops, vdev); if (vdev->enable_ramfb) { - vdev->dpy->ramfb = ramfb_setup(errp); + vdev->dpy->ramfb = ramfb_setup(vdev->use_legacy_x86_rom, errp); if (!vdev->dpy->ramfb) { return false; } @@ -494,7 +494,7 @@ static bool vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) &vfio_display_region_ops, vdev); if (vdev->enable_ramfb) { - vdev->dpy->ramfb = ramfb_setup(errp); + vdev->dpy->ramfb = ramfb_setup(vdev->use_legacy_x86_rom, errp); if (!vdev->dpy->ramfb) { return false; } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 1093b28df7c3f..0b969b3359dbb 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3809,6 +3809,8 @@ static const TypeInfo vfio_pci_dev_info = { static const Property vfio_pci_dev_nohotplug_properties[] = { DEFINE_PROP_BOOL("ramfb", VFIOPCIDevice, enable_ramfb, false), + DEFINE_PROP_BOOL("use-legacy-x86-rom", VFIOPCIDevice, + use_legacy_x86_rom, true), DEFINE_PROP_ON_OFF_AUTO("x-ramfb-migrate", VFIOPCIDevice, ramfb_migrate, ON_OFF_AUTO_AUTO), }; diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 495fae737d3a2..826db8c1319b9 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -188,6 +188,7 @@ struct VFIOPCIDevice { bool no_kvm_ioeventfd; bool no_vfio_ioeventfd; bool enable_ramfb; + bool use_legacy_x86_rom; OnOffAuto ramfb_migrate; bool defer_kvm_irq_routing; bool clear_parent_atomics_on_exit; diff --git a/include/hw/display/ramfb.h b/include/hw/display/ramfb.h index a7e00191445e1..172aa6dc89e80 100644 --- a/include/hw/display/ramfb.h +++ b/include/hw/display/ramfb.h @@ -6,7 +6,7 @@ /* ramfb.c */ typedef struct RAMFBState RAMFBState; void ramfb_display_update(QemuConsole *con, RAMFBState *s); -RAMFBState *ramfb_setup(Error **errp); +RAMFBState *ramfb_setup(bool romfile, Error **errp); extern const VMStateDescription ramfb_vmstate; From b53a3bba5e02df7cbdb26f8bf8bcb11b8290e863 Mon Sep 17 00:00:00 2001 From: Shaoqin Huang Date: Thu, 17 Jul 2025 06:09:40 -0400 Subject: [PATCH 0005/1794] vfio: Move the TYPE_* to hw/vfio/types.h Move the TYPE_* to a new file hw/vfio/types.h because the TYPE_VFIO_PCI will be used in later patch, but directly include the hw/vfio/pci.h can cause some compilation error when cross build the windows version. The hw/vfio/types.h can be included to mitigate that problem. Signed-off-by: Shaoqin Huang Message-ID: <20250717100941.2230408-3-shahuang@redhat.com> Reviewed-by: Zhao Liu Signed-off-by: Gerd Hoffmann --- hw/vfio/pci.h | 10 +--------- hw/vfio/types.h | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 hw/vfio/types.h diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 826db8c1319b9..0fd151c5dc2cc 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -14,6 +14,7 @@ #include "system/memory.h" #include "hw/pci/pci_device.h" +#include "hw/vfio/types.h" #include "hw/vfio/vfio-device.h" #include "hw/vfio/vfio-region.h" #include "qemu/event_notifier.h" @@ -119,17 +120,8 @@ typedef struct VFIOMSIXInfo { MemoryRegion *pba_region; } VFIOMSIXInfo; -/* - * TYPE_VFIO_PCI_BASE is an abstract type used to share code - * between VFIO implementations that use a kernel driver - * with those that use user sockets. - */ -#define TYPE_VFIO_PCI_BASE "vfio-pci-base" OBJECT_DECLARE_SIMPLE_TYPE(VFIOPCIDevice, VFIO_PCI_BASE) -#define TYPE_VFIO_PCI "vfio-pci" -/* TYPE_VFIO_PCI shares struct VFIOPCIDevice. */ - struct VFIOPCIDevice { PCIDevice pdev; VFIODevice vbasedev; diff --git a/hw/vfio/types.h b/hw/vfio/types.h new file mode 100644 index 0000000000000..fa20c29b9fbbf --- /dev/null +++ b/hw/vfio/types.h @@ -0,0 +1,21 @@ +/* + * VFIO types definition + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_VFIO_VFIO_TYPES_H +#define HW_VFIO_VFIO_TYPES_H + +/* + * TYPE_VFIO_PCI_BASE is an abstract type used to share code + * between VFIO implementations that use a kernel driver + * with those that use user sockets. + */ +#define TYPE_VFIO_PCI_BASE "vfio-pci-base" + +#define TYPE_VFIO_PCI "vfio-pci" +/* TYPE_VFIO_PCI shares struct VFIOPCIDevice. */ + +#endif /* HW_VFIO_VFIO_TYPES_H */ From d5fcf0d960d893b1765e6388cefca9c690839267 Mon Sep 17 00:00:00 2001 From: Shaoqin Huang Date: Thu, 17 Jul 2025 06:09:41 -0400 Subject: [PATCH 0006/1794] hw/i386: Add the ramfb romfile compatibility ramfb is a sysbus device so it can only used for machine types where it is explicitly enabled: # git grep machine_class_allow_dynamic_sysbus_dev.*TYPE_RAMFB_DEVICE hw/arm/virt.c: machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); hw/i386/microvm.c: machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); hw/i386/pc_piix.c: machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); hw/i386/pc_q35.c: machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); hw/loongarch/virt.c: machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); hw/riscv/virt.c: machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); So these six are the only machine types we have to worry about. The three x86 machine types (pc, q35, microvm) will actually use the rom (when booting with seabios). For arm/riscv/loongarch virt we want to disable the rom. This patch sets ramfb romfile option to false by default, except for x86 machines types (pc, q35, microvm) which need the rom file when booting with seabios and machine types <= 10.0 (handling the case of arm virt, for compat reasons). At the same time, set the "use-legacy-x86-rom" property to true on those historical versioned machine types in order to avoid the memory layout being changed. Acked-by: Michael S. Tsirkin Reviewed-by: Zhao Liu Reviewed-by: Eric Auger Signed-off-by: Shaoqin Huang Message-ID: <20250717100941.2230408-4-shahuang@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/core/machine.c | 2 ++ hw/display/ramfb-standalone.c | 2 +- hw/i386/microvm.c | 3 +++ hw/i386/pc_piix.c | 10 ++++++++++ hw/i386/pc_q35.c | 3 +++ hw/vfio/pci.c | 2 +- 6 files changed, 20 insertions(+), 2 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index e869821b22463..a7043e2a3425a 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -39,6 +39,8 @@ GlobalProperty hw_compat_10_0[] = { { "scsi-hd", "dpofua", "off" }, + { "ramfb", "use-legacy-x86-rom", "true"}, + { "vfio-pci", "use-legacy-x86-rom", "true" }, }; const size_t hw_compat_10_0_len = G_N_ELEMENTS(hw_compat_10_0); diff --git a/hw/display/ramfb-standalone.c b/hw/display/ramfb-standalone.c index 82d8c69f89039..72b2071aed013 100644 --- a/hw/display/ramfb-standalone.c +++ b/hw/display/ramfb-standalone.c @@ -64,7 +64,7 @@ static const VMStateDescription ramfb_dev_vmstate = { static const Property ramfb_properties[] = { DEFINE_PROP_BOOL("x-migrate", RAMFBStandaloneState, migrate, true), DEFINE_PROP_BOOL("use-legacy-x86-rom", RAMFBStandaloneState, - use_legacy_x86_rom, true), + use_legacy_x86_rom, false), }; static void ramfb_class_initfn(ObjectClass *klass, const void *data) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index e0daf0d4fc301..d90b69a162007 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -49,6 +49,7 @@ #include "hw/acpi/generic_event_device.h" #include "hw/pci-host/gpex.h" #include "hw/usb/xhci.h" +#include "hw/vfio/types.h" #include "elf.h" #include "kvm/kvm_i386.h" @@ -633,6 +634,8 @@ GlobalProperty microvm_properties[] = { * so reserving io space is not going to work. Turn it off. */ { "pcie-root-port", "io-reserve", "0" }, + { TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" }, + { TYPE_VFIO_PCI, "use-legacy-x86-rom", "true" }, }; static void microvm_class_init(ObjectClass *oc, const void *data) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index a3285fbc64509..ad5caff3a5d25 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -49,6 +49,7 @@ #include "hw/i2c/smbus_eeprom.h" #include "system/memory.h" #include "hw/acpi/acpi.h" +#include "hw/vfio/types.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "system/xen.h" @@ -77,6 +78,13 @@ static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; #endif +static GlobalProperty pc_piix_compat_defaults[] = { + { TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" }, + { TYPE_VFIO_PCI, "use-legacy-x86-rom", "true" }, +}; +static const size_t pc_piix_compat_defaults_len = + G_N_ELEMENTS(pc_piix_compat_defaults); + /* * Return the global irq number corresponding to a given device irq * pin. We could also use the bus number to have a more precise mapping. @@ -492,6 +500,8 @@ static void pc_i440fx_machine_options(MachineClass *m) pc_set_south_bridge); object_class_property_set_description(oc, "x-south-bridge", "Use a different south bridge than PIIX3"); + compat_props_add(m->compat_props, + pc_piix_compat_defaults, pc_piix_compat_defaults_len); } static void pc_i440fx_machine_10_1_options(MachineClass *m) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index cf871cfdad863..9b9519fa02d50 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -45,6 +45,7 @@ #include "hw/i386/pc.h" #include "hw/i386/amd_iommu.h" #include "hw/i386/intel_iommu.h" +#include "hw/vfio/types.h" #include "hw/virtio/virtio-iommu.h" #include "hw/display/ramfb.h" #include "hw/ide/pci.h" @@ -67,6 +68,8 @@ static GlobalProperty pc_q35_compat_defaults[] = { { TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "39" }, + { TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" }, + { TYPE_VFIO_PCI, "use-legacy-x86-rom", "true" }, }; static const size_t pc_q35_compat_defaults_len = G_N_ELEMENTS(pc_q35_compat_defaults); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 0b969b3359dbb..174499ecec65f 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3810,7 +3810,7 @@ static const TypeInfo vfio_pci_dev_info = { static const Property vfio_pci_dev_nohotplug_properties[] = { DEFINE_PROP_BOOL("ramfb", VFIOPCIDevice, enable_ramfb, false), DEFINE_PROP_BOOL("use-legacy-x86-rom", VFIOPCIDevice, - use_legacy_x86_rom, true), + use_legacy_x86_rom, false), DEFINE_PROP_ON_OFF_AUTO("x-ramfb-migrate", VFIOPCIDevice, ramfb_migrate, ON_OFF_AUTO_AUTO), }; From 3f9f6299a1bc46ff462142c7398a5953e3640cc2 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 16 Jul 2025 10:28:53 +0300 Subject: [PATCH 0007/1794] net/tap: drop too small packets Theoretically tap_read_packet() may return size less than s->host_vnet_hdr_len, and next, we'll work with negative size (in case of !s->using_vnet_hdr). Let's avoid it. Don't proceed with size == s->host_vnet_hdr_len as well in case of !s->using_vnet_hdr, it doesn't make sense. Tested-by: Lei Yang Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Jason Wang --- net/tap.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/tap.c b/net/tap.c index 23536c09b4652..2a8593601931b 100644 --- a/net/tap.c +++ b/net/tap.c @@ -190,6 +190,11 @@ static void tap_send(void *opaque) break; } + if (s->host_vnet_hdr_len && size <= s->host_vnet_hdr_len) { + /* Invalid packet */ + break; + } + if (s->host_vnet_hdr_len && !s->using_vnet_hdr) { buf += s->host_vnet_hdr_len; size -= s->host_vnet_hdr_len; From ea263e8fd9a8f9cff615631a9bda775f5ddd3f98 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Mon, 14 Jul 2025 13:36:54 -0700 Subject: [PATCH 0008/1794] tap: fix net_init_tap() return code net_init_tap intends to return 0 for success and -1 on error. However, when net_init_tap() succeeds for a multi-queue device, it returns 1, because of this code where ret becomes 1 when g_unix_set_fd_nonblocking succeeds: ret = g_unix_set_fd_nonblocking(fd, true, NULL); if (!ret) { ... error ... free_fail: ... return ret; Luckily, the only current call site checks for negative, rather than non-zero: net_client_init1() if (net_client_init_fun[](...) < 0) Also, in the unlikely case that g_unix_set_fd_nonblocking fails and returns false, ret=0 is returned, and net_client_init1 will use a broken interface. Fix it to be future proof. Signed-off-by: Steve Sistare Signed-off-by: Jason Wang --- net/tap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/tap.c b/net/tap.c index 2a8593601931b..f7df702f978f5 100644 --- a/net/tap.c +++ b/net/tap.c @@ -895,8 +895,8 @@ int net_init_tap(const Netdev *netdev, const char *name, goto free_fail; } - ret = g_unix_set_fd_nonblocking(fd, true, NULL); - if (!ret) { + if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { + ret = -1; error_setg_errno(errp, errno, "%s: Can't use file descriptor %d", name, fd); goto free_fail; From 871a6e5b339f0b5e71925ec7d3f452944a1c82d3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 14 Jul 2025 17:55:20 +0100 Subject: [PATCH 0009/1794] hw/net/npcm_gmac.c: Send the right data for second packet in a row The transmit loop in gmac_try_send_next_packet() is constructed in a way that means it will send incorrect data if it it sends more than one packet. The function assembles the outbound data in a dynamically allocated block of memory which is pointed to by tx_send_buffer. We track the first point in this block of memory which is not yet used with the prev_buf_size offset, initially zero. We track the size of the packet we're sending with the length variable, also initially zero. As we read chunks of data out of guest memory, we write them to tx_send_buffer[prev_buf_size], and then increment both prev_buf_size and length. (We might dynamically reallocate the buffer if needed.) When we send a packet, we checksum and send length bytes, starting at tx_send_buffer, and then we reset length to 0. This gives the right data for the first packet. But we don't reset prev_buf_size. This means that if we process more descriptors with further data for the next packet, that data will continue to accumulate at offset prev_buf_size, i.e. after the data for the first packet. But when we transmit that second packet, we send length bytes from tx_send_buffer, so we will send a packet which has the length of the second packet but the data of the first one. The fix for this is to also clear prev_buf_size after the packet has been sent -- we never need the data from packet one after we've sent it, so we can write packet two's data starting at the beginning of the buffer. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Signed-off-by: Jason Wang --- hw/net/npcm_gmac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/net/npcm_gmac.c b/hw/net/npcm_gmac.c index a43411258090c..921327dd8ca90 100644 --- a/hw/net/npcm_gmac.c +++ b/hw/net/npcm_gmac.c @@ -615,6 +615,7 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) trace_npcm_gmac_packet_sent(DEVICE(gmac)->canonical_path, length); buf = tx_send_buffer; length = 0; + prev_buf_size = 0; } /* step 6 */ From 01b327c9a609cb613da1e04c5af5f4fcc7d9c0a3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 14 Jul 2025 17:55:21 +0100 Subject: [PATCH 0010/1794] hw/net/npcm_gmac.c: Unify length and prev_buf_size variables After the bug fix in the previous commit, the length and prev_buf_size variables are identical, except that prev_buf_size is uint32_t and length is uint16_t. We can therefore unify them. The only place where the type makes a difference is that we will truncate the packet at 64K when sending it; this commit preserves that behaviour by using a local variable when doing the packet send. Signed-off-by: Peter Maydell Signed-off-by: Jason Wang --- hw/net/npcm_gmac.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/net/npcm_gmac.c b/hw/net/npcm_gmac.c index 921327dd8ca90..a0050a7725fcf 100644 --- a/hw/net/npcm_gmac.c +++ b/hw/net/npcm_gmac.c @@ -516,7 +516,6 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) uint32_t desc_addr; struct NPCMGMACTxDesc tx_desc; uint32_t tx_buf_addr, tx_buf_len; - uint16_t length = 0; uint8_t *buf = tx_send_buffer; uint32_t prev_buf_size = 0; int csum = 0; @@ -583,7 +582,6 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) __func__, tx_buf_addr); return; } - length += tx_buf_len; prev_buf_size += tx_buf_len; /* If not chained we'll have a second buffer. */ @@ -606,15 +604,18 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) __func__, tx_buf_addr); return; } - length += tx_buf_len; prev_buf_size += tx_buf_len; } if (tx_desc.tdes1 & TX_DESC_TDES1_LAST_SEG_MASK) { + /* + * This will truncate the packet at 64K. + * TODO: find out if this is the correct behaviour. + */ + uint16_t length = prev_buf_size; net_checksum_calculate(tx_send_buffer, length, csum); qemu_send_packet(qemu_get_queue(gmac->nic), tx_send_buffer, length); trace_npcm_gmac_packet_sent(DEVICE(gmac)->canonical_path, length); buf = tx_send_buffer; - length = 0; prev_buf_size = 0; } From e96a1aa6766f1b188dada3ad8d172c0fbe2f3a5f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 14 Jul 2025 17:55:22 +0100 Subject: [PATCH 0011/1794] hw/net/npcm_gmac.c: Correct test for when to reallocate packet buffer In gmac_try_send_next_packet() we have code that does "if this block of data won't fit in the buffer, reallocate it". However, the condition it uses is if ((prev_buf_size + tx_buf_len) > sizeof(buf)) where buf is a uint8_t *. This means that sizeof(buf) is always 8 bytes, and the condition will almost always be true, so we will reallocate the buffer more often than we need to. Correct the condition to test against tx_buffer_size, which is where we track how big the allocated buffer is. Signed-off-by: Peter Maydell Signed-off-by: Jason Wang --- hw/net/npcm_gmac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/net/npcm_gmac.c b/hw/net/npcm_gmac.c index a0050a7725fcf..0c17ae9b2a973 100644 --- a/hw/net/npcm_gmac.c +++ b/hw/net/npcm_gmac.c @@ -569,7 +569,7 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) tx_buf_len = TX_DESC_TDES1_BFFR1_SZ_MASK(tx_desc.tdes1); buf = &tx_send_buffer[prev_buf_size]; - if ((prev_buf_size + tx_buf_len) > sizeof(buf)) { + if ((prev_buf_size + tx_buf_len) > tx_buffer_size) { tx_buffer_size = prev_buf_size + tx_buf_len; tx_send_buffer = g_realloc(tx_send_buffer, tx_buffer_size); buf = &tx_send_buffer[prev_buf_size]; @@ -591,7 +591,7 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) tx_buf_len = TX_DESC_TDES1_BFFR2_SZ_MASK(tx_desc.tdes1); buf = &tx_send_buffer[prev_buf_size]; - if ((prev_buf_size + tx_buf_len) > sizeof(buf)) { + if ((prev_buf_size + tx_buf_len) > tx_buffer_size) { tx_buffer_size = prev_buf_size + tx_buf_len; tx_send_buffer = g_realloc(tx_send_buffer, tx_buffer_size); buf = &tx_send_buffer[prev_buf_size]; From a6d6e37901551cbd61b0a61c6bab5745fb1833ff Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 14 Jul 2025 17:55:23 +0100 Subject: [PATCH 0012/1794] hw/net/npcm_gmac.c: Drop 'buf' local variable We use the local variable 'buf' only when we call dma_memory_read(), and it is always set to &tx_send_buffer[prev_buf_size] immediately before both of those calls. So remove the variable and pass tx_send_buffer + prev_buf_size to dma_memory_read(). This fixes in passing a place where we set buf = tx_send_buffer but never used that value because we always updated buf to something else later before using it. Coverity: CID 1534027 Signed-off-by: Peter Maydell Signed-off-by: Jason Wang --- hw/net/npcm_gmac.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/hw/net/npcm_gmac.c b/hw/net/npcm_gmac.c index 0c17ae9b2a973..5e32cd3edf3fa 100644 --- a/hw/net/npcm_gmac.c +++ b/hw/net/npcm_gmac.c @@ -516,7 +516,6 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) uint32_t desc_addr; struct NPCMGMACTxDesc tx_desc; uint32_t tx_buf_addr, tx_buf_len; - uint8_t *buf = tx_send_buffer; uint32_t prev_buf_size = 0; int csum = 0; @@ -567,16 +566,15 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) tx_buf_addr = tx_desc.tdes2; gmac->regs[R_NPCM_DMA_CUR_TX_BUF_ADDR] = tx_buf_addr; tx_buf_len = TX_DESC_TDES1_BFFR1_SZ_MASK(tx_desc.tdes1); - buf = &tx_send_buffer[prev_buf_size]; if ((prev_buf_size + tx_buf_len) > tx_buffer_size) { tx_buffer_size = prev_buf_size + tx_buf_len; tx_send_buffer = g_realloc(tx_send_buffer, tx_buffer_size); - buf = &tx_send_buffer[prev_buf_size]; } /* step 5 */ - if (dma_memory_read(&address_space_memory, tx_buf_addr, buf, + if (dma_memory_read(&address_space_memory, tx_buf_addr, + tx_send_buffer + prev_buf_size, tx_buf_len, MEMTXATTRS_UNSPECIFIED)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to read packet @ 0x%x\n", __func__, tx_buf_addr); @@ -589,15 +587,14 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) tx_buf_addr = tx_desc.tdes3; gmac->regs[R_NPCM_DMA_CUR_TX_BUF_ADDR] = tx_buf_addr; tx_buf_len = TX_DESC_TDES1_BFFR2_SZ_MASK(tx_desc.tdes1); - buf = &tx_send_buffer[prev_buf_size]; if ((prev_buf_size + tx_buf_len) > tx_buffer_size) { tx_buffer_size = prev_buf_size + tx_buf_len; tx_send_buffer = g_realloc(tx_send_buffer, tx_buffer_size); - buf = &tx_send_buffer[prev_buf_size]; } - if (dma_memory_read(&address_space_memory, tx_buf_addr, buf, + if (dma_memory_read(&address_space_memory, tx_buf_addr, + tx_send_buffer + prev_buf_size, tx_buf_len, MEMTXATTRS_UNSPECIFIED)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to read packet @ 0x%x\n", @@ -615,7 +612,6 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) net_checksum_calculate(tx_send_buffer, length, csum); qemu_send_packet(qemu_get_queue(gmac->nic), tx_send_buffer, length); trace_npcm_gmac_packet_sent(DEVICE(gmac)->canonical_path, length); - buf = tx_send_buffer; prev_buf_size = 0; } From 2ed74e3c005f236acbf7d85d396a653bfb004a17 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 17 Jul 2025 17:08:00 +0200 Subject: [PATCH 0013/1794] net/passt: Remove unused "err" from passt_vhost_user_event() (CID 1612375) The "err" variable was declared but never used within the passt_vhost_user_event() function. This resulted in a dead code warning (CID 1612375) from Coverity. Remove the unused variable and the associated error block to resolve the issue. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Signed-off-by: Jason Wang --- net/passt.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/passt.c b/net/passt.c index 6f616ba3c2583..9cd5b3e6f2af9 100644 --- a/net/passt.c +++ b/net/passt.c @@ -397,7 +397,6 @@ static int passt_vhost_user_start(NetPasstState *s, VhostUserState *be) static void passt_vhost_user_event(void *opaque, QEMUChrEvent event) { NetPasstState *s = opaque; - Error *err = NULL; switch (event) { case CHR_EVENT_OPENED: @@ -428,10 +427,6 @@ static void passt_vhost_user_event(void *opaque, QEMUChrEvent event) /* Ignore */ break; } - - if (err) { - error_report_err(err); - } } static int net_passt_vhost_user_init(NetPasstState *s, Error **errp) From 37c8b208cc3161c2bcd4e2ff1e1077a64872ced3 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 17 Jul 2025 17:08:01 +0200 Subject: [PATCH 0014/1794] net/vhost-user: Remove unused "err" from net_vhost_user_event() (CID 1612372) The "err" variable was declared but never used within the net_vhost_user_event() function. This resulted in a dead code warning (CID 1612372) from Coverity. Remove the unused variable and the associated error block to resolve the issue. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Signed-off-by: Jason Wang --- net/vhost-user.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/vhost-user.c b/net/vhost-user.c index 1c3b8b36f3518..cec83e925ff0c 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -329,7 +329,6 @@ static void net_vhost_user_event(void *opaque, QEMUChrEvent event) NetClientState *ncs[MAX_QUEUE_NUM]; NetVhostUserState *s; Chardev *chr; - Error *err = NULL; int queues; queues = qemu_find_net_clients_except(name, ncs, @@ -375,10 +374,6 @@ static void net_vhost_user_event(void *opaque, QEMUChrEvent event) /* Ignore */ break; } - - if (err) { - error_report_err(err); - } } static int net_vhost_user_init(NetClientState *peer, const char *device, From c40ef7243fdbec816242cb0ded569a61270ea7c3 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 17 Jul 2025 17:08:02 +0200 Subject: [PATCH 0015/1794] net/passt: Remove dead code in passt_vhost_user_start error path (CID 1612371) In passt_vhost_user_start(), if vhost_net_init() fails, the "net" variable is NULL and execution jumps to the "err:" label. The cleanup code within this label is conditioned on "if (net)", which can never be true in this error case. This makes the cleanup block dead code, as reported by Coverity (CID 1612371). Refactor the error handling to occur inline, removing the goto and the unreachable cleanup block. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Signed-off-by: Jason Wang --- net/passt.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/net/passt.c b/net/passt.c index 9cd5b3e6f2af9..ef59d0682be19 100644 --- a/net/passt.c +++ b/net/passt.c @@ -375,7 +375,8 @@ static int passt_vhost_user_start(NetPasstState *s, VhostUserState *be) net = vhost_net_init(&options); if (!net) { error_report("failed to init passt vhost_net"); - goto err; + passt_vhost_user_stop(s); + return -1; } if (s->vhost_net) { @@ -385,13 +386,6 @@ static int passt_vhost_user_start(NetPasstState *s, VhostUserState *be) s->vhost_net = net; return 0; -err: - if (net) { - vhost_net_cleanup(net); - g_free(net); - } - passt_vhost_user_stop(s); - return -1; } static void passt_vhost_user_event(void *opaque, QEMUChrEvent event) From f74e4f2e60c3b2c6e2f13eb115478df116148fe6 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 17 Jul 2025 17:08:03 +0200 Subject: [PATCH 0016/1794] net/passt: Check return value of g_remove() in net_passt_cleanup() (CID 1612369) If g_remove() fails, use warn_report() to log an error. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Signed-off-by: Jason Wang --- net/passt.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/passt.c b/net/passt.c index ef59d0682be19..43c336e5968c8 100644 --- a/net/passt.c +++ b/net/passt.c @@ -103,7 +103,10 @@ static void net_passt_cleanup(NetClientState *nc) #endif kill(s->pid, SIGTERM); - g_remove(s->pidfile); + if (g_remove(s->pidfile) != 0) { + warn_report("Failed to remove passt pidfile %s: %s", + s->pidfile, strerror(errno)); + } g_free(s->pidfile); g_ptr_array_free(s->args, TRUE); } From 667ad57b7636881eb79b0b319951f3cba9bbf27e Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 17 Jul 2025 17:08:04 +0200 Subject: [PATCH 0017/1794] net/passt: Initialize "error" variable in net_passt_send() (CID 1612368) This was flagged by Coverity as a memory illegal access. Initialize the pointer to NULL at declaration. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Signed-off-by: Jason Wang --- net/passt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/passt.c b/net/passt.c index 43c336e5968c8..32ecffb763b48 100644 --- a/net/passt.c +++ b/net/passt.c @@ -124,7 +124,7 @@ static gboolean net_passt_send(QIOChannel *ioc, GIOCondition condition, { if (net_stream_data_send(ioc, condition, data) == G_SOURCE_REMOVE) { NetPasstState *s = DO_UPCAST(NetPasstState, data, data); - Error *error; + Error *error = NULL; /* we need to restart passt */ kill(s->pid, SIGTERM); From ae9b09972bbf8ff49ae0edf3241fb413391b15ce Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 17 Jul 2025 17:08:05 +0200 Subject: [PATCH 0018/1794] net/vhost-user: Remove unused "err" from chr_closed_bh() (CID 1612365) The "err" variable was declared but never used within the chr_closed_bh() function. This resulted in a dead code warning (CID 1612365) from Coverity. Remove the unused variable and the associated error block to resolve the issue. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Signed-off-by: Jason Wang --- net/vhost-user.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/vhost-user.c b/net/vhost-user.c index cec83e925ff0c..8b96157145a7a 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -298,7 +298,6 @@ static void chr_closed_bh(void *opaque) const char *name = opaque; NetClientState *ncs[MAX_QUEUE_NUM]; NetVhostUserState *s; - Error *err = NULL; int queues, i; queues = qemu_find_net_clients_except(name, ncs, @@ -317,9 +316,6 @@ static void chr_closed_bh(void *opaque) qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event, NULL, opaque, NULL, true); - if (err) { - error_report_err(err); - } qapi_event_send_netdev_vhost_user_disconnected(name); } From 99c6e970a4a6cfd862c6bb0a363c28538436de13 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 10 Jul 2025 14:00:35 +0200 Subject: [PATCH 0019/1794] linux-headers: Remove the 32-bit arm headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KVM support for 32-bit arm has been dropped a while ago, so we don't need these headers in QEMU anymore. Fixes: 82bf7ae84ce ("target/arm: Remove KVM support for 32-bit Arm hosts") Acked-by: Cornelia Huck Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250710120035.169376-1-thuth@redhat.com> --- configure | 1 - linux-headers/asm-arm/bitsperlong.h | 1 - linux-headers/asm-arm/kvm.h | 312 -------------------- linux-headers/asm-arm/mman.h | 4 - linux-headers/asm-arm/unistd-common.h | 397 -------------------------- linux-headers/asm-arm/unistd-eabi.h | 5 - linux-headers/asm-arm/unistd-oabi.h | 17 -- linux-headers/asm-arm/unistd.h | 41 --- scripts/update-linux-headers.sh | 5 - 9 files changed, 783 deletions(-) delete mode 100644 linux-headers/asm-arm/bitsperlong.h delete mode 100644 linux-headers/asm-arm/kvm.h delete mode 100644 linux-headers/asm-arm/mman.h delete mode 100644 linux-headers/asm-arm/unistd-common.h delete mode 100644 linux-headers/asm-arm/unistd-eabi.h delete mode 100644 linux-headers/asm-arm/unistd-oabi.h delete mode 100644 linux-headers/asm-arm/unistd.h diff --git a/configure b/configure index 2b2b3d6597946..95f67c1a8272e 100755 --- a/configure +++ b/configure @@ -453,7 +453,6 @@ case "$cpu" in armv*b|armv*l|arm) cpu=arm host_arch=arm - linux_arch=arm ;; i386|i486|i586|i686) diff --git a/linux-headers/asm-arm/bitsperlong.h b/linux-headers/asm-arm/bitsperlong.h deleted file mode 100644 index 6dc0bb0c13b29..0000000000000 --- a/linux-headers/asm-arm/bitsperlong.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/linux-headers/asm-arm/kvm.h b/linux-headers/asm-arm/kvm.h deleted file mode 100644 index 0db5644e27afb..0000000000000 --- a/linux-headers/asm-arm/kvm.h +++ /dev/null @@ -1,312 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * Copyright (C) 2012 - Virtual Open Systems and Columbia University - * Author: Christoffer Dall - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __ARM_KVM_H__ -#define __ARM_KVM_H__ - -#include -#include -#include - -#define __KVM_HAVE_GUEST_DEBUG -#define __KVM_HAVE_IRQ_LINE -#define __KVM_HAVE_READONLY_MEM -#define __KVM_HAVE_VCPU_EVENTS - -#define KVM_COALESCED_MMIO_PAGE_OFFSET 1 - -#define KVM_REG_SIZE(id) \ - (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) - -/* Valid for svc_regs, abt_regs, und_regs, irq_regs in struct kvm_regs */ -#define KVM_ARM_SVC_sp svc_regs[0] -#define KVM_ARM_SVC_lr svc_regs[1] -#define KVM_ARM_SVC_spsr svc_regs[2] -#define KVM_ARM_ABT_sp abt_regs[0] -#define KVM_ARM_ABT_lr abt_regs[1] -#define KVM_ARM_ABT_spsr abt_regs[2] -#define KVM_ARM_UND_sp und_regs[0] -#define KVM_ARM_UND_lr und_regs[1] -#define KVM_ARM_UND_spsr und_regs[2] -#define KVM_ARM_IRQ_sp irq_regs[0] -#define KVM_ARM_IRQ_lr irq_regs[1] -#define KVM_ARM_IRQ_spsr irq_regs[2] - -/* Valid only for fiq_regs in struct kvm_regs */ -#define KVM_ARM_FIQ_r8 fiq_regs[0] -#define KVM_ARM_FIQ_r9 fiq_regs[1] -#define KVM_ARM_FIQ_r10 fiq_regs[2] -#define KVM_ARM_FIQ_fp fiq_regs[3] -#define KVM_ARM_FIQ_ip fiq_regs[4] -#define KVM_ARM_FIQ_sp fiq_regs[5] -#define KVM_ARM_FIQ_lr fiq_regs[6] -#define KVM_ARM_FIQ_spsr fiq_regs[7] - -struct kvm_regs { - struct pt_regs usr_regs; /* R0_usr - R14_usr, PC, CPSR */ - unsigned long svc_regs[3]; /* SP_svc, LR_svc, SPSR_svc */ - unsigned long abt_regs[3]; /* SP_abt, LR_abt, SPSR_abt */ - unsigned long und_regs[3]; /* SP_und, LR_und, SPSR_und */ - unsigned long irq_regs[3]; /* SP_irq, LR_irq, SPSR_irq */ - unsigned long fiq_regs[8]; /* R8_fiq - R14_fiq, SPSR_fiq */ -}; - -/* Supported Processor Types */ -#define KVM_ARM_TARGET_CORTEX_A15 0 -#define KVM_ARM_TARGET_CORTEX_A7 1 -#define KVM_ARM_NUM_TARGETS 2 - -/* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */ -#define KVM_ARM_DEVICE_TYPE_SHIFT 0 -#define KVM_ARM_DEVICE_TYPE_MASK (0xffff << KVM_ARM_DEVICE_TYPE_SHIFT) -#define KVM_ARM_DEVICE_ID_SHIFT 16 -#define KVM_ARM_DEVICE_ID_MASK (0xffff << KVM_ARM_DEVICE_ID_SHIFT) - -/* Supported device IDs */ -#define KVM_ARM_DEVICE_VGIC_V2 0 - -/* Supported VGIC address types */ -#define KVM_VGIC_V2_ADDR_TYPE_DIST 0 -#define KVM_VGIC_V2_ADDR_TYPE_CPU 1 - -#define KVM_VGIC_V2_DIST_SIZE 0x1000 -#define KVM_VGIC_V2_CPU_SIZE 0x2000 - -/* Supported VGICv3 address types */ -#define KVM_VGIC_V3_ADDR_TYPE_DIST 2 -#define KVM_VGIC_V3_ADDR_TYPE_REDIST 3 -#define KVM_VGIC_ITS_ADDR_TYPE 4 -#define KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION 5 - -#define KVM_VGIC_V3_DIST_SIZE SZ_64K -#define KVM_VGIC_V3_REDIST_SIZE (2 * SZ_64K) -#define KVM_VGIC_V3_ITS_SIZE (2 * SZ_64K) - -#define KVM_ARM_VCPU_POWER_OFF 0 /* CPU is started in OFF state */ -#define KVM_ARM_VCPU_PSCI_0_2 1 /* CPU uses PSCI v0.2 */ - -struct kvm_vcpu_init { - __u32 target; - __u32 features[7]; -}; - -struct kvm_sregs { -}; - -struct kvm_fpu { -}; - -struct kvm_guest_debug_arch { -}; - -struct kvm_debug_exit_arch { -}; - -struct kvm_sync_regs { - /* Used with KVM_CAP_ARM_USER_IRQ */ - __u64 device_irq_level; -}; - -struct kvm_arch_memory_slot { -}; - -/* for KVM_GET/SET_VCPU_EVENTS */ -struct kvm_vcpu_events { - struct { - __u8 serror_pending; - __u8 serror_has_esr; - __u8 ext_dabt_pending; - /* Align it to 8 bytes */ - __u8 pad[5]; - __u64 serror_esr; - } exception; - __u32 reserved[12]; -}; - -/* If you need to interpret the index values, here is the key: */ -#define KVM_REG_ARM_COPROC_MASK 0x000000000FFF0000 -#define KVM_REG_ARM_COPROC_SHIFT 16 -#define KVM_REG_ARM_32_OPC2_MASK 0x0000000000000007 -#define KVM_REG_ARM_32_OPC2_SHIFT 0 -#define KVM_REG_ARM_OPC1_MASK 0x0000000000000078 -#define KVM_REG_ARM_OPC1_SHIFT 3 -#define KVM_REG_ARM_CRM_MASK 0x0000000000000780 -#define KVM_REG_ARM_CRM_SHIFT 7 -#define KVM_REG_ARM_32_CRN_MASK 0x0000000000007800 -#define KVM_REG_ARM_32_CRN_SHIFT 11 -/* - * For KVM currently all guest registers are nonsecure, but we reserve a bit - * in the encoding to distinguish secure from nonsecure for AArch32 system - * registers that are banked by security. This is 1 for the secure banked - * register, and 0 for the nonsecure banked register or if the register is - * not banked by security. - */ -#define KVM_REG_ARM_SECURE_MASK 0x0000000010000000 -#define KVM_REG_ARM_SECURE_SHIFT 28 - -#define ARM_CP15_REG_SHIFT_MASK(x,n) \ - (((x) << KVM_REG_ARM_ ## n ## _SHIFT) & KVM_REG_ARM_ ## n ## _MASK) - -#define __ARM_CP15_REG(op1,crn,crm,op2) \ - (KVM_REG_ARM | (15 << KVM_REG_ARM_COPROC_SHIFT) | \ - ARM_CP15_REG_SHIFT_MASK(op1, OPC1) | \ - ARM_CP15_REG_SHIFT_MASK(crn, 32_CRN) | \ - ARM_CP15_REG_SHIFT_MASK(crm, CRM) | \ - ARM_CP15_REG_SHIFT_MASK(op2, 32_OPC2)) - -#define ARM_CP15_REG32(...) (__ARM_CP15_REG(__VA_ARGS__) | KVM_REG_SIZE_U32) - -#define __ARM_CP15_REG64(op1,crm) \ - (__ARM_CP15_REG(op1, 0, crm, 0) | KVM_REG_SIZE_U64) -#define ARM_CP15_REG64(...) __ARM_CP15_REG64(__VA_ARGS__) - -/* PL1 Physical Timer Registers */ -#define KVM_REG_ARM_PTIMER_CTL ARM_CP15_REG32(0, 14, 2, 1) -#define KVM_REG_ARM_PTIMER_CNT ARM_CP15_REG64(0, 14) -#define KVM_REG_ARM_PTIMER_CVAL ARM_CP15_REG64(2, 14) - -/* Virtual Timer Registers */ -#define KVM_REG_ARM_TIMER_CTL ARM_CP15_REG32(0, 14, 3, 1) -#define KVM_REG_ARM_TIMER_CNT ARM_CP15_REG64(1, 14) -#define KVM_REG_ARM_TIMER_CVAL ARM_CP15_REG64(3, 14) - -/* Normal registers are mapped as coprocessor 16. */ -#define KVM_REG_ARM_CORE (0x0010 << KVM_REG_ARM_COPROC_SHIFT) -#define KVM_REG_ARM_CORE_REG(name) (offsetof(struct kvm_regs, name) / 4) - -/* Some registers need more space to represent values. */ -#define KVM_REG_ARM_DEMUX (0x0011 << KVM_REG_ARM_COPROC_SHIFT) -#define KVM_REG_ARM_DEMUX_ID_MASK 0x000000000000FF00 -#define KVM_REG_ARM_DEMUX_ID_SHIFT 8 -#define KVM_REG_ARM_DEMUX_ID_CCSIDR (0x00 << KVM_REG_ARM_DEMUX_ID_SHIFT) -#define KVM_REG_ARM_DEMUX_VAL_MASK 0x00000000000000FF -#define KVM_REG_ARM_DEMUX_VAL_SHIFT 0 - -/* VFP registers: we could overload CP10 like ARM does, but that's ugly. */ -#define KVM_REG_ARM_VFP (0x0012 << KVM_REG_ARM_COPROC_SHIFT) -#define KVM_REG_ARM_VFP_MASK 0x000000000000FFFF -#define KVM_REG_ARM_VFP_BASE_REG 0x0 -#define KVM_REG_ARM_VFP_FPSID 0x1000 -#define KVM_REG_ARM_VFP_FPSCR 0x1001 -#define KVM_REG_ARM_VFP_MVFR1 0x1006 -#define KVM_REG_ARM_VFP_MVFR0 0x1007 -#define KVM_REG_ARM_VFP_FPEXC 0x1008 -#define KVM_REG_ARM_VFP_FPINST 0x1009 -#define KVM_REG_ARM_VFP_FPINST2 0x100A - -/* KVM-as-firmware specific pseudo-registers */ -#define KVM_REG_ARM_FW (0x0014 << KVM_REG_ARM_COPROC_SHIFT) -#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM | KVM_REG_SIZE_U64 | \ - KVM_REG_ARM_FW | ((r) & 0xffff)) -#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0) -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1 KVM_REG_ARM_FW_REG(1) - /* Higher values mean better protection. */ -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL 0 -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL 1 -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED 2 -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2 KVM_REG_ARM_FW_REG(2) - /* Higher values mean better protection. */ -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL 0 -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN 1 -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL 2 -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED 3 -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED (1U << 4) - -/* Device Control API: ARM VGIC */ -#define KVM_DEV_ARM_VGIC_GRP_ADDR 0 -#define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1 -#define KVM_DEV_ARM_VGIC_GRP_CPU_REGS 2 -#define KVM_DEV_ARM_VGIC_CPUID_SHIFT 32 -#define KVM_DEV_ARM_VGIC_CPUID_MASK (0xffULL << KVM_DEV_ARM_VGIC_CPUID_SHIFT) -#define KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT 32 -#define KVM_DEV_ARM_VGIC_V3_MPIDR_MASK \ - (0xffffffffULL << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT) -#define KVM_DEV_ARM_VGIC_OFFSET_SHIFT 0 -#define KVM_DEV_ARM_VGIC_OFFSET_MASK (0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) -#define KVM_DEV_ARM_VGIC_SYSREG_INSTR_MASK (0xffff) -#define KVM_DEV_ARM_VGIC_GRP_NR_IRQS 3 -#define KVM_DEV_ARM_VGIC_GRP_CTRL 4 -#define KVM_DEV_ARM_VGIC_GRP_REDIST_REGS 5 -#define KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS 6 -#define KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO 7 -#define KVM_DEV_ARM_VGIC_GRP_ITS_REGS 8 -#define KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT 10 -#define KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK \ - (0x3fffffULL << KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT) -#define KVM_DEV_ARM_VGIC_LINE_LEVEL_INTID_MASK 0x3ff -#define VGIC_LEVEL_INFO_LINE_LEVEL 0 - -/* Device Control API on vcpu fd */ -#define KVM_ARM_VCPU_PMU_V3_CTRL 0 -#define KVM_ARM_VCPU_PMU_V3_IRQ 0 -#define KVM_ARM_VCPU_PMU_V3_INIT 1 -#define KVM_ARM_VCPU_TIMER_CTRL 1 -#define KVM_ARM_VCPU_TIMER_IRQ_VTIMER 0 -#define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1 - -#define KVM_DEV_ARM_VGIC_CTRL_INIT 0 -#define KVM_DEV_ARM_ITS_SAVE_TABLES 1 -#define KVM_DEV_ARM_ITS_RESTORE_TABLES 2 -#define KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES 3 -#define KVM_DEV_ARM_ITS_CTRL_RESET 4 - -/* KVM_IRQ_LINE irq field index values */ -#define KVM_ARM_IRQ_VCPU2_SHIFT 28 -#define KVM_ARM_IRQ_VCPU2_MASK 0xf -#define KVM_ARM_IRQ_TYPE_SHIFT 24 -#define KVM_ARM_IRQ_TYPE_MASK 0xf -#define KVM_ARM_IRQ_VCPU_SHIFT 16 -#define KVM_ARM_IRQ_VCPU_MASK 0xff -#define KVM_ARM_IRQ_NUM_SHIFT 0 -#define KVM_ARM_IRQ_NUM_MASK 0xffff - -/* irq_type field */ -#define KVM_ARM_IRQ_TYPE_CPU 0 -#define KVM_ARM_IRQ_TYPE_SPI 1 -#define KVM_ARM_IRQ_TYPE_PPI 2 - -/* out-of-kernel GIC cpu interrupt injection irq_number field */ -#define KVM_ARM_IRQ_CPU_IRQ 0 -#define KVM_ARM_IRQ_CPU_FIQ 1 - -/* - * This used to hold the highest supported SPI, but it is now obsolete - * and only here to provide source code level compatibility with older - * userland. The highest SPI number can be set via KVM_DEV_ARM_VGIC_GRP_NR_IRQS. - */ -#define KVM_ARM_IRQ_GIC_MAX 127 - -/* One single KVM irqchip, ie. the VGIC */ -#define KVM_NR_IRQCHIPS 1 - -/* PSCI interface */ -#define KVM_PSCI_FN_BASE 0x95c1ba5e -#define KVM_PSCI_FN(n) (KVM_PSCI_FN_BASE + (n)) - -#define KVM_PSCI_FN_CPU_SUSPEND KVM_PSCI_FN(0) -#define KVM_PSCI_FN_CPU_OFF KVM_PSCI_FN(1) -#define KVM_PSCI_FN_CPU_ON KVM_PSCI_FN(2) -#define KVM_PSCI_FN_MIGRATE KVM_PSCI_FN(3) - -#define KVM_PSCI_RET_SUCCESS PSCI_RET_SUCCESS -#define KVM_PSCI_RET_NI PSCI_RET_NOT_SUPPORTED -#define KVM_PSCI_RET_INVAL PSCI_RET_INVALID_PARAMS -#define KVM_PSCI_RET_DENIED PSCI_RET_DENIED - -#endif /* __ARM_KVM_H__ */ diff --git a/linux-headers/asm-arm/mman.h b/linux-headers/asm-arm/mman.h deleted file mode 100644 index 41f99c573b93c..0000000000000 --- a/linux-headers/asm-arm/mman.h +++ /dev/null @@ -1,4 +0,0 @@ -#include - -#define arch_mmap_check(addr, len, flags) \ - (((flags) & MAP_FIXED && (addr) < FIRST_USER_ADDRESS) ? -EINVAL : 0) diff --git a/linux-headers/asm-arm/unistd-common.h b/linux-headers/asm-arm/unistd-common.h deleted file mode 100644 index 57cd1f21dbd5f..0000000000000 --- a/linux-headers/asm-arm/unistd-common.h +++ /dev/null @@ -1,397 +0,0 @@ -#ifndef _ASM_ARM_UNISTD_COMMON_H -#define _ASM_ARM_UNISTD_COMMON_H 1 - -#define __NR_restart_syscall (__NR_SYSCALL_BASE + 0) -#define __NR_exit (__NR_SYSCALL_BASE + 1) -#define __NR_fork (__NR_SYSCALL_BASE + 2) -#define __NR_read (__NR_SYSCALL_BASE + 3) -#define __NR_write (__NR_SYSCALL_BASE + 4) -#define __NR_open (__NR_SYSCALL_BASE + 5) -#define __NR_close (__NR_SYSCALL_BASE + 6) -#define __NR_creat (__NR_SYSCALL_BASE + 8) -#define __NR_link (__NR_SYSCALL_BASE + 9) -#define __NR_unlink (__NR_SYSCALL_BASE + 10) -#define __NR_execve (__NR_SYSCALL_BASE + 11) -#define __NR_chdir (__NR_SYSCALL_BASE + 12) -#define __NR_mknod (__NR_SYSCALL_BASE + 14) -#define __NR_chmod (__NR_SYSCALL_BASE + 15) -#define __NR_lchown (__NR_SYSCALL_BASE + 16) -#define __NR_lseek (__NR_SYSCALL_BASE + 19) -#define __NR_getpid (__NR_SYSCALL_BASE + 20) -#define __NR_mount (__NR_SYSCALL_BASE + 21) -#define __NR_setuid (__NR_SYSCALL_BASE + 23) -#define __NR_getuid (__NR_SYSCALL_BASE + 24) -#define __NR_ptrace (__NR_SYSCALL_BASE + 26) -#define __NR_pause (__NR_SYSCALL_BASE + 29) -#define __NR_access (__NR_SYSCALL_BASE + 33) -#define __NR_nice (__NR_SYSCALL_BASE + 34) -#define __NR_sync (__NR_SYSCALL_BASE + 36) -#define __NR_kill (__NR_SYSCALL_BASE + 37) -#define __NR_rename (__NR_SYSCALL_BASE + 38) -#define __NR_mkdir (__NR_SYSCALL_BASE + 39) -#define __NR_rmdir (__NR_SYSCALL_BASE + 40) -#define __NR_dup (__NR_SYSCALL_BASE + 41) -#define __NR_pipe (__NR_SYSCALL_BASE + 42) -#define __NR_times (__NR_SYSCALL_BASE + 43) -#define __NR_brk (__NR_SYSCALL_BASE + 45) -#define __NR_setgid (__NR_SYSCALL_BASE + 46) -#define __NR_getgid (__NR_SYSCALL_BASE + 47) -#define __NR_geteuid (__NR_SYSCALL_BASE + 49) -#define __NR_getegid (__NR_SYSCALL_BASE + 50) -#define __NR_acct (__NR_SYSCALL_BASE + 51) -#define __NR_umount2 (__NR_SYSCALL_BASE + 52) -#define __NR_ioctl (__NR_SYSCALL_BASE + 54) -#define __NR_fcntl (__NR_SYSCALL_BASE + 55) -#define __NR_setpgid (__NR_SYSCALL_BASE + 57) -#define __NR_umask (__NR_SYSCALL_BASE + 60) -#define __NR_chroot (__NR_SYSCALL_BASE + 61) -#define __NR_ustat (__NR_SYSCALL_BASE + 62) -#define __NR_dup2 (__NR_SYSCALL_BASE + 63) -#define __NR_getppid (__NR_SYSCALL_BASE + 64) -#define __NR_getpgrp (__NR_SYSCALL_BASE + 65) -#define __NR_setsid (__NR_SYSCALL_BASE + 66) -#define __NR_sigaction (__NR_SYSCALL_BASE + 67) -#define __NR_setreuid (__NR_SYSCALL_BASE + 70) -#define __NR_setregid (__NR_SYSCALL_BASE + 71) -#define __NR_sigsuspend (__NR_SYSCALL_BASE + 72) -#define __NR_sigpending (__NR_SYSCALL_BASE + 73) -#define __NR_sethostname (__NR_SYSCALL_BASE + 74) -#define __NR_setrlimit (__NR_SYSCALL_BASE + 75) -#define __NR_getrusage (__NR_SYSCALL_BASE + 77) -#define __NR_gettimeofday (__NR_SYSCALL_BASE + 78) -#define __NR_settimeofday (__NR_SYSCALL_BASE + 79) -#define __NR_getgroups (__NR_SYSCALL_BASE + 80) -#define __NR_setgroups (__NR_SYSCALL_BASE + 81) -#define __NR_symlink (__NR_SYSCALL_BASE + 83) -#define __NR_readlink (__NR_SYSCALL_BASE + 85) -#define __NR_uselib (__NR_SYSCALL_BASE + 86) -#define __NR_swapon (__NR_SYSCALL_BASE + 87) -#define __NR_reboot (__NR_SYSCALL_BASE + 88) -#define __NR_munmap (__NR_SYSCALL_BASE + 91) -#define __NR_truncate (__NR_SYSCALL_BASE + 92) -#define __NR_ftruncate (__NR_SYSCALL_BASE + 93) -#define __NR_fchmod (__NR_SYSCALL_BASE + 94) -#define __NR_fchown (__NR_SYSCALL_BASE + 95) -#define __NR_getpriority (__NR_SYSCALL_BASE + 96) -#define __NR_setpriority (__NR_SYSCALL_BASE + 97) -#define __NR_statfs (__NR_SYSCALL_BASE + 99) -#define __NR_fstatfs (__NR_SYSCALL_BASE + 100) -#define __NR_syslog (__NR_SYSCALL_BASE + 103) -#define __NR_setitimer (__NR_SYSCALL_BASE + 104) -#define __NR_getitimer (__NR_SYSCALL_BASE + 105) -#define __NR_stat (__NR_SYSCALL_BASE + 106) -#define __NR_lstat (__NR_SYSCALL_BASE + 107) -#define __NR_fstat (__NR_SYSCALL_BASE + 108) -#define __NR_vhangup (__NR_SYSCALL_BASE + 111) -#define __NR_wait4 (__NR_SYSCALL_BASE + 114) -#define __NR_swapoff (__NR_SYSCALL_BASE + 115) -#define __NR_sysinfo (__NR_SYSCALL_BASE + 116) -#define __NR_fsync (__NR_SYSCALL_BASE + 118) -#define __NR_sigreturn (__NR_SYSCALL_BASE + 119) -#define __NR_clone (__NR_SYSCALL_BASE + 120) -#define __NR_setdomainname (__NR_SYSCALL_BASE + 121) -#define __NR_uname (__NR_SYSCALL_BASE + 122) -#define __NR_adjtimex (__NR_SYSCALL_BASE + 124) -#define __NR_mprotect (__NR_SYSCALL_BASE + 125) -#define __NR_sigprocmask (__NR_SYSCALL_BASE + 126) -#define __NR_init_module (__NR_SYSCALL_BASE + 128) -#define __NR_delete_module (__NR_SYSCALL_BASE + 129) -#define __NR_quotactl (__NR_SYSCALL_BASE + 131) -#define __NR_getpgid (__NR_SYSCALL_BASE + 132) -#define __NR_fchdir (__NR_SYSCALL_BASE + 133) -#define __NR_bdflush (__NR_SYSCALL_BASE + 134) -#define __NR_sysfs (__NR_SYSCALL_BASE + 135) -#define __NR_personality (__NR_SYSCALL_BASE + 136) -#define __NR_setfsuid (__NR_SYSCALL_BASE + 138) -#define __NR_setfsgid (__NR_SYSCALL_BASE + 139) -#define __NR__llseek (__NR_SYSCALL_BASE + 140) -#define __NR_getdents (__NR_SYSCALL_BASE + 141) -#define __NR__newselect (__NR_SYSCALL_BASE + 142) -#define __NR_flock (__NR_SYSCALL_BASE + 143) -#define __NR_msync (__NR_SYSCALL_BASE + 144) -#define __NR_readv (__NR_SYSCALL_BASE + 145) -#define __NR_writev (__NR_SYSCALL_BASE + 146) -#define __NR_getsid (__NR_SYSCALL_BASE + 147) -#define __NR_fdatasync (__NR_SYSCALL_BASE + 148) -#define __NR__sysctl (__NR_SYSCALL_BASE + 149) -#define __NR_mlock (__NR_SYSCALL_BASE + 150) -#define __NR_munlock (__NR_SYSCALL_BASE + 151) -#define __NR_mlockall (__NR_SYSCALL_BASE + 152) -#define __NR_munlockall (__NR_SYSCALL_BASE + 153) -#define __NR_sched_setparam (__NR_SYSCALL_BASE + 154) -#define __NR_sched_getparam (__NR_SYSCALL_BASE + 155) -#define __NR_sched_setscheduler (__NR_SYSCALL_BASE + 156) -#define __NR_sched_getscheduler (__NR_SYSCALL_BASE + 157) -#define __NR_sched_yield (__NR_SYSCALL_BASE + 158) -#define __NR_sched_get_priority_max (__NR_SYSCALL_BASE + 159) -#define __NR_sched_get_priority_min (__NR_SYSCALL_BASE + 160) -#define __NR_sched_rr_get_interval (__NR_SYSCALL_BASE + 161) -#define __NR_nanosleep (__NR_SYSCALL_BASE + 162) -#define __NR_mremap (__NR_SYSCALL_BASE + 163) -#define __NR_setresuid (__NR_SYSCALL_BASE + 164) -#define __NR_getresuid (__NR_SYSCALL_BASE + 165) -#define __NR_poll (__NR_SYSCALL_BASE + 168) -#define __NR_nfsservctl (__NR_SYSCALL_BASE + 169) -#define __NR_setresgid (__NR_SYSCALL_BASE + 170) -#define __NR_getresgid (__NR_SYSCALL_BASE + 171) -#define __NR_prctl (__NR_SYSCALL_BASE + 172) -#define __NR_rt_sigreturn (__NR_SYSCALL_BASE + 173) -#define __NR_rt_sigaction (__NR_SYSCALL_BASE + 174) -#define __NR_rt_sigprocmask (__NR_SYSCALL_BASE + 175) -#define __NR_rt_sigpending (__NR_SYSCALL_BASE + 176) -#define __NR_rt_sigtimedwait (__NR_SYSCALL_BASE + 177) -#define __NR_rt_sigqueueinfo (__NR_SYSCALL_BASE + 178) -#define __NR_rt_sigsuspend (__NR_SYSCALL_BASE + 179) -#define __NR_pread64 (__NR_SYSCALL_BASE + 180) -#define __NR_pwrite64 (__NR_SYSCALL_BASE + 181) -#define __NR_chown (__NR_SYSCALL_BASE + 182) -#define __NR_getcwd (__NR_SYSCALL_BASE + 183) -#define __NR_capget (__NR_SYSCALL_BASE + 184) -#define __NR_capset (__NR_SYSCALL_BASE + 185) -#define __NR_sigaltstack (__NR_SYSCALL_BASE + 186) -#define __NR_sendfile (__NR_SYSCALL_BASE + 187) -#define __NR_vfork (__NR_SYSCALL_BASE + 190) -#define __NR_ugetrlimit (__NR_SYSCALL_BASE + 191) -#define __NR_mmap2 (__NR_SYSCALL_BASE + 192) -#define __NR_truncate64 (__NR_SYSCALL_BASE + 193) -#define __NR_ftruncate64 (__NR_SYSCALL_BASE + 194) -#define __NR_stat64 (__NR_SYSCALL_BASE + 195) -#define __NR_lstat64 (__NR_SYSCALL_BASE + 196) -#define __NR_fstat64 (__NR_SYSCALL_BASE + 197) -#define __NR_lchown32 (__NR_SYSCALL_BASE + 198) -#define __NR_getuid32 (__NR_SYSCALL_BASE + 199) -#define __NR_getgid32 (__NR_SYSCALL_BASE + 200) -#define __NR_geteuid32 (__NR_SYSCALL_BASE + 201) -#define __NR_getegid32 (__NR_SYSCALL_BASE + 202) -#define __NR_setreuid32 (__NR_SYSCALL_BASE + 203) -#define __NR_setregid32 (__NR_SYSCALL_BASE + 204) -#define __NR_getgroups32 (__NR_SYSCALL_BASE + 205) -#define __NR_setgroups32 (__NR_SYSCALL_BASE + 206) -#define __NR_fchown32 (__NR_SYSCALL_BASE + 207) -#define __NR_setresuid32 (__NR_SYSCALL_BASE + 208) -#define __NR_getresuid32 (__NR_SYSCALL_BASE + 209) -#define __NR_setresgid32 (__NR_SYSCALL_BASE + 210) -#define __NR_getresgid32 (__NR_SYSCALL_BASE + 211) -#define __NR_chown32 (__NR_SYSCALL_BASE + 212) -#define __NR_setuid32 (__NR_SYSCALL_BASE + 213) -#define __NR_setgid32 (__NR_SYSCALL_BASE + 214) -#define __NR_setfsuid32 (__NR_SYSCALL_BASE + 215) -#define __NR_setfsgid32 (__NR_SYSCALL_BASE + 216) -#define __NR_getdents64 (__NR_SYSCALL_BASE + 217) -#define __NR_pivot_root (__NR_SYSCALL_BASE + 218) -#define __NR_mincore (__NR_SYSCALL_BASE + 219) -#define __NR_madvise (__NR_SYSCALL_BASE + 220) -#define __NR_fcntl64 (__NR_SYSCALL_BASE + 221) -#define __NR_gettid (__NR_SYSCALL_BASE + 224) -#define __NR_readahead (__NR_SYSCALL_BASE + 225) -#define __NR_setxattr (__NR_SYSCALL_BASE + 226) -#define __NR_lsetxattr (__NR_SYSCALL_BASE + 227) -#define __NR_fsetxattr (__NR_SYSCALL_BASE + 228) -#define __NR_getxattr (__NR_SYSCALL_BASE + 229) -#define __NR_lgetxattr (__NR_SYSCALL_BASE + 230) -#define __NR_fgetxattr (__NR_SYSCALL_BASE + 231) -#define __NR_listxattr (__NR_SYSCALL_BASE + 232) -#define __NR_llistxattr (__NR_SYSCALL_BASE + 233) -#define __NR_flistxattr (__NR_SYSCALL_BASE + 234) -#define __NR_removexattr (__NR_SYSCALL_BASE + 235) -#define __NR_lremovexattr (__NR_SYSCALL_BASE + 236) -#define __NR_fremovexattr (__NR_SYSCALL_BASE + 237) -#define __NR_tkill (__NR_SYSCALL_BASE + 238) -#define __NR_sendfile64 (__NR_SYSCALL_BASE + 239) -#define __NR_futex (__NR_SYSCALL_BASE + 240) -#define __NR_sched_setaffinity (__NR_SYSCALL_BASE + 241) -#define __NR_sched_getaffinity (__NR_SYSCALL_BASE + 242) -#define __NR_io_setup (__NR_SYSCALL_BASE + 243) -#define __NR_io_destroy (__NR_SYSCALL_BASE + 244) -#define __NR_io_getevents (__NR_SYSCALL_BASE + 245) -#define __NR_io_submit (__NR_SYSCALL_BASE + 246) -#define __NR_io_cancel (__NR_SYSCALL_BASE + 247) -#define __NR_exit_group (__NR_SYSCALL_BASE + 248) -#define __NR_lookup_dcookie (__NR_SYSCALL_BASE + 249) -#define __NR_epoll_create (__NR_SYSCALL_BASE + 250) -#define __NR_epoll_ctl (__NR_SYSCALL_BASE + 251) -#define __NR_epoll_wait (__NR_SYSCALL_BASE + 252) -#define __NR_remap_file_pages (__NR_SYSCALL_BASE + 253) -#define __NR_set_tid_address (__NR_SYSCALL_BASE + 256) -#define __NR_timer_create (__NR_SYSCALL_BASE + 257) -#define __NR_timer_settime (__NR_SYSCALL_BASE + 258) -#define __NR_timer_gettime (__NR_SYSCALL_BASE + 259) -#define __NR_timer_getoverrun (__NR_SYSCALL_BASE + 260) -#define __NR_timer_delete (__NR_SYSCALL_BASE + 261) -#define __NR_clock_settime (__NR_SYSCALL_BASE + 262) -#define __NR_clock_gettime (__NR_SYSCALL_BASE + 263) -#define __NR_clock_getres (__NR_SYSCALL_BASE + 264) -#define __NR_clock_nanosleep (__NR_SYSCALL_BASE + 265) -#define __NR_statfs64 (__NR_SYSCALL_BASE + 266) -#define __NR_fstatfs64 (__NR_SYSCALL_BASE + 267) -#define __NR_tgkill (__NR_SYSCALL_BASE + 268) -#define __NR_utimes (__NR_SYSCALL_BASE + 269) -#define __NR_arm_fadvise64_64 (__NR_SYSCALL_BASE + 270) -#define __NR_pciconfig_iobase (__NR_SYSCALL_BASE + 271) -#define __NR_pciconfig_read (__NR_SYSCALL_BASE + 272) -#define __NR_pciconfig_write (__NR_SYSCALL_BASE + 273) -#define __NR_mq_open (__NR_SYSCALL_BASE + 274) -#define __NR_mq_unlink (__NR_SYSCALL_BASE + 275) -#define __NR_mq_timedsend (__NR_SYSCALL_BASE + 276) -#define __NR_mq_timedreceive (__NR_SYSCALL_BASE + 277) -#define __NR_mq_notify (__NR_SYSCALL_BASE + 278) -#define __NR_mq_getsetattr (__NR_SYSCALL_BASE + 279) -#define __NR_waitid (__NR_SYSCALL_BASE + 280) -#define __NR_socket (__NR_SYSCALL_BASE + 281) -#define __NR_bind (__NR_SYSCALL_BASE + 282) -#define __NR_connect (__NR_SYSCALL_BASE + 283) -#define __NR_listen (__NR_SYSCALL_BASE + 284) -#define __NR_accept (__NR_SYSCALL_BASE + 285) -#define __NR_getsockname (__NR_SYSCALL_BASE + 286) -#define __NR_getpeername (__NR_SYSCALL_BASE + 287) -#define __NR_socketpair (__NR_SYSCALL_BASE + 288) -#define __NR_send (__NR_SYSCALL_BASE + 289) -#define __NR_sendto (__NR_SYSCALL_BASE + 290) -#define __NR_recv (__NR_SYSCALL_BASE + 291) -#define __NR_recvfrom (__NR_SYSCALL_BASE + 292) -#define __NR_shutdown (__NR_SYSCALL_BASE + 293) -#define __NR_setsockopt (__NR_SYSCALL_BASE + 294) -#define __NR_getsockopt (__NR_SYSCALL_BASE + 295) -#define __NR_sendmsg (__NR_SYSCALL_BASE + 296) -#define __NR_recvmsg (__NR_SYSCALL_BASE + 297) -#define __NR_semop (__NR_SYSCALL_BASE + 298) -#define __NR_semget (__NR_SYSCALL_BASE + 299) -#define __NR_semctl (__NR_SYSCALL_BASE + 300) -#define __NR_msgsnd (__NR_SYSCALL_BASE + 301) -#define __NR_msgrcv (__NR_SYSCALL_BASE + 302) -#define __NR_msgget (__NR_SYSCALL_BASE + 303) -#define __NR_msgctl (__NR_SYSCALL_BASE + 304) -#define __NR_shmat (__NR_SYSCALL_BASE + 305) -#define __NR_shmdt (__NR_SYSCALL_BASE + 306) -#define __NR_shmget (__NR_SYSCALL_BASE + 307) -#define __NR_shmctl (__NR_SYSCALL_BASE + 308) -#define __NR_add_key (__NR_SYSCALL_BASE + 309) -#define __NR_request_key (__NR_SYSCALL_BASE + 310) -#define __NR_keyctl (__NR_SYSCALL_BASE + 311) -#define __NR_semtimedop (__NR_SYSCALL_BASE + 312) -#define __NR_vserver (__NR_SYSCALL_BASE + 313) -#define __NR_ioprio_set (__NR_SYSCALL_BASE + 314) -#define __NR_ioprio_get (__NR_SYSCALL_BASE + 315) -#define __NR_inotify_init (__NR_SYSCALL_BASE + 316) -#define __NR_inotify_add_watch (__NR_SYSCALL_BASE + 317) -#define __NR_inotify_rm_watch (__NR_SYSCALL_BASE + 318) -#define __NR_mbind (__NR_SYSCALL_BASE + 319) -#define __NR_get_mempolicy (__NR_SYSCALL_BASE + 320) -#define __NR_set_mempolicy (__NR_SYSCALL_BASE + 321) -#define __NR_openat (__NR_SYSCALL_BASE + 322) -#define __NR_mkdirat (__NR_SYSCALL_BASE + 323) -#define __NR_mknodat (__NR_SYSCALL_BASE + 324) -#define __NR_fchownat (__NR_SYSCALL_BASE + 325) -#define __NR_futimesat (__NR_SYSCALL_BASE + 326) -#define __NR_fstatat64 (__NR_SYSCALL_BASE + 327) -#define __NR_unlinkat (__NR_SYSCALL_BASE + 328) -#define __NR_renameat (__NR_SYSCALL_BASE + 329) -#define __NR_linkat (__NR_SYSCALL_BASE + 330) -#define __NR_symlinkat (__NR_SYSCALL_BASE + 331) -#define __NR_readlinkat (__NR_SYSCALL_BASE + 332) -#define __NR_fchmodat (__NR_SYSCALL_BASE + 333) -#define __NR_faccessat (__NR_SYSCALL_BASE + 334) -#define __NR_pselect6 (__NR_SYSCALL_BASE + 335) -#define __NR_ppoll (__NR_SYSCALL_BASE + 336) -#define __NR_unshare (__NR_SYSCALL_BASE + 337) -#define __NR_set_robust_list (__NR_SYSCALL_BASE + 338) -#define __NR_get_robust_list (__NR_SYSCALL_BASE + 339) -#define __NR_splice (__NR_SYSCALL_BASE + 340) -#define __NR_arm_sync_file_range (__NR_SYSCALL_BASE + 341) -#define __NR_tee (__NR_SYSCALL_BASE + 342) -#define __NR_vmsplice (__NR_SYSCALL_BASE + 343) -#define __NR_move_pages (__NR_SYSCALL_BASE + 344) -#define __NR_getcpu (__NR_SYSCALL_BASE + 345) -#define __NR_epoll_pwait (__NR_SYSCALL_BASE + 346) -#define __NR_kexec_load (__NR_SYSCALL_BASE + 347) -#define __NR_utimensat (__NR_SYSCALL_BASE + 348) -#define __NR_signalfd (__NR_SYSCALL_BASE + 349) -#define __NR_timerfd_create (__NR_SYSCALL_BASE + 350) -#define __NR_eventfd (__NR_SYSCALL_BASE + 351) -#define __NR_fallocate (__NR_SYSCALL_BASE + 352) -#define __NR_timerfd_settime (__NR_SYSCALL_BASE + 353) -#define __NR_timerfd_gettime (__NR_SYSCALL_BASE + 354) -#define __NR_signalfd4 (__NR_SYSCALL_BASE + 355) -#define __NR_eventfd2 (__NR_SYSCALL_BASE + 356) -#define __NR_epoll_create1 (__NR_SYSCALL_BASE + 357) -#define __NR_dup3 (__NR_SYSCALL_BASE + 358) -#define __NR_pipe2 (__NR_SYSCALL_BASE + 359) -#define __NR_inotify_init1 (__NR_SYSCALL_BASE + 360) -#define __NR_preadv (__NR_SYSCALL_BASE + 361) -#define __NR_pwritev (__NR_SYSCALL_BASE + 362) -#define __NR_rt_tgsigqueueinfo (__NR_SYSCALL_BASE + 363) -#define __NR_perf_event_open (__NR_SYSCALL_BASE + 364) -#define __NR_recvmmsg (__NR_SYSCALL_BASE + 365) -#define __NR_accept4 (__NR_SYSCALL_BASE + 366) -#define __NR_fanotify_init (__NR_SYSCALL_BASE + 367) -#define __NR_fanotify_mark (__NR_SYSCALL_BASE + 368) -#define __NR_prlimit64 (__NR_SYSCALL_BASE + 369) -#define __NR_name_to_handle_at (__NR_SYSCALL_BASE + 370) -#define __NR_open_by_handle_at (__NR_SYSCALL_BASE + 371) -#define __NR_clock_adjtime (__NR_SYSCALL_BASE + 372) -#define __NR_syncfs (__NR_SYSCALL_BASE + 373) -#define __NR_sendmmsg (__NR_SYSCALL_BASE + 374) -#define __NR_setns (__NR_SYSCALL_BASE + 375) -#define __NR_process_vm_readv (__NR_SYSCALL_BASE + 376) -#define __NR_process_vm_writev (__NR_SYSCALL_BASE + 377) -#define __NR_kcmp (__NR_SYSCALL_BASE + 378) -#define __NR_finit_module (__NR_SYSCALL_BASE + 379) -#define __NR_sched_setattr (__NR_SYSCALL_BASE + 380) -#define __NR_sched_getattr (__NR_SYSCALL_BASE + 381) -#define __NR_renameat2 (__NR_SYSCALL_BASE + 382) -#define __NR_seccomp (__NR_SYSCALL_BASE + 383) -#define __NR_getrandom (__NR_SYSCALL_BASE + 384) -#define __NR_memfd_create (__NR_SYSCALL_BASE + 385) -#define __NR_bpf (__NR_SYSCALL_BASE + 386) -#define __NR_execveat (__NR_SYSCALL_BASE + 387) -#define __NR_userfaultfd (__NR_SYSCALL_BASE + 388) -#define __NR_membarrier (__NR_SYSCALL_BASE + 389) -#define __NR_mlock2 (__NR_SYSCALL_BASE + 390) -#define __NR_copy_file_range (__NR_SYSCALL_BASE + 391) -#define __NR_preadv2 (__NR_SYSCALL_BASE + 392) -#define __NR_pwritev2 (__NR_SYSCALL_BASE + 393) -#define __NR_pkey_mprotect (__NR_SYSCALL_BASE + 394) -#define __NR_pkey_alloc (__NR_SYSCALL_BASE + 395) -#define __NR_pkey_free (__NR_SYSCALL_BASE + 396) -#define __NR_statx (__NR_SYSCALL_BASE + 397) -#define __NR_rseq (__NR_SYSCALL_BASE + 398) -#define __NR_io_pgetevents (__NR_SYSCALL_BASE + 399) -#define __NR_migrate_pages (__NR_SYSCALL_BASE + 400) -#define __NR_kexec_file_load (__NR_SYSCALL_BASE + 401) -#define __NR_clock_gettime64 (__NR_SYSCALL_BASE + 403) -#define __NR_clock_settime64 (__NR_SYSCALL_BASE + 404) -#define __NR_clock_adjtime64 (__NR_SYSCALL_BASE + 405) -#define __NR_clock_getres_time64 (__NR_SYSCALL_BASE + 406) -#define __NR_clock_nanosleep_time64 (__NR_SYSCALL_BASE + 407) -#define __NR_timer_gettime64 (__NR_SYSCALL_BASE + 408) -#define __NR_timer_settime64 (__NR_SYSCALL_BASE + 409) -#define __NR_timerfd_gettime64 (__NR_SYSCALL_BASE + 410) -#define __NR_timerfd_settime64 (__NR_SYSCALL_BASE + 411) -#define __NR_utimensat_time64 (__NR_SYSCALL_BASE + 412) -#define __NR_pselect6_time64 (__NR_SYSCALL_BASE + 413) -#define __NR_ppoll_time64 (__NR_SYSCALL_BASE + 414) -#define __NR_io_pgetevents_time64 (__NR_SYSCALL_BASE + 416) -#define __NR_recvmmsg_time64 (__NR_SYSCALL_BASE + 417) -#define __NR_mq_timedsend_time64 (__NR_SYSCALL_BASE + 418) -#define __NR_mq_timedreceive_time64 (__NR_SYSCALL_BASE + 419) -#define __NR_semtimedop_time64 (__NR_SYSCALL_BASE + 420) -#define __NR_rt_sigtimedwait_time64 (__NR_SYSCALL_BASE + 421) -#define __NR_futex_time64 (__NR_SYSCALL_BASE + 422) -#define __NR_sched_rr_get_interval_time64 (__NR_SYSCALL_BASE + 423) -#define __NR_pidfd_send_signal (__NR_SYSCALL_BASE + 424) -#define __NR_io_uring_setup (__NR_SYSCALL_BASE + 425) -#define __NR_io_uring_enter (__NR_SYSCALL_BASE + 426) -#define __NR_io_uring_register (__NR_SYSCALL_BASE + 427) -#define __NR_open_tree (__NR_SYSCALL_BASE + 428) -#define __NR_move_mount (__NR_SYSCALL_BASE + 429) -#define __NR_fsopen (__NR_SYSCALL_BASE + 430) -#define __NR_fsconfig (__NR_SYSCALL_BASE + 431) -#define __NR_fsmount (__NR_SYSCALL_BASE + 432) -#define __NR_fspick (__NR_SYSCALL_BASE + 433) -#define __NR_pidfd_open (__NR_SYSCALL_BASE + 434) -#define __NR_clone3 (__NR_SYSCALL_BASE + 435) -#define __NR_openat2 (__NR_SYSCALL_BASE + 437) -#define __NR_pidfd_getfd (__NR_SYSCALL_BASE + 438) -#define __NR_faccessat2 (__NR_SYSCALL_BASE + 439) - -#endif /* _ASM_ARM_UNISTD_COMMON_H */ diff --git a/linux-headers/asm-arm/unistd-eabi.h b/linux-headers/asm-arm/unistd-eabi.h deleted file mode 100644 index 266f1fcdfb370..0000000000000 --- a/linux-headers/asm-arm/unistd-eabi.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef _ASM_ARM_UNISTD_EABI_H -#define _ASM_ARM_UNISTD_EABI_H 1 - - -#endif /* _ASM_ARM_UNISTD_EABI_H */ diff --git a/linux-headers/asm-arm/unistd-oabi.h b/linux-headers/asm-arm/unistd-oabi.h deleted file mode 100644 index 47d9afb96d59d..0000000000000 --- a/linux-headers/asm-arm/unistd-oabi.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _ASM_ARM_UNISTD_OABI_H -#define _ASM_ARM_UNISTD_OABI_H 1 - -#define __NR_time (__NR_SYSCALL_BASE + 13) -#define __NR_umount (__NR_SYSCALL_BASE + 22) -#define __NR_stime (__NR_SYSCALL_BASE + 25) -#define __NR_alarm (__NR_SYSCALL_BASE + 27) -#define __NR_utime (__NR_SYSCALL_BASE + 30) -#define __NR_getrlimit (__NR_SYSCALL_BASE + 76) -#define __NR_select (__NR_SYSCALL_BASE + 82) -#define __NR_readdir (__NR_SYSCALL_BASE + 89) -#define __NR_mmap (__NR_SYSCALL_BASE + 90) -#define __NR_socketcall (__NR_SYSCALL_BASE + 102) -#define __NR_syscall (__NR_SYSCALL_BASE + 113) -#define __NR_ipc (__NR_SYSCALL_BASE + 117) - -#endif /* _ASM_ARM_UNISTD_OABI_H */ diff --git a/linux-headers/asm-arm/unistd.h b/linux-headers/asm-arm/unistd.h deleted file mode 100644 index 18b0825885ead..0000000000000 --- a/linux-headers/asm-arm/unistd.h +++ /dev/null @@ -1,41 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * arch/arm/include/asm/unistd.h - * - * Copyright (C) 2001-2005 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Please forward _all_ changes to this file to rmk@arm.linux.org.uk, - * no matter what the change is. Thanks! - */ -#ifndef __ASM_ARM_UNISTD_H -#define __ASM_ARM_UNISTD_H - -#define __NR_OABI_SYSCALL_BASE 0x900000 - -#if defined(__thumb__) || defined(__ARM_EABI__) -#define __NR_SYSCALL_BASE 0 -#include -#else -#define __NR_SYSCALL_BASE __NR_OABI_SYSCALL_BASE -#include -#endif - -#include -#define __NR_sync_file_range2 __NR_arm_sync_file_range - -/* - * The following SWIs are ARM private. - */ -#define __ARM_NR_BASE (__NR_SYSCALL_BASE+0x0f0000) -#define __ARM_NR_breakpoint (__ARM_NR_BASE+1) -#define __ARM_NR_cacheflush (__ARM_NR_BASE+2) -#define __ARM_NR_usr26 (__ARM_NR_BASE+3) -#define __ARM_NR_usr32 (__ARM_NR_BASE+4) -#define __ARM_NR_set_tls (__ARM_NR_BASE+5) -#define __ARM_NR_get_tls (__ARM_NR_BASE+6) - -#endif /* __ASM_ARM_UNISTD_H */ diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index b43b8ef75a632..717c379f9e095 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -156,11 +156,6 @@ EOF cp "$hdrdir/include/asm/unistd_32.h" "$output/linux-headers/asm-s390/" cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-s390/" fi - if [ $arch = arm ]; then - cp "$hdrdir/include/asm/unistd-eabi.h" "$output/linux-headers/asm-arm/" - cp "$hdrdir/include/asm/unistd-oabi.h" "$output/linux-headers/asm-arm/" - cp "$hdrdir/include/asm/unistd-common.h" "$output/linux-headers/asm-arm/" - fi if [ $arch = arm64 ]; then cp "$hdrdir/include/asm/sve_context.h" "$output/linux-headers/asm-arm64/" cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-arm64/" From 069a2ce8a75c9b59a4d08d6d2da3b36bfc5af3f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 15 Jul 2025 15:30:17 +0100 Subject: [PATCH 0020/1794] functional: ensure log handlers are closed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids a resource leak warning from python when the log handler is garbage collected. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20250715143023.1851000-9-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 2082c6fce43b0..71c7160adcf8d 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -232,6 +232,7 @@ def tearDown(self): self.socketdir = None self.machinelog.removeHandler(self._log_fh) self.log.removeHandler(self._log_fh) + self._log_fh.close() def main(): path = os.path.basename(sys.argv[0])[:-3] @@ -399,4 +400,5 @@ def tearDown(self): for vm in self._vms.values(): vm.shutdown() logging.getLogger('console').removeHandler(self._console_log_fh) + self._console_log_fh.close() super().tearDown() From 72bc0134b500d599f0f1c253c78c68df642d1634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 15 Jul 2025 15:30:18 +0100 Subject: [PATCH 0021/1794] functional: ensure sockets and files are closed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The multiprocess and virtio_gpu tests open sockets but then forget to close them, which triggers resource leak warnings The virtio_gpu test also fails to close a log file it opens. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20250715143023.1851000-10-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_multiprocess.py | 3 +++ tests/functional/test_virtio_gpu.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/tests/functional/test_multiprocess.py b/tests/functional/test_multiprocess.py index 751cf10e635cd..92d5207b0eb20 100755 --- a/tests/functional/test_multiprocess.py +++ b/tests/functional/test_multiprocess.py @@ -83,6 +83,9 @@ def do_test(self, kernel_asset, initrd_asset, 'cat /sys/bus/pci/devices/*/uevent', 'PCI_ID=1000:0012') + proxy_sock.close() + remote_sock.close() + def test_multiprocess(self): kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE if self.arch == 'x86_64': diff --git a/tests/functional/test_virtio_gpu.py b/tests/functional/test_virtio_gpu.py index 81c9156d63876..be96de24da219 100755 --- a/tests/functional/test_virtio_gpu.py +++ b/tests/functional/test_virtio_gpu.py @@ -108,6 +108,7 @@ def test_vhost_user_vga_virgl(self): shell=False, close_fds=False, ) + self._vug_log_file.close() self.vm.set_console() self.vm.add_args("-cpu", "host") @@ -135,6 +136,7 @@ def test_vhost_user_vga_virgl(self): "features: +virgl +edid") self.vm.shutdown() qemu_sock.close() + vug_sock.close() vugp.terminate() vugp.wait() From c3fd296cf7b1016671205e1525e4e9caf870f68e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 15 Jul 2025 15:30:19 +0100 Subject: [PATCH 0022/1794] functional: always enable all python warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Of most importance is that this gives us a heads-up if anything we rely on has been deprecated. The default python behaviour only emits a warning if triggered from __main__ which is very limited. Setting the env variable further ensures that any python child processes will also display warnings. Signed-off-by: Daniel P. Berrangé Acked-by: Thomas Huth Message-ID: <20250715143023.1851000-11-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 71c7160adcf8d..2a78e735f1604 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -19,6 +19,7 @@ from subprocess import run import sys import tempfile +import warnings import unittest import uuid @@ -235,6 +236,9 @@ def tearDown(self): self._log_fh.close() def main(): + warnings.simplefilter("default") + os.environ["PYTHONWARNINGS"] = "default" + path = os.path.basename(sys.argv[0])[:-3] cache = os.environ.get("QEMU_TEST_PRECACHE", None) From 2c182e1213a0fd334f13948cb80b886e6fd1ab88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 17 Jul 2025 11:41:05 +0100 Subject: [PATCH 0023/1794] docs/devel: fix over-quoting of QEMU_TEST_KEEP_SCRATCH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-ID: <20250717104105.2656786-1-alex.bennee@linaro.org> Signed-off-by: Thomas Huth --- docs/devel/testing/functional.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/devel/testing/functional.rst b/docs/devel/testing/functional.rst index 9e56dd1b11892..3728bab6c0c4b 100644 --- a/docs/devel/testing/functional.rst +++ b/docs/devel/testing/functional.rst @@ -65,7 +65,7 @@ directory should be your build folder. For example:: The test framework will automatically purge any scratch files created during the tests. If needing to debug a failed test, it is possible to keep these -files around on disk by setting ```QEMU_TEST_KEEP_SCRATCH=1``` as an env +files around on disk by setting ``QEMU_TEST_KEEP_SCRATCH=1`` as an env variable. Any preserved files will be deleted the next time the test is run without this variable set. From 301fbbaf03fb114a1e45833987ddfd7bbb403963 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:10 +1000 Subject: [PATCH 0024/1794] ppc/xive: Fix xive trace event output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Typo, IBP should be IPB. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-2-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/trace-events | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 334aa6a97bad0..9ed2616e58fe9 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -274,9 +274,9 @@ kvm_xive_cpu_connect(uint32_t id) "connect CPU%d to KVM device" kvm_xive_source_reset(uint32_t srcno) "IRQ 0x%x" # xive.c -xive_tctx_accept(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IBP=0x%02x PIPR=0x%02x CPPR=0x%02x NSR=0x%02x ACK" -xive_tctx_notify(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IBP=0x%02x PIPR=0x%02x CPPR=0x%02x NSR=0x%02x raise !" -xive_tctx_set_cppr(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IBP=0x%02x PIPR=0x%02x new CPPR=0x%02x NSR=0x%02x" +xive_tctx_accept(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IPB=0x%02x PIPR=0x%02x CPPR=0x%02x NSR=0x%02x ACK" +xive_tctx_notify(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IPB=0x%02x PIPR=0x%02x CPPR=0x%02x NSR=0x%02x raise !" +xive_tctx_set_cppr(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IPB=0x%02x PIPR=0x%02x new CPPR=0x%02x NSR=0x%02x" xive_source_esb_read(uint64_t addr, uint32_t srcno, uint64_t value) "@0x%"PRIx64" IRQ 0x%x val=0x%"PRIx64 xive_source_esb_write(uint64_t addr, uint32_t srcno, uint64_t value) "@0x%"PRIx64" IRQ 0x%x val=0x%"PRIx64 xive_router_end_notify(uint8_t end_blk, uint32_t end_idx, uint32_t end_data) "END 0x%02x/0x%04x -> enqueue 0x%08x" From f0aab779418ed883ea2b5ffcc3985ef26f6e3545 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:11 +1000 Subject: [PATCH 0025/1794] ppc/xive: Report access size in XIVE TM operation error logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Report access size in XIVE TM operation error logs. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-3-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 27b473e4d7625..120376fb6b6df 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -326,7 +326,7 @@ static void xive_tm_raw_write(XiveTCTX *tctx, hwaddr offset, uint64_t value, */ if (size < 4 || !mask || ring_offset == TM_QW0_USER) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid write access at TIMA @%" - HWADDR_PRIx"\n", offset); + HWADDR_PRIx" size %d\n", offset, size); return; } @@ -357,7 +357,7 @@ static uint64_t xive_tm_raw_read(XiveTCTX *tctx, hwaddr offset, unsigned size) */ if (size < 4 || !mask || ring_offset == TM_QW0_USER) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid read access at TIMA @%" - HWADDR_PRIx"\n", offset); + HWADDR_PRIx" size %d\n", offset, size); return -1; } @@ -688,7 +688,7 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, xto = xive_tm_find_op(tctx->xptr, offset, size, true); if (!xto) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid write access at TIMA " - "@%"HWADDR_PRIx"\n", offset); + "@%"HWADDR_PRIx" size %d\n", offset, size); } else { xto->write_handler(xptr, tctx, offset, value, size); } @@ -727,7 +727,7 @@ uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, xto = xive_tm_find_op(tctx->xptr, offset, size, false); if (!xto) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid read access to TIMA" - "@%"HWADDR_PRIx"\n", offset); + "@%"HWADDR_PRIx" size %d\n", offset, size); return -1; } ret = xto->read_handler(xptr, tctx, offset, size); From f16697292add6c3c15014a20fd5fce70b8c56734 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:12 +1000 Subject: [PATCH 0026/1794] ppc/xive2: Fix calculation of END queue sizes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The queue size of an Event Notification Descriptor (END) is determined by the 'cl' and QsZ fields of the END. If the cl field is 1, then the queue size (in bytes) will be the size of a cache line 128B * 2^QsZ and QsZ is limited to 4. Otherwise, it will be 4096B * 2^QsZ with QsZ limited to 12. Fixes: f8a233dedf2 ("ppc/xive2: Introduce a XIVE2 core framework") Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-4-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 25 +++++++++++++++++++------ include/hw/ppc/xive2_regs.h | 1 + 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index a08cf906d0e65..cb75ca8798534 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -188,12 +188,27 @@ void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, GString *buf) (uint32_t) xive_get_field64(EAS2_END_DATA, eas->w)); } +#define XIVE2_QSIZE_CHUNK_CL 128 +#define XIVE2_QSIZE_CHUNK_4k 4096 +/* Calculate max number of queue entries for an END */ +static uint32_t xive2_end_get_qentries(Xive2End *end) +{ + uint32_t w3 = end->w3; + uint32_t qsize = xive_get_field32(END2_W3_QSIZE, w3); + if (xive_get_field32(END2_W3_CL, w3)) { + g_assert(qsize <= 4); + return (XIVE2_QSIZE_CHUNK_CL << qsize) / sizeof(uint32_t); + } else { + g_assert(qsize <= 12); + return (XIVE2_QSIZE_CHUNK_4k << qsize) / sizeof(uint32_t); + } +} + void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, GString *buf) { uint64_t qaddr_base = xive2_end_qaddr(end); - uint32_t qsize = xive_get_field32(END2_W3_QSIZE, end->w3); uint32_t qindex = xive_get_field32(END2_W1_PAGE_OFF, end->w1); - uint32_t qentries = 1 << (qsize + 10); + uint32_t qentries = xive2_end_get_qentries(end); int i; /* @@ -223,8 +238,7 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, GString *buf) uint64_t qaddr_base = xive2_end_qaddr(end); uint32_t qindex = xive_get_field32(END2_W1_PAGE_OFF, end->w1); uint32_t qgen = xive_get_field32(END2_W1_GENERATION, end->w1); - uint32_t qsize = xive_get_field32(END2_W3_QSIZE, end->w3); - uint32_t qentries = 1 << (qsize + 10); + uint32_t qentries = xive2_end_get_qentries(end); uint32_t nvx_blk = xive_get_field32(END2_W6_VP_BLOCK, end->w6); uint32_t nvx_idx = xive_get_field32(END2_W6_VP_OFFSET, end->w6); @@ -341,13 +355,12 @@ void xive2_nvgc_pic_print_info(Xive2Nvgc *nvgc, uint32_t nvgc_idx, GString *buf) static void xive2_end_enqueue(Xive2End *end, uint32_t data) { uint64_t qaddr_base = xive2_end_qaddr(end); - uint32_t qsize = xive_get_field32(END2_W3_QSIZE, end->w3); uint32_t qindex = xive_get_field32(END2_W1_PAGE_OFF, end->w1); uint32_t qgen = xive_get_field32(END2_W1_GENERATION, end->w1); uint64_t qaddr = qaddr_base + (qindex << 2); uint32_t qdata = cpu_to_be32((qgen << 31) | (data & 0x7fffffff)); - uint32_t qentries = 1 << (qsize + 10); + uint32_t qentries = xive2_end_get_qentries(end); if (dma_memory_write(&address_space_memory, qaddr, &qdata, sizeof(qdata), MEMTXATTRS_UNSPECIFIED)) { diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index b11395c563504..3c28de8a304da 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -87,6 +87,7 @@ typedef struct Xive2End { #define END2_W2_EQ_ADDR_HI PPC_BITMASK32(8, 31) uint32_t w3; #define END2_W3_EQ_ADDR_LO PPC_BITMASK32(0, 24) +#define END2_W3_CL PPC_BIT32(27) #define END2_W3_QSIZE PPC_BITMASK32(28, 31) uint32_t w4; #define END2_W4_END_BLOCK PPC_BITMASK32(4, 7) From e8cf73b849879cd93b1d1b5fd3bde79fb42064dc Mon Sep 17 00:00:00 2001 From: Michael Kowal Date: Mon, 12 May 2025 13:10:13 +1000 Subject: [PATCH 0027/1794] ppc/xive2: Remote VSDs need to match on forwarding address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a multi chip environment there will be remote/forwarded VSDs. The check to find a matching INT controller (XIVE) of the remote block number was checking the INTs chip number. Block numbers are not tied to a chip number. The matching remote INT is the one that matches the forwarded VSD address with VSD types associated MMIO BAR. Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-5-npiggin@gmail.com [ clg: Fixed log format in pnv_xive2_get_remote() ] Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive2.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index ec8b0c68f1a49..6b724fe762f61 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -101,12 +101,10 @@ static uint32_t pnv_xive2_block_id(PnvXive2 *xive) } /* - * Remote access to controllers. HW uses MMIOs. For now, a simple scan - * of the chips is good enough. - * - * TODO: Block scope support + * Remote access to INT controllers. HW uses MMIOs(?). For now, a simple + * scan of all the chips INT controller is good enough. */ -static PnvXive2 *pnv_xive2_get_remote(uint8_t blk) +static PnvXive2 *pnv_xive2_get_remote(uint32_t vsd_type, hwaddr fwd_addr) { PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); int i; @@ -115,10 +113,23 @@ static PnvXive2 *pnv_xive2_get_remote(uint8_t blk) Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]); PnvXive2 *xive = &chip10->xive; - if (pnv_xive2_block_id(xive) == blk) { + /* + * Is this the XIVE matching the forwarded VSD address is for this + * VSD type + */ + if ((vsd_type == VST_ESB && fwd_addr == xive->esb_base) || + (vsd_type == VST_END && fwd_addr == xive->end_base) || + ((vsd_type == VST_NVP || + vsd_type == VST_NVG) && fwd_addr == xive->nvpg_base) || + (vsd_type == VST_NVC && fwd_addr == xive->nvc_base)) { return xive; } } + + qemu_log_mask(LOG_GUEST_ERROR, + "XIVE: >>>>> %s vsd_type %u fwd_addr 0x%"HWADDR_PRIx + " NOT FOUND\n", + __func__, vsd_type, fwd_addr); return NULL; } @@ -251,8 +262,7 @@ static uint64_t pnv_xive2_vst_addr(PnvXive2 *xive, uint32_t type, uint8_t blk, /* Remote VST access */ if (GETFIELD(VSD_MODE, vsd) == VSD_MODE_FORWARD) { - xive = pnv_xive2_get_remote(blk); - + xive = pnv_xive2_get_remote(type, (vsd & VSD_ADDRESS_MASK)); return xive ? pnv_xive2_vst_addr(xive, type, blk, idx) : 0; } From d1023a296c8297454fc4b207d58707c0a5e62e0a Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:14 +1000 Subject: [PATCH 0028/1794] ppc/xive2: fix context push calculation of IPB priority MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pushing a context and loading IPB from NVP is defined to merge ('or') that IPB into the TIMA IPB register. PIPR should therefore be calculated based on the final IPB value, not just the NVP value. Fixes: 9d2b6058c5b ("ppc/xive2: Add grouping level to notification") Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-6-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index cb75ca8798534..01cf96a2af65d 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -835,8 +835,9 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, 0); xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 2); } + /* IPB bits in the backlog are merged with the TIMA IPB bits */ regs[TM_IPB] |= ipb; - backlog_prio = xive_ipb_to_pipr(ipb); + backlog_prio = xive_ipb_to_pipr(regs[TM_IPB]); backlog_level = 0; first_group = xive_get_field32(NVP2_W0_PGOFIRST, nvp.w0); From bde8c148bb22b99cb84cda800fa555851b8cb358 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:15 +1000 Subject: [PATCH 0029/1794] ppc/xive: Fix PHYS NSR ring matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test that the NSR exception bit field is equal to the pool ring value, rather than any common bits set, which is more correct (although there is no practical bug because the LSI NSR type is not implemented and POOL/PHYS NSR are encoded with exclusive bits). Fixes: 4c3ccac636 ("pnv/xive: Add special handling for pool targets") Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-7-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 120376fb6b6df..bc829bebe9d06 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -54,7 +54,8 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) uint8_t *alt_regs; /* POOL interrupt uses IPB in QW2, POOL ring */ - if ((ring == TM_QW3_HV_PHYS) && (nsr & (TM_QW3_NSR_HE_POOL << 6))) { + if ((ring == TM_QW3_HV_PHYS) && + ((nsr & TM_QW3_NSR_HE) == (TM_QW3_NSR_HE_POOL << 6))) { alt_ring = TM_QW2_HV_POOL; } else { alt_ring = ring; From 576830428eea6ebfc85792851a343214b834e401 Mon Sep 17 00:00:00 2001 From: Michael Kowal Date: Mon, 12 May 2025 13:10:16 +1000 Subject: [PATCH 0030/1794] ppc/xive2: Reset Generation Flipped bit on END Cache Watch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the END Event Queue wraps the END EQ Generation bit is flipped and the Generation Flipped bit is set to one. On a END cache Watch read operation, the Generation Flipped bit needs to be reset. While debugging an error modified END not valid error messages to include the method since all were the same. Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-8-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive2.c | 3 ++- hw/intc/xive2.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 6b724fe762f61..ec247ce48ff73 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -1325,10 +1325,11 @@ static uint64_t pnv_xive2_ic_vc_read(void *opaque, hwaddr offset, case VC_ENDC_WATCH3_DATA0: /* * Load DATA registers from cache with data requested by the - * SPEC register + * SPEC register. Clear gen_flipped bit in word 1. */ watch_engine = (offset - VC_ENDC_WATCH0_DATA0) >> 6; pnv_xive2_end_cache_load(xive, watch_engine); + xive->vc_regs[reg] &= ~(uint64_t)END2_W1_GEN_FLIPPED; val = xive->vc_regs[reg]; break; diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 01cf96a2af65d..edf5d9eb94cb7 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -374,8 +374,8 @@ static void xive2_end_enqueue(Xive2End *end, uint32_t data) qgen ^= 1; end->w1 = xive_set_field32(END2_W1_GENERATION, end->w1, qgen); - /* TODO(PowerNV): reset GF bit on a cache watch operation */ - end->w1 = xive_set_field32(END2_W1_GEN_FLIPPED, end->w1, qgen); + /* Set gen flipped to 1, it gets reset on a cache watch operation */ + end->w1 = xive_set_field32(END2_W1_GEN_FLIPPED, end->w1, 1); } end->w1 = xive_set_field32(END2_W1_PAGE_OFF, end->w1, qindex); } From a1577527e212efd27a8ceefbd95321c306abf739 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:17 +1000 Subject: [PATCH 0031/1794] ppc/xive2: Use fair irq target search algorithm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current xive algorithm for finding a matching group vCPU target always uses the first vCPU found. And, since it always starts the search with thread 0 of a core, thread 0 is almost always used to handle group interrupts. This can lead to additional interrupt latency and poor performance for interrupt intensive work loads. Changing this to use a simple round-robin algorithm for deciding which thread number to use when starting a search, which leads to a more distributed use of threads for handling group interrupts. [npiggin: Also round-robin among threads, not just cores] Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-9-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive2.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index ec247ce48ff73..25dc8a372d2f3 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -643,13 +643,18 @@ static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, int i, j; bool gen1_tima_os = xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS; + static int next_start_core; + static int next_start_thread; + int start_core = next_start_core; + int start_thread = next_start_thread; for (i = 0; i < chip->nr_cores; i++) { - PnvCore *pc = chip->cores[i]; + PnvCore *pc = chip->cores[(i + start_core) % chip->nr_cores]; CPUCore *cc = CPU_CORE(pc); for (j = 0; j < cc->nr_threads; j++) { - PowerPCCPU *cpu = pc->threads[j]; + /* Start search for match with different thread each call */ + PowerPCCPU *cpu = pc->threads[(j + start_thread) % cc->nr_threads]; XiveTCTX *tctx; int ring; @@ -694,6 +699,15 @@ static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, if (!match->tctx) { match->ring = ring; match->tctx = tctx; + + next_start_thread = j + start_thread + 1; + if (next_start_thread >= cc->nr_threads) { + next_start_thread = 0; + next_start_core = i + start_core + 1; + if (next_start_core >= chip->nr_cores) { + next_start_core = 0; + } + } } count++; } From 8d373176181fbc11f8d8eae2b4532b867f083ea6 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:18 +1000 Subject: [PATCH 0032/1794] ppc/xive2: Fix irq preempted by lower priority group irq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A problem was seen where uart interrupts would be lost resulting in the console hanging. Traces showed that a lower priority interrupt was preempting a higher priority interrupt, which would result in the higher priority interrupt never being handled. The new interrupt's priority was being compared against the CPPR (Current Processor Priority Register) instead of the PIPR (Post Interrupt Priority Register), as was required by the XIVE spec. This allowed for a window between raising an interrupt and ACK'ing the interrupt where a lower priority interrupt could slip in. Fixes: 26c55b99418 ("ppc/xive2: Process group backlog when updating the CPPR") Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-10-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index edf5d9eb94cb7..36e842f041e5a 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1283,7 +1283,7 @@ bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority) * priority to know if the thread can take the interrupt now or if * it is precluded. */ - if (priority < alt_regs[TM_CPPR]) { + if (priority < alt_regs[TM_PIPR]) { return false; } return true; From d4720a7faf4bb415f3fe7f10e5c888212b81316a Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:19 +1000 Subject: [PATCH 0033/1794] ppc/xive2: Fix treatment of PIPR in CPPR update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the XIVE spec, updating the CPPR should also update the PIPR. The final value of the PIPR depends on other factors, but it should never be set to a value that is above the CPPR. Also added support for redistributing an active group interrupt when it is precluded as a result of changing the CPPR value. Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-11-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 36e842f041e5a..c23933f8f5505 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -995,7 +995,9 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) } } } - regs[TM_PIPR] = pipr_min; + + /* PIPR should not be set to a value greater than CPPR */ + regs[TM_PIPR] = (pipr_min > cppr) ? cppr : pipr_min; rc = xive2_tctx_get_nvp_indexes(tctx, ring_min, &nvp_blk, &nvp_idx); if (rc) { From 3abbec04e627396c32f2b7b7461961fb68c5c122 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:20 +1000 Subject: [PATCH 0034/1794] ppc/xive2: Do not present group interrupt on OS-push if precluded by CPPR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Group interrupts should not be taken from the backlog and presented if they are precluded by CPPR. Fixes: 855434b3b8 ("ppc/xive2: Process group backlog when pushing an OS context") Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-12-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index c23933f8f5505..181d1ae5f9405 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -845,7 +845,9 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, group_prio = xive2_presenter_backlog_scan(xptr, nvp_blk, nvp_idx, first_group, &group_level); regs[TM_LSMFB] = group_prio; - if (regs[TM_LGS] && group_prio < backlog_prio) { + if (regs[TM_LGS] && group_prio < backlog_prio && + group_prio < regs[TM_CPPR]) { + /* VP can take a group interrupt */ xive2_presenter_backlog_decr(xptr, nvp_blk, nvp_idx, group_prio, group_level); From ad9175f8a2d7b4ef7d63e9663a42e7f7a44bc3f5 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:21 +1000 Subject: [PATCH 0035/1794] ppc/xive2: Set CPPR delivery should account for group priority MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The group interrupt delivery flow selects the group backlog scan if LSMFB < IPB, but that scan may find an interrupt with a priority >= IPB. In that case, the VP-direct interrupt should be chosen. This extends to selecting the lowest prio between POOL and PHYS rings. Implement this just by re-starting the selection logic if the backlog irq was not found or priority did not match LSMFB (LSMFB is updated so next time around it would see the right value and not loop infinitely). Signed-off-by: Nicholas Piggin Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-13-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 181d1ae5f9405..cca121b5f13c6 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -939,7 +939,7 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) { uint8_t *regs = &tctx->regs[ring]; Xive2Router *xrtr = XIVE2_ROUTER(tctx->xptr); - uint8_t old_cppr, backlog_prio, first_group, group_level = 0; + uint8_t old_cppr, backlog_prio, first_group, group_level; uint8_t pipr_min, lsmfb_min, ring_min; bool group_enabled; uint32_t nvp_blk, nvp_idx; @@ -961,10 +961,12 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) * Recompute the PIPR based on local pending interrupts. It will * be adjusted below if needed in case of pending group interrupts. */ +again: pipr_min = xive_ipb_to_pipr(regs[TM_IPB]); group_enabled = !!regs[TM_LGS]; - lsmfb_min = (group_enabled) ? regs[TM_LSMFB] : 0xff; + lsmfb_min = group_enabled ? regs[TM_LSMFB] : 0xff; ring_min = ring; + group_level = 0; /* PHYS updates also depend on POOL values */ if (ring == TM_QW3_HV_PHYS) { @@ -998,9 +1000,6 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) } } - /* PIPR should not be set to a value greater than CPPR */ - regs[TM_PIPR] = (pipr_min > cppr) ? cppr : pipr_min; - rc = xive2_tctx_get_nvp_indexes(tctx, ring_min, &nvp_blk, &nvp_idx); if (rc) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: set CPPR on invalid context\n"); @@ -1019,7 +1018,7 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) if (group_enabled && lsmfb_min < cppr && - lsmfb_min < regs[TM_PIPR]) { + lsmfb_min < pipr_min) { /* * Thread has seen a group interrupt with a higher priority * than the new cppr or pending local interrupt. Check the @@ -1048,12 +1047,25 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) nvp_blk, nvp_idx, first_group, &group_level); tctx->regs[ring_min + TM_LSMFB] = backlog_prio; - if (backlog_prio != 0xFF) { - xive2_presenter_backlog_decr(tctx->xptr, nvp_blk, nvp_idx, - backlog_prio, group_level); - regs[TM_PIPR] = backlog_prio; + if (backlog_prio != lsmfb_min) { + /* + * If the group backlog scan finds a less favored or no interrupt, + * then re-do the processing which may turn up a more favored + * interrupt from IPB or the other pool. Backlog should not + * find a priority < LSMFB. + */ + g_assert(backlog_prio >= lsmfb_min); + goto again; } + + xive2_presenter_backlog_decr(tctx->xptr, nvp_blk, nvp_idx, + backlog_prio, group_level); + pipr_min = backlog_prio; } + + /* PIPR should not be set to a value greater than CPPR */ + regs[TM_PIPR] = (pipr_min > cppr) ? cppr : pipr_min; + /* CPPR has changed, check if we need to raise a pending exception */ xive_tctx_notify(tctx, ring_min, group_level); } From 14bcc5239f4d4780ec52881779161c62c46e7243 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:22 +1000 Subject: [PATCH 0036/1794] ppc/xive: tctx_notify should clear the precluded interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If CPPR is lowered to preclude the pending interrupt, NSR should be cleared and the qemu_irq should be lowered. This avoids some cases of supurious interrupts. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-14-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index bc829bebe9d06..a0a60a24f510a 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -110,6 +110,9 @@ void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level) regs[TM_IPB], alt_regs[TM_PIPR], alt_regs[TM_CPPR], alt_regs[TM_NSR]); qemu_irq_raise(xive_tctx_output(tctx, ring)); + } else { + alt_regs[TM_NSR] = 0; + qemu_irq_lower(xive_tctx_output(tctx, ring)); } } From 9d466ab9b6f27a5d5b7a0ec5a7ad6f60e82fafda Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:23 +1000 Subject: [PATCH 0037/1794] ppc/xive: Explicitly zero NSR after accepting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have xive_tctx_accept clear NSR in one shot rather than masking out bits as they are tested, which makes it clear it's reset to 0, and does not have a partial NSR value in the register. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-15-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index a0a60a24f510a..b35d2ec1793e7 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -68,13 +68,11 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) * If the interrupt was for a specific VP, reset the pending * buffer bit, otherwise clear the logical server indicator */ - if (regs[TM_NSR] & TM_NSR_GRP_LVL) { - regs[TM_NSR] &= ~TM_NSR_GRP_LVL; - } else { + if (!(regs[TM_NSR] & TM_NSR_GRP_LVL)) { alt_regs[TM_IPB] &= ~xive_priority_to_ipb(cppr); } - /* Drop the exception bit and any group/crowd */ + /* Clear the exception from NSR */ regs[TM_NSR] = 0; trace_xive_tctx_accept(tctx->cs->cpu_index, alt_ring, From 261626dce11311ba4e866272c9a2c0990c53d85c Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:24 +1000 Subject: [PATCH 0038/1794] ppc/xive: Move NSR decoding into helper functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than functions to return masks to test NSR bits, have functions to test those bits directly. This should be no functional change, it just makes the code more readable. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-16-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 51 +++++++++++++++++++++++++++++++++++-------- include/hw/ppc/xive.h | 4 ++++ 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index b35d2ec1793e7..8537cad27b2a3 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -25,6 +25,45 @@ /* * XIVE Thread Interrupt Management context */ +bool xive_nsr_indicates_exception(uint8_t ring, uint8_t nsr) +{ + switch (ring) { + case TM_QW1_OS: + return !!(nsr & TM_QW1_NSR_EO); + case TM_QW2_HV_POOL: + case TM_QW3_HV_PHYS: + return !!(nsr & TM_QW3_NSR_HE); + default: + g_assert_not_reached(); + } +} + +bool xive_nsr_indicates_group_exception(uint8_t ring, uint8_t nsr) +{ + if ((nsr & TM_NSR_GRP_LVL) > 0) { + g_assert(xive_nsr_indicates_exception(ring, nsr)); + return true; + } + return false; +} + +uint8_t xive_nsr_exception_ring(uint8_t ring, uint8_t nsr) +{ + /* NSR determines if pool/phys ring is for phys or pool interrupt */ + if ((ring == TM_QW3_HV_PHYS) || (ring == TM_QW2_HV_POOL)) { + uint8_t he = (nsr & TM_QW3_NSR_HE) >> 6; + + if (he == TM_QW3_NSR_HE_PHYS) { + return TM_QW3_HV_PHYS; + } else if (he == TM_QW3_NSR_HE_POOL) { + return TM_QW2_HV_POOL; + } else { + /* Don't support LSI mode */ + g_assert_not_reached(); + } + } + return ring; +} static qemu_irq xive_tctx_output(XiveTCTX *tctx, uint8_t ring) { @@ -48,18 +87,12 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) qemu_irq_lower(xive_tctx_output(tctx, ring)); - if (regs[TM_NSR] != 0) { + if (xive_nsr_indicates_exception(ring, nsr)) { uint8_t cppr = regs[TM_PIPR]; uint8_t alt_ring; uint8_t *alt_regs; - /* POOL interrupt uses IPB in QW2, POOL ring */ - if ((ring == TM_QW3_HV_PHYS) && - ((nsr & TM_QW3_NSR_HE) == (TM_QW3_NSR_HE_POOL << 6))) { - alt_ring = TM_QW2_HV_POOL; - } else { - alt_ring = ring; - } + alt_ring = xive_nsr_exception_ring(ring, nsr); alt_regs = &tctx->regs[alt_ring]; regs[TM_CPPR] = cppr; @@ -68,7 +101,7 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) * If the interrupt was for a specific VP, reset the pending * buffer bit, otherwise clear the logical server indicator */ - if (!(regs[TM_NSR] & TM_NSR_GRP_LVL)) { + if (!xive_nsr_indicates_group_exception(ring, nsr)) { alt_regs[TM_IPB] &= ~xive_priority_to_ipb(cppr); } diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 538f438681727..28f0f1b79ad77 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -365,6 +365,10 @@ static inline uint32_t xive_tctx_word2(uint8_t *ring) return *((uint32_t *) &ring[TM_WORD2]); } +bool xive_nsr_indicates_exception(uint8_t ring, uint8_t nsr); +bool xive_nsr_indicates_group_exception(uint8_t ring, uint8_t nsr); +uint8_t xive_nsr_exception_ring(uint8_t ring, uint8_t nsr); + /* * XIVE Router */ From 279031bc03e6f59d58f4de37b06bddaa03e4f209 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:25 +1000 Subject: [PATCH 0039/1794] ppc/xive: Fix pulling pool and phys contexts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves the implementation of pulling pool and phys contexts in XIVE1, by following closer the OS pulling code. In particular, the old ring data is returned rather than the modified, and irq signals are reset on pull. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-17-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 66 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 8537cad27b2a3..5483a815ef077 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -241,25 +241,75 @@ static uint64_t xive_tm_ack_hv_reg(XivePresenter *xptr, XiveTCTX *tctx, return xive_tctx_accept(tctx, TM_QW3_HV_PHYS); } +static void xive_pool_cam_decode(uint32_t cam, uint8_t *nvt_blk, + uint32_t *nvt_idx, bool *vp) +{ + if (nvt_blk) { + *nvt_blk = xive_nvt_blk(cam); + } + if (nvt_idx) { + *nvt_idx = xive_nvt_idx(cam); + } + if (vp) { + *vp = !!(cam & TM_QW2W2_VP); + } +} + +static uint32_t xive_tctx_get_pool_cam(XiveTCTX *tctx, uint8_t *nvt_blk, + uint32_t *nvt_idx, bool *vp) +{ + uint32_t qw2w2 = xive_tctx_word2(&tctx->regs[TM_QW2_HV_POOL]); + uint32_t cam = be32_to_cpu(qw2w2); + + xive_pool_cam_decode(cam, nvt_blk, nvt_idx, vp); + return qw2w2; +} + +static void xive_tctx_set_pool_cam(XiveTCTX *tctx, uint32_t qw2w2) +{ + memcpy(&tctx->regs[TM_QW2_HV_POOL + TM_WORD2], &qw2w2, 4); +} + static uint64_t xive_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size) { - uint32_t qw2w2_prev = xive_tctx_word2(&tctx->regs[TM_QW2_HV_POOL]); uint32_t qw2w2; + uint32_t qw2w2_new; + uint8_t nvt_blk; + uint32_t nvt_idx; + bool vp; - qw2w2 = xive_set_field32(TM_QW2W2_VP, qw2w2_prev, 0); - memcpy(&tctx->regs[TM_QW2_HV_POOL + TM_WORD2], &qw2w2, 4); + qw2w2 = xive_tctx_get_pool_cam(tctx, &nvt_blk, &nvt_idx, &vp); + + if (!vp) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pull invalid POOL NVT %x/%x !?\n", + nvt_blk, nvt_idx); + } + + /* Invalidate CAM line */ + qw2w2_new = xive_set_field32(TM_QW2W2_VP, qw2w2, 0); + xive_tctx_set_pool_cam(tctx, qw2w2_new); + + xive_tctx_reset_signal(tctx, TM_QW1_OS); + xive_tctx_reset_signal(tctx, TM_QW2_HV_POOL); return qw2w2; } static uint64_t xive_tm_pull_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size) { - uint8_t qw3b8_prev = tctx->regs[TM_QW3_HV_PHYS + TM_WORD2]; - uint8_t qw3b8; + uint8_t qw3b8 = tctx->regs[TM_QW3_HV_PHYS + TM_WORD2]; + uint8_t qw3b8_new; + + qw3b8 = tctx->regs[TM_QW3_HV_PHYS + TM_WORD2]; + if (!(qw3b8 & TM_QW3B8_VT)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pulling invalid PHYS thread!?\n"); + } + qw3b8_new = qw3b8 & ~TM_QW3B8_VT; + tctx->regs[TM_QW3_HV_PHYS + TM_WORD2] = qw3b8_new; - qw3b8 = qw3b8_prev & ~TM_QW3B8_VT; - tctx->regs[TM_QW3_HV_PHYS + TM_WORD2] = qw3b8; + xive_tctx_reset_signal(tctx, TM_QW1_OS); + xive_tctx_reset_signal(tctx, TM_QW3_HV_PHYS); return qw3b8; } @@ -489,7 +539,7 @@ static uint64_t xive_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, qw1w2 = xive_tctx_get_os_cam(tctx, &nvt_blk, &nvt_idx, &vo); if (!vo) { - qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pulling invalid NVT %x/%x !?\n", + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pull invalid OS NVT %x/%x !?\n", nvt_blk, nvt_idx); } From c2cee7477f4af8cdfc33c5bb5928a3a1862655ee Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:26 +1000 Subject: [PATCH 0040/1794] pnv/xive2: Support ESB Escalation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for XIVE ESB Interrupt Escalation. Suggested-by: Michael Kowal [This change was taken from a patch provided by Michael Kowal.] Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-18-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 62 ++++++++++++++++++++++++++++++------- include/hw/ppc/xive2.h | 1 + include/hw/ppc/xive2_regs.h | 13 +++++--- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index cca121b5f13c6..541b05225cd21 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1552,18 +1552,39 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, } } - /* - * The END trigger becomes an Escalation trigger - */ - xive2_router_end_notify(xrtr, - xive_get_field32(END2_W4_END_BLOCK, end.w4), - xive_get_field32(END2_W4_ESC_END_INDEX, end.w4), - xive_get_field32(END2_W5_ESC_END_DATA, end.w5)); + if (xive2_end_is_escalate_end(&end)) { + /* + * Perform END Adaptive escalation processing + * The END trigger becomes an Escalation trigger + */ + xive2_router_end_notify(xrtr, + xive_get_field32(END2_W4_END_BLOCK, end.w4), + xive_get_field32(END2_W4_ESC_END_INDEX, end.w4), + xive_get_field32(END2_W5_ESC_END_DATA, end.w5)); + } /* end END adaptive escalation */ + + else { + uint32_t lisn; /* Logical Interrupt Source Number */ + + /* + * Perform ESB escalation processing + * E[N] == 1 --> N + * Req[Block] <- E[ESB_Block] + * Req[Index] <- E[ESB_Index] + * Req[Offset] <- 0x000 + * Execute Req command + */ + lisn = XIVE_EAS(xive_get_field32(END2_W4_END_BLOCK, end.w4), + xive_get_field32(END2_W4_ESC_END_INDEX, end.w4)); + + xive2_notify(xrtr, lisn, true /* pq_checked */); + } + + return; } -void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked) +void xive2_notify(Xive2Router *xrtr , uint32_t lisn, bool pq_checked) { - Xive2Router *xrtr = XIVE2_ROUTER(xn); uint8_t eas_blk = XIVE_EAS_BLOCK(lisn); uint32_t eas_idx = XIVE_EAS_INDEX(lisn); Xive2Eas eas; @@ -1606,13 +1627,30 @@ void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked) return; } + /* TODO: add support for EAS resume */ + if (xive2_eas_is_resume(&eas)) { + qemu_log_mask(LOG_UNIMP, + "XIVE: EAS resume processing unimplemented - LISN %x\n", + lisn); + return; + } + /* * The event trigger becomes an END trigger */ xive2_router_end_notify(xrtr, - xive_get_field64(EAS2_END_BLOCK, eas.w), - xive_get_field64(EAS2_END_INDEX, eas.w), - xive_get_field64(EAS2_END_DATA, eas.w)); + xive_get_field64(EAS2_END_BLOCK, eas.w), + xive_get_field64(EAS2_END_INDEX, eas.w), + xive_get_field64(EAS2_END_DATA, eas.w)); + return; +} + +void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xn); + + xive2_notify(xrtr, lisn, pq_checked); + return; } static const Property xive2_router_properties[] = { diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index 8cdf8191742e9..2436ddb5e53cf 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -80,6 +80,7 @@ int xive2_router_write_nvgc(Xive2Router *xrtr, bool crowd, uint32_t xive2_router_get_config(Xive2Router *xrtr); void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked); +void xive2_notify(Xive2Router *xrtr, uint32_t lisn, bool pq_checked); /* * XIVE2 Presenter (POWER10) diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index 3c28de8a304da..2c535ec0d0f1b 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -39,15 +39,18 @@ typedef struct Xive2Eas { uint64_t w; -#define EAS2_VALID PPC_BIT(0) -#define EAS2_END_BLOCK PPC_BITMASK(4, 7) /* Destination EQ block# */ -#define EAS2_END_INDEX PPC_BITMASK(8, 31) /* Destination EQ index */ -#define EAS2_MASKED PPC_BIT(32) /* Masked */ -#define EAS2_END_DATA PPC_BITMASK(33, 63) /* written to the EQ */ +#define EAS2_VALID PPC_BIT(0) +#define EAS2_QOS PPC_BIT(1, 2) /* Quality of Service(unimp) */ +#define EAS2_RESUME PPC_BIT(3) /* END Resume(unimp) */ +#define EAS2_END_BLOCK PPC_BITMASK(4, 7) /* Destination EQ block# */ +#define EAS2_END_INDEX PPC_BITMASK(8, 31) /* Destination EQ index */ +#define EAS2_MASKED PPC_BIT(32) /* Masked */ +#define EAS2_END_DATA PPC_BITMASK(33, 63) /* written to the EQ */ } Xive2Eas; #define xive2_eas_is_valid(eas) (be64_to_cpu((eas)->w) & EAS2_VALID) #define xive2_eas_is_masked(eas) (be64_to_cpu((eas)->w) & EAS2_MASKED) +#define xive2_eas_is_resume(eas) (be64_to_cpu((eas)->w) & EAS2_RESUME) void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, GString *buf); From 14cbb7bf1245c2d9166be0309c9407845783b281 Mon Sep 17 00:00:00 2001 From: Michael Kowal Date: Mon, 12 May 2025 13:10:27 +1000 Subject: [PATCH 0041/1794] pnv/xive2: Print value in invalid register write logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This can make it easier to see what the target system is trying to do. [npiggin: split from larger patch] Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-19-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive2.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 25dc8a372d2f3..9d53537e3e093 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -1197,7 +1197,8 @@ static void pnv_xive2_ic_cq_write(void *opaque, hwaddr offset, case CQ_FIRMASK_OR: /* FIR error reporting */ break; default: - xive2_error(xive, "CQ: invalid write 0x%"HWADDR_PRIx, offset); + xive2_error(xive, "CQ: invalid write 0x%"HWADDR_PRIx" value 0x%"PRIx64, + offset, val); return; } @@ -1495,7 +1496,8 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, break; default: - xive2_error(xive, "VC: invalid write @%"HWADDR_PRIx, offset); + xive2_error(xive, "VC: invalid write @0x%"HWADDR_PRIx" value 0x%"PRIx64, + offset, val); return; } @@ -1703,7 +1705,8 @@ static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset, break; default: - xive2_error(xive, "PC: invalid write @%"HWADDR_PRIx, offset); + xive2_error(xive, "PC: invalid write @0x%"HWADDR_PRIx" value 0x%"PRIx64, + offset, val); return; } @@ -1790,7 +1793,8 @@ static void pnv_xive2_ic_tctxt_write(void *opaque, hwaddr offset, xive->tctxt_regs[reg] = val; break; default: - xive2_error(xive, "TCTXT: invalid write @%"HWADDR_PRIx, offset); + xive2_error(xive, "TCTXT: invalid write @0x%"HWADDR_PRIx + " data 0x%"PRIx64, offset, val); return; } } @@ -1861,7 +1865,8 @@ static void pnv_xive2_xscom_write(void *opaque, hwaddr offset, pnv_xive2_ic_tctxt_write(opaque, mmio_offset, val, size); break; default: - xive2_error(xive, "XSCOM: invalid write @%"HWADDR_PRIx, offset); + xive2_error(xive, "XSCOM: invalid write @%"HWADDR_PRIx + " value 0x%"PRIx64, offset, val); } } @@ -1929,7 +1934,8 @@ static void pnv_xive2_ic_notify_write(void *opaque, hwaddr offset, break; default: - xive2_error(xive, "NOTIFY: invalid write @%"HWADDR_PRIx, offset); + xive2_error(xive, "NOTIFY: invalid write @%"HWADDR_PRIx + " value 0x%"PRIx64, offset, val); } } @@ -1971,7 +1977,8 @@ static void pnv_xive2_ic_lsi_write(void *opaque, hwaddr offset, { PnvXive2 *xive = PNV_XIVE2(opaque); - xive2_error(xive, "LSI: invalid write @%"HWADDR_PRIx, offset); + xive2_error(xive, "LSI: invalid write @%"HWADDR_PRIx" value 0x%"PRIx64, + offset, val); } static const MemoryRegionOps pnv_xive2_ic_lsi_ops = { @@ -2074,7 +2081,8 @@ static void pnv_xive2_ic_sync_write(void *opaque, hwaddr offset, inject_type = PNV_XIVE2_QUEUE_NXC_ST_RMT_CI; break; default: - xive2_error(xive, "SYNC: invalid write @%"HWADDR_PRIx, offset); + xive2_error(xive, "SYNC: invalid write @%"HWADDR_PRIx" value 0x%"PRIx64, + offset, val); return; } From d273abbfba47ef47e331dc22731c7d2a15e95c9a Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:28 +1000 Subject: [PATCH 0042/1794] pnv/xive2: VC_ENDC_WATCH_SPEC regs should read back WATCH_FULL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Firmware expects to read back the WATCH_FULL bit from the VC_ENDC_WATCH_SPEC register, so don't clear it on read. Don't bother clearing the reads-as-zero CONFLICT bit because it's masked at write already. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-20-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive2.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 9d53537e3e093..e15f414d0bb34 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -1329,7 +1329,6 @@ static uint64_t pnv_xive2_ic_vc_read(void *opaque, hwaddr offset, case VC_ENDC_WATCH2_SPEC: case VC_ENDC_WATCH3_SPEC: watch_engine = (offset - VC_ENDC_WATCH0_SPEC) >> 6; - xive->vc_regs[reg] &= ~(VC_ENDC_WATCH_FULL | VC_ENDC_WATCH_CONFLICT); pnv_xive2_endc_cache_watch_release(xive, watch_engine); val = xive->vc_regs[reg]; break; From 9898cc80306588eb6a1c44b7ba38f207ca8bdcfa Mon Sep 17 00:00:00 2001 From: Michael Kowal Date: Mon, 12 May 2025 13:10:29 +1000 Subject: [PATCH 0043/1794] pnv/xive2: Permit valid writes to VC/PC Flush Control registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Writes to the Flush Control registers were logged as invalid when they are allowed. Clearing the unsupported want_cache_disable feature is supported, so don't log an error in that case. Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-21-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive2.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index e15f414d0bb34..386175a68b292 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -1411,7 +1411,14 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, /* * ESB cache updates (not modeled) */ - /* case VC_ESBC_FLUSH_CTRL: */ + case VC_ESBC_FLUSH_CTRL: + if (val & VC_ESBC_FLUSH_CTRL_WANT_CACHE_DISABLE) { + xive2_error(xive, "VC: unsupported write @0x%"HWADDR_PRIx + " value 0x%"PRIx64" bit[2] poll_want_cache_disable", + offset, val); + return; + } + break; case VC_ESBC_FLUSH_POLL: xive->vc_regs[VC_ESBC_FLUSH_CTRL >> 3] |= VC_ESBC_FLUSH_CTRL_POLL_VALID; /* ESB update */ @@ -1427,7 +1434,14 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, /* * EAS cache updates (not modeled) */ - /* case VC_EASC_FLUSH_CTRL: */ + case VC_EASC_FLUSH_CTRL: + if (val & VC_EASC_FLUSH_CTRL_WANT_CACHE_DISABLE) { + xive2_error(xive, "VC: unsupported write @0x%"HWADDR_PRIx + " value 0x%"PRIx64" bit[2] poll_want_cache_disable", + offset, val); + return; + } + break; case VC_EASC_FLUSH_POLL: xive->vc_regs[VC_EASC_FLUSH_CTRL >> 3] |= VC_EASC_FLUSH_CTRL_POLL_VALID; /* EAS update */ @@ -1466,7 +1480,14 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, break; - /* case VC_ENDC_FLUSH_CTRL: */ + case VC_ENDC_FLUSH_CTRL: + if (val & VC_ENDC_FLUSH_CTRL_WANT_CACHE_DISABLE) { + xive2_error(xive, "VC: unsupported write @0x%"HWADDR_PRIx + " value 0x%"PRIx64" bit[2] poll_want_cache_disable", + offset, val); + return; + } + break; case VC_ENDC_FLUSH_POLL: xive->vc_regs[VC_ENDC_FLUSH_CTRL >> 3] |= VC_ENDC_FLUSH_CTRL_POLL_VALID; break; @@ -1687,7 +1708,14 @@ static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset, pnv_xive2_nxc_update(xive, watch_engine); break; - /* case PC_NXC_FLUSH_CTRL: */ + case PC_NXC_FLUSH_CTRL: + if (val & PC_NXC_FLUSH_CTRL_WANT_CACHE_DISABLE) { + xive2_error(xive, "VC: unsupported write @0x%"HWADDR_PRIx + " value 0x%"PRIx64" bit[2] poll_want_cache_disable", + offset, val); + return; + } + break; case PC_NXC_FLUSH_POLL: xive->pc_regs[PC_NXC_FLUSH_CTRL >> 3] |= PC_NXC_FLUSH_CTRL_POLL_VALID; break; From b22ffb42999504614a1bef3a52c5b2e6549e8de6 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:30 +1000 Subject: [PATCH 0044/1794] ppc/xive2: add interrupt priority configuration flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds support for extracting additional configuration flags from the XIVE configuration register that are needed for redistribution of group interrupts. Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-22-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive2.c | 16 ++++++++++++---- hw/intc/pnv_xive2_regs.h | 1 + include/hw/ppc/xive2.h | 8 +++++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 386175a68b292..7b4a33228e050 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -605,20 +605,28 @@ static uint32_t pnv_xive2_get_config(Xive2Router *xrtr) { PnvXive2 *xive = PNV_XIVE2(xrtr); uint32_t cfg = 0; + uint64_t reg = xive->cq_regs[CQ_XIVE_CFG >> 3]; - if (xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS) { + if (reg & CQ_XIVE_CFG_GEN1_TIMA_OS) { cfg |= XIVE2_GEN1_TIMA_OS; } - if (xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_EN_VP_SAVE_RESTORE) { + if (reg & CQ_XIVE_CFG_EN_VP_SAVE_RESTORE) { cfg |= XIVE2_VP_SAVE_RESTORE; } - if (GETFIELD(CQ_XIVE_CFG_HYP_HARD_RANGE, - xive->cq_regs[CQ_XIVE_CFG >> 3]) == CQ_XIVE_CFG_THREADID_8BITS) { + if (GETFIELD(CQ_XIVE_CFG_HYP_HARD_RANGE, reg) == + CQ_XIVE_CFG_THREADID_8BITS) { cfg |= XIVE2_THREADID_8BITS; } + if (reg & CQ_XIVE_CFG_EN_VP_GRP_PRIORITY) { + cfg |= XIVE2_EN_VP_GRP_PRIORITY; + } + + cfg = SETFIELD(XIVE2_VP_INT_PRIO, cfg, + GETFIELD(CQ_XIVE_CFG_VP_INT_PRIO, reg)); + return cfg; } diff --git a/hw/intc/pnv_xive2_regs.h b/hw/intc/pnv_xive2_regs.h index e8b87b3d2c135..d53300f709b04 100644 --- a/hw/intc/pnv_xive2_regs.h +++ b/hw/intc/pnv_xive2_regs.h @@ -66,6 +66,7 @@ #define CQ_XIVE_CFG_GEN1_TIMA_HYP_BLK0 PPC_BIT(26) /* 0 if bit[25]=0 */ #define CQ_XIVE_CFG_GEN1_TIMA_CROWD_DIS PPC_BIT(27) /* 0 if bit[25]=0 */ #define CQ_XIVE_CFG_GEN1_END_ESX PPC_BIT(28) +#define CQ_XIVE_CFG_EN_VP_GRP_PRIORITY PPC_BIT(32) /* 0 if bit[25]=1 */ #define CQ_XIVE_CFG_EN_VP_SAVE_RESTORE PPC_BIT(38) /* 0 if bit[25]=1 */ #define CQ_XIVE_CFG_EN_VP_SAVE_REST_STRICT PPC_BIT(39) /* 0 if bit[25]=1 */ diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index 2436ddb5e53cf..760b94a962e73 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -29,9 +29,11 @@ OBJECT_DECLARE_TYPE(Xive2Router, Xive2RouterClass, XIVE2_ROUTER); * Configuration flags */ -#define XIVE2_GEN1_TIMA_OS 0x00000001 -#define XIVE2_VP_SAVE_RESTORE 0x00000002 -#define XIVE2_THREADID_8BITS 0x00000004 +#define XIVE2_GEN1_TIMA_OS 0x00000001 +#define XIVE2_VP_SAVE_RESTORE 0x00000002 +#define XIVE2_THREADID_8BITS 0x00000004 +#define XIVE2_EN_VP_GRP_PRIORITY 0x00000008 +#define XIVE2_VP_INT_PRIO 0x00000030 typedef struct Xive2RouterClass { SysBusDeviceClass parent; From 555e446019f58e488ccf9fc416667be450e3f32f Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:31 +1000 Subject: [PATCH 0045/1794] ppc/xive2: Support redistribution of group interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an XIVE context is pulled while it has an active, unacknowledged group interrupt, XIVE will check to see if a context on another thread can handle the interrupt and, if so, notify that context. If there are no contexts that can handle the interrupt, then the interrupt is added to a backlog and XIVE will attempt to escalate the interrupt, if configured to do so, allowing the higher privileged handler to activate a context that can handle the original interrupt. Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-23-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 84 +++++++++++++++++++++++++++++++++++-- include/hw/ppc/xive2_regs.h | 3 ++ 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 541b05225cd21..9ef372b6d1d90 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -19,6 +19,10 @@ #include "hw/ppc/xive2_regs.h" #include "trace.h" +static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, + uint32_t end_idx, uint32_t end_data, + bool redistribute); + uint32_t xive2_router_get_config(Xive2Router *xrtr) { Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr); @@ -597,6 +601,68 @@ static uint32_t xive2_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx) return xive2_nvp_cam_line(blk, 1 << tid_shift | (pir & tid_mask)); } +static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, + uint8_t nvp_blk, uint32_t nvp_idx, uint8_t ring) +{ + uint8_t nsr = tctx->regs[ring + TM_NSR]; + uint8_t crowd = NVx_CROWD_LVL(nsr); + uint8_t group = NVx_GROUP_LVL(nsr); + uint8_t nvgc_blk; + uint8_t nvgc_idx; + uint8_t end_blk; + uint32_t end_idx; + uint8_t pipr = tctx->regs[ring + TM_PIPR]; + Xive2Nvgc nvgc; + uint8_t prio_limit; + uint32_t cfg; + + /* convert crowd/group to blk/idx */ + if (group > 0) { + nvgc_idx = (nvp_idx & (0xffffffff << group)) | + ((1 << (group - 1)) - 1); + } else { + nvgc_idx = nvp_idx; + } + + if (crowd > 0) { + crowd = (crowd == 3) ? 4 : crowd; + nvgc_blk = (nvp_blk & (0xffffffff << crowd)) | + ((1 << (crowd - 1)) - 1); + } else { + nvgc_blk = nvp_blk; + } + + /* Use blk/idx to retrieve the NVGC */ + if (xive2_router_get_nvgc(xrtr, crowd, nvgc_blk, nvgc_idx, &nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: no %s %x/%x\n", + crowd ? "NVC" : "NVG", nvgc_blk, nvgc_idx); + return; + } + + /* retrieve the END blk/idx from the NVGC */ + end_blk = xive_get_field32(NVGC2_W1_END_BLK, nvgc.w1); + end_idx = xive_get_field32(NVGC2_W1_END_IDX, nvgc.w1); + + /* determine number of priorities being used */ + cfg = xive2_router_get_config(xrtr); + if (cfg & XIVE2_EN_VP_GRP_PRIORITY) { + prio_limit = 1 << GETFIELD(NVGC2_W1_PSIZE, nvgc.w1); + } else { + prio_limit = 1 << GETFIELD(XIVE2_VP_INT_PRIO, cfg); + } + + /* add priority offset to end index */ + end_idx += pipr % prio_limit; + + /* trigger the group END */ + xive2_router_end_notify(xrtr, end_blk, end_idx, 0, true); + + /* clear interrupt indication for the context */ + tctx->regs[ring + TM_NSR] = 0; + tctx->regs[ring + TM_PIPR] = tctx->regs[ring + TM_CPPR]; + xive_tctx_reset_signal(tctx, ring); +} + static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size, uint8_t ring) { @@ -608,6 +674,7 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, uint8_t cur_ring; bool valid; bool do_save; + uint8_t nsr; xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &valid, &do_save); @@ -624,6 +691,12 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, memcpy(&tctx->regs[cur_ring + TM_WORD2], &ringw2_new, 4); } + /* Active group/crowd interrupts need to be redistributed */ + nsr = tctx->regs[ring + TM_NSR]; + if (xive_nsr_indicates_group_exception(ring, nsr)) { + xive2_redistribute(xrtr, tctx, nvp_blk, nvp_idx, ring); + } + if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) { xive2_tctx_save_ctx(xrtr, tctx, nvp_blk, nvp_idx, ring); } @@ -1352,7 +1425,8 @@ static bool xive2_router_end_es_notify(Xive2Router *xrtr, uint8_t end_blk, * message has the same parameters than in the function below. */ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, - uint32_t end_idx, uint32_t end_data) + uint32_t end_idx, uint32_t end_data, + bool redistribute) { Xive2End end; uint8_t priority; @@ -1380,7 +1454,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, return; } - if (xive2_end_is_enqueue(&end)) { + if (!redistribute && xive2_end_is_enqueue(&end)) { xive2_end_enqueue(&end, end_data); /* Enqueuing event data modifies the EQ toggle and index */ xive2_router_write_end(xrtr, end_blk, end_idx, &end, 1); @@ -1560,7 +1634,8 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, xive2_router_end_notify(xrtr, xive_get_field32(END2_W4_END_BLOCK, end.w4), xive_get_field32(END2_W4_ESC_END_INDEX, end.w4), - xive_get_field32(END2_W5_ESC_END_DATA, end.w5)); + xive_get_field32(END2_W5_ESC_END_DATA, end.w5), + false); } /* end END adaptive escalation */ else { @@ -1641,7 +1716,8 @@ void xive2_notify(Xive2Router *xrtr , uint32_t lisn, bool pq_checked) xive2_router_end_notify(xrtr, xive_get_field64(EAS2_END_BLOCK, eas.w), xive_get_field64(EAS2_END_INDEX, eas.w), - xive_get_field64(EAS2_END_DATA, eas.w)); + xive_get_field64(EAS2_END_DATA, eas.w), + false); return; } diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index 2c535ec0d0f1b..e22203814312d 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -224,6 +224,9 @@ typedef struct Xive2Nvgc { #define NVGC2_W0_VALID PPC_BIT32(0) #define NVGC2_W0_PGONEXT PPC_BITMASK32(26, 31) uint32_t w1; +#define NVGC2_W1_PSIZE PPC_BITMASK32(0, 1) +#define NVGC2_W1_END_BLK PPC_BITMASK32(4, 7) +#define NVGC2_W1_END_IDX PPC_BITMASK32(8, 31) uint32_t w2; uint32_t w3; uint32_t w4; From 701ab1857a9175a86e3ad6f18958df631af86a62 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:32 +1000 Subject: [PATCH 0046/1794] ppc/xive: Add more interrupt notification tracing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add more tracing around notification, redistribution, and escalation. Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-24-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/trace-events | 6 ++++++ hw/intc/xive.c | 3 +++ hw/intc/xive2.c | 13 ++++++++----- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 9ed2616e58fe9..018c609ca5eb9 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -279,6 +279,8 @@ xive_tctx_notify(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_ xive_tctx_set_cppr(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IPB=0x%02x PIPR=0x%02x new CPPR=0x%02x NSR=0x%02x" xive_source_esb_read(uint64_t addr, uint32_t srcno, uint64_t value) "@0x%"PRIx64" IRQ 0x%x val=0x%"PRIx64 xive_source_esb_write(uint64_t addr, uint32_t srcno, uint64_t value) "@0x%"PRIx64" IRQ 0x%x val=0x%"PRIx64 +xive_source_notify(uint32_t srcno) "Processing notification for queued IRQ 0x%x" +xive_source_blocked(uint32_t srcno) "No action needed for IRQ 0x%x currently" xive_router_end_notify(uint8_t end_blk, uint32_t end_idx, uint32_t end_data) "END 0x%02x/0x%04x -> enqueue 0x%08x" xive_router_end_escalate(uint8_t end_blk, uint32_t end_idx, uint8_t esc_blk, uint32_t esc_idx, uint32_t end_data) "END 0x%02x/0x%04x -> escalate END 0x%02x/0x%04x data 0x%08x" xive_tctx_tm_write(uint32_t index, uint64_t offset, unsigned int size, uint64_t value) "target=%d @0x%"PRIx64" sz=%d val=0x%" PRIx64 @@ -289,6 +291,10 @@ xive_end_source_read(uint8_t end_blk, uint32_t end_idx, uint64_t addr) "END 0x%x # xive2.c xive_nvp_backlog_op(uint8_t blk, uint32_t idx, uint8_t op, uint8_t priority, uint8_t rc) "NVP 0x%x/0x%x operation=%d priority=%d rc=%d" xive_nvgc_backlog_op(bool c, uint8_t blk, uint32_t idx, uint8_t op, uint8_t priority, uint32_t rc) "NVGC crowd=%d 0x%x/0x%x operation=%d priority=%d rc=%d" +xive_redistribute(uint32_t index, uint8_t ring, uint8_t end_blk, uint32_t end_idx) "Redistribute from target=%d ring=0x%x NVP 0x%x/0x%x" +xive_end_enqueue(uint8_t end_blk, uint32_t end_idx, uint32_t end_data) "Queue event for END 0x%x/0x%x data=0x%x" +xive_escalate_end(uint8_t end_blk, uint32_t end_idx, uint8_t esc_blk, uint32_t esc_idx, uint32_t esc_data) "Escalate from END 0x%x/0x%x to END 0x%x/0x%x data=0x%x" +xive_escalate_esb(uint8_t end_blk, uint32_t end_idx, uint32_t lisn) "Escalate from END 0x%x/0x%x to LISN=0x%x" # pnv_xive.c pnv_xive_ic_hw_trigger(uint64_t addr, uint64_t val) "@0x%"PRIx64" val=0x%"PRIx64 diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 5483a815ef077..d65394651650d 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -1276,6 +1276,7 @@ static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size) /* Forward the source event notification for routing */ if (ret) { + trace_xive_source_notify(srcno); xive_source_notify(xsrc, srcno); } break; @@ -1371,6 +1372,8 @@ static void xive_source_esb_write(void *opaque, hwaddr addr, /* Forward the source event notification for routing */ if (notify) { xive_source_notify(xsrc, srcno); + } else { + trace_xive_source_blocked(srcno); } } diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 9ef372b6d1d90..f810e716dee37 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -616,6 +616,7 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t prio_limit; uint32_t cfg; + trace_xive_redistribute(tctx->cs->cpu_index, ring, nvp_blk, nvp_idx); /* convert crowd/group to blk/idx */ if (group > 0) { nvgc_idx = (nvp_idx & (0xffffffff << group)) | @@ -1455,6 +1456,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, } if (!redistribute && xive2_end_is_enqueue(&end)) { + trace_xive_end_enqueue(end_blk, end_idx, end_data); xive2_end_enqueue(&end, end_data); /* Enqueuing event data modifies the EQ toggle and index */ xive2_router_write_end(xrtr, end_blk, end_idx, &end, 1); @@ -1631,11 +1633,11 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, * Perform END Adaptive escalation processing * The END trigger becomes an Escalation trigger */ - xive2_router_end_notify(xrtr, - xive_get_field32(END2_W4_END_BLOCK, end.w4), - xive_get_field32(END2_W4_ESC_END_INDEX, end.w4), - xive_get_field32(END2_W5_ESC_END_DATA, end.w5), - false); + uint8_t esc_blk = xive_get_field32(END2_W4_END_BLOCK, end.w4); + uint32_t esc_idx = xive_get_field32(END2_W4_ESC_END_INDEX, end.w4); + uint32_t esc_data = xive_get_field32(END2_W5_ESC_END_DATA, end.w5); + trace_xive_escalate_end(end_blk, end_idx, esc_blk, esc_idx, esc_data); + xive2_router_end_notify(xrtr, esc_blk, esc_idx, esc_data, false); } /* end END adaptive escalation */ else { @@ -1652,6 +1654,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, lisn = XIVE_EAS(xive_get_field32(END2_W4_END_BLOCK, end.w4), xive_get_field32(END2_W4_ESC_END_INDEX, end.w4)); + trace_xive_escalate_esb(end_blk, end_idx, lisn); xive2_notify(xrtr, lisn, true /* pq_checked */); } From 1a0cd94252bf111b0ace7b9cd88258e837d95ea4 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:33 +1000 Subject: [PATCH 0047/1794] ppc/xive2: Improve pool regs variable name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change pregs to pool_regs, for clarity. [npiggin: split from larger patch] Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-25-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index f810e716dee37..1f4713dabeb1a 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1044,13 +1044,12 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) /* PHYS updates also depend on POOL values */ if (ring == TM_QW3_HV_PHYS) { - uint8_t *pregs = &tctx->regs[TM_QW2_HV_POOL]; + uint8_t *pool_regs = &tctx->regs[TM_QW2_HV_POOL]; /* POOL values only matter if POOL ctx is valid */ - if (pregs[TM_WORD2] & 0x80) { - - uint8_t pool_pipr = xive_ipb_to_pipr(pregs[TM_IPB]); - uint8_t pool_lsmfb = pregs[TM_LSMFB]; + if (pool_regs[TM_WORD2] & 0x80) { + uint8_t pool_pipr = xive_ipb_to_pipr(pool_regs[TM_IPB]); + uint8_t pool_lsmfb = pool_regs[TM_LSMFB]; /* * Determine highest priority interrupt and @@ -1064,7 +1063,7 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) } /* Values needed for group priority calculation */ - if (pregs[TM_LGS] && (pool_lsmfb < lsmfb_min)) { + if (pool_regs[TM_LGS] && (pool_lsmfb < lsmfb_min)) { group_enabled = true; lsmfb_min = pool_lsmfb; if (lsmfb_min < pipr_min) { From 97cd373e6ca0024132e78496b8a585c00531f7a4 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:34 +1000 Subject: [PATCH 0048/1794] ppc/xive2: Implement "Ack OS IRQ to even report line" TIMA op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Booting AIX in a PowerVM partition requires the use of the "Acknowledge O/S Interrupt to even O/S reporting line" special operation provided by the IBM XIVE interrupt controller. This operation is invoked by writing a byte (data is irrelevant) to offset 0xC10 of the Thread Interrupt Management Area (TIMA). It can be used by software to notify the XIVE logic that the interrupt was received. Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-26-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 8 ++++--- hw/intc/xive2.c | 50 ++++++++++++++++++++++++++++++++++++++++++ include/hw/ppc/xive.h | 1 + include/hw/ppc/xive2.h | 3 ++- 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index d65394651650d..8b705727ae2dd 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -80,7 +80,7 @@ static qemu_irq xive_tctx_output(XiveTCTX *tctx, uint8_t ring) } } -static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) +uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) { uint8_t *regs = &tctx->regs[ring]; uint8_t nsr = regs[TM_NSR]; @@ -340,14 +340,14 @@ static uint64_t xive_tm_vt_poll(XivePresenter *xptr, XiveTCTX *tctx, static const uint8_t xive_tm_hw_view[] = { 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-0 User */ - 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-1 OS */ + 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 0, 0, 0, 3, /* QW-1 OS */ 0, 0, 3, 3, 0, 3, 3, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-2 POOL */ 3, 3, 3, 3, 0, 3, 0, 2, 3, 0, 0, 3, 3, 3, 3, 0, /* QW-3 PHYS */ }; static const uint8_t xive_tm_hv_view[] = { 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-0 User */ - 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-1 OS */ + 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 0, 0, 0, 3, /* QW-1 OS */ 0, 0, 3, 3, 0, 3, 3, 0, 0, 3, 3, 3, 0, 0, 0, 0, /* QW-2 POOL */ 3, 3, 3, 3, 0, 3, 0, 2, 3, 0, 0, 3, 0, 0, 0, 0, /* QW-3 PHYS */ }; @@ -718,6 +718,8 @@ static const XiveTmOp xive2_tm_operations[] = { xive_tm_pull_phys_ctx }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_OL, 1, xive2_tm_pull_phys_ctx_ol, NULL }, + { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_EL, 1, xive2_tm_ack_os_el, + NULL }, }; static const XiveTmOp *xive_tm_find_op(XivePresenter *xptr, hwaddr offset, diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 1f4713dabeb1a..e7e364c13e7d6 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1009,6 +1009,56 @@ static int xive2_tctx_get_nvp_indexes(XiveTCTX *tctx, uint8_t ring, return 0; } +static void xive2_tctx_accept_el(XivePresenter *xptr, XiveTCTX *tctx, + uint8_t ring, uint8_t cl_ring) +{ + uint64_t rd; + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + uint32_t nvp_blk, nvp_idx, xive2_cfg; + Xive2Nvp nvp; + uint64_t phys_addr; + uint8_t OGen = 0; + + xive2_tctx_get_nvp_indexes(tctx, cl_ring, &nvp_blk, &nvp_idx); + + if (xive2_router_get_nvp(xrtr, (uint8_t)nvp_blk, nvp_idx, &nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + if (!xive2_nvp_is_valid(&nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + + rd = xive_tctx_accept(tctx, ring); + + if (ring == TM_QW1_OS) { + OGen = tctx->regs[ring + TM_OGEN]; + } + xive2_cfg = xive2_router_get_config(xrtr); + phys_addr = xive2_nvp_reporting_addr(&nvp); + uint8_t report_data[REPORT_LINE_GEN1_SIZE]; + memset(report_data, 0xff, sizeof(report_data)); + if ((OGen == 1) || (xive2_cfg & XIVE2_GEN1_TIMA_OS)) { + report_data[8] = (rd >> 8) & 0xff; + report_data[9] = rd & 0xff; + } else { + report_data[0] = (rd >> 8) & 0xff; + report_data[1] = rd & 0xff; + } + cpu_physical_memory_write(phys_addr, report_data, REPORT_LINE_GEN1_SIZE); +} + +void xive2_tm_ack_os_el(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive2_tctx_accept_el(xptr, tctx, TM_QW1_OS, TM_QW1_OS); +} + static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) { uint8_t *regs = &tctx->regs[ring]; diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 28f0f1b79ad77..46d05d74fbfb6 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -561,6 +561,7 @@ void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, uint8_t group_level); void xive_tctx_reset_signal(XiveTCTX *tctx, uint8_t ring); void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level); +uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring); /* * KVM XIVE device helpers diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index 760b94a962e73..ff02ce2549847 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -142,5 +142,6 @@ void xive2_tm_set_hv_target(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); void xive2_tm_pull_phys_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); - +void xive2_tm_ack_os_el(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size); #endif /* PPC_XIVE2_H */ From 9ad30401ce9aefa319364cb8efdc6893a5bc20ad Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:35 +1000 Subject: [PATCH 0049/1794] ppc/xive2: Redistribute group interrupt precluded by CPPR update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for redistributing a presented group interrupt if it is precluded as a result of changing the CPPR value. Without this, group interrupts can be lost. Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-27-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 82 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 22 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index e7e364c13e7d6..624620e5b44bd 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -601,20 +601,37 @@ static uint32_t xive2_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx) return xive2_nvp_cam_line(blk, 1 << tid_shift | (pir & tid_mask)); } -static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, - uint8_t nvp_blk, uint32_t nvp_idx, uint8_t ring) +static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) { - uint8_t nsr = tctx->regs[ring + TM_NSR]; + uint8_t *regs = &tctx->regs[ring]; + uint8_t nsr = regs[TM_NSR]; + uint8_t pipr = regs[TM_PIPR]; uint8_t crowd = NVx_CROWD_LVL(nsr); uint8_t group = NVx_GROUP_LVL(nsr); - uint8_t nvgc_blk; - uint8_t nvgc_idx; - uint8_t end_blk; - uint32_t end_idx; - uint8_t pipr = tctx->regs[ring + TM_PIPR]; + uint8_t nvgc_blk, end_blk, nvp_blk; + uint32_t nvgc_idx, end_idx, nvp_idx; Xive2Nvgc nvgc; uint8_t prio_limit; uint32_t cfg; + uint8_t alt_ring; + uint32_t target_ringw2; + uint32_t cam; + bool valid; + bool hw; + + /* redistribution is only for group/crowd interrupts */ + if (!xive_nsr_indicates_group_exception(ring, nsr)) { + return; + } + + alt_ring = xive_nsr_exception_ring(ring, nsr); + target_ringw2 = xive_tctx_word2(&tctx->regs[alt_ring]); + cam = be32_to_cpu(target_ringw2); + + /* extract nvp block and index from targeted ring's cam */ + xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &valid, &hw); + + trace_xive_redistribute(tctx->cs->cpu_index, alt_ring, nvp_blk, nvp_idx); trace_xive_redistribute(tctx->cs->cpu_index, ring, nvp_blk, nvp_idx); /* convert crowd/group to blk/idx */ @@ -659,8 +676,8 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, xive2_router_end_notify(xrtr, end_blk, end_idx, 0, true); /* clear interrupt indication for the context */ - tctx->regs[ring + TM_NSR] = 0; - tctx->regs[ring + TM_PIPR] = tctx->regs[ring + TM_CPPR]; + regs[TM_NSR] = 0; + regs[TM_PIPR] = regs[TM_CPPR]; xive_tctx_reset_signal(tctx, ring); } @@ -695,7 +712,7 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, /* Active group/crowd interrupts need to be redistributed */ nsr = tctx->regs[ring + TM_NSR]; if (xive_nsr_indicates_group_exception(ring, nsr)) { - xive2_redistribute(xrtr, tctx, nvp_blk, nvp_idx, ring); + xive2_redistribute(xrtr, tctx, ring); } if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) { @@ -1059,6 +1076,7 @@ void xive2_tm_ack_os_el(XivePresenter *xptr, XiveTCTX *tctx, xive2_tctx_accept_el(xptr, tctx, TM_QW1_OS, TM_QW1_OS); } +/* NOTE: CPPR only exists for TM_QW1_OS and TM_QW3_HV_PHYS */ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) { uint8_t *regs = &tctx->regs[ring]; @@ -1069,10 +1087,11 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) uint32_t nvp_blk, nvp_idx; Xive2Nvp nvp; int rc; + uint8_t nsr = regs[TM_NSR]; trace_xive_tctx_set_cppr(tctx->cs->cpu_index, ring, regs[TM_IPB], regs[TM_PIPR], - cppr, regs[TM_NSR]); + cppr, nsr); if (cppr > XIVE_PRIORITY_MAX) { cppr = 0xff; @@ -1081,6 +1100,35 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) old_cppr = regs[TM_CPPR]; regs[TM_CPPR] = cppr; + /* Handle increased CPPR priority (lower value) */ + if (cppr < old_cppr) { + if (cppr <= regs[TM_PIPR]) { + /* CPPR lowered below PIPR, must un-present interrupt */ + if (xive_nsr_indicates_exception(ring, nsr)) { + if (xive_nsr_indicates_group_exception(ring, nsr)) { + /* redistribute precluded active grp interrupt */ + xive2_redistribute(xrtr, tctx, ring); + return; + } + } + + /* interrupt is VP directed, pending in IPB */ + regs[TM_PIPR] = cppr; + xive_tctx_notify(tctx, ring, 0); /* Ensure interrupt is cleared */ + return; + } else { + /* CPPR was lowered, but still above PIPR. No action needed. */ + return; + } + } + + /* CPPR didn't change, nothing needs to be done */ + if (cppr == old_cppr) { + return; + } + + /* CPPR priority decreased (higher value) */ + /* * Recompute the PIPR based on local pending interrupts. It will * be adjusted below if needed in case of pending group interrupts. @@ -1129,16 +1177,6 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) return; } - if (cppr < old_cppr) { - /* - * FIXME: check if there's a group interrupt being presented - * and if the new cppr prevents it. If so, then the group - * interrupt needs to be re-added to the backlog and - * re-triggered (see re-trigger END info in the NVGC - * structure) - */ - } - if (group_enabled && lsmfb_min < cppr && lsmfb_min < pipr_min) { From 64c772ca16fcb539380a777ff273278c42f5dfef Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:36 +1000 Subject: [PATCH 0050/1794] ppc/xive2: redistribute irqs for pool and phys ctx pull MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When disabling (pulling) an xive interrupt context, we need to redistribute any active group interrupts to other threads that can handle the interrupt if possible. This support had already been added for the OS context but had not yet been added to the pool or physical context. Signed-off-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-28-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 12 ++--- hw/intc/xive2.c | 94 ++++++++++++++++++++++++++----------- include/hw/ppc/xive2.h | 4 ++ include/hw/ppc/xive2_regs.h | 4 +- 4 files changed, 79 insertions(+), 35 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 8b705727ae2dd..2f72d6ecd5a5c 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -693,7 +693,7 @@ static const XiveTmOp xive2_tm_operations[] = { /* MMIOs above 2K : special operations with side effects */ { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, NULL, - xive_tm_ack_os_reg }, + xive_tm_ack_os_reg }, { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, xive_tm_set_os_pending, NULL }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_G2, 4, NULL, @@ -705,17 +705,17 @@ static const XiveTmOp xive2_tm_operations[] = { { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, NULL, xive_tm_ack_hv_reg }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX_G2, 4, NULL, - xive_tm_pull_pool_ctx }, + xive2_tm_pull_pool_ctx }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, NULL, - xive_tm_pull_pool_ctx }, + xive2_tm_pull_pool_ctx }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, NULL, - xive_tm_pull_pool_ctx }, + xive2_tm_pull_pool_ctx }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_OL, 1, xive2_tm_pull_os_ctx_ol, NULL }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_G2, 4, NULL, - xive_tm_pull_phys_ctx }, + xive2_tm_pull_phys_ctx }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX, 1, NULL, - xive_tm_pull_phys_ctx }, + xive2_tm_pull_phys_ctx }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_OL, 1, xive2_tm_pull_phys_ctx_ol, NULL }, { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_EL, 1, xive2_tm_ack_os_el, diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 624620e5b44bd..2791985cf29b0 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -23,6 +23,9 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, uint32_t end_idx, uint32_t end_data, bool redistribute); +static int xive2_tctx_get_nvp_indexes(XiveTCTX *tctx, uint8_t ring, + uint8_t *nvp_blk, uint32_t *nvp_idx); + uint32_t xive2_router_get_config(Xive2Router *xrtr) { Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr); @@ -604,8 +607,10 @@ static uint32_t xive2_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx) static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) { uint8_t *regs = &tctx->regs[ring]; - uint8_t nsr = regs[TM_NSR]; - uint8_t pipr = regs[TM_PIPR]; + uint8_t *alt_regs = (ring == TM_QW2_HV_POOL) ? &tctx->regs[TM_QW3_HV_PHYS] : + regs; + uint8_t nsr = alt_regs[TM_NSR]; + uint8_t pipr = alt_regs[TM_PIPR]; uint8_t crowd = NVx_CROWD_LVL(nsr); uint8_t group = NVx_GROUP_LVL(nsr); uint8_t nvgc_blk, end_blk, nvp_blk; @@ -614,10 +619,6 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) uint8_t prio_limit; uint32_t cfg; uint8_t alt_ring; - uint32_t target_ringw2; - uint32_t cam; - bool valid; - bool hw; /* redistribution is only for group/crowd interrupts */ if (!xive_nsr_indicates_group_exception(ring, nsr)) { @@ -625,11 +626,9 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) } alt_ring = xive_nsr_exception_ring(ring, nsr); - target_ringw2 = xive_tctx_word2(&tctx->regs[alt_ring]); - cam = be32_to_cpu(target_ringw2); - /* extract nvp block and index from targeted ring's cam */ - xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &valid, &hw); + /* Don't check return code since ring is expected to be invalidated */ + xive2_tctx_get_nvp_indexes(tctx, alt_ring, &nvp_blk, &nvp_idx); trace_xive_redistribute(tctx->cs->cpu_index, alt_ring, nvp_blk, nvp_idx); @@ -676,11 +675,23 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) xive2_router_end_notify(xrtr, end_blk, end_idx, 0, true); /* clear interrupt indication for the context */ - regs[TM_NSR] = 0; - regs[TM_PIPR] = regs[TM_CPPR]; + alt_regs[TM_NSR] = 0; + alt_regs[TM_PIPR] = alt_regs[TM_CPPR]; xive_tctx_reset_signal(tctx, ring); } +static uint8_t xive2_hv_irq_ring(uint8_t nsr) +{ + switch (nsr >> 6) { + case TM_QW3_NSR_HE_POOL: + return TM_QW2_HV_POOL; + case TM_QW3_NSR_HE_PHYS: + return TM_QW3_HV_PHYS; + default: + return -1; + } +} + static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size, uint8_t ring) { @@ -696,7 +707,7 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &valid, &do_save); - if (!valid) { + if (xive2_tctx_get_nvp_indexes(tctx, ring, &nvp_blk, &nvp_idx)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pulling invalid NVP %x/%x !?\n", nvp_blk, nvp_idx); } @@ -706,13 +717,25 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, cur_ring += XIVE_TM_RING_SIZE) { uint32_t ringw2 = xive_tctx_word2(&tctx->regs[cur_ring]); uint32_t ringw2_new = xive_set_field32(TM2_QW1W2_VO, ringw2, 0); + bool is_valid = !!(xive_get_field32(TM2_QW1W2_VO, ringw2)); + uint8_t alt_ring; memcpy(&tctx->regs[cur_ring + TM_WORD2], &ringw2_new, 4); - } - /* Active group/crowd interrupts need to be redistributed */ - nsr = tctx->regs[ring + TM_NSR]; - if (xive_nsr_indicates_group_exception(ring, nsr)) { - xive2_redistribute(xrtr, tctx, ring); + /* Skip the rest for USER or invalid contexts */ + if ((cur_ring == TM_QW0_USER) || !is_valid) { + continue; + } + + /* Active group/crowd interrupts need to be redistributed */ + alt_ring = (cur_ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : cur_ring; + nsr = tctx->regs[alt_ring + TM_NSR]; + if (xive_nsr_indicates_group_exception(alt_ring, nsr)) { + /* For HV rings, only redistribute if cur_ring matches NSR */ + if ((cur_ring == TM_QW1_OS) || + (cur_ring == xive2_hv_irq_ring(nsr))) { + xive2_redistribute(xrtr, tctx, cur_ring); + } + } } if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) { @@ -736,6 +759,18 @@ uint64_t xive2_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, return xive2_tm_pull_ctx(xptr, tctx, offset, size, TM_QW1_OS); } +uint64_t xive2_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size) +{ + return xive2_tm_pull_ctx(xptr, tctx, offset, size, TM_QW2_HV_POOL); +} + +uint64_t xive2_tm_pull_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size) +{ + return xive2_tm_pull_ctx(xptr, tctx, offset, size, TM_QW3_HV_PHYS); +} + #define REPORT_LINE_GEN1_SIZE 16 static void xive2_tm_report_line_gen1(XiveTCTX *tctx, uint8_t *data, @@ -993,37 +1028,40 @@ void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, } } +/* returns -1 if ring is invalid, but still populates block and index */ static int xive2_tctx_get_nvp_indexes(XiveTCTX *tctx, uint8_t ring, - uint32_t *nvp_blk, uint32_t *nvp_idx) + uint8_t *nvp_blk, uint32_t *nvp_idx) { - uint32_t w2, cam; + uint32_t w2; + uint32_t cam = 0; + int rc = 0; w2 = xive_tctx_word2(&tctx->regs[ring]); switch (ring) { case TM_QW1_OS: if (!(be32_to_cpu(w2) & TM2_QW1W2_VO)) { - return -1; + rc = -1; } cam = xive_get_field32(TM2_QW1W2_OS_CAM, w2); break; case TM_QW2_HV_POOL: if (!(be32_to_cpu(w2) & TM2_QW2W2_VP)) { - return -1; + rc = -1; } cam = xive_get_field32(TM2_QW2W2_POOL_CAM, w2); break; case TM_QW3_HV_PHYS: if (!(be32_to_cpu(w2) & TM2_QW3W2_VT)) { - return -1; + rc = -1; } cam = xive2_tctx_hw_cam_line(tctx->xptr, tctx); break; default: - return -1; + rc = -1; } *nvp_blk = xive2_nvp_blk(cam); *nvp_idx = xive2_nvp_idx(cam); - return 0; + return rc; } static void xive2_tctx_accept_el(XivePresenter *xptr, XiveTCTX *tctx, @@ -1031,7 +1069,8 @@ static void xive2_tctx_accept_el(XivePresenter *xptr, XiveTCTX *tctx, { uint64_t rd; Xive2Router *xrtr = XIVE2_ROUTER(xptr); - uint32_t nvp_blk, nvp_idx, xive2_cfg; + uint32_t nvp_idx, xive2_cfg; + uint8_t nvp_blk; Xive2Nvp nvp; uint64_t phys_addr; uint8_t OGen = 0; @@ -1084,7 +1123,8 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) uint8_t old_cppr, backlog_prio, first_group, group_level; uint8_t pipr_min, lsmfb_min, ring_min; bool group_enabled; - uint32_t nvp_blk, nvp_idx; + uint8_t nvp_blk; + uint32_t nvp_idx; Xive2Nvp nvp; int rc; uint8_t nsr = regs[TM_NSR]; diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index ff02ce2549847..a91b99057c2ac 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -140,6 +140,10 @@ bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority); void xive2_tm_set_lsmfb(XiveTCTX *tctx, int ring, uint8_t priority); void xive2_tm_set_hv_target(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); +uint64_t xive2_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size); +uint64_t xive2_tm_pull_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size); void xive2_tm_pull_phys_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); void xive2_tm_ack_os_el(XivePresenter *xptr, XiveTCTX *tctx, diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index e22203814312d..f82054661bdad 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -209,9 +209,9 @@ static inline uint32_t xive2_nvp_idx(uint32_t cam_line) return cam_line & ((1 << XIVE2_NVP_SHIFT) - 1); } -static inline uint32_t xive2_nvp_blk(uint32_t cam_line) +static inline uint8_t xive2_nvp_blk(uint32_t cam_line) { - return (cam_line >> XIVE2_NVP_SHIFT) & 0xf; + return (uint8_t)((cam_line >> XIVE2_NVP_SHIFT) & 0xf); } void xive2_nvp_pic_print_info(Xive2Nvp *nvp, uint32_t nvp_idx, GString *buf); From 1319cb8997b9d0fdaae04a79398a2ed06d4cb5e1 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:37 +1000 Subject: [PATCH 0051/1794] ppc/xive: Change presenter .match_nvt to match not present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have the match_nvt method only perform a TCTX match but don't present the interrupt, the caller presents. This has no functional change, but allows for more complicated presentation logic after matching. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-29-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive.c | 16 +++++++------- hw/intc/pnv_xive2.c | 16 +++++++------- hw/intc/spapr_xive.c | 18 +++++++-------- hw/intc/xive.c | 51 +++++++++++++++---------------------------- hw/intc/xive2.c | 31 +++++++++++++------------- hw/ppc/pnv.c | 48 ++++++++++++++-------------------------- hw/ppc/spapr.c | 21 +++++++----------- include/hw/ppc/xive.h | 27 +++++++++++++---------- 8 files changed, 97 insertions(+), 131 deletions(-) diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index 935c0e4742f5f..c2ca40b8be87c 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -470,14 +470,13 @@ static bool pnv_xive_is_cpu_enabled(PnvXive *xive, PowerPCCPU *cpu) return xive->regs[reg >> 3] & PPC_BIT(bit); } -static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, XiveTCTXMatch *match) +static bool pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match) { PnvXive *xive = PNV_XIVE(xptr); PnvChip *chip = xive->chip; - int count = 0; int i, j; for (i = 0; i < chip->nr_cores; i++) { @@ -510,17 +509,18 @@ static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format, qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a " "thread context NVT %x/%x\n", nvt_blk, nvt_idx); - return -1; + match->count++; + continue; } match->ring = ring; match->tctx = tctx; - count++; + match->count++; } } } - return count; + return !!match->count; } static uint32_t pnv_xive_presenter_get_config(XivePresenter *xptr) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 7b4a33228e050..e019cad5c14c1 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -640,14 +640,13 @@ static bool pnv_xive2_is_cpu_enabled(PnvXive2 *xive, PowerPCCPU *cpu) return xive->tctxt_regs[reg >> 3] & PPC_BIT(bit); } -static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, XiveTCTXMatch *match) +static bool pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match) { PnvXive2 *xive = PNV_XIVE2(xptr); PnvChip *chip = xive->chip; - int count = 0; int i, j; bool gen1_tima_os = xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS; @@ -692,7 +691,8 @@ static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, "thread context NVT %x/%x\n", nvt_blk, nvt_idx); /* Should set a FIR if we ever model it */ - return -1; + match->count++; + continue; } /* * For a group notification, we need to know if the @@ -717,13 +717,13 @@ static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, } } } - count++; + match->count++; } } } } - return count; + return !!match->count; } static uint32_t pnv_xive2_presenter_get_config(XivePresenter *xptr) diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index 440edb97d8d39..e393f5dcdccfe 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -428,14 +428,13 @@ static int spapr_xive_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk, g_assert_not_reached(); } -static int spapr_xive_match_nvt(XivePresenter *xptr, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, - uint8_t priority, - uint32_t logic_serv, XiveTCTXMatch *match) +static bool spapr_xive_match_nvt(XivePresenter *xptr, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, + uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match) { CPUState *cs; - int count = 0; CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -463,16 +462,17 @@ static int spapr_xive_match_nvt(XivePresenter *xptr, uint8_t format, if (match->tctx) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a thread " "context NVT %x/%x\n", nvt_blk, nvt_idx); - return -1; + match->count++; + continue; } match->ring = ring; match->tctx = tctx; - count++; + match->count++; } } - return count; + return !!match->count; } static uint32_t spapr_xive_presenter_get_config(XivePresenter *xptr) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 2f72d6ecd5a5c..c92e819053e83 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -1762,8 +1762,8 @@ uint32_t xive_get_vpgroup_size(uint32_t nvp_index) return 1U << (first_zero + 1); } -static uint8_t xive_get_group_level(bool crowd, bool ignore, - uint32_t nvp_blk, uint32_t nvp_index) +uint8_t xive_get_group_level(bool crowd, bool ignore, + uint32_t nvp_blk, uint32_t nvp_index) { int first_zero; uint8_t level; @@ -1881,15 +1881,14 @@ int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, * This is our simple Xive Presenter Engine model. It is merged in the * Router as it does not require an extra object. */ -bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, +bool xive_presenter_match(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, bool *precluded) + uint32_t logic_serv, XiveTCTXMatch *match) { XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xfb); - XiveTCTXMatch match = { .tctx = NULL, .ring = 0, .precluded = false }; - uint8_t group_level; - int count; + + memset(match, 0, sizeof(*match)); /* * Ask the machine to scan the interrupt controllers for a match. @@ -1914,22 +1913,8 @@ bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, * a new command to the presenters (the equivalent of the "assign" * power bus command in the documented full notify sequence. */ - count = xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, crowd, cam_ignore, - priority, logic_serv, &match); - if (count < 0) { - return false; - } - - /* handle CPU exception delivery */ - if (count) { - group_level = xive_get_group_level(crowd, cam_ignore, nvt_blk, nvt_idx); - trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring, group_level); - xive_tctx_pipr_update(match.tctx, match.ring, priority, group_level); - } else { - *precluded = match.precluded; - } - - return !!count; + return xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, crowd, cam_ignore, + priority, logic_serv, match); } /* @@ -1966,7 +1951,7 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas) uint8_t nvt_blk; uint32_t nvt_idx; XiveNVT nvt; - bool found, precluded; + XiveTCTXMatch match; uint8_t end_blk = xive_get_field64(EAS_END_BLOCK, eas->w); uint32_t end_idx = xive_get_field64(EAS_END_INDEX, eas->w); @@ -2046,16 +2031,16 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas) return; } - found = xive_presenter_notify(xrtr->xfb, format, nvt_blk, nvt_idx, - false /* crowd */, - xive_get_field32(END_W7_F0_IGNORE, end.w7), - priority, - xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7), - &precluded); - /* we don't support VP-group notification on P9, so precluded is not used */ /* TODO: Auto EOI. */ - - if (found) { + /* we don't support VP-group notification on P9, so precluded is not used */ + if (xive_presenter_match(xrtr->xfb, format, nvt_blk, nvt_idx, + false /* crowd */, + xive_get_field32(END_W7_F0_IGNORE, end.w7), + priority, + xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7), + &match)) { + trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring, 0); + xive_tctx_pipr_update(match.tctx, match.ring, priority, 0); return; } diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 2791985cf29b0..602b23d06d802 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1559,7 +1559,8 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, Xive2End end; uint8_t priority; uint8_t format; - bool found, precluded; + XiveTCTXMatch match; + bool crowd, cam_ignore; uint8_t nvx_blk; uint32_t nvx_idx; @@ -1629,16 +1630,19 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, */ nvx_blk = xive_get_field32(END2_W6_VP_BLOCK, end.w6); nvx_idx = xive_get_field32(END2_W6_VP_OFFSET, end.w6); - - found = xive_presenter_notify(xrtr->xfb, format, nvx_blk, nvx_idx, - xive2_end_is_crowd(&end), xive2_end_is_ignore(&end), - priority, - xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7), - &precluded); + crowd = xive2_end_is_crowd(&end); + cam_ignore = xive2_end_is_ignore(&end); /* TODO: Auto EOI. */ - - if (found) { + if (xive_presenter_match(xrtr->xfb, format, nvx_blk, nvx_idx, + crowd, cam_ignore, priority, + xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7), + &match)) { + uint8_t group_level; + + group_level = xive_get_group_level(crowd, cam_ignore, nvx_blk, nvx_idx); + trace_xive_presenter_notify(nvx_blk, nvx_idx, match.ring, group_level); + xive_tctx_pipr_update(match.tctx, match.ring, priority, group_level); return; } @@ -1656,7 +1660,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, return; } - if (!xive2_end_is_ignore(&end)) { + if (!cam_ignore) { uint8_t ipb; Xive2Nvp nvp; @@ -1685,9 +1689,6 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, } else { Xive2Nvgc nvgc; uint32_t backlog; - bool crowd; - - crowd = xive2_end_is_crowd(&end); /* * For groups and crowds, the per-priority backlog @@ -1719,9 +1720,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, if (backlog == 1) { XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xrtr->xfb); xfc->broadcast(xrtr->xfb, nvx_blk, nvx_idx, - xive2_end_is_crowd(&end), - xive2_end_is_ignore(&end), - priority); + crowd, cam_ignore, priority); if (!xive2_end_is_precluded_escalation(&end)) { /* diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 4a49e9d1a8650..d84c9067edb35 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -2608,62 +2608,46 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, GString *buf) } } -static int pnv_match_nvt(XiveFabric *xfb, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, - XiveTCTXMatch *match) +static bool pnv_match_nvt(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, + XiveTCTXMatch *match) { PnvMachineState *pnv = PNV_MACHINE(xfb); - int total_count = 0; int i; for (i = 0; i < pnv->num_chips; i++) { Pnv9Chip *chip9 = PNV9_CHIP(pnv->chips[i]); XivePresenter *xptr = XIVE_PRESENTER(&chip9->xive); XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); - int count; - count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, - cam_ignore, priority, logic_serv, match); - - if (count < 0) { - return count; - } - - total_count += count; + xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, + cam_ignore, priority, logic_serv, match); } - return total_count; + return !!match->count; } -static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, - XiveTCTXMatch *match) +static bool pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, + XiveTCTXMatch *match) { PnvMachineState *pnv = PNV_MACHINE(xfb); - int total_count = 0; int i; for (i = 0; i < pnv->num_chips; i++) { Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]); XivePresenter *xptr = XIVE_PRESENTER(&chip10->xive); XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); - int count; - - count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, - cam_ignore, priority, logic_serv, match); - - if (count < 0) { - return count; - } - total_count += count; + xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, + cam_ignore, priority, logic_serv, match); } - return total_count; + return !!match->count; } static int pnv10_xive_broadcast(XiveFabric *xfb, diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 40f53ad7b3444..1855a3cd8d035 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4468,21 +4468,14 @@ static void spapr_pic_print_info(InterruptStatsProvider *obj, GString *buf) /* * This is a XIVE only operation */ -static int spapr_match_nvt(XiveFabric *xfb, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, XiveTCTXMatch *match) +static bool spapr_match_nvt(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match) { SpaprMachineState *spapr = SPAPR_MACHINE(xfb); XivePresenter *xptr = XIVE_PRESENTER(spapr->active_intc); XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); - int count; - - count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, cam_ignore, - priority, logic_serv, match); - if (count < 0) { - return count; - } /* * When we implement the save and restore of the thread interrupt @@ -4493,12 +4486,14 @@ static int spapr_match_nvt(XiveFabric *xfb, uint8_t format, * Until this is done, the sPAPR machine should find at least one * matching context always. */ - if (count == 0) { + if (!xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, cam_ignore, + priority, logic_serv, match)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVT %x/%x is not dispatched\n", nvt_blk, nvt_idx); + return false; } - return count; + return true; } int spapr_get_vcpu_id(PowerPCCPU *cpu) diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 46d05d74fbfb6..8152a9df3d39a 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -425,6 +425,7 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas); typedef struct XiveTCTXMatch { XiveTCTX *tctx; + int count; uint8_t ring; bool precluded; } XiveTCTXMatch; @@ -440,10 +441,10 @@ DECLARE_CLASS_CHECKERS(XivePresenterClass, XIVE_PRESENTER, struct XivePresenterClass { InterfaceClass parent; - int (*match_nvt)(XivePresenter *xptr, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, XiveTCTXMatch *match); + bool (*match_nvt)(XivePresenter *xptr, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match); bool (*in_kernel)(const XivePresenter *xptr); uint32_t (*get_config)(XivePresenter *xptr); int (*broadcast)(XivePresenter *xptr, @@ -455,12 +456,14 @@ int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, bool cam_ignore, uint32_t logic_serv); -bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, bool *precluded); +bool xive_presenter_match(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match); uint32_t xive_get_vpgroup_size(uint32_t nvp_index); +uint8_t xive_get_group_level(bool crowd, bool ignore, + uint32_t nvp_blk, uint32_t nvp_index); /* * XIVE Fabric (Interface between Interrupt Controller and Machine) @@ -475,10 +478,10 @@ DECLARE_CLASS_CHECKERS(XiveFabricClass, XIVE_FABRIC, struct XiveFabricClass { InterfaceClass parent; - int (*match_nvt)(XiveFabric *xfb, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, XiveTCTXMatch *match); + bool (*match_nvt)(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match); int (*broadcast)(XiveFabric *xfb, uint8_t nvt_blk, uint32_t nvt_idx, bool crowd, bool cam_ignore, uint8_t priority); }; From cc15d50b6e8a4090d667755259ea36144b79d22f Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:38 +1000 Subject: [PATCH 0052/1794] ppc/xive2: Redistribute group interrupt preempted by higher priority interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A group interrupt that gets preempted by a higher priority interrupt delivery must be redistributed otherwise it would get lost. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-30-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 602b23d06d802..f51fd38a13eb4 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1638,11 +1638,21 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, crowd, cam_ignore, priority, xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7), &match)) { + XiveTCTX *tctx = match.tctx; + uint8_t ring = match.ring; + uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; + uint8_t *aregs = &tctx->regs[alt_ring]; + uint8_t nsr = aregs[TM_NSR]; uint8_t group_level; + if (priority < aregs[TM_PIPR] && + xive_nsr_indicates_group_exception(alt_ring, nsr)) { + xive2_redistribute(xrtr, tctx, alt_ring); + } + group_level = xive_get_group_level(crowd, cam_ignore, nvx_blk, nvx_idx); - trace_xive_presenter_notify(nvx_blk, nvx_idx, match.ring, group_level); - xive_tctx_pipr_update(match.tctx, match.ring, priority, group_level); + trace_xive_presenter_notify(nvx_blk, nvx_idx, ring, group_level); + xive_tctx_pipr_update(tctx, ring, priority, group_level); return; } From d16214ed2c57a31b5de7e2c115c65b831170a60e Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:39 +1000 Subject: [PATCH 0053/1794] ppc/xive: Add xive_tctx_pipr_present() to present new interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xive_tctx_pipr_update() is used for multiple things. In an effort to make things simpler and less overloaded, split out the function that is used to present a new interrupt to the tctx. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-31-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 8 +++++++- hw/intc/xive2.c | 2 +- include/hw/ppc/xive.h | 2 ++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index c92e819053e83..038c35846d947 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -225,6 +225,12 @@ void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, xive_tctx_notify(tctx, ring, group_level); } +void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority, + uint8_t group_level) +{ + xive_tctx_pipr_update(tctx, ring, priority, group_level); +} + /* * XIVE Thread Interrupt Management Area (TIMA) */ @@ -2040,7 +2046,7 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas) xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7), &match)) { trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring, 0); - xive_tctx_pipr_update(match.tctx, match.ring, priority, 0); + xive_tctx_pipr_present(match.tctx, match.ring, priority, 0); return; } diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index f51fd38a13eb4..fe40f7f07bdd6 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1652,7 +1652,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, group_level = xive_get_group_level(crowd, cam_ignore, nvx_blk, nvx_idx); trace_xive_presenter_notify(nvx_blk, nvx_idx, ring, group_level); - xive_tctx_pipr_update(tctx, ring, priority, group_level); + xive_tctx_pipr_present(tctx, ring, priority, group_level); return; } diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 8152a9df3d39a..0d6b11e818c14 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -562,6 +562,8 @@ void xive_tctx_reset(XiveTCTX *tctx); void xive_tctx_destroy(XiveTCTX *tctx); void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, uint8_t group_level); +void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority, + uint8_t group_level); void xive_tctx_reset_signal(XiveTCTX *tctx, uint8_t ring); void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level); uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring); From 46f5ee8885a521c56e60820bf35aba4e94e16cf7 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:40 +1000 Subject: [PATCH 0054/1794] ppc/xive: Fix high prio group interrupt being preempted by low prio VP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xive_tctx_pipr_present() as implemented with xive_tctx_pipr_update() causes VP-directed (group==0) interrupt to be presented in PIPR and NSR despite being a lower priority than the currently presented group interrupt. This must not happen. The IPB bit should record the low priority VP interrupt, but PIPR and NSR must not present the lower priority interrupt. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-32-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 038c35846d947..7110cf45d74f9 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -228,7 +228,23 @@ void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority, uint8_t group_level) { - xive_tctx_pipr_update(tctx, ring, priority, group_level); + /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ + uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; + uint8_t *aregs = &tctx->regs[alt_ring]; + uint8_t *regs = &tctx->regs[ring]; + uint8_t pipr = xive_priority_to_pipr(priority); + + if (group_level == 0) { + regs[TM_IPB] |= xive_priority_to_ipb(priority); + if (pipr >= aregs[TM_PIPR]) { + /* VP interrupts can come here with lower priority than PIPR */ + return; + } + } + g_assert(pipr <= xive_ipb_to_pipr(regs[TM_IPB])); + g_assert(pipr < aregs[TM_PIPR]); + aregs[TM_PIPR] = pipr; + xive_tctx_notify(tctx, ring, group_level); } /* From 3516b9b6739714068d83bb5ed9ce25cc1b20be8d Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:41 +1000 Subject: [PATCH 0055/1794] ppc/xive: Split xive recompute from IPB function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Further split xive_tctx_pipr_update() by splitting out a new function that is used to re-compute the PIPR from IPB. This is generally only used with XIVE1, because group interrputs require more logic. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-33-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 7110cf45d74f9..5deb2f478fcbf 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -225,6 +225,20 @@ void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, xive_tctx_notify(tctx, ring, group_level); } +static void xive_tctx_pipr_recompute_from_ipb(XiveTCTX *tctx, uint8_t ring) +{ + /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ + uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; + uint8_t *aregs = &tctx->regs[alt_ring]; + uint8_t *regs = &tctx->regs[ring]; + + /* Does not support a presented group interrupt */ + g_assert(!xive_nsr_indicates_group_exception(alt_ring, aregs[TM_NSR])); + + aregs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); + xive_tctx_notify(tctx, ring, 0); +} + void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority, uint8_t group_level) { @@ -517,7 +531,12 @@ static void xive_tm_set_os_lgs(XivePresenter *xptr, XiveTCTX *tctx, static void xive_tm_set_os_pending(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size) { - xive_tctx_pipr_update(tctx, TM_QW1_OS, value & 0xff, 0); + uint8_t ring = TM_QW1_OS; + uint8_t *regs = &tctx->regs[ring]; + + /* XXX: how should this work exactly? */ + regs[TM_IPB] |= xive_priority_to_ipb(value & 0xff); + xive_tctx_pipr_recompute_from_ipb(tctx, ring); } static void xive_os_cam_decode(uint32_t cam, uint8_t *nvt_blk, @@ -601,14 +620,14 @@ static void xive_tctx_need_resend(XiveRouter *xrtr, XiveTCTX *tctx, } /* - * Always call xive_tctx_pipr_update(). Even if there were no + * Always call xive_tctx_recompute_from_ipb(). Even if there were no * escalation triggered, there could be a pending interrupt which * was saved when the context was pulled and that we need to take * into account by recalculating the PIPR (which is not * saved/restored). * It will also raise the External interrupt signal if needed. */ - xive_tctx_pipr_update(tctx, TM_QW1_OS, 0xFF, 0); /* fxb */ + xive_tctx_pipr_recompute_from_ipb(tctx, TM_QW1_OS); /* fxb */ } /* From 581bec5a04c5c27a86cfae93ca531c101f2df2ec Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:42 +1000 Subject: [PATCH 0056/1794] ppc/xive: tctx signaling registers rework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tctx "signaling" registers (PIPR, CPPR, NSR) raise an interrupt on the target CPU thread. The POOL and PHYS rings both raise hypervisor interrupts, so they both share one set of signaling registers in the PHYS ring. The PHYS NSR register contains a field that indicates which ring has presented the interrupt being signaled to the CPU. This sharing results in all the "alt_regs" throughout the code. alt_regs is not very descriptive, and worse is that the name is used for conversions in both directions, i.e., to find the presenting ring from the signaling ring, and the signaling ring from the presenting ring. Instead of alt_regs, use the names sig_regs and sig_ring, and regs and ring for the presenting ring being worked on. Add a helper function to get the sign_regs, and add some asserts to ensure the POOL regs are never used to signal interrupts. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-34-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 112 ++++++++++++++++++++++-------------------- hw/intc/xive2.c | 94 ++++++++++++++++------------------- include/hw/ppc/xive.h | 26 +++++++++- 3 files changed, 126 insertions(+), 106 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 5deb2f478fcbf..119a178f2e23f 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -80,69 +80,77 @@ static qemu_irq xive_tctx_output(XiveTCTX *tctx, uint8_t ring) } } -uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) +/* + * interrupt is accepted on the presentation ring, for PHYS ring the NSR + * directs it to the PHYS or POOL rings. + */ +uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t sig_ring) { - uint8_t *regs = &tctx->regs[ring]; - uint8_t nsr = regs[TM_NSR]; + uint8_t *sig_regs = &tctx->regs[sig_ring]; + uint8_t nsr = sig_regs[TM_NSR]; - qemu_irq_lower(xive_tctx_output(tctx, ring)); + g_assert(sig_ring == TM_QW1_OS || sig_ring == TM_QW3_HV_PHYS); + + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_NSR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0); + + qemu_irq_lower(xive_tctx_output(tctx, sig_ring)); - if (xive_nsr_indicates_exception(ring, nsr)) { - uint8_t cppr = regs[TM_PIPR]; - uint8_t alt_ring; - uint8_t *alt_regs; + if (xive_nsr_indicates_exception(sig_ring, nsr)) { + uint8_t cppr = sig_regs[TM_PIPR]; + uint8_t ring; + uint8_t *regs; - alt_ring = xive_nsr_exception_ring(ring, nsr); - alt_regs = &tctx->regs[alt_ring]; + ring = xive_nsr_exception_ring(sig_ring, nsr); + regs = &tctx->regs[ring]; - regs[TM_CPPR] = cppr; + sig_regs[TM_CPPR] = cppr; /* * If the interrupt was for a specific VP, reset the pending * buffer bit, otherwise clear the logical server indicator */ - if (!xive_nsr_indicates_group_exception(ring, nsr)) { - alt_regs[TM_IPB] &= ~xive_priority_to_ipb(cppr); + if (!xive_nsr_indicates_group_exception(sig_ring, nsr)) { + regs[TM_IPB] &= ~xive_priority_to_ipb(cppr); } /* Clear the exception from NSR */ - regs[TM_NSR] = 0; + sig_regs[TM_NSR] = 0; - trace_xive_tctx_accept(tctx->cs->cpu_index, alt_ring, - alt_regs[TM_IPB], regs[TM_PIPR], - regs[TM_CPPR], regs[TM_NSR]); + trace_xive_tctx_accept(tctx->cs->cpu_index, ring, + regs[TM_IPB], sig_regs[TM_PIPR], + sig_regs[TM_CPPR], sig_regs[TM_NSR]); } - return ((uint64_t)nsr << 8) | regs[TM_CPPR]; + return ((uint64_t)nsr << 8) | sig_regs[TM_CPPR]; } void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level) { - /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ - uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; - uint8_t *alt_regs = &tctx->regs[alt_ring]; + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; - if (alt_regs[TM_PIPR] < alt_regs[TM_CPPR]) { + if (sig_regs[TM_PIPR] < sig_regs[TM_CPPR]) { switch (ring) { case TM_QW1_OS: - regs[TM_NSR] = TM_QW1_NSR_EO | (group_level & 0x3F); + sig_regs[TM_NSR] = TM_QW1_NSR_EO | (group_level & 0x3F); break; case TM_QW2_HV_POOL: - alt_regs[TM_NSR] = (TM_QW3_NSR_HE_POOL << 6) | (group_level & 0x3F); + sig_regs[TM_NSR] = (TM_QW3_NSR_HE_POOL << 6) | (group_level & 0x3F); break; case TM_QW3_HV_PHYS: - regs[TM_NSR] = (TM_QW3_NSR_HE_PHYS << 6) | (group_level & 0x3F); + sig_regs[TM_NSR] = (TM_QW3_NSR_HE_PHYS << 6) | (group_level & 0x3F); break; default: g_assert_not_reached(); } trace_xive_tctx_notify(tctx->cs->cpu_index, ring, - regs[TM_IPB], alt_regs[TM_PIPR], - alt_regs[TM_CPPR], alt_regs[TM_NSR]); + regs[TM_IPB], sig_regs[TM_PIPR], + sig_regs[TM_CPPR], sig_regs[TM_NSR]); qemu_irq_raise(xive_tctx_output(tctx, ring)); } else { - alt_regs[TM_NSR] = 0; + sig_regs[TM_NSR] = 0; qemu_irq_lower(xive_tctx_output(tctx, ring)); } } @@ -159,25 +167,32 @@ void xive_tctx_reset_signal(XiveTCTX *tctx, uint8_t ring) static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) { - uint8_t *regs = &tctx->regs[ring]; + uint8_t *sig_regs = &tctx->regs[ring]; uint8_t pipr_min; uint8_t ring_min; + g_assert(ring == TM_QW1_OS || ring == TM_QW3_HV_PHYS); + + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_NSR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0); + + /* XXX: should show pool IPB for PHYS ring */ trace_xive_tctx_set_cppr(tctx->cs->cpu_index, ring, - regs[TM_IPB], regs[TM_PIPR], - cppr, regs[TM_NSR]); + sig_regs[TM_IPB], sig_regs[TM_PIPR], + cppr, sig_regs[TM_NSR]); if (cppr > XIVE_PRIORITY_MAX) { cppr = 0xff; } - tctx->regs[ring + TM_CPPR] = cppr; + sig_regs[TM_CPPR] = cppr; /* * Recompute the PIPR based on local pending interrupts. The PHYS * ring must take the minimum of both the PHYS and POOL PIPR values. */ - pipr_min = xive_ipb_to_pipr(regs[TM_IPB]); + pipr_min = xive_ipb_to_pipr(sig_regs[TM_IPB]); ring_min = ring; /* PHYS updates also depend on POOL values */ @@ -186,7 +201,6 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) /* POOL values only matter if POOL ctx is valid */ if (pool_regs[TM_WORD2] & 0x80) { - uint8_t pool_pipr = xive_ipb_to_pipr(pool_regs[TM_IPB]); /* @@ -200,7 +214,7 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) } } - regs[TM_PIPR] = pipr_min; + sig_regs[TM_PIPR] = pipr_min; /* CPPR has changed, check if we need to raise a pending exception */ xive_tctx_notify(tctx, ring_min, 0); @@ -208,56 +222,50 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, uint8_t group_level) - { - /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ - uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; - uint8_t *alt_regs = &tctx->regs[alt_ring]; +{ + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; if (group_level == 0) { /* VP-specific */ regs[TM_IPB] |= xive_priority_to_ipb(priority); - alt_regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); + sig_regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); } else { /* VP-group */ - alt_regs[TM_PIPR] = xive_priority_to_pipr(priority); + sig_regs[TM_PIPR] = xive_priority_to_pipr(priority); } xive_tctx_notify(tctx, ring, group_level); } static void xive_tctx_pipr_recompute_from_ipb(XiveTCTX *tctx, uint8_t ring) { - /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ - uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; - uint8_t *aregs = &tctx->regs[alt_ring]; + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; /* Does not support a presented group interrupt */ - g_assert(!xive_nsr_indicates_group_exception(alt_ring, aregs[TM_NSR])); + g_assert(!xive_nsr_indicates_group_exception(ring, sig_regs[TM_NSR])); - aregs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); + sig_regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); xive_tctx_notify(tctx, ring, 0); } void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority, uint8_t group_level) { - /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ - uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; - uint8_t *aregs = &tctx->regs[alt_ring]; + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; uint8_t pipr = xive_priority_to_pipr(priority); if (group_level == 0) { regs[TM_IPB] |= xive_priority_to_ipb(priority); - if (pipr >= aregs[TM_PIPR]) { + if (pipr >= sig_regs[TM_PIPR]) { /* VP interrupts can come here with lower priority than PIPR */ return; } } g_assert(pipr <= xive_ipb_to_pipr(regs[TM_IPB])); - g_assert(pipr < aregs[TM_PIPR]); - aregs[TM_PIPR] = pipr; + g_assert(pipr < sig_regs[TM_PIPR]); + sig_regs[TM_PIPR] = pipr; xive_tctx_notify(tctx, ring, group_level); } diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index fe40f7f07bdd6..71b40f702a6f1 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -606,11 +606,9 @@ static uint32_t xive2_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx) static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) { - uint8_t *regs = &tctx->regs[ring]; - uint8_t *alt_regs = (ring == TM_QW2_HV_POOL) ? &tctx->regs[TM_QW3_HV_PHYS] : - regs; - uint8_t nsr = alt_regs[TM_NSR]; - uint8_t pipr = alt_regs[TM_PIPR]; + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); + uint8_t nsr = sig_regs[TM_NSR]; + uint8_t pipr = sig_regs[TM_PIPR]; uint8_t crowd = NVx_CROWD_LVL(nsr); uint8_t group = NVx_GROUP_LVL(nsr); uint8_t nvgc_blk, end_blk, nvp_blk; @@ -618,19 +616,16 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) Xive2Nvgc nvgc; uint8_t prio_limit; uint32_t cfg; - uint8_t alt_ring; /* redistribution is only for group/crowd interrupts */ if (!xive_nsr_indicates_group_exception(ring, nsr)) { return; } - alt_ring = xive_nsr_exception_ring(ring, nsr); - /* Don't check return code since ring is expected to be invalidated */ - xive2_tctx_get_nvp_indexes(tctx, alt_ring, &nvp_blk, &nvp_idx); + xive2_tctx_get_nvp_indexes(tctx, ring, &nvp_blk, &nvp_idx); - trace_xive_redistribute(tctx->cs->cpu_index, alt_ring, nvp_blk, nvp_idx); + trace_xive_redistribute(tctx->cs->cpu_index, ring, nvp_blk, nvp_idx); trace_xive_redistribute(tctx->cs->cpu_index, ring, nvp_blk, nvp_idx); /* convert crowd/group to blk/idx */ @@ -675,23 +670,11 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) xive2_router_end_notify(xrtr, end_blk, end_idx, 0, true); /* clear interrupt indication for the context */ - alt_regs[TM_NSR] = 0; - alt_regs[TM_PIPR] = alt_regs[TM_CPPR]; + sig_regs[TM_NSR] = 0; + sig_regs[TM_PIPR] = sig_regs[TM_CPPR]; xive_tctx_reset_signal(tctx, ring); } -static uint8_t xive2_hv_irq_ring(uint8_t nsr) -{ - switch (nsr >> 6) { - case TM_QW3_NSR_HE_POOL: - return TM_QW2_HV_POOL; - case TM_QW3_NSR_HE_PHYS: - return TM_QW3_HV_PHYS; - default: - return -1; - } -} - static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size, uint8_t ring) { @@ -718,7 +701,8 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, uint32_t ringw2 = xive_tctx_word2(&tctx->regs[cur_ring]); uint32_t ringw2_new = xive_set_field32(TM2_QW1W2_VO, ringw2, 0); bool is_valid = !!(xive_get_field32(TM2_QW1W2_VO, ringw2)); - uint8_t alt_ring; + uint8_t *sig_regs; + memcpy(&tctx->regs[cur_ring + TM_WORD2], &ringw2_new, 4); /* Skip the rest for USER or invalid contexts */ @@ -727,12 +711,11 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, } /* Active group/crowd interrupts need to be redistributed */ - alt_ring = (cur_ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : cur_ring; - nsr = tctx->regs[alt_ring + TM_NSR]; - if (xive_nsr_indicates_group_exception(alt_ring, nsr)) { - /* For HV rings, only redistribute if cur_ring matches NSR */ - if ((cur_ring == TM_QW1_OS) || - (cur_ring == xive2_hv_irq_ring(nsr))) { + sig_regs = xive_tctx_signal_regs(tctx, ring); + nsr = sig_regs[TM_NSR]; + if (xive_nsr_indicates_group_exception(cur_ring, nsr)) { + /* Ensure ring matches NSR (for HV NSR POOL vs PHYS rings) */ + if (cur_ring == xive_nsr_exception_ring(cur_ring, nsr)) { xive2_redistribute(xrtr, tctx, cur_ring); } } @@ -1118,7 +1101,7 @@ void xive2_tm_ack_os_el(XivePresenter *xptr, XiveTCTX *tctx, /* NOTE: CPPR only exists for TM_QW1_OS and TM_QW3_HV_PHYS */ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) { - uint8_t *regs = &tctx->regs[ring]; + uint8_t *sig_regs = &tctx->regs[ring]; Xive2Router *xrtr = XIVE2_ROUTER(tctx->xptr); uint8_t old_cppr, backlog_prio, first_group, group_level; uint8_t pipr_min, lsmfb_min, ring_min; @@ -1127,33 +1110,41 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) uint32_t nvp_idx; Xive2Nvp nvp; int rc; - uint8_t nsr = regs[TM_NSR]; + uint8_t nsr = sig_regs[TM_NSR]; + + g_assert(ring == TM_QW1_OS || ring == TM_QW3_HV_PHYS); + + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_NSR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0); + /* XXX: should show pool IPB for PHYS ring */ trace_xive_tctx_set_cppr(tctx->cs->cpu_index, ring, - regs[TM_IPB], regs[TM_PIPR], + sig_regs[TM_IPB], sig_regs[TM_PIPR], cppr, nsr); if (cppr > XIVE_PRIORITY_MAX) { cppr = 0xff; } - old_cppr = regs[TM_CPPR]; - regs[TM_CPPR] = cppr; + old_cppr = sig_regs[TM_CPPR]; + sig_regs[TM_CPPR] = cppr; /* Handle increased CPPR priority (lower value) */ if (cppr < old_cppr) { - if (cppr <= regs[TM_PIPR]) { + if (cppr <= sig_regs[TM_PIPR]) { /* CPPR lowered below PIPR, must un-present interrupt */ if (xive_nsr_indicates_exception(ring, nsr)) { if (xive_nsr_indicates_group_exception(ring, nsr)) { /* redistribute precluded active grp interrupt */ - xive2_redistribute(xrtr, tctx, ring); + xive2_redistribute(xrtr, tctx, + xive_nsr_exception_ring(ring, nsr)); return; } } /* interrupt is VP directed, pending in IPB */ - regs[TM_PIPR] = cppr; + sig_regs[TM_PIPR] = cppr; xive_tctx_notify(tctx, ring, 0); /* Ensure interrupt is cleared */ return; } else { @@ -1174,9 +1165,9 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) * be adjusted below if needed in case of pending group interrupts. */ again: - pipr_min = xive_ipb_to_pipr(regs[TM_IPB]); - group_enabled = !!regs[TM_LGS]; - lsmfb_min = group_enabled ? regs[TM_LSMFB] : 0xff; + pipr_min = xive_ipb_to_pipr(sig_regs[TM_IPB]); + group_enabled = !!sig_regs[TM_LGS]; + lsmfb_min = group_enabled ? sig_regs[TM_LSMFB] : 0xff; ring_min = ring; group_level = 0; @@ -1265,7 +1256,7 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) } /* PIPR should not be set to a value greater than CPPR */ - regs[TM_PIPR] = (pipr_min > cppr) ? cppr : pipr_min; + sig_regs[TM_PIPR] = (pipr_min > cppr) ? cppr : pipr_min; /* CPPR has changed, check if we need to raise a pending exception */ xive_tctx_notify(tctx, ring_min, group_level); @@ -1490,9 +1481,7 @@ int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority) { - /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ - uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; - uint8_t *alt_regs = &tctx->regs[alt_ring]; + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); /* * The xive2_presenter_tctx_match() above tells if there's a match @@ -1500,7 +1489,7 @@ bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority) * priority to know if the thread can take the interrupt now or if * it is precluded. */ - if (priority < alt_regs[TM_PIPR]) { + if (priority < sig_regs[TM_PIPR]) { return false; } return true; @@ -1640,14 +1629,13 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, &match)) { XiveTCTX *tctx = match.tctx; uint8_t ring = match.ring; - uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; - uint8_t *aregs = &tctx->regs[alt_ring]; - uint8_t nsr = aregs[TM_NSR]; + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); + uint8_t nsr = sig_regs[TM_NSR]; uint8_t group_level; - if (priority < aregs[TM_PIPR] && - xive_nsr_indicates_group_exception(alt_ring, nsr)) { - xive2_redistribute(xrtr, tctx, alt_ring); + if (priority < sig_regs[TM_PIPR] && + xive_nsr_indicates_group_exception(ring, nsr)) { + xive2_redistribute(xrtr, tctx, xive_nsr_exception_ring(ring, nsr)); } group_level = xive_get_group_level(crowd, cam_ignore, nvx_blk, nvx_idx); diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 0d6b11e818c14..a3c2f50ecef7c 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -539,7 +539,7 @@ static inline uint8_t xive_ipb_to_pipr(uint8_t ibp) } /* - * XIVE Thread Interrupt Management Aera (TIMA) + * XIVE Thread Interrupt Management Area (TIMA) * * This region gives access to the registers of the thread interrupt * management context. It is four page wide, each page providing a @@ -551,6 +551,30 @@ static inline uint8_t xive_ipb_to_pipr(uint8_t ibp) #define XIVE_TM_OS_PAGE 0x2 #define XIVE_TM_USER_PAGE 0x3 +/* + * The TCTX (TIMA) has 4 rings (phys, pool, os, user), but only signals + * (raises an interrupt on) the CPU from 3 of them. Phys and pool both + * cause a hypervisor privileged interrupt so interrupts presented on + * those rings signal using the phys ring. This helper returns the signal + * regs from the given ring. + */ +static inline uint8_t *xive_tctx_signal_regs(XiveTCTX *tctx, uint8_t ring) +{ + /* + * This is a good point to add invariants to ensure nothing has tried to + * signal using the POOL ring. + */ + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_NSR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0); + + if (ring == TM_QW2_HV_POOL) { + /* POOL and PHYS rings share the signal regs (PIPR, NSR, CPPR) */ + ring = TM_QW3_HV_PHYS; + } + return &tctx->regs[ring]; +} + void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, From cf454eaa96e8a0c3c1de63b0f7b85542d7c5ecbf Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:43 +1000 Subject: [PATCH 0057/1794] ppc/xive: tctx_accept only lower irq line if an interrupt was presented MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The relationship between an interrupt signaled in the TIMA and the QEMU irq line to the processor to be 1:1, so they should be raised and lowered together and "just in case" lowering should be avoided (it could mask Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-35-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 119a178f2e23f..db26dae7dbf46 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -95,8 +95,6 @@ uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t sig_ring) g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0); g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0); - qemu_irq_lower(xive_tctx_output(tctx, sig_ring)); - if (xive_nsr_indicates_exception(sig_ring, nsr)) { uint8_t cppr = sig_regs[TM_PIPR]; uint8_t ring; @@ -117,6 +115,7 @@ uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t sig_ring) /* Clear the exception from NSR */ sig_regs[TM_NSR] = 0; + qemu_irq_lower(xive_tctx_output(tctx, sig_ring)); trace_xive_tctx_accept(tctx->cs->cpu_index, ring, regs[TM_IPB], sig_regs[TM_PIPR], From 64a18e0c37a6b5c3d94541ff0599ea84fec998c0 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:44 +1000 Subject: [PATCH 0058/1794] ppc/xive: Add xive_tctx_pipr_set() helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have xive_tctx_notify() also set the new PIPR value and rename it to xive_tctx_pipr_set(). This can replace the last xive_tctx_pipr_update() caller because it does not need to update IPB (it already sets it). Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-36-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 39 +++++++++++---------------------------- hw/intc/xive2.c | 16 +++++++--------- include/hw/ppc/xive.h | 5 ++--- 3 files changed, 20 insertions(+), 40 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index db26dae7dbf46..6ad84f93c77a5 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -125,12 +125,16 @@ uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t sig_ring) return ((uint64_t)nsr << 8) | sig_regs[TM_CPPR]; } -void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level) +/* Change PIPR and calculate NSR and irq based on PIPR, CPPR, group */ +void xive_tctx_pipr_set(XiveTCTX *tctx, uint8_t ring, uint8_t pipr, + uint8_t group_level) { uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; - if (sig_regs[TM_PIPR] < sig_regs[TM_CPPR]) { + sig_regs[TM_PIPR] = pipr; + + if (pipr < sig_regs[TM_CPPR]) { switch (ring) { case TM_QW1_OS: sig_regs[TM_NSR] = TM_QW1_NSR_EO | (group_level & 0x3F); @@ -145,7 +149,7 @@ void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level) g_assert_not_reached(); } trace_xive_tctx_notify(tctx->cs->cpu_index, ring, - regs[TM_IPB], sig_regs[TM_PIPR], + regs[TM_IPB], pipr, sig_regs[TM_CPPR], sig_regs[TM_NSR]); qemu_irq_raise(xive_tctx_output(tctx, ring)); } else { @@ -213,29 +217,10 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) } } - sig_regs[TM_PIPR] = pipr_min; - - /* CPPR has changed, check if we need to raise a pending exception */ - xive_tctx_notify(tctx, ring_min, 0); + /* CPPR has changed, this may present or preclude a pending exception */ + xive_tctx_pipr_set(tctx, ring_min, pipr_min, 0); } -void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, - uint8_t group_level) -{ - uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); - uint8_t *regs = &tctx->regs[ring]; - - if (group_level == 0) { - /* VP-specific */ - regs[TM_IPB] |= xive_priority_to_ipb(priority); - sig_regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); - } else { - /* VP-group */ - sig_regs[TM_PIPR] = xive_priority_to_pipr(priority); - } - xive_tctx_notify(tctx, ring, group_level); - } - static void xive_tctx_pipr_recompute_from_ipb(XiveTCTX *tctx, uint8_t ring) { uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); @@ -244,8 +229,7 @@ static void xive_tctx_pipr_recompute_from_ipb(XiveTCTX *tctx, uint8_t ring) /* Does not support a presented group interrupt */ g_assert(!xive_nsr_indicates_group_exception(ring, sig_regs[TM_NSR])); - sig_regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); - xive_tctx_notify(tctx, ring, 0); + xive_tctx_pipr_set(tctx, ring, xive_ipb_to_pipr(regs[TM_IPB]), 0); } void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority, @@ -264,8 +248,7 @@ void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority, } g_assert(pipr <= xive_ipb_to_pipr(regs[TM_IPB])); g_assert(pipr < sig_regs[TM_PIPR]); - sig_regs[TM_PIPR] = pipr; - xive_tctx_notify(tctx, ring, group_level); + xive_tctx_pipr_set(tctx, ring, pipr, group_level); } /* diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 71b40f702a6f1..0ee50a6bca484 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -966,10 +966,10 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, } /* - * Compute the PIPR based on the restored state. + * Set the PIPR/NSR based on the restored state. * It will raise the External interrupt signal if needed. */ - xive_tctx_pipr_update(tctx, TM_QW1_OS, backlog_prio, backlog_level); + xive_tctx_pipr_set(tctx, TM_QW1_OS, backlog_prio, backlog_level); } /* @@ -1144,8 +1144,7 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) } /* interrupt is VP directed, pending in IPB */ - sig_regs[TM_PIPR] = cppr; - xive_tctx_notify(tctx, ring, 0); /* Ensure interrupt is cleared */ + xive_tctx_pipr_set(tctx, ring, cppr, 0); return; } else { /* CPPR was lowered, but still above PIPR. No action needed. */ @@ -1255,11 +1254,10 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) pipr_min = backlog_prio; } - /* PIPR should not be set to a value greater than CPPR */ - sig_regs[TM_PIPR] = (pipr_min > cppr) ? cppr : pipr_min; - - /* CPPR has changed, check if we need to raise a pending exception */ - xive_tctx_notify(tctx, ring_min, group_level); + if (pipr_min > cppr) { + pipr_min = cppr; + } + xive_tctx_pipr_set(tctx, ring_min, pipr_min, group_level); } void xive2_tm_set_hv_cppr(XivePresenter *xptr, XiveTCTX *tctx, diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index a3c2f50ecef7c..2372d1014bd24 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -584,12 +584,11 @@ void xive_tctx_pic_print_info(XiveTCTX *tctx, GString *buf); Object *xive_tctx_create(Object *cpu, XivePresenter *xptr, Error **errp); void xive_tctx_reset(XiveTCTX *tctx); void xive_tctx_destroy(XiveTCTX *tctx); -void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, - uint8_t group_level); +void xive_tctx_pipr_set(XiveTCTX *tctx, uint8_t ring, uint8_t priority, + uint8_t group_level); void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority, uint8_t group_level); void xive_tctx_reset_signal(XiveTCTX *tctx, uint8_t ring); -void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level); uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring); /* From 384f0365abae49b3d6a1cd46e8ae3cf4b9d4013e Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:45 +1000 Subject: [PATCH 0059/1794] ppc/xive2: split tctx presentation processing from set CPPR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The second part of the set CPPR operation is to process (or re-present) any pending interrupts after CPPR is adjusted. Split this presentation processing out into a standalone function that can be used in other places. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-37-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 137 +++++++++++++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 61 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 0ee50a6bca484..c7356c5b2fd86 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1098,66 +1098,19 @@ void xive2_tm_ack_os_el(XivePresenter *xptr, XiveTCTX *tctx, xive2_tctx_accept_el(xptr, tctx, TM_QW1_OS, TM_QW1_OS); } -/* NOTE: CPPR only exists for TM_QW1_OS and TM_QW3_HV_PHYS */ -static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) +/* Re-calculate and present pending interrupts */ +static void xive2_tctx_process_pending(XiveTCTX *tctx, uint8_t sig_ring) { - uint8_t *sig_regs = &tctx->regs[ring]; + uint8_t *sig_regs = &tctx->regs[sig_ring]; Xive2Router *xrtr = XIVE2_ROUTER(tctx->xptr); - uint8_t old_cppr, backlog_prio, first_group, group_level; + uint8_t backlog_prio, first_group, group_level; uint8_t pipr_min, lsmfb_min, ring_min; + uint8_t cppr = sig_regs[TM_CPPR]; bool group_enabled; - uint8_t nvp_blk; - uint32_t nvp_idx; Xive2Nvp nvp; int rc; - uint8_t nsr = sig_regs[TM_NSR]; - - g_assert(ring == TM_QW1_OS || ring == TM_QW3_HV_PHYS); - - g_assert(tctx->regs[TM_QW2_HV_POOL + TM_NSR] == 0); - g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0); - g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0); - - /* XXX: should show pool IPB for PHYS ring */ - trace_xive_tctx_set_cppr(tctx->cs->cpu_index, ring, - sig_regs[TM_IPB], sig_regs[TM_PIPR], - cppr, nsr); - - if (cppr > XIVE_PRIORITY_MAX) { - cppr = 0xff; - } - - old_cppr = sig_regs[TM_CPPR]; - sig_regs[TM_CPPR] = cppr; - - /* Handle increased CPPR priority (lower value) */ - if (cppr < old_cppr) { - if (cppr <= sig_regs[TM_PIPR]) { - /* CPPR lowered below PIPR, must un-present interrupt */ - if (xive_nsr_indicates_exception(ring, nsr)) { - if (xive_nsr_indicates_group_exception(ring, nsr)) { - /* redistribute precluded active grp interrupt */ - xive2_redistribute(xrtr, tctx, - xive_nsr_exception_ring(ring, nsr)); - return; - } - } - /* interrupt is VP directed, pending in IPB */ - xive_tctx_pipr_set(tctx, ring, cppr, 0); - return; - } else { - /* CPPR was lowered, but still above PIPR. No action needed. */ - return; - } - } - - /* CPPR didn't change, nothing needs to be done */ - if (cppr == old_cppr) { - return; - } - - /* CPPR priority decreased (higher value) */ + g_assert(sig_ring == TM_QW3_HV_PHYS || sig_ring == TM_QW1_OS); /* * Recompute the PIPR based on local pending interrupts. It will @@ -1167,11 +1120,11 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) pipr_min = xive_ipb_to_pipr(sig_regs[TM_IPB]); group_enabled = !!sig_regs[TM_LGS]; lsmfb_min = group_enabled ? sig_regs[TM_LSMFB] : 0xff; - ring_min = ring; + ring_min = sig_ring; group_level = 0; /* PHYS updates also depend on POOL values */ - if (ring == TM_QW3_HV_PHYS) { + if (sig_ring == TM_QW3_HV_PHYS) { uint8_t *pool_regs = &tctx->regs[TM_QW2_HV_POOL]; /* POOL values only matter if POOL ctx is valid */ @@ -1201,20 +1154,25 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) } } - rc = xive2_tctx_get_nvp_indexes(tctx, ring_min, &nvp_blk, &nvp_idx); - if (rc) { - qemu_log_mask(LOG_GUEST_ERROR, "XIVE: set CPPR on invalid context\n"); - return; - } - if (group_enabled && lsmfb_min < cppr && lsmfb_min < pipr_min) { + + uint8_t nvp_blk; + uint32_t nvp_idx; + /* * Thread has seen a group interrupt with a higher priority * than the new cppr or pending local interrupt. Check the * backlog */ + rc = xive2_tctx_get_nvp_indexes(tctx, ring_min, &nvp_blk, &nvp_idx); + if (rc) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: set CPPR on invalid " + "context\n"); + return; + } + if (xive2_router_get_nvp(xrtr, nvp_blk, nvp_idx, &nvp)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVP %x/%x\n", nvp_blk, nvp_idx); @@ -1260,6 +1218,63 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) xive_tctx_pipr_set(tctx, ring_min, pipr_min, group_level); } +/* NOTE: CPPR only exists for TM_QW1_OS and TM_QW3_HV_PHYS */ +static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t sig_ring, uint8_t cppr) +{ + uint8_t *sig_regs = &tctx->regs[sig_ring]; + Xive2Router *xrtr = XIVE2_ROUTER(tctx->xptr); + uint8_t old_cppr; + uint8_t nsr = sig_regs[TM_NSR]; + + g_assert(sig_ring == TM_QW1_OS || sig_ring == TM_QW3_HV_PHYS); + + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_NSR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0); + + /* XXX: should show pool IPB for PHYS ring */ + trace_xive_tctx_set_cppr(tctx->cs->cpu_index, sig_ring, + sig_regs[TM_IPB], sig_regs[TM_PIPR], + cppr, nsr); + + if (cppr > XIVE_PRIORITY_MAX) { + cppr = 0xff; + } + + old_cppr = sig_regs[TM_CPPR]; + sig_regs[TM_CPPR] = cppr; + + /* Handle increased CPPR priority (lower value) */ + if (cppr < old_cppr) { + if (cppr <= sig_regs[TM_PIPR]) { + /* CPPR lowered below PIPR, must un-present interrupt */ + if (xive_nsr_indicates_exception(sig_ring, nsr)) { + if (xive_nsr_indicates_group_exception(sig_ring, nsr)) { + /* redistribute precluded active grp interrupt */ + xive2_redistribute(xrtr, tctx, + xive_nsr_exception_ring(sig_ring, nsr)); + return; + } + } + + /* interrupt is VP directed, pending in IPB */ + xive_tctx_pipr_set(tctx, sig_ring, cppr, 0); + return; + } else { + /* CPPR was lowered, but still above PIPR. No action needed. */ + return; + } + } + + /* CPPR didn't change, nothing needs to be done */ + if (cppr == old_cppr) { + return; + } + + /* CPPR priority decreased (higher value) */ + xive2_tctx_process_pending(tctx, sig_ring); +} + void xive2_tm_set_hv_cppr(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size) { From 04627e2206dce95ecda486abf70d0ef7dd9f39f4 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:46 +1000 Subject: [PATCH 0060/1794] ppc/xive2: Consolidate presentation processing in context push MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OS-push operation must re-present pending interrupts. Use the newly created xive2_tctx_process_pending() function instead of duplicating the logic. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-38-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 42 ++++++++++-------------------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index c7356c5b2fd86..be945bef1c532 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -903,18 +903,14 @@ static uint8_t xive2_tctx_restore_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx, return cppr; } +static void xive2_tctx_process_pending(XiveTCTX *tctx, uint8_t sig_ring); + static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t nvp_blk, uint32_t nvp_idx, bool do_restore) { - XivePresenter *xptr = XIVE_PRESENTER(xrtr); - uint8_t ipb; - uint8_t backlog_level; - uint8_t group_level; - uint8_t first_group; - uint8_t backlog_prio; - uint8_t group_prio; uint8_t *regs = &tctx->regs[TM_QW1_OS]; + uint8_t ipb; Xive2Nvp nvp; /* @@ -946,30 +942,8 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, } /* IPB bits in the backlog are merged with the TIMA IPB bits */ regs[TM_IPB] |= ipb; - backlog_prio = xive_ipb_to_pipr(regs[TM_IPB]); - backlog_level = 0; - - first_group = xive_get_field32(NVP2_W0_PGOFIRST, nvp.w0); - if (first_group && regs[TM_LSMFB] < backlog_prio) { - group_prio = xive2_presenter_backlog_scan(xptr, nvp_blk, nvp_idx, - first_group, &group_level); - regs[TM_LSMFB] = group_prio; - if (regs[TM_LGS] && group_prio < backlog_prio && - group_prio < regs[TM_CPPR]) { - - /* VP can take a group interrupt */ - xive2_presenter_backlog_decr(xptr, nvp_blk, nvp_idx, - group_prio, group_level); - backlog_prio = group_prio; - backlog_level = group_level; - } - } - /* - * Set the PIPR/NSR based on the restored state. - * It will raise the External interrupt signal if needed. - */ - xive_tctx_pipr_set(tctx, TM_QW1_OS, backlog_prio, backlog_level); + xive2_tctx_process_pending(tctx, TM_QW1_OS); } /* @@ -1103,8 +1077,12 @@ static void xive2_tctx_process_pending(XiveTCTX *tctx, uint8_t sig_ring) { uint8_t *sig_regs = &tctx->regs[sig_ring]; Xive2Router *xrtr = XIVE2_ROUTER(tctx->xptr); - uint8_t backlog_prio, first_group, group_level; - uint8_t pipr_min, lsmfb_min, ring_min; + uint8_t backlog_prio; + uint8_t first_group; + uint8_t group_level; + uint8_t pipr_min; + uint8_t lsmfb_min; + uint8_t ring_min; uint8_t cppr = sig_regs[TM_CPPR]; bool group_enabled; Xive2Nvp nvp; From 370ea4a4b6fffc30324fc8f8134483e5a749114d Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:47 +1000 Subject: [PATCH 0061/1794] ppc/xive2: Avoid needless interrupt re-check on CPPR set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When CPPR priority is decreased, pending interrupts do not need to be re-checked if one is already presented because by definition that will be the highest priority. This prevents a presented group interrupt from being lost. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-39-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index be945bef1c532..531e6517baa29 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1250,7 +1250,9 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t sig_ring, uint8_t cppr) } /* CPPR priority decreased (higher value) */ - xive2_tctx_process_pending(tctx, sig_ring); + if (!xive_nsr_indicates_exception(sig_ring, nsr)) { + xive2_tctx_process_pending(tctx, sig_ring); + } } void xive2_tm_set_hv_cppr(XivePresenter *xptr, XiveTCTX *tctx, From 203181cebdb96283520b496d6eaa49634eb51579 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:48 +1000 Subject: [PATCH 0062/1794] ppc/xive: Assert group interrupts were redistributed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add some assertions to try to ensure presented group interrupts do not get lost without being redistributed, if they become precluded by CPPR or preempted by a higher priority interrupt. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-40-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 2 ++ hw/intc/xive2.c | 1 + 2 files changed, 3 insertions(+) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 6ad84f93c77a5..d609d552e89ea 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -132,6 +132,8 @@ void xive_tctx_pipr_set(XiveTCTX *tctx, uint8_t ring, uint8_t pipr, uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; + g_assert(!xive_nsr_indicates_group_exception(ring, sig_regs[TM_NSR])); + sig_regs[TM_PIPR] = pipr; if (pipr < sig_regs[TM_CPPR]) { diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 531e6517baa29..a0a6b1a88179f 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1089,6 +1089,7 @@ static void xive2_tctx_process_pending(XiveTCTX *tctx, uint8_t sig_ring) int rc; g_assert(sig_ring == TM_QW3_HV_PHYS || sig_ring == TM_QW1_OS); + g_assert(!xive_nsr_indicates_group_exception(sig_ring, sig_regs[TM_NSR])); /* * Recompute the PIPR based on local pending interrupts. It will From 365e322cfb86b2e7131c3290c3a61f8d2bb224d3 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:49 +1000 Subject: [PATCH 0063/1794] ppc/xive2: implement NVP context save restore for POOL ring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to implement POOL context push, add support for POOL NVP context save/restore. The NVP p bit is defined in the spec as follows: If TRUE, the CPPR of a Pool VP in the NVP is updated during store of the context with the CPPR of the Hard context it was running under. It's not clear whether non-pool VPs always or never get CPPR updated. Before this patch, OS contexts always save CPPR, so we will assume that is the behaviour. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-41-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 51 +++++++++++++++++++++++++------------ include/hw/ppc/xive2_regs.h | 1 + 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index a0a6b1a88179f..7631d48862064 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -512,12 +512,13 @@ static void xive2_presenter_backlog_decr(XivePresenter *xptr, */ static void xive2_tctx_save_ctx(Xive2Router *xrtr, XiveTCTX *tctx, - uint8_t nvp_blk, uint32_t nvp_idx, - uint8_t ring) + uint8_t ring, + uint8_t nvp_blk, uint32_t nvp_idx) { CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env; uint32_t pir = env->spr_cb[SPR_PIR].default_value; Xive2Nvp nvp; + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; if (xive2_router_get_nvp(xrtr, nvp_blk, nvp_idx, &nvp)) { @@ -553,7 +554,14 @@ static void xive2_tctx_save_ctx(Xive2Router *xrtr, XiveTCTX *tctx, } nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, regs[TM_IPB]); - nvp.w2 = xive_set_field32(NVP2_W2_CPPR, nvp.w2, regs[TM_CPPR]); + + if ((nvp.w0 & NVP2_W0_P) || ring != TM_QW2_HV_POOL) { + /* + * Non-pool contexts always save CPPR (ignore p bit). XXX: Clarify + * whether that is the correct behaviour. + */ + nvp.w2 = xive_set_field32(NVP2_W2_CPPR, nvp.w2, sig_regs[TM_CPPR]); + } if (nvp.w0 & NVP2_W0_L) { /* * Typically not used. If LSMFB is restored with 0, it will @@ -722,7 +730,7 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, } if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) { - xive2_tctx_save_ctx(xrtr, tctx, nvp_blk, nvp_idx, ring); + xive2_tctx_save_ctx(xrtr, tctx, ring, nvp_blk, nvp_idx); } /* @@ -863,12 +871,15 @@ void xive2_tm_pull_phys_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, xive2_tm_pull_ctx_ol(xptr, tctx, offset, value, size, TM_QW3_HV_PHYS); } -static uint8_t xive2_tctx_restore_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx, - uint8_t nvp_blk, uint32_t nvp_idx, - Xive2Nvp *nvp) +static uint8_t xive2_tctx_restore_ctx(Xive2Router *xrtr, XiveTCTX *tctx, + uint8_t ring, + uint8_t nvp_blk, uint32_t nvp_idx, + Xive2Nvp *nvp) { CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env; uint32_t pir = env->spr_cb[SPR_PIR].default_value; + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); + uint8_t *regs = &tctx->regs[ring]; uint8_t cppr; if (!xive2_nvp_is_hw(nvp)) { @@ -881,10 +892,10 @@ static uint8_t xive2_tctx_restore_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx, nvp->w2 = xive_set_field32(NVP2_W2_CPPR, nvp->w2, 0); xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, nvp, 2); - tctx->regs[TM_QW1_OS + TM_CPPR] = cppr; - tctx->regs[TM_QW1_OS + TM_LSMFB] = xive_get_field32(NVP2_W2_LSMFB, nvp->w2); - tctx->regs[TM_QW1_OS + TM_LGS] = xive_get_field32(NVP2_W2_LGS, nvp->w2); - tctx->regs[TM_QW1_OS + TM_T] = xive_get_field32(NVP2_W2_T, nvp->w2); + sig_regs[TM_CPPR] = cppr; + regs[TM_LSMFB] = xive_get_field32(NVP2_W2_LSMFB, nvp->w2); + regs[TM_LGS] = xive_get_field32(NVP2_W2_LGS, nvp->w2); + regs[TM_T] = xive_get_field32(NVP2_W2_T, nvp->w2); nvp->w1 = xive_set_field32(NVP2_W1_CO, nvp->w1, 1); nvp->w1 = xive_set_field32(NVP2_W1_CO_THRID_VALID, nvp->w1, 1); @@ -893,9 +904,18 @@ static uint8_t xive2_tctx_restore_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx, /* * Checkout privilege: 0:OS, 1:Pool, 2:Hard * - * TODO: we only support OS push/pull + * TODO: we don't support hard push/pull */ - nvp->w1 = xive_set_field32(NVP2_W1_CO_PRIV, nvp->w1, 0); + switch (ring) { + case TM_QW1_OS: + nvp->w1 = xive_set_field32(NVP2_W1_CO_PRIV, nvp->w1, 0); + break; + case TM_QW2_HV_POOL: + nvp->w1 = xive_set_field32(NVP2_W1_CO_PRIV, nvp->w1, 1); + break; + default: + g_assert_not_reached(); + } xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, nvp, 1); @@ -930,9 +950,8 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, } /* Automatically restore thread context registers */ - if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && - do_restore) { - xive2_tctx_restore_os_ctx(xrtr, tctx, nvp_blk, nvp_idx, &nvp); + if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_restore) { + xive2_tctx_restore_ctx(xrtr, tctx, TM_QW1_OS, nvp_blk, nvp_idx, &nvp); } ipb = xive_get_field32(NVP2_W2_IPB, nvp.w2); diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index f82054661bdad..2a3e60abadbf9 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -158,6 +158,7 @@ typedef struct Xive2Nvp { #define NVP2_W0_L PPC_BIT32(8) #define NVP2_W0_G PPC_BIT32(9) #define NVP2_W0_T PPC_BIT32(10) +#define NVP2_W0_P PPC_BIT32(11) #define NVP2_W0_ESC_END PPC_BIT32(25) /* 'N' bit 0:ESB 1:END */ #define NVP2_W0_PGOFIRST PPC_BITMASK32(26, 31) uint32_t w1; From 7a40b50757b55c2d4233c304f32df5afdd1bd63a Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:50 +1000 Subject: [PATCH 0064/1794] ppc/xive2: Prevent pulling of pool context losing phys interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the pool context is pulled, the shared pool/phys signal is reset, which loses the qemu irq if a phys interrupt was presented. Only reset the signal if a poll irq was presented. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-42-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 7631d48862064..112397459afed 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -727,20 +727,22 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, xive2_redistribute(xrtr, tctx, cur_ring); } } + + /* + * Lower external interrupt line of requested ring and below except for + * USER, which doesn't exist. + */ + if (xive_nsr_indicates_exception(cur_ring, nsr)) { + if (cur_ring == xive_nsr_exception_ring(cur_ring, nsr)) { + xive_tctx_reset_signal(tctx, cur_ring); + } + } } if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) { xive2_tctx_save_ctx(xrtr, tctx, ring, nvp_blk, nvp_idx); } - /* - * Lower external interrupt line of requested ring and below except for - * USER, which doesn't exist. - */ - for (cur_ring = TM_QW1_OS; cur_ring <= ring; - cur_ring += XIVE_TM_RING_SIZE) { - xive_tctx_reset_signal(tctx, cur_ring); - } return target_ringw2; } From 565e6d4d2151e856026ee60d16c12a61e667cd15 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:51 +1000 Subject: [PATCH 0065/1794] ppc/xive: Redistribute phys after pulling of pool context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After pulling the pool context, if a pool irq had been presented and was cleared in the process, there could be a pending irq in phys that should be presented. Process the phys irq ring after pulling pool ring to catch this case and avoid losing irqs. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-43-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 3 +++ hw/intc/xive2.c | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index d609d552e89ea..50a38bbf2ef9d 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -320,6 +320,9 @@ static uint64_t xive_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, xive_tctx_reset_signal(tctx, TM_QW1_OS); xive_tctx_reset_signal(tctx, TM_QW2_HV_POOL); + /* Re-check phys for interrupts if pool was disabled */ + xive_tctx_pipr_recompute_from_ipb(tctx, TM_QW3_HV_PHYS); + return qw2w2; } diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 112397459afed..e3f9ff384a1a9 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -683,6 +683,8 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) xive_tctx_reset_signal(tctx, ring); } +static void xive2_tctx_process_pending(XiveTCTX *tctx, uint8_t sig_ring); + static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size, uint8_t ring) { @@ -739,6 +741,18 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, } } + if (ring == TM_QW2_HV_POOL) { + /* Re-check phys for interrupts if pool was disabled */ + nsr = tctx->regs[TM_QW3_HV_PHYS + TM_NSR]; + if (xive_nsr_indicates_exception(TM_QW3_HV_PHYS, nsr)) { + /* Ring must be PHYS because POOL would have been redistributed */ + g_assert(xive_nsr_exception_ring(TM_QW3_HV_PHYS, nsr) == + TM_QW3_HV_PHYS); + } else { + xive2_tctx_process_pending(tctx, TM_QW3_HV_PHYS); + } + } + if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) { xive2_tctx_save_ctx(xrtr, tctx, ring, nvp_blk, nvp_idx); } @@ -925,8 +939,6 @@ static uint8_t xive2_tctx_restore_ctx(Xive2Router *xrtr, XiveTCTX *tctx, return cppr; } -static void xive2_tctx_process_pending(XiveTCTX *tctx, uint8_t sig_ring); - static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t nvp_blk, uint32_t nvp_idx, bool do_restore) From ca0081ef7ece566e877e9f4ceab9e9988d08fb78 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:52 +1000 Subject: [PATCH 0066/1794] ppc/xive: Check TIMA operations validity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Certain TIMA operations should only be performed when a ring is valid, others when the ring is invalid, and they are considered undefined if used incorrectly. Add checks for this condition. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-44-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 196 +++++++++++++++++++++++++----------------- include/hw/ppc/xive.h | 1 + 2 files changed, 116 insertions(+), 81 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 50a38bbf2ef9d..6589c0a523c94 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -25,6 +25,19 @@ /* * XIVE Thread Interrupt Management context */ +bool xive_ring_valid(XiveTCTX *tctx, uint8_t ring) +{ + uint8_t cur_ring; + + for (cur_ring = ring; cur_ring <= TM_QW3_HV_PHYS; + cur_ring += XIVE_TM_RING_SIZE) { + if (!(tctx->regs[cur_ring + TM_WORD2] & 0x80)) { + return false; + } + } + return true; +} + bool xive_nsr_indicates_exception(uint8_t ring, uint8_t nsr) { switch (ring) { @@ -663,6 +676,8 @@ typedef struct XiveTmOp { uint8_t page_offset; uint32_t op_offset; unsigned size; + bool hw_ok; + bool sw_ok; void (*write_handler)(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); @@ -675,34 +690,34 @@ static const XiveTmOp xive_tm_operations[] = { * MMIOs below 2K : raw values and special operations without side * effects */ - { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive_tm_set_os_cppr, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, xive_tm_push_os_ctx, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive_tm_set_hv_cppr, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, xive_tm_vt_push, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, NULL, - xive_tm_vt_poll }, + { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, true, true, + xive_tm_set_os_cppr, NULL }, + { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, true, true, + xive_tm_push_os_ctx, NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, true, true, + xive_tm_set_hv_cppr, NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, false, true, + xive_tm_vt_push, NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, true, true, + NULL, xive_tm_vt_poll }, /* MMIOs above 2K : special operations with side effects */ - { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, NULL, - xive_tm_ack_os_reg }, - { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, xive_tm_set_os_pending, - NULL }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 4, NULL, - xive_tm_pull_os_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, NULL, - xive_tm_pull_os_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, NULL, - xive_tm_ack_hv_reg }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, NULL, - xive_tm_pull_pool_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, NULL, - xive_tm_pull_pool_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX, 1, NULL, - xive_tm_pull_phys_ctx }, + { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, true, false, + NULL, xive_tm_ack_os_reg }, + { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, true, false, + xive_tm_set_os_pending, NULL }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 4, true, false, + NULL, xive_tm_pull_os_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, true, false, + NULL, xive_tm_pull_os_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, true, false, + NULL, xive_tm_ack_hv_reg }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, true, false, + NULL, xive_tm_pull_pool_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, true, false, + NULL, xive_tm_pull_pool_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX, 1, true, false, + NULL, xive_tm_pull_phys_ctx }, }; static const XiveTmOp xive2_tm_operations[] = { @@ -710,52 +725,48 @@ static const XiveTmOp xive2_tm_operations[] = { * MMIOs below 2K : raw values and special operations without side * effects */ - { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive2_tm_set_os_cppr, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, xive2_tm_push_os_ctx, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 8, xive2_tm_push_os_ctx, - NULL }, - { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_LGS, 1, xive_tm_set_os_lgs, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive2_tm_set_hv_cppr, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, xive_tm_vt_push, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, NULL, - xive_tm_vt_poll }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_T, 1, xive2_tm_set_hv_target, - NULL }, + { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, true, true, + xive2_tm_set_os_cppr, NULL }, + { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, true, true, + xive2_tm_push_os_ctx, NULL }, + { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 8, true, true, + xive2_tm_push_os_ctx, NULL }, + { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_LGS, 1, true, true, + xive_tm_set_os_lgs, NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, true, true, + xive2_tm_set_hv_cppr, NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, true, true, + NULL, xive_tm_vt_poll }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_T, 1, true, true, + xive2_tm_set_hv_target, NULL }, /* MMIOs above 2K : special operations with side effects */ - { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, NULL, - xive_tm_ack_os_reg }, - { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, xive_tm_set_os_pending, - NULL }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_G2, 4, NULL, - xive2_tm_pull_os_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 4, NULL, - xive2_tm_pull_os_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, NULL, - xive2_tm_pull_os_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, NULL, - xive_tm_ack_hv_reg }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX_G2, 4, NULL, - xive2_tm_pull_pool_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, NULL, - xive2_tm_pull_pool_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, NULL, - xive2_tm_pull_pool_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_OL, 1, xive2_tm_pull_os_ctx_ol, - NULL }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_G2, 4, NULL, - xive2_tm_pull_phys_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX, 1, NULL, - xive2_tm_pull_phys_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_OL, 1, xive2_tm_pull_phys_ctx_ol, - NULL }, - { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_EL, 1, xive2_tm_ack_os_el, - NULL }, + { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, true, false, + NULL, xive_tm_ack_os_reg }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_G2, 4, true, false, + NULL, xive2_tm_pull_os_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 4, true, false, + NULL, xive2_tm_pull_os_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, true, false, + NULL, xive2_tm_pull_os_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, true, false, + NULL, xive_tm_ack_hv_reg }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX_G2, 4, true, false, + NULL, xive2_tm_pull_pool_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, true, false, + NULL, xive2_tm_pull_pool_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, true, false, + NULL, xive2_tm_pull_pool_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_OL, 1, true, false, + xive2_tm_pull_os_ctx_ol, NULL }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_G2, 4, true, false, + NULL, xive2_tm_pull_phys_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX, 1, true, false, + NULL, xive2_tm_pull_phys_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_OL, 1, true, false, + xive2_tm_pull_phys_ctx_ol, NULL }, + { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_EL, 1, true, false, + xive2_tm_ack_os_el, NULL }, }; static const XiveTmOp *xive_tm_find_op(XivePresenter *xptr, hwaddr offset, @@ -797,18 +808,28 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size) { const XiveTmOp *xto; + uint8_t ring = offset & TM_RING_OFFSET; + bool is_valid = xive_ring_valid(tctx, ring); + bool hw_owned = is_valid; trace_xive_tctx_tm_write(tctx->cs->cpu_index, offset, size, value); - /* - * TODO: check V bit in Q[0-3]W2 - */ - /* * First, check for special operations in the 2K region */ + xto = xive_tm_find_op(tctx->xptr, offset, size, true); + if (xto) { + if (hw_owned && !xto->hw_ok) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: undefined write to HW TIMA " + "@%"HWADDR_PRIx" size %d\n", offset, size); + } + if (!hw_owned && !xto->sw_ok) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: undefined write to SW TIMA " + "@%"HWADDR_PRIx" size %d\n", offset, size); + } + } + if (offset & TM_SPECIAL_OP) { - xto = xive_tm_find_op(tctx->xptr, offset, size, true); if (!xto) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid write access at TIMA " "@%"HWADDR_PRIx" size %d\n", offset, size); @@ -821,7 +842,6 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, /* * Then, for special operations in the region below 2K. */ - xto = xive_tm_find_op(tctx->xptr, offset, size, true); if (xto) { xto->write_handler(xptr, tctx, offset, value, size); return; @@ -830,6 +850,11 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, /* * Finish with raw access to the register values */ + if (hw_owned) { + /* Store context operations are dangerous when context is valid */ + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: undefined write to HW TIMA " + "@%"HWADDR_PRIx" size %d\n", offset, size); + } xive_tm_raw_write(tctx, offset, value, size); } @@ -837,17 +862,27 @@ uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size) { const XiveTmOp *xto; + uint8_t ring = offset & TM_RING_OFFSET; + bool is_valid = xive_ring_valid(tctx, ring); + bool hw_owned = is_valid; uint64_t ret; - /* - * TODO: check V bit in Q[0-3]W2 - */ + xto = xive_tm_find_op(tctx->xptr, offset, size, false); + if (xto) { + if (hw_owned && !xto->hw_ok) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: undefined read to HW TIMA " + "@%"HWADDR_PRIx" size %d\n", offset, size); + } + if (!hw_owned && !xto->sw_ok) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: undefined read to SW TIMA " + "@%"HWADDR_PRIx" size %d\n", offset, size); + } + } /* * First, check for special operations in the 2K region */ if (offset & TM_SPECIAL_OP) { - xto = xive_tm_find_op(tctx->xptr, offset, size, false); if (!xto) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid read access to TIMA" "@%"HWADDR_PRIx" size %d\n", offset, size); @@ -860,7 +895,6 @@ uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, /* * Then, for special operations in the region below 2K. */ - xto = xive_tm_find_op(tctx->xptr, offset, size, false); if (xto) { ret = xto->read_handler(xptr, tctx, offset, size); goto out; diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 2372d1014bd24..b7ca8544e4314 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -365,6 +365,7 @@ static inline uint32_t xive_tctx_word2(uint8_t *ring) return *((uint32_t *) &ring[TM_WORD2]); } +bool xive_ring_valid(XiveTCTX *tctx, uint8_t ring); bool xive_nsr_indicates_exception(uint8_t ring, uint8_t nsr); bool xive_nsr_indicates_group_exception(uint8_t ring, uint8_t nsr); uint8_t xive_nsr_exception_ring(uint8_t ring, uint8_t nsr); From ba127a1e48d34f6c805000dc881e240fc4f2bd1d Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:53 +1000 Subject: [PATCH 0067/1794] ppc/xive2: Implement pool context push TIMA op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement pool context push TIMA op. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-45-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 4 ++++ hw/intc/xive2.c | 50 ++++++++++++++++++++++++++++-------------- include/hw/ppc/xive2.h | 2 ++ 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 6589c0a523c94..e7f77be2f7116 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -733,6 +733,10 @@ static const XiveTmOp xive2_tm_operations[] = { xive2_tm_push_os_ctx, NULL }, { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_LGS, 1, true, true, xive_tm_set_os_lgs, NULL }, + { XIVE_TM_HV_PAGE, TM_QW2_HV_POOL + TM_WORD2, 4, true, true, + xive2_tm_push_pool_ctx, NULL }, + { XIVE_TM_HV_PAGE, TM_QW2_HV_POOL + TM_WORD2, 8, true, true, + xive2_tm_push_pool_ctx, NULL }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, true, true, xive2_tm_set_hv_cppr, NULL }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, true, true, diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index e3f9ff384a1a9..4244e1d02b618 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -583,6 +583,7 @@ static void xive2_tctx_save_ctx(Xive2Router *xrtr, XiveTCTX *tctx, xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 1); } +/* POOL cam is the same as OS cam encoding */ static void xive2_cam_decode(uint32_t cam, uint8_t *nvp_blk, uint32_t *nvp_idx, bool *valid, bool *hw) { @@ -940,10 +941,11 @@ static uint8_t xive2_tctx_restore_ctx(Xive2Router *xrtr, XiveTCTX *tctx, } static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, + uint8_t ring, uint8_t nvp_blk, uint32_t nvp_idx, bool do_restore) { - uint8_t *regs = &tctx->regs[TM_QW1_OS]; + uint8_t *regs = &tctx->regs[ring]; uint8_t ipb; Xive2Nvp nvp; @@ -965,7 +967,7 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, /* Automatically restore thread context registers */ if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_restore) { - xive2_tctx_restore_ctx(xrtr, tctx, TM_QW1_OS, nvp_blk, nvp_idx, &nvp); + xive2_tctx_restore_ctx(xrtr, tctx, ring, nvp_blk, nvp_idx, &nvp); } ipb = xive_get_field32(NVP2_W2_IPB, nvp.w2); @@ -976,48 +978,62 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, /* IPB bits in the backlog are merged with the TIMA IPB bits */ regs[TM_IPB] |= ipb; - xive2_tctx_process_pending(tctx, TM_QW1_OS); + xive2_tctx_process_pending(tctx, ring == TM_QW2_HV_POOL ? + TM_QW3_HV_PHYS : ring); } /* - * Updating the OS CAM line can trigger a resend of interrupt + * Updating the ring CAM line can trigger a resend of interrupt */ -void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, - hwaddr offset, uint64_t value, unsigned size) +static void xive2_tm_push_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size, + uint8_t ring) { uint32_t cam; - uint32_t qw1w2; - uint64_t qw1dw1; + uint32_t w2; + uint64_t dw1; uint8_t nvp_blk; uint32_t nvp_idx; - bool vo; + bool v; bool do_restore; /* First update the thead context */ switch (size) { case 4: cam = value; - qw1w2 = cpu_to_be32(cam); - memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1w2, 4); + w2 = cpu_to_be32(cam); + memcpy(&tctx->regs[ring + TM_WORD2], &w2, 4); break; case 8: cam = value >> 32; - qw1dw1 = cpu_to_be64(value); - memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1dw1, 8); + dw1 = cpu_to_be64(value); + memcpy(&tctx->regs[ring + TM_WORD2], &dw1, 8); break; default: g_assert_not_reached(); } - xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &vo, &do_restore); + xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &v, &do_restore); /* Check the interrupt pending bits */ - if (vo) { - xive2_tctx_need_resend(XIVE2_ROUTER(xptr), tctx, nvp_blk, nvp_idx, - do_restore); + if (v) { + xive2_tctx_need_resend(XIVE2_ROUTER(xptr), tctx, ring, + nvp_blk, nvp_idx, do_restore); } } +void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive2_tm_push_ctx(xptr, tctx, offset, value, size, TM_QW1_OS); +} + +void xive2_tm_push_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive2_tm_push_ctx(xptr, tctx, offset, value, size, TM_QW2_HV_POOL); +} + /* returns -1 if ring is invalid, but still populates block and index */ static int xive2_tctx_get_nvp_indexes(XiveTCTX *tctx, uint8_t ring, uint8_t *nvp_blk, uint32_t *nvp_idx) diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index a91b99057c2ac..c1ab06a55adf6 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -140,6 +140,8 @@ bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority); void xive2_tm_set_lsmfb(XiveTCTX *tctx, int ring, uint8_t priority); void xive2_tm_set_hv_target(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); +void xive2_tm_push_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size); uint64_t xive2_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size); uint64_t xive2_tm_pull_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx, From 6936d2f561759c00993217a424ddeb1554c5f1ff Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:54 +1000 Subject: [PATCH 0068/1794] ppc/xive2: redistribute group interrupts on context push MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pushing a context, any presented group interrupt should be redistributed before processing pending interrupts to present highest priority. This can occur when pushing the POOL ring when the valid PHYS ring has a group interrupt presented, because they share signal registers. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-46-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 4244e1d02b618..23eb85bb86690 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -945,8 +945,9 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t nvp_blk, uint32_t nvp_idx, bool do_restore) { + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; - uint8_t ipb; + uint8_t ipb, nsr = sig_regs[TM_NSR]; Xive2Nvp nvp; /* @@ -978,6 +979,11 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, /* IPB bits in the backlog are merged with the TIMA IPB bits */ regs[TM_IPB] |= ipb; + if (xive_nsr_indicates_group_exception(ring, nsr)) { + /* redistribute precluded active grp interrupt */ + g_assert(ring == TM_QW2_HV_POOL); /* PHYS ring has the grp interrupt */ + xive2_redistribute(xrtr, tctx, xive_nsr_exception_ring(ring, nsr)); + } xive2_tctx_process_pending(tctx, ring == TM_QW2_HV_POOL ? TM_QW3_HV_PHYS : ring); } From 6ef77843603b89b1e48a06ca0644e74e45297839 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:55 +1000 Subject: [PATCH 0069/1794] ppc/xive2: Implement set_os_pending TIMA op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xive2 must take into account redistribution of group interrupts if the VP directed priority exceeds the group interrupt priority after this operation. The xive1 code is not group aware so implement this for xive2. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-47-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 2 ++ hw/intc/xive2.c | 28 ++++++++++++++++++++++++++++ include/hw/ppc/xive2.h | 2 ++ 3 files changed, 32 insertions(+) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index e7f77be2f7116..25cb3877cb155 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -747,6 +747,8 @@ static const XiveTmOp xive2_tm_operations[] = { /* MMIOs above 2K : special operations with side effects */ { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, true, false, NULL, xive_tm_ack_os_reg }, + { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, true, false, + xive2_tm_set_os_pending, NULL }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_G2, 4, true, false, NULL, xive2_tm_pull_os_ctx }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 4, true, false, diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 23eb85bb86690..f9eaea1192896 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1323,6 +1323,34 @@ void xive2_tm_set_os_cppr(XivePresenter *xptr, XiveTCTX *tctx, xive2_tctx_set_cppr(tctx, TM_QW1_OS, value & 0xff); } +/* + * Adjust the IPB to allow a CPU to process event queues of other + * priorities during one physical interrupt cycle. + */ +void xive2_tm_set_os_pending(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + uint8_t ring = TM_QW1_OS; + uint8_t *regs = &tctx->regs[ring]; + uint8_t priority = value & 0xff; + + /* + * XXX: should this simply set a bit in IPB and wait for it to be picked + * up next cycle, or is it supposed to present it now? We implement the + * latter here. + */ + regs[TM_IPB] |= xive_priority_to_ipb(priority); + if (xive_ipb_to_pipr(regs[TM_IPB]) >= regs[TM_PIPR]) { + return; + } + if (xive_nsr_indicates_group_exception(ring, regs[TM_NSR])) { + xive2_redistribute(xrtr, tctx, ring); + } + + xive_tctx_pipr_present(tctx, ring, priority, 0); +} + static void xive2_tctx_set_target(XiveTCTX *tctx, uint8_t ring, uint8_t target) { uint8_t *regs = &tctx->regs[ring]; diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index c1ab06a55adf6..45266c2a8b9eb 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -130,6 +130,8 @@ void xive2_tm_set_hv_cppr(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); void xive2_tm_set_os_cppr(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); +void xive2_tm_set_os_pending(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size); void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); uint64_t xive2_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, From f030f35109062a3cf815e12939a66c9df8354714 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:56 +1000 Subject: [PATCH 0070/1794] ppc/xive2: Implement POOL LGS push TIMA op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement set LGS for the POOL ring. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-48-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 25cb3877cb155..725ba72b8f7a0 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -532,6 +532,12 @@ static void xive_tm_set_os_lgs(XivePresenter *xptr, XiveTCTX *tctx, xive_tctx_set_lgs(tctx, TM_QW1_OS, value & 0xff); } +static void xive_tm_set_pool_lgs(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive_tctx_set_lgs(tctx, TM_QW2_HV_POOL, value & 0xff); +} + /* * Adjust the PIPR to allow a CPU to process event queues of other * priorities during one physical interrupt cycle. @@ -737,6 +743,8 @@ static const XiveTmOp xive2_tm_operations[] = { xive2_tm_push_pool_ctx, NULL }, { XIVE_TM_HV_PAGE, TM_QW2_HV_POOL + TM_WORD2, 8, true, true, xive2_tm_push_pool_ctx, NULL }, + { XIVE_TM_HV_PAGE, TM_QW2_HV_POOL + TM_LGS, 1, true, true, + xive_tm_set_pool_lgs, NULL }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, true, true, xive2_tm_set_hv_cppr, NULL }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, true, true, From 714bae7351d9c85643d6b48109f59f20f05c8466 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:57 +1000 Subject: [PATCH 0071/1794] ppc/xive2: Implement PHYS ring VP push TIMA op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the phys (aka hard) VP push. PowerVM uses this operation. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-49-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 2 ++ hw/intc/xive2.c | 11 +++++++++++ include/hw/ppc/xive2.h | 2 ++ 3 files changed, 15 insertions(+) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 725ba72b8f7a0..8b7182fbb86c4 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -747,6 +747,8 @@ static const XiveTmOp xive2_tm_operations[] = { xive_tm_set_pool_lgs, NULL }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, true, true, xive2_tm_set_hv_cppr, NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, false, true, + xive2_tm_push_phys_ctx, NULL }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, true, true, NULL, xive_tm_vt_poll }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_T, 1, true, true, diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index f9eaea1192896..1b005687961a5 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1005,6 +1005,11 @@ static void xive2_tm_push_ctx(XivePresenter *xptr, XiveTCTX *tctx, /* First update the thead context */ switch (size) { + case 1: + tctx->regs[ring + TM_WORD2] = value & 0xff; + cam = xive2_tctx_hw_cam_line(xptr, tctx); + cam |= ((value & 0xc0) << 24); /* V and H bits */ + break; case 4: cam = value; w2 = cpu_to_be32(cam); @@ -1040,6 +1045,12 @@ void xive2_tm_push_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, xive2_tm_push_ctx(xptr, tctx, offset, value, size, TM_QW2_HV_POOL); } +void xive2_tm_push_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive2_tm_push_ctx(xptr, tctx, offset, value, size, TM_QW3_HV_PHYS); +} + /* returns -1 if ring is invalid, but still populates block and index */ static int xive2_tctx_get_nvp_indexes(XiveTCTX *tctx, uint8_t ring, uint8_t *nvp_blk, uint32_t *nvp_idx) diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index 45266c2a8b9eb..f4437e2c79a72 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -146,6 +146,8 @@ void xive2_tm_push_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); uint64_t xive2_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size); +void xive2_tm_push_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size); uint64_t xive2_tm_pull_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size); void xive2_tm_pull_phys_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, From 3a50f36469c318d3c66360a8e5ada6f2dc1a349d Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:58 +1000 Subject: [PATCH 0072/1794] ppc/xive: Split need_resend into restore_nvp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is needed by the next patch which will re-send on all lower rings when pushing a context. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-50-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 24 ++++++++++++------------ hw/intc/xive2.c | 28 ++++++++++++++++------------ 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 8b7182fbb86c4..e0ffcf89ebff6 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -606,7 +606,7 @@ static uint64_t xive_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, return qw1w2; } -static void xive_tctx_need_resend(XiveRouter *xrtr, XiveTCTX *tctx, +static void xive_tctx_restore_nvp(XiveRouter *xrtr, XiveTCTX *tctx, uint8_t nvt_blk, uint32_t nvt_idx) { XiveNVT nvt; @@ -632,16 +632,6 @@ static void xive_tctx_need_resend(XiveRouter *xrtr, XiveTCTX *tctx, uint8_t *regs = &tctx->regs[TM_QW1_OS]; regs[TM_IPB] |= ipb; } - - /* - * Always call xive_tctx_recompute_from_ipb(). Even if there were no - * escalation triggered, there could be a pending interrupt which - * was saved when the context was pulled and that we need to take - * into account by recalculating the PIPR (which is not - * saved/restored). - * It will also raise the External interrupt signal if needed. - */ - xive_tctx_pipr_recompute_from_ipb(tctx, TM_QW1_OS); /* fxb */ } /* @@ -663,7 +653,17 @@ static void xive_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, /* Check the interrupt pending bits */ if (vo) { - xive_tctx_need_resend(XIVE_ROUTER(xptr), tctx, nvt_blk, nvt_idx); + xive_tctx_restore_nvp(XIVE_ROUTER(xptr), tctx, nvt_blk, nvt_idx); + + /* + * Always call xive_tctx_recompute_from_ipb(). Even if there were no + * escalation triggered, there could be a pending interrupt which + * was saved when the context was pulled and that we need to take + * into account by recalculating the PIPR (which is not + * saved/restored). + * It will also raise the External interrupt signal if needed. + */ + xive_tctx_pipr_recompute_from_ipb(tctx, TM_QW1_OS); /* fxb */ } } diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 1b005687961a5..c3c6871e91b35 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -940,14 +940,14 @@ static uint8_t xive2_tctx_restore_ctx(Xive2Router *xrtr, XiveTCTX *tctx, return cppr; } -static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, +/* Restore TIMA VP context from NVP backlog */ +static void xive2_tctx_restore_nvp(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring, uint8_t nvp_blk, uint32_t nvp_idx, bool do_restore) { - uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; - uint8_t ipb, nsr = sig_regs[TM_NSR]; + uint8_t ipb; Xive2Nvp nvp; /* @@ -978,14 +978,6 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, } /* IPB bits in the backlog are merged with the TIMA IPB bits */ regs[TM_IPB] |= ipb; - - if (xive_nsr_indicates_group_exception(ring, nsr)) { - /* redistribute precluded active grp interrupt */ - g_assert(ring == TM_QW2_HV_POOL); /* PHYS ring has the grp interrupt */ - xive2_redistribute(xrtr, tctx, xive_nsr_exception_ring(ring, nsr)); - } - xive2_tctx_process_pending(tctx, ring == TM_QW2_HV_POOL ? - TM_QW3_HV_PHYS : ring); } /* @@ -1028,8 +1020,20 @@ static void xive2_tm_push_ctx(XivePresenter *xptr, XiveTCTX *tctx, /* Check the interrupt pending bits */ if (v) { - xive2_tctx_need_resend(XIVE2_ROUTER(xptr), tctx, ring, + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); + uint8_t nsr = sig_regs[TM_NSR]; + + xive2_tctx_restore_nvp(xrtr, tctx, ring, nvp_blk, nvp_idx, do_restore); + + if (xive_nsr_indicates_group_exception(ring, nsr)) { + /* redistribute precluded active grp interrupt */ + g_assert(ring == TM_QW2_HV_POOL); /* PHYS ring has the interrupt */ + xive2_redistribute(xrtr, tctx, xive_nsr_exception_ring(ring, nsr)); + } + xive2_tctx_process_pending(tctx, ring == TM_QW2_HV_POOL ? + TM_QW3_HV_PHYS : ring); } } From df3614b7983e0629b0d422259968985ca0117bfa Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:59 +1000 Subject: [PATCH 0073/1794] ppc/xive2: Enable lower level contexts on VP push MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pushing a context, the lower-level context becomes valid if it had V=1, and so on. Iterate lower level contexts and send them pending interrupts if they become enabled. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-51-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index c3c6871e91b35..ee5fa26178497 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -995,6 +995,12 @@ static void xive2_tm_push_ctx(XivePresenter *xptr, XiveTCTX *tctx, bool v; bool do_restore; + if (xive_ring_valid(tctx, ring)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Attempt to push VP to enabled" + " ring 0x%02x\n", ring); + return; + } + /* First update the thead context */ switch (size) { case 1: @@ -1021,19 +1027,32 @@ static void xive2_tm_push_ctx(XivePresenter *xptr, XiveTCTX *tctx, /* Check the interrupt pending bits */ if (v) { Xive2Router *xrtr = XIVE2_ROUTER(xptr); - uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); - uint8_t nsr = sig_regs[TM_NSR]; + uint8_t cur_ring; xive2_tctx_restore_nvp(xrtr, tctx, ring, nvp_blk, nvp_idx, do_restore); - if (xive_nsr_indicates_group_exception(ring, nsr)) { - /* redistribute precluded active grp interrupt */ - g_assert(ring == TM_QW2_HV_POOL); /* PHYS ring has the interrupt */ - xive2_redistribute(xrtr, tctx, xive_nsr_exception_ring(ring, nsr)); + for (cur_ring = TM_QW1_OS; cur_ring <= ring; + cur_ring += XIVE_TM_RING_SIZE) { + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, cur_ring); + uint8_t nsr = sig_regs[TM_NSR]; + + if (!xive_ring_valid(tctx, cur_ring)) { + continue; + } + + if (cur_ring == TM_QW2_HV_POOL) { + if (xive_nsr_indicates_exception(cur_ring, nsr)) { + g_assert(xive_nsr_exception_ring(cur_ring, nsr) == + TM_QW3_HV_PHYS); + xive2_redistribute(xrtr, tctx, + xive_nsr_exception_ring(ring, nsr)); + } + xive2_tctx_process_pending(tctx, TM_QW3_HV_PHYS); + break; + } + xive2_tctx_process_pending(tctx, cur_ring); } - xive2_tctx_process_pending(tctx, ring == TM_QW2_HV_POOL ? - TM_QW3_HV_PHYS : ring); } } @@ -1159,6 +1178,7 @@ static void xive2_tctx_process_pending(XiveTCTX *tctx, uint8_t sig_ring) int rc; g_assert(sig_ring == TM_QW3_HV_PHYS || sig_ring == TM_QW1_OS); + g_assert(sig_regs[TM_WORD2] & 0x80); g_assert(!xive_nsr_indicates_group_exception(sig_ring, sig_regs[TM_NSR])); /* From 8c8f62baa61b55b297453a8d416c6b0932398948 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Mon, 21 Jul 2025 10:07:52 +0100 Subject: [PATCH 0074/1794] hvf: arm: Remove $pc from trace_hvf_data_abort() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't synchronize vcpu registers from the hardware accelerator (e.g., by cpu_synchronize_state()) in the Dabort handler, so env->pc points to the instruction which has nothing to do with the Dabort at all. And it doesn't seem to make much sense to log PC in every Dabort handler, let's just remove it from this trace event. Signed-off-by: Zenghui Yu Reviewed-by: Mads Ynddal Message-id: 20250713154719.4248-1-zenghui.yu@linux.dev Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 2 +- target/arm/hvf/trace-events | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index c9cfcdc08bb16..8f93e42b34b92 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -2005,7 +2005,7 @@ int hvf_vcpu_exec(CPUState *cpu) uint32_t cm = (syndrome >> 8) & 0x1; uint64_t val = 0; - trace_hvf_data_abort(env->pc, hvf_exit->exception.virtual_address, + trace_hvf_data_abort(hvf_exit->exception.virtual_address, hvf_exit->exception.physical_address, isv, iswrite, s1ptw, len, srt); diff --git a/target/arm/hvf/trace-events b/target/arm/hvf/trace-events index b49746f28d1d4..b29a995f3d305 100644 --- a/target/arm/hvf/trace-events +++ b/target/arm/hvf/trace-events @@ -2,7 +2,7 @@ hvf_unhandled_sysreg_read(uint64_t pc, uint32_t reg, uint32_t op0, uint32_t op1, hvf_unhandled_sysreg_write(uint64_t pc, uint32_t reg, uint32_t op0, uint32_t op1, uint32_t crn, uint32_t crm, uint32_t op2) "unhandled sysreg write at pc=0x%"PRIx64": 0x%08x (op0=%d op1=%d crn=%d crm=%d op2=%d)" hvf_inject_fiq(void) "injecting FIQ" hvf_inject_irq(void) "injecting IRQ" -hvf_data_abort(uint64_t pc, uint64_t va, uint64_t pa, bool isv, bool iswrite, bool s1ptw, uint32_t len, uint32_t srt) "data abort: [pc=0x%"PRIx64" va=0x%016"PRIx64" pa=0x%016"PRIx64" isv=%d iswrite=%d s1ptw=%d len=%d srt=%d]" +hvf_data_abort(uint64_t va, uint64_t pa, bool isv, bool iswrite, bool s1ptw, uint32_t len, uint32_t srt) "data abort: [va=0x%016"PRIx64" pa=0x%016"PRIx64" isv=%d iswrite=%d s1ptw=%d len=%d srt=%d]" hvf_sysreg_read(uint32_t reg, uint32_t op0, uint32_t op1, uint32_t crn, uint32_t crm, uint32_t op2, uint64_t val) "sysreg read 0x%08x (op0=%d op1=%d crn=%d crm=%d op2=%d) = 0x%016"PRIx64 hvf_sysreg_write(uint32_t reg, uint32_t op0, uint32_t op1, uint32_t crn, uint32_t crm, uint32_t op2, uint64_t val) "sysreg write 0x%08x (op0=%d op1=%d crn=%d crm=%d op2=%d, val=0x%016"PRIx64")" hvf_unknown_hvc(uint64_t pc, uint64_t x0) "pc=0x%"PRIx64" unknown HVC! 0x%016"PRIx64 From 655659a74a36b63e33d2dc969d3c44beb1b008b3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 21 Jul 2025 10:07:52 +0100 Subject: [PATCH 0075/1794] target/arm: Correct encoding of Debug Communications Channel registers We don't implement the Debug Communications Channel (DCC), but we do attempt to provide dummy versions of its system registers so that software that tries to access them doesn't fall over. However, we got the tx/rx register definitions wrong. These should be: AArch32: DBGDTRTX p14 0 c0 c5 0 (on writes) DBGDTRRX p14 0 c0 c5 0 (on reads) AArch64: DBGDTRTX_EL0 2 3 0 5 0 (on writes) DBGDTRRX_EL0 2 3 0 5 0 (on reads) DBGDTR_EL0 2 3 0 4 0 (reads and writes) where DBGDTRTX and DBGDTRRX are effectively different names for the same 32-bit register, which has tx behaviour on writes and rx behaviour on reads. The AArch64-only DBGDTR_EL0 is a 64-bit wide register whose top and bottom halves map to the DBGDTRRX and DBGDTRTX registers. Currently we have just one cpreg struct, which: * calls itself DBGDTR_EL0 * uses the DBGDTRTX_EL0/DBGDTRRX_EL0 encoding * is marked as ARM_CP_STATE_BOTH but has the wrong opc1 value for AArch32 * is implemented as RAZ/WI Correct the encoding so: * we name the DBGDTRTX/DBGDTRRX register correctly * we split it into AA64 and AA32 versions so we can get the AA32 encoding right * we implement DBGDTR_EL0 at its correct encoding Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2986 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250708141049.778361-1-peter.maydell@linaro.org --- target/arm/debug_helper.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index 69fb1d0d9ff8d..aee06d4d42662 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -988,11 +988,20 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 2, .access = PL1_RW, .accessfn = access_tdcc, .type = ARM_CP_CONST, .resetvalue = 0 }, - /* DBGDTRTX_EL0/DBGDTRRX_EL0 depend on direction */ - { .name = "DBGDTR_EL0", .state = ARM_CP_STATE_BOTH, .cp = 14, + /* Architecturally DBGDTRTX is named DBGDTRRX when used for reads */ + { .name = "DBGDTRTX_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 2, .opc1 = 3, .crn = 0, .crm = 5, .opc2 = 0, .access = PL0_RW, .accessfn = access_tdcc, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "DBGDTRTX", .state = ARM_CP_STATE_AA32, .cp = 14, + .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 0, + .access = PL0_RW, .accessfn = access_tdcc, + .type = ARM_CP_CONST, .resetvalue = 0 }, + /* This is AArch64-only and is a combination of DBGDTRTX and DBGDTRRX */ + { .name = "DBGDTR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 2, .opc1 = 3, .crn = 0, .crm = 4, .opc2 = 0, + .access = PL0_RW, .accessfn = access_tdcc, + .type = ARM_CP_CONST, .resetvalue = 0 }, /* * OSECCR_EL1 provides a mechanism for an operating system * to access the contents of EDECCR. EDECCR is not implemented though, From 8ccd35f25cdf2e03f44585a11b7daf93d1d46a3a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 21 Jul 2025 10:07:53 +0100 Subject: [PATCH 0076/1794] hw/misc/ivshmem-pci: Improve error handling Coverity points out that the ivshmem-pci code has some error handling cases where it incorrectly tries to use an invalid filedescriptor. These generally happen because ivshmem_recv_msg() calls qemu_chr_fe_get_msgfd(), which might return -1, but the code in process_msg() generally assumes that the file descriptor was provided when it was supposed to be. In particular: * the error case in process_msg() only needs to close the fd if one was provided * process_msg_shmem() should fail if no fd was provided Coverity: CID 1508726 Signed-off-by: Peter Maydell Reviewed-by: Markus Armbruster Message-id: 20250711145012.1521936-1-peter.maydell@linaro.org --- hw/misc/ivshmem-pci.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/misc/ivshmem-pci.c b/hw/misc/ivshmem-pci.c index 5a10bca633d07..d47ae739d61ad 100644 --- a/hw/misc/ivshmem-pci.c +++ b/hw/misc/ivshmem-pci.c @@ -479,6 +479,11 @@ static void process_msg_shmem(IVShmemState *s, int fd, Error **errp) struct stat buf; size_t size; + if (fd < 0) { + error_setg(errp, "server didn't provide fd with shared memory message"); + return; + } + if (s->ivshmem_bar2) { error_setg(errp, "server sent unexpected shared memory message"); close(fd); @@ -553,7 +558,9 @@ static void process_msg(IVShmemState *s, int64_t msg, int fd, Error **errp) if (msg < -1 || msg > IVSHMEM_MAX_PEERS) { error_setg(errp, "server sent invalid message %" PRId64, msg); - close(fd); + if (fd >= 0) { + close(fd); + } return; } From 32d8fb61e5a1fb5feeecce85d461be019cc30a54 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 21 Jul 2025 10:07:53 +0100 Subject: [PATCH 0077/1794] target/arm: Provide always-false kvm_arm_*_supported() stubs for usermode If you try to build aarch64-linux-user with clang and --enable-debug then it fails to compile: ld: libqemu-aarch64-linux-user.a.p/target_arm_cpu64.c.o: in function `cpu_arm_set_sve': ../../target/arm/cpu64.c:321:(.text+0x1254): undefined reference to `kvm_arm_sve_supported' This is a regression introduced in commit f86d4220, which switched the kvm-stub.c file away from being built for all arm targets to only being built for system emulation binaries. It doesn't affect gcc, presumably because even at -O0 gcc folds away the always-false kvm_enabled() condition but clang does not. We would prefer not to build kvm-stub.c once for usermode and once for system-emulation binaries, and we can't build it just once for both because it includes cpu.h. So instead provide always-false versions of the five functions that are valid to call without KVM support in kvm_arm.h. Fixes: f86d42205c2eba ("target/arm/meson: accelerator files are not needed in user mode") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3033 Signed-off-by: Peter Maydell Reviewed-by: Pierrick Bouvier Message-id: 20250714135152.1896214-1-peter.maydell@linaro.org --- target/arm/kvm_arm.h | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index b4cad051551fb..6a9b6374a6df7 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -161,6 +161,14 @@ void kvm_arm_add_vcpu_properties(ARMCPU *cpu); */ void kvm_arm_steal_time_finalize(ARMCPU *cpu, Error **errp); +/* + * These "is some KVM subfeature enabled?" functions may be called + * when KVM support is not present, including in the user-mode + * emulators. The kvm-stub.c file is only built into the system + * emulators, so for user-mode emulation we provide "always false" + * stubs here. + */ +#ifndef CONFIG_USER_ONLY /** * kvm_arm_aarch32_supported: * @@ -197,6 +205,33 @@ bool kvm_arm_mte_supported(void); * Returns true if KVM can enable EL2 and false otherwise. */ bool kvm_arm_el2_supported(void); +#else + +static inline bool kvm_arm_aarch32_supported(void) +{ + return false; +} + +static inline bool kvm_arm_pmu_supported(void) +{ + return false; +} + +static inline bool kvm_arm_sve_supported(void) +{ + return false; +} + +static inline bool kvm_arm_mte_supported(void) +{ + return false; +} + +static inline bool kvm_arm_el2_supported(void) +{ + return false; +} +#endif /** * kvm_arm_get_max_vm_ipa_size: From e74aad9f81cc2bfee2057086610e21bd98e9c5a5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 21 Jul 2025 10:07:53 +0100 Subject: [PATCH 0078/1794] host-utils: Drop workaround for buggy Apple Clang __builtin_subcll() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit b0438861efe ("host-utils: Avoid using __builtin_subcll on buggy versions of Apple Clang") we added a workaround for a bug in Apple Clang 14 where its __builtin_subcll() implementation was wrong. This bug was only present in Apple Clang 14, not in upstream clang, and is not present in Apple Clang versions 15 and newer. Since commit 4e035201 we have required at least Apple Clang 15, so we no longer build with the buggy versions. We can therefore drop the workaround. This is effectively a revert of b0438861efe. This should not be backported to stable branches, which may still need to support Apple Clang 14. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3030 Signed-off-by: Peter Maydell Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250714145033.1908788-1-peter.maydell@linaro.org --- include/qemu/compiler.h | 13 ------------- include/qemu/host-utils.h | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index 65b89958d36fe..1c2b673c05836 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -182,19 +182,6 @@ #define QEMU_DISABLE_CFI #endif -/* - * Apple clang version 14 has a bug in its __builtin_subcll(); define - * BUILTIN_SUBCLL_BROKEN for the offending versions so we can avoid it. - * When a version of Apple clang which has this bug fixed is released - * we can add an upper bound to this check. - * See https://gitlab.com/qemu-project/qemu/-/issues/1631 - * and https://gitlab.com/qemu-project/qemu/-/issues/1659 for details. - * The bug never made it into any upstream LLVM releases, only Apple ones. - */ -#if defined(__apple_build_version__) && __clang_major__ >= 14 -#define BUILTIN_SUBCLL_BROKEN -#endif - #if __has_attribute(annotate) #define QEMU_ANNOTATE(x) __attribute__((annotate(x))) #else diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index 4d28fa22cfa8d..dd558589cb523 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -677,7 +677,7 @@ static inline uint64_t uadd64_carry(uint64_t x, uint64_t y, bool *pcarry) */ static inline uint64_t usub64_borrow(uint64_t x, uint64_t y, bool *pborrow) { -#if __has_builtin(__builtin_subcll) && !defined(BUILTIN_SUBCLL_BROKEN) +#if __has_builtin(__builtin_subcll) unsigned long long b = *pborrow; x = __builtin_subcll(x, y, b, &b); *pborrow = b & 1; From 30dbcd9283988ba352181cb42c6a69ae32075363 Mon Sep 17 00:00:00 2001 From: Jackson Donaldson Date: Mon, 21 Jul 2025 10:07:53 +0100 Subject: [PATCH 0079/1794] hw/misc/max78000_aes: Comment Internal Key Storage Coverity Scan noted an unusual pattern in the MAX78000 aes device, with duplicated calls to set_decrypt. This commit adds a comment noting why the implementation is correct. Signed-off-by: Jackson Donaldson Message-id: 20250716002622.84685-1-jcksn@duck.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/misc/max78000_aes.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/misc/max78000_aes.c b/hw/misc/max78000_aes.c index 0bfb2f02b5b32..d883ddd2b614b 100644 --- a/hw/misc/max78000_aes.c +++ b/hw/misc/max78000_aes.c @@ -79,6 +79,12 @@ static void max78000_aes_do_crypto(Max78000AesState *s) keydata += 8; } + /* + * The MAX78000 AES engine stores an internal key, which it uses only + * for decryption. This results in the slighly odd looking pairs of + * set_encrypt and set_decrypt calls below; s->internal_key is + * being stored for later use in both cases. + */ AES_KEY key; if ((s->ctrl & TYPE) == 0) { AES_set_encrypt_key(keydata, keylen, &key); From 012bf8ad134ecec5d7e68101c216204107fd9741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 21 Jul 2025 10:07:54 +0100 Subject: [PATCH 0080/1794] docs: Fix Aspeed title MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ad8e0e8a0088 removed the "======" underlining the file title which broke documentation rendering. Add it back. Fixes: ad8e0e8a0088 ("docs: add support for gb200-bmc") Cc: Ed Tanous Reported-by: Peter Maydell Signed-off-by: Cédric Le Goater Reviewed-by: Thomas Huth Reviewed-by: Ed Tanous Message-id: 20250715061904.97540-1-clg@redhat.com Signed-off-by: Peter Maydell --- docs/system/arm/aspeed.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index bec0a1dfa8b8a..bf18c56347008 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -1,4 +1,5 @@ Aspeed family boards (``ast2500-evb``, ``ast2600-evb``, ``ast2700-evb``, ``bletchley-bmc``, ``fuji-bmc``, ``gb200nvl-bmc``, ``fby35-bmc``, ``fp5280g2-bmc``, ``g220a-bmc``, ``palmetto-bmc``, ``qcom-dc-scm-v1-bmc``, ``qcom-firework-bmc``, ``quanta-q71l-bmc``, ``rainier-bmc``, ``romulus-bmc``, ``sonorapass-bmc``, ``supermicrox11-bmc``, ``supermicrox11spi-bmc``, ``tiogapass-bmc``, ``witherspoon-bmc``, ``yosemitev2-bmc``) +==================================================================================================================================================================================================================================================================================================================================================================================================================================== The QEMU Aspeed machines model BMCs of various OpenPOWER systems and Aspeed evaluation boards. They are based on different releases of the From 2b5a9bbbadb39750584f36e6bee3a36ebe134418 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:23 +0100 Subject: [PATCH 0081/1794] target/arm: Add BFADD, BFSUB, BFMUL (unpredicated) FEAT_SVE_B16B16 adds bfloat16 versions of the SVE floating point (unpredicated) instructions, which are encoded via sz==0b00. Fixes: 7b1613a1020d2942 ("target/arm: Enable FEAT_SME2p1 on -cpu max") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-2-peter.maydell@linaro.org --- target/arm/tcg/helper.h | 3 +++ target/arm/tcg/translate-sve.c | 6 +++++- target/arm/tcg/vec_helper.c | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h index 0a006d95142ab..d9ca5b7c56eeb 100644 --- a/target/arm/tcg/helper.h +++ b/target/arm/tcg/helper.h @@ -728,16 +728,19 @@ DEF_HELPER_FLAGS_4(gvec_fclt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(gvec_fclt0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(gvec_fclt0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fadd_b16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_bfadd, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fsub_b16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_bfsub, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_b16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmul_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 7b575734fdea7..f00cccf15480f 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -190,6 +190,10 @@ static bool gen_gvec_fpst_zzz(DisasContext *s, gen_helper_gvec_3_ptr *fn, static bool gen_gvec_fpst_arg_zzz(DisasContext *s, gen_helper_gvec_3_ptr *fn, arg_rrr_esz *a, int data) { + /* These insns use MO_8 to encode BFloat16 */ + if (a->esz == MO_8 && !dc_isar_feature(aa64_sve_b16b16, s)) { + return false; + } return gen_gvec_fpst_zzz(s, fn, a->rd, a->rn, a->rm, data, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); } @@ -4146,7 +4150,7 @@ static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a) #define DO_FP3(NAME, name) \ static gen_helper_gvec_3_ptr * const name##_fns[4] = { \ - NULL, gen_helper_gvec_##name##_h, \ + gen_helper_gvec_##name##_b16, gen_helper_gvec_##name##_h, \ gen_helper_gvec_##name##_s, gen_helper_gvec_##name##_d \ }; \ TRANS_FEAT(NAME, aa64_sve, gen_gvec_fpst_arg_zzz, name##_fns[a->esz], a, 0) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index bae6165b505c7..76a9ab0da39a1 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1467,16 +1467,19 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, \ clear_tail(d, oprsz, simd_maxsz(desc)); \ } +DO_3OP(gvec_fadd_b16, bfloat16_add, float16) DO_3OP(gvec_fadd_h, float16_add, float16) DO_3OP(gvec_fadd_s, float32_add, float32) DO_3OP(gvec_fadd_d, float64_add, float64) DO_3OP(gvec_bfadd, bfloat16_add, bfloat16) +DO_3OP(gvec_fsub_b16, bfloat16_sub, float16) DO_3OP(gvec_fsub_h, float16_sub, float16) DO_3OP(gvec_fsub_s, float32_sub, float32) DO_3OP(gvec_fsub_d, float64_sub, float64) DO_3OP(gvec_bfsub, bfloat16_sub, bfloat16) +DO_3OP(gvec_fmul_b16, bfloat16_mul, float16) DO_3OP(gvec_fmul_h, float16_mul, float16) DO_3OP(gvec_fmul_s, float32_mul, float32) DO_3OP(gvec_fmul_d, float64_mul, float64) From 86fa06f8d9f953252a7919fa56a402d789bf1b78 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:24 +0100 Subject: [PATCH 0082/1794] target/arm: Add BFADD, BFSUB, BFMUL, BFMAXNM, BFMINNM (predicated) FEAT_SVE_B16B16 adds bfloat16 versions of the SVE floating point (predicated) instructions, which are encoded via sz=0b00. Add BFADD, BFSUB, BFMUL, BFMAXNM, BFMINNM; these are all the insns in this group which do not change behaviour for AH=1. We will deal with BFMAX/BFMIN (which do have different AH=1 behaviour) in a following commit. Fixes: 7b1613a1020d2942 ("target/arm: Enable FEAT_SME2p1 on -cpu max") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-3-peter.maydell@linaro.org --- target/arm/tcg/helper-sve.h | 10 ++++++++++ target/arm/tcg/sve_helper.c | 5 +++++ target/arm/tcg/translate-sve.c | 22 +++++++++++++++++----- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index c36090d13d1d2..d612bcaded39a 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -1196,6 +1196,8 @@ DEF_HELPER_FLAGS_5(sve_fcmne0_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve_fcmne0_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_fadd_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fadd_s, TCG_CALL_NO_RWG, @@ -1203,6 +1205,8 @@ DEF_HELPER_FLAGS_6(sve_fadd_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_fsub_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsub_s, TCG_CALL_NO_RWG, @@ -1210,6 +1214,8 @@ DEF_HELPER_FLAGS_6(sve_fsub_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_fmul_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmul_s, TCG_CALL_NO_RWG, @@ -1252,6 +1258,8 @@ DEF_HELPER_FLAGS_6(sve_ah_fmax_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_ah_fmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_fminnum_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnum_s, TCG_CALL_NO_RWG, @@ -1259,6 +1267,8 @@ DEF_HELPER_FLAGS_6(sve_fminnum_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fminnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_fmaxnum_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxnum_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 43b872c7fd6f8..a229503bc214b 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4629,14 +4629,17 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, \ } while (i != 0); \ } +DO_ZPZZ_FP(sve_fadd_b16, uint16_t, H1_2, bfloat16_add) DO_ZPZZ_FP(sve_fadd_h, uint16_t, H1_2, float16_add) DO_ZPZZ_FP(sve_fadd_s, uint32_t, H1_4, float32_add) DO_ZPZZ_FP(sve_fadd_d, uint64_t, H1_8, float64_add) +DO_ZPZZ_FP(sve_fsub_b16, uint16_t, H1_2, bfloat16_sub) DO_ZPZZ_FP(sve_fsub_h, uint16_t, H1_2, float16_sub) DO_ZPZZ_FP(sve_fsub_s, uint32_t, H1_4, float32_sub) DO_ZPZZ_FP(sve_fsub_d, uint64_t, H1_8, float64_sub) +DO_ZPZZ_FP(sve_fmul_b16, uint16_t, H1_2, bfloat16_mul) DO_ZPZZ_FP(sve_fmul_h, uint16_t, H1_2, float16_mul) DO_ZPZZ_FP(sve_fmul_s, uint32_t, H1_4, float32_mul) DO_ZPZZ_FP(sve_fmul_d, uint64_t, H1_8, float64_mul) @@ -4661,10 +4664,12 @@ DO_ZPZZ_FP(sve_ah_fmax_h, uint16_t, H1_2, helper_vfp_ah_maxh) DO_ZPZZ_FP(sve_ah_fmax_s, uint32_t, H1_4, helper_vfp_ah_maxs) DO_ZPZZ_FP(sve_ah_fmax_d, uint64_t, H1_8, helper_vfp_ah_maxd) +DO_ZPZZ_FP(sve_fminnum_b16, uint16_t, H1_2, bfloat16_minnum) DO_ZPZZ_FP(sve_fminnum_h, uint16_t, H1_2, float16_minnum) DO_ZPZZ_FP(sve_fminnum_s, uint32_t, H1_4, float32_minnum) DO_ZPZZ_FP(sve_fminnum_d, uint64_t, H1_8, float64_minnum) +DO_ZPZZ_FP(sve_fmaxnum_b16, uint16_t, H1_2, bfloat16_maxnum) DO_ZPZZ_FP(sve_fmaxnum_h, uint16_t, H1_2, float16_maxnum) DO_ZPZZ_FP(sve_fmaxnum_s, uint32_t, H1_4, float32_maxnum) DO_ZPZZ_FP(sve_fmaxnum_d, uint64_t, H1_8, float64_maxnum) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index f00cccf15480f..2739c226d7300 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -407,6 +407,10 @@ static bool gen_gvec_fpst_zzzp(DisasContext *s, gen_helper_gvec_4_ptr *fn, static bool gen_gvec_fpst_arg_zpzz(DisasContext *s, gen_helper_gvec_4_ptr *fn, arg_rprr_esz *a) { + /* These insns use MO_8 to encode BFloat16. */ + if (a->esz == MO_8 && !dc_isar_feature(aa64_sve_b16b16, s)) { + return false; + } return gen_gvec_fpst_zzzp(s, fn, a->rd, a->rn, a->rm, a->pg, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); } @@ -4206,13 +4210,21 @@ TRANS_FEAT_NONSTREAMING(FTSMUL, aa64_sve, gen_gvec_fpst_arg_zzz, s->fpcr_ah ? name##_ah_zpzz_fns[a->esz] : \ name##_zpzz_fns[a->esz], a) -DO_ZPZZ_FP(FADD_zpzz, aa64_sve, sve_fadd) -DO_ZPZZ_FP(FSUB_zpzz, aa64_sve, sve_fsub) -DO_ZPZZ_FP(FMUL_zpzz, aa64_sve, sve_fmul) +/* Similar, but for insns where sz == 0 encodes bfloat16 */ +#define DO_ZPZZ_FP_B16(NAME, FEAT, name) \ + static gen_helper_gvec_4_ptr * const name##_zpzz_fns[4] = { \ + gen_helper_##name##_b16, gen_helper_##name##_h, \ + gen_helper_##name##_s, gen_helper_##name##_d \ + }; \ + TRANS_FEAT(NAME, FEAT, gen_gvec_fpst_arg_zpzz, name##_zpzz_fns[a->esz], a) + +DO_ZPZZ_FP_B16(FADD_zpzz, aa64_sve, sve_fadd) +DO_ZPZZ_FP_B16(FSUB_zpzz, aa64_sve, sve_fsub) +DO_ZPZZ_FP_B16(FMUL_zpzz, aa64_sve, sve_fmul) DO_ZPZZ_AH_FP(FMIN_zpzz, aa64_sve, sve_fmin, sve_ah_fmin) DO_ZPZZ_AH_FP(FMAX_zpzz, aa64_sve, sve_fmax, sve_ah_fmax) -DO_ZPZZ_FP(FMINNM_zpzz, aa64_sve, sve_fminnum) -DO_ZPZZ_FP(FMAXNM_zpzz, aa64_sve, sve_fmaxnum) +DO_ZPZZ_FP_B16(FMINNM_zpzz, aa64_sve, sve_fminnum) +DO_ZPZZ_FP_B16(FMAXNM_zpzz, aa64_sve, sve_fmaxnum) DO_ZPZZ_AH_FP(FABD, aa64_sve, sve_fabd, sve_ah_fabd) DO_ZPZZ_FP(FSCALE, aa64_sve, sve_fscalbn) DO_ZPZZ_FP(FDIV, aa64_sve, sve_fdiv) From 279438560ba8575266e9105202c6e87044d24885 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:25 +0100 Subject: [PATCH 0083/1794] target/arm: Add BFMIN, BFMAX (predicated) FEAT_SVE_B16B16 adds bfloat16 versions of the SVE floating point (predicated) instructions, which are encoded via sz=0b00. Add the BFMAX and BFMIN insns. These have separate behaviour for AH=1 and AH=0; we have already implemented the AH=1 helper for the SME2 versions of these insns. Fixes: 7b1613a1020d2942 ("target/arm: Enable FEAT_SME2p1 on -cpu max") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-4-peter.maydell@linaro.org --- target/arm/tcg/helper-sve.h | 8 ++++++++ target/arm/tcg/sve_helper.c | 4 ++++ target/arm/tcg/translate-sve.c | 17 +++++++++++++++-- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index d612bcaded39a..cb6c2355e5893 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -1230,6 +1230,8 @@ DEF_HELPER_FLAGS_6(sve_fdiv_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_fmin_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmin_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmin_s, TCG_CALL_NO_RWG, @@ -1237,6 +1239,8 @@ DEF_HELPER_FLAGS_6(sve_fmin_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_fmax_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmax_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmax_s, TCG_CALL_NO_RWG, @@ -1244,6 +1248,8 @@ DEF_HELPER_FLAGS_6(sve_fmax_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmin_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_ah_fmin_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_ah_fmin_s, TCG_CALL_NO_RWG, @@ -1251,6 +1257,8 @@ DEF_HELPER_FLAGS_6(sve_ah_fmin_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_ah_fmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmax_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_ah_fmax_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_ah_fmax_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index a229503bc214b..1a56fa86d9cf9 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4648,18 +4648,22 @@ DO_ZPZZ_FP(sve_fdiv_h, uint16_t, H1_2, float16_div) DO_ZPZZ_FP(sve_fdiv_s, uint32_t, H1_4, float32_div) DO_ZPZZ_FP(sve_fdiv_d, uint64_t, H1_8, float64_div) +DO_ZPZZ_FP(sve_fmin_b16, uint16_t, H1_2, bfloat16_min) DO_ZPZZ_FP(sve_fmin_h, uint16_t, H1_2, float16_min) DO_ZPZZ_FP(sve_fmin_s, uint32_t, H1_4, float32_min) DO_ZPZZ_FP(sve_fmin_d, uint64_t, H1_8, float64_min) +DO_ZPZZ_FP(sve_fmax_b16, uint16_t, H1_2, bfloat16_max) DO_ZPZZ_FP(sve_fmax_h, uint16_t, H1_2, float16_max) DO_ZPZZ_FP(sve_fmax_s, uint32_t, H1_4, float32_max) DO_ZPZZ_FP(sve_fmax_d, uint64_t, H1_8, float64_max) +DO_ZPZZ_FP(sve_ah_fmin_b16, uint16_t, H1_2, helper_sme2_ah_fmin_b16) DO_ZPZZ_FP(sve_ah_fmin_h, uint16_t, H1_2, helper_vfp_ah_minh) DO_ZPZZ_FP(sve_ah_fmin_s, uint32_t, H1_4, helper_vfp_ah_mins) DO_ZPZZ_FP(sve_ah_fmin_d, uint64_t, H1_8, helper_vfp_ah_mind) +DO_ZPZZ_FP(sve_ah_fmax_b16, uint16_t, H1_2, helper_sme2_ah_fmax_b16) DO_ZPZZ_FP(sve_ah_fmax_h, uint16_t, H1_2, helper_vfp_ah_maxh) DO_ZPZZ_FP(sve_ah_fmax_s, uint32_t, H1_4, helper_vfp_ah_maxs) DO_ZPZZ_FP(sve_ah_fmax_d, uint64_t, H1_8, helper_vfp_ah_maxd) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 2739c226d7300..27af3df9a4b2e 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -4218,11 +4218,24 @@ TRANS_FEAT_NONSTREAMING(FTSMUL, aa64_sve, gen_gvec_fpst_arg_zzz, }; \ TRANS_FEAT(NAME, FEAT, gen_gvec_fpst_arg_zpzz, name##_zpzz_fns[a->esz], a) +#define DO_ZPZZ_AH_FP_B16(NAME, FEAT, name, ah_name) \ + static gen_helper_gvec_4_ptr * const name##_zpzz_fns[4] = { \ + gen_helper_##name##_b16, gen_helper_##name##_h, \ + gen_helper_##name##_s, gen_helper_##name##_d \ + }; \ + static gen_helper_gvec_4_ptr * const name##_ah_zpzz_fns[4] = { \ + gen_helper_##ah_name##_b16, gen_helper_##ah_name##_h, \ + gen_helper_##ah_name##_s, gen_helper_##ah_name##_d \ + }; \ + TRANS_FEAT(NAME, FEAT, gen_gvec_fpst_arg_zpzz, \ + s->fpcr_ah ? name##_ah_zpzz_fns[a->esz] : \ + name##_zpzz_fns[a->esz], a) + DO_ZPZZ_FP_B16(FADD_zpzz, aa64_sve, sve_fadd) DO_ZPZZ_FP_B16(FSUB_zpzz, aa64_sve, sve_fsub) DO_ZPZZ_FP_B16(FMUL_zpzz, aa64_sve, sve_fmul) -DO_ZPZZ_AH_FP(FMIN_zpzz, aa64_sve, sve_fmin, sve_ah_fmin) -DO_ZPZZ_AH_FP(FMAX_zpzz, aa64_sve, sve_fmax, sve_ah_fmax) +DO_ZPZZ_AH_FP_B16(FMIN_zpzz, aa64_sve, sve_fmin, sve_ah_fmin) +DO_ZPZZ_AH_FP_B16(FMAX_zpzz, aa64_sve, sve_fmax, sve_ah_fmax) DO_ZPZZ_FP_B16(FMINNM_zpzz, aa64_sve, sve_fminnum) DO_ZPZZ_FP_B16(FMAXNM_zpzz, aa64_sve, sve_fmaxnum) DO_ZPZZ_AH_FP(FABD, aa64_sve, sve_fabd, sve_ah_fabd) From f71c3f470f48bece6d0601171bfda63a0b746879 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:26 +0100 Subject: [PATCH 0084/1794] target/arm: Add BFMUL (indexed) FEAT_SVE_B16B16 adds a bfloat16 version of the FMUL insn in the floating-point multiply (indexed) instruction group. The encoding is slightly bespoke; in our implementation we use MO_8 to indicate bfloat16, as with the other B16B16 insns. Fixes: 7b1613a1020d2942 ("target/arm: Enable FEAT_SME2p1 on -cpu max") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-5-peter.maydell@linaro.org --- target/arm/tcg/helper.h | 2 ++ target/arm/tcg/sve.decode | 1 + target/arm/tcg/translate-sve.c | 2 +- target/arm/tcg/vec_helper.c | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h index d9ca5b7c56eeb..4da32db9021c9 100644 --- a/target/arm/tcg/helper.h +++ b/target/arm/tcg/helper.h @@ -823,6 +823,8 @@ DEF_HELPER_FLAGS_5(gvec_ftsmul_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_ftsmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_idx_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmul_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmul_idx_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 2efd5f57e457e..a76f2236f4331 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1062,6 +1062,7 @@ FMLS_zzxz 01100100 11 1 ..... 000001 ..... ..... @rrxr_1 esz=3 ### SVE FP Multiply Indexed Group # SVE floating-point multiply (indexed) +FMUL_zzx 01100100 0. 1 ..... 001010 ..... ..... @rrx_3 esz=0 FMUL_zzx 01100100 0. 1 ..... 001000 ..... ..... @rrx_3 esz=1 FMUL_zzx 01100100 10 1 ..... 001000 ..... ..... @rrx_2 esz=2 FMUL_zzx 01100100 11 1 ..... 001000 ..... ..... @rrx_1 esz=3 diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 27af3df9a4b2e..918cf6e1bd4ea 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3907,7 +3907,7 @@ TRANS_FEAT(FMLS_zzxz, aa64_sve, gen_gvec_fpst_zzzz, */ static gen_helper_gvec_3_ptr * const fmul_idx_fns[4] = { - NULL, gen_helper_gvec_fmul_idx_h, + gen_helper_gvec_fmul_idx_b16, gen_helper_gvec_fmul_idx_h, gen_helper_gvec_fmul_idx_s, gen_helper_gvec_fmul_idx_d, }; TRANS_FEAT(FMUL_zzx, aa64_sve, gen_gvec_fpst_zzz, diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 76a9ab0da39a1..33a136b90a61e 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1785,6 +1785,7 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, \ #define nop(N, M, S) (M) +DO_FMUL_IDX(gvec_fmul_idx_b16, nop, bfloat16_mul, float16, H2) DO_FMUL_IDX(gvec_fmul_idx_h, nop, float16_mul, float16, H2) DO_FMUL_IDX(gvec_fmul_idx_s, nop, float32_mul, float32, H4) DO_FMUL_IDX(gvec_fmul_idx_d, nop, float64_mul, float64, H8) From 929bec5581966c43d083588f9be38af48150c4fe Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:27 +0100 Subject: [PATCH 0085/1794] target/arm: Add BFMLA, BFMLS (vectors) FEAT_SVE_B16B16 adds bfloat16 versions of the FMLA and FMLS insns in the "SVE floating-point multiply-accumulate writing addend" group, encoded as sz=0b00. Fixes: 7b1613a1020d2942 ("target/arm: Enable FEAT_SME2p1 on -cpu max") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-6-peter.maydell@linaro.org --- target/arm/tcg/helper-sve.h | 14 +++++++ target/arm/tcg/sve_helper.c | 69 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sve.c | 21 ++++++++--- 3 files changed, 98 insertions(+), 6 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index cb6c2355e5893..5e4b7fd8cf400 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -1541,6 +1541,8 @@ DEF_HELPER_FLAGS_6(sve_fcadd_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fcadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_fmla_zpzzz_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmla_zpzzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmla_zpzzz_s, TCG_CALL_NO_RWG, @@ -1548,6 +1550,8 @@ DEF_HELPER_FLAGS_7(sve_fmla_zpzzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_7(sve_fmla_zpzzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_fmls_zpzzz_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmls_zpzzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmls_zpzzz_s, TCG_CALL_NO_RWG, @@ -1555,6 +1559,8 @@ DEF_HELPER_FLAGS_7(sve_fmls_zpzzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_7(sve_fmls_zpzzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_fnmla_zpzzz_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmla_zpzzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmla_zpzzz_s, TCG_CALL_NO_RWG, @@ -1562,6 +1568,8 @@ DEF_HELPER_FLAGS_7(sve_fnmla_zpzzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_7(sve_fnmla_zpzzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_s, TCG_CALL_NO_RWG, @@ -1569,6 +1577,8 @@ DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fmls_zpzzz_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_ah_fmls_zpzzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_ah_fmls_zpzzz_s, TCG_CALL_NO_RWG, @@ -1576,6 +1586,8 @@ DEF_HELPER_FLAGS_7(sve_ah_fmls_zpzzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_7(sve_ah_fmls_zpzzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fnmla_zpzzz_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_ah_fnmla_zpzzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_ah_fnmla_zpzzz_s, TCG_CALL_NO_RWG, @@ -1583,6 +1595,8 @@ DEF_HELPER_FLAGS_7(sve_ah_fnmla_zpzzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_7(sve_ah_fnmla_zpzzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fnmls_zpzzz_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_ah_fnmls_zpzzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_ah_fnmls_zpzzz_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 1a56fa86d9cf9..105cc5dd122fc 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -5099,6 +5099,75 @@ DO_ZPZ_FP(flogb_d, float64, H1_8, do_float64_logb_as_int) #undef DO_ZPZ_FP +static void do_fmla_zpzzz_b16(void *vd, void *vn, void *vm, void *va, void *vg, + float_status *status, uint32_t desc, + uint16_t neg1, uint16_t neg3, int flags) +{ + intptr_t i = simd_oprsz(desc); + uint64_t *g = vg; + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + i -= 2; + if (likely((pg >> (i & 63)) & 1)) { + float16 e1, e2, e3, r; + + e1 = *(uint16_t *)(vn + H1_2(i)) ^ neg1; + e2 = *(uint16_t *)(vm + H1_2(i)); + e3 = *(uint16_t *)(va + H1_2(i)) ^ neg3; + r = bfloat16_muladd(e1, e2, e3, flags, status); + *(uint16_t *)(vd + H1_2(i)) = r; + } + } while (i & 63); + } while (i != 0); +} + +void HELPER(sve_fmla_zpzzz_b16)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_b16(vd, vn, vm, va, vg, status, desc, 0, 0, 0); +} + +void HELPER(sve_fmls_zpzzz_b16)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_b16(vd, vn, vm, va, vg, status, desc, 0x8000, 0, 0); +} + +void HELPER(sve_fnmla_zpzzz_b16)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_b16(vd, vn, vm, va, vg, status, desc, 0x8000, 0x8000, 0); +} + +void HELPER(sve_fnmls_zpzzz_b16)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_b16(vd, vn, vm, va, vg, status, desc, 0, 0x8000, 0); +} + +void HELPER(sve_ah_fmls_zpzzz_b16)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_b16(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_product); +} + +void HELPER(sve_ah_fnmla_zpzzz_b16)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_b16(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_product | float_muladd_negate_c); +} + +void HELPER(sve_ah_fnmls_zpzzz_b16)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_b16(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_c); +} + static void do_fmla_zpzzz_h(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc, uint16_t neg1, uint16_t neg3, int flags) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 918cf6e1bd4ea..37ecbc2b7c0d3 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -4368,19 +4368,28 @@ TRANS_FEAT(FCADD, aa64_sve, gen_gvec_fpst_zzzp, fcadd_fns[a->esz], a->rd, a->rn, a->rm, a->pg, a->rot | (s->fpcr_ah << 1), a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) +static bool do_fmla_zpzzz(DisasContext *s, arg_rprrr_esz *a, + gen_helper_gvec_5_ptr *fn) +{ + /* These insns use MO_8 to encode BFloat16 */ + if (a->esz == MO_8 && !dc_isar_feature(aa64_sve_b16b16, s)) { + return false; + } + return gen_gvec_fpst_zzzzp(s, fn, a->rd, a->rn, a->rm, a->ra, a->pg, 0, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); +} + #define DO_FMLA(NAME, name, ah_name) \ static gen_helper_gvec_5_ptr * const name##_fns[4] = { \ - NULL, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_b16, gen_helper_sve_##name##_h, \ gen_helper_sve_##name##_s, gen_helper_sve_##name##_d \ }; \ static gen_helper_gvec_5_ptr * const name##_ah_fns[4] = { \ - NULL, gen_helper_sve_##ah_name##_h, \ + gen_helper_sve_##ah_name##_b16, gen_helper_sve_##ah_name##_h, \ gen_helper_sve_##ah_name##_s, gen_helper_sve_##ah_name##_d \ }; \ - TRANS_FEAT(NAME, aa64_sve, gen_gvec_fpst_zzzzp, \ - s->fpcr_ah ? name##_ah_fns[a->esz] : name##_fns[a->esz], \ - a->rd, a->rn, a->rm, a->ra, a->pg, 0, \ - a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) + TRANS_FEAT(NAME, aa64_sve, do_fmla_zpzzz, a, \ + s->fpcr_ah ? name##_ah_fns[a->esz] : name##_fns[a->esz]) /* We don't need an ah_fmla_zpzzz because fmla doesn't negate anything */ DO_FMLA(FMLA_zpzzz, fmla_zpzzz, fmla_zpzzz) From 67fbc4c8079226eb9e47369cc45eb3fe56c3c9c3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:28 +0100 Subject: [PATCH 0086/1794] target/arm: Add BFMLA, BFMLS (indexed) FEAT_SVE_B16B16 adds bfloat16 versions of the FMLA and FMLS insns in the SVE floating-point multiply-add (indexed) insn group. Implement these. Fixes: 7b1613a1020d2942 ("target/arm: Enable FEAT_SME2p1 on -cpu max") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-7-peter.maydell@linaro.org --- target/arm/tcg/sve.decode | 2 ++ target/arm/tcg/translate-sve.c | 25 ++++++++++++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index a76f2236f4331..a77b725c876cd 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1052,9 +1052,11 @@ FCMLA_zzxz 01100100 11 1 index:1 rm:4 0001 rot:2 rn:5 rd:5 \ ### SVE FP Multiply-Add Indexed Group # SVE floating-point multiply-add (indexed) +FMLA_zzxz 01100100 0. 1 ..... 000010 ..... ..... @rrxr_3 esz=0 FMLA_zzxz 01100100 0. 1 ..... 000000 ..... ..... @rrxr_3 esz=1 FMLA_zzxz 01100100 10 1 ..... 000000 ..... ..... @rrxr_2 esz=2 FMLA_zzxz 01100100 11 1 ..... 000000 ..... ..... @rrxr_1 esz=3 +FMLS_zzxz 01100100 0. 1 ..... 000011 ..... ..... @rrxr_3 esz=0 FMLS_zzxz 01100100 0. 1 ..... 000001 ..... ..... @rrxr_3 esz=1 FMLS_zzxz 01100100 10 1 ..... 000001 ..... ..... @rrxr_2 esz=2 FMLS_zzxz 01100100 11 1 ..... 000001 ..... ..... @rrxr_1 esz=3 diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 37ecbc2b7c0d3..fc76624b5a150 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3883,24 +3883,31 @@ DO_SVE2_RRXR_ROT(CDOT_zzxw_d, gen_helper_sve2_cdot_idx_d) *** SVE Floating Point Multiply-Add Indexed Group */ +static bool do_fmla_zzxz(DisasContext *s, arg_rrxr_esz *a, + gen_helper_gvec_4_ptr *fn) +{ + /* These insns use MO_8 to encode BFloat16 */ + if (a->esz == MO_8 && !dc_isar_feature(aa64_sve_b16b16, s)) { + return false; + } + return gen_gvec_fpst_zzzz(s, fn, a->rd, a->rn, a->rm, a->ra, a->index, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); +} + static gen_helper_gvec_4_ptr * const fmla_idx_fns[4] = { - NULL, gen_helper_gvec_fmla_idx_h, + gen_helper_gvec_bfmla_idx, gen_helper_gvec_fmla_idx_h, gen_helper_gvec_fmla_idx_s, gen_helper_gvec_fmla_idx_d }; -TRANS_FEAT(FMLA_zzxz, aa64_sve, gen_gvec_fpst_zzzz, - fmla_idx_fns[a->esz], a->rd, a->rn, a->rm, a->ra, a->index, - a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) +TRANS_FEAT(FMLA_zzxz, aa64_sve, do_fmla_zzxz, a, fmla_idx_fns[a->esz]) static gen_helper_gvec_4_ptr * const fmls_idx_fns[4][2] = { - { NULL, NULL }, + { gen_helper_gvec_bfmls_idx, gen_helper_gvec_ah_bfmls_idx }, { gen_helper_gvec_fmls_idx_h, gen_helper_gvec_ah_fmls_idx_h }, { gen_helper_gvec_fmls_idx_s, gen_helper_gvec_ah_fmls_idx_s }, { gen_helper_gvec_fmls_idx_d, gen_helper_gvec_ah_fmls_idx_d }, }; -TRANS_FEAT(FMLS_zzxz, aa64_sve, gen_gvec_fpst_zzzz, - fmls_idx_fns[a->esz][s->fpcr_ah], - a->rd, a->rn, a->rm, a->ra, a->index, - a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) +TRANS_FEAT(FMLS_zzxz, aa64_sve, do_fmla_zzxz, a, + fmls_idx_fns[a->esz][s->fpcr_ah]) /* *** SVE Floating Point Multiply Indexed Group From 17f6436822ff600cae4590c4b06b3321c97f1f42 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:29 +0100 Subject: [PATCH 0087/1794] target/arm: Correct sense of FPCR.AH test for FMAXQV and FMINQV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we implemented the FMAXQV and FMINQV insns we accidentally inverted the sense of the FPCR.AH test, so we gave the AH=1 behaviour when FPCR.AH was zero, and vice-versa. (The difference is limited to handling of negative zero and NaN inputs.) Fixes: 1de7ecfc12d05 ("target/arm: Implement FADDQV, F{MIN, MAX}{NM}QV for SVE2p1") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250718173032.2498900-8-peter.maydell@linaro.org --- target/arm/tcg/translate-sve.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index fc76624b5a150..2ed440aff150c 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -4020,7 +4020,7 @@ static gen_helper_gvec_3_ptr * const fmaxqv_ah_fns[4] = { gen_helper_sve2p1_ah_fmaxqv_s, gen_helper_sve2p1_ah_fmaxqv_d, }; TRANS_FEAT(FMAXQV, aa64_sme2p1_or_sve2p1, gen_gvec_fpst_arg_zpz, - (s->fpcr_ah ? fmaxqv_fns : fmaxqv_ah_fns)[a->esz], a, 0, + (s->fpcr_ah ? fmaxqv_ah_fns : fmaxqv_fns)[a->esz], a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) static gen_helper_gvec_3_ptr * const fminqv_fns[4] = { @@ -4032,7 +4032,7 @@ static gen_helper_gvec_3_ptr * const fminqv_ah_fns[4] = { gen_helper_sve2p1_ah_fminqv_s, gen_helper_sve2p1_ah_fminqv_d, }; TRANS_FEAT(FMINQV, aa64_sme2p1_or_sve2p1, gen_gvec_fpst_arg_zpz, - (s->fpcr_ah ? fminqv_fns : fminqv_ah_fns)[a->esz], a, 0, + (s->fpcr_ah ? fminqv_ah_fns : fminqv_fns)[a->esz], a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) /* From 07327d5f451162a841747836ff05cc6dd6e8c023 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:30 +0100 Subject: [PATCH 0088/1794] target/arm: Don't nest H() macro calls in SVE DO_REDUCE In the part of the SVE DO_REDUCE macro used by the SVE2p1 FMAXQV, FMINQV, etc insns, we incorrectly applied the H() macro twice when calculating an offset to add to the vn pointer. This has no effect on little-endian hosts but on big-endian hosts the two invocations will cancel each other out and we will access the wrong part of the array. The "s * 16" part of the expression is already aligned, so we only need to use the H macro on the "e". Correct the macro usage. Fixes: 1de7ecfc12d05 ("target/arm: Implement FADDQV, F{MIN, MAX}{NM}QV for SVE2p1") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-9-peter.maydell@linaro.org --- target/arm/tcg/sve_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 105cc5dd122fc..bf894f0bf1321 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4509,7 +4509,7 @@ void helper_sve2p1_##NAME##qv_##SUF(void *vd, void *vn, void *vg, \ TYPE data[ARM_MAX_VQ]; \ for (unsigned s = 0; s < segments; s++) { \ uint16_t pg = *(uint16_t *)(vg + H1_2(s * 2)); \ - TYPE nn = *(TYPE *)(vn + H(s * 16 + H(e))); \ + TYPE nn = *(TYPE *)(vn + (s * 16 + H(e))); \ data[s] = (pg >> e) & 1 ? nn : IDENT; \ } \ *(TYPE *)(vd + H(e)) = FUNC##_reduce(data, status, segments); \ From 82a1c5c661ef9ab567b7946b75240963c153a3b0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:31 +0100 Subject: [PATCH 0089/1794] target/arm: Honour FPCR.AH=1 default NaN value in FMAXNMQV, FMINNMQV The FMAXNMQV and FMINNMQV insns use the default NaN as their identity value for inactive source vector elements. We open-coded this in sve_helper.c, hoping to avoid a function call. However, this fails to account for FPCR.AH=1 changing the default NaN value to set the sign bit. Use a call to floatN_default_nan() to obtain this value. Fixes: 1de7ecfc12d05 ("target/arm: Implement FADDQV, F{MIN, MAX}{NM}QV for SVE2p1") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-10-peter.maydell@linaro.org --- target/arm/tcg/sve_helper.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index bf894f0bf1321..803f0a094dcd6 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4484,33 +4484,35 @@ static TYPE FUNC##_reduce(TYPE *data, float_status *status, uintptr_t n) \ } \ } \ uint64_t helper_sve_##NAME##v_##SUF(void *vn, void *vg, \ - float_status *s, uint32_t desc) \ + float_status *status, uint32_t desc) \ { \ uintptr_t i, oprsz = simd_oprsz(desc), maxsz = simd_data(desc); \ TYPE data[sizeof(ARMVectorReg) / sizeof(TYPE)]; \ + TYPE ident = IDENT; \ for (i = 0; i < oprsz; ) { \ uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ do { \ TYPE nn = *(TYPE *)(vn + H(i)); \ - *(TYPE *)((void *)data + i) = (pg & 1 ? nn : IDENT); \ + *(TYPE *)((void *)data + i) = (pg & 1 ? nn : ident); \ i += sizeof(TYPE), pg >>= sizeof(TYPE); \ } while (i & 15); \ } \ for (; i < maxsz; i += sizeof(TYPE)) { \ - *(TYPE *)((void *)data + i) = IDENT; \ + *(TYPE *)((void *)data + i) = ident; \ } \ - return FUNC##_reduce(data, s, maxsz / sizeof(TYPE)); \ + return FUNC##_reduce(data, status, maxsz / sizeof(TYPE)); \ } \ void helper_sve2p1_##NAME##qv_##SUF(void *vd, void *vn, void *vg, \ float_status *status, uint32_t desc) \ { \ unsigned oprsz = simd_oprsz(desc), segments = oprsz / 16; \ + TYPE ident = IDENT; \ for (unsigned e = 0; e < 16; e += sizeof(TYPE)) { \ TYPE data[ARM_MAX_VQ]; \ for (unsigned s = 0; s < segments; s++) { \ uint16_t pg = *(uint16_t *)(vg + H1_2(s * 2)); \ TYPE nn = *(TYPE *)(vn + (s * 16 + H(e))); \ - data[s] = (pg >> e) & 1 ? nn : IDENT; \ + data[s] = (pg >> e) & 1 ? nn : ident; \ } \ *(TYPE *)(vd + H(e)) = FUNC##_reduce(data, status, segments); \ } \ @@ -4521,14 +4523,17 @@ DO_REDUCE(fadd,h, float16, H1_2, float16_add, float16_zero) DO_REDUCE(fadd,s, float32, H1_4, float32_add, float32_zero) DO_REDUCE(fadd,d, float64, H1_8, float64_add, float64_zero) -/* Identity is floatN_default_nan, without the function call. */ -DO_REDUCE(fminnm,h, float16, H1_2, float16_minnum, 0x7E00) -DO_REDUCE(fminnm,s, float32, H1_4, float32_minnum, 0x7FC00000) -DO_REDUCE(fminnm,d, float64, H1_8, float64_minnum, 0x7FF8000000000000ULL) +/* + * We can't avoid the function call for the default NaN value, because + * it changes when FPCR.AH is set. + */ +DO_REDUCE(fminnm,h, float16, H1_2, float16_minnum, float16_default_nan(status)) +DO_REDUCE(fminnm,s, float32, H1_4, float32_minnum, float32_default_nan(status)) +DO_REDUCE(fminnm,d, float64, H1_8, float64_minnum, float64_default_nan(status)) -DO_REDUCE(fmaxnm,h, float16, H1_2, float16_maxnum, 0x7E00) -DO_REDUCE(fmaxnm,s, float32, H1_4, float32_maxnum, 0x7FC00000) -DO_REDUCE(fmaxnm,d, float64, H1_8, float64_maxnum, 0x7FF8000000000000ULL) +DO_REDUCE(fmaxnm,h, float16, H1_2, float16_maxnum, float16_default_nan(status)) +DO_REDUCE(fmaxnm,s, float32, H1_4, float32_maxnum, float32_default_nan(status)) +DO_REDUCE(fmaxnm,d, float64, H1_8, float64_maxnum, float64_default_nan(status)) DO_REDUCE(fmin,h, float16, H1_2, float16_min, float16_infinity) DO_REDUCE(fmin,s, float32, H1_4, float32_min, float32_infinity) From 082933a1f7d3c8e4a9e999c3d284928ef866c67d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:32 +0100 Subject: [PATCH 0090/1794] target/arm: Make LD1Q decode and trans fn agree about a->u For the LD1Q instruction (gather load of quadwords) we use the LD1_zprz pattern with MO_128 elements. At this element size there is no signed vs unsigned distinction, and we only set the 'u' bit in the arg_LD1_zprz struct because we share the code and decode struct with smaller element sizes. However, we set u=0 in the decode pattern line but then accidentally asserted that it was 1 in the trans function. Since our usual convention is that the "default" is unsigned and we only mark operations as signed when they really do need to extend, change the decode pattern line to set u=1 to match the assert. Fixes: d2aa9a804ee6 ("target/arm: Implement LD1Q, ST1Q for SVE2p1") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-11-peter.maydell@linaro.org --- target/arm/tcg/sve.decode | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index a77b725c876cd..aea7f5197307f 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1345,7 +1345,7 @@ LD1_zprz 1100010 11 1. ..... 11. ... ..... ..... \ # LD1Q LD1_zprz 1100 0100 000 rm:5 101 pg:3 rn:5 rd:5 \ - &rprr_gather_load u=0 ff=0 xs=2 esz=4 msz=4 scale=0 + &rprr_gather_load u=1 ff=0 xs=2 esz=4 msz=4 scale=0 # SVE 64-bit gather load (vector plus immediate) LD1_zpiz 1100010 .. 01 ..... 1.. ... ..... ..... \ From f19310b23a00b5c19f930e4d57fc298744d11740 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Tue, 15 Jul 2025 00:01:38 +0800 Subject: [PATCH 0091/1794] hvf: arm: Add permission check in GIC sysreg handlers Quoting Peter Maydell: " hvf_sysreg_read_cp() and hvf_sysreg_write_cp() do not check the .access field of the ARMCPRegInfo to ensure that they forbid writes to registers that are marked with a .access field that says they're read-only (and ditto reads to write-only registers). " Before we add more registers in GIC sysreg handlers, let's get it correct by adding the .access checks to hvf_sysreg_read_cp() and hvf_sysreg_write_cp(). With that, a sysreg access with invalid permission will result in an UNDEFINED exception. Suggested-by: Peter Maydell Signed-off-by: Zenghui Yu Message-id: 20250714160139.10404-2-zenghui.yu@linux.dev Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 8f93e42b34b92..861657df966ae 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1263,6 +1263,9 @@ static bool hvf_sysreg_read_cp(CPUState *cpu, uint32_t reg, uint64_t *val) ri = get_arm_cp_reginfo(arm_cpu->cp_regs, hvf_reg2cp_reg(reg)); if (ri) { + if (!cp_access_ok(1, ri, true)) { + return false; + } if (ri->accessfn) { if (ri->accessfn(env, ri, true) != CP_ACCESS_OK) { return false; @@ -1543,6 +1546,9 @@ static bool hvf_sysreg_write_cp(CPUState *cpu, uint32_t reg, uint64_t val) ri = get_arm_cp_reginfo(arm_cpu->cp_regs, hvf_reg2cp_reg(reg)); if (ri) { + if (!cp_access_ok(1, ri, false)) { + return false; + } if (ri->accessfn) { if (ri->accessfn(env, ri, false) != CP_ACCESS_OK) { return false; From e6da704b711d5d731e4d933ad56cbbc25ee0a825 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Tue, 15 Jul 2025 00:01:39 +0800 Subject: [PATCH 0092/1794] hvf: arm: Emulate ICC_RPR_EL1 accesses properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit a2260983c655 ("hvf: arm: Add support for GICv3") added GICv3 support by implementing emulation for a few system registers. ICC_RPR_EL1 was defined but not plugged in the sysreg handlers (for no good reason). Fix it. Fixes: a2260983c655 ("hvf: arm: Add support for GICv3") Signed-off-by: Zenghui Yu Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250714160139.10404-3-zenghui.yu@linux.dev Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 861657df966ae..bd6b5d11de8fe 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1361,6 +1361,7 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val) case SYSREG_ICC_IGRPEN0_EL1: case SYSREG_ICC_IGRPEN1_EL1: case SYSREG_ICC_PMR_EL1: + case SYSREG_ICC_RPR_EL1: case SYSREG_ICC_SGI0R_EL1: case SYSREG_ICC_SGI1R_EL1: case SYSREG_ICC_SRE_EL1: @@ -1678,6 +1679,7 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_ICC_IGRPEN0_EL1: case SYSREG_ICC_IGRPEN1_EL1: case SYSREG_ICC_PMR_EL1: + case SYSREG_ICC_RPR_EL1: case SYSREG_ICC_SGI0R_EL1: case SYSREG_ICC_SGI1R_EL1: case SYSREG_ICC_SRE_EL1: From 6f7f3419cce86553dd239f10a5deb9ab872bd8c2 Mon Sep 17 00:00:00 2001 From: Henry Kleynhans Date: Wed, 22 Dec 2021 15:05:59 +0000 Subject: [PATCH 0093/1794] crypto: load all certificates in X509 CA file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some CA files may contain multiple intermediaries and roots of trust. These may not fit into the hard-coded limit of 16. Extend the validation code to allocate enough space to load all of the certificates present in the CA file and ensure they are cleaned up. Reviewed-by: Daniel P. Berrangé Signed-off-by: Henry Kleynhans [DB: drop MAX_CERTS constant & whitespace tweaks] Signed-off-by: Daniel P. Berrangé --- crypto/tlscredsx509.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c index 63a72fe47c8ef..cd1f504471574 100644 --- a/crypto/tlscredsx509.c +++ b/crypto/tlscredsx509.c @@ -426,9 +426,8 @@ qcrypto_tls_creds_load_cert(QCryptoTLSCredsX509 *creds, static int qcrypto_tls_creds_load_ca_cert_list(QCryptoTLSCredsX509 *creds, const char *certFile, - gnutls_x509_crt_t *certs, - unsigned int certMax, - size_t *ncerts, + gnutls_x509_crt_t **certs, + unsigned int *ncerts, Error **errp) { gnutls_datum_t data; @@ -449,20 +448,18 @@ qcrypto_tls_creds_load_ca_cert_list(QCryptoTLSCredsX509 *creds, data.data = (unsigned char *)buf; data.size = strlen(buf); - if (gnutls_x509_crt_list_import(certs, &certMax, &data, - GNUTLS_X509_FMT_PEM, 0) < 0) { + if (gnutls_x509_crt_list_import2(certs, ncerts, &data, + GNUTLS_X509_FMT_PEM, 0) < 0) { error_setg(errp, "Unable to import CA certificate list %s", certFile); return -1; } - *ncerts = certMax; return 0; } -#define MAX_CERTS 16 static int qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, bool isServer, @@ -471,12 +468,11 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, Error **errp) { gnutls_x509_crt_t cert = NULL; - gnutls_x509_crt_t cacerts[MAX_CERTS]; - size_t ncacerts = 0; + gnutls_x509_crt_t *cacerts = NULL; + unsigned int ncacerts = 0; size_t i; int ret = -1; - memset(cacerts, 0, sizeof(cacerts)); if (certFile && access(certFile, R_OK) == 0) { cert = qcrypto_tls_creds_load_cert(creds, @@ -488,8 +484,9 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, } if (access(cacertFile, R_OK) == 0) { if (qcrypto_tls_creds_load_ca_cert_list(creds, - cacertFile, cacerts, - MAX_CERTS, &ncacerts, + cacertFile, + &cacerts, + &ncacerts, errp) < 0) { goto cleanup; } @@ -526,6 +523,8 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, for (i = 0; i < ncacerts; i++) { gnutls_x509_crt_deinit(cacerts[i]); } + g_free(cacerts); + return ret; } From 7724ca9a772594b96939ff549c74f46e11d7870b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 16 Jul 2025 19:28:11 +0200 Subject: [PATCH 0094/1794] accel/hvf: Display executable bit as 'X' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Developers are accustomed to read RWX, not RWE. Replace E -> X. Reported-by: Alex Bennée Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Reviewed-by: Mads Ynddal Reviewed-by: Xiaoyao Li Signed-off-by: Peter Maydell --- accel/hvf/hvf-all.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index e67a8105a66a0..0a4b498e8369c 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -84,7 +84,7 @@ static int do_hvf_set_memory(hvf_slot *slot, hv_memory_flags_t flags) trace_hvf_vm_map(slot->start, slot->size, slot->mem, flags, flags & HV_MEMORY_READ ? 'R' : '-', flags & HV_MEMORY_WRITE ? 'W' : '-', - flags & HV_MEMORY_EXEC ? 'E' : '-'); + flags & HV_MEMORY_EXEC ? 'X' : '-'); ret = hv_vm_map(slot->mem, slot->start, slot->size, flags); assert_hvf_ok(ret); return 0; From 2c047bdb77e8f636936edd9ac5000521c9580477 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 Jul 2025 17:34:06 +0000 Subject: [PATCH 0095/1794] tcg/optimize: Don't fold INDEX_op_and_vec to extract There is no such thing as vector extract. Fixes: 932522a9ddc1 ("tcg/optimize: Fold and to extract during optimize") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3036 Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Tested-by: Pierrick Bouvier Reviewed-by: Peter Maydell --- tcg/optimize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 62a128bc9b794..3638ab9fea051 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1454,7 +1454,7 @@ static bool fold_and(OptContext *ctx, TCGOp *op) a_mask = t1->z_mask & ~t2->o_mask; if (!fold_masks_zosa_int(ctx, op, z_mask, o_mask, s_mask, a_mask)) { - if (ti_is_const(t2)) { + if (op->opc == INDEX_op_and && ti_is_const(t2)) { /* * Canonicalize on extract, if valid. This aids x86 with its * 2 operand MOVZBL and 2 operand AND, selecting the TCGOpcode From 0d70c5aa1bbfb0f5099d53d6e084337a8246cc0c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 21 Jul 2025 12:51:31 +0200 Subject: [PATCH 0096/1794] rust: devices are not staticlibs This is only cosmetic for now, but hopefully later on Meson will parse more of Cargo.toml. Devices are linked into a staticlib but are not staticlibs themselves. Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/Cargo.toml | 3 --- rust/hw/timer/hpet/Cargo.toml | 3 --- 2 files changed, 6 deletions(-) diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 003ef9613d4e9..88ef110507de7 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -12,9 +12,6 @@ license.workspace = true repository.workspace = true rust-version.workspace = true -[lib] -crate-type = ["staticlib"] - [dependencies] bilge = { version = "0.2.0" } bilge-impl = { version = "0.2.0" } diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index 6f075027843aa..ac5df23c1d00b 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -10,9 +10,6 @@ license.workspace = true repository.workspace = true rust-version.workspace = true -[lib] -crate-type = ["staticlib"] - [dependencies] qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } From f63000d943bd7db60b338f12546a96efaa6058aa Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Fri, 18 Jul 2025 10:00:52 +0300 Subject: [PATCH 0097/1794] rust/pl011: merge device_class.rs into device.rs The split was a relic of early development and is not necessary. Signed-off-by: Manos Pitsidianakis Link: https://lore.kernel.org/r/20250718-rust-pl011-cleanup-v1-1-c71b1d6a69a5@linaro.org Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 103 +++++++++++++++++++++++-- rust/hw/char/pl011/src/device_class.rs | 103 ------------------------- rust/hw/char/pl011/src/lib.rs | 1 - 3 files changed, 96 insertions(+), 111 deletions(-) delete mode 100644 rust/hw/char/pl011/src/device_class.rs diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 5b53f2649f161..ceb71dd99b661 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -2,9 +2,14 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use std::{ffi::CStr, mem::size_of}; +use std::{ + ffi::{c_int, c_void, CStr}, + mem::size_of, + ptr::NonNull, +}; use qemu_api::{ + bindings::{qdev_prop_bool, qdev_prop_chr}, chardev::{CharBackend, Chardev, Event}, impl_vmstate_forward, irq::{IRQState, InterruptSource}, @@ -18,12 +23,11 @@ use qemu_api::{ sysbus::{SysBusDevice, SysBusDeviceImpl}, uninit_field_mut, vmstate::VMStateDescription, + vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused, + zeroable::Zeroable, }; -use crate::{ - device_class, - registers::{self, Interrupt, RegisterOffset}, -}; +use crate::registers::{self, Interrupt, RegisterOffset}; // TODO: You must disable the UART before any of the control registers are // reprogrammed. When the UART is disabled in the middle of transmission or @@ -173,10 +177,10 @@ impl ObjectImpl for PL011State { impl DeviceImpl for PL011State { fn properties() -> &'static [Property] { - &device_class::PL011_PROPERTIES + &PL011_PROPERTIES } fn vmsd() -> Option<&'static VMStateDescription> { - Some(&device_class::VMSTATE_PL011) + Some(&VMSTATE_PL011) } const REALIZE: Option qemu_api::Result<()>> = Some(Self::realize); } @@ -712,3 +716,88 @@ impl PL011Impl for PL011Luminary { impl DeviceImpl for PL011Luminary {} impl ResettablePhasesImpl for PL011Luminary {} impl SysBusDeviceImpl for PL011Luminary {} + +extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { + let state = NonNull::new(opaque).unwrap().cast::(); + unsafe { state.as_ref().migrate_clock } +} + +/// Migration subsection for [`PL011State`] clock. +static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { + name: c"pl011/clock".as_ptr(), + version_id: 1, + minimum_version_id: 1, + needed: Some(pl011_clock_needed), + fields: vmstate_fields! { + vmstate_clock!(PL011State, clock), + }, + ..Zeroable::ZERO +}; + +extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { + let state = NonNull::new(opaque).unwrap().cast::(); + let result = unsafe { state.as_ref().post_load(version_id as u32) }; + if result.is_err() { + -1 + } else { + 0 + } +} + +static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription { + name: c"pl011/regs".as_ptr(), + version_id: 2, + minimum_version_id: 2, + fields: vmstate_fields! { + vmstate_of!(PL011Registers, flags), + vmstate_of!(PL011Registers, line_control), + vmstate_of!(PL011Registers, receive_status_error_clear), + vmstate_of!(PL011Registers, control), + vmstate_of!(PL011Registers, dmacr), + vmstate_of!(PL011Registers, int_enabled), + vmstate_of!(PL011Registers, int_level), + vmstate_of!(PL011Registers, read_fifo), + vmstate_of!(PL011Registers, ilpr), + vmstate_of!(PL011Registers, ibrd), + vmstate_of!(PL011Registers, fbrd), + vmstate_of!(PL011Registers, ifl), + vmstate_of!(PL011Registers, read_pos), + vmstate_of!(PL011Registers, read_count), + vmstate_of!(PL011Registers, read_trigger), + }, + ..Zeroable::ZERO +}; + +pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { + name: c"pl011".as_ptr(), + version_id: 2, + minimum_version_id: 2, + post_load: Some(pl011_post_load), + fields: vmstate_fields! { + vmstate_unused!(core::mem::size_of::()), + vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell), + }, + subsections: vmstate_subsections! { + VMSTATE_PL011_CLOCK + }, + ..Zeroable::ZERO +}; + +qemu_api::declare_properties! { + PL011_PROPERTIES, + qemu_api::define_property!( + c"chardev", + PL011State, + char_backend, + unsafe { &qdev_prop_chr }, + CharBackend + ), + qemu_api::define_property!( + c"migrate-clk", + PL011State, + migrate_clock, + unsafe { &qdev_prop_bool }, + bool, + default = true + ), +} diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs deleted file mode 100644 index d328d846323f6..0000000000000 --- a/rust/hw/char/pl011/src/device_class.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use std::{ - ffi::{c_int, c_void}, - ptr::NonNull, -}; - -use qemu_api::{ - bindings::{qdev_prop_bool, qdev_prop_chr}, - prelude::*, - vmstate::VMStateDescription, - vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused, - zeroable::Zeroable, -}; - -use crate::device::{PL011Registers, PL011State}; - -extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { - let state = NonNull::new(opaque).unwrap().cast::(); - unsafe { state.as_ref().migrate_clock } -} - -/// Migration subsection for [`PL011State`] clock. -static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { - name: c"pl011/clock".as_ptr(), - version_id: 1, - minimum_version_id: 1, - needed: Some(pl011_clock_needed), - fields: vmstate_fields! { - vmstate_clock!(PL011State, clock), - }, - ..Zeroable::ZERO -}; - -extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { - let state = NonNull::new(opaque).unwrap().cast::(); - let result = unsafe { state.as_ref().post_load(version_id as u32) }; - if result.is_err() { - -1 - } else { - 0 - } -} - -static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription { - name: c"pl011/regs".as_ptr(), - version_id: 2, - minimum_version_id: 2, - fields: vmstate_fields! { - vmstate_of!(PL011Registers, flags), - vmstate_of!(PL011Registers, line_control), - vmstate_of!(PL011Registers, receive_status_error_clear), - vmstate_of!(PL011Registers, control), - vmstate_of!(PL011Registers, dmacr), - vmstate_of!(PL011Registers, int_enabled), - vmstate_of!(PL011Registers, int_level), - vmstate_of!(PL011Registers, read_fifo), - vmstate_of!(PL011Registers, ilpr), - vmstate_of!(PL011Registers, ibrd), - vmstate_of!(PL011Registers, fbrd), - vmstate_of!(PL011Registers, ifl), - vmstate_of!(PL011Registers, read_pos), - vmstate_of!(PL011Registers, read_count), - vmstate_of!(PL011Registers, read_trigger), - }, - ..Zeroable::ZERO -}; - -pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { - name: c"pl011".as_ptr(), - version_id: 2, - minimum_version_id: 2, - post_load: Some(pl011_post_load), - fields: vmstate_fields! { - vmstate_unused!(core::mem::size_of::()), - vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell), - }, - subsections: vmstate_subsections! { - VMSTATE_PL011_CLOCK - }, - ..Zeroable::ZERO -}; - -qemu_api::declare_properties! { - PL011_PROPERTIES, - qemu_api::define_property!( - c"chardev", - PL011State, - char_backend, - unsafe { &qdev_prop_chr }, - CharBackend - ), - qemu_api::define_property!( - c"migrate-clk", - PL011State, - migrate_clock, - unsafe { &qdev_prop_bool }, - bool, - default = true - ), -} diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 5c4fbc9d148fc..2b70d2ff56041 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -13,7 +13,6 @@ //! the [`registers`] module for register types. mod device; -mod device_class; mod registers; pub use device::pl011_create; From 2b1791323e7ce043cbc3857699e5d5b0ad021cbc Mon Sep 17 00:00:00 2001 From: Tanish Desai Date: Tue, 22 Jul 2025 11:43:52 +0000 Subject: [PATCH 0098/1794] tracetool: removed the unused vcpu property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vcpu property is no longer used in these backends. Removing it avoids unnecessary checks and simplifies the code generation for these trace backends. Reviewed-by: Stefan Hajnoczi Reviewed-by: Alex Bennée Signed-off-by: Tanish Desai Message-id: 20250722114352.3624-1-tanishdesai37@gmail.com Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/__init__.py | 2 +- scripts/tracetool/backend/log.py | 6 +----- scripts/tracetool/backend/simple.py | 6 +----- scripts/tracetool/backend/syslog.py | 6 +----- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 6dfcbf71e1b88..2ae2e562d6444 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -219,7 +219,7 @@ class Event(object): r"(?:(?:(?P\".+),)?\s*(?P\".+))?" r"\s*") - _VALID_PROPS = set(["disable", "vcpu"]) + _VALID_PROPS = set(["disable"]) def __init__(self, name, props, fmt, args, lineno, filename, orig=None, event_trans=None, event_exec=None): diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index 17ba1cd90ebb8..5c9d09dd115f2 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -29,11 +29,7 @@ def generate_h(event, group): if len(event.args) > 0: argnames = ", " + argnames - if "vcpu" in event.properties: - # already checked on the generic format code - cond = "true" - else: - cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) + cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) out(' if (%(cond)s && qemu_loglevel_mask(LOG_TRACE)) {', ' if (message_with_timestamp) {', diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py index 2688d4b64b33f..7c84c06b20084 100644 --- a/scripts/tracetool/backend/simple.py +++ b/scripts/tracetool/backend/simple.py @@ -37,11 +37,7 @@ def generate_h_begin(events, group): def generate_h(event, group): event_id = 'TRACE_' + event.name.upper() - if "vcpu" in event.properties: - # already checked on the generic format code - cond = "true" - else: - cond = "trace_event_get_state(%s)" % event_id + cond = "trace_event_get_state(%s)" % event_id out(' if (%(cond)s) {', ' _simple_%(api)s(%(args)s);', ' }', diff --git a/scripts/tracetool/backend/syslog.py b/scripts/tracetool/backend/syslog.py index 5a3a00fe310ae..3f82e54aabfdf 100644 --- a/scripts/tracetool/backend/syslog.py +++ b/scripts/tracetool/backend/syslog.py @@ -28,11 +28,7 @@ def generate_h(event, group): if len(event.args) > 0: argnames = ", " + argnames - if "vcpu" in event.properties: - # already checked on the generic format code - cond = "true" - else: - cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) + cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) out(' if (%(cond)s) {', '#line %(event_lineno)d "%(event_filename)s"', From 9e601684dc24a521bb1d23215a63e5c6e79ea0bb Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 22 Jul 2025 15:48:48 -0400 Subject: [PATCH 0099/1794] Update version for the v10.1.0-rc0 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 54e6ccf8546b6..c52f1cc9fe4b6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.0.50 +10.0.90 From 2251f9ac9261cda05b6b19e9ba329b15d9d89bae Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 16 Jul 2025 15:26:46 -0300 Subject: [PATCH 0100/1794] migration: HMP: Fix possible out-of-bounds access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity has caught a bug in the formatting of time intervals for postcopy latency distribution display in 'info migrate'. While bounds checking the labels array, sizeof is incorrectly being used. ARRAY_SIZE is the correct form of obtaining the size of an array. Fixes: 3345fb3b6d ("migration/postcopy: Add latency distribution report for blocktime") Resolves: Coverity CID 1612248 Suggested-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250716182648.30202-2-farosas@suse.de Signed-off-by: Fabiano Rosas --- migration/migration-hmp-cmds.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index cef5608210e83..bb954881d782b 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -57,11 +57,9 @@ static const gchar *format_time_str(uint64_t us) const char *units[] = {"us", "ms", "sec"}; int index = 0; - while (us > 1000) { + while (us > 1000 && index + 1 < ARRAY_SIZE(units)) { us /= 1000; - if (++index >= (sizeof(units) - 1)) { - break; - } + index++; } return g_strdup_printf("%"PRIu64" %s", us, units[index]); From fd1514cbd97bcad5b3dc5d002cab6fee4d7cd45e Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 16 Jul 2025 15:26:47 -0300 Subject: [PATCH 0101/1794] migration: HMP: Fix postcopy latency distribution label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the loop condition to avoid having a label with "1000 us" instead of "1 ms". Reported-by: Prasad Pandit Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250716182648.30202-3-farosas@suse.de Signed-off-by: Fabiano Rosas --- migration/migration-hmp-cmds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index bb954881d782b..a8b879c9d60ea 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -57,7 +57,7 @@ static const gchar *format_time_str(uint64_t us) const char *units[] = {"us", "ms", "sec"}; int index = 0; - while (us > 1000 && index + 1 < ARRAY_SIZE(units)) { + while (us >= 1000 && index + 1 < ARRAY_SIZE(units)) { us /= 1000; index++; } From eaec556bc88cc1196f7bbf23d5de311aac1d812f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 21 Jul 2025 14:39:13 +0100 Subject: [PATCH 0102/1794] migration: show error message when postcopy fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'info migrate' command only shows the error message when the migration state is 'failed'. When postcopy is used, however, the 'postcopy-paused' state is used instead of 'failed', so we must show the error message there too. Signed-off-by: Daniel P. Berrangé Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250721133913.2914669-1-berrange@redhat.com [line break to satisfy checkpatch] Signed-off-by: Fabiano Rosas --- migration/migration-hmp-cmds.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index a8b879c9d60ea..0fc21f06473df 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -151,7 +151,9 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) if (info->has_status) { monitor_printf(mon, "Status: \t\t%s", MigrationStatus_str(info->status)); - if (info->status == MIGRATION_STATUS_FAILED && info->error_desc) { + if ((info->status == MIGRATION_STATUS_FAILED || + info->status == MIGRATION_STATUS_POSTCOPY_PAUSED) && + info->error_desc) { monitor_printf(mon, " (%s)\n", info->error_desc); } else { monitor_printf(mon, "\n"); From 24ad5e19952b326796c8a3a1595c57ff180dab84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 18 Jul 2025 16:05:11 +0100 Subject: [PATCH 0103/1794] crypto: implement workaround for GNUTLS thread safety problems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When TLS 1.3 is negotiated on a TLS session, GNUTLS will perform automatic rekeying of the session after 16 million records. This is done for all algorithms except CHACHA20_POLY1305 which does not require rekeying. Unfortunately the rekeying breaks GNUTLS' promise that it is safe to use a gnutls_session_t object concurrently from multiple threads if they are exclusively calling gnutls_record_send/recv. This patch implements a workaround for QEMU that adds a mutex lock around any gnutls_record_send/recv call to serialize execution within GNUTLS code. When GNUTLS calls into the push/pull functions we can release the lock so the OS level I/O calls can at least have some parallelism. The big downside of this is that the actual encryption/decryption code is fully serialized, which will halve performance of that cipher operations if two threads are contending. The workaround is not enabled by default, since most use of GNUTLS in QEMU does not tickle the problem, only non-multifd migration with a return path open is affected. Fortunately the migration code also won't trigger the halving of performance, since only the outbound channel diretion needs to sustain high data rates, the inbound direction is low volume. Signed-off-by: Daniel P. Berrangé Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250718150514.2635338-2-berrange@redhat.com [add stub for qcrypto_tls_session_require_thread_safety; fix unused var] Signed-off-by: Fabiano Rosas --- crypto/tlssession.c | 92 +++++++++++++++++++++++++++++++++-- include/crypto/tlssession.h | 14 ++++++ meson.build | 9 ++++ meson_options.txt | 2 + scripts/meson-buildoptions.sh | 5 ++ 5 files changed, 119 insertions(+), 3 deletions(-) diff --git a/crypto/tlssession.c b/crypto/tlssession.c index 6d8f8df62323b..baef878fa0cc0 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/thread.h" #include "crypto/tlssession.h" #include "crypto/tlscredsanon.h" #include "crypto/tlscredspsk.h" @@ -51,6 +52,14 @@ struct QCryptoTLSSession { */ Error *rerr; Error *werr; + + /* + * Used to protect against broken GNUTLS thread safety + * https://gitlab.com/gnutls/gnutls/-/issues/1717 + */ + bool requireThreadSafety; + bool lockEnabled; + QemuMutex lock; }; @@ -69,6 +78,7 @@ qcrypto_tls_session_free(QCryptoTLSSession *session) g_free(session->peername); g_free(session->authzid); object_unref(OBJECT(session->creds)); + qemu_mutex_destroy(&session->lock); g_free(session); } @@ -84,10 +94,19 @@ qcrypto_tls_session_push(void *opaque, const void *buf, size_t len) return -1; }; + if (session->lockEnabled) { + qemu_mutex_unlock(&session->lock); + } + error_free(session->werr); session->werr = NULL; ret = session->writeFunc(buf, len, session->opaque, &session->werr); + + if (session->lockEnabled) { + qemu_mutex_lock(&session->lock); + } + if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { errno = EAGAIN; return -1; @@ -114,7 +133,16 @@ qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) error_free(session->rerr); session->rerr = NULL; + if (session->lockEnabled) { + qemu_mutex_unlock(&session->lock); + } + ret = session->readFunc(buf, len, session->opaque, &session->rerr); + + if (session->lockEnabled) { + qemu_mutex_lock(&session->lock); + } + if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { errno = EAGAIN; return -1; @@ -153,6 +181,8 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, session->creds = creds; object_ref(OBJECT(creds)); + qemu_mutex_init(&session->lock); + if (creds->endpoint != endpoint) { error_setg(errp, "Credentials endpoint doesn't match session"); goto error; @@ -289,6 +319,11 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, return NULL; } +void qcrypto_tls_session_require_thread_safety(QCryptoTLSSession *sess) +{ + sess->requireThreadSafety = true; +} + static int qcrypto_tls_session_check_certificate(QCryptoTLSSession *session, Error **errp) @@ -480,7 +515,17 @@ qcrypto_tls_session_write(QCryptoTLSSession *session, size_t len, Error **errp) { - ssize_t ret = gnutls_record_send(session->handle, buf, len); + ssize_t ret; + + if (session->lockEnabled) { + qemu_mutex_lock(&session->lock); + } + + ret = gnutls_record_send(session->handle, buf, len); + + if (session->lockEnabled) { + qemu_mutex_unlock(&session->lock); + } if (ret < 0) { if (ret == GNUTLS_E_AGAIN) { @@ -509,7 +554,17 @@ qcrypto_tls_session_read(QCryptoTLSSession *session, bool gracefulTermination, Error **errp) { - ssize_t ret = gnutls_record_recv(session->handle, buf, len); + ssize_t ret; + + if (session->lockEnabled) { + qemu_mutex_lock(&session->lock); + } + + ret = gnutls_record_recv(session->handle, buf, len); + + if (session->lockEnabled) { + qemu_mutex_unlock(&session->lock); + } if (ret < 0) { if (ret == GNUTLS_E_AGAIN) { @@ -545,8 +600,29 @@ int qcrypto_tls_session_handshake(QCryptoTLSSession *session, Error **errp) { - int ret = gnutls_handshake(session->handle); + int ret; + ret = gnutls_handshake(session->handle); + if (!ret) { +#ifdef CONFIG_GNUTLS_BUG1717_WORKAROUND + gnutls_cipher_algorithm_t cipher = + gnutls_cipher_get(session->handle); + + /* + * Any use of rekeying in TLS 1.3 is unsafe for + * a gnutls with bug 1717, however, we know that + * QEMU won't initiate manual rekeying. Thus we + * only have to protect against automatic rekeying + * which doesn't trigger with CHACHA20 + */ + if (session->requireThreadSafety && + gnutls_protocol_get_version(session->handle) == + GNUTLS_TLS1_3 && + cipher != GNUTLS_CIPHER_CHACHA20_POLY1305) { + session->lockEnabled = true; + } +#endif + session->handshakeComplete = true; return QCRYPTO_TLS_HANDSHAKE_COMPLETE; } @@ -584,8 +660,15 @@ qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp) return 0; } + if (session->lockEnabled) { + qemu_mutex_lock(&session->lock); + } ret = gnutls_bye(session->handle, GNUTLS_SHUT_WR); + if (session->lockEnabled) { + qemu_mutex_unlock(&session->lock); + } + if (!ret) { return QCRYPTO_TLS_BYE_COMPLETE; } @@ -651,6 +734,9 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED, return NULL; } +void qcrypto_tls_session_require_thread_safety(QCryptoTLSSession *sess) +{ +} void qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED) diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h index d77ae0d423709..2f62ce2d67a3f 100644 --- a/include/crypto/tlssession.h +++ b/include/crypto/tlssession.h @@ -165,6 +165,20 @@ void qcrypto_tls_session_free(QCryptoTLSSession *sess); G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSSession, qcrypto_tls_session_free) +/** + * qcrypto_tls_session_require_thread_safety: + * @sess: the TLS session object + * + * Mark that this TLS session will require thread safety + * for concurrent I/O in both directions. This must be + * called before the handshake is performed. + * + * This will activate a workaround for GNUTLS thread + * safety issues, where appropriate for the negotiated + * TLS session parameters. + */ +void qcrypto_tls_session_require_thread_safety(QCryptoTLSSession *sess); + /** * qcrypto_tls_session_check_credentials: * @sess: the TLS session object diff --git a/meson.build b/meson.build index c2bc3eeedce91..e53cd5b413847 100644 --- a/meson.build +++ b/meson.build @@ -1809,6 +1809,7 @@ endif gnutls = not_found gnutls_crypto = not_found +gnutls_bug1717_workaround = false if get_option('gnutls').enabled() or (get_option('gnutls').auto() and have_system) # For general TLS support our min gnutls matches # that implied by our platform support matrix @@ -1834,6 +1835,12 @@ if get_option('gnutls').enabled() or (get_option('gnutls').auto() and have_syste method: 'pkg-config', required: get_option('gnutls')) endif + + if gnutls.found() and not get_option('gnutls-bug1717-workaround').disabled() + # XXX: when bug 1717 is resolved, add logic to probe for + # the GNUTLS fixed version number to handle the 'auto' case + gnutls_bug1717_workaround = true + endif endif # We prefer use of gnutls for crypto, unless the options @@ -2585,6 +2592,7 @@ config_host_data.set('CONFIG_KEYUTILS', keyutils.found()) config_host_data.set('CONFIG_GETTID', has_gettid) config_host_data.set('CONFIG_GNUTLS', gnutls.found()) config_host_data.set('CONFIG_GNUTLS_CRYPTO', gnutls_crypto.found()) +config_host_data.set('CONFIG_GNUTLS_BUG1717_WORKAROUND', gnutls_bug1717_workaround) config_host_data.set('CONFIG_TASN1', tasn1.found()) config_host_data.set('CONFIG_GCRYPT', gcrypt.found()) config_host_data.set('CONFIG_NETTLE', nettle.found()) @@ -4869,6 +4877,7 @@ summary_info += {'TLS priority': get_option('tls_priority')} summary_info += {'GNUTLS support': gnutls} if gnutls.found() summary_info += {' GNUTLS crypto': gnutls_crypto.found()} + summary_info += {' GNUTLS bug 1717 workaround': gnutls_bug1717_workaround } endif summary_info += {'libgcrypt': gcrypt} summary_info += {'nettle': nettle} diff --git a/meson_options.txt b/meson_options.txt index fff1521e580de..dd335307505fb 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -174,6 +174,8 @@ option('libcbor', type : 'feature', value : 'auto', description: 'libcbor support') option('gnutls', type : 'feature', value : 'auto', description: 'GNUTLS cryptography support') +option('gnutls-bug1717-workaround', type: 'feature', value : 'auto', + description: 'GNUTLS workaround for https://gitlab.com/gnutls/gnutls/-/issues/1717') option('nettle', type : 'feature', value : 'auto', description: 'nettle cryptography support') option('gcrypt', type : 'feature', value : 'auto', diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 0ebe6bc52a6b9..d559e260ed17e 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -123,6 +123,9 @@ meson_options_help() { printf "%s\n" ' gio use libgio for D-Bus support' printf "%s\n" ' glusterfs Glusterfs block device driver' printf "%s\n" ' gnutls GNUTLS cryptography support' + printf "%s\n" ' gnutls-bug1717-workaround' + printf "%s\n" ' GNUTLS workaround for' + printf "%s\n" ' https://gitlab.com/gnutls/gnutls/-/issues/1717' printf "%s\n" ' gtk GTK+ user interface' printf "%s\n" ' gtk-clipboard clipboard support for the gtk UI (EXPERIMENTAL, MAY HANG)' printf "%s\n" ' guest-agent Build QEMU Guest Agent' @@ -331,6 +334,8 @@ _meson_option_parse() { --disable-glusterfs) printf "%s" -Dglusterfs=disabled ;; --enable-gnutls) printf "%s" -Dgnutls=enabled ;; --disable-gnutls) printf "%s" -Dgnutls=disabled ;; + --enable-gnutls-bug1717-workaround) printf "%s" -Dgnutls-bug1717-workaround=enabled ;; + --disable-gnutls-bug1717-workaround) printf "%s" -Dgnutls-bug1717-workaround=disabled ;; --enable-gtk) printf "%s" -Dgtk=enabled ;; --disable-gtk) printf "%s" -Dgtk=disabled ;; --enable-gtk-clipboard) printf "%s" -Dgtk_clipboard=enabled ;; From edea818371bd7179b55f38d3b113d342251d8f9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 18 Jul 2025 16:05:12 +0100 Subject: [PATCH 0104/1794] io: add support for activating TLS thread safety workaround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a QIO_CHANNEL_FEATURE_CONCURRENT_IO feature flag. If this is set on a QIOChannelTLS session object, the TLS session will be marked as requiring thread safety, which will activate the workaround for GNUTLS bug 1717 if needed. Signed-off-by: Daniel P. Berrangé Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250718150514.2635338-3-berrange@redhat.com Signed-off-by: Fabiano Rosas --- include/io/channel.h | 1 + io/channel-tls.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/include/io/channel.h b/include/io/channel.h index 62b657109c7d9..234e5db70dd4a 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -46,6 +46,7 @@ enum QIOChannelFeature { QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY, QIO_CHANNEL_FEATURE_READ_MSG_PEEK, QIO_CHANNEL_FEATURE_SEEKABLE, + QIO_CHANNEL_FEATURE_CONCURRENT_IO, }; diff --git a/io/channel-tls.c b/io/channel-tls.c index db2ac1deae63e..a8248a9216b6a 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -241,6 +241,11 @@ void qio_channel_tls_handshake(QIOChannelTLS *ioc, { QIOTask *task; + if (qio_channel_has_feature(QIO_CHANNEL(ioc), + QIO_CHANNEL_FEATURE_CONCURRENT_IO)) { + qcrypto_tls_session_require_thread_safety(ioc->session); + } + task = qio_task_new(OBJECT(ioc), func, opaque, destroy); From eb3618e9e259ef93f5a1a76867fbccae540fcd61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 18 Jul 2025 16:05:13 +0100 Subject: [PATCH 0105/1794] migration: activate TLS thread safety workaround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When either the postcopy or return path capabilities are enabled, the migration code will use the primary channel for bidirectional I/O. If either of those capabilities are enabled, the migration code needs to mark the channel as expecting concurrent I/O in order to activate the thread safety workarounds for GNUTLS bug 1717 Closes: https://gitlab.com/qemu-project/qemu/-/issues/1937 Signed-off-by: Daniel P. Berrangé Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250718150514.2635338-4-berrange@redhat.com Signed-off-by: Fabiano Rosas --- migration/tls.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/migration/tls.c b/migration/tls.c index 5cbf952383691..284a6194b2bf7 100644 --- a/migration/tls.c +++ b/migration/tls.c @@ -90,6 +90,10 @@ void migration_tls_channel_process_incoming(MigrationState *s, trace_migration_tls_incoming_handshake_start(); qio_channel_set_name(QIO_CHANNEL(tioc), "migration-tls-incoming"); + if (migrate_postcopy_ram() || migrate_return_path()) { + qio_channel_set_feature(QIO_CHANNEL(tioc), + QIO_CHANNEL_FEATURE_CONCURRENT_IO); + } qio_channel_tls_handshake(tioc, migration_tls_incoming_handshake, NULL, @@ -149,6 +153,11 @@ void migration_tls_channel_connect(MigrationState *s, s->hostname = g_strdup(hostname); trace_migration_tls_outgoing_handshake_start(hostname); qio_channel_set_name(QIO_CHANNEL(tioc), "migration-tls-outgoing"); + + if (migrate_postcopy_ram() || migrate_return_path()) { + qio_channel_set_feature(QIO_CHANNEL(tioc), + QIO_CHANNEL_FEATURE_CONCURRENT_IO); + } qio_channel_tls_handshake(tioc, migration_tls_outgoing_handshake, s, From 0db6f798024ea6f57ecf2020209b761b50a01d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 18 Jul 2025 16:05:14 +0100 Subject: [PATCH 0106/1794] crypto: add tracing & warning about GNUTLS countermeasures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want some visibility on stderr when the GNUTLS thread safety countermeasures are activated, to encourage people to get the real fix deployed (once it exists). Some trace points will also help if we see any further wierd crash scenario we've not anticipated. Reviewed-by: Daniel P. Berrangé Signed-off-by: Daniel P. Berrangé Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250718150514.2635338-5-berrange@redhat.com [add missing include] Signed-off-by: Fabiano Rosas --- crypto/tlssession.c | 11 +++++++++++ crypto/trace-events | 2 ++ 2 files changed, 13 insertions(+) diff --git a/crypto/tlssession.c b/crypto/tlssession.c index baef878fa0cc0..86d407a14296e 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu/thread.h" #include "crypto/tlssession.h" #include "crypto/tlscredsanon.h" @@ -615,10 +616,20 @@ qcrypto_tls_session_handshake(QCryptoTLSSession *session, * only have to protect against automatic rekeying * which doesn't trigger with CHACHA20 */ + trace_qcrypto_tls_session_parameters( + session, + session->requireThreadSafety, + gnutls_protocol_get_version(session->handle), + cipher); + if (session->requireThreadSafety && gnutls_protocol_get_version(session->handle) == GNUTLS_TLS1_3 && cipher != GNUTLS_CIPHER_CHACHA20_POLY1305) { + warn_report("WARNING: activating thread safety countermeasures " + "for potentially broken GNUTLS with TLS1.3 cipher=%d", + cipher); + trace_qcrypto_tls_session_bug1717_workaround(session); session->lockEnabled = true; } #endif diff --git a/crypto/trace-events b/crypto/trace-events index bccd0bbf291a2..d0e33427faa89 100644 --- a/crypto/trace-events +++ b/crypto/trace-events @@ -21,6 +21,8 @@ qcrypto_tls_creds_x509_load_cert_list(void *creds, const char *file) "TLS creds # tlssession.c qcrypto_tls_session_new(void *session, void *creds, const char *hostname, const char *authzid, int endpoint) "TLS session new session=%p creds=%p hostname=%s authzid=%s endpoint=%d" qcrypto_tls_session_check_creds(void *session, const char *status) "TLS session check creds session=%p status=%s" +qcrypto_tls_session_parameters(void *session, int threadSafety, int protocol, int cipher) "TLS session parameters session=%p threadSafety=%d protocol=%d cipher=%d" +qcrypto_tls_session_bug1717_workaround(void *session) "TLS session bug1717 workaround session=%p" # tls-cipher-suites.c qcrypto_tls_cipher_suite_priority(const char *name) "priority: %s" From c4103b27973653a7a9f64244de8fb5243e4397c5 Mon Sep 17 00:00:00 2001 From: Harsh Prateek Bora Date: Thu, 24 Jul 2025 15:36:15 +0530 Subject: [PATCH 0107/1794] MAINTAINERS: Adding myself as a co-maintainer for ppc/spapr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have been contributing in ppc/spapr and related areas for quite some time as a contributor and reviewer. I think its time to step up as a co-maintainer to help with maintainer activities. Signed-off-by: Harsh Prateek Bora Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250724100623.3071131-2-harshpb@linux.ibm.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index a462345618350..afc94e6e67e50 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1550,6 +1550,7 @@ F: tests/functional/test_ppc_40p.py sPAPR (pseries) M: Nicholas Piggin +M: Harsh Prateek Bora R: Daniel Henrique Barboza R: Harsh Prateek Bora L: qemu-ppc@nongnu.org From 1da3e7f863d65bb850a7662c6b8dfba623ac7cb7 Mon Sep 17 00:00:00 2001 From: Harsh Prateek Bora Date: Thu, 24 Jul 2025 15:36:16 +0530 Subject: [PATCH 0108/1794] MAINTAINERS: Adding myself as reviewer for PPC KVM cpus. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have been contributing in ppc/spapr from tcg/kvm perspective, stepping up to help with patch reviews and get notified of incoming changes. Signed-off-by: Harsh Prateek Bora Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250724100623.3071131-3-harshpb@linux.ibm.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index afc94e6e67e50..6e95ef00c1c7f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -453,6 +453,7 @@ F: target/mips/system/ PPC KVM CPUs M: Nicholas Piggin R: Daniel Henrique Barboza +R: Harsh Prateek Bora S: Odd Fixes F: target/ppc/kvm.c From 884216cf419bb56664e3a8e7c0ce19d180fe2b24 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Thu, 24 Jul 2025 15:36:17 +0530 Subject: [PATCH 0109/1794] MAINTAINERS: Add myself as reviewer for PowerPC TCG CPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I have been working on Power ISA for a long time now and have mostly contributed in TCG instruction translation area (moved 300+ instructions to decodetree as of yet) and would like to continue contributing to PPC TCG in best possible ways I can. I think it's time to step up and assist in reviewing related patches to enable myself contribute more effectively in this direction. Signed-off-by: Chinmay Rath Reviewed-by: Cédric Le Goater Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/qemu-devel/20250724100623.3071131-4-harshpb@linux.ibm.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6e95ef00c1c7f..31bcb82e932f0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -296,6 +296,7 @@ F: tests/tcg/openrisc/ PowerPC TCG CPUs M: Nicholas Piggin M: Daniel Henrique Barboza +R: Chinmay Rath L: qemu-ppc@nongnu.org S: Odd Fixes F: target/ppc/ From 01286ee41ea3fc030a171dc5813945370f18955b Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 24 Jul 2025 15:36:18 +0530 Subject: [PATCH 0110/1794] MAINTAINERS: Add myself as a reviewer of PowerNV emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Proposing myself as a reviewer in the PowerNV emulation in QEMU Have been working on PowerNV QEMU for sometime, with contributions in Power11, MPIPL and minor fixes and things such as dtb support Cc: Cédric Le Goater Cc: Frédéric Barrat Cc: Mahesh J Salgaonkar Cc: Madhavan Srinivasan Cc: Nicholas Piggin Signed-off-by: Aditya Gupta Signed-off-by: Harsh Prateek Bora Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250724100623.3071131-5-harshpb@linux.ibm.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 31bcb82e932f0..1ba161d75bb19 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1579,6 +1579,7 @@ F: tests/functional/test_ppc64_tuxrun.py PowerNV (Non-Virtualized) M: Nicholas Piggin R: Frédéric Barrat +R: Aditya Gupta L: qemu-ppc@nongnu.org S: Odd Fixes F: docs/system/ppc/powernv.rst From 076b4306f9b6743059592e9632b149ac7708fa40 Mon Sep 17 00:00:00 2001 From: Gautam Menghani Date: Thu, 24 Jul 2025 15:36:19 +0530 Subject: [PATCH 0111/1794] MAINTAINERS: Add myself as a reviewer for XIVE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Proposing myself as a reviewer for XIVE on PPC. I have been looking at XIVE in context of KVM internally at IBM for some time in addition to testing a few XIVE upstream patches; and I'll be closely looking at XIVE going forward. Signed-off-by: Gautam Menghani Signed-off-by: Harsh Prateek Bora Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250724100623.3071131-6-harshpb@linux.ibm.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1ba161d75bb19..f3f981f90deb8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2781,6 +2781,7 @@ T: git https://github.com/philmd/qemu.git fw_cfg-next XIVE R: Frédéric Barrat +R: Gautam Menghani L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/*/*xive* From cf8f0f006d380d50ef10ab77a4673e83eb19006c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 24 Jul 2025 09:59:16 +0200 Subject: [PATCH 0112/1794] =?UTF-8?q?MAINTAINERS:=20Remove=20Fr=C3=A9d?= =?UTF-8?q?=C3=A9ric=20as=20reviewer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Frédéric has moved to other tasks within IBM and no longer does QEMU development. Cc: Frédéric Barrat Acked-by: Frédéric Barrat Reviewed-by: Daniel Henrique Barboza Link: https://lore.kernel.org/qemu-devel/20250724075916.1593420-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 2 -- 1 file changed, 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index f3f981f90deb8..9481a21c80189 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1578,7 +1578,6 @@ F: tests/functional/test_ppc64_tuxrun.py PowerNV (Non-Virtualized) M: Nicholas Piggin -R: Frédéric Barrat R: Aditya Gupta L: qemu-ppc@nongnu.org S: Odd Fixes @@ -2780,7 +2779,6 @@ F: tests/qtest/fw_cfg-test.c T: git https://github.com/philmd/qemu.git fw_cfg-next XIVE -R: Frédéric Barrat R: Gautam Menghani L: qemu-ppc@nongnu.org S: Odd Fixes From 0fb961e392e2055adc5429236989b01bb763f12c Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 24 Jul 2025 09:34:16 -0300 Subject: [PATCH 0113/1794] MAINTAINERS: remove myself as ppc maintainer/reviewer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It has been awhile since I actively did anything for qemu-ppc aside from reading the qemu-ppc inbox a couple of times each month. It's not enough to justify a reviewer role, let alone being a maintainer. Given that we're doing qemu-ppc maintainership changes across the board I'll take the opportunity and remove myself from the premises too. Feel free to reach out with questions about code I did in the past, but at this moment I'm no longer able to keep up with qemu-ppc activities. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250724123416.3115941-1-danielhb413@gmail.com [ clg: Adjusted context ] Signed-off-by: Cédric Le Goater --- MAINTAINERS | 3 --- 1 file changed, 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 9481a21c80189..f1bd69c3db729 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -295,7 +295,6 @@ F: tests/tcg/openrisc/ PowerPC TCG CPUs M: Nicholas Piggin -M: Daniel Henrique Barboza R: Chinmay Rath L: qemu-ppc@nongnu.org S: Odd Fixes @@ -453,7 +452,6 @@ F: target/mips/system/ PPC KVM CPUs M: Nicholas Piggin -R: Daniel Henrique Barboza R: Harsh Prateek Bora S: Odd Fixes F: target/ppc/kvm.c @@ -1553,7 +1551,6 @@ F: tests/functional/test_ppc_40p.py sPAPR (pseries) M: Nicholas Piggin M: Harsh Prateek Bora -R: Daniel Henrique Barboza R: Harsh Prateek Bora L: qemu-ppc@nongnu.org S: Odd Fixes From 012842c075520dbe1bd96a2fdcf4e218874ba443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 21 Jul 2025 19:54:52 +0100 Subject: [PATCH 0114/1794] log: make '-msg timestamp=on' apply to all qemu_log usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the tracing 'log' back emits special code to add timestamps to trace points sent via qemu_log(). This current impl is a bad design for a number of reasons. * It changes the QEMU headers, such that 'error-report.h' content is visible to all files using tracing, but only when the 'log' backend is enabled. This has led to build failure bugs as devs rarely test without the (default) 'log' backend enabled, and CI can't cover every scenario for every trace backend. * It bloats the trace points definitions which are inlined into every probe location due to repeated inlining of timestamp formatting code, adding MBs of overhead to QEMU. * The tracing subsystem should not be treated any differently from other users of qemu_log. They all would benefit from having timestamps present. * The timestamp emitted with the tracepoints is in a needlessly different format to that used by error_report() in response to '-msg timestamp=on'. This fixes all these issues simply by moving timestamp formatting into qemu_log, using the same approach as for error_report. The code before: static inline void _nocheck__trace_qcrypto_tls_creds_get_path(void * creds, const char * filename, const char * path) { if (trace_event_get_state(TRACE_QCRYPTO_TLS_CREDS_GET_PATH) && qemu_loglevel_mask(LOG_TRACE)) { if (message_with_timestamp) { struct timeval _now; gettimeofday(&_now, NULL); qemu_log("%d@%zu.%06zu:qcrypto_tls_creds_get_path " "TLS creds path creds=%p filename=%s path=%s" "\n", qemu_get_thread_id(), (size_t)_now.tv_sec, (size_t)_now.tv_usec , creds, filename, path); } else { qemu_log("qcrypto_tls_creds_get_path " "TLS creds path creds=%p filename=%s path=%s" "\n", creds, filename, path); } } } and after: static inline void _nocheck__trace_qcrypto_tls_creds_get_path(void * creds, const char * filename, const char * path) { if (trace_event_get_state(TRACE_QCRYPTO_TLS_CREDS_GET_PATH) && qemu_loglevel_mask(LOG_TRACE)) { qemu_log("qcrypto_tls_creds_get_path " "TLS creds path creds=%p filename=%s path=%s" "\n", creds, filename, path); } } The log and error messages before: $ qemu-system-x86_64 -trace qcrypto* -object tls-creds-x509,id=tls0,dir=$HOME/tls -msg timestamp=on 2986097@1753122905.917608:qcrypto_tls_creds_x509_load TLS creds x509 load creds=0x55d925bd9490 dir=/var/home/berrange/tls 2986097@1753122905.917621:qcrypto_tls_creds_get_path TLS creds path creds=0x55d925bd9490 filename=ca-cert.pem path= 2025-07-21T18:35:05.917626Z qemu-system-x86_64: Unable to access credentials /var/home/berrange/tls/ca-cert.pem: No such file or directory and after: $ qemu-system-x86_64 -trace qcrypto* -object tls-creds-x509,id=tls0,dir=$HOME/tls -msg timestamp=on 2025-07-21T18:43:28.089797Z qcrypto_tls_creds_x509_load TLS creds x509 load creds=0x55bf5bf12380 dir=/var/home/berrange/tls 2025-07-21T18:43:28.089815Z qcrypto_tls_creds_get_path TLS creds path creds=0x55bf5bf12380 filename=ca-cert.pem path= 2025-07-21T18:43:28.089819Z qemu-system-x86_64: Unable to access credentials /var/home/berrange/tls/ca-cert.pem: No such file or directory The binary size before: $ ls -alh qemu-system-x86_64 -rwxr-xr-x. 1 berrange berrange 87M Jul 21 19:39 qemu-system-x86_64 $ strip qemu-system-x86_64 $ ls -alh qemu-system-x86_64 -rwxr-xr-x. 1 berrange berrange 30M Jul 21 19:39 qemu-system-x86_64 and after: $ ls -alh qemu-system-x86_64 -rwxr-xr-x. 1 berrange berrange 85M Jul 21 19:41 qemu-system-x86_64 $ strip qemu-system-x86_64 $ ls -alh qemu-system-x86_64 -rwxr-xr-x. 1 berrange berrange 29M Jul 21 19:41 qemu-system-x86_64 Signed-off-by: Daniel P. Berrangé Reviewed-by: Markus Armbruster Reviewed-by: Vladimir Sementsov-Ogievskiy Message-id: 20250721185452.3016488-1-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/backend/log.py | 14 +------------- util/log.c | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index 5c9d09dd115f2..eb50ceea34c09 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -20,7 +20,6 @@ def generate_h_begin(events, group): out('#include "qemu/log-for-trace.h"', - '#include "qemu/error-report.h"', '') @@ -32,20 +31,9 @@ def generate_h(event, group): cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) out(' if (%(cond)s && qemu_loglevel_mask(LOG_TRACE)) {', - ' if (message_with_timestamp) {', - ' struct timeval _now;', - ' gettimeofday(&_now, NULL);', '#line %(event_lineno)d "%(event_filename)s"', - ' qemu_log("%%d@%%zu.%%06zu:%(name)s " %(fmt)s "\\n",', - ' qemu_get_thread_id(),', - ' (size_t)_now.tv_sec, (size_t)_now.tv_usec', - ' %(argnames)s);', + ' qemu_log("%(name)s " %(fmt)s "\\n"%(argnames)s);', '#line %(out_next_lineno)d "%(out_filename)s"', - ' } else {', - '#line %(event_lineno)d "%(event_filename)s"', - ' qemu_log("%(name)s " %(fmt)s "\\n"%(argnames)s);', - '#line %(out_next_lineno)d "%(out_filename)s"', - ' }', ' }', cond=cond, event_lineno=event.lineno, diff --git a/util/log.c b/util/log.c index 58d24de48a01a..abdcb6b31111f 100644 --- a/util/log.c +++ b/util/log.c @@ -145,10 +145,28 @@ void qemu_log_unlock(FILE *logfile) void qemu_log(const char *fmt, ...) { - FILE *f = qemu_log_trylock(); + FILE *f; + g_autofree const char *timestr = NULL; + + /* + * Prepare the timestamp *outside* the logging + * lock so it better reflects when the message + * was emitted if we are delayed acquiring the + * mutex + */ + if (message_with_timestamp) { + g_autoptr(GDateTime) dt = g_date_time_new_now_utc(); + timestr = g_date_time_format_iso8601(dt); + } + + f = qemu_log_trylock(); if (f) { va_list ap; + if (timestr) { + fprintf(f, "%s ", timestr); + } + va_start(ap, fmt); vfprintf(f, fmt, ap); va_end(ap); From b8882becd572d3afb888c836a6ffc7f92c17d1c5 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Fri, 21 Feb 2025 16:34:52 +0300 Subject: [PATCH 0115/1794] hw/display/qxl-render.c: fix qxl_unpack_chunks() chunk size calculation In case of multiple chunks, code in qxl_unpack_chunks() takes size of the wrong (next in the chain) chunk, instead of using current chunk size. This leads to wrong number of bytes being copied, and to crashes if next chunk size is larger than the current one. Based on the code by Gao Yong. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1628 Signed-off-by: Michael Tokarev Reviewed-by: Thomas Huth --- hw/display/qxl-render.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index eda6d3de37c3a..c6a9ac1da104f 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -222,6 +222,7 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, uint32_t max_chunks = 32; size_t offset = 0; size_t bytes; + QXLPHYSICAL next_chunk_phys = 0; for (;;) { bytes = MIN(size - offset, chunk->data_size); @@ -230,7 +231,15 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, if (offset == size) { return; } - chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id, + next_chunk_phys = chunk->next_chunk; + /* fist time, only get the next chunk's data size */ + chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id, + sizeof(QXLDataChunk)); + if (!chunk) { + return; + } + /* second time, check data size and get data */ + chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id, sizeof(QXLDataChunk) + chunk->data_size); if (!chunk) { return; From 0e171b427b1d2f68d76a2b7cae987432c10ca8aa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 23 Jul 2025 17:54:53 +0100 Subject: [PATCH 0116/1794] target/arm: Expand the descriptor for SME/SVE memory ops to i64 We have run out of room attempting to pack both the gvec descriptor and the mte descriptor into 32 bits. Here, change nothing except the parameter type, which affects all declarations, the function typedefs, and the type used with tcg expansion. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Message-id: 20250723165458.3509150-2-peter.maydell@linaro.org --- target/arm/tcg/helper-sme.h | 162 ++-- target/arm/tcg/helper-sve.h | 1340 ++++++++++++++++---------------- target/arm/tcg/sme_helper.c | 16 +- target/arm/tcg/sve_helper.c | 96 +-- target/arm/tcg/translate-a64.h | 2 +- target/arm/tcg/translate-sme.c | 6 +- target/arm/tcg/translate-sve.c | 33 +- 7 files changed, 828 insertions(+), 827 deletions(-) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 1fc756bec6e55..c551797c6fa6a 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -48,87 +48,87 @@ DEF_HELPER_FLAGS_3(sme2p1_movaz_zc_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2p1_movaz_zc_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2p1_movaz_zc_q, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sme_ld1b_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1b_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1b_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1b_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_ld1h_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1h_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1h_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1h_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1h_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1h_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1h_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1h_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_ld1s_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1s_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1s_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1s_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1s_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1s_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1s_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1s_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_ld1d_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1d_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1d_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1d_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1d_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1d_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1d_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1d_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_ld1q_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1q_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1q_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1q_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1q_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1q_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1q_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1q_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_st1b_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1b_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1b_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1b_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_st1h_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1h_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1h_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1h_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1h_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1h_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1h_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1h_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_st1s_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1s_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1s_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1s_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1s_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1s_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1s_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1s_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_st1d_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1d_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1d_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1d_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1d_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1d_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1d_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1d_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_st1q_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1q_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1q_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1q_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1q_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1q_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1q_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1q_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1b_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1b_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1b_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1b_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_ld1h_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1h_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1h_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1h_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1h_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1h_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1h_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1h_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_ld1s_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1s_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1s_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1s_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1s_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1s_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1s_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1s_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_ld1d_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1d_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1d_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1d_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1d_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1d_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1d_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1d_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_ld1q_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1q_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1q_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1q_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1q_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1q_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1q_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1q_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_st1b_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1b_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1b_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1b_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_st1h_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1h_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1h_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1h_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1h_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1h_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1h_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1h_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_st1s_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1s_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1s_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1s_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1s_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1s_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1s_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1s_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_st1d_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1d_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1d_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1d_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1d_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1d_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1d_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1d_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_st1q_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1q_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1q_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1q_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1q_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1q_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1q_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1q_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_5(sme_addha_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sme_addva_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 5e4b7fd8cf400..c3541a8ca864a 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -1655,1015 +1655,1015 @@ DEF_HELPER_FLAGS_4(sve2_usubw_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_usubw_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_usubw_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve_ld1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld2qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld2qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1hsu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1hsu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1sdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1sds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1squ_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1dqu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1sdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1sds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1squ_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1dqu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld2qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld2qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1bhu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bsu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bdu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bhs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bss_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bds_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1hsu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hdu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hds_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1hsu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hdu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hds_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1sdu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1sds_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1squ_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1dqu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1sdu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1sds_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1squ_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1dqu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldff1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldff1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hsu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldff1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hsu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldff1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1sdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1sds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldff1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1sdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1sds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldff1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldff1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bhu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bsu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bdu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bhs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bss_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bds_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld2qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld2qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1hsu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1hsu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1sdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1sds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1squ_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1dqu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1sdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1sds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1squ_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1dqu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld2qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld2qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1bhu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bsu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bdu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bhs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bss_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bds_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1hsu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hdu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hds_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1hsu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hdu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hds_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1sdu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1sds_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1squ_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1dqu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1sdu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1sds_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1squ_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1dqu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldff1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldff1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1hsu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1hdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1hss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1hds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldff1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1hsu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1hdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1hss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1hds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldff1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1sdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1sds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldff1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1sdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1sds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldff1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldff1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bhu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bsu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bdu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bhs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bss_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bds_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hh_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hsu_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hdu_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hss_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hds_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hh_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hsu_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hdu_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hss_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hds_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1ss_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1sdu_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1sds_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1ss_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1sdu_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1sds_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1dd_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1dd_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldnf1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldnf1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hsu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldnf1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hsu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldnf1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1sdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1sds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldnf1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1sdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1sds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldnf1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldnf1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bhu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bsu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bdu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bhs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bss_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bds_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldnf1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldnf1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1hsu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1hdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1hss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1hds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldnf1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1hsu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1hdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1hss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1hds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldnf1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1sdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1sds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldnf1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1sdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1sds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldnf1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldnf1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bhu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bsu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bdu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bhs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bss_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bds_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hh_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hsu_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hdu_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hss_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hds_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hh_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hsu_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hdu_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hss_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hds_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1ss_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1sdu_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1sds_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1ss_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1sdu_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1sds_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1dd_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1dd_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st2qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st2qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1bh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1bs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1bd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1hs_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1hd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1hs_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1hd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1sd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1sd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1sq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1sq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1dq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1dq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st2qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st2qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1bh_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1bs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1bd_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1hs_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1hd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1hs_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1hd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1sd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1sd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1sq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1sq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1dq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1dq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st2qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st2qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1bh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1bs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1bd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1hs_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1hd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1hs_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1hd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1sd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1sd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1sq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1sq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1dq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1dq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st2qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st2qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1bh_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1bs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1bd_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1hs_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1hd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1hs_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1hd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1sd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1sd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1sq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1sq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1dq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1dq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbsu_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhsu_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhsu_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldss_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldss_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbss_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhss_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhss_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbsu_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhsu_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhsu_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldss_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldss_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbss_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhss_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhss_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbdu_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbds_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbdu_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbds_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbdu_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbds_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldqq_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldqq_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbsu_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhsu_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhsu_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldss_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldss_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbss_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhss_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhss_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbsu_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhsu_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhsu_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldss_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldss_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbss_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhss_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhss_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbdu_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbds_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbdu_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbds_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbdu_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbds_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldqq_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldqq_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbsu_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhsu_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhsu_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffss_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffss_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbss_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhss_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhss_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbsu_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhsu_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhsu_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffss_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffss_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbss_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhss_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhss_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbdu_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbds_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbdu_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbds_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbdu_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbds_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbsu_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhsu_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhsu_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffss_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffss_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbss_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhss_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhss_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbsu_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhsu_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhsu_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffss_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffss_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbss_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhss_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhss_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbdu_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbds_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbdu_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbds_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbdu_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbds_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbs_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sths_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sths_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stss_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stss_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbs_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sths_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sths_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stss_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stss_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbd_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbd_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbd_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stqq_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stqq_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbs_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sths_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sths_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stss_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stss_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbs_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sths_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sths_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stss_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stss_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbd_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbd_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbd_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stqq_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stqq_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve2_sqdmull_zzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) @@ -3151,18 +3151,18 @@ DEF_HELPER_FLAGS_3(pmov_vp_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(pmov_vp_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(pmov_vp_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve2p1_ld1bb_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_ld1hh_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_ld1hh_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_ld1ss_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_ld1ss_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_ld1dd_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_ld1dd_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) - -DEF_HELPER_FLAGS_5(sve2p1_st1bb_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_st1hh_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_st1hh_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_st1ss_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_st1ss_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_st1dd_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_st1dd_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) +DEF_HELPER_FLAGS_5(sve2p1_ld1bb_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_ld1hh_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_ld1hh_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_ld1ss_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_ld1ss_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_ld1dd_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_ld1dd_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) + +DEF_HELPER_FLAGS_5(sve2p1_st1bb_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_st1hh_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_st1hh_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_st1ss_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_st1ss_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_st1dd_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_st1dd_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index bb8ed1ed0e2f3..0b55f13f8c5b9 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -691,28 +691,28 @@ void sme_ld1_mte(CPUARMState *env, void *za, uint64_t *vg, #define DO_LD(L, END, ESZ) \ void HELPER(sme_ld1##L##END##_h)(CPUARMState *env, void *za, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sme_ld1(env, za, vg, addr, desc, GETPC(), ESZ, 0, false, \ sve_ld1##L##L##END##_host, sve_ld1##L##L##END##_tlb, \ clear_horizontal, copy_horizontal); \ } \ void HELPER(sme_ld1##L##END##_v)(CPUARMState *env, void *za, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sme_ld1(env, za, vg, addr, desc, GETPC(), ESZ, 0, true, \ sme_ld1##L##END##_v_host, sme_ld1##L##END##_v_tlb, \ clear_vertical_##L, copy_vertical_##L); \ } \ void HELPER(sme_ld1##L##END##_h_mte)(CPUARMState *env, void *za, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sme_ld1_mte(env, za, vg, addr, desc, GETPC(), ESZ, false, \ sve_ld1##L##L##END##_host, sve_ld1##L##L##END##_tlb, \ clear_horizontal, copy_horizontal); \ } \ void HELPER(sme_ld1##L##END##_v_mte)(CPUARMState *env, void *za, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sme_ld1_mte(env, za, vg, addr, desc, GETPC(), ESZ, true, \ sme_ld1##L##END##_v_host, sme_ld1##L##END##_v_tlb, \ @@ -876,25 +876,25 @@ void sme_st1_mte(CPUARMState *env, void *za, uint64_t *vg, target_ulong addr, #define DO_ST(L, END, ESZ) \ void HELPER(sme_st1##L##END##_h)(CPUARMState *env, void *za, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sme_st1(env, za, vg, addr, desc, GETPC(), ESZ, 0, false, \ sve_st1##L##L##END##_host, sve_st1##L##L##END##_tlb); \ } \ void HELPER(sme_st1##L##END##_v)(CPUARMState *env, void *za, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sme_st1(env, za, vg, addr, desc, GETPC(), ESZ, 0, true, \ sme_st1##L##END##_v_host, sme_st1##L##END##_v_tlb); \ } \ void HELPER(sme_st1##L##END##_h_mte)(CPUARMState *env, void *za, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sme_st1_mte(env, za, vg, addr, desc, GETPC(), ESZ, false, \ sve_st1##L##L##END##_host, sve_st1##L##L##END##_tlb); \ } \ void HELPER(sme_st1##L##END##_v_mte)(CPUARMState *env, void *za, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sme_st1_mte(env, za, vg, addr, desc, GETPC(), ESZ, true, \ sme_st1##L##END##_v_host, sme_st1##L##END##_v_tlb); \ diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 803f0a094dcd6..9fc2c05879b97 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -6384,13 +6384,13 @@ void sve_ldN_r_mte(CPUARMState *env, uint64_t *vg, target_ulong addr, #define DO_LD1_1(NAME, ESZ) \ void HELPER(sve_##NAME##_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r(env, vg, addr, desc, GETPC(), ESZ, MO_8, 1, 0, \ sve_##NAME##_host, sve_##NAME##_tlb); \ } \ void HELPER(sve_##NAME##_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r_mte(env, vg, addr, desc, GETPC(), ESZ, MO_8, 1, \ sve_##NAME##_host, sve_##NAME##_tlb); \ @@ -6398,25 +6398,25 @@ void HELPER(sve_##NAME##_r_mte)(CPUARMState *env, void *vg, \ #define DO_LD1_2(NAME, ESZ, MSZ) \ void HELPER(sve_##NAME##_le_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r(env, vg, addr, desc, GETPC(), ESZ, MSZ, 1, 0, \ sve_##NAME##_le_host, sve_##NAME##_le_tlb); \ } \ void HELPER(sve_##NAME##_be_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r(env, vg, addr, desc, GETPC(), ESZ, MSZ, 1, 0, \ sve_##NAME##_be_host, sve_##NAME##_be_tlb); \ } \ void HELPER(sve_##NAME##_le_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, 1, \ sve_##NAME##_le_host, sve_##NAME##_le_tlb); \ } \ void HELPER(sve_##NAME##_be_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, 1, \ sve_##NAME##_be_host, sve_##NAME##_be_tlb); \ @@ -6450,13 +6450,13 @@ DO_LD1_2(ld1dqu, MO_64, MO_128) #define DO_LDN_1(N) \ void HELPER(sve_ld##N##bb_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r(env, vg, addr, desc, GETPC(), MO_8, MO_8, N, 0, \ sve_ld1bb_host, sve_ld1bb_tlb); \ } \ void HELPER(sve_ld##N##bb_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r_mte(env, vg, addr, desc, GETPC(), MO_8, MO_8, N, \ sve_ld1bb_host, sve_ld1bb_tlb); \ @@ -6464,25 +6464,25 @@ void HELPER(sve_ld##N##bb_r_mte)(CPUARMState *env, void *vg, \ #define DO_LDN_2(N, SUFF, ESZ) \ void HELPER(sve_ld##N##SUFF##_le_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r(env, vg, addr, desc, GETPC(), ESZ, ESZ, N, 0, \ sve_ld1##SUFF##_le_host, sve_ld1##SUFF##_le_tlb); \ } \ void HELPER(sve_ld##N##SUFF##_be_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r(env, vg, addr, desc, GETPC(), ESZ, ESZ, N, 0, \ sve_ld1##SUFF##_be_host, sve_ld1##SUFF##_be_tlb); \ } \ void HELPER(sve_ld##N##SUFF##_le_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r_mte(env, vg, addr, desc, GETPC(), ESZ, ESZ, N, \ sve_ld1##SUFF##_le_host, sve_ld1##SUFF##_le_tlb); \ } \ void HELPER(sve_ld##N##SUFF##_be_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r_mte(env, vg, addr, desc, GETPC(), ESZ, ESZ, N, \ sve_ld1##SUFF##_be_host, sve_ld1##SUFF##_be_tlb); \ @@ -6750,25 +6750,25 @@ void sve_ldnfff1_r_mte(CPUARMState *env, void *vg, target_ulong addr, #define DO_LDFF1_LDNF1_1(PART, ESZ) \ void HELPER(sve_ldff1##PART##_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r(env, vg, addr, desc, GETPC(), 0, ESZ, MO_8, FAULT_FIRST, \ sve_ld1##PART##_host, sve_ld1##PART##_tlb); \ } \ void HELPER(sve_ldnf1##PART##_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r(env, vg, addr, desc, GETPC(), 0, ESZ, MO_8, FAULT_NO, \ sve_ld1##PART##_host, sve_ld1##PART##_tlb); \ } \ void HELPER(sve_ldff1##PART##_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r_mte(env, vg, addr, desc, GETPC(), ESZ, MO_8, FAULT_FIRST, \ sve_ld1##PART##_host, sve_ld1##PART##_tlb); \ } \ void HELPER(sve_ldnf1##PART##_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r_mte(env, vg, addr, desc, GETPC(), ESZ, MO_8, FAULT_NO, \ sve_ld1##PART##_host, sve_ld1##PART##_tlb); \ @@ -6776,49 +6776,49 @@ void HELPER(sve_ldnf1##PART##_r_mte)(CPUARMState *env, void *vg, \ #define DO_LDFF1_LDNF1_2(PART, ESZ, MSZ) \ void HELPER(sve_ldff1##PART##_le_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r(env, vg, addr, desc, GETPC(), 0, ESZ, MSZ, FAULT_FIRST, \ sve_ld1##PART##_le_host, sve_ld1##PART##_le_tlb); \ } \ void HELPER(sve_ldnf1##PART##_le_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r(env, vg, addr, desc, GETPC(), 0, ESZ, MSZ, FAULT_NO, \ sve_ld1##PART##_le_host, sve_ld1##PART##_le_tlb); \ } \ void HELPER(sve_ldff1##PART##_be_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r(env, vg, addr, desc, GETPC(), 0, ESZ, MSZ, FAULT_FIRST, \ sve_ld1##PART##_be_host, sve_ld1##PART##_be_tlb); \ } \ void HELPER(sve_ldnf1##PART##_be_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r(env, vg, addr, desc, GETPC(), 0, ESZ, MSZ, FAULT_NO, \ sve_ld1##PART##_be_host, sve_ld1##PART##_be_tlb); \ } \ void HELPER(sve_ldff1##PART##_le_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, FAULT_FIRST, \ sve_ld1##PART##_le_host, sve_ld1##PART##_le_tlb); \ } \ void HELPER(sve_ldnf1##PART##_le_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, FAULT_NO, \ sve_ld1##PART##_le_host, sve_ld1##PART##_le_tlb); \ } \ void HELPER(sve_ldff1##PART##_be_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, FAULT_FIRST, \ sve_ld1##PART##_be_host, sve_ld1##PART##_be_tlb); \ } \ void HELPER(sve_ldnf1##PART##_be_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, FAULT_NO, \ sve_ld1##PART##_be_host, sve_ld1##PART##_be_tlb); \ @@ -7007,13 +7007,13 @@ void sve_stN_r_mte(CPUARMState *env, uint64_t *vg, target_ulong addr, #define DO_STN_1(N, NAME, ESZ) \ void HELPER(sve_st##N##NAME##_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_stN_r(env, vg, addr, desc, GETPC(), ESZ, MO_8, N, 0, \ sve_st1##NAME##_host, sve_st1##NAME##_tlb); \ } \ void HELPER(sve_st##N##NAME##_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_stN_r_mte(env, vg, addr, desc, GETPC(), ESZ, MO_8, N, \ sve_st1##NAME##_host, sve_st1##NAME##_tlb); \ @@ -7021,25 +7021,25 @@ void HELPER(sve_st##N##NAME##_r_mte)(CPUARMState *env, void *vg, \ #define DO_STN_2(N, NAME, ESZ, MSZ) \ void HELPER(sve_st##N##NAME##_le_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_stN_r(env, vg, addr, desc, GETPC(), ESZ, MSZ, N, 0, \ sve_st1##NAME##_le_host, sve_st1##NAME##_le_tlb); \ } \ void HELPER(sve_st##N##NAME##_be_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_stN_r(env, vg, addr, desc, GETPC(), ESZ, MSZ, N, 0, \ sve_st1##NAME##_be_host, sve_st1##NAME##_be_tlb); \ } \ void HELPER(sve_st##N##NAME##_le_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_stN_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, N, \ sve_st1##NAME##_le_host, sve_st1##NAME##_le_tlb); \ } \ void HELPER(sve_st##N##NAME##_be_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_stN_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, N, \ sve_st1##NAME##_be_host, sve_st1##NAME##_be_tlb); \ @@ -7204,13 +7204,13 @@ void sve_ld1_z_mte(CPUARMState *env, void *vd, uint64_t *vg, void *vm, #define DO_LD1_ZPZ_S(MEM, OFS, MSZ) \ void HELPER(sve_ld##MEM##_##OFS)(CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_ld1_z(env, vd, vg, vm, base, desc, GETPC(), 0, 4, 1 << MSZ, \ off_##OFS##_s, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ } \ void HELPER(sve_ld##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_ld1_z_mte(env, vd, vg, vm, base, desc, GETPC(), 4, 1 << MSZ, \ off_##OFS##_s, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ @@ -7218,13 +7218,13 @@ void HELPER(sve_ld##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ #define DO_LD1_ZPZ_D(MEM, OFS, MSZ) \ void HELPER(sve_ld##MEM##_##OFS)(CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_ld1_z(env, vd, vg, vm, base, desc, GETPC(), 0, 8, 1 << MSZ, \ off_##OFS##_d, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ } \ void HELPER(sve_ld##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_ld1_z_mte(env, vd, vg, vm, base, desc, GETPC(), 8, 1 << MSZ, \ off_##OFS##_d, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ @@ -7418,14 +7418,14 @@ void sve_ldff1_z_mte(CPUARMState *env, void *vd, uint64_t *vg, void *vm, #define DO_LDFF1_ZPZ_S(MEM, OFS, MSZ) \ void HELPER(sve_ldff##MEM##_##OFS) \ (CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_ldff1_z(env, vd, vg, vm, base, desc, GETPC(), 0, MO_32, MSZ, \ off_##OFS##_s, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ } \ void HELPER(sve_ldff##MEM##_##OFS##_mte) \ (CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_ldff1_z_mte(env, vd, vg, vm, base, desc, GETPC(), MO_32, MSZ, \ off_##OFS##_s, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ @@ -7434,14 +7434,14 @@ void HELPER(sve_ldff##MEM##_##OFS##_mte) \ #define DO_LDFF1_ZPZ_D(MEM, OFS, MSZ) \ void HELPER(sve_ldff##MEM##_##OFS) \ (CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_ldff1_z(env, vd, vg, vm, base, desc, GETPC(), 0, MO_64, MSZ, \ off_##OFS##_d, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ } \ void HELPER(sve_ldff##MEM##_##OFS##_mte) \ (CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_ldff1_z_mte(env, vd, vg, vm, base, desc, GETPC(), MO_64, MSZ, \ off_##OFS##_d, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ @@ -7621,13 +7621,13 @@ void sve_st1_z_mte(CPUARMState *env, void *vd, uint64_t *vg, void *vm, #define DO_ST1_ZPZ_S(MEM, OFS, MSZ) \ void HELPER(sve_st##MEM##_##OFS)(CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_st1_z(env, vd, vg, vm, base, desc, GETPC(), 0, 4, 1 << MSZ, \ off_##OFS##_s, sve_st1##MEM##_host, sve_st1##MEM##_tlb); \ } \ void HELPER(sve_st##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_st1_z_mte(env, vd, vg, vm, base, desc, GETPC(), 4, 1 << MSZ, \ off_##OFS##_s, sve_st1##MEM##_host, sve_st1##MEM##_tlb); \ @@ -7635,13 +7635,13 @@ void HELPER(sve_st##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ #define DO_ST1_ZPZ_D(MEM, OFS, MSZ) \ void HELPER(sve_st##MEM##_##OFS)(CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_st1_z(env, vd, vg, vm, base, desc, GETPC(), 0, 8, 1 << MSZ, \ off_##OFS##_d, sve_st1##MEM##_host, sve_st1##MEM##_tlb); \ } \ void HELPER(sve_st##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_st1_z_mte(env, vd, vg, vm, base, desc, GETPC(), 8, 1 << MSZ, \ off_##OFS##_d, sve_st1##MEM##_host, sve_st1##MEM##_tlb); \ @@ -7995,7 +7995,7 @@ void sve2p1_ld1_c(CPUARMState *env, ARMVectorReg *zd, const vaddr addr, } void HELPER(sve2p1_ld1bb_c)(CPUARMState *env, void *vd, target_ulong addr, - uint32_t png, uint32_t desc) + uint32_t png, uint64_t desc) { sve2p1_ld1_c(env, vd, addr, png, desc, GETPC(), MO_8, sve_ld1bb_host, sve_ld1bb_tlb); @@ -8004,14 +8004,14 @@ void HELPER(sve2p1_ld1bb_c)(CPUARMState *env, void *vd, target_ulong addr, #define DO_LD1_2(NAME, ESZ) \ void HELPER(sve2p1_##NAME##_le_c)(CPUARMState *env, void *vd, \ target_ulong addr, uint32_t png, \ - uint32_t desc) \ + uint64_t desc) \ { \ sve2p1_ld1_c(env, vd, addr, png, desc, GETPC(), ESZ, \ sve_##NAME##_le_host, sve_##NAME##_le_tlb); \ } \ void HELPER(sve2p1_##NAME##_be_c)(CPUARMState *env, void *vd, \ target_ulong addr, uint32_t png, \ - uint32_t desc) \ + uint64_t desc) \ { \ sve2p1_ld1_c(env, vd, addr, png, desc, GETPC(), ESZ, \ sve_##NAME##_be_host, sve_##NAME##_be_tlb); \ @@ -8154,7 +8154,7 @@ void sve2p1_st1_c(CPUARMState *env, ARMVectorReg *zd, const vaddr addr, } void HELPER(sve2p1_st1bb_c)(CPUARMState *env, void *vd, target_ulong addr, - uint32_t png, uint32_t desc) + uint32_t png, uint64_t desc) { sve2p1_st1_c(env, vd, addr, png, desc, GETPC(), MO_8, sve_st1bb_host, sve_st1bb_tlb); @@ -8163,14 +8163,14 @@ void HELPER(sve2p1_st1bb_c)(CPUARMState *env, void *vd, target_ulong addr, #define DO_ST1_2(NAME, ESZ) \ void HELPER(sve2p1_##NAME##_le_c)(CPUARMState *env, void *vd, \ target_ulong addr, uint32_t png, \ - uint32_t desc) \ + uint64_t desc) \ { \ sve2p1_st1_c(env, vd, addr, png, desc, GETPC(), ESZ, \ sve_##NAME##_le_host, sve_##NAME##_le_tlb); \ } \ void HELPER(sve2p1_##NAME##_be_c)(CPUARMState *env, void *vd, \ target_ulong addr, uint32_t png, \ - uint32_t desc) \ + uint64_t desc) \ { \ sve2p1_st1_c(env, vd, addr, png, desc, GETPC(), ESZ, \ sve_##NAME##_be_host, sve_##NAME##_be_tlb); \ diff --git a/target/arm/tcg/translate-a64.h b/target/arm/tcg/translate-a64.h index 993dde61a4de4..9c45f89305b4c 100644 --- a/target/arm/tcg/translate-a64.h +++ b/target/arm/tcg/translate-a64.h @@ -28,7 +28,7 @@ bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, bool sve_access_check(DisasContext *s); bool sme_enabled_check(DisasContext *s); bool sme_enabled_check_with_svcr(DisasContext *s, unsigned); -uint32_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, +uint64_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, uint32_t msz, bool is_write, uint32_t data); /* This function corresponds to CheckStreamingSVEEnabled. */ diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 65fc8bc9b2ff5..091c56da4f4ea 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -387,7 +387,7 @@ TRANS_FEAT(MOVT_ztr, aa64_sme2, do_movt, a, tcg_gen_st_i64) static bool trans_LDST1(DisasContext *s, arg_LDST1 *a) { - typedef void GenLdSt1(TCGv_env, TCGv_ptr, TCGv_ptr, TCGv, TCGv_i32); + typedef void GenLdSt1(TCGv_env, TCGv_ptr, TCGv_ptr, TCGv, TCGv_i64); /* * Indexed by [esz][be][v][mte][st], which is (except for load/store) @@ -415,7 +415,7 @@ static bool trans_LDST1(DisasContext *s, arg_LDST1 *a) TCGv_ptr t_za, t_pg; TCGv_i64 addr; - uint32_t desc; + uint64_t desc; bool be = s->be_data == MO_BE; bool mte = s->mte_active[0]; @@ -440,7 +440,7 @@ static bool trans_LDST1(DisasContext *s, arg_LDST1 *a) desc = make_svemte_desc(s, streaming_vec_reg_size(s), 1, a->esz, a->st, 0); fns[a->esz][be][a->v][mte][a->st](tcg_env, t_za, t_pg, addr, - tcg_constant_i32(desc)); + tcg_constant_i64(desc)); return true; } diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 2ed440aff150c..849151826e2b5 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -31,9 +31,9 @@ typedef void gen_helper_gvec_flags_3(TCGv_i32, TCGv_ptr, TCGv_ptr, typedef void gen_helper_gvec_flags_4(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); -typedef void gen_helper_gvec_mem(TCGv_env, TCGv_ptr, TCGv_i64, TCGv_i32); +typedef void gen_helper_gvec_mem(TCGv_env, TCGv_ptr, TCGv_i64, TCGv_i64); typedef void gen_helper_gvec_mem_scatter(TCGv_env, TCGv_ptr, TCGv_ptr, - TCGv_ptr, TCGv_i64, TCGv_i32); + TCGv_ptr, TCGv_i64, TCGv_i64); /* * Helpers for extracting complex instruction fields. @@ -4883,11 +4883,11 @@ static const uint8_t dtype_esz[19] = { 4, 4, 4, }; -uint32_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, +uint64_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, uint32_t msz, bool is_write, uint32_t data) { uint32_t sizem1; - uint32_t desc = 0; + uint64_t desc = 0; /* Assert all of the data fits, with or without MTE enabled. */ assert(nregs >= 1 && nregs <= 4); @@ -4911,7 +4911,7 @@ static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, gen_helper_gvec_mem *fn) { TCGv_ptr t_pg; - uint32_t desc; + uint64_t desc; if (!s->mte_active[0]) { addr = clean_data_tbi(s, addr); @@ -4927,7 +4927,7 @@ static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, t_pg = tcg_temp_new_ptr(); tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, pg)); - fn(tcg_env, t_pg, addr, tcg_constant_i32(desc)); + fn(tcg_env, t_pg, addr, tcg_constant_i64(desc)); } /* Indexed by [mte][be][dtype][nreg] */ @@ -5379,7 +5379,7 @@ static void do_ldrq(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) unsigned vsz = vec_full_reg_size(s); TCGv_ptr t_pg; int poff; - uint32_t desc; + uint64_t desc; /* Load the first quadword using the normal predicated load helpers. */ if (!s->mte_active[0]) { @@ -5410,7 +5410,7 @@ static void do_ldrq(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) gen_helper_gvec_mem *fn = ldr_fns[s->mte_active[0]][s->be_data == MO_BE][dtype][0]; desc = make_svemte_desc(s, 16, 1, dtype_msz(dtype), false, zt); - fn(tcg_env, t_pg, addr, tcg_constant_i32(desc)); + fn(tcg_env, t_pg, addr, tcg_constant_i64(desc)); /* Replicate that first quadword. */ if (vsz > 16) { @@ -5453,7 +5453,7 @@ static void do_ldro(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) unsigned vsz_r32; TCGv_ptr t_pg; int poff, doff; - uint32_t desc; + uint64_t desc; if (vsz < 32) { /* @@ -5494,7 +5494,7 @@ static void do_ldro(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) gen_helper_gvec_mem *fn = ldr_fns[s->mte_active[0]][s->be_data == MO_BE][dtype][0]; desc = make_svemte_desc(s, 32, 1, dtype_msz(dtype), false, zt); - fn(tcg_env, t_pg, addr, tcg_constant_i32(desc)); + fn(tcg_env, t_pg, addr, tcg_constant_i64(desc)); /* * Replicate that first octaword. @@ -5828,14 +5828,14 @@ static void do_mem_zpz(DisasContext *s, int zt, int pg, int zm, TCGv_ptr t_zm = tcg_temp_new_ptr(); TCGv_ptr t_pg = tcg_temp_new_ptr(); TCGv_ptr t_zt = tcg_temp_new_ptr(); - uint32_t desc; + uint64_t desc; tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, pg)); tcg_gen_addi_ptr(t_zm, tcg_env, vec_full_reg_offset(s, zm)); tcg_gen_addi_ptr(t_zt, tcg_env, vec_full_reg_offset(s, zt)); desc = make_svemte_desc(s, vec_full_reg_size(s), 1, msz, is_write, scale); - fn(tcg_env, t_zt, t_pg, t_zm, scalar, tcg_constant_i32(desc)); + fn(tcg_env, t_zt, t_pg, t_zm, scalar, tcg_constant_i64(desc)); } /* Indexed by [mte][be][ff][xs][u][msz]. */ @@ -8079,7 +8079,7 @@ static bool gen_ldst_c(DisasContext *s, TCGv_i64 addr, int zd, int png, MemOp esz, bool is_write, int n, bool strided) { typedef void ldst_c_fn(TCGv_env, TCGv_ptr, TCGv_i64, - TCGv_i32, TCGv_i32); + TCGv_i32, TCGv_i64); static ldst_c_fn * const f_ldst[2][2][4] = { { { gen_helper_sve2p1_ld1bb_c, gen_helper_sve2p1_ld1hh_le_c, @@ -8100,9 +8100,10 @@ static bool gen_ldst_c(DisasContext *s, TCGv_i64 addr, int zd, int png, gen_helper_sve2p1_st1dd_be_c, } } }; - TCGv_i32 t_png, t_desc; + TCGv_i32 t_png; + TCGv_i64 t_desc; TCGv_ptr t_zd; - uint32_t desc, lg2_rstride = 0; + uint64_t desc, lg2_rstride = 0; bool be = s->be_data == MO_BE; assert(n == 2 || n == 4); @@ -8132,7 +8133,7 @@ static bool gen_ldst_c(DisasContext *s, TCGv_i64 addr, int zd, int png, desc = n == 2 ? 0 : 1; desc = desc | (lg2_rstride << 1); desc = make_svemte_desc(s, vec_full_reg_size(s), 1, esz, is_write, desc); - t_desc = tcg_constant_i32(desc); + t_desc = tcg_constant_i64(desc); t_png = tcg_temp_new_i32(); tcg_gen_ld16u_i32(t_png, tcg_env, From aba39946baaf5ca73aae0b79e2cd0790ddafe291 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 23 Jul 2025 17:54:54 +0100 Subject: [PATCH 0117/1794] target/arm: Pack mtedesc into upper 32 bits of descriptor Instead of trying to pack mtedesc into the upper 17 bits of a 32-bit gvec descriptor, pass the gvec descriptor in the lower 32 bits and the mte descriptor in the upper 32 bits of a 64-bit operand. This fixes two bugs: (1) in gen_sve_ldr() and gen_sve_str() call gen_mte_checkN() with a length value which is the SVE vector length and can be up to 256 bytes. We don't assert there that it fits in the descriptor, so we would just fail to do the MTE checks on the right length of memory if the VL is more than 32 bytes (2) the new-in-SVE2p1 insns LD3Q, LD4Q, ST3Q, ST4Q also involve transfers of more than 32 bytes of memory. In this case we would assert at translate time. (Note for potential backporting: this commit depends on the previous "target/arm: Expand the descriptor for SME/SVE memory ops to i64".) Fixes: 7b1613a1020d2942 ("target/arm: Enable FEAT_SME2p1 on -cpu max") Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Message-id: 20250723165458.3509150-3-peter.maydell@linaro.org [PMM: expand commit message to clarify that we are fixing bugs here] Signed-off-by: Peter Maydell --- target/arm/internals.h | 8 +----- target/arm/tcg/sme_helper.c | 14 +++------- target/arm/tcg/sve_helper.c | 49 +++++++++++++--------------------- target/arm/tcg/translate-sve.c | 5 ++-- 4 files changed, 25 insertions(+), 51 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index c4765e44893e3..1b3d0244fd6ee 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1623,19 +1623,13 @@ FIELD(PREDDESC, OPRSZ, 0, 6) FIELD(PREDDESC, ESZ, 6, 2) FIELD(PREDDESC, DATA, 8, 24) -/* - * The SVE simd_data field, for memory ops, contains either - * rd (5 bits) or a shift count (2 bits). - */ -#define SVE_MTEDESC_SHIFT 5 - /* Bits within a descriptor passed to the helper_mte_check* functions. */ FIELD(MTEDESC, MIDX, 0, 4) FIELD(MTEDESC, TBI, 4, 2) FIELD(MTEDESC, TCMA, 6, 2) FIELD(MTEDESC, WRITE, 8, 1) FIELD(MTEDESC, ALIGN, 9, 3) -FIELD(MTEDESC, SIZEM1, 12, SIMD_DATA_BITS - SVE_MTEDESC_SHIFT - 12) /* size - 1 */ +FIELD(MTEDESC, SIZEM1, 12, 32 - 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); diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 0b55f13f8c5b9..075360d8b8abf 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -666,19 +666,16 @@ void sme_ld1(CPUARMState *env, void *za, uint64_t *vg, static inline QEMU_ALWAYS_INLINE void sme_ld1_mte(CPUARMState *env, void *za, uint64_t *vg, - target_ulong addr, uint32_t desc, uintptr_t ra, + target_ulong addr, uint64_t desc, uintptr_t ra, const int esz, bool vertical, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn, ClearFn *clr_fn, CopyFn *cpy_fn) { - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + uint32_t mtedesc = desc >> 32; int bit55 = extract64(addr, 55, 1); - /* Remove mtedesc from the normal sve descriptor. */ - desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); - /* Perform gross MTE suppression early. */ if (!tbi_check(mtedesc, bit55) || tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { @@ -854,16 +851,13 @@ void sme_st1(CPUARMState *env, void *za, uint64_t *vg, static inline QEMU_ALWAYS_INLINE void sme_st1_mte(CPUARMState *env, void *za, uint64_t *vg, target_ulong addr, - uint32_t desc, uintptr_t ra, int esz, bool vertical, + uint64_t desc, uintptr_t ra, int esz, bool vertical, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + uint32_t mtedesc = desc >> 32; int bit55 = extract64(addr, 55, 1); - /* Remove mtedesc from the normal sve descriptor. */ - desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); - /* Perform gross MTE suppression early. */ if (!tbi_check(mtedesc, bit55) || tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 9fc2c05879b97..d0fb4138d21f8 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -6362,17 +6362,14 @@ void sve_ldN_r(CPUARMState *env, uint64_t *vg, const target_ulong addr, static inline QEMU_ALWAYS_INLINE void sve_ldN_r_mte(CPUARMState *env, uint64_t *vg, target_ulong addr, - uint32_t desc, const uintptr_t ra, + uint64_t desc, const uintptr_t ra, const int esz, const int msz, const int N, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + uint32_t mtedesc = desc >> 32; int bit55 = extract64(addr, 55, 1); - /* Remove mtedesc from the normal sve descriptor. */ - desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); - /* Perform gross MTE suppression early. */ if (!tbi_check(mtedesc, bit55) || tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { @@ -6727,17 +6724,14 @@ void sve_ldnfff1_r(CPUARMState *env, void *vg, const target_ulong addr, static inline QEMU_ALWAYS_INLINE void sve_ldnfff1_r_mte(CPUARMState *env, void *vg, target_ulong addr, - uint32_t desc, const uintptr_t retaddr, + uint64_t desc, const uintptr_t retaddr, const int esz, const int msz, const SVEContFault fault, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + uint32_t mtedesc = desc >> 32; int bit55 = extract64(addr, 55, 1); - /* Remove mtedesc from the normal sve descriptor. */ - desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); - /* Perform gross MTE suppression early. */ if (!tbi_check(mtedesc, bit55) || tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { @@ -6985,17 +6979,14 @@ void sve_stN_r(CPUARMState *env, uint64_t *vg, target_ulong addr, static inline QEMU_ALWAYS_INLINE void sve_stN_r_mte(CPUARMState *env, uint64_t *vg, target_ulong addr, - uint32_t desc, const uintptr_t ra, + uint64_t desc, const uintptr_t ra, const int esz, const int msz, const int N, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + uint32_t mtedesc = desc >> 32; int bit55 = extract64(addr, 55, 1); - /* Remove mtedesc from the normal sve descriptor. */ - desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); - /* Perform gross MTE suppression early. */ if (!tbi_check(mtedesc, bit55) || tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { @@ -7183,14 +7174,12 @@ void sve_ld1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, static inline QEMU_ALWAYS_INLINE void sve_ld1_z_mte(CPUARMState *env, void *vd, uint64_t *vg, void *vm, - target_ulong base, uint32_t desc, uintptr_t retaddr, + target_ulong base, uint64_t desc, uintptr_t retaddr, int esize, int msize, zreg_off_fn *off_fn, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); - /* Remove mtedesc from the normal sve descriptor. */ - desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + uint32_t mtedesc = desc >> 32; /* * ??? TODO: For the 32-bit offset extractions, base + ofs cannot @@ -7395,15 +7384,13 @@ void sve_ldff1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, static inline QEMU_ALWAYS_INLINE void sve_ldff1_z_mte(CPUARMState *env, void *vd, uint64_t *vg, void *vm, - target_ulong base, uint32_t desc, uintptr_t retaddr, + target_ulong base, uint64_t desc, uintptr_t retaddr, const int esz, const int msz, zreg_off_fn *off_fn, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); - /* Remove mtedesc from the normal sve descriptor. */ - desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + uint32_t mtedesc = desc >> 32; /* * ??? TODO: For the 32-bit offset extractions, base + ofs cannot @@ -7600,14 +7587,12 @@ void sve_st1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, static inline QEMU_ALWAYS_INLINE void sve_st1_z_mte(CPUARMState *env, void *vd, uint64_t *vg, void *vm, - target_ulong base, uint32_t desc, uintptr_t retaddr, + target_ulong base, uint64_t desc, uintptr_t retaddr, int esize, int msize, zreg_off_fn *off_fn, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); - /* Remove mtedesc from the normal sve descriptor. */ - desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + uint32_t mtedesc = desc >> 32; /* * ??? TODO: For the 32-bit offset extractions, base + ofs cannot @@ -7853,14 +7838,15 @@ static void sve2p1_cont_ldst_mte_check(SVEContLdSt *info, CPUARMState *env, static inline QEMU_ALWAYS_INLINE void sve2p1_ld1_c(CPUARMState *env, ARMVectorReg *zd, const vaddr addr, - uint32_t png, uint32_t desc, + uint32_t png, uint64_t desc64, const uintptr_t ra, const MemOp esz, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { + uint32_t mtedesc = desc64 >> 32; + uint32_t desc = desc64; const unsigned N = (desc >> SIMD_DATA_SHIFT) & 1 ? 4 : 2; const unsigned rstride = 1 << ((desc >> (SIMD_DATA_SHIFT + 1)) % 4); - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); const intptr_t reg_max = simd_oprsz(desc); const unsigned esize = 1 << esz; intptr_t count_off, count_last; @@ -8025,14 +8011,15 @@ DO_LD1_2(ld1dd, MO_64) static inline QEMU_ALWAYS_INLINE void sve2p1_st1_c(CPUARMState *env, ARMVectorReg *zd, const vaddr addr, - uint32_t png, uint32_t desc, + uint32_t png, uint64_t desc64, const uintptr_t ra, const int esz, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { + uint32_t mtedesc = desc64 >> 32; + uint32_t desc = desc64; const unsigned N = (desc >> SIMD_DATA_SHIFT) & 1 ? 4 : 2; const unsigned rstride = 1 << ((desc >> (SIMD_DATA_SHIFT + 1)) % 4); - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); const intptr_t reg_max = simd_oprsz(desc); const unsigned esize = 1 << esz; intptr_t count_off, count_last; diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 849151826e2b5..5cba7b87bdcea 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -4893,7 +4893,6 @@ uint64_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, assert(nregs >= 1 && nregs <= 4); sizem1 = (nregs << msz) - 1; assert(sizem1 <= R_MTEDESC_SIZEM1_MASK >> R_MTEDESC_SIZEM1_SHIFT); - assert(data < 1u << SVE_MTEDESC_SHIFT); if (s->mte_active[0]) { desc = FIELD_DP32(desc, MTEDESC, MIDX, get_mem_index(s)); @@ -4901,9 +4900,9 @@ uint64_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); desc = FIELD_DP32(desc, MTEDESC, SIZEM1, sizem1); - desc <<= SVE_MTEDESC_SHIFT; + desc <<= 32; } - return simd_desc(vsz, vsz, desc | data); + return simd_desc(vsz, vsz, data) | desc; } static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, From b79f944e09657f63b6dd6e78ac7966fdc7a3e6d1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 23 Jul 2025 17:54:55 +0100 Subject: [PATCH 0118/1794] decodetree: Infer argument set before inferring format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Failure to confirm an argument set first may result in the selection of a format which leaves extra arguments to be filled in by the pattern. Signed-off-by: Richard Henderson Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Tested-by: Peter Maydell Message-id: 20250723165458.3509150-4-peter.maydell@linaro.org Message-id: 20250722183343.273533-1-richard.henderson@linaro.org Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Tested-by: Peter Maydell Signed-off-by: Peter Maydell --- scripts/decodetree.py | 7 ++++--- tests/decode/meson.build | 1 + tests/decode/succ_infer1.decode | 4 ++++ 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 tests/decode/succ_infer1.decode diff --git a/scripts/decodetree.py b/scripts/decodetree.py index e8b72da3a97a2..f992472b73e9d 100644 --- a/scripts/decodetree.py +++ b/scripts/decodetree.py @@ -1016,9 +1016,12 @@ def infer_format(arg, fieldmask, flds, width): else: var_flds[n] = c + if not arg: + arg = infer_argument_set(flds) + # Look for an existing format with the same argument set and fields for fmt in formats.values(): - if arg and fmt.base != arg: + if fmt.base != arg: continue if fieldmask != fmt.fieldmask: continue @@ -1029,8 +1032,6 @@ def infer_format(arg, fieldmask, flds, width): return (fmt, const_flds) name = decode_function + '_Fmt_' + str(len(formats)) - if not arg: - arg = infer_argument_set(flds) fmt = Format(name, 0, arg, 0, 0, 0, fieldmask, var_flds, width) formats[name] = fmt diff --git a/tests/decode/meson.build b/tests/decode/meson.build index b13fada9800e6..63405ca08fdf6 100644 --- a/tests/decode/meson.build +++ b/tests/decode/meson.build @@ -41,6 +41,7 @@ succ_tests = [ 'succ_argset_type1.decode', 'succ_function.decode', 'succ_ident1.decode', + 'succ_infer1.decode', 'succ_named_field.decode', 'succ_pattern_group_nest1.decode', 'succ_pattern_group_nest2.decode', diff --git a/tests/decode/succ_infer1.decode b/tests/decode/succ_infer1.decode new file mode 100644 index 0000000000000..6fa40bada5c90 --- /dev/null +++ b/tests/decode/succ_infer1.decode @@ -0,0 +1,4 @@ +&rprr_load rd pg rn rm dtype nreg +@rprr_load .... .... ... rm:5 ... pg:3 rn:5 rd:5 &rprr_load + +LD1Q 1100 0100 000 rm:5 101 pg:3 rn:5 rd:5 From 1c6aae5efbd28ac35003dea341364cd63a4515a1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 23 Jul 2025 17:54:56 +0100 Subject: [PATCH 0119/1794] target/arm: LD1Q, ST1Q are vector + scalar, not scalar + vector Unlike the "LD1D (scalar + vector)" etc instructions, LD1Q is vector + scalar. This means that: * the vector and the scalar register are in opposite fields in the encoding * 31 in the scalar register field is XZR, not XSP The same applies for ST1Q. This means we can't reuse the trans_LD1_zprz() and trans_ST1_zprz() functions for LD1Q and ST1Q. Split them out to use their own trans functions. Note that the change made here to sve.decode requires the decodetree bugfix "decodetree: Infer argument set before inferring format" to avoid a spurious compile-time error about "dtype". Fixes: d2aa9a804ee678f ("target/arm: Implement LD1Q, ST1Q for SVE2p1") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250723165458.3509150-5-peter.maydell@linaro.org --- target/arm/tcg/sve.decode | 12 +++---- target/arm/tcg/translate-sve.c | 65 ++++++++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index aea7f5197307f..ab63cfaa0f0c0 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1343,9 +1343,9 @@ LD1_zprz 1100010 10 1. ..... 1.. ... ..... ..... \ LD1_zprz 1100010 11 1. ..... 11. ... ..... ..... \ @rprr_g_load_sc esz=3 msz=3 u=1 -# LD1Q -LD1_zprz 1100 0100 000 rm:5 101 pg:3 rn:5 rd:5 \ - &rprr_gather_load u=1 ff=0 xs=2 esz=4 msz=4 scale=0 +# LD1Q. Note that this is subtly different from LD1_zprz because +# it is vector + scalar, not scalar + vector. +LD1Q 1100 0100 000 rm:5 101 pg:3 rn:5 rd:5 # SVE 64-bit gather load (vector plus immediate) LD1_zpiz 1100010 .. 01 ..... 1.. ... ..... ..... \ @@ -1450,9 +1450,9 @@ ST1_zprz 1110010 .. 01 ..... 101 ... ..... ..... \ ST1_zprz 1110010 .. 00 ..... 101 ... ..... ..... \ @rprr_scatter_store xs=2 esz=3 scale=0 -# ST1Q -ST1_zprz 1110 0100 001 rm:5 001 pg:3 rn:5 rd:5 \ - &rprr_scatter_store xs=2 msz=4 esz=4 scale=0 +# ST1Q. Note that this is subtly different from ST1_zprz because +# it is vector + scalar, not scalar + vector. +ST1Q 1110 0100 001 rm:5 001 pg:3 rn:5 rd:5 # SVE 64-bit scatter store (vector plus immediate) ST1_zpiz 1110010 .. 10 ..... 101 ... ..... ..... \ diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 5cba7b87bdcea..07b827fa8e898 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -6179,9 +6179,7 @@ static bool trans_LD1_zprz(DisasContext *s, arg_LD1_zprz *a) bool be = s->be_data == MO_BE; bool mte = s->mte_active[0]; - if (a->esz < MO_128 - ? !dc_isar_feature(aa64_sve, s) - : !dc_isar_feature(aa64_sve2p1, s)) { + if (!dc_isar_feature(aa64_sve, s)) { return false; } s->is_nonstreaming = true; @@ -6196,10 +6194,6 @@ static bool trans_LD1_zprz(DisasContext *s, arg_LD1_zprz *a) case MO_64: fn = gather_load_fn64[mte][be][a->ff][a->xs][a->u][a->msz]; break; - case MO_128: - assert(!a->ff && a->u && a->xs == 2 && a->msz == MO_128); - fn = gather_load_fn128[mte][be]; - break; default: g_assert_not_reached(); } @@ -6210,6 +6204,32 @@ static bool trans_LD1_zprz(DisasContext *s, arg_LD1_zprz *a) return true; } +static bool trans_LD1Q(DisasContext *s, arg_LD1Q *a) +{ + gen_helper_gvec_mem_scatter *fn = NULL; + bool be = s->be_data == MO_BE; + bool mte = s->mte_active[0]; + + if (!dc_isar_feature(aa64_sve2p1, s)) { + return false; + } + s->is_nonstreaming = true; + if (!sve_access_check(s)) { + return true; + } + + fn = gather_load_fn128[mte][be]; + assert(fn != NULL); + + /* + * Unlike LD1_zprz, a->rm is the scalar register and it can be XZR, not XSP. + * a->rn is the vector register. + */ + do_mem_zpz(s, a->rd, a->pg, a->rn, 0, + cpu_reg(s, a->rm), MO_128, false, fn); + return true; +} + static bool trans_LD1_zpiz(DisasContext *s, arg_LD1_zpiz *a) { gen_helper_gvec_mem_scatter *fn = NULL; @@ -6386,9 +6406,7 @@ static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a) if (a->esz < a->msz || (a->msz == 0 && a->scale)) { return false; } - if (a->esz < MO_128 - ? !dc_isar_feature(aa64_sve, s) - : !dc_isar_feature(aa64_sve2p1, s)) { + if (!dc_isar_feature(aa64_sve, s)) { return false; } s->is_nonstreaming = true; @@ -6402,10 +6420,6 @@ static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a) case MO_64: fn = scatter_store_fn64[mte][be][a->xs][a->msz]; break; - case MO_128: - assert(a->xs == 2 && a->msz == MO_128); - fn = scatter_store_fn128[mte][be]; - break; default: g_assert_not_reached(); } @@ -6414,6 +6428,29 @@ static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a) return true; } +static bool trans_ST1Q(DisasContext *s, arg_ST1Q *a) +{ + gen_helper_gvec_mem_scatter *fn; + bool be = s->be_data == MO_BE; + bool mte = s->mte_active[0]; + + if (!dc_isar_feature(aa64_sve2p1, s)) { + return false; + } + s->is_nonstreaming = true; + if (!sve_access_check(s)) { + return true; + } + fn = scatter_store_fn128[mte][be]; + /* + * Unlike ST1_zprz, a->rm is the scalar register, and it + * can be XZR, not XSP. a->rn is the vector register. + */ + do_mem_zpz(s, a->rd, a->pg, a->rn, 0, + cpu_reg(s, a->rm), MO_128, true, fn); + return true; +} + static bool trans_ST1_zpiz(DisasContext *s, arg_ST1_zpiz *a) { gen_helper_gvec_mem_scatter *fn = NULL; From 4726be1c69606e34c3cc4c26e39e252a9856b3d3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 23 Jul 2025 17:54:57 +0100 Subject: [PATCH 0120/1794] target/arm: Pass correct esize to sve_st1_z() for LD1Q, ST1Q Our implementation of the helper functions for the LD1Q and ST1Q insns reused the existing DO_LD1_ZPZ_D and DO_ST1_ZPZ_D macros. This passes the wrong esize (8, not 16) to sve_ldl_z(). Create new macros DO_LD1_ZPZ_Q and DO_ST1_ZPZ_Q which pass the correct esize, and use them for the LD1Q and ST1Q helpers. Fixes: d2aa9a804ee ("target/arm: Implement LD1Q, ST1Q for SVE2p1") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250723165458.3509150-6-peter.maydell@linaro.org --- target/arm/tcg/sve_helper.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index d0fb4138d21f8..c4aaf0cc45f4e 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -7219,6 +7219,20 @@ void HELPER(sve_ld##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ off_##OFS##_d, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ } +#define DO_LD1_ZPZ_Q(MEM, OFS, MSZ) \ +void HELPER(sve_ld##MEM##_##OFS)(CPUARMState *env, void *vd, void *vg, \ + void *vm, target_ulong base, uint64_t desc) \ +{ \ + sve_ld1_z(env, vd, vg, vm, base, desc, GETPC(), 0, 16, 1 << MSZ, \ + off_##OFS##_d, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ +} \ +void HELPER(sve_ld##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ + void *vm, target_ulong base, uint64_t desc) \ +{ \ + sve_ld1_z_mte(env, vd, vg, vm, base, desc, GETPC(), 16, 1 << MSZ, \ + off_##OFS##_d, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ +} + DO_LD1_ZPZ_S(bsu, zsu, MO_8) DO_LD1_ZPZ_S(bsu, zss, MO_8) DO_LD1_ZPZ_D(bdu, zsu, MO_8) @@ -7283,8 +7297,8 @@ DO_LD1_ZPZ_D(dd_be, zsu, MO_64) DO_LD1_ZPZ_D(dd_be, zss, MO_64) DO_LD1_ZPZ_D(dd_be, zd, MO_64) -DO_LD1_ZPZ_D(qq_le, zd, MO_128) -DO_LD1_ZPZ_D(qq_be, zd, MO_128) +DO_LD1_ZPZ_Q(qq_le, zd, MO_128) +DO_LD1_ZPZ_Q(qq_be, zd, MO_128) #undef DO_LD1_ZPZ_S #undef DO_LD1_ZPZ_D @@ -7632,6 +7646,20 @@ void HELPER(sve_st##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ off_##OFS##_d, sve_st1##MEM##_host, sve_st1##MEM##_tlb); \ } +#define DO_ST1_ZPZ_Q(MEM, OFS, MSZ) \ +void HELPER(sve_st##MEM##_##OFS)(CPUARMState *env, void *vd, void *vg, \ + void *vm, target_ulong base, uint64_t desc) \ +{ \ + sve_st1_z(env, vd, vg, vm, base, desc, GETPC(), 0, 16, 1 << MSZ, \ + off_##OFS##_d, sve_st1##MEM##_host, sve_st1##MEM##_tlb); \ +} \ +void HELPER(sve_st##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ + void *vm, target_ulong base, uint64_t desc) \ +{ \ + sve_st1_z_mte(env, vd, vg, vm, base, desc, GETPC(), 16, 1 << MSZ, \ + off_##OFS##_d, sve_st1##MEM##_host, sve_st1##MEM##_tlb); \ +} + DO_ST1_ZPZ_S(bs, zsu, MO_8) DO_ST1_ZPZ_S(hs_le, zsu, MO_16) DO_ST1_ZPZ_S(hs_be, zsu, MO_16) @@ -7668,8 +7696,8 @@ DO_ST1_ZPZ_D(sd_be, zd, MO_32) DO_ST1_ZPZ_D(dd_le, zd, MO_64) DO_ST1_ZPZ_D(dd_be, zd, MO_64) -DO_ST1_ZPZ_D(qq_le, zd, MO_128) -DO_ST1_ZPZ_D(qq_be, zd, MO_128) +DO_ST1_ZPZ_Q(qq_le, zd, MO_128) +DO_ST1_ZPZ_Q(qq_be, zd, MO_128) #undef DO_ST1_ZPZ_S #undef DO_ST1_ZPZ_D From 7428c46c06b4365ee5131dcdcc3da218c5e99ddd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 23 Jul 2025 17:54:58 +0100 Subject: [PATCH 0121/1794] target/arm: Fix LD1W, LD1D to 128-bit elements In our implementation of the SVE2p1 contiguous load to 128-bit element insns such as LD1D (scalar plus scalar, single register), we got the order of the arguments to the DO_LD1_2() macro wrong. Here the first argument is the element size and the second is the memory size, and the element size is always the same size or larger than the memory size. For the 128-bit versions, we want to load either 32-bit or 64-bit values from memory and extend them to the 128-bit vector element, but were trying to load 128 bit values and then stuff them into 32-bit or 64-bit vector elements. Correct the macro ordering. Fixes: fc5f060bcb7b ("target/arm: Implement {LD1, ST1}{W, D} (128-bit element) for SVE2p1") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250723165458.3509150-7-peter.maydell@linaro.org --- target/arm/tcg/sve_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index c4aaf0cc45f4e..c442fcb540df2 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -6439,8 +6439,8 @@ DO_LD1_2(ld1sds, MO_64, MO_32) DO_LD1_2(ld1dd, MO_64, MO_64) -DO_LD1_2(ld1squ, MO_32, MO_128) -DO_LD1_2(ld1dqu, MO_64, MO_128) +DO_LD1_2(ld1squ, MO_128, MO_32) +DO_LD1_2(ld1dqu, MO_128, MO_64) #undef DO_LD1_1 #undef DO_LD1_2 From bd52d8bc9e01dcf68731dcdd9d2b8ebcb9fc5692 Mon Sep 17 00:00:00 2001 From: JianChunfu Date: Fri, 14 Feb 2025 15:20:29 +0800 Subject: [PATCH 0122/1794] hw/arm/smmu-common: Avoid using inlined functions with external linkage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similarly to commit 9de9fa5c ("hw/arm/smmu-common: Avoid using inlined functions with external linkage"): None of our code base require / use inlined functions with external linkage. Some places use internal inlining in the hot path. These two functions are certainly not in any hot path and don't justify any inlining, so these are likely oversights rather than intentional. Fixes: b8fa4c23 (hw/arm/smmu: Support nesting in the rest of commands) Signed-off-by: JianChunfu Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- hw/arm/smmu-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index f39b99e5269df..0dcaf2f589710 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -319,7 +319,7 @@ void smmu_iotlb_inv_vmid(SMMUState *s, int vmid) g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_vmid, &vmid); } -inline void smmu_iotlb_inv_vmid_s1(SMMUState *s, int vmid) +void smmu_iotlb_inv_vmid_s1(SMMUState *s, int vmid) { trace_smmu_iotlb_inv_vmid_s1(vmid); g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_vmid_s1, &vmid); From a7aa2af13e287e11cb2d73972353bfec161803a4 Mon Sep 17 00:00:00 2001 From: Mohamed Mediouni Date: Mon, 21 Jul 2025 17:29:02 +0200 Subject: [PATCH 0123/1794] target/arm: hvf: stubbing reads to LORC_EL1 Linux zeroes LORC_EL1 on boot at EL2, without further interaction with FEAT_LOR afterwards. Stub out LORC_EL1 accesses as FEAT_LOR is a mandatory extension on Armv8.1+. Signed-off-by: Mohamed Mediouni Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index bd6b5d11de8fe..47b0cd3a351c9 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -186,6 +186,7 @@ void hvf_arm_init_debug(void) #define SYSREG_OSLAR_EL1 SYSREG(2, 0, 1, 0, 4) #define SYSREG_OSLSR_EL1 SYSREG(2, 0, 1, 1, 4) #define SYSREG_OSDLR_EL1 SYSREG(2, 0, 1, 3, 4) +#define SYSREG_LORC_EL1 SYSREG(3, 0, 10, 4, 3) #define SYSREG_CNTPCT_EL0 SYSREG(3, 3, 14, 0, 1) #define SYSREG_CNTP_CTL_EL0 SYSREG(3, 3, 14, 2, 1) #define SYSREG_PMCR_EL0 SYSREG(3, 3, 9, 12, 0) @@ -1657,6 +1658,9 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_OSDLR_EL1: /* Dummy register */ return 0; + case SYSREG_LORC_EL1: + /* Dummy register */ + return 0; case SYSREG_ICC_AP0R0_EL1: case SYSREG_ICC_AP0R1_EL1: case SYSREG_ICC_AP0R2_EL1: From fbc8fb36e3636854195705cba13278befb94158d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 22 Jul 2025 09:50:01 +0200 Subject: [PATCH 0124/1794] scripts: add script to help distros use global Rust packages Some distros prefer to avoid vendored crate sources, and instead use local sources from e.g. ``/usr/share/cargo/registry``. Add a script, inspired by the Mesa spec file(*), that automatically performs this task. The script is meant to be invoked after unpacking the QEMU tarball. (*) This is the hack that Mesa uses: export MESON_PACKAGE_CACHE_DIR="%{cargo_registry}/" %define inst_crate_nameversion() %(basename %{cargo_registry}/%{1}-*) %define rewrite_wrap_file() sed -e "/source.*/d" -e "s/%{1}-.*/%{inst_crate_nameversion %{1}}/" -i subprojects/%{1}.wrap %rewrite_wrap_file proc-macro2 ... more %rewrite_wrap_file invocations follow ... Reviewed-by: Neal Gompa Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + docs/about/build-platforms.rst | 8 + scripts/get-wraps-from-cargo-registry.py | 190 +++++++++++++++++++++++ 3 files changed, 199 insertions(+) create mode 100755 scripts/get-wraps-from-cargo-registry.py diff --git a/MAINTAINERS b/MAINTAINERS index a462345618350..1604f3dfc1fde 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3511,6 +3511,7 @@ S: Maintained F: rust/qemu-api F: rust/qemu-api-macros F: rust/rustfmt.toml +F: scripts/get-wraps-from-cargo-registry.py Rust-related patches CC here L: qemu-rust@nongnu.org diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index 8ecbd6b26f7d0..8671c3be9cdbe 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -127,6 +127,14 @@ Rust build dependencies (or newer) package. The path to ``rustc`` and ``rustdoc`` must be provided manually to the configure script. + Some distros prefer to avoid vendored crate sources, and instead use + local sources from e.g. ``/usr/share/cargo/registry``. QEMU includes a + script, ``scripts/get-wraps-from-cargo-registry.py``, that automatically + performs this task. The script is meant to be invoked after unpacking + the QEMU tarball. QEMU also includes ``rust/Cargo.toml`` and + ``rust/Cargo.lock`` files that can be used to compute QEMU's build + dependencies, e.g. using ``cargo2rpm -p rust/Cargo.toml buildrequires``. + Optional build dependencies Build components whose absence does not affect the ability to build QEMU may not be available in distros, or may be too old for our requirements. diff --git a/scripts/get-wraps-from-cargo-registry.py b/scripts/get-wraps-from-cargo-registry.py new file mode 100755 index 0000000000000..31eed5c2dd451 --- /dev/null +++ b/scripts/get-wraps-from-cargo-registry.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python3 + +# SPDX-License-Identifier: GPL-2.0-or-later + +""" +get-wraps-from-cargo-registry.py - Update Meson subprojects from a global registry +""" + +# Copyright (C) 2025 Red Hat, Inc. +# +# Author: Paolo Bonzini + +import argparse +import configparser +import filecmp +import glob +import os +import subprocess +import sys + + +def get_name_and_semver(namever: str) -> tuple[str, str]: + """Split a subproject name into its name and semantic version parts""" + parts = namever.rsplit("-", 1) + if len(parts) != 2: + return namever, "" + + return parts[0], parts[1] + + +class UpdateSubprojects: + cargo_registry: str + top_srcdir: str + dry_run: bool + changes: int = 0 + + def find_installed_crate(self, namever: str) -> str | None: + """Find installed crate matching name and semver prefix""" + name, semver = get_name_and_semver(namever) + + # exact version match + path = os.path.join(self.cargo_registry, f"{name}-{semver}") + if os.path.exists(path): + return f"{name}-{semver}" + + # semver match + matches = sorted(glob.glob(f"{path}.*")) + return os.path.basename(matches[0]) if matches else None + + def compare_build_rs(self, orig_dir: str, registry_namever: str) -> None: + """Warn if the build.rs in the original directory differs from the registry version.""" + orig_build_rs = os.path.join(orig_dir, "build.rs") + new_build_rs = os.path.join(self.cargo_registry, registry_namever, "build.rs") + + msg = None + if os.path.isfile(orig_build_rs) != os.path.isfile(new_build_rs): + if os.path.isfile(orig_build_rs): + msg = f"build.rs removed in {registry_namever}" + if os.path.isfile(new_build_rs): + msg = f"build.rs added in {registry_namever}" + + elif os.path.isfile(orig_build_rs) and not filecmp.cmp(orig_build_rs, new_build_rs): + msg = f"build.rs changed from {orig_dir} to {registry_namever}" + + if msg: + print(f"⚠️ Warning: {msg}") + print(" This may affect the build process - please review the differences.") + + def update_subproject(self, wrap_file: str, registry_namever: str) -> None: + """Modify [wrap-file] section to point to self.cargo_registry.""" + assert wrap_file.endswith("-rs.wrap") + wrap_name = wrap_file[:-5] + + env = os.environ.copy() + env["MESON_PACKAGE_CACHE_DIR"] = self.cargo_registry + + config = configparser.ConfigParser() + config.read(wrap_file) + if "wrap-file" not in config: + return + + # do not download the wrap, always use the local copy + orig_dir = config["wrap-file"]["directory"] + if os.path.exists(orig_dir) and orig_dir != registry_namever: + self.compare_build_rs(orig_dir, registry_namever) + + if self.dry_run: + if orig_dir == registry_namever: + print(f"Will install {orig_dir} from registry.") + else: + print(f"Will replace {orig_dir} with {registry_namever}.") + self.changes += 1 + return + + config["wrap-file"]["directory"] = registry_namever + for key in list(config["wrap-file"].keys()): + if key.startswith("source"): + del config["wrap-file"][key] + + # replace existing directory with installed version + if os.path.exists(orig_dir): + subprocess.run( + ["meson", "subprojects", "purge", "--confirm", wrap_name], + cwd=self.top_srcdir, + env=env, + check=True, + ) + + with open(wrap_file, "w") as f: + config.write(f) + + if orig_dir == registry_namever: + print(f"Installing {orig_dir} from registry.") + else: + print(f"Replacing {orig_dir} with {registry_namever}.") + patch_dir = config["wrap-file"]["patch_directory"] + patch_dir = os.path.join("packagefiles", patch_dir) + _, ver = registry_namever.rsplit("-", 1) + subprocess.run( + ["meson", "rewrite", "kwargs", "set", "project", "/", "version", ver], + cwd=patch_dir, + env=env, + check=True, + ) + + subprocess.run( + ["meson", "subprojects", "download", wrap_name], + cwd=self.top_srcdir, + env=env, + check=True, + ) + self.changes += 1 + + @staticmethod + def parse_cmdline() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Replace Meson subprojects with packages in a Cargo registry" + ) + parser.add_argument( + "--cargo-registry", + default=os.environ.get("CARGO_REGISTRY"), + help="Path to Cargo registry (default: CARGO_REGISTRY env var)", + ) + parser.add_argument( + "--dry-run", + action="store_true", + default=False, + help="Do not actually replace anything", + ) + + args = parser.parse_args() + if not args.cargo_registry: + print("error: CARGO_REGISTRY environment variable not set and --cargo-registry not provided") + sys.exit(1) + + return args + + def __init__(self, args: argparse.Namespace): + self.cargo_registry = args.cargo_registry + self.dry_run = args.dry_run + self.top_srcdir = os.getcwd() + + def main(self) -> None: + if not os.path.exists("subprojects"): + print("'subprojects' directory not found, nothing to do.") + return + + os.chdir("subprojects") + for wrap_file in sorted(glob.glob("*-rs.wrap")): + namever = wrap_file[:-8] # Remove '-rs.wrap' + + registry_namever = self.find_installed_crate(namever) + if not registry_namever: + print(f"No installed crate found for {wrap_file}") + continue + + self.update_subproject(wrap_file, registry_namever) + + if self.changes: + if self.dry_run: + print("Rerun without --dry-run to apply changes.") + else: + print(f"✨ {self.changes} subproject(s) updated!") + else: + print("No changes.") + + +if __name__ == "__main__": + args = UpdateSubprojects.parse_cmdline() + UpdateSubprojects(args).main() From feea87cd6b645d5166bdd304aac88f47f63dc2ef Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 25 Jul 2025 01:10:12 +0200 Subject: [PATCH 0125/1794] target/i386: fix width of third operand of VINSERTx128 Table A-5 of the Intel manual incorrectly lists the third operand of VINSERTx128 as Wqq, but it is actually a 128-bit value. This is visible when W is a memory operand close to the end of the page. Fixes the recently-added poly1305_kunit test in linux-next. (No testcase yet, but I plan to modify test-avx2 to use memory close to the end of the page. This would work because the test vectors correctly have the memory operand as xmm2/m128). Reported-by: Eric Biggers Tested-by: Eric Biggers Cc: Ard Biesheuvel Cc: "Jason A. Donenfeld" Cc: Guenter Roeck Cc: qemu-stable@nongnu.org Fixes: 79068477686 ("target/i386: reimplement 0x0f 0x3a, add AVX", 2022-10-18) Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 853b1c8bf95e8..51038657f0f99 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -878,10 +878,10 @@ static const X86OpEntry opcodes_0F3A[256] = { [0x0e] = X86_OP_ENTRY4(VPBLENDW, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), [0x0f] = X86_OP_ENTRY4(PALIGNR, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), - [0x18] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX) p_66), + [0x18] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,dq, vex6 chk(W0) cpuid(AVX) p_66), [0x19] = X86_OP_ENTRY3(VEXTRACTx128, W,dq, V,qq, I,b, vex6 chk(W0) cpuid(AVX) p_66), - [0x38] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX2) p_66), + [0x38] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,dq, vex6 chk(W0) cpuid(AVX2) p_66), [0x39] = X86_OP_ENTRY3(VEXTRACTx128, W,dq, V,qq, I,b, vex6 chk(W0) cpuid(AVX2) p_66), /* Listed incorrectly as type 4 */ From 3cdd990aa920ec8f2994b634f758dab4a86ac167 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 25 Jul 2025 18:55:08 +0100 Subject: [PATCH 0126/1794] linux-user/aarch64: Clear TPIDR2_EL0 when delivering signals A recent change to the kernel (Linux commit b376108e1f88 "arm64/fpsimd: signal: Clear TPIDR2 when delivering signals") updated the signal-handler entry code to always clear TPIDR2_EL0. This is necessary for the userspace ZA lazy saving scheme to work correctly when unwinding exceptions across a signal boundary. (For the essay-length description of the incorrect behaviour and why this is the correct fix, see the commit message for the kernel commit.) Make QEMU also clear TPIDR2_EL0 on signal entry, applying the equivalent bugfix to our implementation. Note that getting this unwinding to work correctly also requires changes to the userspace code, e.g. as implemented in gcc in https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=b5ffc8e75a8 This change is technically an ABI change; from the kernel's point of view SME was never enabled (it was hidden behind CONFIG_BROKEN) before the change. From QEMU's point of view our SME-related signal handling was broken anyway as we weren't saving and restoring TPIDR2_EL0. Cc: qemu-stable@nongnu.org Fixes: 78011586b90d1 ("target/arm: Enable SME for user-only") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250725175510.3864231-2-peter.maydell@linaro.org> --- linux-user/aarch64/signal.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index d50cab78d83ab..6514b73ad98ef 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -666,8 +666,12 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, env->btype = 2; } - /* Invoke the signal handler with both SM and ZA disabled. */ + /* + * Invoke the signal handler with a clean SME state: both SM and ZA + * disabled and TPIDR2_EL0 cleared. + */ aarch64_set_svcr(env, 0, R_SVCR_SM_MASK | R_SVCR_ZA_MASK); + env->cp15.tpidr2_el0 = 0; if (info) { frame->info = *info; From 99870aff907b1c863cd32558b543f0ab0d0e74ba Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 25 Jul 2025 18:55:09 +0100 Subject: [PATCH 0127/1794] linux-user/aarch64: Support TPIDR2_MAGIC signal frame record FEAT_SME adds the TPIDR2 userspace-accessible system register, which is used as part of the procedure calling standard's lazy saving scheme for the ZA registers: https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#66the-za-lazy-saving-scheme The Linux kernel has a signal frame record for saving and restoring this value when calling signal handlers, but we forgot to implement this. The result is that code which tries to unwind an exception out of a signal handler will not work correctly. Add support for the missing record. Cc: qemu-stable@nongnu.org Fixes: 78011586b90d1 ("target/arm: Enable SME for user-only") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250725175510.3864231-3-peter.maydell@linaro.org> --- linux-user/aarch64/signal.c | 42 +++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index 6514b73ad98ef..f28ba807549da 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -121,6 +121,13 @@ struct target_za_context { #define TARGET_ZA_SIG_CONTEXT_SIZE(VQ) \ TARGET_ZA_SIG_ZAV_OFFSET(VQ, VQ * TARGET_SVE_VQ_BYTES) +#define TARGET_TPIDR2_MAGIC 0x54504902 + +struct target_tpidr2_context { + struct target_aarch64_ctx head; + uint64_t tpidr2; +}; + struct target_rt_sigframe { struct target_siginfo info; struct target_ucontext uc; @@ -253,6 +260,14 @@ static void target_setup_za_record(struct target_za_context *za, } } +static void target_setup_tpidr2_record(struct target_tpidr2_context *tpidr2, + CPUARMState *env) +{ + __put_user(TARGET_TPIDR2_MAGIC, &tpidr2->head.magic); + __put_user(sizeof(struct target_tpidr2_context), &tpidr2->head.size); + __put_user(env->cp15.tpidr2_el0, &tpidr2->tpidr2); +} + static void target_restore_general_frame(CPUARMState *env, struct target_rt_sigframe *sf) { @@ -403,6 +418,12 @@ static bool target_restore_za_record(CPUARMState *env, return true; } +static void target_restore_tpidr2_record(CPUARMState *env, + struct target_tpidr2_context *tpidr2) +{ + __get_user(env->cp15.tpidr2_el0, &tpidr2->tpidr2); +} + static int target_restore_sigframe(CPUARMState *env, struct target_rt_sigframe *sf) { @@ -410,6 +431,7 @@ static int target_restore_sigframe(CPUARMState *env, struct target_fpsimd_context *fpsimd = NULL; struct target_sve_context *sve = NULL; struct target_za_context *za = NULL; + struct target_tpidr2_context *tpidr2 = NULL; uint64_t extra_datap = 0; bool used_extra = false; int sve_size = 0; @@ -460,6 +482,14 @@ static int target_restore_sigframe(CPUARMState *env, za_size = size; break; + case TARGET_TPIDR2_MAGIC: + if (tpidr2 || size != sizeof(struct target_tpidr2_context) || + !cpu_isar_feature(aa64_sme, env_archcpu(env))) { + goto err; + } + tpidr2 = (struct target_tpidr2_context *)ctx; + break; + case TARGET_EXTRA_MAGIC: if (extra || size != sizeof(struct target_extra_context)) { goto err; @@ -497,6 +527,9 @@ static int target_restore_sigframe(CPUARMState *env, if (za && !target_restore_za_record(env, za, za_size, &svcr)) { goto err; } + if (tpidr2) { + target_restore_tpidr2_record(env, tpidr2); + } if (env->svcr != svcr) { env->svcr = svcr; arm_rebuild_hflags(env); @@ -568,8 +601,8 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, .total_size = offsetof(struct target_rt_sigframe, uc.tuc_mcontext.__reserved), }; - int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0; - int sve_size = 0, za_size = 0; + int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0, tpidr2_ofs = 0; + int sve_size = 0, za_size = 0, tpidr2_size = 0; struct target_rt_sigframe *frame; struct target_rt_frame_record *fr; abi_ulong frame_addr, return_addr; @@ -585,6 +618,8 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, sve_ofs = alloc_sigframe_space(sve_size, &layout); } if (cpu_isar_feature(aa64_sme, env_archcpu(env))) { + tpidr2_size = sizeof(struct target_tpidr2_context); + tpidr2_ofs = alloc_sigframe_space(tpidr2_size, &layout); /* ZA state needs saving only if it is enabled. */ if (FIELD_EX64(env->svcr, SVCR, ZA)) { za_size = TARGET_ZA_SIG_CONTEXT_SIZE(sme_vq(env)); @@ -644,6 +679,9 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, if (za_ofs) { target_setup_za_record((void *)frame + za_ofs, env, za_size); } + if (tpidr2_ofs) { + target_setup_tpidr2_record((void *)frame + tpidr2_ofs, env); + } /* Set up the stack frame for unwinding. */ fr = (void *)frame + fr_ofs; From e35215db401113867e20634622a370c0e8931797 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 25 Jul 2025 18:55:10 +0100 Subject: [PATCH 0128/1794] linux-user/aarch64: Support ZT_MAGIC signal frame record FEAT_SME2 adds the ZT0 register, whose contents may need to be preserved and restored on signal handler entry and exit. This is done with a new ZT_MAGIC record. We forgot to implement support for this in our linux-user code before enabling the SME2p1 emulation, which meant that a signal handler using SME would corrupt the ZT0 register value, and code that attempted to unwind an exception from inside a signal handler would not work. Add the missing record handling. Fixes: 7b1613a1020d2942 ("target/arm: Enable FEAT_SME2p1 on -cpu max") Signed-off-by: Peter Maydell Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250725175510.3864231-4-peter.maydell@linaro.org> --- linux-user/aarch64/signal.c | 93 ++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index f28ba807549da..668353bbda4bb 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -128,6 +128,23 @@ struct target_tpidr2_context { uint64_t tpidr2; }; +#define TARGET_ZT_MAGIC 0x5a544e01 + +struct target_zt_context { + struct target_aarch64_ctx head; + uint16_t nregs; + uint16_t reserved[3]; + /* ZTn register data immediately follows */ +}; + +#define TARGET_ZT_SIG_REG_BYTES (512 / 8) +#define TARGET_ZT_SIG_REGS_SIZE(n) (TARGET_ZT_SIG_REG_BYTES * (n)) +#define TARGET_ZT_SIG_CONTEXT_SIZE(n) (sizeof(struct target_zt_context) + \ + TARGET_ZT_SIG_REGS_SIZE(n)) +#define TARGET_ZT_SIG_REGS_OFFSET sizeof(struct target_zt_context) +QEMU_BUILD_BUG_ON(TARGET_ZT_SIG_REG_BYTES != \ + sizeof_field(CPUARMState, za_state.zt0)); + struct target_rt_sigframe { struct target_siginfo info; struct target_ucontext uc; @@ -268,6 +285,28 @@ static void target_setup_tpidr2_record(struct target_tpidr2_context *tpidr2, __put_user(env->cp15.tpidr2_el0, &tpidr2->tpidr2); } +static void target_setup_zt_record(struct target_zt_context *zt, + CPUARMState *env, int size) +{ + uint64_t *z; + + memset(zt, 0, sizeof(*zt)); + __put_user(TARGET_ZT_MAGIC, &zt->head.magic); + __put_user(size, &zt->head.size); + /* + * The record format allows for multiple ZT regs, but + * currently there is only one, ZT0. + */ + __put_user(1, &zt->nregs); + assert(size == TARGET_ZT_SIG_CONTEXT_SIZE(1)); + + /* ZT0 is the same byte-stream format as SVE regs and ZA */ + z = (void *)zt + TARGET_ZT_SIG_REGS_OFFSET; + for (int i = 0; i < ARRAY_SIZE(env->za_state.zt0); i++) { + __put_user_e(env->za_state.zt0[i], z + i, le); + } +} + static void target_restore_general_frame(CPUARMState *env, struct target_rt_sigframe *sf) { @@ -424,6 +463,30 @@ static void target_restore_tpidr2_record(CPUARMState *env, __get_user(env->cp15.tpidr2_el0, &tpidr2->tpidr2); } +static bool target_restore_zt_record(CPUARMState *env, + struct target_zt_context *zt, int size, + int svcr) +{ + uint16_t nregs; + uint64_t *z; + + if (!(FIELD_EX64(svcr, SVCR, ZA))) { + return false; + } + + __get_user(nregs, &zt->nregs); + + if (nregs != 1) { + return false; + } + + z = (void *)zt + TARGET_ZT_SIG_REGS_OFFSET; + for (int i = 0; i < ARRAY_SIZE(env->za_state.zt0); i++) { + __get_user_e(env->za_state.zt0[i], z + i, le); + } + return true; +} + static int target_restore_sigframe(CPUARMState *env, struct target_rt_sigframe *sf) { @@ -432,10 +495,12 @@ static int target_restore_sigframe(CPUARMState *env, struct target_sve_context *sve = NULL; struct target_za_context *za = NULL; struct target_tpidr2_context *tpidr2 = NULL; + struct target_zt_context *zt = NULL; uint64_t extra_datap = 0; bool used_extra = false; int sve_size = 0; int za_size = 0; + int zt_size = 0; int svcr = 0; target_restore_general_frame(env, sf); @@ -490,6 +555,15 @@ static int target_restore_sigframe(CPUARMState *env, tpidr2 = (struct target_tpidr2_context *)ctx; break; + case TARGET_ZT_MAGIC: + if (zt || size != TARGET_ZT_SIG_CONTEXT_SIZE(1) || + !cpu_isar_feature(aa64_sme2, env_archcpu(env))) { + goto err; + } + zt = (struct target_zt_context *)ctx; + zt_size = size; + break; + case TARGET_EXTRA_MAGIC: if (extra || size != sizeof(struct target_extra_context)) { goto err; @@ -530,6 +604,13 @@ static int target_restore_sigframe(CPUARMState *env, if (tpidr2) { target_restore_tpidr2_record(env, tpidr2); } + /* + * NB that we must restore ZT after ZA so the check that there's + * no ZT record if SVCR.ZA is 0 gets the right value of SVCR. + */ + if (zt && !target_restore_zt_record(env, zt, zt_size, svcr)) { + goto err; + } if (env->svcr != svcr) { env->svcr = svcr; arm_rebuild_hflags(env); @@ -602,7 +683,8 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, uc.tuc_mcontext.__reserved), }; int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0, tpidr2_ofs = 0; - int sve_size = 0, za_size = 0, tpidr2_size = 0; + int zt_ofs = 0; + int sve_size = 0, za_size = 0, tpidr2_size = 0, zt_size = 0; struct target_rt_sigframe *frame; struct target_rt_frame_record *fr; abi_ulong frame_addr, return_addr; @@ -628,6 +710,12 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, } za_ofs = alloc_sigframe_space(za_size, &layout); } + if (cpu_isar_feature(aa64_sme2, env_archcpu(env)) && + FIELD_EX64(env->svcr, SVCR, ZA)) { + /* If SME ZA storage is enabled, we must also save SME2 ZT0 */ + zt_size = TARGET_ZT_SIG_CONTEXT_SIZE(1); + zt_ofs = alloc_sigframe_space(zt_size, &layout); + } if (layout.extra_ofs) { /* Reserve space for the extra end marker. The standard end marker @@ -682,6 +770,9 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, if (tpidr2_ofs) { target_setup_tpidr2_record((void *)frame + tpidr2_ofs, env); } + if (zt_ofs) { + target_setup_zt_record((void *)frame + zt_ofs, env, zt_size); + } /* Set up the stack frame for unwinding. */ fr = (void *)frame + fr_ofs; From 8d6c7de1cc71207ccc047583df0c84363a5da16b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:04 +0100 Subject: [PATCH 0129/1794] docs/user: clarify user-mode expects the same OS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While we somewhat cover this later when we talk about supported operating systems make it clear in the front matter. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-2-alex.bennee@linaro.org> --- docs/user/index.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/user/index.rst b/docs/user/index.rst index 782d27cda2762..2307580cb97c2 100644 --- a/docs/user/index.rst +++ b/docs/user/index.rst @@ -5,8 +5,9 @@ User Mode Emulation ------------------- This section of the manual is the overall guide for users using QEMU -for user-mode emulation. In this mode, QEMU can launch -processes compiled for one CPU on another CPU. +for user-mode emulation. In this mode, QEMU can launch programs +compiled for one CPU architecture on the same Operating System (OS) +but running on a different CPU architecture. .. toctree:: :maxdepth: 2 From 9b6656668f01144288e1190980e1689394cc236c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:05 +0100 Subject: [PATCH 0130/1794] docs/system: reword the TAP notes to remove tarball ref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't ship the tarball and users should generally look to the distribution specific packaging. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/560 Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-3-alex.bennee@linaro.org> --- docs/system/devices/net.rst | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/system/devices/net.rst b/docs/system/devices/net.rst index 4d787c3aeb0a2..7d76fe88c4541 100644 --- a/docs/system/devices/net.rst +++ b/docs/system/devices/net.rst @@ -21,11 +21,17 @@ configure it as if it was a real ethernet card. Linux host ^^^^^^^^^^ -As an example, you can download the ``linux-test-xxx.tar.gz`` archive -and copy the script ``qemu-ifup`` in ``/etc`` and configure properly -``sudo`` so that the command ``ifconfig`` contained in ``qemu-ifup`` can -be executed as root. You must verify that your host kernel supports the -TAP network interfaces: the device ``/dev/net/tun`` must be present. +A distribution will generally provide specific helper scripts when it +packages QEMU. By default these are found at ``/etc/qemu-ifup`` and +``/etc/qemu-ifdown`` and are called appropriately when QEMU wants to +change the network state. + +If QEMU is being run as a non-privileged user you may need properly +configure ``sudo`` so that network commands in the scripts can be +executed as root. + +You must verify that your host kernel supports the TAP network +interfaces: the device ``/dev/net/tun`` must be present. See :ref:`sec_005finvocation` to have examples of command lines using the TAP network interfaces. From 1ab41da6bb17b8c8abaabe5c9dbf65d0667cd9ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:06 +0100 Subject: [PATCH 0131/1794] docs/user: clean up headings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was a slightly duff format for rst, make it use proper headings. Reviewed-by: Manos Pitsidianakis Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-4-alex.bennee@linaro.org> --- docs/user/main.rst | 50 ++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/docs/user/main.rst b/docs/user/main.rst index 9a1c60448c58e..b8ff203c212fc 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -17,28 +17,34 @@ Features QEMU user space emulation has the following notable features: -**System call translation:** - QEMU includes a generic system call translator. This means that the - parameters of the system calls can be converted to fix endianness and - 32/64-bit mismatches between hosts and targets. IOCTLs can be - converted too. - -**POSIX signal handling:** - QEMU can redirect to the running program all signals coming from the - host (such as ``SIGALRM``), as well as synthesize signals from - virtual CPU exceptions (for example ``SIGFPE`` when the program - executes a division by zero). - - QEMU relies on the host kernel to emulate most signal system calls, - for example to emulate the signal mask. On Linux, QEMU supports both - normal and real-time signals. - -**Threading:** - On Linux, QEMU can emulate the ``clone`` syscall and create a real - host thread (with a separate virtual CPU) for each emulated thread. - Note that not all targets currently emulate atomic operations - correctly. x86 and Arm use a global lock in order to preserve their - semantics. +System call translation +~~~~~~~~~~~~~~~~~~~~~~~ + +QEMU includes a generic system call translator. This means that the +parameters of the system calls can be converted to fix endianness +and 32/64-bit mismatches between hosts and targets. IOCTLs can be +converted too. + +POSIX signal handling +~~~~~~~~~~~~~~~~~~~~~ + +QEMU can redirect to the running program all signals coming from the +host (such as ``SIGALRM``), as well as synthesize signals from +virtual CPU exceptions (for example ``SIGFPE`` when the program +executes a division by zero). + +QEMU relies on the host kernel to emulate most signal system calls, +for example to emulate the signal mask. On Linux, QEMU supports both +normal and real-time signals. + +Threading +~~~~~~~~~ + +On Linux, QEMU can emulate the ``clone`` syscall and create a real +host thread (with a separate virtual CPU) for each emulated thread. +Note that not all targets currently emulate atomic operations +correctly. x86 and Arm use a global lock in order to preserve their +semantics. QEMU was conceived so that ultimately it can emulate itself. Although it is not very useful, it is an important test to show the power of the From bd0eb9b0cdb69ce8eafa85258a564596eeb165b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:07 +0100 Subject: [PATCH 0132/1794] docs/user: slightly reword section on system calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expand the description slightly and quote ioctl(). I did ponder mentioning something about why DRM ioctls are often missing but I see we have the I915 ones so I guess its just no one has done them. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-5-alex.bennee@linaro.org> --- docs/user/main.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/user/main.rst b/docs/user/main.rst index b8ff203c212fc..05de904225c7d 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -20,10 +20,14 @@ QEMU user space emulation has the following notable features: System call translation ~~~~~~~~~~~~~~~~~~~~~~~ -QEMU includes a generic system call translator. This means that the -parameters of the system calls can be converted to fix endianness -and 32/64-bit mismatches between hosts and targets. IOCTLs can be -converted too. +System calls are the principle interface between user-space and the +kernel. Generally the same system calls exist on all versions of the +kernel so QEMU includes a generic system call translator. The +translator takes care of adjusting endianess, 32/64 bit parameter size +and then calling the equivalent host system call. + +QEMU can also adjust device specific ``ioctl()`` calls in a similar +fashion. POSIX signal handling ~~~~~~~~~~~~~~~~~~~~~ From f1f25eed03308f9f85770e0b6b911b6caf83c268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:08 +0100 Subject: [PATCH 0133/1794] docs/user: expand section on threading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Potentially too many weasel words when describing atomic and memory order issues. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-6-alex.bennee@linaro.org> --- docs/user/main.rst | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/user/main.rst b/docs/user/main.rst index 05de904225c7d..347bdfabf8cf6 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -46,9 +46,15 @@ Threading On Linux, QEMU can emulate the ``clone`` syscall and create a real host thread (with a separate virtual CPU) for each emulated thread. -Note that not all targets currently emulate atomic operations -correctly. x86 and Arm use a global lock in order to preserve their -semantics. +However as QEMU relies on the system libc to call ``clone`` on its +behalf we limit the flags accepted to those it uses. Specifically this +means flags affecting namespaces (e.g. container runtimes) are not +supported. QEMU user-mode processes can still be run inside containers +though. + +While QEMU does its best to emulate atomic operations properly +differences between the host and guest memory models can cause issues +for software that makes assumptions about the memory model. QEMU was conceived so that ultimately it can emulate itself. Although it is not very useful, it is an important test to show the power of the From ebbc04adbb079066f8d180b8744c1c01c6de23f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:09 +0100 Subject: [PATCH 0134/1794] tests/functional: add hypervisor test for aarch64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a simple test case that runs an image with kvmtool and kvm-unit-tests which can validate virtualisation works. This is useful for exercising TCG but can also be applied to any nested virt setup which is why it doesn't specify an accelerator. Tested-by: Philippe Mathieu-Daudé Tested-by: Manos Pitsidianakis Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-7-alex.bennee@linaro.org> --- tests/functional/meson.build | 1 + tests/functional/test_aarch64_kvm.py | 71 ++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100755 tests/functional/test_aarch64_kvm.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 8bebcd4d94ec3..ecf965adc6c45 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -89,6 +89,7 @@ tests_aarch64_system_thorough = [ 'aarch64_device_passthrough', 'aarch64_hotplug_pci', 'aarch64_imx8mp_evk', + 'aarch64_kvm', 'aarch64_raspi3', 'aarch64_raspi4', 'aarch64_replay', diff --git a/tests/functional/test_aarch64_kvm.py b/tests/functional/test_aarch64_kvm.py new file mode 100755 index 0000000000000..9fb9286139f44 --- /dev/null +++ b/tests/functional/test_aarch64_kvm.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# +# Functional test that runs subsets of kvm-unit-tests on Aarch64. +# These can run on TCG and any accelerator supporting nested +# virtualisation. +# +# Copyright (c) 2025 Linaro +# +# Author: +# Alex Bennée +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test import exec_command_and_wait_for_pattern as ec_and_wait +from qemu_test.linuxkernel import LinuxKernelTest + + +class Aarch64VirtKVMTests(LinuxKernelTest): + + ASSET_KVM_TEST_KERNEL = Asset( + 'https://fileserver.linaro.org/s/HmjaxXXYHYSqbes/' + 'download?path=%2F&files=' + 'image-with-kvm-tool-and-unit-tests.gz', + '34de4aaea90db5da42729e7d28b77f392c37a2f4da859f889a5234aaf0970696') + + # make it easier to detect successful return to shell + PS1 = 'RES=[$?] # ' + OK_CMD = 'RES=[0] # ' + + # base of tests + KUT_BASE = "/usr/share/kvm-unit-tests/" + + def _launch_guest(self, kvm_mode="nvhe"): + + self.set_machine('virt') + kernel_path = self.ASSET_KVM_TEST_KERNEL.fetch() + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + f"console=ttyAMA0 kvm-arm.mode={kvm_mode}") + + self.vm.add_args("-cpu", "cortex-a72") + self.vm.add_args("-machine", "virt,gic-version=3,virtualization=on", + '-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.add_args("-smp", "2", "-m", "320") + + self.vm.launch() + + self.wait_for_console_pattern('buildroot login:') + ec_and_wait(self, 'root', '#') + ec_and_wait(self, f"export PS1='{self.PS1}'", self.OK_CMD) + + # this is just a smoketest, we don't run all the tests in the image + def _smoketest_kvm(self): + ec_and_wait(self, f"{self.KUT_BASE}/selftest-setup", self.OK_CMD) + ec_and_wait(self, f"{self.KUT_BASE}/selftest-smp", self.OK_CMD) + ec_and_wait(self, f"{self.KUT_BASE}/selftest-vectors-kernel", self.OK_CMD) + ec_and_wait(self, f"{self.KUT_BASE}/selftest-vectors-user", self.OK_CMD) + + def test_aarch64_nvhe_selftest(self): + self._launch_guest("nvhe") + self._smoketest_kvm() + + def test_aarch64_vhe_selftest(self): + self._launch_guest("vhe") + self._smoketest_kvm() + +if __name__ == '__main__': + LinuxKernelTest.main() From 78029e9283f69140d377f589b76e369971dbe0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:10 +0100 Subject: [PATCH 0135/1794] tests/tcg: skip libsyscall.so on softmmu tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It isn't testing anything and just expanding the runtime of testing. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-8-alex.bennee@linaro.org> --- tests/tcg/Makefile.target | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index af68f11664fb0..3d96182a7b987 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -155,6 +155,12 @@ VPATH+=$(PLUGIN_LIB) # For example, libpatch.so only needs to run against the arch-specific patch # target test, so we explicitly run it in the arch-specific Makefile. DISABLE_PLUGINS=libpatch.so + +# Likewise don't bother with the syscall plugin for softmmu +ifneq ($(filter %-softmmu, $(TARGET)),) +DISABLE_PLUGINS += libsyscall.so +endif + PLUGINS=$(filter-out $(DISABLE_PLUGINS), \ $(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c)))) From a80e2c26f1cbac07d923415fecbf274ce4fa2d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:11 +0100 Subject: [PATCH 0136/1794] tests/tcg: remove ADDITIONAL_PLUGINS_TESTS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We never actually used this is the end. Remove it to enable re-factoring. Fixes: 7cefff22d54 (tests/tcg: add mechanism to run specific tests with plugins) Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-9-alex.bennee@linaro.org> --- tests/tcg/Makefile.target | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index 3d96182a7b987..97ebe8f9bc9e8 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -169,11 +169,10 @@ PLUGINS=$(filter-out $(DISABLE_PLUGINS), \ # only expand MULTIARCH_TESTS which are common on most of our targets # to avoid an exponential explosion as new tests are added. We also # add some special helpers the run-plugin- rules can use below. -# In more, extra tests can be added using ADDITIONAL_PLUGINS_TESTS variable. ifneq ($(MULTIARCH_TESTS),) $(foreach p,$(PLUGINS), \ - $(foreach t,$(MULTIARCH_TESTS) $(ADDITIONAL_PLUGINS_TESTS),\ + $(foreach t,$(MULTIARCH_TESTS),\ $(eval run-plugin-$(t)-with-$(p): $t $p) \ $(eval RUN_TESTS+=run-plugin-$(t)-with-$(p)))) endif # MULTIARCH_TESTS From ab8bf8f6e42e321dfb313cfdf95200135e9f7f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:12 +0100 Subject: [PATCH 0137/1794] tests/tcg: don't include multiarch tests if not supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are about to change the way the plugin runs are done and having this included by default will complicate things. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-10-alex.bennee@linaro.org> --- tests/tcg/Makefile.target | 6 ++++++ tests/tcg/multiarch/system/Makefile.softmmu-target | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index 97ebe8f9bc9e8..a12b15637ea5e 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -127,8 +127,14 @@ else # build options for bare programs are usually pretty different. They # are expected to provide their own build recipes. EXTRA_CFLAGS += -ffreestanding -fno-stack-protector + +# We skip the multiarch tests if the target hasn't provided a boot.S +MULTIARCH_SOFTMMU_TARGETS = i386 alpha aarch64 arm loongarch64 s390x x86_64 + +ifneq ($(filter $(TARGET_NAME),$(MULTIARCH_SOFTMMU_TARGETS)),) -include $(SRC_PATH)/tests/tcg/minilib/Makefile.target -include $(SRC_PATH)/tests/tcg/multiarch/system/Makefile.softmmu-target +endif -include $(SRC_PATH)/tests/tcg/$(TARGET_NAME)/Makefile.softmmu-target endif diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target index 07be001102bd5..5acf270081238 100644 --- a/tests/tcg/multiarch/system/Makefile.softmmu-target +++ b/tests/tcg/multiarch/system/Makefile.softmmu-target @@ -6,6 +6,11 @@ # architecture to add to the test dependencies and deal with the # complications of building. # +# To support the multiarch guests the target arch needs to provide a +# boot.S that jumps to main and provides a __sys_outc functions. +# Remember to update MULTIARCH_SOFTMMU_TARGETS in the tcg test +# Makefile.target when this is done. +# MULTIARCH_SRC=$(SRC_PATH)/tests/tcg/multiarch MULTIARCH_SYSTEM_SRC=$(MULTIARCH_SRC)/system From d0aa5df7752deb4bbce34fcc625ce59fe36862be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:13 +0100 Subject: [PATCH 0138/1794] configure: expose PYTHON to test/tcg/config-host.mak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will be useful for making $shell calls to something more flexible than the shell builtins. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-11-alex.bennee@linaro.org> --- configure | 1 + 1 file changed, 1 insertion(+) diff --git a/configure b/configure index 95f67c1a8272e..825057ebf1551 100755 --- a/configure +++ b/configure @@ -1800,6 +1800,7 @@ echo "SRC_PATH=$source_path" >> tests/tcg/$config_host_mak if test "$plugins" = "yes" ; then echo "CONFIG_PLUGIN=y" >> tests/tcg/$config_host_mak fi +echo "PYTHON=$python" >> tests/tcg/$config_host_mak tcg_tests_targets= for target in $target_list; do From 25aaf0cb7f13aa9e537fb6202099f142d9ffc58a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:14 +0100 Subject: [PATCH 0139/1794] tests/tcg: reduce the number of plugin tests combinations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As our set of multiarch tests has grown the practice of running every plugin with every test is becoming unsustainable. If we switch to ensuring every test gets run with at least one plugin we can speed things up. Some plugins do need to be run with specific tests (for example the memory instrumentation test). We can handle this by manually adding them to EXTRA_RUNS. We also need to wrap rules in a CONFIG_PLUGIN test so we don't enable the runs when plugins are not enabled. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-12-alex.bennee@linaro.org> --- tests/tcg/Makefile.target | 23 ++++++++++++++----- tests/tcg/multiarch/Makefile.target | 8 +++++-- .../multiarch/system/Makefile.softmmu-target | 11 +++++---- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index a12b15637ea5e..18afd5be19407 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -173,14 +173,25 @@ PLUGINS=$(filter-out $(DISABLE_PLUGINS), \ # We need to ensure expand the run-plugin-TEST-with-PLUGIN # pre-requistes manually here as we can't use stems to handle it. We # only expand MULTIARCH_TESTS which are common on most of our targets -# to avoid an exponential explosion as new tests are added. We also -# add some special helpers the run-plugin- rules can use below. +# and rotate the plugins so we don't grow too out of control as new +# tests are added. Plugins that need to run with a specific test +# should ensure they add their combination to EXTRA_RUNS. ifneq ($(MULTIARCH_TESTS),) -$(foreach p,$(PLUGINS), \ - $(foreach t,$(MULTIARCH_TESTS),\ - $(eval run-plugin-$(t)-with-$(p): $t $p) \ - $(eval RUN_TESTS+=run-plugin-$(t)-with-$(p)))) + +NUM_PLUGINS := $(words $(PLUGINS)) +NUM_TESTS := $(words $(MULTIARCH_TESTS)) + +define mod_plus_one + $(shell $(PYTHON) -c "print( ($(1) % $(2)) + 1 )") +endef + +$(foreach _idx, $(shell seq 1 $(NUM_TESTS)), \ + $(eval _test := $(word $(_idx), $(MULTIARCH_TESTS))) \ + $(eval _plugin := $(word $(call mod_plus_one, $(_idx), $(NUM_PLUGINS)), $(PLUGINS))) \ + $(eval run-plugin-$(_test)-with-$(_plugin): $(_test) $(_plugin)) \ + $(eval RUN_TESTS+=run-plugin-$(_test)-with-$(_plugin))) + endif # MULTIARCH_TESTS endif # CONFIG_PLUGIN diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index bfdf7197a7b6f..38345ff88052c 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -189,6 +189,10 @@ run-plugin-semiconsole-with-%: TESTS += semihosting semiconsole endif +test-plugin-mem-access: CFLAGS+=-pthread -O0 +test-plugin-mem-access: LDFLAGS+=-pthread -O0 + +ifeq ($(CONFIG_PLUGIN),y) # Test plugin memory access instrumentation run-plugin-test-plugin-mem-access-with-libmem.so: \ PLUGIN_ARGS=$(COMMA)print-accesses=true @@ -197,8 +201,8 @@ run-plugin-test-plugin-mem-access-with-libmem.so: \ $(SRC_PATH)/tests/tcg/multiarch/check-plugin-output.sh \ $(QEMU) $< -test-plugin-mem-access: CFLAGS+=-pthread -O0 -test-plugin-mem-access: LDFLAGS+=-pthread -O0 +EXTRA_RUNS += run-plugin-test-plugin-mem-access-with-libmem.so +endif # Update TESTS TESTS += $(MULTIARCH_TESTS) diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target index 5acf270081238..4171b4e6aa0ce 100644 --- a/tests/tcg/multiarch/system/Makefile.softmmu-target +++ b/tests/tcg/multiarch/system/Makefile.softmmu-target @@ -71,8 +71,11 @@ endif MULTIARCH_RUNS += run-gdbstub-memory run-gdbstub-interrupt \ run-gdbstub-untimely-packet run-gdbstub-registers +ifeq ($(CONFIG_PLUGIN),y) # Test plugin memory access instrumentation -run-plugin-memory-with-libmem.so: \ - PLUGIN_ARGS=$(COMMA)region-summary=true -run-plugin-memory-with-libmem.so: \ - CHECK_PLUGIN_OUTPUT_COMMAND=$(MULTIARCH_SYSTEM_SRC)/validate-memory-counts.py $@.out +run-plugin-memory-with-libmem.so: memory libmem.so +run-plugin-memory-with-libmem.so: PLUGIN_ARGS=$(COMMA)region-summary=true +run-plugin-memory-with-libmem.so: CHECK_PLUGIN_OUTPUT_COMMAND=$(MULTIARCH_SYSTEM_SRC)/validate-memory-counts.py $@.out + +EXTRA_RUNS += run-plugin-memory-with-libmem.so +endif From 408c8629105f32aa1d02d3004998ea453f69809b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:15 +0100 Subject: [PATCH 0140/1794] tests/docker: add --arch-only to qemu deps for all-test-cross MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we want to build this container on non-x86 systems we might not have all the cross-compilers needed for the ROM blobs we don't actually build. Use --arch-only to avoid stalling on these missing bits. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-13-alex.bennee@linaro.org> --- tests/docker/dockerfiles/debian-all-test-cross.docker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker index 8ab244e018ab8..5aa43749ebe20 100644 --- a/tests/docker/dockerfiles/debian-all-test-cross.docker +++ b/tests/docker/dockerfiles/debian-all-test-cross.docker @@ -15,7 +15,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ apt-get install -y eatmydata && \ eatmydata apt-get dist-upgrade -y && \ - apt build-dep -yy qemu + apt build-dep -yy --arch-only qemu # Add extra build tools and as many cross compilers as we can for testing RUN DEBIAN_FRONTEND=noninteractive eatmydata \ From 6da616bb17004f9332b2798353ebef88cac61cc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:16 +0100 Subject: [PATCH 0141/1794] tests/docker: handle host-arch selection for all-test-cross MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building on non-x86 we get a bunch but not all of the compilers. Handle this in the Dockerfile by probing the arch and expanding the list available. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-14-alex.bennee@linaro.org> --- .../dockerfiles/debian-all-test-cross.docker | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/tests/docker/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker index 5aa43749ebe20..ef69bbc8a518a 100644 --- a/tests/docker/dockerfiles/debian-all-test-cross.docker +++ b/tests/docker/dockerfiles/debian-all-test-cross.docker @@ -23,7 +23,9 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ bison \ ccache \ clang \ + dpkg-dev \ flex \ + gcc \ git \ libclang-rt-dev \ ninja-build \ @@ -33,16 +35,11 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ python3-venv \ python3-wheel -RUN DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - gcc-aarch64-linux-gnu \ +# All the generally available compilers +ENV AVAILABLE_COMPILERS gcc-aarch64-linux-gnu \ libc6-dev-arm64-cross \ gcc-arm-linux-gnueabihf \ libc6-dev-armhf-cross \ - gcc-hppa-linux-gnu \ - libc6-dev-hppa-cross \ - gcc-m68k-linux-gnu \ - libc6-dev-m68k-cross \ gcc-mips-linux-gnu \ libc6-dev-mips-cross \ gcc-mips64-linux-gnuabi64 \ @@ -51,18 +48,25 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ libc6-dev-mips64el-cross \ gcc-mipsel-linux-gnu \ libc6-dev-mipsel-cross \ - gcc-powerpc-linux-gnu \ - libc6-dev-powerpc-cross \ - gcc-powerpc64-linux-gnu \ - libc6-dev-ppc64-cross \ gcc-powerpc64le-linux-gnu \ libc6-dev-ppc64el-cross \ gcc-riscv64-linux-gnu \ libc6-dev-riscv64-cross \ gcc-s390x-linux-gnu \ - libc6-dev-s390x-cross \ - gcc-sparc64-linux-gnu \ - libc6-dev-sparc64-cross && \ + libc6-dev-s390x-cross + +RUN if dpkg-architecture -e amd64; then \ + export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-hppa-linux-gnu libc6-dev-hppa-cross"; \ + export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-m68k-linux-gnu libc6-dev-m68k-cross"; \ + export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-powerpc-linux-gnu libc6-dev-powerpc-cross"; \ + export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-powerpc64-linux-gnu libc6-dev-ppc64-cross"; \ + export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-sparc64-linux-gnu libc6-dev-sparc64-cross"; \ + fi && \ + echo "compilers: ${AVAILABLE_COMPILERS}" + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ + ${AVAILABLE_COMPILERS} && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt From cac08383f06dc378afb23913d65a95442bbd5516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:17 +0100 Subject: [PATCH 0142/1794] tests/functional: expose sys.argv to unittest.main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this we can call the supported we can take advantage of the argument the module supports: env PYTHONPATH=/home/alex/lsrc/qemu.git/python:/home/alex/lsrc/qemu.git/tests/functional ./pyvenv/bin/python /home/alex/lsrc/qemu.git/tests/functional/test_aarch64_kvm.py --help usage: test_aarch64_kvm.py [-h] [-v] [-q] [--locals] [--durations N] [-f] [-c] [-b] [-k TESTNAMEPATTERNS] [tests ...] positional arguments: tests a list of any number of test modules, classes and test methods. options: -h, --help show this help message and exit -v, --verbose Verbose output -q, --quiet Quiet output --locals Show local variables in tracebacks --durations N Show the N slowest test cases (N=0 for all) -f, --failfast Stop on first fail or error -c, --catch Catch Ctrl-C and display results so far -b, --buffer Buffer stdout and stderr during tests -k TESTNAMEPATTERNS Only run tests which match the given substring Examples: test_aarch64_kvm.py test_module - run tests from test_module test_aarch64_kvm.py module.TestClass - run tests from module.TestClass test_aarch64_kvm.py module.Class.test_method - run specified test method test_aarch64_kvm.py path/to/test_file.py - run tests from test_file.py usage: test_aarch64_kvm.py discover [-h] [-v] [-q] [--locals] [--durations N] [-f] [-c] [-b] [-k TESTNAMEPATTERNS] [-s START] [-p PATTERN] [-t TOP] options: -h, --help show this help message and exit -v, --verbose Verbose output -q, --quiet Quiet output --locals Show local variables in tracebacks --durations N Show the N slowest test cases (N=0 for all) -f, --failfast Stop on first fail or error -c, --catch Catch Ctrl-C and display results so far -b, --buffer Buffer stdout and stderr during tests -k TESTNAMEPATTERNS Only run tests which match the given substring -s, --start-directory START Directory to start discovery ('.' default) -p, --pattern PATTERN Pattern to match tests ('test*.py' default) -t, --top-level-directory TOP Top level directory of project (defaults to start directory) For test discovery all test modules must be importable from the top level directory of the project. Suggested-by: Daniel P. Berrangé Reviewed-by: Daniel P. Berrangé Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-15-alex.bennee@linaro.org> --- tests/functional/qemu_test/testcase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 2a78e735f1604..5caf7b13fe36b 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -249,7 +249,7 @@ def main(): tr = pycotap.TAPTestRunner(message_log = pycotap.LogMode.LogToError, test_output_log = pycotap.LogMode.LogToError) res = unittest.main(module = None, testRunner = tr, exit = False, - argv=["__dummy__", path]) + argv=[sys.argv[0], path] + sys.argv[1:]) for (test, message) in res.result.errors + res.result.failures: if hasattr(test, "log_filename"): From bb743978f0aaf6aa7f7c796bae05bb9deb83b3f9 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 17 Jul 2025 13:52:43 +0200 Subject: [PATCH 0143/1794] qga: Fix guest-network-get-route return value documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tagged sections are only recognized at the beginning of a paragraph. guest-network-get-route's Returns: isn't, and therefore gets rendered as ordinary text within its paragraph: Retrieve information about route of network. Returns: List of route info of guest. Since there is no (recognized) Returns: section, the doc generator adds Return: [GuestNetworkRoute] Note: only since recent commit 636c96cd77d (qapi: Fix undocumented return values by generating something). Insert the required blank line so that Returns: is recognized. Result: Retrieve information about route of network. Return: [GuestNetworkRoute] -- List of route info of guest. Fixes: commit 8e326d36dd16 (qga/linux: Add new api 'guest-network-get-route') Signed-off-by: Markus Armbruster Reviewed-by: Daniel P. Berrangé Message-ID: <20250717115246.3830007-2-armbru@redhat.com> Reviewed-by: John Snow --- qga/qapi-schema.json | 1 + 1 file changed, 1 insertion(+) diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 6d770f7b8e0a5..a569a14b55561 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1966,6 +1966,7 @@ # @guest-network-get-route: # # Retrieve information about route of network. +# # Returns: List of route info of guest. # # Since: 9.1 From d27340ff8a1036f9d174e9389dd3510083ad276d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 17 Jul 2025 13:52:44 +0200 Subject: [PATCH 0144/1794] qga: Remove trivial "Returns:" sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QAPI doc generator recently started to auto-generate return documentation when there is no "Returns:" section (commit 636c96cd77d "qapi: Fix undocumented return values by generating something"). Remove "Returns:" sections where the auto-generated text is obviously no worse. For instance, guest-info's documentation changes from Return: GuestAgentInfo -- GuestAgentInfo to Return: GuestAgentInfo The auto-generated returns all are in the exact same spot. We did this for qapi/ in commit 0462da9d6b1 (qapi: remove trivial "Returns:" sections). Signed-off-by: Markus Armbruster Reviewed-by: Daniel P. Berrangé Message-ID: <20250717115246.3830007-3-armbru@redhat.com> Reviewed-by: John Snow --- qga/qapi-schema.json | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index a569a14b55561..a9cc9150dc09f 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -202,8 +202,6 @@ # # Get some information about the guest agent. # -# Returns: @GuestAgentInfo -# # Since: 0.15.0 ## { 'command': 'guest-info', @@ -285,8 +283,6 @@ # @count: maximum number of bytes to read (default is 4KB, maximum is # 48MB) # -# Returns: @GuestFileRead -# # Since: 0.15.0 ## { 'command': 'guest-file-read', @@ -320,8 +316,6 @@ # @count: bytes to write (actual bytes, after base64-decode), default # is all content in buf-b64 buffer after base64 decoding # -# Returns: @GuestFileWrite -# # Since: 0.15.0 ## { 'command': 'guest-file-write', @@ -387,8 +381,6 @@ # # @whence: Symbolic or numeric code for interpreting offset # -# Returns: @GuestFileSeek -# # Since: 0.15.0 ## { 'command': 'guest-file-seek', @@ -428,9 +420,6 @@ # # Get guest fsfreeze state. # -# Returns: GuestFsfreezeStatus ("thawed", "frozen", etc., as defined -# below) -# # .. note:: This may fail to properly report the current state as a # result of some other guest processes having issued an fs # freeze/thaw. @@ -749,8 +738,6 @@ # # Get list of guest IP addresses, MAC addresses and netmasks. # -# Returns: List of GuestNetworkInterface -# # Since: 1.1 ## { 'command': 'guest-network-get-interfaces', @@ -1251,8 +1238,6 @@ # # Get information relating to guest memory blocks. # -# Returns: @GuestMemoryBlockInfo -# # Since: 2.3 ## { 'command': 'guest-get-memory-block-info', @@ -1298,8 +1283,6 @@ # # @pid: pid returned from guest-exec # -# Returns: GuestExecStatus -# # Since: 2.5 ## { 'command': 'guest-exec-status', @@ -1458,8 +1441,6 @@ # # Retrieves the timezone information from the guest. # -# Returns: A GuestTimezone dictionary. -# # Since: 2.10 ## { 'command': 'guest-get-timezone', @@ -1533,8 +1514,6 @@ # # Retrieve guest operating system information # -# Returns: @GuestOSInfo -# # Since: 2.10 ## { 'command': 'guest-get-osinfo', @@ -1604,8 +1583,6 @@ # # Retrieve information about device drivers in Windows guest # -# Returns: @GuestDeviceInfo -# # Since: 5.2 ## { 'command': 'guest-get-devices', @@ -1633,8 +1610,6 @@ # # @username: the user account to add the authorized keys # -# Returns: @GuestAuthorizedKeys -# # Since: 5.2 ## { 'command': 'guest-ssh-get-authorized-keys', From ef7e21964d54db7b7267c30578c3c6ec0f16ca08 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 17 Jul 2025 13:52:45 +0200 Subject: [PATCH 0145/1794] qga: Rephrase return docs to avoid type name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Returns: " is rendered like Return: Mentioning the type in the description again is commonly redundant. There is just one such description. Rephrase it not to mention the type. We did this for qapi/ in commit f7296f8de5c (qapi: rephrase return docs to avoid type name). Signed-off-by: Markus Armbruster Reviewed-by: Daniel P. Berrangé Message-ID: <20250717115246.3830007-4-armbru@redhat.com> Reviewed-by: John Snow --- qga/qapi-schema.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index a9cc9150dc09f..6c26ace3c980d 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -534,8 +534,7 @@ # discarded. The default value is zero, meaning "discard every # free block". # -# Returns: A @GuestFilesystemTrimResponse which contains the status of -# all trimmed paths. (since 2.4) +# Returns: status of all trimmed paths. (since 2.4) # # Since: 1.2 ## From 62e1fa22f5734d1325e3b75cdc59f02c16339330 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 17 Jul 2025 13:52:46 +0200 Subject: [PATCH 0146/1794] qga: Add cross-references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enclose command and type names in `backquotes`, so they become links in generated HTML. We did this for qapi/ in merge commit 504632dcc631. Signed-off-by: Markus Armbruster Message-ID: <20250717115246.3830007-5-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: John Snow --- qga/qapi-schema.json | 80 ++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 6c26ace3c980d..8162d888bb3e5 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -96,11 +96,11 @@ # In cases where a partial stale response was previously received by # the client, this cannot always be done reliably. One particular # scenario being if qemu-ga responses are fed character-by-character -# into a JSON parser. In these situations, using guest-sync-delimited +# into a JSON parser. In these situations, using `guest-sync-delimited` # may be optimal. # # For clients that fetch responses line by line and convert them to -# JSON objects, guest-sync should be sufficient, but note that in +# JSON objects, `guest-sync` should be sufficient, but note that in # cases where the channel is dirty some attempts at parsing the # response may result in a parser error. # @@ -217,7 +217,7 @@ # # This command does NOT return a response on success. Success # condition is indicated by the VM exiting with a zero exit status or, -# when running with --no-shutdown, by issuing the query-status QMP +# when running with --no-shutdown, by issuing the `query-status` QMP # command to confirm the VM status is "shutdown". # # Since: 0.15.0 @@ -247,7 +247,7 @@ # # Close an open file in the guest # -# @handle: filehandle returned by guest-file-open +# @handle: filehandle returned by `guest-file-open` # # Since: 0.15.0 ## @@ -278,7 +278,7 @@ # As this command is just for limited, ad-hoc debugging, such as log # file access, the number of bytes to read is limited to 48 MB. # -# @handle: filehandle returned by guest-file-open +# @handle: filehandle returned by `guest-file-open` # # @count: maximum number of bytes to read (default is 4KB, maximum is # 48MB) @@ -309,7 +309,7 @@ # # Write to an open file in the guest. # -# @handle: filehandle returned by guest-file-open +# @handle: filehandle returned by `guest-file-open` # # @buf-b64: base64-encoded string representing data to be written # @@ -340,7 +340,7 @@ ## # @QGASeek: # -# Symbolic names for use in @guest-file-seek +# Symbolic names for use in `guest-file-seek` # # @set: Set to the specified offset (same effect as 'whence':0) # @@ -355,7 +355,7 @@ ## # @GuestFileWhence: # -# Controls the meaning of offset to @guest-file-seek. +# Controls the meaning of offset to `guest-file-seek`. # # @value: Integral value (0 for set, 1 for cur, 2 for end), available # for historical reasons, and might differ from the host's or @@ -375,7 +375,7 @@ # current file position afterward. Also encapsulates ftell()'s # functionality, with offset=0 and whence=1. # -# @handle: filehandle returned by guest-file-open +# @handle: filehandle returned by `guest-file-open` # # @offset: bytes to skip over in the file stream # @@ -393,7 +393,7 @@ # # Write file changes buffered in userspace to disk/kernel buffers # -# @handle: filehandle returned by guest-file-open +# @handle: filehandle returned by `guest-file-open` # # Since: 0.15.0 ## @@ -434,12 +434,12 @@ # @guest-fsfreeze-freeze: # # Sync and freeze all freezable, local guest filesystems. If this -# command succeeded, you may call @guest-fsfreeze-thaw later to +# command succeeded, you may call `guest-fsfreeze-thaw` later to # unfreeze. # # On error, all filesystems will be thawed. If no filesystems are -# frozen as a result of this call, then @guest-fsfreeze-status will -# remain "thawed" and calling @guest-fsfreeze-thaw is not necessary. +# frozen as a result of this call, then `guest-fsfreeze-status` will +# remain "thawed" and calling `guest-fsfreeze-thaw` is not necessary. # # Returns: Number of file systems currently frozen. # @@ -457,7 +457,7 @@ # @guest-fsfreeze-freeze-list: # # Sync and freeze specified guest filesystems. See also -# @guest-fsfreeze-freeze. +# `guest-fsfreeze-freeze`. # # On error, all filesystems will be thawed. # @@ -482,7 +482,7 @@ # Returns: Number of file systems thawed by this call # # .. note:: If the return value does not match the previous call to -# guest-fsfreeze-freeze, this likely means some freezable filesystems +# `guest-fsfreeze-freeze`, this likely means some freezable filesystems # were unfrozen before this call, and that the filesystem state may # have changed before issuing this command. # @@ -513,7 +513,7 @@ ## # @GuestFilesystemTrimResponse: # -# @paths: list of @GuestFilesystemTrimResult per path that was trimmed +# @paths: list of `GuestFilesystemTrimResult` per path that was trimmed # # Since: 2.4 ## @@ -557,7 +557,7 @@ # # This command does NOT return a response on success. There is a high # chance the command succeeded if the VM exits with a zero exit status -# or, when running with --no-shutdown, by issuing the query-status QMP +# or, when running with --no-shutdown, by issuing the `query-status` QMP # command to to confirm the VM status is "shutdown". However, the VM # could also exit (or set its status to "shutdown") due to other # reasons. @@ -565,7 +565,7 @@ # Errors: # - If suspend to disk is not supported, Unsupported # -# .. note:: It's strongly recommended to issue the guest-sync command +# .. note:: It's strongly recommended to issue the `guest-sync` command # before sending commands when the guest resumes. # # Since: 1.1 @@ -585,8 +585,8 @@ # - pm-utils (via pm-hibernate) # - manual write into sysfs # -# IMPORTANT: guest-suspend-ram requires working wakeup support in -# QEMU. You should check QMP command query-current-machine returns +# IMPORTANT: `guest-suspend-ram` requires working wakeup support in +# QEMU. You should check QMP command `query-current-machine` returns # wakeup-suspend-support: true before issuing this command. Failure # in doing so can result in a suspended guest that QEMU will not be # able to awaken, forcing the user to power cycle the guest to bring @@ -595,14 +595,14 @@ # This command does NOT return a response on success. There are two # options to check for success: # -# 1. Wait for the SUSPEND QMP event from QEMU -# 2. Issue the query-status QMP command to confirm the VM status is +# 1. Wait for the `SUSPEND` QMP event from QEMU +# 2. Issue the `query-status` QMP command to confirm the VM status is # "suspended" # # Errors: # - If suspend to ram is not supported, Unsupported # -# .. note:: It's strongly recommended to issue the guest-sync command +# .. note:: It's strongly recommended to issue the `guest-sync` command # before sending commands when the guest resumes. # # Since: 1.1 @@ -621,8 +621,8 @@ # - systemd hybrid-sleep # - pm-utils (via pm-suspend-hybrid) # -# IMPORTANT: guest-suspend-hybrid requires working wakeup support in -# QEMU. You should check QMP command query-current-machine returns +# IMPORTANT: `guest-suspend-hybrid` requires working wakeup support in +# QEMU. You should check QMP command `query-current-machine` returns # wakeup-suspend-support: true before issuing this command. Failure # in doing so can result in a suspended guest that QEMU will not be # able to awaken, forcing the user to power cycle the guest to bring @@ -631,14 +631,14 @@ # This command does NOT return a response on success. There are two # options to check for success: # -# 1. Wait for the SUSPEND QMP event from QEMU -# 2. Issue the query-status QMP command to confirm the VM status is +# 1. Wait for the `SUSPEND` QMP event from QEMU +# 2. Issue the `query-status` QMP command to confirm the VM status is # "suspended" # # Errors: # - If hybrid suspend is not supported, Unsupported # -# .. note:: It's strongly recommended to issue the guest-sync command +# .. note:: It's strongly recommended to issue the `guest-sync` command # before sending commands when the guest resumes. # # Since: 1.1 @@ -793,7 +793,7 @@ # There's no restriction on list length or on repeating the same # @logical-id (with possibly different @online field). Preferably # the input list should describe a modified subset of -# @guest-get-vcpus' return value. +# `guest-get-vcpus`' return value. # # Returns: The length of the initial sublist that has been # successfully processed. The guest agent maximizes this value. @@ -1069,7 +1069,7 @@ # # Returns: The list of filesystems information mounted in the guest. # The returned mountpoints may be specified to -# @guest-fsfreeze-freeze-list. Network filesystems (such as CIFS +# `guest-fsfreeze-freeze-list`. Network filesystems (such as CIFS # and NFS) are not listed. # # Since: 2.2 @@ -1171,7 +1171,7 @@ ## # @GuestMemoryBlockResponse: # -# @phys-index: same with the 'phys-index' member of @GuestMemoryBlock. +# @phys-index: same with the 'phys-index' member of `GuestMemoryBlock`. # # @response: the result of memory block operation. # @@ -1201,11 +1201,11 @@ # guest-supported identifiers. There's no restriction on list # length or on repeating the same @phys-index (with possibly # different @online field). Preferably the input list should -# describe a modified subset of @guest-get-memory-blocks' return +# describe a modified subset of `guest-get-memory-blocks`' return # value. # # Returns: The operation results, it is a list of -# @GuestMemoryBlockResponse, which is corresponding to the input +# `GuestMemoryBlockResponse`, which is corresponding to the input # list. # # Note: it will return an empty list if the @mem-blks list was @@ -1258,7 +1258,7 @@ # # @err-data: base64-encoded stderr of the process. Note: @out-data # and @err-data are present only if 'capture-output' was specified -# for 'guest-exec'. This field will only be populated after the +# for `guest-exec`. This field will only be populated after the # process exits. # # @out-truncated: true if stdout was not fully captured due to size @@ -1277,10 +1277,10 @@ # @guest-exec-status: # # Check status of process associated with PID retrieved via -# guest-exec. Reap the process and associated metadata if it has +# `guest-exec`. Reap the process and associated metadata if it has # exited. # -# @pid: pid returned from guest-exec +# @pid: pid returned from `guest-exec` # # Since: 2.5 ## @@ -1301,7 +1301,7 @@ ## # @GuestExecCaptureOutputMode: # -# An enumeration of guest-exec capture modes. +# An enumeration of `guest-exec` capture modes. # # @none: do not capture any output # @@ -1310,7 +1310,7 @@ # @stderr: only capture stderr # # @separated: capture both stdout and stderr, but separated into -# GuestExecStatus out-data and err-data, respectively +# `GuestExecStatus` out-data and err-data, respectively # # @merged: capture both stdout and stderr, but merge together into # out-data. Not effective on windows guests. @@ -1324,10 +1324,10 @@ ## # @GuestExecCaptureOutput: # -# Controls what guest-exec output gets captures. +# Controls what `guest-exec` output gets captures. # # @flag: captures both stdout and stderr if true. Equivalent to -# GuestExecCaptureOutputMode::all. (since 2.5) +# `GuestExecCaptureOutputMode`::all. (since 2.5) # # @mode: capture mode; preferred interface # From 1ebdd2d92611a573f9fcdf0fc7f31b1efd07ac30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 23 Jul 2025 08:27:14 +0200 Subject: [PATCH 0147/1794] hw/i386: Fix 'use-legacy-x86-rom' property compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 350785d41d8b ("ramfb: Add property to control if load the romfile") introduced the `use-legacy-x86-rom` property for the `vfio-pci-nohotplug` device, allowing control over VGA BIOS ROM loading. However, the property compatibility setting was incorrectly applied to the `vfio-pci` device instead, which causes all `vfio-pci` devices to fail to load. This change fixes the issue by ensuring the property is set on the correct device. Fixes: d5fcf0d960d8 ("hw/i386: Add the ramfb romfile compatibility") Cc: Gerd Hoffmann Cc: Shaoqin Huang Reviewed-by: Zhao Liu Reviewed-by: Thomas Huth Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffmann Link: https://lore.kernel.org/qemu-devel/20250723062714.1245826-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/core/machine.c | 2 +- hw/i386/microvm.c | 2 +- hw/i386/pc_piix.c | 2 +- hw/i386/pc_q35.c | 2 +- hw/vfio/pci.c | 2 -- hw/vfio/types.h | 2 ++ 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index d6b2240fc250d..bd47527479a79 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -41,7 +41,7 @@ GlobalProperty hw_compat_10_0[] = { { "scsi-hd", "dpofua", "off" }, { "vfio-pci", "x-migration-load-config-after-iter", "off" }, { "ramfb", "use-legacy-x86-rom", "true"}, - { "vfio-pci", "use-legacy-x86-rom", "true" }, + { "vfio-pci-nohotplug", "use-legacy-x86-rom", "true" }, }; const size_t hw_compat_10_0_len = G_N_ELEMENTS(hw_compat_10_0); diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index d90b69a162007..94d22a232aca1 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -635,7 +635,7 @@ GlobalProperty microvm_properties[] = { */ { "pcie-root-port", "io-reserve", "0" }, { TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" }, - { TYPE_VFIO_PCI, "use-legacy-x86-rom", "true" }, + { TYPE_VFIO_PCI_NOHOTPLUG, "use-legacy-x86-rom", "true" }, }; static void microvm_class_init(ObjectClass *oc, const void *data) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index ad5caff3a5d25..c03324281bdb3 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -80,7 +80,7 @@ static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; static GlobalProperty pc_piix_compat_defaults[] = { { TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" }, - { TYPE_VFIO_PCI, "use-legacy-x86-rom", "true" }, + { TYPE_VFIO_PCI_NOHOTPLUG, "use-legacy-x86-rom", "true" }, }; static const size_t pc_piix_compat_defaults_len = G_N_ELEMENTS(pc_piix_compat_defaults); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 9b9519fa02d50..b309b2b378db4 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -69,7 +69,7 @@ static GlobalProperty pc_q35_compat_defaults[] = { { TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "39" }, { TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" }, - { TYPE_VFIO_PCI, "use-legacy-x86-rom", "true" }, + { TYPE_VFIO_PCI_NOHOTPLUG, "use-legacy-x86-rom", "true" }, }; static const size_t pc_q35_compat_defaults_len = G_N_ELEMENTS(pc_q35_compat_defaults); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index e72d514a4cb91..0c4606d9cb5e1 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -49,8 +49,6 @@ #include "vfio-migration-internal.h" #include "vfio-helpers.h" -#define TYPE_VFIO_PCI_NOHOTPLUG "vfio-pci-nohotplug" - /* Protected by BQL */ static KVMRouteChange vfio_route_change; diff --git a/hw/vfio/types.h b/hw/vfio/types.h index fa20c29b9fbbf..c19334ff25ae2 100644 --- a/hw/vfio/types.h +++ b/hw/vfio/types.h @@ -18,4 +18,6 @@ #define TYPE_VFIO_PCI "vfio-pci" /* TYPE_VFIO_PCI shares struct VFIOPCIDevice. */ +#define TYPE_VFIO_PCI_NOHOTPLUG "vfio-pci-nohotplug" + #endif /* HW_VFIO_VFIO_TYPES_H */ From 1dc1220fbd30a200aea972fc3aa53d439aff466b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 16 Jul 2025 09:15:54 +0200 Subject: [PATCH 0148/1794] i386: Build SEV only for 64-bit target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent changes broke build on 32-bit host. Since there is no 32-bit support, restrict SEV to 64-bit. Reviewed-by: Xiaoyao Li Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/qemu-devel/20250716071554.377356-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/i386/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 14d23e27b580b..5139d23087771 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -4,7 +4,7 @@ config X86_FW_OVMF config SEV bool select X86_FW_OVMF - depends on KVM + depends on KVM && X86_64 config SGX bool From 9751377c3a9445f597c5d0bf134a79c8cb58e45d Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Mon, 14 Jul 2025 12:21:30 -0700 Subject: [PATCH 0149/1794] vfio: fix sub-page bar after cpr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regions for sub-page BARs are normally mapped here, in response to the guest writing to PCI config space: vfio_pci_write_config() pci_default_write_config() pci_update_mappings() memory_region_add_subregion() vfio_sub_page_bar_update_mapping() ... vfio_dma_map() However, after CPR, the guest does not reconfigure the device and the code path above is not taken. To fix, in vfio_cpr_pci_post_load, call vfio_sub_page_bar_update_mapping for each sub-page BAR with a valid address. Fixes: 7e9f21411302 ("vfio/container: restore DMA vaddr") Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1752520890-223356-1-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr.c | 2 ++ hw/vfio/pci.c | 14 ++++++++++++++ hw/vfio/pci.h | 1 + 3 files changed, 17 insertions(+) diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index af0f12a7adf5a..384b56c4c74d8 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -116,6 +116,8 @@ static int vfio_cpr_pci_post_load(void *opaque, int version_id) PCIDevice *pdev = &vdev->pdev; int nr_vectors; + vfio_sub_page_bar_update_mappings(vdev); + if (msix_enabled(pdev)) { vfio_pci_msix_set_notifiers(vdev); nr_vectors = vdev->msix->entries; diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 0c4606d9cb5e1..f5bc5359eb8bd 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2824,6 +2824,20 @@ static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f) return ret; } +void vfio_sub_page_bar_update_mappings(VFIOPCIDevice *vdev) +{ + PCIDevice *pdev = &vdev->pdev; + int page_size = qemu_real_host_page_size(); + int bar; + + for (bar = 0; bar < PCI_ROM_SLOT; bar++) { + PCIIORegion *r = &pdev->io_regions[bar]; + if (r->addr != PCI_BAR_UNMAPPED && r->size > 0 && r->size < page_size) { + vfio_sub_page_bar_update_mapping(pdev, bar); + } + } +} + static VFIODeviceOps vfio_pci_ops = { .vfio_compute_needs_reset = vfio_pci_compute_needs_reset, .vfio_hot_reset_multi = vfio_pci_hot_reset_multi, diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 248e5c4b16269..bca289035aaeb 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -219,6 +219,7 @@ void vfio_pci_write_config(PCIDevice *pdev, uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size); void vfio_vga_write(void *opaque, hwaddr addr, uint64_t data, unsigned size); +void vfio_sub_page_bar_update_mappings(VFIOPCIDevice *vdev); bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev); bool vfio_config_quirk_setup(VFIOPCIDevice *vdev, Error **errp); void vfio_vga_quirk_setup(VFIOPCIDevice *vdev); From e0b33efe2acc3c6c651bb761394ef597e763e349 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Thu, 24 Jul 2025 00:09:05 +0800 Subject: [PATCH 0150/1794] vfio/igd: Require host VGA decode for legacy mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit a59d06305fff ("vfio/pci: Introduce x-pci-class-code option") allows user to expose non-VGA IGD device as VGA controller to the guest. However, legacy mode requires host VGA range access. Check that GGC.IVD == 0 before enabling legacy mode to ensure IGD is a real VGA device claiming host VGA ranges. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250723160906.44941-2-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- docs/igd-assign.txt | 1 + hw/vfio/igd.c | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/igd-assign.txt b/docs/igd-assign.txt index af4e8391fc258..e54040335ba72 100644 --- a/docs/igd-assign.txt +++ b/docs/igd-assign.txt @@ -48,6 +48,7 @@ Intel document [1] shows how to dump VBIOS to file. For UEFI Option ROM, see QEMU also provides a "Legacy" mode that implicitly enables full functionality on IGD, it is automatically enabled when * IGD generation is 6 to 9 (Sandy Bridge to Comet Lake) +* IGD claims VGA cycles on host (IGD is VGA controller on host) * Machine type is i440fx * IGD is assigned to guest BDF 00:02.0 * ROM BAR or romfile is present diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index e7a9d1ffc1304..5b1ad1a8048b8 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -113,6 +113,7 @@ static int igd_gen(VFIOPCIDevice *vdev) #define IGD_BDSM 0x5c /* Base Data of Stolen Memory */ #define IGD_BDSM_GEN11 0xc0 /* Base Data of Stolen Memory of gen 11 and later */ +#define IGD_GMCH_VGA_DISABLE BIT(1) #define IGD_GMCH_GEN6_GMS_SHIFT 3 /* SNB_GMCH in i915 */ #define IGD_GMCH_GEN6_GMS_MASK 0x1f #define IGD_GMCH_GEN8_GMS_SHIFT 8 /* BDW_GMCH in i915 */ @@ -533,12 +534,14 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) /* * For backward compatibility, enable legacy mode when * - Device geneation is 6 to 9 (including both) + * - IGD claims VGA cycles on host * - Machine type is i440fx (pc_piix) * - IGD device is at guest BDF 00:02.0 * - Not manually disabled by x-igd-legacy-mode=off */ if ((vdev->igd_legacy_mode != ON_OFF_AUTO_OFF) && (gen >= 6 && gen <= 9) && + !(gmch & IGD_GMCH_VGA_DISABLE) && !strcmp(MACHINE_GET_CLASS(qdev_get_machine())->family, "pc_piix") && (&vdev->pdev == pci_find_device(pci_device_root_bus(&vdev->pdev), 0, PCI_DEVFN(0x2, 0)))) { @@ -568,12 +571,10 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) } /* - * If IGD VGA Disable is clear (expected) and VGA is not already - * enabled, try to enable it. Probably shouldn't be using legacy mode - * without VGA, but also no point in us enabling VGA if disabled in - * hardware. + * If VGA is not already enabled, try to enable it. We shouldn't be + * using legacy mode without VGA. */ - if (!(gmch & 0x2) && !vdev->vga && !vfio_populate_vga(vdev, &err)) { + if (!vdev->vga && !vfio_populate_vga(vdev, &err)) { error_setg(&err, "Unable to enable VGA access"); goto error; } From 0db7e4cb62026196f06755c77f943294d9879e5a Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Thu, 24 Jul 2025 00:09:06 +0800 Subject: [PATCH 0151/1794] vfio/igd: Fix VGA regions are not exposed in legacy mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit a59d06305fff ("vfio/pci: Introduce x-pci-class-code option"), pci_register_vga() has been moved ouside of vfio_populate_vga(). As a result, IGD VGA ranges are no longer properly exposed to guest. To fix this, call pci_register_vga() after vfio_populate_vga() legacy mode. A wrapper function vfio_pci_config_register_vga() is introduced to handle it. Fixes: a59d06305fff ("vfio/pci: Introduce x-pci-class-code option") Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250723160906.44941-3-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 10 +++++++--- hw/vfio/pci.c | 13 ++++++++++--- hw/vfio/pci.h | 1 + 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 5b1ad1a8048b8..ee0767b0b89c0 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -574,9 +574,13 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) * If VGA is not already enabled, try to enable it. We shouldn't be * using legacy mode without VGA. */ - if (!vdev->vga && !vfio_populate_vga(vdev, &err)) { - error_setg(&err, "Unable to enable VGA access"); - goto error; + if (!vdev->vga) { + if (vfio_populate_vga(vdev, &err)) { + vfio_pci_config_register_vga(vdev); + } else { + error_setg(&err, "Unable to enable VGA access"); + goto error; + } } /* Enable OpRegion and LPC bridge quirk */ diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index f5bc5359eb8bd..4fa692c1a32bc 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3162,6 +3162,15 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) vdev->req_enabled = false; } +void vfio_pci_config_register_vga(VFIOPCIDevice *vdev) +{ + assert(vdev->vga != NULL); + + pci_register_vga(&vdev->pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem, + &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem, + &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem); +} + bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) { PCIDevice *pdev = &vdev->pdev; @@ -3283,9 +3292,7 @@ bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) vfio_bars_register(vdev); if (vdev->vga && vfio_is_vga(vdev)) { - pci_register_vga(&vdev->pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem, - &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem, - &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem); + vfio_pci_config_register_vga(vdev); } return true; diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index bca289035aaeb..81465a8214a40 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -253,6 +253,7 @@ extern const VMStateDescription vfio_display_vmstate; void vfio_pci_bars_exit(VFIOPCIDevice *vdev); bool vfio_pci_add_capabilities(VFIOPCIDevice *vdev, Error **errp); +void vfio_pci_config_register_vga(VFIOPCIDevice *vdev); bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp); bool vfio_pci_interrupt_setup(VFIOPCIDevice *vdev, Error **errp); void vfio_pci_intx_eoi(VFIODevice *vbasedev); From e895095c78ab877d40df2dd31ee79d85757d963b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 27 Apr 2021 15:33:37 +0200 Subject: [PATCH 0152/1794] target/mips: Only update MVPControl.EVP bit if executed by master VPE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the 'MIPS MT Application-Specific Extension' manual: If the VPE executing the instruction is not a Master VPE, with the MVP bit of the VPEConf0 register set, the EVP bit is unchanged by the instruction. Modify the DVPE/EVPE opcodes to only update the MVPControl.EVP bit if executed on a master VPE. Cc: qemu-stable@nongnu.org Reported-by: Hansni Bu Buglink: https://bugs.launchpad.net/qemu/+bug/1926277 Fixes: f249412c749 ("mips: Add MT halting and waking of VPEs") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Jiaxun Yang Message-ID: <20210427133343.159718-1-f4bug@amsat.org> Signed-off-by: Philippe Mathieu-Daudé --- target/mips/tcg/system/cp0_helper.c | 32 ++++++++++++++++------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/target/mips/tcg/system/cp0_helper.c b/target/mips/tcg/system/cp0_helper.c index 101b1e65fdd3a..b69e70d7fcf74 100644 --- a/target/mips/tcg/system/cp0_helper.c +++ b/target/mips/tcg/system/cp0_helper.c @@ -1562,12 +1562,14 @@ target_ulong helper_dvpe(CPUMIPSState *env) CPUState *other_cs = first_cpu; target_ulong prev = env->mvp->CP0_MVPControl; - CPU_FOREACH(other_cs) { - MIPSCPU *other_cpu = MIPS_CPU(other_cs); - /* Turn off all VPEs except the one executing the dvpe. */ - if (&other_cpu->env != env) { - other_cpu->env.mvp->CP0_MVPControl &= ~(1 << CP0MVPCo_EVP); - mips_vpe_sleep(other_cpu); + if (env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) { + CPU_FOREACH(other_cs) { + MIPSCPU *other_cpu = MIPS_CPU(other_cs); + /* Turn off all VPEs except the one executing the dvpe. */ + if (&other_cpu->env != env) { + other_cpu->env.mvp->CP0_MVPControl &= ~(1 << CP0MVPCo_EVP); + mips_vpe_sleep(other_cpu); + } } } return prev; @@ -1578,15 +1580,17 @@ target_ulong helper_evpe(CPUMIPSState *env) CPUState *other_cs = first_cpu; target_ulong prev = env->mvp->CP0_MVPControl; - CPU_FOREACH(other_cs) { - MIPSCPU *other_cpu = MIPS_CPU(other_cs); + if (env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) { + CPU_FOREACH(other_cs) { + MIPSCPU *other_cpu = MIPS_CPU(other_cs); - if (&other_cpu->env != env - /* If the VPE is WFI, don't disturb its sleep. */ - && !mips_vpe_is_wfi(other_cpu)) { - /* Enable the VPE. */ - other_cpu->env.mvp->CP0_MVPControl |= (1 << CP0MVPCo_EVP); - mips_vpe_wake(other_cpu); /* And wake it up. */ + if (&other_cpu->env != env + /* If the VPE is WFI, don't disturb its sleep. */ + && !mips_vpe_is_wfi(other_cpu)) { + /* Enable the VPE. */ + other_cpu->env.mvp->CP0_MVPControl |= (1 << CP0MVPCo_EVP); + mips_vpe_wake(other_cpu); /* And wake it up. */ + } } } return prev; From 8e8cb3b5722babe7e7b597b3805bf09f24ed6979 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Fri, 21 Feb 2025 16:48:56 +0300 Subject: [PATCH 0153/1794] hw/display/qxl-render: fix qxl_unpack_chunks() chunk size calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case of multiple chunks, code in qxl_unpack_chunks() takes size of the wrong (next in the chain) chunk, instead of using current chunk size. This leads to wrong number of bytes being copied, and to crashes if next chunk size is larger than the current one. Based on the code by Gao Yong. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1628 Tested-by: Thaddeus Hogan Tested-by: Vadim Zeitlin Signed-off-by: Michael Tokarev Reviewed-by: Thomas Huth Message-ID: <20250221134856.478806-1-mjt@tls.msk.ru> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/qxl-render.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index eda6d3de37c3a..c6a9ac1da104f 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -222,6 +222,7 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, uint32_t max_chunks = 32; size_t offset = 0; size_t bytes; + QXLPHYSICAL next_chunk_phys = 0; for (;;) { bytes = MIN(size - offset, chunk->data_size); @@ -230,7 +231,15 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, if (offset == size) { return; } - chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id, + next_chunk_phys = chunk->next_chunk; + /* fist time, only get the next chunk's data size */ + chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id, + sizeof(QXLDataChunk)); + if (!chunk) { + return; + } + /* second time, check data size and get data */ + chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id, sizeof(QXLDataChunk) + chunk->data_size); if (!chunk) { return; From 962316a6a339367e187b4728ef131ec6e635f364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 Jul 2025 10:55:33 +0200 Subject: [PATCH 0154/1794] hw/vfio/vfio-migration: Remove unnecessary 'qemu/typedefs.h' include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "qemu/typedefs.h" is already included by "qemu/osdep.h". Reviewed-by: Cédric Le Goater Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250708085859.7885-3-philmd@linaro.org> --- hw/vfio/vfio-migration-internal.h | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/vfio/vfio-migration-internal.h b/hw/vfio/vfio-migration-internal.h index 54141e27e6b2f..814fbd9ebaef3 100644 --- a/hw/vfio/vfio-migration-internal.h +++ b/hw/vfio/vfio-migration-internal.h @@ -13,7 +13,6 @@ #include #endif -#include "qemu/typedefs.h" #include "qemu/notify.h" /* From b496a392fec656d8e7af81c496efa3d45fd59023 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Fri, 25 Jul 2025 13:17:28 -0700 Subject: [PATCH 0155/1794] migration: rename target.c to vfio.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Acked-by: Fabiano Rosas Reviewed-by: Peter Xu Message-ID: <20250725201729.17100-3-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- migration/meson.build | 2 +- migration/{target.c => vfio.c} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename migration/{target.c => vfio.c} (90%) diff --git a/migration/meson.build b/migration/meson.build index 9aa48b290e2a5..276da3be5a32e 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -51,4 +51,4 @@ system_ss.add(when: qatzip, if_true: files('multifd-qatzip.c')) specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: files('ram.c', - 'target.c')) + 'vfio.c')) diff --git a/migration/target.c b/migration/vfio.c similarity index 90% rename from migration/target.c rename to migration/vfio.c index 12fd399f0c521..0b64e49ef060b 100644 --- a/migration/target.c +++ b/migration/vfio.c @@ -1,5 +1,5 @@ /* - * QEMU live migration - functions that need to be compiled target-specific + * QEMU live migration - VFIO * * This work is licensed under the terms of the GNU GPL, version 2 * or (at your option) any later version. From 2bfcd27e00a49da2efa5d703121b94cd9cd4948b Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Wed, 16 Jul 2025 11:53:43 +0200 Subject: [PATCH 0156/1794] hw/net/cadence_gem: fix register mask initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gem_init_register_masks function was called at init time but it relies on the num-priority-queues property. Call it at realize time instead. Cc: qemu-stable@nongnu.org Fixes: 4c70e32f05f ("net: cadence_gem: Define access permission for interrupt registers") Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Sai Pavan Boddu Message-ID: <20250716095432.81923-2-luc.michel@amd.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/cadence_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 50025d5a6f2b3..44446666deb26 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -1756,6 +1756,7 @@ static void gem_realize(DeviceState *dev, Error **errp) sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]); } + gem_init_register_masks(s); qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_gem_info, &s->conf, @@ -1776,7 +1777,6 @@ static void gem_init(Object *obj) DB_PRINT("\n"); - gem_init_register_masks(s); memory_region_init_io(&s->iomem, OBJECT(s), &gem_ops, s, "enet", sizeof(s->regs)); From 014bb30d218ef0d91c300324dec99138e999e32d Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Thu, 17 Jul 2025 15:02:07 -0700 Subject: [PATCH 0157/1794] hw/xen/passthrough: add missing error-report include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit cfcacbab38e ("xen/passthrough: use gsi to map pirq when dom0 is PVH") an `error_report` was added to this file, but the corresponding include of `qemu/error-report.h` was missed. This only becomes apparent when building against Xen 4.20+ with trace backend log disabled. Fixes: cfcacbab38e4 (xen/passthrough: use gsi to map pirq when dom0 is PVH) Signed-off-by: Adam Williamson Reviewed-by: Markus Armbruster Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250717220207.171040-1-awilliam@redhat.com> [PMD: Improved commit description, added Fixes: tag] Signed-off-by: Philippe Mathieu-Daudé --- hw/xen/xen_pt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 9d16644d82e8c..006b5b55f249d 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -54,6 +54,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include #include "hw/pci/pci.h" From 2865bf1c5795371ef441e9029bc22566ccff8299 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 24 Jul 2025 09:11:42 -0700 Subject: [PATCH 0158/1794] system/physmem: fix use-after-free with dispatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A use-after-free bug was reported when booting a Linux kernel during the pci setup phase. It's quite hard to reproduce (needs smp, and favored by having several pci devices with BAR and specific Linux config, which is Debian default one in this case). After investigation (see the associated bug ticket), it appears that, under specific conditions, we might access a cached AddressSpaceDispatch that was reclaimed by RCU thread meanwhile. In the Linux boot scenario, during the pci phase, memory region are destroyed/recreated, resulting in exposition of the bug. The core of the issue is that we cache the dispatch associated to current cpu in cpu->cpu_ases[asidx].memory_dispatch. It is updated with tcg_commit, which runs asynchronously on a given cpu. At some point, we leave the rcu critial section, and the RCU thread starts reclaiming it, but tcg_commit is not yet invoked, resulting in the use-after-free. It's not the first problem around this area, and commit 0d58c660689 [1] ("softmmu: Use async_run_on_cpu in tcg_commit") already tried to address it. It did a good job, but it seems that we found a specific situation where it's not enough. This patch takes a simple approach: remove the cached value creating the issue, and make sure we always get the current mapping for address space, using address_space_to_dispatch(cpu->cpu_ases[asidx].as). It's equivalent to qatomic_rcu_read(&as->current_map)->dispatch; This is not really costly, we just need two dereferences, including one atomic (rcu) read, which is negligible considering we are already on mmu slow path anyway. Note that tcg_commit is still needed, as it's taking care of flushing TLB, removing previously mapped entries. Another solution would be to cache directly values under the dispatch (dispatch themselves are not ref counted), keep an active reference on associated memory section, and release it when appropriate (tricky). Given the time already spent debugging this area now and previously, I strongly prefer eliminating the root of the issue, instead of adding more complexity for a hypothetical performance gain. RCU is precisely used to ensure good performance when reading data, so caching is not as beneficial as it might seem IMHO. [1] https://gitlab.com/qemu-project/qemu/-/commit/0d58c660689f6da1e3feff8a997014003d928b3b Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3040 Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Reviewed-by: Michael Tokarev Tested-by: Michael Tokarev Message-ID: <20250724161142.2803091-1-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- system/physmem.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index 130c148ffb5c1..e5dd760e0bcae 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -165,13 +165,11 @@ static bool ram_is_cpr_compatible(RAMBlock *rb); * CPUAddressSpace: all the information a CPU needs about an AddressSpace * @cpu: the CPU whose AddressSpace this is * @as: the AddressSpace itself - * @memory_dispatch: its dispatch pointer (cached, RCU protected) * @tcg_as_listener: listener for tracking changes to the AddressSpace */ typedef struct CPUAddressSpace { CPUState *cpu; AddressSpace *as; - struct AddressSpaceDispatch *memory_dispatch; MemoryListener tcg_as_listener; } CPUAddressSpace; @@ -692,7 +690,7 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr orig_addr, IOMMUTLBEntry iotlb; int iommu_idx; hwaddr addr = orig_addr; - AddressSpaceDispatch *d = cpu->cpu_ases[asidx].memory_dispatch; + AddressSpaceDispatch *d = address_space_to_dispatch(cpu->cpu_ases[asidx].as); for (;;) { section = address_space_translate_internal(d, addr, &addr, plen, false); @@ -753,7 +751,7 @@ MemoryRegionSection *iotlb_to_section(CPUState *cpu, { int asidx = cpu_asidx_from_attrs(cpu, attrs); CPUAddressSpace *cpuas = &cpu->cpu_ases[asidx]; - AddressSpaceDispatch *d = cpuas->memory_dispatch; + AddressSpaceDispatch *d = address_space_to_dispatch(cpuas->as); int section_index = index & ~TARGET_PAGE_MASK; MemoryRegionSection *ret; @@ -2780,9 +2778,6 @@ static void tcg_log_global_after_sync(MemoryListener *listener) static void tcg_commit_cpu(CPUState *cpu, run_on_cpu_data data) { - CPUAddressSpace *cpuas = data.host_ptr; - - cpuas->memory_dispatch = address_space_to_dispatch(cpuas->as); tlb_flush(cpu); } @@ -2798,11 +2793,7 @@ static void tcg_commit(MemoryListener *listener) cpu = cpuas->cpu; /* - * Defer changes to as->memory_dispatch until the cpu is quiescent. - * Otherwise we race between (1) other cpu threads and (2) ongoing - * i/o for the current cpu thread, with data cached by mmu_lookup(). - * - * In addition, queueing the work function will kick the cpu back to + * Queueing the work function will kick the cpu back to * the main loop, which will end the RCU critical section and reclaim * the memory data structures. * From 653a75a9d7f957c4ba9387d1a54bccfdce49cb9c Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Mon, 28 Jul 2025 00:55:07 +0300 Subject: [PATCH 0159/1794] roms/Makefile: fix npcmNxx_bootrom build rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 70ce076fa6dff60, the actual rom source dirs are subdirs of vbootrom/ submodule, not in top-level of it. Fixes: 70ce076fa6dff60 "roms: Update vbootrom to 1287b6e" Fixes: 269b7effd90 ("pc-bios: Add NPCM8XX vBootrom") Cc: qemu-stable@nongnu.org Signed-off-by: Michael Tokarev Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20250727215511.807880-1-mjt@tls.msk.ru> Signed-off-by: Philippe Mathieu-Daudé --- roms/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/roms/Makefile b/roms/Makefile index beff58d9d50c5..6af68a922f30d 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -193,12 +193,12 @@ qboot: cp qboot/build/bios.bin ../pc-bios/qboot.rom npcm7xx_bootrom: - $(MAKE) -C vbootrom CROSS_COMPILE=$(arm_cross_prefix) - cp vbootrom/npcm7xx_bootrom.bin ../pc-bios/npcm7xx_bootrom.bin + $(MAKE) -C vbootrom/npcm7xx CROSS_COMPILE=$(arm_cross_prefix) + cp vbootrom/npcm7xx/npcm7xx_bootrom.bin ../pc-bios/npcm7xx_bootrom.bin npcm8xx_bootrom: - $(MAKE) -C vbootrom CROSS_COMPILE=$(aarch64_cross_prefix) - cp vbootrom/npcm8xx_bootrom.bin ../pc-bios/npcm8xx_bootrom.bin + $(MAKE) -C vbootrom/npcm8xx CROSS_COMPILE=$(aarch64_cross_prefix) + cp vbootrom/npcm8xx/npcm8xx_bootrom.bin ../pc-bios/npcm8xx_bootrom.bin hppa-firmware: $(MAKE) -C seabios-hppa parisc From 67e4808403471427b73c8d2c3f4273d64908f480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chigot?= Date: Mon, 28 Jul 2025 11:05:18 +0200 Subject: [PATCH 0160/1794] hw/display/sm501: fix missing error-report.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "qemu/error-report.h" was previously implicitly included. This is no longer the case following 012842c075520dbe1bd96a2fdcf4e218874ba443. However, the issue predates this change as `error-report.h` should have been included when the `warn_report` call was introduced. Fixes: fa140b9562 ("hw/sm501: allow compiling without PIXMAN") Signed-off-by: Clément Chigot Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-ID: <20250728090518.963573-1-chigot@adacore.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/sm501.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 6d2f18684c366..bc091b3c9fb1e 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/module.h" #include "hw/usb/hcd-ohci.h" From d4d91ed42eb93f64950a036f35efbd915feb95b3 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 17 Jul 2025 13:57:51 +0200 Subject: [PATCH 0161/1794] qapi: Add more cross-references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We recently (merge commit 504632dcc631) enclosed command and type names in `backquotes`, so they become links in generated HTML. Take care of a few we missed. Signed-off-by: Markus Armbruster Message-ID: <20250717115751.3832597-1-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: John Snow --- qapi/dump.json | 2 +- qapi/machine.json | 2 +- qapi/migration.json | 4 ++-- qapi/misc-i386.json | 2 +- qapi/run-state.json | 2 +- qapi/sockets.json | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/qapi/dump.json b/qapi/dump.json index 32c8c1f06e403..726b5208703c0 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -79,7 +79,7 @@ # # @detach: if true, QMP will return immediately rather than waiting # for the dump to finish. The user can track progress using -# "query-dump". (since 2.6). +# `query-dump`. (since 2.6). # # @begin: if specified, the starting physical address. # diff --git a/qapi/machine.json b/qapi/machine.json index 6f59f70ca61f1..038eab281c78c 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -2087,7 +2087,7 @@ # # @deprecated-props: an optional list of properties that are flagged as # deprecated by the CPU vendor. The list depends on the -# CpuModelExpansionType: "static" properties are a subset of the +# `CpuModelExpansionType`: "static" properties are a subset of the # enabled-properties for the expanded model; "full" properties are # a set of properties that are deprecated across all models for # the architecture. (since: 10.1 -- since 9.1 on s390x --). diff --git a/qapi/migration.json b/qapi/migration.json index e08a99bb82672..2387c21e9c188 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -641,7 +641,7 @@ # # This mode supports VFIO devices provided the user first puts the # guest in the suspended runstate, such as by issuing -# guest-suspend-ram to the QEMU guest agent. +# `guest-suspend-ram` to the QEMU guest agent. # # Best performance is achieved when the memory backend is shared # and the @x-ignore-shared migration capability is set, but this @@ -1704,7 +1704,7 @@ # # .. admonition:: Notes # -# 1. The 'query-migrate' command should be used to check +# 1. The `query-migrate` command should be used to check # migration's progress and final result (this information is # provided by the 'status' member). # diff --git a/qapi/misc-i386.json b/qapi/misc-i386.json index c8c91a241cc01..d1ce8caf253b6 100644 --- a/qapi/misc-i386.json +++ b/qapi/misc-i386.json @@ -8,7 +8,7 @@ # # Reset the RTC interrupt reinjection backlog. Can be used if another # mechanism to synchronize guest time is in effect, for example QEMU -# guest agent's guest-set-time command. +# guest agent's `guest-set-time` command. # # Use of this command is only applicable for x86 machines with an RTC, # and on other machines will silently return without performing any diff --git a/qapi/run-state.json b/qapi/run-state.json index 54ba5c9a3f51c..4757947ca6bfc 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -20,7 +20,7 @@ # @inmigrate: guest is paused waiting for an incoming migration. Note # that this state does not tell whether the machine will start at # the end of the migration. This depends on the command-line -S -# option and any invocation of 'stop' or 'cont' that has happened +# option and any invocation of `stop` or `cont` that has happened # since QEMU was started. # # @internal-error: An internal error that prevents further guest diff --git a/qapi/sockets.json b/qapi/sockets.json index 82046b0b3ab89..32fac5172887b 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -143,7 +143,7 @@ # # @str: decimal is for file descriptor number, otherwise it's a file # descriptor name. Named file descriptors are permitted in -# monitor commands, in combination with the 'getfd' command. +# monitor commands, in combination with the `getfd` command. # Decimal file descriptors are permitted at startup or other # contexts where no monitor context is active. # From a3004697f7342258de6fdc3ed85943a33a4809cf Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 24 Jul 2025 11:17:41 +0200 Subject: [PATCH 0162/1794] qapi/accelerator: Fix markup of heading The docs generated for qapi/accelerator.json shows text "= Accelerators" instead of a heading. This is because the patch that added the heading crossed with the commit that changed heading markup (commit 6c10778826a "docs/sphinx: remove special parsing for freeform sections"). Fix the markup. Fixes: 18da42ee4273 (qapi/accel: Move definitions related to accelerators in their own file) Signed-off-by: Markus Armbruster Message-ID: <20250724091742.1950167-2-armbru@redhat.com> --- qapi/accelerator.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qapi/accelerator.json b/qapi/accelerator.json index 28d5ff4c493a1..fb28c8d920aec 100644 --- a/qapi/accelerator.json +++ b/qapi/accelerator.json @@ -4,7 +4,9 @@ # SPDX-License-Identifier: GPL-2.0-or-later ## -# = Accelerators +# ************ +# Accelerators +# ************ ## { 'include': 'common.json' } From 1047cc281631063ff2db481b8ea7eb755005236a Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 24 Jul 2025 11:17:42 +0200 Subject: [PATCH 0163/1794] tests/qapi-schema: Bury dead test case doc-non-first-section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test passed when it was added. However, the commit adding it neglected to make Meson aware of it, so it never ran automatically. The test stopped making sense when we changed headings markup, and ceased to pass then. It should've been removed then. Do that now. Fixes: 6c10778826a8 (docs/sphinx: remove special parsing for freeform sections) Signed-off-by: Markus Armbruster Message-ID: <20250724091742.1950167-3-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: John Snow --- tests/qapi-schema/doc-non-first-section.err | 1 - tests/qapi-schema/doc-non-first-section.json | 6 ------ tests/qapi-schema/doc-non-first-section.out | 0 3 files changed, 7 deletions(-) delete mode 100644 tests/qapi-schema/doc-non-first-section.err delete mode 100644 tests/qapi-schema/doc-non-first-section.json delete mode 100644 tests/qapi-schema/doc-non-first-section.out diff --git a/tests/qapi-schema/doc-non-first-section.err b/tests/qapi-schema/doc-non-first-section.err deleted file mode 100644 index eeced2bca71e1..0000000000000 --- a/tests/qapi-schema/doc-non-first-section.err +++ /dev/null @@ -1 +0,0 @@ -doc-non-first-section.json:5:1: '=' heading must come first in a comment block diff --git a/tests/qapi-schema/doc-non-first-section.json b/tests/qapi-schema/doc-non-first-section.json deleted file mode 100644 index 1590876061d57..0000000000000 --- a/tests/qapi-schema/doc-non-first-section.json +++ /dev/null @@ -1,6 +0,0 @@ -# = section must be first line - -## -# -# = Not first -## diff --git a/tests/qapi-schema/doc-non-first-section.out b/tests/qapi-schema/doc-non-first-section.out deleted file mode 100644 index e69de29bb2d1d..0000000000000 From a14f6d3288bcfd4f3f7e6aca4572e2443cc1f91b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 29 Jul 2025 11:16:41 +0200 Subject: [PATCH 0164/1794] docs/qapi-domain: Fix typos Signed-off-by: Markus Armbruster Message-ID: <20250729091642.3513895-2-armbru@redhat.com> Reviewed-by: Manos Pitsidianakis --- docs/devel/qapi-domain.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/devel/qapi-domain.rst b/docs/devel/qapi-domain.rst index b71890f6609da..fe540d1e40a75 100644 --- a/docs/devel/qapi-domain.rst +++ b/docs/devel/qapi-domain.rst @@ -9,7 +9,7 @@ in Sphinx is provided by the QAPI Domain, located in `Python Domain `_ included with Sphinx, but provides special directives and roles -speciically for annotating and documenting QAPI definitions +for annotating and documenting QAPI definitions specifically. A `Domain @@ -101,7 +101,7 @@ without types. The QAPI domain uses this class for features, returns, and enum values. TypedField: - * Creates a grouped, typed field. Multiple adjacent entres will be + * Creates a grouped, typed field. Multiple adjacent entries will be merged into one section, and the content will form a bulleted list. * *Must* take at least one argument, but supports up to two - nominally, a name and a type. From a95b3c0ad857055198cdb272bca2af975092d060 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 29 Jul 2025 11:16:42 +0200 Subject: [PATCH 0165/1794] MAINTAINERS: Cover docs/devel/qapi-domain.rst properly Section QAPI already covers it, and that's fine. It's missing from "Sphinx documentation configuration and build machinery". Add it there. Signed-off-by: Markus Armbruster Message-ID: <20250729091642.3513895-3-armbru@redhat.com> Reviewed-by: Manos Pitsidianakis [Improved commit message] --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 37879ab64e57a..069d77f2f8004 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4433,6 +4433,7 @@ F: docs/requirements.txt F: docs/sphinx/ F: docs/_templates/ F: docs/devel/docs.rst +F: docs/devel/qapi-domain.rst Rust build system integration M: Manos Pitsidianakis From 9b80226ece693197af8a981b424391b68b5bc38e Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 29 Jul 2025 13:00:41 -0400 Subject: [PATCH 0166/1794] Update version for the v10.1.0-rc1 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index c52f1cc9fe4b6..8ae905b0ddbce 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.0.90 +10.0.91 From 77707bfdf871199bbee665e721ced961aaf3a798 Mon Sep 17 00:00:00 2001 From: Vac Chen Date: Sun, 6 Jul 2025 14:55:54 +0800 Subject: [PATCH 0167/1794] target/riscv: Fix pmp range wraparound on zero pmp_is_in_range() prefers to match addresses within the interval [start, end]. To archieve this, pmpaddrX is decremented during the end address update. In TOR mode, a rule is ignored if its start address is greater than or equal to its end address. However, if pmpaddrX is set to 0, this decrement operation causes the calulated end address to wrap around to UINT_MAX. In this scenario, the address guard for this PMP entry would become ineffective. This patch addresses the issue by moving the guard check earlier, preventing the problematic wraparound when pmpaddrX is zero. Signed-off-by: Vac Chen Reviewed-by: Alistair Francis Message-ID: <20250706065554.42953-1-vacantron@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/pmp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 3540327c9af0a..72f1372a4958c 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -211,11 +211,12 @@ void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index) break; case PMP_AMATCH_TOR: - sa = prev_addr << 2; /* shift up from [xx:0] to [xx+2:2] */ - ea = (this_addr << 2) - 1u; - if (sa > ea) { + if (prev_addr >= this_addr) { sa = ea = 0u; + break; } + sa = prev_addr << 2; /* shift up from [xx:0] to [xx+2:2] */ + ea = (this_addr << 2) - 1u; break; case PMP_AMATCH_NA4: From 35d129399d319b7ab806e82ebacd52392e36bf61 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Thu, 24 Jul 2025 16:33:48 +0530 Subject: [PATCH 0168/1794] bios-tables-test-allowed-diff.h: Allow RISC-V FADT and MADT changes Signed-off-by: Sunil V L Acked-by: Michael S. Tsirkin Reviewed-by: Daniel Henrique Barboza Reviewed-by: Nutty Liu Message-ID: <20250724110350.452828-2-sunilvl@ventanamicro.com> Signed-off-by: Alistair Francis --- tests/qtest/bios-tables-test-allowed-diff.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8bf45..0c3f7a6cac2b2 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,3 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/riscv64/virt/APIC", +"tests/data/acpi/riscv64/virt/FACP", From a3b95362ce90d03d150dffed3add0cb600fb0850 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Thu, 24 Jul 2025 16:33:49 +0530 Subject: [PATCH 0169/1794] hw/riscv/virt-acpi-build.c: Update FADT and MADT versions RISC-V support is added only in ACPI 6.6. According to the ACPI 6.6 specification, the minor version of the Fixed ACPI Description Table (FADT) should be 6, and the Multiple APIC Description Table (MADT) should use revision 7. So, update the RISC-V FADT and MADT to reflect correct versions. Update the code comments to reflect ACPI 6.6 version details. Signed-off-by: Sunil V L Acked-by: Michael S. Tsirkin Reviewed-by: Daniel Henrique Barboza Reviewed-by: Nutty Liu Message-ID: <20250724110350.452828-3-sunilvl@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt-acpi-build.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c index ee1416d264598..f1406cb68339b 100644 --- a/hw/riscv/virt-acpi-build.c +++ b/hw/riscv/virt-acpi-build.c @@ -270,11 +270,8 @@ spcr_setup(GArray *table_data, BIOSLinker *linker, RISCVVirtState *s) #define RHCT_NODE_ARRAY_OFFSET 56 /* - * ACPI spec, Revision 6.5+ - * 5.2.36 RISC-V Hart Capabilities Table (RHCT) - * REF: https://github.com/riscv-non-isa/riscv-acpi/issues/16 - * https://drive.google.com/file/d/1nP3nFiH4jkPMp6COOxP6123DCZKR-tia/view - * https://drive.google.com/file/d/1sKbOa8m1UZw1JkquZYe3F1zQBN1xXsaf/view + * ACPI spec, Revision 6.6 + * 5.2.37 RISC-V Hart Capabilities Table (RHCT) */ static void build_rhct(GArray *table_data, BIOSLinker *linker, @@ -421,7 +418,10 @@ static void build_rhct(GArray *table_data, acpi_table_end(linker, &table); } -/* FADT */ +/* + * ACPI spec, Revision 6.6 + * 5.2.9 Fixed ACPI Description Table (MADT) + */ static void build_fadt_rev6(GArray *table_data, BIOSLinker *linker, RISCVVirtState *s, @@ -429,7 +429,7 @@ static void build_fadt_rev6(GArray *table_data, { AcpiFadtData fadt = { .rev = 6, - .minor_ver = 5, + .minor_ver = 6, .flags = 1 << ACPI_FADT_F_HW_REDUCED_ACPI, .xdsdt_tbl_offset = &dsdt_tbl_offset, }; @@ -508,11 +508,8 @@ static void build_dsdt(GArray *table_data, } /* - * ACPI spec, Revision 6.5+ + * ACPI spec, Revision 6.6 * 5.2.12 Multiple APIC Description Table (MADT) - * REF: https://github.com/riscv-non-isa/riscv-acpi/issues/15 - * https://drive.google.com/file/d/1R6k4MshhN3WTT-hwqAquu5nX6xSEqK2l/view - * https://drive.google.com/file/d/1oMGPyOD58JaPgMl1pKasT-VKsIKia7zR/view */ static void build_madt(GArray *table_data, BIOSLinker *linker, @@ -537,7 +534,7 @@ static void build_madt(GArray *table_data, hart_index_bits = imsic_num_bits(imsic_max_hart_per_socket); - AcpiTable table = { .sig = "APIC", .rev = 6, .oem_id = s->oem_id, + AcpiTable table = { .sig = "APIC", .rev = 7, .oem_id = s->oem_id, .oem_table_id = s->oem_table_id }; acpi_table_begin(&table, table_data); @@ -812,10 +809,8 @@ static void build_rimt(GArray *table_data, BIOSLinker *linker, } /* - * ACPI spec, Revision 6.5+ + * ACPI spec, Revision 6.6 * 5.2.16 System Resource Affinity Table (SRAT) - * REF: https://github.com/riscv-non-isa/riscv-acpi/issues/25 - * https://drive.google.com/file/d/1YTdDx2IPm5IeZjAW932EYU-tUtgS08tX/view */ static void build_srat(GArray *table_data, BIOSLinker *linker, RISCVVirtState *vms) From f3c8b7767f2e1fac37c727ca17b69e4f1e3351f2 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Thu, 24 Jul 2025 16:33:50 +0530 Subject: [PATCH 0170/1794] tests/data/acpi/riscv64: Update expected FADT and MADT Update the expected tables for the version change. /* * * ACPI Data Table [FACP] * * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue (in hex) */ [000h 0000 004h] Signature : "FACP" [Fixed ACPI Description Table (FADT)] [004h 0004 004h] Table Length : 00000114 [008h 0008 001h] Revision : 06 -[009h 0009 001h] Checksum : 13 +[009h 0009 001h] Checksum : 12 [00Ah 0010 006h] Oem ID : "BOCHS " [010h 0016 008h] Oem Table ID : "BXPC " [018h 0024 004h] Oem Revision : 00000001 [01Ch 0028 004h] Asl Compiler ID : "BXPC" [020h 0032 004h] Asl Compiler Revision : 00000001 [024h 0036 004h] FACS Address : 00000000 [028h 0040 004h] DSDT Address : 00000000 [02Ch 0044 001h] Model : 00 [02Dh 0045 001h] PM Profile : 00 [Unspecified] [02Eh 0046 002h] SCI Interrupt : 0000 [030h 0048 004h] SMI Command Port : 00000000 [034h 0052 001h] ACPI Enable Value : 00 [035h 0053 001h] ACPI Disable Value : 00 [036h 0054 001h] S4BIOS Command : 00 [037h 0055 001h] P-State Control : 00 @@ -86,33 +86,33 @@ Use APIC Physical Destination Mode (V4) : 0 Hardware Reduced (V5) : 1 Low Power S0 Idle (V5) : 0 [074h 0116 00Ch] Reset Register : [Generic Address Structure] [074h 0116 001h] Space ID : 00 [SystemMemory] [075h 0117 001h] Bit Width : 00 [076h 0118 001h] Bit Offset : 00 [077h 0119 001h] Encoded Access Width : 00 [Undefined/Legacy] [078h 0120 008h] Address : 0000000000000000 [080h 0128 001h] Value to cause reset : 00 [081h 0129 002h] ARM Flags (decoded below) : 0000 PSCI Compliant : 0 Must use HVC for PSCI : 0 -[083h 0131 001h] FADT Minor Revision : 05 +[083h 0131 001h] FADT Minor Revision : 06 [084h 0132 008h] FACS Address : 0000000000000000 [...] /* * * ACPI Data Table [APIC] * * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue (in hex) */ [000h 0000 004h] Signature : "APIC" [Multiple APIC Description Table (MADT)] [004h 0004 004h] Table Length : 00000074 -[008h 0008 001h] Revision : 06 -[009h 0009 001h] Checksum : B4 +[008h 0008 001h] Revision : 07 +[009h 0009 001h] Checksum : B3 [00Ah 0010 006h] Oem ID : "BOCHS " [010h 0016 008h] Oem Table ID : "BXPC " [...] Signed-off-by: Sunil V L Acked-by: Michael S. Tsirkin Reviewed-by: Daniel Henrique Barboza Reviewed-by: Nutty Liu Message-ID: <20250724110350.452828-4-sunilvl@ventanamicro.com> Signed-off-by: Alistair Francis --- tests/data/acpi/riscv64/virt/APIC | Bin 116 -> 116 bytes tests/data/acpi/riscv64/virt/FACP | Bin 276 -> 276 bytes tests/qtest/bios-tables-test-allowed-diff.h | 2 -- 3 files changed, 2 deletions(-) diff --git a/tests/data/acpi/riscv64/virt/APIC b/tests/data/acpi/riscv64/virt/APIC index 66a25dfd2d6ea2b607c024722b2eab95873a01e9..3fb5b753596fc7c2618b3d5210be8b00b0c177f6 100644 GIT binary patch delta 16 XcmXRZ;c^V{bS`0FU|`=okt+)TB&q~M delta 16 XcmXRZ;c^V{bS`0FU|`!akt+)TB&h^L diff --git a/tests/data/acpi/riscv64/virt/FACP b/tests/data/acpi/riscv64/virt/FACP index a5276b65ea8ce46cc9b40d96d98f0669c9089ed4..78e1b14b1d4ff0533a6a4c041f195a69b4ef114d 100644 GIT binary patch delta 30 mcmbQjG=+)F&CxkPgpq-PO=u!lB_rF!iPaMgcqj8PasU8k76z;U delta 30 mcmbQjG=+)F&CxkPgpq-PO?V<#B_r#^iPaMgcqj8PasU8k8wRWZ diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 0c3f7a6cac2b2..dfb8523c8bf45 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,3 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/riscv64/virt/APIC", -"tests/data/acpi/riscv64/virt/FACP", From b6f1244678bebaf7e2c775cfc66d452f95678ebf Mon Sep 17 00:00:00 2001 From: Yang Jialong Date: Mon, 28 Jul 2025 13:51:14 +0800 Subject: [PATCH 0171/1794] intc/riscv_aplic: Fix target register read when source is inactive The RISC-V Advanced interrupt Architecture: 4.5.16. Interrupt targets: If interrupt source i is inactive in this domain, register target[i] is read-only zero. Signed-off-by: Yang Jialong Reviewed-by: Daniel Henrique Barboza Message-ID: <20250728055114.252024-1-z_bajeer@yeah.net> Signed-off-by: Alistair Francis --- hw/intc/riscv_aplic.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index 4fa5f7597b08a..a1d9fa50855f0 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -628,7 +628,7 @@ static void riscv_aplic_request(void *opaque, int irq, int level) static uint64_t riscv_aplic_read(void *opaque, hwaddr addr, unsigned size) { - uint32_t irq, word, idc; + uint32_t irq, word, idc, sm; RISCVAPLICState *aplic = opaque; /* Reads must be 4 byte words */ @@ -696,6 +696,10 @@ static uint64_t riscv_aplic_read(void *opaque, hwaddr addr, unsigned size) } else if ((APLIC_TARGET_BASE <= addr) && (addr < (APLIC_TARGET_BASE + (aplic->num_irqs - 1) * 4))) { irq = ((addr - APLIC_TARGET_BASE) >> 2) + 1; + sm = aplic->sourcecfg[irq] & APLIC_SOURCECFG_SM_MASK; + if (sm == APLIC_SOURCECFG_SM_INACTIVE) { + return 0; + } return aplic->target[irq]; } else if (!aplic->msimode && (APLIC_IDC_BASE <= addr) && (addr < (APLIC_IDC_BASE + aplic->num_harts * APLIC_IDC_SIZE))) { From e111ffe48b29ca8abd450af9ee5dd71af3f93536 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 28 Jul 2025 14:06:33 -0300 Subject: [PATCH 0172/1794] linux-user/strace.list: add riscv_hwprobe entry We're missing a strace entry for riscv_hwprobe, and using -strace will report it as "Unknown syscall 258". After this patch we'll have: $ ./build/qemu-riscv64 -strace test_mutex_riscv 110182 riscv_hwprobe(0x7f207efdc700,1,0,0,0,0) = 0 110182 brk(NULL) = 0x0000000000082000 (...) Signed-off-by: Daniel Henrique Barboza Reviewed-by: Richard Henderson Message-ID: <20250728170633.113384-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- linux-user/strace.list | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux-user/strace.list b/linux-user/strace.list index fdf94ef32ad2e..ab818352a90cd 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -1716,3 +1716,6 @@ { TARGET_NR_clock_gettime64, "clock_gettime64" , NULL, print_clock_gettime64, print_syscall_ret_clock_gettime64 }, #endif +#ifdef TARGET_NR_riscv_hwprobe +{ TARGET_NR_riscv_hwprobe, "riscv_hwprobe" , "%s(%p,%d,%d,%d,%d,%d)", NULL, NULL }, +#endif From 16aa7771afeac422dcf7be2833d5426da6b814fa Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 14 Jul 2025 10:37:39 -0300 Subject: [PATCH 0173/1794] target/riscv: do not call GETPC() in check_ret_from_m_mode() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GETPC() should always be called from the top level helper, e.g. the first helper that is called by the translation code. We stopped doing that in commit 3157a553ec, and then we introduced problems when unwinding the exceptions being thrown by helper_mret(), as reported by [1]. Call GETPC() at the top level helper and pass the value along. [1] https://gitlab.com/qemu-project/qemu/-/issues/3020 Suggested-by: Richard Henderson Fixes: 3157a553ec ("target/riscv: Add Smrnmi mnret instruction") Closes: https://gitlab.com/qemu-project/qemu/-/issues/3020 Signed-off-by: Daniel Henrique Barboza Reviewed-by: Nutty Liu Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-ID: <20250714133739.1248296-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/op_helper.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 15460bf84bd37..110292e84dad2 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -355,21 +355,22 @@ target_ulong helper_sret(CPURISCVState *env) } static void check_ret_from_m_mode(CPURISCVState *env, target_ulong retpc, - target_ulong prev_priv) + target_ulong prev_priv, + uintptr_t ra) { if (!(env->priv >= PRV_M)) { - riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra); } if (!riscv_cpu_allow_16bit_insn(&env_archcpu(env)->cfg, env->priv_ver, env->misa_ext) && (retpc & 0x3)) { - riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, ra); } if (riscv_cpu_cfg(env)->pmp && !pmp_get_num_rules(env) && (prev_priv != PRV_M)) { - riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, ra); } } static target_ulong ssdbltrp_mxret(CPURISCVState *env, target_ulong mstatus, @@ -394,8 +395,9 @@ target_ulong helper_mret(CPURISCVState *env) target_ulong retpc = env->mepc & get_xepc_mask(env); uint64_t mstatus = env->mstatus; target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); + uintptr_t ra = GETPC(); - check_ret_from_m_mode(env, retpc, prev_priv); + check_ret_from_m_mode(env, retpc, prev_priv, ra); target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV) && (prev_priv != PRV_M); @@ -443,8 +445,9 @@ target_ulong helper_mnret(CPURISCVState *env) target_ulong retpc = env->mnepc; target_ulong prev_priv = get_field(env->mnstatus, MNSTATUS_MNPP); target_ulong prev_virt; + uintptr_t ra = GETPC(); - check_ret_from_m_mode(env, retpc, prev_priv); + check_ret_from_m_mode(env, retpc, prev_priv, ra); prev_virt = get_field(env->mnstatus, MNSTATUS_MNPV) && (prev_priv != PRV_M); From 09ac27a9b59bf87786cb35f7126fb5788b0b4bca Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 10 Jul 2025 07:05:25 -0300 Subject: [PATCH 0174/1794] riscv: Revert "Generate strided vector loads/stores with tcg nodes." This reverts commit 28c12c1f2f50d7f7f1ebfc587c4777ecd50aac5b. As reported in [1] this commit is breaking Linux vector code, and although a simpler reproducer was provided, the fix itself isn't trivial due to the amount and the nature of the changes. And we really do not want to keep Linux broken while we work on it. The revert will fix Linux and will give us time to do a proper fix. [1] https://mail.gnu.org/archive/html/qemu-devel/2025-07/msg02525.html Signed-off-by: Daniel Henrique Barboza Tested-by: Eric Biggers Message-ID: <20250710100525.372985-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 323 ++++-------------------- 1 file changed, 50 insertions(+), 273 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 610bf9ff30cef..71f98fb350b68 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -864,286 +864,32 @@ GEN_VEXT_TRANS(vlm_v, MO_8, vlm_v, ld_us_mask_op, ld_us_mask_check) GEN_VEXT_TRANS(vsm_v, MO_8, vsm_v, st_us_mask_op, st_us_mask_check) /* - * MAXSZ returns the maximum vector size can be operated in bytes, - * which is used in GVEC IR when vl_eq_vlmax flag is set to true - * to accelerate vector operation. - */ -static inline uint32_t MAXSZ(DisasContext *s) -{ - int max_sz = s->cfg_ptr->vlenb << 3; - return max_sz >> (3 - s->lmul); -} - -static inline uint32_t get_log2(uint32_t a) -{ - uint32_t i = 0; - for (; a > 0;) { - a >>= 1; - i++; - } - return i; -} - -typedef void gen_tl_ldst(TCGv, TCGv_ptr, tcg_target_long); - -/* - * Simulate the strided load/store main loop: - * - * for (i = env->vstart; i < env->vl; env->vstart = ++i) { - * k = 0; - * while (k < nf) { - * if (!vm && !vext_elem_mask(v0, i)) { - * vext_set_elems_1s(vd, vma, (i + k * max_elems) * esz, - * (i + k * max_elems + 1) * esz); - * k++; - * continue; - * } - * target_ulong addr = base + stride * i + (k << log2_esz); - * ldst(env, adjust_addr(env, addr), i + k * max_elems, vd, ra); - * k++; - * } - * } - */ -static void gen_ldst_stride_main_loop(DisasContext *s, TCGv dest, uint32_t rs1, - uint32_t rs2, uint32_t vm, uint32_t nf, - gen_tl_ldst *ld_fn, gen_tl_ldst *st_fn, - bool is_load) -{ - TCGv addr = tcg_temp_new(); - TCGv base = get_gpr(s, rs1, EXT_NONE); - TCGv stride = get_gpr(s, rs2, EXT_NONE); - - TCGv i = tcg_temp_new(); - TCGv i_esz = tcg_temp_new(); - TCGv k = tcg_temp_new(); - TCGv k_esz = tcg_temp_new(); - TCGv k_max = tcg_temp_new(); - TCGv mask = tcg_temp_new(); - TCGv mask_offs = tcg_temp_new(); - TCGv mask_offs_64 = tcg_temp_new(); - TCGv mask_elem = tcg_temp_new(); - TCGv mask_offs_rem = tcg_temp_new(); - TCGv vreg = tcg_temp_new(); - TCGv dest_offs = tcg_temp_new(); - TCGv stride_offs = tcg_temp_new(); - - uint32_t max_elems = MAXSZ(s) >> s->sew; - - TCGLabel *start = gen_new_label(); - TCGLabel *end = gen_new_label(); - TCGLabel *start_k = gen_new_label(); - TCGLabel *inc_k = gen_new_label(); - TCGLabel *end_k = gen_new_label(); - - MemOp atomicity = MO_ATOM_NONE; - if (s->sew == 0) { - atomicity = MO_ATOM_NONE; - } else { - atomicity = MO_ATOM_IFALIGN_PAIR; - } - - mark_vs_dirty(s); - - tcg_gen_addi_tl(mask, (TCGv)tcg_env, vreg_ofs(s, 0)); - - /* Start of outer loop. */ - tcg_gen_mov_tl(i, cpu_vstart); - gen_set_label(start); - tcg_gen_brcond_tl(TCG_COND_GE, i, cpu_vl, end); - tcg_gen_shli_tl(i_esz, i, s->sew); - /* Start of inner loop. */ - tcg_gen_movi_tl(k, 0); - gen_set_label(start_k); - tcg_gen_brcond_tl(TCG_COND_GE, k, tcg_constant_tl(nf), end_k); - /* - * If we are in mask agnostic regime and the operation is not unmasked we - * set the inactive elements to 1. - */ - if (!vm && s->vma) { - TCGLabel *active_element = gen_new_label(); - /* (i + k * max_elems) * esz */ - tcg_gen_shli_tl(mask_offs, k, get_log2(max_elems << s->sew)); - tcg_gen_add_tl(mask_offs, mask_offs, i_esz); - - /* - * Check whether the i bit of the mask is 0 or 1. - * - * static inline int vext_elem_mask(void *v0, int index) - * { - * int idx = index / 64; - * int pos = index % 64; - * return (((uint64_t *)v0)[idx] >> pos) & 1; - * } - */ - tcg_gen_shri_tl(mask_offs_64, mask_offs, 3); - tcg_gen_add_tl(mask_offs_64, mask_offs_64, mask); - tcg_gen_ld_i64((TCGv_i64)mask_elem, (TCGv_ptr)mask_offs_64, 0); - tcg_gen_rem_tl(mask_offs_rem, mask_offs, tcg_constant_tl(8)); - tcg_gen_shr_tl(mask_elem, mask_elem, mask_offs_rem); - tcg_gen_andi_tl(mask_elem, mask_elem, 1); - tcg_gen_brcond_tl(TCG_COND_NE, mask_elem, tcg_constant_tl(0), - active_element); - /* - * Set masked-off elements in the destination vector register to 1s. - * Store instructions simply skip this bit as memory ops access memory - * only for active elements. - */ - if (is_load) { - tcg_gen_shli_tl(mask_offs, mask_offs, s->sew); - tcg_gen_add_tl(mask_offs, mask_offs, dest); - st_fn(tcg_constant_tl(-1), (TCGv_ptr)mask_offs, 0); - } - tcg_gen_br(inc_k); - gen_set_label(active_element); - } - /* - * The element is active, calculate the address with stride: - * target_ulong addr = base + stride * i + (k << log2_esz); - */ - tcg_gen_mul_tl(stride_offs, stride, i); - tcg_gen_shli_tl(k_esz, k, s->sew); - tcg_gen_add_tl(stride_offs, stride_offs, k_esz); - tcg_gen_add_tl(addr, base, stride_offs); - /* Calculate the offset in the dst/src vector register. */ - tcg_gen_shli_tl(k_max, k, get_log2(max_elems)); - tcg_gen_add_tl(dest_offs, i, k_max); - tcg_gen_shli_tl(dest_offs, dest_offs, s->sew); - tcg_gen_add_tl(dest_offs, dest_offs, dest); - if (is_load) { - tcg_gen_qemu_ld_tl(vreg, addr, s->mem_idx, MO_LE | s->sew | atomicity); - st_fn((TCGv)vreg, (TCGv_ptr)dest_offs, 0); - } else { - ld_fn((TCGv)vreg, (TCGv_ptr)dest_offs, 0); - tcg_gen_qemu_st_tl(vreg, addr, s->mem_idx, MO_LE | s->sew | atomicity); - } - /* - * We don't execute the load/store above if the element was inactive. - * We jump instead directly to incrementing k and continuing the loop. - */ - if (!vm && s->vma) { - gen_set_label(inc_k); - } - tcg_gen_addi_tl(k, k, 1); - tcg_gen_br(start_k); - /* End of the inner loop. */ - gen_set_label(end_k); - - tcg_gen_addi_tl(i, i, 1); - tcg_gen_mov_tl(cpu_vstart, i); - tcg_gen_br(start); - - /* End of the outer loop. */ - gen_set_label(end); - - return; -} - - -/* - * Set the tail bytes of the strided loads/stores to 1: - * - * for (k = 0; k < nf; ++k) { - * cnt = (k * max_elems + vl) * esz; - * tot = (k * max_elems + max_elems) * esz; - * for (i = cnt; i < tot; i += esz) { - * store_1s(-1, vd[vl+i]); - * } - * } + *** stride load and store */ -static void gen_ldst_stride_tail_loop(DisasContext *s, TCGv dest, uint32_t nf, - gen_tl_ldst *st_fn) -{ - TCGv i = tcg_temp_new(); - TCGv k = tcg_temp_new(); - TCGv tail_cnt = tcg_temp_new(); - TCGv tail_tot = tcg_temp_new(); - TCGv tail_addr = tcg_temp_new(); - - TCGLabel *start = gen_new_label(); - TCGLabel *end = gen_new_label(); - TCGLabel *start_i = gen_new_label(); - TCGLabel *end_i = gen_new_label(); - - uint32_t max_elems_b = MAXSZ(s); - uint32_t esz = 1 << s->sew; - - /* Start of the outer loop. */ - tcg_gen_movi_tl(k, 0); - tcg_gen_shli_tl(tail_cnt, cpu_vl, s->sew); - tcg_gen_movi_tl(tail_tot, max_elems_b); - tcg_gen_add_tl(tail_addr, dest, tail_cnt); - gen_set_label(start); - tcg_gen_brcond_tl(TCG_COND_GE, k, tcg_constant_tl(nf), end); - /* Start of the inner loop. */ - tcg_gen_mov_tl(i, tail_cnt); - gen_set_label(start_i); - tcg_gen_brcond_tl(TCG_COND_GE, i, tail_tot, end_i); - /* store_1s(-1, vd[vl+i]); */ - st_fn(tcg_constant_tl(-1), (TCGv_ptr)tail_addr, 0); - tcg_gen_addi_tl(tail_addr, tail_addr, esz); - tcg_gen_addi_tl(i, i, esz); - tcg_gen_br(start_i); - /* End of the inner loop. */ - gen_set_label(end_i); - /* Update the counts */ - tcg_gen_addi_tl(tail_cnt, tail_cnt, max_elems_b); - tcg_gen_addi_tl(tail_tot, tail_cnt, max_elems_b); - tcg_gen_addi_tl(k, k, 1); - tcg_gen_br(start); - /* End of the outer loop. */ - gen_set_label(end); - - return; -} +typedef void gen_helper_ldst_stride(TCGv_ptr, TCGv_ptr, TCGv, + TCGv, TCGv_env, TCGv_i32); static bool ldst_stride_trans(uint32_t vd, uint32_t rs1, uint32_t rs2, - uint32_t data, DisasContext *s, bool is_load) + uint32_t data, gen_helper_ldst_stride *fn, + DisasContext *s) { - if (!s->vstart_eq_zero) { - return false; - } - - TCGv dest = tcg_temp_new(); - - uint32_t nf = FIELD_EX32(data, VDATA, NF); - uint32_t vm = FIELD_EX32(data, VDATA, VM); - - /* Destination register and mask register */ - tcg_gen_addi_tl(dest, (TCGv)tcg_env, vreg_ofs(s, vd)); - - /* - * Select the appropriate load/tore to retrieve data from the vector - * register given a specific sew. - */ - static gen_tl_ldst * const ld_fns[4] = { - tcg_gen_ld8u_tl, tcg_gen_ld16u_tl, - tcg_gen_ld32u_tl, tcg_gen_ld_tl - }; - - static gen_tl_ldst * const st_fns[4] = { - tcg_gen_st8_tl, tcg_gen_st16_tl, - tcg_gen_st32_tl, tcg_gen_st_tl - }; + TCGv_ptr dest, mask; + TCGv base, stride; + TCGv_i32 desc; - gen_tl_ldst *ld_fn = ld_fns[s->sew]; - gen_tl_ldst *st_fn = st_fns[s->sew]; + dest = tcg_temp_new_ptr(); + mask = tcg_temp_new_ptr(); + base = get_gpr(s, rs1, EXT_NONE); + stride = get_gpr(s, rs2, EXT_NONE); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); - if (ld_fn == NULL || st_fn == NULL) { - return false; - } + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(mask, tcg_env, vreg_ofs(s, 0)); mark_vs_dirty(s); - gen_ldst_stride_main_loop(s, dest, rs1, rs2, vm, nf, ld_fn, st_fn, is_load); - - tcg_gen_movi_tl(cpu_vstart, 0); - - /* - * Set the tail bytes to 1 if tail agnostic: - */ - if (s->vta != 0 && is_load) { - gen_ldst_stride_tail_loop(s, dest, nf, st_fn); - } + fn(dest, mask, base, stride, tcg_env, desc); finalize_rvv_inst(s); return true; @@ -1152,6 +898,16 @@ static bool ldst_stride_trans(uint32_t vd, uint32_t rs1, uint32_t rs2, static bool ld_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) { uint32_t data = 0; + gen_helper_ldst_stride *fn; + static gen_helper_ldst_stride * const fns[4] = { + gen_helper_vlse8_v, gen_helper_vlse16_v, + gen_helper_vlse32_v, gen_helper_vlse64_v + }; + + fn = fns[eew]; + if (fn == NULL) { + return false; + } uint8_t emul = vext_get_emul(s, eew); data = FIELD_DP32(data, VDATA, VM, a->vm); @@ -1159,7 +915,7 @@ static bool ld_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) data = FIELD_DP32(data, VDATA, NF, a->nf); data = FIELD_DP32(data, VDATA, VTA, s->vta); data = FIELD_DP32(data, VDATA, VMA, s->vma); - return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, s, true); + return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s); } static bool ld_stride_check(DisasContext *s, arg_rnfvm* a, uint8_t eew) @@ -1177,13 +933,23 @@ GEN_VEXT_TRANS(vlse64_v, MO_64, rnfvm, ld_stride_op, ld_stride_check) static bool st_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) { uint32_t data = 0; + gen_helper_ldst_stride *fn; + static gen_helper_ldst_stride * const fns[4] = { + /* masked stride store */ + gen_helper_vsse8_v, gen_helper_vsse16_v, + gen_helper_vsse32_v, gen_helper_vsse64_v + }; uint8_t emul = vext_get_emul(s, eew); data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, emul); data = FIELD_DP32(data, VDATA, NF, a->nf); + fn = fns[eew]; + if (fn == NULL) { + return false; + } - return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, s, false); + return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s); } static bool st_stride_check(DisasContext *s, arg_rnfvm* a, uint8_t eew) @@ -1534,6 +1300,17 @@ GEN_LDST_WHOLE_TRANS(vs8r_v, int8_t, 8, false) *** Vector Integer Arithmetic Instructions */ +/* + * MAXSZ returns the maximum vector size can be operated in bytes, + * which is used in GVEC IR when vl_eq_vlmax flag is set to true + * to accelerate vector operation. + */ +static inline uint32_t MAXSZ(DisasContext *s) +{ + int max_sz = s->cfg_ptr->vlenb * 8; + return max_sz >> (3 - s->lmul); +} + static bool opivv_check(DisasContext *s, arg_rmrr *a) { return require_rvv(s) && From 30ef718423e8018723087cd17be0fd9c6dfa2e53 Mon Sep 17 00:00:00 2001 From: Xu Lu Date: Tue, 8 Jul 2025 14:07:20 +0800 Subject: [PATCH 0175/1794] target/riscv: Fix exception type when VU accesses supervisor CSRs When supervisor CSRs are accessed from VU-mode, a virtual instruction exception should be raised instead of an illegal instruction. Fixes: c1fbcecb3a (target/riscv: Fix csr number based privilege checking) Signed-off-by: Xu Lu Reviewed-by: Anup Patel Reviewed-by: Nutty Liu Message-ID: <20250708060720.7030-1-luxu.kernel@bytedance.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 8631be97c58d1..9bebfae3f031f 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -5577,7 +5577,7 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, csr_priv = get_field(csrno, 0x300); if (!env->debugger && (effective_priv < csr_priv)) { - if (csr_priv == (PRV_S + 1) && env->virt_enabled) { + if (csr_priv <= (PRV_S + 1) && env->virt_enabled) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } return RISCV_EXCP_ILLEGAL_INST; From e443ba03361b63218e6c3aa4f73d2cb5b9b1d372 Mon Sep 17 00:00:00 2001 From: Jay Chang Date: Tue, 1 Jul 2025 11:00:20 +0800 Subject: [PATCH 0176/1794] target/riscv: Restrict mideleg/medeleg/medelegh access to S-mode harts RISC-V Privileged Spec states: "In harts with S-mode, the medeleg and mideleg registers must exist, and setting a bit in medeleg or mideleg will delegate the corresponding trap , when occurring in S-mode or U-mode, to the S-mode trap handler. In harts without S-mode, the medeleg and mideleg registers should not exist." Add smode predicate to ensure these CSRs are only accessible when S-mode is supported. Reviewed-by: Frank Chang Reviewed-by: Alistair Francis Signed-off-by: Jay Chang Reviewed-by: Nutty Liu Message-ID: <20250701030021.99218-2-jay.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 9bebfae3f031f..5a6de074867e0 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -5862,8 +5862,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { NULL, read_mstatus_i128 }, [CSR_MISA] = { "misa", any, read_misa, write_misa, NULL, read_misa_i128 }, - [CSR_MIDELEG] = { "mideleg", any, NULL, NULL, rmw_mideleg }, - [CSR_MEDELEG] = { "medeleg", any, read_medeleg, write_medeleg }, + [CSR_MIDELEG] = { "mideleg", smode, NULL, NULL, rmw_mideleg }, + [CSR_MEDELEG] = { "medeleg", smode, read_medeleg, write_medeleg }, [CSR_MIE] = { "mie", any, NULL, NULL, rmw_mie }, [CSR_MTVEC] = { "mtvec", any, read_mtvec, write_mtvec }, [CSR_MCOUNTEREN] = { "mcounteren", umode, read_mcounteren, @@ -5871,7 +5871,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MSTATUSH] = { "mstatush", any32, read_mstatush, write_mstatush }, - [CSR_MEDELEGH] = { "medelegh", any32, read_zero, write_ignore, + [CSR_MEDELEGH] = { "medelegh", smode32, read_zero, write_ignore, .min_priv_ver = PRIV_VERSION_1_13_0 }, [CSR_HEDELEGH] = { "hedelegh", hmode32, read_hedelegh, write_hedelegh, .min_priv_ver = PRIV_VERSION_1_13_0 }, From 86bc3a0abf10072081cddd8dff25aa72c60e67b8 Mon Sep 17 00:00:00 2001 From: Jay Chang Date: Tue, 1 Jul 2025 11:00:21 +0800 Subject: [PATCH 0177/1794] target/riscv: Restrict midelegh access to S-mode harts RISC-V AIA Spec states: "For a machine-level environment, extension Smaia encompasses all added CSRs and all modifications to interrupt response behavior that the AIA specifies for a hart, over all privilege levels. For a supervisor-level environment, extension Ssaia is essentially the same as Smaia except excluding the machine-level CSRs and behavior not directly visible to supervisor level." Since midelegh is an AIA machine-mode CSR, add Smaia extension check in aia_smode32 predicate. Reviewed-by: Frank Chang Reviewed-by: Alistair Francis Signed-off-by: Jay Chang Reviewed-by: Nutty Liu Message-ID: <20250701030021.99218-3-jay.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 5a6de074867e0..8842e07a735a5 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -374,8 +374,11 @@ static RISCVException aia_smode(CPURISCVState *env, int csrno) static RISCVException aia_smode32(CPURISCVState *env, int csrno) { int ret; + int csr_priv = get_field(csrno, 0x300); - if (!riscv_cpu_cfg(env)->ext_ssaia) { + if (csr_priv == PRV_M && !riscv_cpu_cfg(env)->ext_smaia) { + return RISCV_EXCP_ILLEGAL_INST; + } else if (!riscv_cpu_cfg(env)->ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } @@ -5911,7 +5914,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MVIP] = { "mvip", aia_any, NULL, NULL, rmw_mvip }, /* Machine-Level High-Half CSRs (AIA) */ - [CSR_MIDELEGH] = { "midelegh", aia_any32, NULL, NULL, rmw_midelegh }, + [CSR_MIDELEGH] = { "midelegh", aia_smode32, NULL, NULL, rmw_midelegh }, [CSR_MIEH] = { "mieh", aia_any32, NULL, NULL, rmw_mieh }, [CSR_MVIENH] = { "mvienh", aia_any32, NULL, NULL, rmw_mvienh }, [CSR_MVIPH] = { "mviph", aia_any32, NULL, NULL, rmw_mviph }, From caab7ac83507e3e9a5fe2f37be5cfa759e766ba2 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 14 Jul 2025 09:54:46 +0800 Subject: [PATCH 0178/1794] target/loongarch: Fix valid virtual address checking On LoongArch64 system, the high 32 bit of 64 bit virtual address should be 0x00000[0-7]yyy or 0xffff8yyy. The bit from 47 to 63 should be all 0 or all 1. Function get_physical_address() only checks bit 48 to 63, there will be problem with the following test case. On physical machine, there is bus error report and program exits abnormally. However on qemu TCG system emulation mode, the program runs normally. The virtual address 0xffff000000000000ULL + addr and addr are treated the same on TLB entry checking. This patch fixes this issue. void main() { void *addr, *addr1; int val; addr = malloc(100); *(int *)addr = 1; addr1 = 0xffff000000000000ULL + addr; val = *(int *)addr1; printf("val %d \n", val); } Cc: qemu-stable@nongnu.org Signed-off-by: Bibo Mao Acked-by: Song Gao Reviewed-by: Song Gao Message-ID: <20250714015446.746163-1-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/cpu_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index e172b11ce1546..b5f732f15b7bd 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -196,8 +196,8 @@ int get_physical_address(CPULoongArchState *env, hwaddr *physical, } /* Check valid extension */ - addr_high = sextract64(address, TARGET_VIRT_ADDR_SPACE_BITS, 16); - if (!(addr_high == 0 || addr_high == -1)) { + addr_high = (int64_t)address >> (TARGET_VIRT_ADDR_SPACE_BITS - 1); + if (!(addr_high == 0 || addr_high == -1ULL)) { return TLBRET_BADADDR; } From 31995cc4087123a13e9345153e0c39ffb44b9277 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 25 Jul 2025 16:12:13 +0800 Subject: [PATCH 0179/1794] hw/intc/loongarch_ipi: Fix start fail with smp cpu < smp maxcpus on KVM QEMU start failed when smp cpu < smp maxcpus , because qemu send a NULL cpu to KVM, this patch adds a check for kvm_ipi_access_regs() to fix it. run with '-smp 1,maxcpus=4,sockets=4,cores=1,threads=1' we got: Unexpected error in kvm_device_access() at ../accel/kvm/kvm-all.c:3477: qemu-system-loongarch64: KVM_SET_DEVICE_ATTR failed: Group 1073741825 attr 0x0000000000010000: Invalid argument Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-ID: <20250725081213.3867592-1-gaosong@loongson.cn> --- hw/intc/loongarch_ipi_kvm.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/hw/intc/loongarch_ipi_kvm.c b/hw/intc/loongarch_ipi_kvm.c index 4cb3acc921f8b..dd4c367abf9d0 100644 --- a/hw/intc/loongarch_ipi_kvm.c +++ b/hw/intc/loongarch_ipi_kvm.c @@ -23,36 +23,41 @@ static void kvm_ipi_access_regs(void *opaque, bool write) LoongarchIPIState *lis = LOONGARCH_IPI(opaque); IPICore *core; uint64_t attr; - int cpu, fd = lis->dev_fd; + int i, cpu_index, fd = lis->dev_fd; if (fd == 0) { return; } - for (cpu = 0; cpu < ipi->num_cpu; cpu++) { - core = &ipi->cpu[cpu]; - attr = (cpu << 16) | CORE_STATUS_OFF; + for (i = 0; i < ipi->num_cpu; i++) { + core = &ipi->cpu[i]; + if (core->cpu == NULL) { + continue; + } + cpu_index = i; + + attr = (cpu_index << 16) | CORE_STATUS_OFF; kvm_ipi_access_reg(fd, attr, &core->status, write); - attr = (cpu << 16) | CORE_EN_OFF; + attr = (cpu_index << 16) | CORE_EN_OFF; kvm_ipi_access_reg(fd, attr, &core->en, write); - attr = (cpu << 16) | CORE_SET_OFF; + attr = (cpu_index << 16) | CORE_SET_OFF; kvm_ipi_access_reg(fd, attr, &core->set, write); - attr = (cpu << 16) | CORE_CLEAR_OFF; + attr = (cpu_index << 16) | CORE_CLEAR_OFF; kvm_ipi_access_reg(fd, attr, &core->clear, write); - attr = (cpu << 16) | CORE_BUF_20; + attr = (cpu_index << 16) | CORE_BUF_20; kvm_ipi_access_reg(fd, attr, &core->buf[0], write); - attr = (cpu << 16) | CORE_BUF_28; + attr = (cpu_index << 16) | CORE_BUF_28; kvm_ipi_access_reg(fd, attr, &core->buf[2], write); - attr = (cpu << 16) | CORE_BUF_30; + attr = (cpu_index << 16) | CORE_BUF_30; kvm_ipi_access_reg(fd, attr, &core->buf[4], write); - attr = (cpu << 16) | CORE_BUF_38; + attr = (cpu_index << 16) | CORE_BUF_38; kvm_ipi_access_reg(fd, attr, &core->buf[6], write); } } From cd9f752fee75238f842a91be1146c988bd16a010 Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Fri, 25 Jul 2025 10:01:36 -0700 Subject: [PATCH 0180/1794] target/arm: add support for 64-bit PMCCNTR in AArch32 mode In the PMUv3, a new AArch32 64-bit (MCRR/MRRC) accessor for the PMCCNTR was added. In QEMU we forgot to implement this, so only provide the 32-bit accessor. Since we have a 64-bit PMCCNTR sysreg for AArch64, adding the 64-bit AArch32 version is easy. We add the PMCCNTR to the v8_cp_reginfo because PMUv3 was added in the ARMv8 architecture. This is consistent with how we handle the existing PMCCNTR support, where we always implement it for all v7 CPUs. This is arguably something we should clean up so it is gated on ARM_FEATURE_PMU and/or an ID register check for the relevant PMU version, but we should do that as its own tidyup rather than being inconsistent between this PMCCNTR accessor and the others. Since the register name is the same as the 32-bit PMCCNTR, we set ARM_CP_NO_GDB on the 32-bit one to avoid generating an invalid GDB XML. See https://developer.arm.com/documentation/ddi0601/2024-06/AArch32-Registers/PMCCNTR--Performance-Monitors-Cycle-Count-Register?lang=en Note for potential backporting: * this code in cpregs-pmu.c will be in helper.c on stable branches that don't have commit ae2086426d37 Cc: qemu-stable@nongnu.org Signed-off-by: Alex Richardson Message-id: 20250725170136.145116-1-alexrichardson@google.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpregs-pmu.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/target/arm/cpregs-pmu.c b/target/arm/cpregs-pmu.c index 0f295b1376cc9..9c4431c18bae8 100644 --- a/target/arm/cpregs-pmu.c +++ b/target/arm/cpregs-pmu.c @@ -1067,11 +1067,6 @@ static const ARMCPRegInfo v7_pm_reginfo[] = { .fgt = FGT_PMSELR_EL0, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmselr), .writefn = pmselr_write, .raw_writefn = raw_write, }, - { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0, - .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_ALIAS | ARM_CP_IO, - .fgt = FGT_PMCCNTR_EL0, - .readfn = pmccntr_read, .writefn = pmccntr_write32, - .accessfn = pmreg_access_ccntr }, { .name = "PMCCNTR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 0, .access = PL0_RW, .accessfn = pmreg_access_ccntr, @@ -1211,6 +1206,23 @@ void define_pm_cpregs(ARMCPU *cpu) define_one_arm_cp_reg(cpu, &pmcr); define_one_arm_cp_reg(cpu, &pmcr64); define_arm_cp_regs(cpu, v7_pm_reginfo); + /* + * 32-bit AArch32 PMCCNTR. We don't expose this to GDB if the + * new-in-v8 PMUv3 64-bit AArch32 PMCCNTR register is implemented + * (as that will provide the GDB user's view of "PMCCNTR"). + */ + ARMCPRegInfo pmccntr = { + .name = "PMCCNTR", + .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0, + .access = PL0_RW, .accessfn = pmreg_access_ccntr, + .resetvalue = 0, .type = ARM_CP_ALIAS | ARM_CP_IO, + .fgt = FGT_PMCCNTR_EL0, + .readfn = pmccntr_read, .writefn = pmccntr_write32, + }; + if (arm_feature(env, ARM_FEATURE_V8)) { + pmccntr.type |= ARM_CP_NO_GDB; + } + define_one_arm_cp_reg(cpu, &pmccntr); for (unsigned i = 0, pmcrn = pmu_num_counters(env); i < pmcrn; i++) { g_autofree char *pmevcntr_name = g_strdup_printf("PMEVCNTR%d", i); @@ -1276,6 +1288,13 @@ void define_pm_cpregs(ARMCPU *cpu) .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, .fgt = FGT_PMCEIDN_EL0, .resetvalue = cpu->pmceid1 }, + /* AArch32 64-bit PMCCNTR view: added in PMUv3 with Armv8 */ + { .name = "PMCCNTR", .state = ARM_CP_STATE_AA32, + .cp = 15, .crm = 9, .opc1 = 0, + .access = PL0_RW, .accessfn = pmreg_access_ccntr, .resetvalue = 0, + .type = ARM_CP_ALIAS | ARM_CP_IO | ARM_CP_64BIT, + .fgt = FGT_PMCCNTR_EL0, .readfn = pmccntr_read, + .writefn = pmccntr_write, }, }; define_arm_cp_regs(cpu, v8_pm_reginfo); } From 6fcf5ebafad65adc19a616260ca7dc90005785d1 Mon Sep 17 00:00:00 2001 From: Jonah Palmer Date: Mon, 21 Jul 2025 15:02:08 +0000 Subject: [PATCH 0181/1794] virtio: fix off-by-one and invalid access in virtqueue_ordered_fill Commit b44135daa372 introduced virtqueue_ordered_fill for VIRTIO_F_IN_ORDER support but had a few issues: * Conditional while loop used 'steps <= max_steps' but should've been 'steps < max_steps' since reaching steps == max_steps would indicate that we didn't find an element, which is an error. Without this change, the code would attempt to read invalid data at an index outside of our search range. * Incremented 'steps' using the next chain's ndescs instead of the current one. This patch corrects the loop bounds and synchronizes 'steps' and index increments. We also add a defensive sanity check against malicious or invalid descriptor counts to avoid a potential infinite loop and DoS. Fixes: b44135daa372 ("virtio: virtqueue_ordered_fill - VIRTIO_F_IN_ORDER support") Reported-by: terrynini Signed-off-by: Jonah Palmer Message-Id: <20250721150208.2409779-1-jonah.palmer@oracle.com> Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 2ab1d20769495..9a81ad912e013 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -938,18 +938,18 @@ static void virtqueue_packed_fill(VirtQueue *vq, const VirtQueueElement *elem, static void virtqueue_ordered_fill(VirtQueue *vq, const VirtQueueElement *elem, unsigned int len) { - unsigned int i, steps, max_steps; + unsigned int i, steps, max_steps, ndescs; i = vq->used_idx % vq->vring.num; steps = 0; /* - * We shouldn't need to increase 'i' by more than the distance - * between used_idx and last_avail_idx. + * We shouldn't need to increase 'i' by more than or equal to + * the distance between used_idx and last_avail_idx (max_steps). */ max_steps = (vq->last_avail_idx - vq->used_idx) % vq->vring.num; /* Search for element in vq->used_elems */ - while (steps <= max_steps) { + while (steps < max_steps) { /* Found element, set length and mark as filled */ if (vq->used_elems[i].index == elem->index) { vq->used_elems[i].len = len; @@ -957,8 +957,18 @@ static void virtqueue_ordered_fill(VirtQueue *vq, const VirtQueueElement *elem, break; } - i += vq->used_elems[i].ndescs; - steps += vq->used_elems[i].ndescs; + ndescs = vq->used_elems[i].ndescs; + + /* Defensive sanity check */ + if (unlikely(ndescs == 0 || ndescs > vq->vring.num)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s invalid ndescs %u at position %u\n", + __func__, vq->vdev->name, ndescs, i); + return; + } + + i += ndescs; + steps += ndescs; if (i >= vq->vring.num) { i -= vq->vring.num; From c1997099dc26d95eb9f2249f2894aabf4cf0bf8b Mon Sep 17 00:00:00 2001 From: Hanna Czenczek Date: Thu, 24 Jul 2025 14:59:27 +0200 Subject: [PATCH 0182/1794] vhost: Do not abort on log-start error Commit 3688fec8923 ("memory: Add Error** argument to .log_global_start() handler") enabled vhost_log_global_start() to return a proper error, but did not change it to do so; instead, it still aborts the whole process on error. This crash can be reproduced by e.g. killing a virtiofsd daemon before initiating migration. In such a case, qemu should not crash, but just make the attempted migration fail. Buglink: https://issues.redhat.com/browse/RHEL-94534 Reported-by: Tingting Mao Signed-off-by: Hanna Czenczek Message-Id: <20250724125928.61045-2-hreitz@redhat.com> Reviewed-by: Manos Pitsidianakis Reviewed-by: Stefano Garzarella Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index c30ea1156e811..05ad5de629b47 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1110,7 +1110,8 @@ static bool vhost_log_global_start(MemoryListener *listener, Error **errp) r = vhost_migration_log(listener, true); if (r < 0) { - abort(); + error_setg_errno(errp, -r, "vhost: Failed to start logging"); + return false; } return true; } From d63c388dadb7717f6391e1bb7f11728c0c1a3e36 Mon Sep 17 00:00:00 2001 From: Hanna Czenczek Date: Thu, 24 Jul 2025 14:59:28 +0200 Subject: [PATCH 0183/1794] vhost: Do not abort on log-stop error Failing to stop logging in a vhost device is not exactly fatal. We can log such an error, but there is no need to abort the whole qemu process because of it. Signed-off-by: Hanna Czenczek Message-Id: <20250724125928.61045-3-hreitz@redhat.com> Reviewed-by: Manos Pitsidianakis Reviewed-by: Stefano Garzarella Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 05ad5de629b47..6557c58d12af2 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1122,7 +1122,8 @@ static void vhost_log_global_stop(MemoryListener *listener) r = vhost_migration_log(listener, false); if (r < 0) { - abort(); + /* Not fatal, so report it, but take no further action */ + warn_report("vhost: Failed to stop logging"); } } From 6071d13c6a37493a6b26e1609b09a98aa058038a Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 27 Jul 2025 15:22:36 +0900 Subject: [PATCH 0184/1794] virtio-net: Fix VLAN filter table reset timing Problem ------- The expected initial state of the table depends on feature negotiation: With VIRTIO_NET_F_CTRL_VLAN: The table must be empty in accordance with the specification. Without VIRTIO_NET_F_CTRL_VLAN: The table must be filled to permit all VLAN traffic. Prior to commit 06b636a1e2ad ("virtio-net: do not reset vlan filtering at set_features"), virtio_net_set_features() always reset the VLAN table. That commit changed the behavior to skip table reset when VIRTIO_NET_F_CTRL_VLAN was negotiated, assuming the table would be properly cleared during device reset and remain stable. However, this assumption breaks when a driver renegotiates features: 1. Initial negotiation without VIRTIO_NET_F_CTRL_VLAN (table filled) 2. Renegotiation with VIRTIO_NET_F_CTRL_VLAN (table will not be cleared) The problem was exacerbated by commit 0caed25cd171 ("virtio: Call set_features during reset"), which triggered virtio_net_set_features() during device reset, exposing the bug whenever VIRTIO_NET_F_CTRL_VLAN was negotiated after a device reset. Solution -------- Fix the issue by initializing the table when virtio_net_set_features() is called to change the VIRTIO_NET_F_CTRL_VLAN bit of vdev->guest_features. This approach ensures the correct table state regardless of feature negotiation sequence by performing initialization in virtio_net_set_features() as QEMU did prior to commit 06b636a1e2ad ("virtio-net: do not reset vlan filtering at set_features"). This change still preserves the goal of the commit, which was to avoid resetting the table during migration, by checking whether the VIRTIO_NET_F_CTRL_VLAN bit of vdev->guest_features is being changed; vdev->guest_features is set before virtio_net_set_features() gets called during migration. It also avoids resetting the table when the driver sets a feature bitmask with no change for the VIRTIO_NET_F_CTRL_VLAN bit, which makes the operation idempotent and its semantics cleaner. Additionally, this change ensures the table is initialized after feature negotiation and before the DRIVER_OK status bit being set for compatibility with the Linux driver before commit 50c0ada627f5 ("virtio-net: fix race between ndo_open() and virtio_device_ready()"), which did not ensure to set the DRIVER_OK status bit before modifying the table. Fixes: 06b636a1e2ad ("virtio-net: do not reset vlan filtering at set_features") Cc: qemu-stable@nongnu.org Reported-by: Konstantin Shkolnyy Signed-off-by: Akihiko Odaki Tested-by: Konstantin Shkolnyy Tested-by: Lei Yang Message-Id: <20250727-vlan-v3-1-bbee738619b1@rsg.ci.i.u-tokyo.ac.jp> Tested-by: Konstantin Shkolnyy Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/virtio-net.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index c4c49b0f9caa1..6b5b5dace334a 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -929,8 +929,9 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) vhost_net_save_acked_features(nc->peer); } - if (!virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) { - memset(n->vlans, 0xff, MAX_VLAN >> 3); + if (virtio_has_feature(vdev->guest_features ^ features, VIRTIO_NET_F_CTRL_VLAN)) { + bool vlan = virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN); + memset(n->vlans, vlan ? 0 : 0xff, MAX_VLAN >> 3); } if (virtio_has_feature(features, VIRTIO_NET_F_STANDBY)) { @@ -3942,6 +3943,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); n->vlans = g_malloc0(MAX_VLAN >> 3); + memset(n->vlans, 0xff, MAX_VLAN >> 3); nc = qemu_get_queue(n->nic); nc->rxfilter_notify_enabled = 1; @@ -4041,7 +4043,6 @@ static void virtio_net_reset(VirtIODevice *vdev) memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac)); qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); - memset(n->vlans, 0, MAX_VLAN >> 3); /* Flush any async TX */ for (i = 0; i < n->max_queue_pairs; i++) { From cad9aa6fbdccd95e56e10cfa57c354a20a333717 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 27 Jul 2025 15:50:08 +0900 Subject: [PATCH 0185/1794] pcie_sriov: Fix configuration and state synchronization Fix issues in PCIe SR-IOV configuration register handling that caused inconsistent internal state due to improper write mask handling and incorrect migration behavior. Two main problems were identified: 1. VF Enable bit write mask handling: pcie_sriov_config_write() incorrectly assumed that its val parameter was already masked, causing it to ignore the actual write mask. This led to the VF Enable bit being processed even when masked, resulting in incorrect VF registration/unregistration. It is identified as CVE-2025-54567. 2. Migration state inconsistency: pcie_sriov_pf_post_load() unconditionally called register_vfs() regardless of the VF Enable bit state, creating inconsistent internal state when VFs should not be enabled. Additionally, it failed to properly update the NumVFs write mask based on the current configuration. It is identified as CVE-2025-54566. Root cause analysis revealed that both functions relied on incorrect special-case assumptions instead of properly reading and consuming the actual configuration values. This change introduces a unified consume_config() function that reads actual configuration values and synchronize the internal state without special-case assumptions. The solution only adds register read overhead in non-hot-path code while ensuring correct SR-IOV state management across configuration writes and migration scenarios. Fixes: 5e7dd17e4348 ("pcie_sriov: Remove num_vfs from PCIESriovPF") Fixes: f9efcd47110d ("pcie_sriov: Register VFs after migration") Fixes: CVE-2025-54566 Fixes: CVE-2025-54567 Cc: qemu-stable@nongnu.org Reported-by: Corentin BAYET Signed-off-by: Akihiko Odaki Message-Id: <20250727-wmask-v2-1-394910b1c0b6@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie_sriov.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 3ad18744f4a8e..8a4bf0d6f7c0c 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -64,6 +64,27 @@ static void unregister_vfs(PCIDevice *dev) pci_set_word(dev->wmask + dev->exp.sriov_cap + PCI_SRIOV_NUM_VF, 0xffff); } +static void consume_config(PCIDevice *dev) +{ + uint8_t *cfg = dev->config + dev->exp.sriov_cap; + + if (pci_get_word(cfg + PCI_SRIOV_CTRL) & PCI_SRIOV_CTRL_VFE) { + register_vfs(dev); + } else { + uint8_t *wmask = dev->wmask + dev->exp.sriov_cap; + uint16_t num_vfs = pci_get_word(cfg + PCI_SRIOV_NUM_VF); + uint16_t wmask_val = PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI; + + unregister_vfs(dev); + + if (num_vfs <= pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)) { + wmask_val |= PCI_SRIOV_CTRL_VFE; + } + + pci_set_word(wmask + PCI_SRIOV_CTRL, wmask_val); + } +} + static bool pcie_sriov_pf_init_common(PCIDevice *dev, uint16_t offset, uint16_t vf_dev_id, uint16_t init_vfs, uint16_t total_vfs, uint16_t vf_offset, @@ -416,30 +437,13 @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, trace_sriov_config_write(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), off, val, len); - if (range_covers_byte(off, len, PCI_SRIOV_CTRL)) { - if (val & PCI_SRIOV_CTRL_VFE) { - register_vfs(dev); - } else { - unregister_vfs(dev); - } - } else if (range_covers_byte(off, len, PCI_SRIOV_NUM_VF)) { - uint8_t *cfg = dev->config + sriov_cap; - uint8_t *wmask = dev->wmask + sriov_cap; - uint16_t num_vfs = pci_get_word(cfg + PCI_SRIOV_NUM_VF); - uint16_t wmask_val = PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI; - - if (num_vfs <= pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)) { - wmask_val |= PCI_SRIOV_CTRL_VFE; - } - - pci_set_word(wmask + PCI_SRIOV_CTRL, wmask_val); - } + consume_config(dev); } void pcie_sriov_pf_post_load(PCIDevice *dev) { if (dev->exp.sriov_cap) { - register_vfs(dev); + consume_config(dev); } } From 96c75abc872c1a90c8e1f4a4d2ed35e89a1befe9 Mon Sep 17 00:00:00 2001 From: Sairaj Kodilkar Date: Fri, 1 Aug 2025 11:35:02 +0530 Subject: [PATCH 0186/1794] hw/i386/amd_iommu: Fix MMIO register write tracing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define separate functions to trace MMIO write accesses instead of using `trace_amdvi_mmio_read()` for both read and write. Signed-off-by: Sairaj Kodilkar Reviewed-by: Vasant Hegde Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250801060507.3382-2-sarunkod@amd.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 5a24c17548d45..7fb0bb68f008e 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -592,18 +592,31 @@ static void amdvi_cmdbuf_run(AMDVIState *s) } } -static void amdvi_mmio_trace(hwaddr addr, unsigned size) +static inline uint8_t amdvi_mmio_get_index(hwaddr addr) { uint8_t index = (addr & ~0x2000) / 8; if ((addr & 0x2000)) { /* high table */ index = index >= AMDVI_MMIO_REGS_HIGH ? AMDVI_MMIO_REGS_HIGH : index; - trace_amdvi_mmio_read(amdvi_mmio_high[index], addr, size, addr & ~0x07); } else { index = index >= AMDVI_MMIO_REGS_LOW ? AMDVI_MMIO_REGS_LOW : index; - trace_amdvi_mmio_read(amdvi_mmio_low[index], addr, size, addr & ~0x07); } + + return index; +} + +static void amdvi_mmio_trace_read(hwaddr addr, unsigned size) +{ + uint8_t index = amdvi_mmio_get_index(addr); + trace_amdvi_mmio_read(amdvi_mmio_low[index], addr, size, addr & ~0x07); +} + +static void amdvi_mmio_trace_write(hwaddr addr, unsigned size, uint64_t val) +{ + uint8_t index = amdvi_mmio_get_index(addr); + trace_amdvi_mmio_write(amdvi_mmio_low[index], addr, size, val, + addr & ~0x07); } static uint64_t amdvi_mmio_read(void *opaque, hwaddr addr, unsigned size) @@ -623,7 +636,7 @@ static uint64_t amdvi_mmio_read(void *opaque, hwaddr addr, unsigned size) } else if (size == 8) { val = amdvi_readq(s, addr); } - amdvi_mmio_trace(addr, size); + amdvi_mmio_trace_read(addr, size); return val; } @@ -770,7 +783,7 @@ static void amdvi_mmio_write(void *opaque, hwaddr addr, uint64_t val, return; } - amdvi_mmio_trace(addr, size); + amdvi_mmio_trace_write(addr, size, val); switch (addr & ~0x07) { case AMDVI_MMIO_CONTROL: amdvi_mmio_reg_write(s, size, val, addr); From 47d50cc421b832650822d73431d920bb8a80bc38 Mon Sep 17 00:00:00 2001 From: Sairaj Kodilkar Date: Fri, 1 Aug 2025 11:35:03 +0530 Subject: [PATCH 0187/1794] hw/i386/amd_iommu: Remove unused and wrongly set ats_enabled field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ats_enabled field is set using HTTUNEN, which is wrong. Fix this by removing the field as it is never used. MST: includes a tweak suggested by Philippe Fixes: d29a09ca68428 ("hw/i386: Introduce AMD IOMMU") Signed-off-by: Sairaj Kodilkar Reviewed-by: Vasant Hegde Message-Id: <20250801060507.3382-3-sarunkod@amd.com> Message-ID: <948a6ac3-ded9-475b-8c45-9d36220b442b@linaro.org> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 5 ++--- hw/i386/amd_iommu.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 7fb0bb68f008e..037e78056d0a6 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -646,7 +646,6 @@ static void amdvi_handle_control_write(AMDVIState *s) unsigned long control = amdvi_readq(s, AMDVI_MMIO_CONTROL); s->enabled = !!(control & AMDVI_MMIO_CONTROL_AMDVIEN); - s->ats_enabled = !!(control & AMDVI_MMIO_CONTROL_HTTUNEN); s->evtlog_enabled = s->enabled && !!(control & AMDVI_MMIO_CONTROL_EVENTLOGEN); @@ -1555,7 +1554,6 @@ static void amdvi_init(AMDVIState *s) s->excl_allow = false; s->mmio_enabled = false; s->enabled = false; - s->ats_enabled = false; s->cmdbuf_enabled = false; /* reset MMIO */ @@ -1626,7 +1624,8 @@ static const VMStateDescription vmstate_amdvi_sysbus_migratable = { /* Updated in amdvi_handle_control_write() */ VMSTATE_BOOL(enabled, AMDVIState), VMSTATE_BOOL(ga_enabled, AMDVIState), - VMSTATE_BOOL(ats_enabled, AMDVIState), + /* bool ats_enabled is obsolete */ + VMSTATE_UNUSED(1), /* was ats_enabled */ VMSTATE_BOOL(cmdbuf_enabled, AMDVIState), VMSTATE_BOOL(completion_wait_intr, AMDVIState), VMSTATE_BOOL(evtlog_enabled, AMDVIState), diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 8b42913ed8dab..67078c6f1e224 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -322,7 +322,6 @@ struct AMDVIState { uint64_t mmio_addr; bool enabled; /* IOMMU enabled */ - bool ats_enabled; /* address translation enabled */ bool cmdbuf_enabled; /* command buffer enabled */ bool evtlog_enabled; /* event log enabled */ bool excl_enabled; From a7842d94067cddc80b47ac42fb6e49e2fc02a3c5 Mon Sep 17 00:00:00 2001 From: Sairaj Kodilkar Date: Fri, 1 Aug 2025 11:35:04 +0530 Subject: [PATCH 0188/1794] hw/i386/amd_iommu: Move IOAPIC memory region initialization to the end Setting up IOAPIC memory region requires mr_sys and mr_ir. Currently these two memory regions are setup after the initializing the IOAPIC memory region, which cause `amdvi_host_dma_iommu()` to use unitialized mr_sys and mr_ir. Move the IOAPIC memory region initialization to the end in order to use the mr_sys and mr_ir regions after they are fully initialized. Fixes: 577c470f4326 ("x86_iommu/amd: Prepare for interrupt remap support") Signed-off-by: Sairaj Kodilkar Reviewed-by: Vasant Hegde Message-Id: <20250801060507.3382-4-sarunkod@amd.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 037e78056d0a6..7308611bf16a5 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1698,9 +1698,6 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) s->iotlb = g_hash_table_new_full(amdvi_uint64_hash, amdvi_uint64_equal, g_free, g_free); - /* Pseudo address space under root PCI bus. */ - x86ms->ioapic_as = amdvi_host_dma_iommu(bus, s, AMDVI_IOAPIC_SB_DEVID); - /* set up MMIO */ memory_region_init_io(&s->mr_mmio, OBJECT(s), &mmio_mem_ops, s, "amdvi-mmio", AMDVI_MMIO_SIZE); @@ -1723,6 +1720,9 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) memory_region_add_subregion_overlap(&s->mr_sys, AMDVI_INT_ADDR_FIRST, &s->mr_ir, 1); + /* Pseudo address space under root PCI bus. */ + x86ms->ioapic_as = amdvi_host_dma_iommu(bus, s, AMDVI_IOAPIC_SB_DEVID); + if (kvm_enabled() && x86ms->apic_id_limit > 255 && !s->xtsup) { error_report("AMD IOMMU with x2APIC configuration requires xtsup=on"); exit(EXIT_FAILURE); From 47d3b32d6fb1c6ec8afb78d12d2420dbbb4c3499 Mon Sep 17 00:00:00 2001 From: Sairaj Kodilkar Date: Fri, 1 Aug 2025 11:35:05 +0530 Subject: [PATCH 0189/1794] hw/i386/amd_iommu: Fix amdvi_write*() amdvi_write*() function do not preserve the older values of W1C bits in the MMIO register. This results in all W1C bits set to 0, when guest tries to reset a single bit by writing 1 to it. Fix this by preserving W1C bits in the old value of the MMIO register. Fixes: d29a09ca68428 ("hw/i386: Introduce AMD IOMMU") Suggested-by: Ethan MILON Signed-off-by: Sairaj Kodilkar Message-Id: <20250801060507.3382-5-sarunkod@amd.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 7308611bf16a5..c9c32cf7b0ab4 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -123,8 +123,13 @@ static void amdvi_writew(AMDVIState *s, hwaddr addr, uint16_t val) uint16_t romask = lduw_le_p(&s->romask[addr]); uint16_t w1cmask = lduw_le_p(&s->w1cmask[addr]); uint16_t oldval = lduw_le_p(&s->mmior[addr]); + + uint16_t oldval_preserved = oldval & (romask | w1cmask); + uint16_t newval_write = val & ~romask; + uint16_t newval_w1c_set = val & w1cmask; + stw_le_p(&s->mmior[addr], - ((oldval & romask) | (val & ~romask)) & ~(val & w1cmask)); + (oldval_preserved | newval_write) & ~newval_w1c_set); } static void amdvi_writel(AMDVIState *s, hwaddr addr, uint32_t val) @@ -132,8 +137,13 @@ static void amdvi_writel(AMDVIState *s, hwaddr addr, uint32_t val) uint32_t romask = ldl_le_p(&s->romask[addr]); uint32_t w1cmask = ldl_le_p(&s->w1cmask[addr]); uint32_t oldval = ldl_le_p(&s->mmior[addr]); + + uint32_t oldval_preserved = oldval & (romask | w1cmask); + uint32_t newval_write = val & ~romask; + uint32_t newval_w1c_set = val & w1cmask; + stl_le_p(&s->mmior[addr], - ((oldval & romask) | (val & ~romask)) & ~(val & w1cmask)); + (oldval_preserved | newval_write) & ~newval_w1c_set); } static void amdvi_writeq(AMDVIState *s, hwaddr addr, uint64_t val) @@ -141,8 +151,13 @@ static void amdvi_writeq(AMDVIState *s, hwaddr addr, uint64_t val) uint64_t romask = ldq_le_p(&s->romask[addr]); uint64_t w1cmask = ldq_le_p(&s->w1cmask[addr]); uint64_t oldval = ldq_le_p(&s->mmior[addr]); + + uint64_t oldval_preserved = oldval & (romask | w1cmask); + uint64_t newval_write = val & ~romask; + uint64_t newval_w1c_set = val & w1cmask; + stq_le_p(&s->mmior[addr], - ((oldval & romask) | (val & ~romask)) & ~(val & w1cmask)); + (oldval_preserved | newval_write) & ~newval_w1c_set); } /* OR a 64-bit register with a 64-bit value */ From 10690920b0efb3ed8b166443bae8077104bb129d Mon Sep 17 00:00:00 2001 From: Sairaj Kodilkar Date: Fri, 1 Aug 2025 11:35:06 +0530 Subject: [PATCH 0190/1794] hw/i386/amd_iommu: Support MMIO writes to the status register Support the writes to the status register so that guest can reset the EventOverflow, EventLogInt, ComWaitIntr, etc bits after servicing the respective interrupt. Signed-off-by: Sairaj Kodilkar Reviewed-by: Vasant Hegde Message-Id: <20250801060507.3382-6-sarunkod@amd.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index c9c32cf7b0ab4..6925085d29e33 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -862,6 +862,9 @@ static void amdvi_mmio_write(void *opaque, hwaddr addr, uint64_t val, amdvi_mmio_reg_write(s, size, val, addr); amdvi_handle_pprtail_write(s); break; + case AMDVI_MMIO_STATUS: + amdvi_mmio_reg_write(s, size, val, addr); + break; } } From c0ef803a879b97f3d269348c968fb3874c2761f6 Mon Sep 17 00:00:00 2001 From: Sairaj Kodilkar Date: Fri, 1 Aug 2025 11:35:07 +0530 Subject: [PATCH 0191/1794] hw/i386/amd_iommu: Fix event log generation Current event logging code is broken, because of following issues 1. The code uses '|' instead of '&' to test the bit field, which causes vIOMMU to generate overflow interrupt for every log entry. 2. Code does not update the eventlog tail MMIO register after adding an entry to the buffer, because of which guest cannot process new entries (as head == tail means buffer is empty). 3. Compares eventlog tail (which is byte offset in the buffer) to eventlog length (which is number of maximum entries in the buffer). This causes vIOMMU to generate only fix number of event logs, after which it keeps on generating overflow interrupts, without actually resetting the log buffer. 4. Updates ComWaitInt instead of EventLogInt bitfield in Status register. Guest checks this field to see if there are new event log entries in the buffer. 5. Does not reset event log head and tail pointers when guest writes to eventlog base register. Fix above issues, so that guest can process event log entries. Fixes: d29a09ca68428 ("hw/i386: Introduce AMD IOMMU") Signed-off-by: Sairaj Kodilkar Reviewed-by: Vasant Hegde Message-Id: <20250801060507.3382-7-sarunkod@amd.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 44 +++++++++++++++++++++++++++++++++++--------- hw/i386/amd_iommu.h | 1 + 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 6925085d29e33..26be69bec8ae2 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -160,10 +160,10 @@ static void amdvi_writeq(AMDVIState *s, hwaddr addr, uint64_t val) (oldval_preserved | newval_write) & ~newval_w1c_set); } -/* OR a 64-bit register with a 64-bit value */ +/* AND a 64-bit register with a 64-bit value */ static bool amdvi_test_mask(AMDVIState *s, hwaddr addr, uint64_t val) { - return amdvi_readq(s, addr) | val; + return amdvi_readq(s, addr) & val; } /* OR a 64-bit register with a 64-bit value storing result in the register */ @@ -192,19 +192,31 @@ static void amdvi_generate_msi_interrupt(AMDVIState *s) } } +static uint32_t get_next_eventlog_entry(AMDVIState *s) +{ + uint32_t evtlog_size = s->evtlog_len * AMDVI_EVENT_LEN; + return (s->evtlog_tail + AMDVI_EVENT_LEN) % evtlog_size; +} + static void amdvi_log_event(AMDVIState *s, uint64_t *evt) { + uint32_t evtlog_tail_next; + /* event logging not enabled */ if (!s->evtlog_enabled || amdvi_test_mask(s, AMDVI_MMIO_STATUS, AMDVI_MMIO_STATUS_EVT_OVF)) { return; } + evtlog_tail_next = get_next_eventlog_entry(s); + /* event log buffer full */ - if (s->evtlog_tail >= s->evtlog_len) { - amdvi_assign_orq(s, AMDVI_MMIO_STATUS, AMDVI_MMIO_STATUS_EVT_OVF); - /* generate interrupt */ - amdvi_generate_msi_interrupt(s); + if (evtlog_tail_next == s->evtlog_head) { + /* generate overflow interrupt */ + if (s->evtlog_intr) { + amdvi_assign_orq(s, AMDVI_MMIO_STATUS, AMDVI_MMIO_STATUS_EVT_OVF); + amdvi_generate_msi_interrupt(s); + } return; } @@ -213,9 +225,13 @@ static void amdvi_log_event(AMDVIState *s, uint64_t *evt) trace_amdvi_evntlog_fail(s->evtlog, s->evtlog_tail); } - s->evtlog_tail += AMDVI_EVENT_LEN; - amdvi_assign_orq(s, AMDVI_MMIO_STATUS, AMDVI_MMIO_STATUS_COMP_INT); - amdvi_generate_msi_interrupt(s); + s->evtlog_tail = evtlog_tail_next; + amdvi_writeq_raw(s, AMDVI_MMIO_EVENT_TAIL, s->evtlog_tail); + + if (s->evtlog_intr) { + amdvi_assign_orq(s, AMDVI_MMIO_STATUS, AMDVI_MMIO_STATUS_EVENT_INT); + amdvi_generate_msi_interrupt(s); + } } static void amdvi_setevent_bits(uint64_t *buffer, uint64_t value, int start, @@ -731,9 +747,19 @@ static inline void amdvi_handle_excllim_write(AMDVIState *s) static inline void amdvi_handle_evtbase_write(AMDVIState *s) { uint64_t val = amdvi_readq(s, AMDVI_MMIO_EVENT_BASE); + + if (amdvi_readq(s, AMDVI_MMIO_STATUS) & AMDVI_MMIO_STATUS_EVENT_INT) + /* Do not reset if eventlog interrupt bit is set*/ + return; + s->evtlog = val & AMDVI_MMIO_EVTLOG_BASE_MASK; s->evtlog_len = 1UL << (amdvi_readq(s, AMDVI_MMIO_EVTLOG_SIZE_BYTE) & AMDVI_MMIO_EVTLOG_SIZE_MASK); + + /* clear tail and head pointer to 0 when event base is updated */ + s->evtlog_tail = s->evtlog_head = 0; + amdvi_writeq_raw(s, AMDVI_MMIO_EVENT_HEAD, s->evtlog_head); + amdvi_writeq_raw(s, AMDVI_MMIO_EVENT_TAIL, s->evtlog_tail); } static inline void amdvi_handle_evttail_write(AMDVIState *s) diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 67078c6f1e224..2476296c49023 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -111,6 +111,7 @@ #define AMDVI_MMIO_STATUS_CMDBUF_RUN (1 << 4) #define AMDVI_MMIO_STATUS_EVT_RUN (1 << 3) #define AMDVI_MMIO_STATUS_COMP_INT (1 << 2) +#define AMDVI_MMIO_STATUS_EVENT_INT (1 << 1) #define AMDVI_MMIO_STATUS_EVT_OVF (1 << 0) #define AMDVI_CMDBUF_ID_BYTE 0x07 From 8d5613d2eefa6ca94b3c642583488e20915c48f7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 12 Jun 2025 17:17:25 +0200 Subject: [PATCH 0192/1794] tests/acpi: virt: add an empty HEST file Such file will be used to track HEST table changes. For now, disallow HEST table check until we update it to the current data. Signed-off-by: Mauro Carvalho Chehab Acked-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: --- tests/data/acpi/aarch64/virt/HEST | 0 tests/qtest/bios-tables-test-allowed-diff.h | 1 + 2 files changed, 1 insertion(+) create mode 100644 tests/data/acpi/aarch64/virt/HEST diff --git a/tests/data/acpi/aarch64/virt/HEST b/tests/data/acpi/aarch64/virt/HEST new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8bf45..39901c58d647d 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,2 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/aarch64/virt/HEST", From 5088651138b94542807414eb841363b16d6aa535 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 12 Jun 2025 17:17:26 +0200 Subject: [PATCH 0193/1794] tests/qtest/bios-tables-test: extend to also check HEST table Currently, aarch64 can generate a HEST table when loaded with -machine ras=on. Add support for it. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: <9ce77140500ef68cc939d63952c25579f711ea52.1749741085.git.mchehab+huawei@kernel.org> --- tests/qtest/bios-tables-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 6aec68deccfa3..e7e6926c816f1 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -2208,7 +2208,7 @@ static void test_acpi_aarch64_virt_tcg(void) data.smbios_cpu_max_speed = 2900; data.smbios_cpu_curr_speed = 2700; - test_acpi_one("-cpu cortex-a57 " + test_acpi_one("-cpu cortex-a57 -machine ras=on " "-smbios type=4,max-speed=2900,current-speed=2700", &data); free_test_data(&data); } From cd16f08ad4bda8191e4de986d17184c9da5466cd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 12 Jun 2025 17:17:27 +0200 Subject: [PATCH 0194/1794] tests/acpi: virt: update HEST file with its current data Now that HEST table is checked for aarch64, add the current firmware file. Signed-off-by: Mauro Carvalho Chehab Acked-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: --- tests/data/acpi/aarch64/virt/HEST | Bin 0 -> 132 bytes tests/qtest/bios-tables-test-allowed-diff.h | 1 - 2 files changed, 1 deletion(-) diff --git a/tests/data/acpi/aarch64/virt/HEST b/tests/data/acpi/aarch64/virt/HEST index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4c5d8c5b5da5b3241f93cd0839e94272bf6b1486 100644 GIT binary patch literal 132 zcmeZp4Gw8xU|?W; Date: Mon, 14 Jul 2025 09:00:47 +0100 Subject: [PATCH 0195/1794] intel_iommu: Allow both Status Write and Interrupt Flag in QI wait FreeBSD does both, and this appears to be perfectly valid. The VT-d spec even talks about the ordering (the status write should be done first, unsurprisingly). We certainly shouldn't assert() and abort QEMU if the guest asks for both. Fixes: ed7b8fbcfb88 ("intel-iommu: add supports for queued invalidation interface") Closes: https://gitlab.com/qemu-project/qemu/-/issues/3028 Signed-off-by: David Woodhouse Message-Id: <0122cbabc0adcc3cf878f5fd7834d8f258c7a2f2.camel@infradead.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index fe9a5f2872799..83c5e444131a3 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -2828,6 +2828,7 @@ static bool vtd_process_wait_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) { uint64_t mask[4] = {VTD_INV_DESC_WAIT_RSVD_LO, VTD_INV_DESC_WAIT_RSVD_HI, VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE}; + bool ret = true; if (!vtd_inv_desc_reserved_check(s, inv_desc, mask, false, __func__, "wait")) { @@ -2839,8 +2840,6 @@ static bool vtd_process_wait_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) uint32_t status_data = (uint32_t)(inv_desc->lo >> VTD_INV_DESC_WAIT_DATA_SHIFT); - assert(!(inv_desc->lo & VTD_INV_DESC_WAIT_IF)); - /* FIXME: need to be masked with HAW? */ dma_addr_t status_addr = inv_desc->hi; trace_vtd_inv_desc_wait_sw(status_addr, status_data); @@ -2849,18 +2848,22 @@ static bool vtd_process_wait_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) &status_data, sizeof(status_data), MEMTXATTRS_UNSPECIFIED)) { trace_vtd_inv_desc_wait_write_fail(inv_desc->hi, inv_desc->lo); - return false; + ret = false; } - } else if (inv_desc->lo & VTD_INV_DESC_WAIT_IF) { + } + + if (inv_desc->lo & VTD_INV_DESC_WAIT_IF) { /* Interrupt flag */ vtd_generate_completion_event(s); - } else { + } + + if (!(inv_desc->lo & (VTD_INV_DESC_WAIT_IF | VTD_INV_DESC_WAIT_SW))) { error_report_once("%s: invalid wait desc: hi=%"PRIx64", lo=%"PRIx64 " (unknown type)", __func__, inv_desc->hi, inv_desc->lo); return false; } - return true; + return ret; } static bool vtd_process_context_cache_desc(IntelIOMMUState *s, From 4164adc476d85d46ef4901c05a9807b24473b00d Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Mon, 14 Jul 2025 12:26:23 +0200 Subject: [PATCH 0196/1794] MAINTAINERS: add net/vhost* files under `vhost` net/vhost* files should be interesting for vhost maintainers/reviewers. Suggested-by: Peter Maydell Signed-off-by: Stefano Garzarella Message-Id: <20250714102626.34431-1-sgarzare@redhat.com> Reviewed-by: Manos Pitsidianakis Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 069d77f2f8004..28cea342718db 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2322,6 +2322,7 @@ F: include/*/vhost* F: subprojects/libvhost-user/ F: block/export/vhost-user* F: util/vhost-user-server.c +F: net/vhost* vhost-shadow-virtqueue R: Eugenio Pérez From 4caf74916d09019e61c91f8cb1166510836d35e8 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Mon, 14 Jul 2025 12:11:56 +0200 Subject: [PATCH 0197/1794] net/vdpa: fix potential fd leak in net_init_vhost_vdpa() Coverity reported a file descriptor leak (CID 1490785) that happens if `vhost_vdpa_get_max_queue_pairs()` returns 0, since in that case net_host_vdpa_init(), which should take ownership of the fd, is never called. vhost_vdpa_get_max_queue_pairs() returns 1 if VIRTIO_NET_F_MQ is not negotiated, or a negative error if the ioctl() fails, or the maximum number of queue pairs exposed by the device in the config space in the `max_virtqueue_pairs` field. In the VIRTIO spec we have: The device MUST set max_virtqueue_pairs to between 1 and 0x8000 inclusive, if it offers VIRTIO_NET_F_MQ. So, if `vhost_vdpa_get_max_queue_pairs()` returns 0, it's really an error since the device is violating the VIRTIO spec. Treat also `queue_pairs == 0` as an error, and jump to the `err` label, to return a negative value to the caller in any case. Coverity: CID 1490785 Suggested-by: Peter Maydell Signed-off-by: Stefano Garzarella Message-Id: <20250714101156.30024-1-sgarzare@redhat.com> Suggested-by: Peter Maydell Signed-off-by: Stefano Garzarella Reviewed-by: Manos Pitsidianakis Acked-by: Jason Wang --- net/vhost-vdpa.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 6a30a44d2bc80..74d26a9497257 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -1840,9 +1840,8 @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name, queue_pairs = vhost_vdpa_get_max_queue_pairs(vdpa_device_fd, features, &has_cvq, errp); - if (queue_pairs < 0) { - qemu_close(vdpa_device_fd); - return queue_pairs; + if (queue_pairs <= 0) { + goto err; } r = vhost_vdpa_get_iova_range(vdpa_device_fd, &iova_range); From a0555e36fc44ea98edf7c50924de8b973cd4267d Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Wed, 30 Jul 2025 00:16:49 +0800 Subject: [PATCH 0198/1794] hw/intc/arm_gicv3_kvm: Remove writes to ICPENDR registers As per the arm-vgic-v3 kernel doc [1]: Accesses to GICD_ICPENDR register region and GICR_ICPENDR0 registers have RAZ/WI semantics, meaning that reads always return 0 and writes are always ignored. The state behind these registers (both 0 and 1 bits) is written by writing to the GICD_ISPENDR and GICR_ISPENDR0 registers, unlike some of the other set/clear register pairs. Remove the useless writes to ICPENDR registers in kvm_arm_gicv3_put(). [1] https://docs.kernel.org/virt/kvm/devices/arm-vgic-v3.html Signed-off-by: Zenghui Yu Message-id: 20250729161650.43758-2-zenghui.yu@linux.dev Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_kvm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 8ed88e742994b..f798a6e28cac0 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -387,8 +387,6 @@ static void kvm_arm_gicv3_put(GICv3State *s) reg = c->level; kvm_gic_line_level_access(s, 0, ncpu, ®, true); - reg = ~0; - kvm_gicr_access(s, GICR_ICPENDR0, ncpu, ®, true); reg = c->gicr_ipendr0; kvm_gicr_access(s, GICR_ISPENDR0, ncpu, ®, true); @@ -445,7 +443,7 @@ static void kvm_arm_gicv3_put(GICv3State *s) kvm_gic_put_line_level_bmp(s, s->level); /* s->pending bitmap -> GICD_ISPENDRn */ - kvm_dist_putbmp(s, GICD_ISPENDR, GICD_ICPENDR, s->pending); + kvm_dist_putbmp(s, GICD_ISPENDR, 0, s->pending); /* s->active bitmap -> GICD_ISACTIVERn */ kvm_dist_putbmp(s, GICD_ISACTIVER, GICD_ICACTIVER, s->active); From b10bd4bd17ac8628ede8735a08ad82dc3b721c64 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Wed, 30 Jul 2025 00:16:50 +0800 Subject: [PATCH 0199/1794] hw/intc/arm_gicv3_kvm: Write all 1's to clear enable/active KVM's userspace access interface to the GICD enable and active bits is via set/clear register pairs which implement the hardware's "write 1s to the clear register to clear the 0 bits, and write 1s to the set register to set the 1 bits" semantics. We didn't get this right, because we were writing 0 to the clear register. Writing 0 to GICD_IC{ENABLE,ACTIVE}R architecturally has no effect on interrupt status (all writes are simply ignored by KVM) and doesn't comply with the intention of "first write to the clear-reg to clear all bits". Write all 1's to actually clear the enable/active status. This didn't have any adverse effects on migration because there we start with a clean VM state; it would be guest-visible when doing a system reset, but since Linux always cleans up the register state of the GIC during bootup before it enables it most users won't have run into a problem here. Cc: qemu-stable@nongnu.org Fixes: 367b9f527bec ("hw/intc/arm_gicv3_kvm: Implement get/put functions") Signed-off-by: Zenghui Yu Message-id: 20250729161650.43758-3-zenghui.yu@linux.dev Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_kvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index f798a6e28cac0..6166283cd1a3d 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -295,7 +295,7 @@ static void kvm_dist_putbmp(GICv3State *s, uint32_t offset, * the 1 bits. */ if (clroffset != 0) { - reg = 0; + reg = ~0; kvm_gicd_access(s, clroffset, ®, true); clroffset += 4; } From e7b77e681f8ecf7d9360e47243f7c1a0fb88f51c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 10 Jul 2025 18:43:12 +0100 Subject: [PATCH 0200/1794] hw/display/framebuffer: Add cast to force 64x64 multiply In framebuffer_update_display(), Coverity complains because we multiply two values of type 'int' (which will be done as a 32x32 multiply and so in theory might overflow) and then add the result to a ram_addr_t, which can be 64 bits. 4GB framebuffers are not plausible anyway, but keep Coverity happy by adding casts which force these multiplies to be done as 64x64. Coverity: CID 1487248 Signed-off-by: Peter Maydell Reviewed-by: Manos Pitsidianakis Message-id: 20250710174312.1313177-1-peter.maydell@linaro.org --- hw/display/framebuffer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/display/framebuffer.c b/hw/display/framebuffer.c index 4485aa335bbce..b4296e8a33ec2 100644 --- a/hw/display/framebuffer.c +++ b/hw/display/framebuffer.c @@ -95,9 +95,9 @@ void framebuffer_update_display( } first = -1; - addr += i * src_width; - src += i * src_width; - dest += i * dest_row_pitch; + addr += (uint64_t)i * src_width; + src += (uint64_t)i * src_width; + dest += (uint64_t)i * dest_row_pitch; snap = memory_region_snapshot_and_clear_dirty(mem, addr, src_width * rows, DIRTY_MEMORY_VGA); From 4f2b82f60431e4792ecfd86a4d6b824248ee4c21 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 Jul 2025 14:43:38 +0100 Subject: [PATCH 0201/1794] target/arm: Reinstate bogus AArch32 DBGDTRTX register for migration compat In commit 655659a74a we fixed some bugs in the encoding of the Debug Communications Channel registers, including that we were incorrectly exposing an AArch32 register at p14, 3, c0, c5, 0. Unfortunately removing a register is a break of forwards migration compatibility for TCG, because we will fail the migration if the source QEMU passes us a cpreg which the destination QEMU does not have. We don't have a mechanism for saying "it's OK to ignore this sysreg in the inbound data", so for the 10.1 release reinstate the incorrect AArch32 register. (We probably have had other cases in the past of breaking migration compatibility like this, but we didn't notice because we didn't test and in any case not that many people care about TCG migration compatibility. KVM migration compat is not affected because for KVM we treat the kernel as the source of truth for what system registers are present.) Fixes: 655659a74a36b ("target/arm: Correct encoding of Debug Communications Channel registers") Reported-by: Fabiano Rosas Signed-off-by: Peter Maydell Reviewed-by: Fabiano Rosas Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-id: 20250731134338.250203-1-peter.maydell@linaro.org --- target/arm/debug_helper.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index aee06d4d42662..579516e154142 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -940,6 +940,13 @@ static void dbgclaimclr_write(CPUARMState *env, const ARMCPRegInfo *ri, env->cp15.dbgclaim &= ~(value & 0xFF); } +static CPAccessResult access_bogus(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + /* Always UNDEF, as if this cpreg didn't exist */ + return CP_ACCESS_UNDEFINED; +} + static const ARMCPRegInfo debug_cp_reginfo[] = { /* * DBGDRAR, DBGDSAR: always RAZ since we don't implement memory mapped @@ -1002,6 +1009,28 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { .opc0 = 2, .opc1 = 3, .crn = 0, .crm = 4, .opc2 = 0, .access = PL0_RW, .accessfn = access_tdcc, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* + * This is not a real AArch32 register. We used to incorrectly expose + * this due to a QEMU bug; to avoid breaking migration compatibility we + * need to continue to provide it so that we don't fail the inbound + * migration when it tells us about a sysreg that we don't have. + * We set an always-fails .accessfn, which means that the guest doesn't + * actually see this register (it will always UNDEF, identically to if + * there were no cpreg definition for it other than that we won't print + * a LOG_UNIMP message about it), and we set the ARM_CP_NO_GDB flag so the + * gdbstub won't see it either. + * (We can't just set .access = 0, because add_cpreg_to_hashtable() + * helpfully ignores cpregs which aren't accessible to the highest + * implemented EL.) + * + * TODO: implement a system for being able to describe "this register + * can be ignored if it appears in the inbound stream"; then we can + * remove this temporary hack. + */ + { .name = "BOGUS_DBGDTR_EL0", .state = ARM_CP_STATE_AA32, + .cp = 14, .opc1 = 3, .crn = 0, .crm = 5, .opc2 = 0, + .access = PL0_RW, .accessfn = access_bogus, + .type = ARM_CP_CONST | ARM_CP_NO_GDB, .resetvalue = 0 }, /* * OSECCR_EL1 provides a mechanism for an operating system * to access the contents of EDECCR. EDECCR is not implemented though, From 35cca0f95ff5345f54c11d116efc8940a0dab8aa Mon Sep 17 00:00:00 2001 From: Vacha Bhavsar Date: Tue, 22 Jul 2025 17:37:35 +0000 Subject: [PATCH 0202/1794] target/arm: Fix big-endian handling of NEON gdb remote debugging In the code for allowing the gdbstub to set the value of an AArch64 FP/SIMD register, we weren't accounting for target_big_endian() being true. This meant that for aarch64_be-linux-user we would set the two halves of the FP register the wrong way around. The much more common case of a little-endian guest is not affected; nor are big-endian hosts. Correct the handling of this case. Cc: qemu-stable@nongnu.org Signed-off-by: Vacha Bhavsar Message-id: 20250722173736.2332529-2-vacha.bhavsar@oss.qualcomm.com [PMM: added comment, expanded commit message, fixed missing space] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/gdbstub64.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index 64ee9b3b56791..4fce58d895ee9 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -115,8 +115,22 @@ int aarch64_gdb_set_fpu_reg(CPUState *cs, uint8_t *buf, int reg) /* 128 bit FP register */ { uint64_t *q = aa64_vfp_qreg(env, reg); - q[0] = ldq_le_p(buf); - q[1] = ldq_le_p(buf + 8); + + /* + * On the wire these are target-endian 128 bit values. + * In the CPU state these are host-order uint64_t values + * with the least-significant one first. This means they're + * the other way around for target_big_endian() (which is + * only true for us for aarch64_be-linux-user). + */ + if (target_big_endian()) { + q[1] = ldq_p(buf); + q[0] = ldq_p(buf + 8); + } else{ + q[0] = ldq_p(buf); + q[1] = ldq_p(buf + 8); + } + return 16; } case 32: From 97b3d732afec9b165c33697452e31267a845338f Mon Sep 17 00:00:00 2001 From: Vacha Bhavsar Date: Tue, 22 Jul 2025 17:37:36 +0000 Subject: [PATCH 0203/1794] target/arm: Fix handling of setting SVE registers from gdb The code to handle setting SVE registers via the gdbstub is broken: * it sets each pair of elements in the zregs[].d[] array in the wrong order for the most common (little endian) case: the least significant 64-bit value comes first * it makes no attempt to handle target_endian() * it does a simple copy out of the (target endian) gdbstub buffer into the (host endan) zregs data structure, which is wrong on big endian hosts Fix all these problems: * use ldq_p() to read from the gdbstub buffer * check target_big_endian() to see if we need to handle the 128-bit values the opposite way around Cc: qemu-stable@nongnu.org Signed-off-by: Vacha Bhavsar Message-id: 20250722173736.2332529-3-vacha.bhavsar@oss.qualcomm.com [PMM: adjusted commit message, fixed spacing] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/gdbstub64.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index 4fce58d895ee9..08e2858539681 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -206,10 +206,17 @@ int aarch64_gdb_set_sve_reg(CPUState *cs, uint8_t *buf, int reg) case 0 ... 31: { int vq, len = 0; - uint64_t *p = (uint64_t *) buf; for (vq = 0; vq < cpu->sve_max_vq; vq++) { - env->vfp.zregs[reg].d[vq * 2 + 1] = *p++; - env->vfp.zregs[reg].d[vq * 2] = *p++; + if (target_big_endian()) { + env->vfp.zregs[reg].d[vq * 2 + 1] = ldq_p(buf); + buf += 8; + env->vfp.zregs[reg].d[vq * 2] = ldq_p(buf); + } else{ + env->vfp.zregs[reg].d[vq * 2] = ldq_p(buf); + buf += 8; + env->vfp.zregs[reg].d[vq * 2 + 1] = ldq_p(buf); + } + buf += 8; len += 16; } return len; @@ -224,9 +231,9 @@ int aarch64_gdb_set_sve_reg(CPUState *cs, uint8_t *buf, int reg) { int preg = reg - 34; int vq, len = 0; - uint64_t *p = (uint64_t *) buf; for (vq = 0; vq < cpu->sve_max_vq; vq = vq + 4) { - env->vfp.pregs[preg].p[vq / 4] = *p++; + env->vfp.pregs[preg].p[vq / 4] = ldq_p(buf); + buf += 8; len += 8; } return len; From 676ab6a21117858393a4440e4cdc3d314277cf20 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 1 Aug 2025 00:13:05 +0000 Subject: [PATCH 0204/1794] tests/tcg: Fix run for tests with specific plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 25aaf0cb7f (“tests/tcg: reduce the number of plugin test combinations”) added support for running tests with specific plugins passed via the EXTRA_RUNS variable. However, due to the optimization, the rules generated as a shuffled combination of tests and plugins might not cover the rules required to run the tests with a specific plugin passed via EXTRA_RUNS. This commit fixes it by correctly generating the rules for the tests that require a specific plugin to run, which are now passed via the EXTRA_RUNS_WITH_PLUGIN instead of via the EXTRA_RUNS variable. The fix essentially excludes the tests passed via EXTRA_RUNS_WITH_PLUGIN from the rules created by the shuffled combination of tests and plugins, to avoid running the tests twice, and generates the rules for the test/plugin combinations listed in the EXTRA_RUNS_WITH_PLUGIN variable. Signed-off-by: Gustavo Romero Reviewed-by: Pierrick Bouvier Tested-by: Pierrick Bouvier Message-id: 20250801001305.2352554-1-gustavo.romero@linaro.org Signed-off-by: Peter Maydell --- tests/tcg/Makefile.target | 20 ++++++++++++++++--- tests/tcg/multiarch/Makefile.target | 2 +- .../multiarch/system/Makefile.softmmu-target | 2 +- tests/tcg/x86_64/Makefile.softmmu-target | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index 18afd5be19407..af72903f89861 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -170,6 +170,10 @@ endif PLUGINS=$(filter-out $(DISABLE_PLUGINS), \ $(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c)))) +strip-plugin = $(wordlist 1, 1, $(subst -with-, ,$1)) +extract-plugin = $(wordlist 2, 2, $(subst -with-, ,$1)) +extract-test = $(subst run-plugin-,,$(wordlist 1, 1, $(subst -with-, ,$1))) + # We need to ensure expand the run-plugin-TEST-with-PLUGIN # pre-requistes manually here as we can't use stems to handle it. We # only expand MULTIARCH_TESTS which are common on most of our targets @@ -179,6 +183,13 @@ PLUGINS=$(filter-out $(DISABLE_PLUGINS), \ ifneq ($(MULTIARCH_TESTS),) +# Extract extra tests from the extra test+plugin combination. +EXTRA_TESTS_WITH_PLUGIN=$(foreach test, \ + $(EXTRA_RUNS_WITH_PLUGIN),$(call extract-test,$(test))) +# Exclude tests that were specified to run with specific plugins from the tests +# which can run with any plugin combination, so we don't run it twice. +MULTIARCH_TESTS:=$(filter-out $(EXTRA_TESTS_WITH_PLUGIN), $(MULTIARCH_TESTS)) + NUM_PLUGINS := $(words $(PLUGINS)) NUM_TESTS := $(words $(MULTIARCH_TESTS)) @@ -186,19 +197,22 @@ define mod_plus_one $(shell $(PYTHON) -c "print( ($(1) % $(2)) + 1 )") endef +# Rules for running tests with any plugin combination, i.e., no specific plugin. $(foreach _idx, $(shell seq 1 $(NUM_TESTS)), \ $(eval _test := $(word $(_idx), $(MULTIARCH_TESTS))) \ $(eval _plugin := $(word $(call mod_plus_one, $(_idx), $(NUM_PLUGINS)), $(PLUGINS))) \ $(eval run-plugin-$(_test)-with-$(_plugin): $(_test) $(_plugin)) \ $(eval RUN_TESTS+=run-plugin-$(_test)-with-$(_plugin))) +# Rules for running extra tests with specific plugins. +$(foreach f,$(EXTRA_RUNS_WITH_PLUGIN), \ + $(eval $(f): $(call extract-test,$(f)) $(call extract-plugin,$(f)))) + endif # MULTIARCH_TESTS endif # CONFIG_PLUGIN -strip-plugin = $(wordlist 1, 1, $(subst -with-, ,$1)) -extract-plugin = $(wordlist 2, 2, $(subst -with-, ,$1)) - RUN_TESTS+=$(EXTRA_RUNS) +RUN_TESTS+=$(EXTRA_RUNS_WITH_PLUGIN) # Some plugins need additional arguments above the default to fully # exercise things. We can define them on a per-test basis here. diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index 38345ff88052c..8dc65d7a064e9 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -201,7 +201,7 @@ run-plugin-test-plugin-mem-access-with-libmem.so: \ $(SRC_PATH)/tests/tcg/multiarch/check-plugin-output.sh \ $(QEMU) $< -EXTRA_RUNS += run-plugin-test-plugin-mem-access-with-libmem.so +EXTRA_RUNS_WITH_PLUGIN += run-plugin-test-plugin-mem-access-with-libmem.so endif # Update TESTS diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target index 4171b4e6aa0ce..98c4eda5e0000 100644 --- a/tests/tcg/multiarch/system/Makefile.softmmu-target +++ b/tests/tcg/multiarch/system/Makefile.softmmu-target @@ -77,5 +77,5 @@ run-plugin-memory-with-libmem.so: memory libmem.so run-plugin-memory-with-libmem.so: PLUGIN_ARGS=$(COMMA)region-summary=true run-plugin-memory-with-libmem.so: CHECK_PLUGIN_OUTPUT_COMMAND=$(MULTIARCH_SYSTEM_SRC)/validate-memory-counts.py $@.out -EXTRA_RUNS += run-plugin-memory-with-libmem.so +EXTRA_RUNS_WITH_PLUGIN += run-plugin-memory-with-libmem.so endif diff --git a/tests/tcg/x86_64/Makefile.softmmu-target b/tests/tcg/x86_64/Makefile.softmmu-target index 3e30ca930749b..4e65f58b570c1 100644 --- a/tests/tcg/x86_64/Makefile.softmmu-target +++ b/tests/tcg/x86_64/Makefile.softmmu-target @@ -40,5 +40,5 @@ run-plugin-patch-target-with-libpatch.so: \ run-plugin-patch-target-with-libpatch.so: \ CHECK_PLUGIN_OUTPUT_COMMAND=$(X64_SYSTEM_SRC)/validate-patch.py $@.out run-plugin-patch-target-with-libpatch.so: patch-target libpatch.so -EXTRA_RUNS+=run-plugin-patch-target-with-libpatch.so +EXTRA_RUNS_WITH_PLUGIN+=run-plugin-patch-target-with-libpatch.so endif From 7bf9ae8cc2484f83ec77ad475538d78355e424ea Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Mon, 28 Jul 2025 01:19:18 +0300 Subject: [PATCH 0205/1794] roms/vbootrom: update to 7b1eb5f7fe6a MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: 7b1eb5f ast27x0: Fix Makefile to unconditionally set CC to support correct cross-compilation 601d410 ast27x0: Fix missing SCU module reset for SSP and TSP initialization 80768e4 ast27x0: Initialize and enable SSP/TSP using SCU with reserved-memory from DTB f8ab635 ast27x0: Show build date and git version 53294f5 Add initial support for AST27x0 b1c2803 Dynamically detects NPCM8XX UBOOT destination and size. 4f54dfc Automatically search for UBOOT location for NPCM8xx images. The actual bootroms are not updated yet. Signed-off-by: Michael Tokarev Link: https://lore.kernel.org/qemu-devel/2a89ad4c8f5665d07952a4f1749caa6ec0cd3d9c.1753654515.git.mjt@tls.msk.ru [ clg: Update to latest vbootrom ] Reviewed-by: Jamin Lin Signed-off-by: Cédric Le Goater --- roms/vbootrom | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roms/vbootrom b/roms/vbootrom index 1287b6e42e839..183c9ff8056b7 160000 --- a/roms/vbootrom +++ b/roms/vbootrom @@ -1 +1 @@ -Subproject commit 1287b6e42e839ba2ab0f06268c5b53ae60df3537 +Subproject commit 183c9ff8056b7946db1ae49cc23e8980ac413174 From 5ff7ad61c0af08d81dc7fd050c14b944fbeb9c35 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Mon, 28 Jul 2025 01:19:19 +0300 Subject: [PATCH 0206/1794] roms/Makefile: build ast27x0_bootrom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3052 Signed-off-by: Michael Tokarev Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/607a943a587248fbe0ff0897de80aee98a093caa.1753654515.git.mjt@tls.msk.ru [ clg: Removed make CC= workaround ] Reviewed-by: Jamin Lin Signed-off-by: Cédric Le Goater --- roms/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/roms/Makefile b/roms/Makefile index 6af68a922f30d..4c8793c5bd454 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -68,6 +68,7 @@ default help: @echo " u-boot.sam460 -- update u-boot.sam460" @echo " npcm7xx_bootrom -- update vbootrom for npcm7xx" @echo " npcm8xx_bootrom -- update vbootrom for npcm8xx" + @echo " ast27x0_bootrom -- update vbootrom for ast27x0" @echo " efi -- update UEFI (edk2) platform firmware" @echo " opensbi32-generic -- update OpenSBI for 32-bit generic machine" @echo " opensbi64-generic -- update OpenSBI for 64-bit generic machine" @@ -200,6 +201,10 @@ npcm8xx_bootrom: $(MAKE) -C vbootrom/npcm8xx CROSS_COMPILE=$(aarch64_cross_prefix) cp vbootrom/npcm8xx/npcm8xx_bootrom.bin ../pc-bios/npcm8xx_bootrom.bin +ast27x0_bootrom: + $(MAKE) -C vbootrom/ast27x0 CROSS_COMPILE=$(aarch64_cross_prefix) + cp vbootrom/ast27x0/ast27x0_bootrom.bin ../pc-bios/ast27x0_bootrom.bin + hppa-firmware: $(MAKE) -C seabios-hppa parisc cp seabios-hppa/out/hppa-firmware.img ../pc-bios/ From d63961f957ffaa586b166846f2c6a580923b08f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 29 Jul 2025 19:34:59 +0200 Subject: [PATCH 0207/1794] pc-bios: Update vbootrom image to commit 183c9ff8056b MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Full changelog since last update (1287b6e42e83) : Hao Wu (2): Automatically search for UBOOT location for NPCM8xx images. Dynamically detects NPCM8XX UBOOT destination and size. Jamin Lin (5): Add initial support for AST27x0 ast27x0: Show build date and git version ast27x0: Initialize and enable SSP/TSP using SCU with reserved-memory from DTB ast27x0: Fix missing SCU module reset for SSP and TSP initialization ast27x0: Fix Makefile to unconditionally set CC to support correct cross-compilation Compiled with gcc version 13.3.0 Reviewed-by: Jamin Lin Reviewed-by: Michael Tokarev Signed-off-by: Cédric Le Goater --- pc-bios/ast27x0_bootrom.bin | Bin 15552 -> 16408 bytes pc-bios/npcm7xx_bootrom.bin | Bin 768 -> 672 bytes pc-bios/npcm8xx_bootrom.bin | Bin 608 -> 672 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/pc-bios/ast27x0_bootrom.bin b/pc-bios/ast27x0_bootrom.bin index 0b9b3a2360e375bb6007ecdf13b39d931870f6fa..a4c94d64da55ade9b4beb2a409132f315fb51347 100644 GIT binary patch literal 16408 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 H*FOIj5gCL{ literal 15552 zcmdU0eRNdinSbuRlMq5mfqX!L-P{D|!2#qefdQ$xnM4 zU5INhuJdt?;yMpk;{Jd7w3^0+SS&VK!2c2PT`2Ha-zwFlTThcUWnvAl6FFWDm=6Hc zQDR(p1K)t;zx*zq3qGf<@k{49za?i&0)=gl8?xqkeBZ>kL(&E#B30|ltrK4t>De5Uajle7xk`dhD6>oH1CBp3HR!9JX_>U^U*mW81hW` zmvce`S0vPL&(x6G0I=pgkDd_sUMs_@Z>?GFg zHL72}f;HX--vcF9PwNC}+6BFcSUr-_d;e98x0=%Q9LBT^>lPDZrTCgbiTn|B_S)G{ zCDyB$8(NKZ-om&oPfwQwzHu&a@aVhAGF@M1bPYFny5657!+)jt%GeA1P@XPf4Ri`- z&I>7ej~0bKgLxfe{%}!9&7CN+=Pnh6mS9ee`HJna`r^<+MGw3Li$b^JzEFD4RY^tY zilIlM_*#a9EnSM&rb9mR{A6*c&&ZDWArtHxhNGRi{^g|@n+*8u$dk}#pCMjLA6lZ| z!%IU`05=!0wAOEhswHQzU?f&mDivpEz}FPawk4rYx_s;@3S~p@pO3}be-FKoW-it^ z{_fJye2;{$f_7LO+HWrp5r#g{XXL=1*^&ET_deM5J=iE0`r&w|ND=35b?`4cbQ`e- z{l#+apW%ZXd6Bo_vx+b9G6l5f01m?B^aGajYOci>>IdAwGa7L0wUypq_k|9mOLz#d zL6PqMMysbqHp6=Fj+QPf{A4-8OUmki^!3KN!j47~5PUJ!8LGgTkc?gak9%>BsOfho5 z7afXKxpuqVv0G3}*bR%@cq={=`WN>eI++Zeymcei&U{L}PKAB$zp^wm4c8rS_(DI0 z&p@wU!d(Ns>m#vluA_L!xjh7;Va8PyV~nlq>&oEY; zp83v&F>_>C%d&b3R--KYLmLbR)!AjDw z^V^TR+`N{9HFr(5hL3>%Y^>Yrm8Kf#v{MUKno{yI=ipmj`pIQOi;V0zrTjwl{FgcA zJ|}2*$7wS^M-3yS>cI>R`bvkd>8Jt0$}ecabIhmF+hjPJ9-I{2Aw98H@Y|NrHL!1N z*PGwT?D}1`3;nLxkvPsEVogMK=)DIFRUTF##Qq`O;A^2@2 z@$;Y3nvQs!dEECp^B&C4aA0I4R+RrD=F+6oW&roB!X^rd+1mDL*bSTa+ZW`7W?@{2KzybQy}gq?}?415Rh%=Jfb&5vqL z8gg-Tu{GSc!|Hk7BSpK?hL&n8Bvf$utKaHd411$zh^^C>m597!Wmwy_v52SiVC6T# z$9V8TJmMeGQt>7OPec4)PnWYkkA$KRZSw`7&#!j<}co_dp}mJ=+8~ztLHV$%>a!7&}e0>|0zM^+^D(H^vx{mTLmAKzZ9$w z_yYB_d{HAULK)LMa@I4&+MX4QRmo0kpGOZ?ra|6aBCD!JYj~J-9Wa#TkzTVcnrHcf z!?B{3;Fmh}AlK}9_4d#J{7&@Xxj~FY;lFkD1-}2my{oSp1Nusm2f4E6Ka8P&?@S9; z7K0XY<7w*8$awkCb>OcJ=6%QCD-#Rri+#*X1=OiwM5<@XJC~rl>wjwM@DAN4&8 z__1_ZxC=7;GuBMj^G5iFt7rNGb1?glaJo#K47_jSp79Rf?`B>ooMH_hMbC)-X4sq} z!)`pA@Sz(HEc5h?`n5jvmc{w?g+9ilfvX=p{s}N{?8E=;UTvHVGe%oQ9}GaBX8k&! z1$_o|qYZvbxhs)4!mKS<#<3>Yu}`{zhi3TJfiC@md=|jxb+MMwHbKzZ4IW^}CXtRq zgcraLCBag6kl`G9;bpDBkqNsGkrPEmPz)$Ek1jyPT)I2IQTI zZ>xA3n72q{9cTo^v&yAId8@@3jG_jmLB^T*qK{Vab%4JJ`f48dg1G{GMKpy!0Dq*6 z)Hi!|t~&s}-hwZ@$-WFWVLx93KEZGCMfd{Oqfb16UYk7Bc&ugA)h?a=PkN*q_6@{h z#|gWOc&aZ-k1(g~0vv7LA+qeE%D2xQBdwF@d0l!O=RUz*>pJdj+n!Xpmpu)i2NeFv zu8ZK8`Tl#L-v?T-PaETUWCv+`6m9b9(s1G51pZr)@i1VCL%H679s|(R5$NbS#1He{^P*U77EFWz&m6zh$~NsbHpu&RqrTwo6If3xqq zK7hWvTNk~Jc(hS%js?H6{hvJ7Fn3{Xu1TD@&m-3%MvL-Ai}NJV19?GwtNjacTze3Y zd|_wC7;3N1u-?^B&)DBT{+o`<$F(19;T{2g+MX{)Lk)Cgp?|U1> zJ-=LpZj==UE6Z;D^A_Tv2dJ|4O6U#11<}_Y7;cD4aFV$wS8;-6Owdoxe)oTT9!+@iFvzhY!p-g_=NYk`DO(oAc(yQxbzM46}sIx91==cS#4h=J-) zNYkUxw`O)$a;%s4mVF=4rOJ!GjO#~@Oz%lIKLwq6g?FTqd8*Gy_ZDK^s4*t;g6QL( z;rcgXTz{E~dU8s}uIe+!dO80j<}B=`UdWP(FSPviq;&G`(BM4zf1LOq#JUae&p28K zo@*q1PT>M;d;R;U{m|p5(KC4fm%l=qGGN!%>C*HI^aVc=V^u4BJ%Ia9FOZ3SaU0rw zdtbiv9Kqf@K;0$PXE!c@MO8Vc# zUOlKG2YO^;5PFRQK3T>mq4!;wM<1|y7v^z>v4r&BgKWH>hF)3=WML_2VSoBg!^DpJ z+3$^mu6XW~EI)Z7-@hWi;JXj_CEx=+%l*k~!uM6Kb8-LMk=dlb0N<8veB_6q!@OZ3 zmZJIivrmKN2dl{SVkK+5LBE(Mk)Qr>U1<$fS%aNQm`%F!bd>M1C zdd?v8E6-=_eGzJu+Kc@pUW5CwH|6;Y>sLPZ(x}*HD?OiEYaFJtXYF%b{tC1DOf+Lh9vaJ^MfnUdH*wl zeReitHzyLM9Ps5R>~dv3Yy|%+z6`*{gW&dc>)n z?V#<)7;|V1_P+$ zH&O*25YrLlD#&6D@5VFa88$uEF!NXd_wc_;#vf%&_L+dg3wdB|qs@3OKeEH_(^+#^ zFKz>!UdZwm;O$zK9z@GtwEE;n)> zvRT*zU&lGgQk)5KoPMBX;EWA?A&(x9=~|NoUnI?bb*_~YSpl7r=U3u#j?P2&IX?5j zHOROU>-0mH%zHfZyryzNPD(CF_^fkY=j=;Hz=x_=%D$=llan`MlSl8>ZM~<`xbZPG z{_P`a{6N``??AT0zlM?D-1j{^=f7rnBDM{uMuv>E$XV!`ItijC=cdVVoco>2Ll4;x zecJZOi#!dz(GQp}B!hi|7Wy8>SVwuj`U38it*Wga=)K`NWvi*c|5RjyW2+N*Z(#d@ ztv~l1dt}E8abIxP`w@8hHEfh^NDpIaAAI${aD4_o`fA*EC%fXkGWxU%@Su7$tc|mX zZuZp{&RZF)tUV{4x@X6(y;t7}9ol+7+4UCWQ++pReI|)ktwSsLk+jkyX%79)IBy@} zSIGT{A=p6ke*b;e%{=8>^fBdM_W5aweLUN7=q0QupQ4&Rrc zgXsKVk1UW7_hSJ(BNz7E&Uy5&+!GX_rqc&`-jr^)bj z_*OsU@q#XSfFDI3;eLQVf_N^(xa%X-X%u-Fyd1}Sh3+Wgx)b)KZQQ(keHzcN61kxl zGQEZvpj}r&hA{G9N2c{|6#NHJYcm~rxc3S8CG_9mJFrMXZ^!xn^LDuBFSG;poAKJD zi#+8oDp7mj0onuaQz`pZ*tH)vfvHWcKSJFr_~|7f!n*IjVApmNI&#2{lFxq1om?ZQs*l$?{3 zX}k9NDfq*?vxeh^!Kv)Yomx@JzGi<)t#J5IKB-0eAR}e#MXw%Z@0KA8Ev)wvax!dP zY3IF!EP{F&06q2rg7b=@xSpR)szDlhMB#g*y#-YzAv-_o-Uo2*X`(kK&lj&qXaG5y z?~Gah7J~<@+uc_JJ2*YQhV$}*%hkSJ=(9EK8B3l;KMB3ZeGvIQ`Q5dx=L8?S)!v}1@l(#8IH$$!Egd>7=NSB~s( zY7~5)HGw)|4*BYz80*={2doKhjkM3Aac1ho&WGb}`WW1=#&J{qJ?;|=O+InHZO+;Q+MjF<{d&GrGBH( zoYr+2^cb7;pZqb>v=RPuZ`^PY2LB$i(T+J{Ces=U z&U8%OVQAy>abLs!4DXNKoN^cZqrcI@`&Wqs5ZCzqgZDgSe*ixl>M*7>`~fl5d8(*w z#~7Iy#@CNNCn z3Z0@EfbNTs8?m+%w~pRN)g$&H+&jhY5tUQS&cEHxvjdz*Ec{342>E^?d09MR-&EEj z*0_AhX=t9bYifz)HZaG30kxqQ`Htfe#OwgzSSuNKVaUi_#=R0{`AuAwAM>|6mF!Oj znkDpe`1vlZ!~WXr3{K^lh)mdpbH>EGA6o|rA7;F;KA|?E?hGx}VQ;qu)h4Cf!9>DgG?<)AjArhCKCTdSVe@DOBX4V|d^bN()iXGfDGdjT83 zyMVQTQ~Mym^in4{lh7phG|rM%H|yG}Tn`44gZB>c?IpGJiKKp*=@@L`e<_(4jIRlhe#_z(GD-gspQzgxN|*n^kN6SwLK zzHqKz1s*5SHZFg7H?8>F2L5#9tb8MPkbP&qOjiEJ--u9-BEaF?xQM)8!QPYoCi#Co z&VRzbR}D|Zk-iHS-%r{w`;8GX;nX`BbH>Z z|H+cjT=3nCT+g0vbPvZKmV21HkXx9OqLVwO@=RfL+&TRM>N5A&dSoGZj;b7C$2eo< z-lW`-*n{RoSlijVHbAD}jaV`4p!{!i?UvNf1U|0Vx~XAP<0G4HmyL}zwerPT%@3Nh znwOiKw={09+tTtCb5moBxxR7hrrONJ$YvKTh~%oYZjS}TTI_AnPOI(_*AQpeNFy5dR)yB!M(8o+`l$zHAvbcnLEv$ra2$~ zVeA(5_W|dxf=L-M&7JcJ%n$$WRA|-nZAcJ!U_L3&Czy?>$U6@h`jG#slg4)PLGgtv z8GC><6&BbdAn(DiONit-Jm030{8xcg&ovcE1*5PW>bRIY@dp!}!~eV|4&}myGnop) z#sDXPu>ab#fY@2R(;iW{{6l13A&Ejz5B4o5@c+(g8znPSW;M%#h07N$UcS&LrJJ{y ziHw+ue=rfb)Cv^q&}eSh098OJE|@7bE$d}{ts`!-sB_KrH5)gsty$M# zVwJ7)s1y_2r*a*qmaGo5210_COCVG3*s`T@%W~6y_u_@-?5ZtWo0%i-+_2^0M{2gz z%`3en!R%bKzGhnZTb1q&A~E-GHK)VHi=?Yi2!^|Eop zrmfrNZmtJI8|!2~vQ|xVU9-Tao4@k#+QyB|)zz}D@!`!iEgRMXAn{Sj5s%vTO7U#_ zYi(LqK8}n;rr`S`4hGKr-%dZ2_8pJj1N2}2*K4o5nnD9u zLVm*d9>hj!CI|507!L5{7{<5`Kjaa=+m%-_hBE+rJ@c^VF~h%44{M*p0xoxc_ZB}E zlFNj};ctcE;Ph&Tr#v>RQ@oY!)9)r{w%Ujr1YCU*O^)$L=Ru6kCN4f4JBG0^#!L?2 z!?9;E);S99492=KR-vG5Jzd1uWsFgW^11O74(pF;^D4l{c=6BkkGo^M{N}gw$fKGO zF#$FOMZXpYutmfvo#(gb{>H|^kcsaKz_b9S3inDzh5va)m~KHHajqjz0A>YY@Znes z#$LpjYfFwjim?wcmaN0?Vk`>{)dC_W>+lXog%~F8^MK0%oZ{6Pdlh3v7)#dS+ZbDk zu`IywWAYFDz`|HJ@8ddHmmhMPbl{tp6>EXAmBEOBr@LoNMUUi(FiQy*!Qx6y)>6N+k?sp7XI*u@B0JHOr zSZpQk6<^Bl?ug4n81!J$`Db9Jc%WCnoJgT#uSMIE&I-V+i8{88+vl#7wE}?I44Bk7 zu$_-|Xj}ZYOr8MDHg~NBhtnTR^Y8P7jXU@22Q*SlP88Jb3E*4;PCaqrx~LefTIC-X z0Xq{v&PcZV5XPosES@`*@2-yX&NZf|q0t1)My^5m5_}QR%`a+=m4FQcHiNME&@Ugr z*rQ2fb1}9VV>>annLw#D%l7M^rKEAX)J-Sgp9TDE+B_BBPu)B;N;h2hJaC@sPsmF; zmoRn=V;`bptb0JacN85HIMdTb-Udzu?xABh2LO_Z!?v9Tm?=2Gi2F%GM~c6|D7rq7 zY5~r7fpdv;DO1Ja%*V$1_hp2~`1hwDNGogi95krm)D)~@>M7u^#Kz@r7dNq9`F{Pb zxcaE;*8y7(*m4)vtq*?#+D;B;abPqOJ`4X4Zs%VJ&wRE5@OuFtmm_dE>#=eEZ+i|I z<@++jWA|qq7*p1selU&cWK^}If9(Xm4=>5%vM3*<57_!GwQrQ%#{jzqu*w$H*@wZF z%@fLTo^k*-o|jW}bU<5`2q&2dcn_Sm6Ywp#Pxx5bK2KFbPt2`z0rxiGdR@4i?e+y= zypkNN*n@oZ3zagnJ5bC;ki^W9(J(0l3Y$PuNuH9oes6 z9~A|t(V(*jbSiKF@gnX&A|H_96rGs>eHM5Zfk$~2p5pVfb`8J~{UQpOA;46mz$m>k z(A->`(j%&W_=+F1KI`J#?B^evpXloqXokbU{ichnd>644Hume4STU}4o4+97F9W{Q zg&*ZFW$pTdNusOx?gq{SIB*K4;5fZGb6e6H)bB;WKMD9=7hcIz#(W!3w=02`oNlSR z=~=K3Ui_VK@ka5+R(5nuRRHG%a8hF(@^b+v=Q5_Y0q!E;T3p(R9EEyR6_;TFa@ogy F{ugK9U^D;# diff --git a/pc-bios/npcm7xx_bootrom.bin b/pc-bios/npcm7xx_bootrom.bin index 903f126636f9ef5d1100c056656ccfb2b32e5e10..92282892b70dbcd6828c5054dff33ffe3385c485 100644 GIT binary patch delta 218 zcmZo*Tfn-ZgR!26f#J8ofBlEI{{Mf)!0`7!1H=FS4NU+4D=_{AV+Q4dtyd(DNipaw ze9g?taHn9eP6ms$Ny3-s+{*qRs+|P3PEPm#z~G{kA?7{HiTQ%0M}^D(?-Sl9We9`$ zVqkv9djW6hYVVCel}MH_TFGPFz+dgf%7AV11(R#0rhhO)&5q2sIM?+ddlRu;4y>3 zl&38Q^Pe&^1U+P8VBo4@nE#Z4q5r|Y|NmdJC`_q;T7V>(0hQbiluQ7M^B7zPDh__k z2Gq!;2sB(|!D9}^NlzIR8lSo-01Y)>@Gt^ok>G;I4vZ6lY6Bj#0M)uEPJGIw)AZ;( z&>SY7hDSihzK{T#4Wa|sLsv~;U|v H6ovr+8yI@# diff --git a/pc-bios/npcm8xx_bootrom.bin b/pc-bios/npcm8xx_bootrom.bin index 6370d6475635c4d445d2b927311edcd591949c82..45fb40fb5987ab681cc902659d85e76a7385cb53 100644 GIT binary patch delta 215 zcmaFBvVe6%2BV+@69X75fYFmH8MW&beohQhuv<{X$k23&QDEOE1r~;#P7Av(IlWl< z=~3sYPfi^#KRGit1Ti-(0;<_?iGgLqB^3sTpYs`*UN2;12vK2h+Bu(rIolD){#(6& zTzdWg|MW!0rXa% nCLPt3|NsB1Kx|@=0n$1^TmlsH0f{;C3Cv}h#~3)dkx3o^*ts%% From 13ed972b4ce57198914a37217251d30fbec20e41 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 4 Aug 2025 09:46:33 +0800 Subject: [PATCH 0208/1794] hw/ssi/aspeed_smc: Fix incorrect FMC_WDT2 register read on AST1030 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On AST1030, reading the FMC_WDT2 register always returns 0xFFFFFFFF. This issue is due to the aspeed_smc_read function, which checks for the ASPEED_SMC_FEATURE_WDT_CONTROL feature. Since AST1030 was missing this feature flag, the read operation fails and returns -1. To resolve this, add the WDT_CONTROL feature to AST1030's feature set so that FMC_WDT2 can be correctly accessed by firmware. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Fixes: 2850df6a81bcdc2e063dfdd56751ee2d11c58030 ("aspeed/smc: Add AST1030 support ") Link: https://lore.kernel.org/qemu-devel/20250804014633.512737-1-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 614528b8ef654..e33496f502987 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -1857,7 +1857,8 @@ static void aspeed_1030_fmc_class_init(ObjectClass *klass, const void *data) asc->resets = aspeed_1030_fmc_resets; asc->flash_window_base = 0x80000000; asc->flash_window_size = 0x10000000; - asc->features = ASPEED_SMC_FEATURE_DMA; + asc->features = ASPEED_SMC_FEATURE_DMA | + ASPEED_SMC_FEATURE_WDT_CONTROL; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x000BFFFC; asc->dma_start_length = 1; From 41ae2640c4d88dabed9469b3a3bda4d6c0317240 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 17 Jul 2025 21:59:28 +0300 Subject: [PATCH 0209/1794] docs/devel/submitting-a-patch.rst: add b4 section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a section about b4, an actively maintained and widely packaged CLI tool for contributing to patch-based development projects. Reviewed-by: Gustavo Romero Signed-off-by: Manos Pitsidianakis Message-ID: <20250717-docs_add_b4_section-v2-1-69212ed39299@linaro.org> Signed-off-by: Alex Bennée --- docs/devel/submitting-a-patch.rst | 40 +++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/docs/devel/submitting-a-patch.rst b/docs/devel/submitting-a-patch.rst index f7917b899f689..dd1cf32ad35a9 100644 --- a/docs/devel/submitting-a-patch.rst +++ b/docs/devel/submitting-a-patch.rst @@ -235,6 +235,38 @@ to another list.) ``git send-email`` (`step-by-step setup guide works best for delivering the patch without mangling it, but attachments can be used as a last resort on a first-time submission. +.. _use_b4: + +Use B4 +~~~~~~ + +The `b4`_ tool, used for Linux kernel development, can also be used for QEMU +development. It is packaged in most distros and PyPi. The QEMU source tree +includes a ``b4`` project configuration file at the root: ``.b4-config``. + +Example workflow to prepare a patch series: + +1. Start with a clean checkout of the ``master`` branch. +2. Create a new series with a topical branch name using ``b4 prep -n descriptive-name``. + ``b4`` will create a ``b4/descriptive-name`` branch and switch to it. +3. Commit your changes, following this page's guidelines about proper commit messages etc. +4. Write a descriptive cover letter with ``b4 prep --edit-cover``. +5. Add maintainer and reviewer CCs with ``b4 prep --auto-to-cc``. You can make + changes to Cc: and To: recipients by editing the cover letter. +6. Run patch checks with ``b4 prep --check``. +7. Optionally review the patches with ``b4 send --dry-run`` which will print the + raw patches in standard output. + +To send the patches, you can: + +- Setup ``git-send-email`` and use ``b4 send``, or +- Export the patches to files using ``b4 send -o OUTPUT_DIR`` and send them manually. + +For more details, consult the `b4 documentation`_. + +.. _b4 documentation: https://b4.docs.kernel.org/ +.. _b4: https://github.com/mricon/b4/ + .. _use_git_publish: Use git-publish @@ -418,7 +450,7 @@ Retrieve an existing series --------------------------- If you want to apply an existing series on top of your tree, you can simply use -`b4 `__. +`b4`_. :: @@ -533,7 +565,11 @@ summary belongs. The `git-publish `__ script can help with tracking a good summary across versions. Also, the `git-backport-diff `__ script can help focus -reviewers on what changed between revisions. +reviewers on what changed between revisions. The ``b4`` tool automatically +generates a version history section in the cover letter, including links to the +previous versions on `Lore`_. + +.. _Lore: https://lore.kernel.org/ .. _tips_and_tricks: From 0311a6edb9db34a41a2662d94c37e1fbaabf6ecf Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 21 Jul 2025 16:33:41 +0100 Subject: [PATCH 0210/1794] scripts/make-release: Go back to cloning all the EDK2 submodules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit bd0da3a3d4f we changed make-release so that instead of cloning every git submodule of EDK2 we only cloned a fixed list. The original motivation for this was that one of the submodules: * was from a non-github repo * that repo had a "SSL certificate expired" failure * wasn't actually needed for the set of EDK2 binaries we build and at the time we were trying to build the EDK2 binaries in one of our CI jobs. Unfortunately this change meant that we were exposed to bugs where EDK2 adds a new submodule and the sources we ship in the release tarball won't build any more. In particular, in EDK2 commit c6bb7d54beb05 the MipiSysTLib submodule was added, causing failure of the ROM build in our tarball starting from QEMU release 8.2.0: /tmp/qemu-10.0.0/roms/edk2/MdePkg/MdePkg.dec(32): error 000E: File/directory not found in workspace Library/MipiSysTLib/mipisyst/library/include is not found in packages path: /tmp/qemu-10.0.0/roms/. /tmp/qemu-10.0.0/roms/edk2 (Building from a QEMU git checkout works fine.) In the intervening time EDK2 moved the submodule that had a problem to be one they mirrored themselves (and at time of writing all their submodules are hosted on github), and we stopped trying to build EDK2 binaries in our own CI jobs with commit 690ceb71936f9037f6. Go back to cloning every EDK2 submodule, so we don't have an untested explicit list of submodules which will break without our noticing it. This increases the size of the QEMU tarball .tar.xz file from 133M to 139M in my testing. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3041 Signed-off-by: Peter Maydell Reviewed-by: Michael Tokarev Message-ID: <20250721153341.2910800-1-peter.maydell@linaro.org> Signed-off-by: Alex Bennée --- scripts/make-release | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/scripts/make-release b/scripts/make-release index 4509a9fabf500..87f563ef5f773 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -62,17 +62,15 @@ meson subprojects download $SUBPROJECTS (cd roms/skiboot && ./make_version.sh > .version) # Fetch edk2 submodule's submodules, since it won't have access to them via # the tarball later. -# -# A more uniform way to handle this sort of situation would be nice, but we -# don't necessarily have much control over how a submodule handles its -# submodule dependencies, so we continue to handle these on a case-by-case -# basis for now. -(cd roms/edk2 && \ - git submodule update --init --depth 1 -- \ - ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 \ - BaseTools/Source/C/BrotliCompress/brotli \ - CryptoPkg/Library/OpensslLib/openssl \ - MdeModulePkg/Library/BrotliCustomDecompressLib/brotli) + +# As recommended by the EDK2 readme, we don't use --recursive here. +# EDK2 won't use any code or feature from a submodule of a submodule, +# so we don't need to add them to the tarball. +# Although we don't necessarily need all of the submodules that EDK2 +# has, we clone them all, to avoid running into problems where EDK2 +# adds a new submodule or changes its use of an existing one and +# the sources we ship in the tarball then fail to build. +(cd roms/edk2 && git submodule update --init --depth 1) popd exclude=(--exclude=.git) From 61432e805e5028df0a3df5a76915cdc3007ecd41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 4 Aug 2025 11:43:08 +0100 Subject: [PATCH 0211/1794] tests/docker: fix debian-all-test-cross MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It turns out you can't easily expand an ENV var across multiple steps in a dockerfile. This meant we silently dropped the architectures we should have even on amd64 hosts. As the updated AVAILABLE_COMPILERS is only needed for the following apt install line just merge them. Fixes: 6da616bb170 (tests/docker: handle host-arch selection for all-test-cross) Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250804104308.250949-1-alex.bennee@linaro.org> --- tests/docker/dockerfiles/debian-all-test-cross.docker | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/docker/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker index ef69bbc8a518a..420a4e33e6087 100644 --- a/tests/docker/dockerfiles/debian-all-test-cross.docker +++ b/tests/docker/dockerfiles/debian-all-test-cross.docker @@ -62,9 +62,7 @@ RUN if dpkg-architecture -e amd64; then \ export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-powerpc64-linux-gnu libc6-dev-ppc64-cross"; \ export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-sparc64-linux-gnu libc6-dev-sparc64-cross"; \ fi && \ - echo "compilers: ${AVAILABLE_COMPILERS}" - -RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + DEBIAN_FRONTEND=noninteractive eatmydata \ apt install -y --no-install-recommends \ ${AVAILABLE_COMPILERS} && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt From 0a9a27305da030a83e65714f6ba202dbcaf448a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Jul 2025 15:56:39 +0200 Subject: [PATCH 0212/1794] hw/sd/sdcard: Do not ignore errors in sd_cmd_to_sendingdata() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unfortunately when adding sd_cmd_to_sendingdata() in commit f486bf7d109 we neglected to return any possible error. Fix. Fixes: f486bf7d109 ("hw/sd/sdcard: Introduce sd_cmd_to_sendingdata and sd_generic_read_byte") Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250804133406.17456-2-philmd@linaro.org> --- hw/sd/sd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index c275fdda2d02a..0bb385268edac 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1305,7 +1305,7 @@ static sd_rsp_type_t sd_cmd_to_sendingdata(SDState *sd, SDRequest req, const void *data, size_t size) { if (sd->state != sd_transfer_state) { - sd_invalid_state_for_cmd(sd, req); + return sd_invalid_state_for_cmd(sd, req); } sd->state = sd_sendingdata_state; From 3025ea65bd515196e871adc8959336c51b9d27bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 4 Aug 2025 11:32:44 +0200 Subject: [PATCH 0213/1794] hw/sd/sdcard: Factor sd_response_size() out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set @rsplen once before switching to fill the response buffer. This will allow to assert in a single place that the buffer is big enough to be filled with the response. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250804133406.17456-3-philmd@linaro.org> --- hw/sd/sd.c | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 0bb385268edac..76ce54664f29c 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -729,6 +729,33 @@ static int sd_req_crc_validate(SDRequest *req) return sd_crc7(buffer, 5) != req->crc; /* TODO */ } +static size_t sd_response_size(SDState *sd, sd_rsp_type_t rtype) +{ + switch (rtype) { + case sd_r1: + case sd_r1b: + return 4; + + case sd_r2_i: + case sd_r2_s: + return 16; + + case sd_r3: + case sd_r7: + return 4; + + case sd_r6: + return 4; + + case sd_r0: + case sd_illegal: + return 0; + + default: + g_assert_not_reached(); + } +} + static void sd_response_r1_make(SDState *sd, uint8_t *response) { stl_be_p(response, sd->card_status); @@ -2203,36 +2230,32 @@ static int sd_do_command(SDState *sd, SDRequest *req, } send_response: + rsplen = sd_response_size(sd, rtype); + switch (rtype) { case sd_r1: case sd_r1b: sd_response_r1_make(sd, response); - rsplen = 4; break; case sd_r2_i: memcpy(response, sd->cid, sizeof(sd->cid)); - rsplen = 16; break; case sd_r2_s: memcpy(response, sd->csd, sizeof(sd->csd)); - rsplen = 16; break; case sd_r3: sd_response_r3_make(sd, response); - rsplen = 4; break; case sd_r6: sd_response_r6_make(sd, response); - rsplen = 4; break; case sd_r7: sd_response_r7_make(sd, response); - rsplen = 4; break; case sd_r0: @@ -2244,7 +2267,6 @@ static int sd_do_command(SDState *sd, SDRequest *req, sd->data_offset = 0; /* fall-through */ case sd_illegal: - rsplen = 0; break; default: g_assert_not_reached(); From b82e7a2a1da5638c4c51fcf5a254b65762080b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 31 Jul 2025 13:55:25 +0200 Subject: [PATCH 0214/1794] hw/sd/sdbus: Provide buffer size to sdbus_do_command() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We provide to sdbus_do_command() a pointer to a buffer to be filled with a varying number of bytes. By not providing the buffer size, the callee can not check the buffer is big enough. Pass the buffer size as argument to follow good practices. sdbus_do_command() doesn't return any error, only the size filled in the buffer. Convert the returned type to unsigned and remove the few unreachable lines in callers. This allow to check for possible overflow in sd_do_command(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250804133406.17456-4-philmd@linaro.org> --- hw/sd/allwinner-sdhost.c | 7 ++----- hw/sd/bcm2835_sdhost.c | 7 ++----- hw/sd/core.c | 5 +++-- hw/sd/omap_mmc.c | 5 +++-- hw/sd/pl181.c | 6 ++---- hw/sd/sd.c | 6 ++++-- hw/sd/sdhci.c | 6 +++--- hw/sd/ssi-sd.c | 12 +++++++----- include/hw/sd/sd.h | 23 +++++++++++++++++++++-- 9 files changed, 47 insertions(+), 30 deletions(-) diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c index b31da5c399c10..9d61b372e7065 100644 --- a/hw/sd/allwinner-sdhost.c +++ b/hw/sd/allwinner-sdhost.c @@ -233,7 +233,7 @@ static void allwinner_sdhost_send_command(AwSdHostState *s) { SDRequest request; uint8_t resp[16]; - int rlen; + size_t rlen; /* Auto clear load flag */ s->command &= ~SD_CMDR_LOAD; @@ -246,10 +246,7 @@ static void allwinner_sdhost_send_command(AwSdHostState *s) request.arg = s->command_arg; /* Send request to SD bus */ - rlen = sdbus_do_command(&s->sdbus, &request, resp); - if (rlen < 0) { - goto error; - } + rlen = sdbus_do_command(&s->sdbus, &request, resp, sizeof(resp)); /* If the command has a response, store it in the response registers */ if ((s->command & SD_CMDR_RESPONSE)) { diff --git a/hw/sd/bcm2835_sdhost.c b/hw/sd/bcm2835_sdhost.c index 29debdf59e44e..f7cef7bb1cdf9 100644 --- a/hw/sd/bcm2835_sdhost.c +++ b/hw/sd/bcm2835_sdhost.c @@ -113,15 +113,12 @@ static void bcm2835_sdhost_send_command(BCM2835SDHostState *s) { SDRequest request; uint8_t rsp[16]; - int rlen; + size_t rlen; request.cmd = s->cmd & SDCMD_CMD_MASK; request.arg = s->cmdarg; - rlen = sdbus_do_command(&s->sdbus, &request, rsp); - if (rlen < 0) { - goto error; - } + rlen = sdbus_do_command(&s->sdbus, &request, rsp, sizeof(rsp)); if (!(s->cmd & SDCMD_NO_RESPONSE)) { if (rlen == 0 || (rlen == 4 && (s->cmd & SDCMD_LONG_RESPONSE))) { goto error; diff --git a/hw/sd/core.c b/hw/sd/core.c index 4b30218b5202a..d3c9017445e01 100644 --- a/hw/sd/core.c +++ b/hw/sd/core.c @@ -90,7 +90,8 @@ void sdbus_set_voltage(SDBus *sdbus, uint16_t millivolts) } } -int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response) +size_t sdbus_do_command(SDBus *sdbus, SDRequest *req, + uint8_t *resp, size_t respsz) { SDState *card = get_card(sdbus); @@ -98,7 +99,7 @@ int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response) if (card) { SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); - return sc->do_command(card, req, response); + return sc->do_command(card, req, resp, respsz); } return 0; diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index b7648d41cc53c..5a1d25defaaf5 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -130,7 +130,8 @@ static void omap_mmc_command(OMAPMMCState *host, int cmd, int dir, sd_rsp_type_t resptype, int init) { uint32_t rspstatus, mask; - int rsplen, timeout; + size_t rsplen; + int timeout; SDRequest request; uint8_t response[16]; @@ -157,7 +158,7 @@ static void omap_mmc_command(OMAPMMCState *host, int cmd, int dir, request.arg = host->arg; request.crc = 0; /* FIXME */ - rsplen = sdbus_do_command(&host->sdbus, &request, response); + rsplen = sdbus_do_command(&host->sdbus, &request, response, sizeof(response)); /* TODO: validate CRCs */ switch (resptype) { diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index b8fc9f86f13e7..5d56ead4d911e 100644 --- a/hw/sd/pl181.c +++ b/hw/sd/pl181.c @@ -173,14 +173,12 @@ static void pl181_do_command(PL181State *s) { SDRequest request; uint8_t response[16]; - int rlen; + size_t rlen; request.cmd = s->cmd & PL181_CMD_INDEX; request.arg = s->cmdarg; trace_pl181_command_send(request.cmd, request.arg); - rlen = sdbus_do_command(&s->sdbus, &request, response); - if (rlen < 0) - goto error; + rlen = sdbus_do_command(&s->sdbus, &request, response, sizeof(response)); if (s->cmd & PL181_CMD_RESPONSE) { if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP))) goto error; diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 76ce54664f29c..069107a2e7013 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2166,8 +2166,9 @@ static bool cmd_valid_while_locked(SDState *sd, unsigned cmd) return cmd_class == 0 || cmd_class == 7; } -static int sd_do_command(SDState *sd, SDRequest *req, - uint8_t *response) { +static size_t sd_do_command(SDState *sd, SDRequest *req, + uint8_t *response, size_t respsz) +{ int last_state; sd_rsp_type_t rtype; int rsplen; @@ -2231,6 +2232,7 @@ static int sd_do_command(SDState *sd, SDRequest *req, send_response: rsplen = sd_response_size(sd, rtype); + assert(rsplen <= respsz); switch (rtype) { case sd_r1: diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 226ff133ff9fd..3c897e54b7210 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -337,7 +337,7 @@ static void sdhci_send_command(SDHCIState *s) { SDRequest request; uint8_t response[16]; - int rlen; + size_t rlen; bool timeout = false; s->errintsts = 0; @@ -346,7 +346,7 @@ static void sdhci_send_command(SDHCIState *s) request.arg = s->argument; trace_sdhci_send_command(request.cmd, request.arg); - rlen = sdbus_do_command(&s->sdbus, &request, response); + rlen = sdbus_do_command(&s->sdbus, &request, response, sizeof(response)); if (s->cmdreg & SDHC_CMD_RESPONSE) { if (rlen == 4) { @@ -400,7 +400,7 @@ static void sdhci_end_transfer(SDHCIState *s) request.cmd = 0x0C; request.arg = 0; trace_sdhci_end_transfer(request.cmd, request.arg); - sdbus_do_command(&s->sdbus, &request, response); + sdbus_do_command(&s->sdbus, &request, response, sizeof(response)); /* Auto CMD12 response goes to the upper Response register */ s->rspreg[3] = ldl_be_p(response); } diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 6c90a86ab4164..3025f8f15f462 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -146,8 +146,9 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) /* manually issue cmd12 to stop the transfer */ request.cmd = 12; request.arg = 0; - s->arglen = sdbus_do_command(&s->sdbus, &request, longresp); - if (s->arglen <= 0) { + s->arglen = sdbus_do_command(&s->sdbus, &request, + longresp, sizeof(longresp)); + if (s->arglen == 0) { s->arglen = 1; /* a zero value indicates the card is busy */ s->response[0] = 0; @@ -171,8 +172,9 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) request.cmd = s->cmd; request.arg = ldl_be_p(s->cmdarg); DPRINTF("CMD%d arg 0x%08x\n", s->cmd, request.arg); - s->arglen = sdbus_do_command(&s->sdbus, &request, longresp); - if (s->arglen <= 0) { + s->arglen = sdbus_do_command(&s->sdbus, &request, + longresp, sizeof(longresp)); + if (s->arglen == 0) { s->arglen = 1; s->response[0] = 4; DPRINTF("SD command failed\n"); @@ -333,7 +335,7 @@ static int ssi_sd_post_load(void *opaque, int version_id) return -EINVAL; } if (s->mode == SSI_SD_CMDARG && - (s->arglen < 0 || s->arglen >= ARRAY_SIZE(s->cmdarg))) { + (s->arglen >= ARRAY_SIZE(s->cmdarg))) { return -EINVAL; } if (s->mode == SSI_SD_RESPONSE && diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h index d6bad175131c2..55d363f58fb17 100644 --- a/include/hw/sd/sd.h +++ b/include/hw/sd/sd.h @@ -96,7 +96,17 @@ struct SDCardClass { DeviceClass parent_class; /*< public >*/ - int (*do_command)(SDState *sd, SDRequest *req, uint8_t *response); + /** + * Process a SD command request. + * @sd: card + * @req: command request + * @resp: buffer to receive the command response + * @respsz: size of @resp buffer + * + * Return: size of the response + */ + size_t (*do_command)(SDState *sd, SDRequest *req, + uint8_t *resp, size_t respsz); /** * Write a byte to a SD card. * @sd: card @@ -153,7 +163,16 @@ struct SDBusClass { void sdbus_set_voltage(SDBus *sdbus, uint16_t millivolts); uint8_t sdbus_get_dat_lines(SDBus *sdbus); bool sdbus_get_cmd_line(SDBus *sdbus); -int sdbus_do_command(SDBus *sd, SDRequest *req, uint8_t *response); +/** + * sdbus_do_command: Process a SD command request + * @sd: card + * @req: command request + * @resp: buffer to receive the command response + * @respsz: size of @resp buffer + * + * Return: size of the response + */ +size_t sdbus_do_command(SDBus *sd, SDRequest *req, uint8_t *resp, size_t respsz); /** * Write a byte to a SD bus. * @sd: bus From 1585ab9f1baf6f0b471483455a74ea7abe9add0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Jul 2025 14:04:43 +0200 Subject: [PATCH 0215/1794] hw/sd/sdcard: Fill SPI response bits in card code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ssi-sd.c contains the SPI link layer adaptation, while sd.c contains all the SD card internal details. We already handle the response values in sd.c, but missed the SPI case. Complete them (fill R1, prepend R1 in R3/R7 and always return something in SPI mode). Remove all the duplication in ssi-sd.c. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250804133406.17456-5-philmd@linaro.org> --- hw/sd/sd.c | 32 ++++++++++++++++--- hw/sd/ssi-sd.c | 87 ++++---------------------------------------------- 2 files changed, 35 insertions(+), 84 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 069107a2e7013..cbcc180f6a455 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -734,22 +734,24 @@ static size_t sd_response_size(SDState *sd, sd_rsp_type_t rtype) switch (rtype) { case sd_r1: case sd_r1b: - return 4; + return sd_is_spi(sd) ? 1 : 4; case sd_r2_i: case sd_r2_s: + assert(!sd_is_spi(sd)); return 16; case sd_r3: case sd_r7: - return 4; + return sd_is_spi(sd) ? 5 : 4; case sd_r6: + assert(!sd_is_spi(sd)); return 4; case sd_r0: case sd_illegal: - return 0; + return sd_is_spi(sd) ? 1 : 0; default: g_assert_not_reached(); @@ -758,7 +760,19 @@ static size_t sd_response_size(SDState *sd, sd_rsp_type_t rtype) static void sd_response_r1_make(SDState *sd, uint8_t *response) { - stl_be_p(response, sd->card_status); + if (sd_is_spi(sd)) { + response[0] = sd->state == sd_idle_state + && !FIELD_EX32(sd->ocr, OCR, CARD_POWER_UP); + response[0] |= FIELD_EX32(sd->card_status, CSR, ERASE_RESET) << 1; + response[0] |= FIELD_EX32(sd->card_status, CSR, ILLEGAL_COMMAND) << 2; + response[0] |= FIELD_EX32(sd->card_status, CSR, COM_CRC_ERROR) << 3; + response[0] |= FIELD_EX32(sd->card_status, CSR, ERASE_SEQ_ERROR) << 4; + response[0] |= FIELD_EX32(sd->card_status, CSR, ADDRESS_ERROR) << 5; + response[0] |= FIELD_EX32(sd->card_status, CSR, BLOCK_LEN_ERROR) << 6; + response[0] |= 0 << 7; + } else { + stl_be_p(response, sd->card_status); + } /* Clear the "clear on read" status bits */ sd->card_status &= ~CARD_STATUS_C; @@ -766,6 +780,11 @@ static void sd_response_r1_make(SDState *sd, uint8_t *response) static void sd_response_r3_make(SDState *sd, uint8_t *response) { + if (sd_is_spi(sd)) { + /* Prepend R1 */ + sd_response_r1_make(sd, response); + response++; + } stl_be_p(response, sd->ocr & ACMD41_R3_MASK); } @@ -783,6 +802,11 @@ static void sd_response_r6_make(SDState *sd, uint8_t *response) static void sd_response_r7_make(SDState *sd, uint8_t *response) { + if (sd_is_spi(sd)) { + /* Prepend R1 */ + sd_response_r1_make(sd, response); + response++; + } stl_be_p(response, sd->vhs); } diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 3025f8f15f462..2d5c0ad5016bb 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -70,23 +70,6 @@ struct ssi_sd_state { #define TYPE_SSI_SD "ssi-sd" OBJECT_DECLARE_SIMPLE_TYPE(ssi_sd_state, SSI_SD) -/* State word bits. */ -#define SSI_SDR_LOCKED 0x0001 -#define SSI_SDR_WP_ERASE 0x0002 -#define SSI_SDR_ERROR 0x0004 -#define SSI_SDR_CC_ERROR 0x0008 -#define SSI_SDR_ECC_FAILED 0x0010 -#define SSI_SDR_WP_VIOLATION 0x0020 -#define SSI_SDR_ERASE_PARAM 0x0040 -#define SSI_SDR_OUT_OF_RANGE 0x0080 -#define SSI_SDR_IDLE 0x0100 -#define SSI_SDR_ERASE_RESET 0x0200 -#define SSI_SDR_ILLEGAL_COMMAND 0x0400 -#define SSI_SDR_COM_CRC_ERROR 0x0800 -#define SSI_SDR_ERASE_SEQ_ERROR 0x1000 -#define SSI_SDR_ADDRESS_ERROR 0x2000 -#define SSI_SDR_PARAMETER_ERROR 0x4000 - /* multiple block write */ #define SSI_TOKEN_MULTI_WRITE 0xfc /* terminate multiple block write */ @@ -104,7 +87,7 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) { ssi_sd_state *s = SSI_SD(dev); SDRequest request; - uint8_t longresp[16]; + uint8_t longresp[5]; /* * Special case: allow CMD12 (STOP TRANSMISSION) while reading data. @@ -171,74 +154,18 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) /* FIXME: Check CRC. */ request.cmd = s->cmd; request.arg = ldl_be_p(s->cmdarg); - DPRINTF("CMD%d arg 0x%08x\n", s->cmd, request.arg); s->arglen = sdbus_do_command(&s->sdbus, &request, longresp, sizeof(longresp)); - if (s->arglen == 0) { - s->arglen = 1; - s->response[0] = 4; - DPRINTF("SD command failed\n"); - } else if (s->cmd == 8 || s->cmd == 58) { - /* CMD8/CMD58 returns R3/R7 response */ - DPRINTF("Returned R3/R7\n"); - s->arglen = 5; - s->response[0] = 1; - memcpy(&s->response[1], longresp, 4); - } else if (s->arglen != 4) { - BADF("Unexpected response to cmd %d\n", s->cmd); - /* Illegal command is about as near as we can get. */ - s->arglen = 1; - s->response[0] = 4; - } else { - /* All other commands return status. */ - uint32_t cardstatus; - uint16_t status; + DPRINTF("CMD%d arg 0x%08x = %d\n", s->cmd, request.arg, s->arglen); + assert(s->arglen > 0); /* CMD13 returns a 2-byte statuse work. Other commands only return the first byte. */ s->arglen = (s->cmd == 13) ? 2 : 1; + memcpy(s->response, longresp, s->arglen); - /* handle R1b */ - if (s->cmd == 28 || s->cmd == 29 || s->cmd == 38) { - s->stopping = 1; - } - - cardstatus = ldl_be_p(longresp); - status = 0; - if (((cardstatus >> 9) & 0xf) < 4) - status |= SSI_SDR_IDLE; - if (cardstatus & ERASE_RESET) - status |= SSI_SDR_ERASE_RESET; - if (cardstatus & ILLEGAL_COMMAND) - status |= SSI_SDR_ILLEGAL_COMMAND; - if (cardstatus & COM_CRC_ERROR) - status |= SSI_SDR_COM_CRC_ERROR; - if (cardstatus & ERASE_SEQ_ERROR) - status |= SSI_SDR_ERASE_SEQ_ERROR; - if (cardstatus & ADDRESS_ERROR) - status |= SSI_SDR_ADDRESS_ERROR; - if (cardstatus & CARD_IS_LOCKED) - status |= SSI_SDR_LOCKED; - if (cardstatus & (LOCK_UNLOCK_FAILED | WP_ERASE_SKIP)) - status |= SSI_SDR_WP_ERASE; - if (cardstatus & SD_ERROR) - status |= SSI_SDR_ERROR; - if (cardstatus & CC_ERROR) - status |= SSI_SDR_CC_ERROR; - if (cardstatus & CARD_ECC_FAILED) - status |= SSI_SDR_ECC_FAILED; - if (cardstatus & WP_VIOLATION) - status |= SSI_SDR_WP_VIOLATION; - if (cardstatus & ERASE_PARAM) - status |= SSI_SDR_ERASE_PARAM; - if (cardstatus & (OUT_OF_RANGE | CID_CSD_OVERWRITE)) - status |= SSI_SDR_OUT_OF_RANGE; - /* ??? Don't know what Parameter Error really means, so - assume it's set if the second byte is nonzero. */ - if (status & 0xff) - status |= SSI_SDR_PARAMETER_ERROR; - s->response[0] = status >> 8; - s->response[1] = status; - DPRINTF("Card status 0x%02x\n", status); + /* handle R1b (busy signal) */ + if (s->cmd == 28 || s->cmd == 29 || s->cmd == 38) { + s->stopping = 1; } s->mode = SSI_SD_PREP_RESP; s->response_pos = 0; From 0f2ff994793247c710c79f95302f3e8c10d070de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Jul 2025 14:05:09 +0200 Subject: [PATCH 0216/1794] hw/sd/sdcard: Implement SPI R2 return value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In SPI mode, R2 is a 2-byte value. Implement in spi_response_r2_make() and return SPI R2 in the SEND_STATUS commands. Reported-by: Guenter Roeck Fixes: 775616c3ae8 ("Partial SD card SPI mode support") Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250804133406.17456-6-philmd@linaro.org> --- hw/sd/sd.c | 38 +++++++++++++++++++++++++++++++++++--- hw/sd/ssi-sd.c | 3 --- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index cbcc180f6a455..01ec6d951c8ea 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -61,6 +61,7 @@ typedef enum { sd_r0 = 0, /* no response */ sd_r1, /* normal response command */ + spi_r2, /* STATUS */ sd_r2_i, /* CID register */ sd_r2_s, /* CSD register */ sd_r3, /* OCR register */ @@ -247,6 +248,7 @@ static const char *sd_response_name(sd_rsp_type_t rsp) static const char *response_name[] = { [sd_r0] = "RESP#0 (no response)", [sd_r1] = "RESP#1 (normal cmd)", + [spi_r2] = "RESP#2 (STATUS reg)", [sd_r2_i] = "RESP#2 (CID reg)", [sd_r2_s] = "RESP#2 (CSD reg)", [sd_r3] = "RESP#3 (OCR reg)", @@ -736,6 +738,10 @@ static size_t sd_response_size(SDState *sd, sd_rsp_type_t rtype) case sd_r1b: return sd_is_spi(sd) ? 1 : 4; + case spi_r2: + assert(sd_is_spi(sd)); + return 2; + case sd_r2_i: case sd_r2_s: assert(!sd_is_spi(sd)); @@ -778,6 +784,22 @@ static void sd_response_r1_make(SDState *sd, uint8_t *response) sd->card_status &= ~CARD_STATUS_C; } +static void spi_response_r2_make(SDState *sd, uint8_t *resp) +{ + /* Prepend R1 */ + sd_response_r1_make(sd, resp); + + resp[1] = FIELD_EX32(sd->card_status, CSR, CARD_IS_LOCKED) << 0; + resp[1] |= (FIELD_EX32(sd->card_status, CSR, LOCK_UNLOCK_FAILED) + || FIELD_EX32(sd->card_status, CSR, WP_ERASE_SKIP)) << 1; + resp[1] |= FIELD_EX32(sd->card_status, CSR, ERROR) << 2; + resp[1] |= FIELD_EX32(sd->card_status, CSR, CC_ERROR) << 3; + resp[1] |= FIELD_EX32(sd->card_status, CSR, CARD_ECC_FAILED) << 4; + resp[1] |= FIELD_EX32(sd->card_status, CSR, WP_VIOLATION) << 5; + resp[1] |= FIELD_EX32(sd->card_status, CSR, ERASE_PARAM) << 6; + resp[1] |= FIELD_EX32(sd->card_status, CSR, OUT_OF_RANGE) << 7; +} + static void sd_response_r3_make(SDState *sd, uint8_t *response) { if (sd_is_spi(sd)) { @@ -1643,7 +1665,7 @@ static sd_rsp_type_t sd_cmd_SEND_STATUS(SDState *sd, SDRequest req) } if (sd_is_spi(sd)) { - return sd_r2_s; + return spi_r2; } return sd_req_rca_same(sd, req) ? sd_r1 : sd_r0; @@ -1957,8 +1979,14 @@ static sd_rsp_type_t sd_acmd_SET_BUS_WIDTH(SDState *sd, SDRequest req) /* ACMD13 */ static sd_rsp_type_t sd_acmd_SD_STATUS(SDState *sd, SDRequest req) { - return sd_cmd_to_sendingdata(sd, req, 0, - sd->sd_status, sizeof(sd->sd_status)); + sd_rsp_type_t rsp; + + rsp = sd_cmd_to_sendingdata(sd, req, 0, + sd->sd_status, sizeof(sd->sd_status)); + if (sd_is_spi(sd) && rsp != sd_illegal) { + return spi_r2; + } + return rsp; } /* ACMD22 */ @@ -2264,6 +2292,10 @@ static size_t sd_do_command(SDState *sd, SDRequest *req, sd_response_r1_make(sd, response); break; + case spi_r2: + spi_response_r2_make(sd, response); + break; + case sd_r2_i: memcpy(response, sd->cid, sizeof(sd->cid)); break; diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 2d5c0ad5016bb..594dead19eea1 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -158,9 +158,6 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) longresp, sizeof(longresp)); DPRINTF("CMD%d arg 0x%08x = %d\n", s->cmd, request.arg, s->arglen); assert(s->arglen > 0); - /* CMD13 returns a 2-byte statuse work. Other commands - only return the first byte. */ - s->arglen = (s->cmd == 13) ? 2 : 1; memcpy(s->response, longresp, s->arglen); /* handle R1b (busy signal) */ From 3241a61a1374b4d9b0e835fb3fa5b4085377eebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 30 Jul 2025 17:35:06 +0200 Subject: [PATCH 0217/1794] hw/sd/sdcard: Use complete SEND_OP_COND implementation in SPI mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While spi_cmd_SEND_OP_COND() is incomplete, sd_cmd_SEND_OP_COND() is, except it doesn't return the correct value in SPI mode. Correct and use, removing the need for spi_cmd_SEND_OP_COND(). Fixes: 775616c3ae8 ("Partial SD card SPI mode support") Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250804133406.17456-7-philmd@linaro.org> --- hw/sd/sd.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 01ec6d951c8ea..df2a272c6a205 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1414,14 +1414,6 @@ static sd_rsp_type_t sd_cmd_GO_IDLE_STATE(SDState *sd, SDRequest req) return sd_is_spi(sd) ? sd_r1 : sd_r0; } -/* CMD1 */ -static sd_rsp_type_t spi_cmd_SEND_OP_COND(SDState *sd, SDRequest req) -{ - sd->state = sd_transfer_state; - - return sd_r1; -} - /* CMD2 */ static sd_rsp_type_t sd_cmd_ALL_SEND_CID(SDState *sd, SDRequest req) { @@ -2046,6 +2038,9 @@ static sd_rsp_type_t sd_cmd_SEND_OP_COND(SDState *sd, SDRequest req) sd->state = sd_ready_state; } + if (sd_is_spi(sd)) { + return sd_r1; + } return sd_r3; } @@ -2590,7 +2585,7 @@ static const SDProto sd_proto_spi = { .name = "SPI", .cmd = { [0] = {0, sd_spi, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, - [1] = {0, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, + [1] = {0, sd_spi, "SEND_OP_COND", sd_cmd_SEND_OP_COND}, [5] = {9, sd_spi, "IO_SEND_OP_COND", sd_cmd_optional}, [6] = {10, sd_spi, "SWITCH_FUNCTION", sd_cmd_SWITCH_FUNCTION}, [8] = {0, sd_spi, "SEND_IF_COND", sd_cmd_SEND_IF_COND}, @@ -2626,7 +2621,7 @@ static const SDProto sd_proto_spi = { [13] = {8, sd_spi, "SD_STATUS", sd_acmd_SD_STATUS}, [22] = {8, sd_spi, "SEND_NUM_WR_BLOCKS", sd_acmd_SEND_NUM_WR_BLOCKS}, [23] = {8, sd_spi, "SET_WR_BLK_ERASE_COUNT", sd_acmd_SET_WR_BLK_ERASE_COUNT}, - [41] = {8, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, + [41] = {8, sd_spi, "SEND_OP_COND", sd_cmd_SEND_OP_COND}, [42] = {8, sd_spi, "SET_CLR_CARD_DETECT", sd_acmd_SET_CLR_CARD_DETECT}, [51] = {8, sd_spi, "SEND_SCR", sd_acmd_SEND_SCR}, }, From 3c7bde41a37546a49e10adafc54e9201ac087585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 31 Jul 2025 11:17:52 +0200 Subject: [PATCH 0218/1794] hw/sd/sdcard: Allow using SWITCH_FUNCTION in more SPI states MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In SPI mode, SWITCH_FUNCTION is valid in all mode (except the IDLE one). Fixes: 775616c3ae8 ("Partial SD card SPI mode support") Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250804133406.17456-8-philmd@linaro.org> --- hw/sd/sd.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index df2a272c6a205..a9efa1585941e 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1488,8 +1488,14 @@ static sd_rsp_type_t sd_cmd_SWITCH_FUNCTION(SDState *sd, SDRequest req) if (sd->mode != sd_data_transfer_mode) { return sd_invalid_mode_for_cmd(sd, req); } - if (sd->state != sd_transfer_state) { - return sd_invalid_state_for_cmd(sd, req); + if (sd_is_spi(sd)) { + if (sd->state == sd_idle_state) { + return sd_invalid_state_for_cmd(sd, req); + } + } else { + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } } sd_function_switch(sd, req.arg); From 7574baef43f0d1bd6982be7d5087af3bca2a7a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 31 Jul 2025 15:38:18 +0200 Subject: [PATCH 0219/1794] hw/sd/sdcard: Factor spi_cmd_SEND_CxD() out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spi_cmd_SEND_CSD() and spi_cmd_SEND_CID() are very similar. Factor the common code as spi_cmd_SEND_CxD(). Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250804133406.17456-9-philmd@linaro.org> --- hw/sd/sd.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index a9efa1585941e..ee81dc0999146 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1588,14 +1588,19 @@ static sd_rsp_type_t emmc_cmd_SEND_EXT_CSD(SDState *sd, SDRequest req) sd->ext_csd, sizeof(sd->ext_csd)); } -/* CMD9 */ -static sd_rsp_type_t spi_cmd_SEND_CSD(SDState *sd, SDRequest req) +static sd_rsp_type_t spi_cmd_SEND_CxD(SDState *sd, SDRequest req, + const void *data, size_t size) { if (sd->state != sd_standby_state) { return sd_invalid_state_for_cmd(sd, req); } - return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req), - sd->csd, 16); + return sd_cmd_to_sendingdata(sd, req, 0, data, size); +} + +/* CMD9 */ +static sd_rsp_type_t spi_cmd_SEND_CSD(SDState *sd, SDRequest req) +{ + return spi_cmd_SEND_CxD(sd, req, sd->csd, sizeof(sd->csd)); } static sd_rsp_type_t sd_cmd_SEND_CSD(SDState *sd, SDRequest req) @@ -1610,11 +1615,7 @@ static sd_rsp_type_t sd_cmd_SEND_CSD(SDState *sd, SDRequest req) /* CMD10 */ static sd_rsp_type_t spi_cmd_SEND_CID(SDState *sd, SDRequest req) { - if (sd->state != sd_standby_state) { - return sd_invalid_state_for_cmd(sd, req); - } - return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req), - sd->cid, 16); + return spi_cmd_SEND_CxD(sd, req, sd->cid, sizeof(sd->cid)); } static sd_rsp_type_t sd_cmd_SEND_CID(SDState *sd, SDRequest req) From 823d9b00452b62dcee1b0692c578c6fa5eef517e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 31 Jul 2025 15:58:31 +0200 Subject: [PATCH 0220/1794] hw/sd/sdcard: Disable checking STBY mode in SPI SEND_CSD/CID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The card should be in STANDBY mode to process SEND_CSD or SEND_CID, but is still in IDLE mode. Unfortunately I don't have enough time to keep debugging this issue, so disable the check for the time being and the next release, as it blocks Linux. I'll keep looking. Reported-by: Guenter Roeck Reported-by: Ben Dooks Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250804133406.17456-10-philmd@linaro.org> --- hw/sd/sd.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index ee81dc0999146..22f30997713c5 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1591,9 +1591,20 @@ static sd_rsp_type_t emmc_cmd_SEND_EXT_CSD(SDState *sd, SDRequest req) static sd_rsp_type_t spi_cmd_SEND_CxD(SDState *sd, SDRequest req, const void *data, size_t size) { + /* + * XXX as of v10.1.0-rc1 command is reached in sd_idle_state, + * so disable this check. if (sd->state != sd_standby_state) { return sd_invalid_state_for_cmd(sd, req); } + */ + + /* + * Since SPI returns CSD and CID on the DAT lines, + * switch to sd_transfer_state. + */ + sd->state = sd_transfer_state; + return sd_cmd_to_sendingdata(sd, req, 0, data, size); } From 40b242884ed7aeb30613dd42eca6e8712d607c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 31 Jul 2025 11:44:28 +0200 Subject: [PATCH 0221/1794] hw/sd/sdcard: Remove SDState::mode field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SD card mode is a superset of its state (SDState::state), no need to migrate it. Use sd_mode() to get the SDCardModes from the SDCardStates. Fixes: 50a5be6c3d5 ("hw/sd.c: add SD card save/load support") Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250804133406.17456-11-philmd@linaro.org> --- hw/sd/sd.c | 35 +++++++++++++++++------------------ hw/sd/trace-events | 4 ++-- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 22f30997713c5..8c290595f011a 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -147,7 +147,6 @@ struct SDState { /* Runtime changeables */ - uint32_t mode; /* current card mode, one of SDCardModes */ int32_t state; /* current card state, one of SDCardStates */ uint32_t vhs; bool wp_switch; @@ -315,27 +314,24 @@ static void sd_set_voltage(SDState *sd, uint16_t millivolts) } } -static void sd_set_mode(SDState *sd) +static enum SDCardModes sd_mode(SDState *sd) { switch (sd->state) { case sd_inactive_state: - sd->mode = sd_inactive; - break; - + return sd_inactive; case sd_idle_state: case sd_ready_state: case sd_identification_state: - sd->mode = sd_card_identification_mode; - break; - + return sd_card_identification_mode; case sd_standby_state: case sd_transfer_state: case sd_sendingdata_state: case sd_receivingdata_state: case sd_programming_state: case sd_disconnect_state: - sd->mode = sd_data_transfer_mode; - break; + return sd_data_transfer_mode; + default: + g_assert_not_reached(); } } @@ -1025,7 +1021,7 @@ static const VMStateDescription sd_vmstate = { .minimum_version_id = 2, .pre_load = sd_vmstate_pre_load, .fields = (const VMStateField[]) { - VMSTATE_UINT32(mode, SDState), + VMSTATE_UNUSED(4), VMSTATE_INT32(state, SDState), VMSTATE_UINT8_ARRAY(cid, SDState, 16), VMSTATE_UINT8_ARRAY(csd, SDState, 16), @@ -1325,7 +1321,7 @@ static sd_rsp_type_t sd_invalid_state_for_cmd(SDState *sd, SDRequest req) static sd_rsp_type_t sd_invalid_mode_for_cmd(SDState *sd, SDRequest req) { qemu_log_mask(LOG_GUEST_ERROR, "%s: CMD%i in a wrong mode: %s (spec %s)\n", - sd->proto->name, req.cmd, sd_mode_name(sd->mode), + sd->proto->name, req.cmd, sd_mode_name(sd_mode(sd)), sd_version_str(sd->spec_version)); return sd_illegal; @@ -1485,7 +1481,7 @@ static sd_rsp_type_t emmc_cmd_sleep_awake(SDState *sd, SDRequest req) /* CMD6 */ static sd_rsp_type_t sd_cmd_SWITCH_FUNCTION(SDState *sd, SDRequest req) { - if (sd->mode != sd_data_transfer_mode) { + if (sd_mode(sd) != sd_data_transfer_mode) { return sd_invalid_mode_for_cmd(sd, req); } if (sd_is_spi(sd)) { @@ -1658,7 +1654,7 @@ static sd_rsp_type_t sd_cmd_STOP_TRANSMISSION(SDState *sd, SDRequest req) /* CMD13 */ static sd_rsp_type_t sd_cmd_SEND_STATUS(SDState *sd, SDRequest req) { - if (sd->mode != sd_data_transfer_mode) { + if (sd_mode(sd) != sd_data_transfer_mode) { return sd_invalid_mode_for_cmd(sd, req); } @@ -1684,7 +1680,7 @@ static sd_rsp_type_t sd_cmd_SEND_STATUS(SDState *sd, SDRequest req) /* CMD15 */ static sd_rsp_type_t sd_cmd_GO_INACTIVE_STATE(SDState *sd, SDRequest req) { - if (sd->mode != sd_data_transfer_mode) { + if (sd_mode(sd) != sd_data_transfer_mode) { return sd_invalid_mode_for_cmd(sd, req); } switch (sd->state) { @@ -2090,7 +2086,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) if (req.cmd != 55 || sd->expecting_acmd) { trace_sdcard_normal_command(sd->proto->name, sd->last_cmd_name, req.cmd, - req.arg, sd_state_name(sd->state)); + req.arg, + sd_mode_name(sd_mode(sd)), + sd_state_name(sd->state)); } /* Not interpreting this as an app command */ @@ -2176,7 +2174,9 @@ static sd_rsp_type_t sd_app_command(SDState *sd, { sd->last_cmd_name = sd_acmd_name(sd, req.cmd); trace_sdcard_app_command(sd->proto->name, sd->last_cmd_name, - req.cmd, req.arg, sd_state_name(sd->state)); + req.cmd, req.arg, + sd_mode_name(sd_mode(sd)), + sd_state_name(sd->state)); sd->card_status |= APP_CMD; if (sd->proto->acmd[req.cmd].handler) { @@ -2276,7 +2276,6 @@ static size_t sd_do_command(SDState *sd, SDRequest *req, } last_state = sd->state; - sd_set_mode(sd); if (sd->expecting_acmd) { sd->expecting_acmd = false; diff --git a/hw/sd/trace-events b/hw/sd/trace-events index db0644256d913..8d49840917e22 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -37,8 +37,8 @@ sdhci_write_dataport(uint16_t data_count) "write buffer filled with %u bytes of sdhci_capareg(const char *desc, uint16_t val) "%s: %u" # sd.c -sdcard_normal_command(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t arg, const char *state) "%s %20s/ CMD%02d arg 0x%08x (state %s)" -sdcard_app_command(const char *proto, const char *acmd_desc, uint8_t acmd, uint32_t arg, const char *state) "%s %23s/ACMD%02d arg 0x%08x (state %s)" +sdcard_normal_command(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t arg, const char *mode, const char *state) "%s %20s/ CMD%02d arg 0x%08x (mode %s, state %s)" +sdcard_app_command(const char *proto, const char *acmd_desc, uint8_t acmd, uint32_t arg, const char *mode, const char *state) "%s %23s/ACMD%02d arg 0x%08x (mode %s, state %s)" sdcard_response(const char *rspdesc, int rsplen) "%s (sz:%d)" sdcard_powerup(void) "" sdcard_inquiry_cmd41(void) "" From 90fd1591314781e7da8560e4008f411aba501b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 30 Jul 2025 17:10:24 +0200 Subject: [PATCH 0222/1794] tests/functional: Test SD cards in SPI mode (using sifive_u machine) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test which uses the sifive_u machine to boot a Linux kernel from a SD card connected via a SPI interface. Inspired from the command provided in: - https://lore.kernel.org/qemu-devel/94b2c5bf-53d0-4c74-8264-f3021916f38c@roeck-us.net/ - https://lore.kernel.org/qemu-devel/840016d0-0d49-4ef4-8372-b62b3bcd0ac6@codethink.co.uk/ Inspired-by: Guenter Roeck Inspired-by: Ben Dooks Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250804133406.17456-12-philmd@linaro.org> --- MAINTAINERS | 1 + tests/functional/meson.build | 1 + tests/functional/test_riscv64_sifive_u.py | 51 +++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100755 tests/functional/test_riscv64_sifive_u.py diff --git a/MAINTAINERS b/MAINTAINERS index 28cea342718db..a07086ed76213 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1695,6 +1695,7 @@ S: Supported F: docs/system/riscv/sifive_u.rst F: hw/*/*sifive*.c F: include/hw/*/*sifive*.h +F: tests/functional/test_riscv64_sifive_u.py AMD Microblaze-V Generic Board M: Sai Pavan Boddu diff --git a/tests/functional/meson.build b/tests/functional/meson.build index ecf965adc6c45..311c6f1806508 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -274,6 +274,7 @@ tests_riscv64_system_quick = [ ] tests_riscv64_system_thorough = [ + 'riscv64_sifive_u', 'riscv64_tuxrun', ] diff --git a/tests/functional/test_riscv64_sifive_u.py b/tests/functional/test_riscv64_sifive_u.py new file mode 100755 index 0000000000000..dc4cb8a4a96f3 --- /dev/null +++ b/tests/functional/test_riscv64_sifive_u.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a Sifive U machine +# and checks the console +# +# Copyright (c) Linaro Ltd. +# +# Author: +# Philippe Mathieu-Daudé +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import Asset, LinuxKernelTest +from qemu_test import skipIfMissingCommands + + +class SifiveU(LinuxKernelTest): + + ASSET_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/riscv64/Image', + '2bd8132a3bf21570290042324fff48c987f42f2a00c08de979f43f0662ebadba') + ASSET_ROOTFS = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '9819da19e6eef291686fdd7b029ea00e764dc62f/rootfs/riscv64/' + 'rootfs.ext2.gz'), + 'b6ed95610310b7956f9bf20c4c9c0c05fea647900df441da9dfe767d24e8b28b') + + def test_riscv64_sifive_u_mmc_spi(self): + self.set_machine('sifive_u') + kernel_path = self.ASSET_KERNEL.fetch() + rootfs_path = self.uncompress(self.ASSET_ROOTFS) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'root=/dev/mmcblk0 rootwait ' + 'earlycon=sbi console=ttySIF0 ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-drive', f'file={rootfs_path},if=sd,format=raw', + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + os.remove(rootfs_path) + + +if __name__ == '__main__': + LinuxKernelTest.main() From 4e5d58969ed6927a7f1b08ba6223c34c8b990c92 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 4 Aug 2025 13:35:48 +0800 Subject: [PATCH 0223/1794] target/i386/cpu: Move addressable ID encoding out of compat property in CPUID[0x1] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the addressable ID encoding for CPUID[0x1].EBX[bits 16-23] (Maximum number of addressable IDs for logical processors in this physical package) is covered by vendor_cpuid_only_v2 compat property. The previous consideration was to avoid breaking migration and this compat property makes it unfriendly to backport the commit f985a1195ba2 ("i386/cpu: Fix number of addressable IDs field for CPUID.01H.EBX [23:16]"). However, NetBSD booting is broken since the commit 88dd4ca06c83 ("i386/cpu: Use APIC ID info to encode cache topo in CPUID[4]"), because NetBSD calculates smt information via `lp_max` / `core_max` for legacy Intel CPUs which doesn't support 0xb leaf, where `lp_max` is from CPUID[0x1].EBX.bits[16-23] and `core_max` is from CPUID[0x4].0x0.bits[26 -31]. The commit 88dd4ca0 changed the encoding rule of `core_max` but didn't update `lp_max`, so that NetBSD would get the wrong smt information, which leads to the module loading failure. Luckily, the commit f985a1195ba2 ("i386/cpu: Fix number of addressable IDs field for CPUID.01H.EBX[23:16]") updated the encoding rule for `lp_max` and accidentally fixed the NetBSD issue too. This also shows that using CPUID[0x1] and CPUID[0x4].0x0 to calculate HT/SMT information is a common practice to detect CPU topology on legacy Intel CPUs. Therefore, it's necessary to backport the commit f985a1195ba2 to previous stable QEMU to help address the similar issues as well. Then the compat property is not needed any more since all stable QEMUs will follow the same encoding way. So, in CPUID[0x1], move addressable ID encoding out of compat property. Reported-by: Michael Tokarev Inspired-by: Chuang Xu Fixes: commit f985a1195ba2 ("i386/cpu: Fix number of addressable IDs field for CPUID.01H.EBX[23:16]") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3061 Signed-off-by: Zhao Liu Reviewed-by: Michael Tokarev Tested-by: Michael Tokarev Message-ID: <20250804053548.1808629-1-zhao1.liu@intel.com> Signed-off-by: Philippe Mathieu-Daudé --- target/i386/cpu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 251d5760a0bd1..673f8583c8095 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7885,8 +7885,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, * count, but Intel needs maximum number of addressable IDs for * logical processors per package. */ - if (cpu->vendor_cpuid_only_v2 && - (IS_INTEL_CPU(env) || IS_ZHAOXIN_CPU(env))) { + if ((IS_INTEL_CPU(env) || IS_ZHAOXIN_CPU(env))) { num = 1 << apicid_pkg_offset(topo_info); } else { num = threads_per_pkg; From eb013cd6a11951a8d76e737e9f6e89c96b059b48 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 4 Aug 2025 17:20:07 +0200 Subject: [PATCH 0224/1794] hw/i386/microvm: Explicitly select ACPI_PCI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With a microvm-only build based on a custom device config, we get a link failure due to undefined reference to build_pci_host_bridge_osc_method() which is defined in hw/acpi/pci.c and whose compilation depends on CONFIG_ACPI_PCI. Although CONFIG_ACPI and CONFIG_PCI are set with such configuration, implied CONFIG_ACPI_PCI in config PCI_EXPRESS_GENERIC_BRIDGE is not selected as expected. It Looks like CONFIG_ACPI_PCI must be enforced and this patch selects CONFIG_ACPI_PCI in MICROVM config directly as done for PC config. Reproducer: ../configure \ --without-default-features \ --target-list=x86_64-softmmu \ --enable-kvm --disable-tcg \ --enable-pixman \ --enable-vnc \ --audio-drv-list="" \ --without-default-devices \ --with-devices-x86_64=microvm \ --enable-vhost-user with configs/devices/x86_64-softmmu/microvm.mak: CONFIG_PCI_DEVICES=n CONFIG_MICROVM=y CONFIG_VIRTIO_BLK=y CONFIG_VIRTIO_SERIAL=y CONFIG_VIRTIO_INPUT=y CONFIG_VIRTIO_INPUT_HOST=y CONFIG_VHOST_USER_INPUT=y CONFIG_VIRTIO_NET=y CONFIG_VIRTIO_SCSI=y CONFIG_VIRTIO_RNG=y CONFIG_VIRTIO_CRYPTO=y CONFIG_VIRTIO_BALLOON=y CONFIG_VIRTIO_GPU=y CONFIG_VHOST_USER_GPU=y FAILED: qemu-system-x86_64 cc -m64 @qemu-system-x86_64.rsp /usr/bin/ld: libsystem.a.p/hw_pci-host_gpex-acpi.c.o: in function `acpi_dsdt_add_host_bridge_methods': hw/pci-host/gpex-acpi.c:83:(.text+0x274): undefined reference to `build_pci_host_bridge_osc_method' collect2: error: ld returned 1 exit status Fixes: af151d50eac24 "hw/pci-host/gpex-acpi: Use build_pci_host_bridge_osc_method" Signed-off-by: Eric Auger Reported-by: Michael Tokarev Reviewed-by: Michael Tokarev Message-ID: <20250804152008.247673-1-eric.auger@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 5139d23087771..3a0e2b8ebbb6f 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -131,6 +131,7 @@ config MICROVM select I8259 select MC146818RTC select VIRTIO_MMIO + select ACPI_PCI select ACPI_HW_REDUCED select PCI_EXPRESS_GENERIC_BRIDGE select USB_XHCI_SYSBUS From b217d987a3c5c6e2473956723b396bc1ff0f5b2b Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Fri, 1 Aug 2025 14:53:14 +0300 Subject: [PATCH 0225/1794] qga: correctly write to /sys/power/state on linux Commit v9.0.0-343-g2048129625 introduced usage of g_file_set_contents() function to write to /sys/power/state. This function uses G_FILE_SET_CONTENTS_CONSISTENT flag to g_file_set_contents_full(), which is implemented by creating a temp file in the same directory and renaming it to the final destination. Which is not how sysfs works. Here, there's not a big deal to do open/write/close - it becomes almost the same as using g_file_set_contents[_full](). But it does not have surprises like this. Also, since this is linux code, it should be ok to use %m in the error reporting function. Fixes: 2048129625 "qga/commands-posix: don't do fork()/exec() when suspending via sysfs" Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3057 Signed-off-by: Michael Tokarev Reviewed-by: Stefan Hajnoczi Message-ID: <20250801115316.6845-1-mjt@tls.msk.ru> Signed-off-by: Stefan Hajnoczi --- qga/commands-linux.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/qga/commands-linux.c b/qga/commands-linux.c index 9e8a934b9a610..9dc0c82503563 100644 --- a/qga/commands-linux.c +++ b/qga/commands-linux.c @@ -1400,20 +1400,22 @@ static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp) static void linux_sys_state_suspend(SuspendMode mode, Error **errp) { - g_autoptr(GError) local_gerr = NULL; const char *sysfile_strs[3] = {"disk", "mem", NULL}; const char *sysfile_str = sysfile_strs[mode]; + int fd; if (!sysfile_str) { error_setg(errp, "unknown guest suspend mode"); return; } - if (!g_file_set_contents(LINUX_SYS_STATE_FILE, sysfile_str, - -1, &local_gerr)) { - error_setg(errp, "suspend: cannot write to '%s': %s", - LINUX_SYS_STATE_FILE, local_gerr->message); - return; + fd = open(LINUX_SYS_STATE_FILE, O_WRONLY); + if (fd < 0 || write(fd, sysfile_str, strlen(sysfile_str)) < 0) { + error_setg(errp, "suspend: cannot write to '%s': %m", + LINUX_SYS_STATE_FILE); + } + if (fd >= 0) { + close(fd); } } From afeb002e0ad49dcf388dedbc6995f6561885e376 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Tue, 5 Aug 2025 21:17:30 +0300 Subject: [PATCH 0226/1794] tests/qemu-iotests/tests/mirror-sparse: skip if O_DIRECT is not supported This test uses cache.direct=true, but does not check if O_DIRECT is supported by the underlying filesystem, and fails, for example, on a tmpfs (which is rather common on various auto-builders, in CI, etc). Fix this by using `_supported_cache_modes none directsync`. Fixes: c0ddcb2cbc146e "tests: Add iotest mirror-sparse for recent patches" Signed-off-by: Michael Tokarev Reviewed-by: Stefan Hajnoczi Message-ID: <20250805181731.282677-1-mjt@tls.msk.ru> Signed-off-by: Stefan Hajnoczi --- tests/qemu-iotests/tests/mirror-sparse | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qemu-iotests/tests/mirror-sparse b/tests/qemu-iotests/tests/mirror-sparse index cfcaa600ab4fb..3b183eea88afd 100755 --- a/tests/qemu-iotests/tests/mirror-sparse +++ b/tests/qemu-iotests/tests/mirror-sparse @@ -40,6 +40,7 @@ cd .. _supported_fmt qcow2 raw # Format of the source. dst is always raw file _supported_proto file _supported_os Linux +_supported_cache_modes none directsync _require_disk_usage echo From e0006a86615baa70bc9d8b183e528aed91c1ac90 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 5 Aug 2025 15:05:57 -0400 Subject: [PATCH 0227/1794] Update version for the v10.1.0-rc2 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 8ae905b0ddbce..c08f99e23ed2e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.0.91 +10.0.92 From a04ba043a3b03b49466b8ba95290e0507f268069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 5 Aug 2025 19:24:31 +0100 Subject: [PATCH 0228/1794] meson: remove 'gnutls-bug1717-workaround' for migration TLS crashes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The implementation of this workaround does not currently work, so remove the option entirely to avoid exposing it to users. The code will remain (temporarily dormant) to be fixed in the next release cycle. Signed-off-by: Daniel P. Berrangé Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250805182431.504158-1-berrange@redhat.com Signed-off-by: Fabiano Rosas --- meson.build | 6 +++--- meson_options.txt | 2 -- scripts/meson-buildoptions.sh | 5 ----- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/meson.build b/meson.build index e53cd5b413847..a7b3c683ce8c0 100644 --- a/meson.build +++ b/meson.build @@ -1836,11 +1836,11 @@ if get_option('gnutls').enabled() or (get_option('gnutls').auto() and have_syste required: get_option('gnutls')) endif - if gnutls.found() and not get_option('gnutls-bug1717-workaround').disabled() + #if gnutls.found() and not get_option('gnutls-bug1717-workaround').disabled() # XXX: when bug 1717 is resolved, add logic to probe for # the GNUTLS fixed version number to handle the 'auto' case - gnutls_bug1717_workaround = true - endif + # gnutls_bug1717_workaround = true + #endif endif # We prefer use of gnutls for crypto, unless the options diff --git a/meson_options.txt b/meson_options.txt index dd335307505fb..fff1521e580de 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -174,8 +174,6 @@ option('libcbor', type : 'feature', value : 'auto', description: 'libcbor support') option('gnutls', type : 'feature', value : 'auto', description: 'GNUTLS cryptography support') -option('gnutls-bug1717-workaround', type: 'feature', value : 'auto', - description: 'GNUTLS workaround for https://gitlab.com/gnutls/gnutls/-/issues/1717') option('nettle', type : 'feature', value : 'auto', description: 'nettle cryptography support') option('gcrypt', type : 'feature', value : 'auto', diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index d559e260ed17e..0ebe6bc52a6b9 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -123,9 +123,6 @@ meson_options_help() { printf "%s\n" ' gio use libgio for D-Bus support' printf "%s\n" ' glusterfs Glusterfs block device driver' printf "%s\n" ' gnutls GNUTLS cryptography support' - printf "%s\n" ' gnutls-bug1717-workaround' - printf "%s\n" ' GNUTLS workaround for' - printf "%s\n" ' https://gitlab.com/gnutls/gnutls/-/issues/1717' printf "%s\n" ' gtk GTK+ user interface' printf "%s\n" ' gtk-clipboard clipboard support for the gtk UI (EXPERIMENTAL, MAY HANG)' printf "%s\n" ' guest-agent Build QEMU Guest Agent' @@ -334,8 +331,6 @@ _meson_option_parse() { --disable-glusterfs) printf "%s" -Dglusterfs=disabled ;; --enable-gnutls) printf "%s" -Dgnutls=enabled ;; --disable-gnutls) printf "%s" -Dgnutls=disabled ;; - --enable-gnutls-bug1717-workaround) printf "%s" -Dgnutls-bug1717-workaround=enabled ;; - --disable-gnutls-bug1717-workaround) printf "%s" -Dgnutls-bug1717-workaround=disabled ;; --enable-gtk) printf "%s" -Dgtk=enabled ;; --disable-gtk) printf "%s" -Dgtk=disabled ;; --enable-gtk-clipboard) printf "%s" -Dgtk_clipboard=enabled ;; From c7ac771ee750e37808928b62388fd27dcbf00f46 Mon Sep 17 00:00:00 2001 From: William Hu Date: Thu, 3 Apr 2025 01:07:56 +0000 Subject: [PATCH 0229/1794] ui/curses: Fix infinite loop on windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace -1 comparisons for wint_t with WEOF to fix infinite loop caused by a 65535 == -1 comparison. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2905 Signed-off-by: William Hu Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau [ Marc-André - Add missing similar code change, remove a comment ] Signed-off-by: Marc-André Lureau Message-ID: --- ui/curses.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ui/curses.c b/ui/curses.c index a39aee8762344..161f78c35c32f 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -265,7 +265,8 @@ static int curses2foo(const int _curses2foo[], const int _curseskey2foo[], static void curses_refresh(DisplayChangeListener *dcl) { - int chr, keysym, keycode, keycode_alt; + wint_t chr = 0; + int keysym, keycode, keycode_alt; enum maybe_keycode maybe_keycode = CURSES_KEYCODE; curses_winch_check(); @@ -284,8 +285,9 @@ static void curses_refresh(DisplayChangeListener *dcl) /* while there are any pending key strokes to process */ chr = console_getch(&maybe_keycode); - if (chr == -1) + if (chr == WEOF) { break; + } #ifdef KEY_RESIZE /* this shouldn't occur when we use a custom SIGWINCH handler */ @@ -304,9 +306,9 @@ static void curses_refresh(DisplayChangeListener *dcl) /* alt or esc key */ if (keycode == 1) { enum maybe_keycode next_maybe_keycode = CURSES_KEYCODE; - int nextchr = console_getch(&next_maybe_keycode); + wint_t nextchr = console_getch(&next_maybe_keycode); - if (nextchr != -1) { + if (nextchr != WEOF) { chr = nextchr; maybe_keycode = next_maybe_keycode; keycode_alt = ALT; From 5a5d18c75666378ff5db70268dbd39bd38488f21 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Wed, 6 Aug 2025 10:48:37 +0300 Subject: [PATCH 0230/1794] tests/qemu-iotests/tests/mirror-sparse: actually require O_DIRECT Commit c0ddcb2cbc146e introduced the test which uses cache=direct mode, without checking if the scratch filesystem supports O_DIRECT. A subsequent commit, afeb002e0ad49d, tried to fix that issue, but instead of checking for o_direct, it checked for `_supported_cache_modes none directsync`, which is not what the original mirror-sparse test uses. Fix both by actually checking for o_direct. Fixes: c0ddcb2cbc146e "tests: Add iotest mirror-sparse for recent patches" Fixes: afeb002e0ad49d "tests/qemu-iotests/tests/mirror-sparse: skip if O_DIRECT is not supported" Reviewed-by: Stefan Hajnoczi Signed-off-by: Michael Tokarev --- tests/qemu-iotests/tests/mirror-sparse | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/tests/mirror-sparse b/tests/qemu-iotests/tests/mirror-sparse index 3b183eea88afd..ee7101bd50ec8 100755 --- a/tests/qemu-iotests/tests/mirror-sparse +++ b/tests/qemu-iotests/tests/mirror-sparse @@ -40,7 +40,7 @@ cd .. _supported_fmt qcow2 raw # Format of the source. dst is always raw file _supported_proto file _supported_os Linux -_supported_cache_modes none directsync +_require_o_direct _require_disk_usage echo From 8c9aae4364b25f0fd791eb3128b7650d7720d473 Mon Sep 17 00:00:00 2001 From: Stefan Weil via Date: Wed, 6 Aug 2025 22:45:58 +0200 Subject: [PATCH 0231/1794] meson: Fix brlapi compile test for Windows builds brlapi__openConnection returns a brlapi_fileDescriptor which is a pointer for Windows builds. The test for brlapi fails with cross builds on Debian trixie (x86_64-w64-mingw32-gcc (GCC) 14-win32): testfile.c:4:30: error: returning 'brlapi_fileDescriptor' {aka 'void *'} from a function with return type 'int' makes integer from pointer without a cast [-Wint-conversion] 4 | int main(void) { return brlapi__openConnection (NULL, NULL, NULL); } | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ----------- ../../../meson.build:1607: WARNING: could not link brlapi, disabling Signed-off-by: Stefan Weil Reviewed-by: Pierrick Bouvier Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- meson.build | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index a7b3c683ce8c0..50c774a19557a 100644 --- a/meson.build +++ b/meson.build @@ -1586,9 +1586,11 @@ if not get_option('brlapi').auto() or have_system brlapi = cc.find_library('brlapi', has_headers: ['brlapi.h'], required: get_option('brlapi')) if brlapi.found() and not cc.links(''' - #include - #include - int main(void) { return brlapi__openConnection (NULL, NULL, NULL); }''', dependencies: brlapi) + #include + #include + int main(void) { + return brlapi__openConnection(NULL, NULL, NULL) == BRLAPI_INVALID_FILE_DESCRIPTOR; + }''', dependencies: brlapi) brlapi = not_found if get_option('brlapi').enabled() error('could not link brlapi') From e66644c48e96e81848c6aa94b185f59fc212d080 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Mon, 4 Aug 2025 21:22:12 +0800 Subject: [PATCH 0232/1794] target/loongarch: Fix [X]VLDI raising exception incorrectly According to the specification, [X]VLDI should trigger an invalid instruction exception only when Bit[12] is 1 and Bit[11:8] > 12. This patch fixes an issue where an exception was incorrectly raised even when Bit[12] was 0. Test case: ``` .global main main: vldi $vr0, 3328 ret ``` Reported-by: Zhou Qiankang Signed-off-by: WANG Rui Reviewed-by: Song Gao Message-ID: <20250804132212.4816-1-wangrui@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/tcg/insn_trans/trans_vec.c.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/loongarch/tcg/insn_trans/trans_vec.c.inc b/target/loongarch/tcg/insn_trans/trans_vec.c.inc index 78730029cbaad..38bccf28386fc 100644 --- a/target/loongarch/tcg/insn_trans/trans_vec.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_vec.c.inc @@ -3585,7 +3585,9 @@ static bool gen_vldi(DisasContext *ctx, arg_vldi *a, uint32_t oprsz) int sel, vece; uint64_t value; - if (!check_valid_vldi_mode(a)) { + sel = (a->imm >> 12) & 0x1; + + if (sel && !check_valid_vldi_mode(a)) { generate_exception(ctx, EXCCODE_INE); return true; } @@ -3594,8 +3596,6 @@ static bool gen_vldi(DisasContext *ctx, arg_vldi *a, uint32_t oprsz) return true; } - sel = (a->imm >> 12) & 0x1; - if (sel) { value = vldi_get_value(ctx, a->imm); vece = MO_64; From 76cfb87f5fcad4008359e44bf37508c265da1221 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 16 Jul 2025 11:06:08 -0700 Subject: [PATCH 0233/1794] vfio/pci: augment set_handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend vfio_pci_msi_set_handler() so it can set or clear the handler. Add a similar accessor for INTx. No functional change. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1752689169-233452-2-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr.c | 2 +- hw/vfio/pci.c | 13 +++++++++++-- hw/vfio/pci.h | 3 ++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index 384b56c4c74d8..ade7ff745dc26 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -70,7 +70,7 @@ static void vfio_cpr_claim_vectors(VFIOPCIDevice *vdev, int nr_vectors, fd = vfio_cpr_load_vector_fd(vdev, "interrupt", i); if (fd >= 0) { vfio_pci_vector_init(vdev, i); - vfio_pci_msi_set_handler(vdev, i); + vfio_pci_msi_set_handler(vdev, i, true); } if (vfio_cpr_load_vector_fd(vdev, "kvm_interrupt", i) >= 0) { diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 4fa692c1a32bc..4cd6894ca8173 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -413,6 +413,14 @@ bool vfio_pci_intx_enable(VFIOPCIDevice *vdev, Error **errp) return vfio_intx_enable(vdev, errp); } +void vfio_pci_intx_set_handler(VFIOPCIDevice *vdev, bool enable) +{ + int fd = event_notifier_get_fd(&vdev->intx.interrupt); + IOHandler *handler = (enable ? vfio_intx_interrupt : NULL); + + qemu_set_fd_handler(fd, handler, NULL, vdev); +} + /* * MSI/X */ @@ -451,12 +459,13 @@ static void vfio_msi_interrupt(void *opaque) notify(&vdev->pdev, nr); } -void vfio_pci_msi_set_handler(VFIOPCIDevice *vdev, int nr) +void vfio_pci_msi_set_handler(VFIOPCIDevice *vdev, int nr, bool enable) { VFIOMSIVector *vector = &vdev->msi_vectors[nr]; int fd = event_notifier_get_fd(&vector->interrupt); + IOHandler *handler = (enable ? vfio_msi_interrupt : NULL); - qemu_set_fd_handler(fd, vfio_msi_interrupt, NULL, vector); + qemu_set_fd_handler(fd, handler, NULL, vector); } /* diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 81465a8214a40..2b564baf88640 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -209,8 +209,9 @@ void vfio_pci_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, void vfio_pci_prepare_kvm_msi_virq_batch(VFIOPCIDevice *vdev); void vfio_pci_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev); bool vfio_pci_intx_enable(VFIOPCIDevice *vdev, Error **errp); +void vfio_pci_intx_set_handler(VFIOPCIDevice *vdev, bool enable); void vfio_pci_msix_set_notifiers(VFIOPCIDevice *vdev); -void vfio_pci_msi_set_handler(VFIOPCIDevice *vdev, int nr); +void vfio_pci_msi_set_handler(VFIOPCIDevice *vdev, int nr, bool enable); uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len); void vfio_pci_write_config(PCIDevice *pdev, From 322ee16824dc3d301477812a5dacb0249e1efe8c Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 16 Jul 2025 11:06:09 -0700 Subject: [PATCH 0234/1794] vfio/pci: preserve pending interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpr-transfer may lose a VFIO interrupt because the KVM instance is destroyed and recreated. If an interrupt arrives in the middle, it is dropped. To fix, stop pending new interrupts during cpr save, and pick up the pieces. In more detail: Stop the VCPUs. Call kvm_irqchip_remove_irqfd_notifier_gsi --> KVM_IRQFD to deassign the irqfd gsi that routes interrupts directly to the VCPU and KVM. After this call, interrupts fall back to the kernel vfio_msihandler, which writes to QEMU's kvm_interrupt eventfd. CPR already preserves that eventfd. When the route is re-established in new QEMU, the kernel tests the eventfd and injects an interrupt to KVM if necessary. Deassign INTx in a similar manner. For both MSI and INTx, remove the eventfd handler so old QEMU does not consume an event. If an interrupt was already pended to KVM prior to the completion of kvm_irqchip_remove_irqfd_notifier_gsi, it will be recovered by the subsequent call to cpu_synchronize_all_states, which pulls KVM interrupt state to userland prior to saving it in vmstate. Signed-off-by: Steve Sistare Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1752689169-233452-3-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr.c | 91 ++++++++++++++++++++++++++++++++++++++ hw/vfio/pci.c | 2 + hw/vfio/pci.h | 1 + include/hw/vfio/vfio-cpr.h | 6 +++ 4 files changed, 100 insertions(+) diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index ade7ff745dc26..a831243e02071 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -200,3 +200,94 @@ void vfio_cpr_add_kvm_notifier(void) MIG_MODE_CPR_TRANSFER); } } + +static int set_irqfd_notifier_gsi(KVMState *s, EventNotifier *n, + EventNotifier *rn, int virq, bool enable) +{ + if (enable) { + return kvm_irqchip_add_irqfd_notifier_gsi(s, n, rn, virq); + } else { + return kvm_irqchip_remove_irqfd_notifier_gsi(s, n, virq); + } +} + +static int vfio_cpr_set_msi_virq(VFIOPCIDevice *vdev, Error **errp, bool enable) +{ + const char *op = (enable ? "enable" : "disable"); + PCIDevice *pdev = &vdev->pdev; + int i, nr_vectors, ret = 0; + + if (msix_enabled(pdev)) { + nr_vectors = vdev->msix->entries; + + } else if (msi_enabled(pdev)) { + nr_vectors = msi_nr_vectors_allocated(pdev); + + } else if (vfio_pci_read_config(pdev, PCI_INTERRUPT_PIN, 1)) { + ret = set_irqfd_notifier_gsi(kvm_state, &vdev->intx.interrupt, + &vdev->intx.unmask, vdev->intx.route.irq, + enable); + if (ret) { + error_setg_errno(errp, -ret, "failed to %s INTx irq %d", + op, vdev->intx.route.irq); + return ret; + } + vfio_pci_intx_set_handler(vdev, enable); + return ret; + + } else { + return 0; + } + + for (i = 0; i < nr_vectors; i++) { + VFIOMSIVector *vector = &vdev->msi_vectors[i]; + if (vector->use) { + ret = set_irqfd_notifier_gsi(kvm_state, &vector->kvm_interrupt, + NULL, vector->virq, enable); + if (ret) { + error_setg_errno(errp, -ret, + "failed to %s msi vector %d virq %d", + op, i, vector->virq); + return ret; + } + vfio_pci_msi_set_handler(vdev, i, enable); + } + } + + return ret; +} + +/* + * When CPR starts, detach IRQs from the VFIO device so future interrupts + * are posted to kvm_interrupt, which is preserved in new QEMU. Interrupts + * that were already posted to the old KVM instance, but not delivered to the + * VCPU, are recovered via KVM_GET_LAPIC and pushed to the new KVM instance + * in new QEMU. + * + * If CPR fails, reattach the IRQs. + */ +static int vfio_cpr_pci_notifier(NotifierWithReturn *notifier, + MigrationEvent *e, Error **errp) +{ + VFIOPCIDevice *vdev = + container_of(notifier, VFIOPCIDevice, cpr.transfer_notifier); + + if (e->type == MIG_EVENT_PRECOPY_SETUP) { + return vfio_cpr_set_msi_virq(vdev, errp, false); + } else if (e->type == MIG_EVENT_PRECOPY_FAILED) { + return vfio_cpr_set_msi_virq(vdev, errp, true); + } + return 0; +} + +void vfio_cpr_pci_register_device(VFIOPCIDevice *vdev) +{ + migration_add_notifier_mode(&vdev->cpr.transfer_notifier, + vfio_cpr_pci_notifier, + MIG_MODE_CPR_TRANSFER); +} + +void vfio_cpr_pci_unregister_device(VFIOPCIDevice *vdev) +{ + migration_remove_notifier(&vdev->cpr.transfer_notifier); +} diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 4cd6894ca8173..d5ea4a5a8341c 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3001,6 +3001,7 @@ void vfio_pci_put_device(VFIOPCIDevice *vdev) { vfio_display_finalize(vdev); vfio_bars_finalize(vdev); + vfio_cpr_pci_unregister_device(vdev); g_free(vdev->emulated_config_bits); g_free(vdev->rom); /* @@ -3480,6 +3481,7 @@ static void vfio_pci_realize(PCIDevice *pdev, Error **errp) vfio_pci_register_err_notifier(vdev); vfio_pci_register_req_notifier(vdev); vfio_setup_resetfn_quirk(vdev); + vfio_cpr_pci_register_device(vdev); return; diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 2b564baf88640..810a842f4a153 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -188,6 +188,7 @@ struct VFIOPCIDevice { bool skip_vsc_check; VFIODisplay *dpy; Notifier irqchip_change_notifier; + VFIOPCICPR cpr; }; /* Use uin32_t for vendor & device so PCI_ANY_ID expands and cannot match hw */ diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index 80ad20d216908..d37daffbc5b3d 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -38,6 +38,10 @@ typedef struct VFIODeviceCPR { uint32_t ioas_id; } VFIODeviceCPR; +typedef struct VFIOPCICPR { + NotifierWithReturn transfer_notifier; +} VFIOPCICPR; + bool vfio_legacy_cpr_register_container(struct VFIOContainer *container, Error **errp); void vfio_legacy_cpr_unregister_container(struct VFIOContainer *container); @@ -77,5 +81,7 @@ extern const VMStateDescription vfio_cpr_pci_vmstate; extern const VMStateDescription vmstate_cpr_vfio_devices; void vfio_cpr_add_kvm_notifier(void); +void vfio_cpr_pci_register_device(struct VFIOPCIDevice *vdev); +void vfio_cpr_pci_unregister_device(struct VFIOPCIDevice *vdev); #endif /* HW_VFIO_VFIO_CPR_H */ From d9f4b45713e4e000a42ed1f33cb06b806173b559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 5 Aug 2025 08:55:43 +0200 Subject: [PATCH 0235/1794] vfio: Document 'use-legacy-x86-rom' property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 350785d41d8b ("ramfb: Add property to control if load the romfile") introduced the `use-legacy-x86-rom` property for the `vfio-pci-nohotplug` device. Add documentation for the property. Fixes: d5fcf0d960d8 ("hw/i386: Add the ramfb romfile compatibility") Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250805065543.120091-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index d5ea4a5a8341c..07257d0fa049b 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3901,6 +3901,9 @@ static void vfio_pci_nohotplug_dev_class_init(ObjectClass *klass, "x-ramfb-migrate", "Override default migration support for ramfb support " "(DEBUG)"); + object_class_property_set_description(klass, /* 10.1 */ + "use-legacy-x86-rom", + "Controls loading of a legacy VGA BIOS ROM"); } static const TypeInfo vfio_pci_nohotplug_dev_info = { From 13b4d19ced4430a777a0ab9d08bda86b3fd01fca Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 31 Jul 2025 07:40:42 +0200 Subject: [PATCH 0236/1794] docs/devel/qapi-code-gen: Add two cross-references we missed Missed in commit 9c66762a601 (docs/qapi-code-gen: add cross-references). Signed-off-by: Markus Armbruster Message-ID: <20250731054044.4011789-2-armbru@redhat.com> Reviewed-by: John Snow --- docs/devel/qapi-code-gen.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index dfdbeac5a5a7f..138921b38633a 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -646,9 +646,9 @@ Member 'event' names the event. This is the event name used in the Client JSON Protocol. Member 'data' defines the event-specific data. It defaults to an -empty MEMBERS object. +empty MEMBERS_ object. -If 'data' is a MEMBERS object, then MEMBERS defines event-specific +If 'data' is a MEMBERS_ object, then MEMBERS defines event-specific data just like a struct type's 'data' defines struct type members. If 'data' is a STRING, then STRING names a complex type whose members From 60e847dcf06aa4cc41cf7535d5ea4b1f5b34a616 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 31 Jul 2025 07:40:43 +0200 Subject: [PATCH 0237/1794] docs/devel/qapi-code-gen: Fix typos in QAPI schema language grammar Fixes: 3248c1aaf2db (docs: update the documentation upfront about schema configuration) Signed-off-by: Markus Armbruster Message-ID: <20250731054044.4011789-3-armbru@redhat.com> Reviewed-by: John Snow --- docs/devel/qapi-code-gen.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index 138921b38633a..2cd51729c36db 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -786,8 +786,8 @@ Configuring the schema Syntax:: COND = STRING - | { 'all: [ COND, ... ] } - | { 'any: [ COND, ... ] } + | { 'all': [ COND, ... ] } + | { 'any': [ COND, ... ] } | { 'not': COND } All definitions take an optional 'if' member. Its value must be a From 79f57adce686d0027608af4be363cde2409e5740 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 31 Jul 2025 07:40:44 +0200 Subject: [PATCH 0238/1794] docs/devel/qapi-code-gen: Update cross-reference syntax The new QAPI code generator creates a cross-reference target for each definition documentation. Enabled for the QEMU QMP Reference manual in commit a377f39f38f, and for the QEMU Storage Daemon QMP Reference Manual and the QEMU Guest Agent Protocol Reference in commit a6af5443440. We've put these targets to use since, but neglected to update doc comment markup documentation. Do that now. Co-developed-by: John Snow Signed-off-by: John Snow Signed-off-by: Markus Armbruster Message-ID: <20250731054044.4011789-4-armbru@redhat.com> --- docs/devel/qapi-code-gen.rst | 11 ++++++++--- docs/devel/qapi-domain.rst | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index 2cd51729c36db..d97602f464c7e 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -943,9 +943,14 @@ The usual ****strong****, *\*emphasized\** and ````literal```` markup should be used. If you need a single literal ``*``, you will need to backslash-escape it. -Use ``@foo`` to reference a name in the schema. This is an rST -extension. It is rendered the same way as ````foo````, but carries -additional meaning. +Use ```foo``` to reference a definition in the schema. This generates +a link to the definition. In the event that such a cross-reference is +ambiguous, you can use `QAPI cross-reference roles +` to disambiguate. + +Use @foo to reference a member description within the current +definition. This is an rST extension. It is currently rendered the +same way as ````foo````, but carries additional meaning. Example:: diff --git a/docs/devel/qapi-domain.rst b/docs/devel/qapi-domain.rst index fe540d1e40a75..1924f12d42c05 100644 --- a/docs/devel/qapi-domain.rst +++ b/docs/devel/qapi-domain.rst @@ -375,6 +375,7 @@ Will allow you to add arbitrary field lists in QAPI directives:: :see also: Lorem ipsum, dolor sit amet ... +.. _QAPI-domain-cross-references: Cross-references ================ From 31b737b19dca4d50758f5e9834d27b683102f2f1 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Tue, 3 Jun 2025 14:59:05 +0200 Subject: [PATCH 0239/1794] hw/nvme: fix namespace attachment Commit 6ccca4b6bb9f ("hw/nvme: rework csi handling") introduced a bug in Namespace Attachment, causing it to a) not allow a controller to attach namespaces to other controllers b) assert if a valid non-attached namespace is detached This fixes both issues. Fixes: 6ccca4b6bb9f ("hw/nvme: rework csi handling") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2976 Reviewed-by: Jesper Wendel Devantier Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index e764ec7683ab4..6c06d7f8f9dda 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -6816,7 +6816,7 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) switch (sel) { case NVME_NS_ATTACHMENT_ATTACH: - if (nvme_ns(n, nsid)) { + if (nvme_ns(ctrl, nsid)) { return NVME_NS_ALREADY_ATTACHED | NVME_DNR; } @@ -6824,7 +6824,7 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) return NVME_NS_PRIVATE | NVME_DNR; } - if (!nvme_csi_supported(n, ns->csi)) { + if (!nvme_csi_supported(ctrl, ns->csi)) { return NVME_IOCS_NOT_SUPPORTED | NVME_DNR; } @@ -6834,6 +6834,10 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) break; case NVME_NS_ATTACHMENT_DETACH: + if (!nvme_ns(ctrl, nsid)) { + return NVME_NS_NOT_ATTACHED | NVME_DNR; + } + nvme_detach_ns(ctrl, ns); nvme_update_dsm_limits(ctrl, NULL); From bc0c24fdb157b014932a5012fe4ccbeba7617f17 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Tue, 3 Jun 2025 14:59:06 +0200 Subject: [PATCH 0240/1794] hw/nvme: revert CMIC behavior Commit cd59f50ab017 ("hw/nvme: always initialize a subsystem") causes the controller to always set the CMIC.MCTRS ("Multiple Controllers") bit. While spec-compliant, this is a deviation from the previous behavior where this was only set if an nvme-subsys device was explicitly created (to configure a subsystem with multiple controllers/namespaces). Revert the behavior to only set CMIC.MCTRS if an nvme-subsys device is created explicitly. Reported-by: Alan Adamson Fixes: cd59f50ab017 ("hw/nvme: always initialize a subsystem") Reviewed-by: Alan Adamson Tested-by: Alan Adamson Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 6c06d7f8f9dda..fa48412ef48e0 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -8780,7 +8780,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) uint8_t *pci_conf = pci_dev->config; uint64_t cap = ldq_le_p(&n->bar.cap); NvmeSecCtrlEntry *sctrl = nvme_sctrl(n); - uint32_t ctratt; + uint32_t ctratt = le32_to_cpu(id->ctratt); uint16_t oacs; memcpy(n->cse.acs, nvme_cse_acs_default, sizeof(n->cse.acs)); @@ -8798,10 +8798,11 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->oaes = cpu_to_le32(NVME_OAES_NS_ATTR); - ctratt = NVME_CTRATT_ELBAS; + ctratt |= NVME_CTRATT_ELBAS; if (n->params.ctratt.mem) { ctratt |= NVME_CTRATT_MEM; } + id->ctratt = cpu_to_le32(ctratt); id->rab = 6; @@ -8884,17 +8885,6 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->psd[0].enlat = cpu_to_le32(0x10); id->psd[0].exlat = cpu_to_le32(0x4); - id->cmic |= NVME_CMIC_MULTI_CTRL; - ctratt |= NVME_CTRATT_ENDGRPS; - - id->endgidmax = cpu_to_le16(0x1); - - if (n->subsys->endgrp.fdp.enabled) { - ctratt |= NVME_CTRATT_FDPS; - } - - id->ctratt = cpu_to_le32(ctratt); - NVME_CAP_SET_MQES(cap, n->params.mqes); NVME_CAP_SET_CQR(cap, 1); NVME_CAP_SET_TO(cap, 0xf); @@ -8927,6 +8917,20 @@ static int nvme_init_subsys(NvmeCtrl *n, Error **errp) } n->subsys = NVME_SUBSYS(dev); + } else { + NvmeIdCtrl *id = &n->id_ctrl; + uint32_t ctratt = le32_to_cpu(id->ctratt); + + id->cmic |= NVME_CMIC_MULTI_CTRL; + ctratt |= NVME_CTRATT_ENDGRPS; + + id->endgidmax = cpu_to_le16(0x1); + + if (n->subsys->endgrp.fdp.enabled) { + ctratt |= NVME_CTRATT_FDPS; + } + + id->ctratt = cpu_to_le32(ctratt); } cntlid = nvme_subsys_register_ctrl(n, errp); From 53493c1f836f4dda90a6b5f2fb3d9264918c6871 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Fri, 1 Aug 2025 07:24:57 -0700 Subject: [PATCH 0241/1794] hw/nvme: cap MDTS value for internal limitation The emulated device had let the user set whatever max transfers size they wanted, including no limit. However the device does have an internal limit of 1024 segments. NVMe doesn't report max segments, though. This is implicitly inferred based on the MDTS and MPSMIN values. IOV_MAX is currently 1024 which 4k PRPs can exceed with 2MB transfers. Don't allow MDTS values that can exceed this, otherwise users risk seeing "internal error" status to their otherwise protocol compliant commands. Signed-off-by: Keith Busch Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index fa48412ef48e0..f5ee6bf260f15 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -8339,6 +8339,11 @@ static bool nvme_check_params(NvmeCtrl *n, Error **errp) host_memory_backend_set_mapped(n->pmr.dev, true); } + if (!n->params.mdts || ((1 << n->params.mdts) + 1) > IOV_MAX) { + error_setg(errp, "mdts exceeds IOV_MAX"); + return false; + } + if (n->params.zasl > n->params.mdts) { error_setg(errp, "zoned.zasl (Zone Append Size Limit) must be less " "than or equal to mdts (Maximum Data Transfer Size)"); From 621dbaee0152a283042db3cac6a51f91b3e06024 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 11 Aug 2025 12:23:15 -0400 Subject: [PATCH 0242/1794] tests/functional: fix URLs in PCI hotplug test for aarch64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Debian trixie has been released. The "stable" alias no longer refers to the Debian bookworm release, so URLs referring to bookworm artifacts via the "stable" alias no longer work. Switch to explicit release naming ("bookworm") to make a permalink so the test passes again. Suggested-by: Peter Maydell Fixes: 374a245573b8 ("tests/functional: Add PCI hotplug test for aarch64") Resolves: #3073 ("PCI hotplug test for aarch64 fails due to broken Debian installer URL") Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-ID: <20250811162315.59997-1-stefanha@redhat.com> Signed-off-by: Stefan Hajnoczi --- tests/functional/test_aarch64_hotplug_pci.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/test_aarch64_hotplug_pci.py b/tests/functional/test_aarch64_hotplug_pci.py index c9bb7f1d9753f..0c67991b89e3a 100755 --- a/tests/functional/test_aarch64_hotplug_pci.py +++ b/tests/functional/test_aarch64_hotplug_pci.py @@ -15,12 +15,12 @@ class HotplugPCI(LinuxKernelTest): ASSET_KERNEL = Asset( - ('https://ftp.debian.org/debian/dists/stable/main/installer-arm64/' + ('https://ftp.debian.org/debian/dists/bookworm/main/installer-arm64/' '20230607+deb12u11/images/netboot/debian-installer/arm64/linux'), 'd92a60392ce1e379ca198a1a820899f8f0d39a62d047c41ab79492f81541a9d9') ASSET_INITRD = Asset( - ('https://ftp.debian.org/debian/dists/stable/main/installer-arm64/' + ('https://ftp.debian.org/debian/dists/bookworm/main/installer-arm64/' '20230607+deb12u11/images/netboot/debian-installer/arm64/initrd.gz'), '9f817f76951f3237bca8216bee35267bfb826815687f4b2fcdd5e6c2a917790c') From f757d9d90d19b914d4023663bfc4da73bbbf007e Mon Sep 17 00:00:00 2001 From: Mauro Matteo Cascella Date: Mon, 11 Aug 2025 12:11:24 +0200 Subject: [PATCH 0243/1794] hw/uefi: clear uefi-vars buffer in uefi_vars_write callback When the guest writes to register UEFI_VARS_REG_BUFFER_SIZE, the .write callback `uefi_vars_write` is invoked. The function allocates a heap buffer without zeroing the memory, leaving the buffer filled with residual data from prior allocations. When the guest later reads from register UEFI_VARS_REG_PIO_BUFFER_TRANSFER, the .read callback `uefi_vars_read` returns leftover metadata or other sensitive process memory from the previously allocated buffer, leading to an information disclosure vulnerability. Fixes: CVE-2025-8860 Fixes: 90ca4e03c27d ("hw/uefi: add var-service-core.c") Reported-by: ZDI Suggested-by: Gerd Hoffmann Signed-off-by: Mauro Matteo Cascella Message-ID: <20250811101128.17661-1-mcascell@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/uefi/var-service-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/uefi/var-service-core.c b/hw/uefi/var-service-core.c index feec5a59583b5..6ab8df091aaf4 100644 --- a/hw/uefi/var-service-core.c +++ b/hw/uefi/var-service-core.c @@ -259,8 +259,8 @@ static void uefi_vars_write(void *opaque, hwaddr addr, uint64_t val, unsigned si uv->buf_size = val; g_free(uv->buffer); g_free(uv->pio_xfer_buffer); - uv->buffer = g_malloc(uv->buf_size); - uv->pio_xfer_buffer = g_malloc(uv->buf_size); + uv->buffer = g_malloc0(uv->buf_size); + uv->pio_xfer_buffer = g_malloc0(uv->buf_size); break; case UEFI_VARS_REG_DMA_BUFFER_ADDR_LO: uv->buf_addr_lo = val; From 88e5a28d5aabb57f44c1805fbba0a458023f5106 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 11 Aug 2025 15:01:08 +0200 Subject: [PATCH 0244/1794] hw/uefi: return success for notifications MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set status to SUCCESS for ready-to-boot and exit-boot-services notification calls. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Gerd Hoffmann Message-ID: <20250811130110.820958-2-kraxel@redhat.com> --- hw/uefi/var-service-vars.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/uefi/var-service-vars.c b/hw/uefi/var-service-vars.c index 37d05b71cf702..cbeccdbd26643 100644 --- a/hw/uefi/var-service-vars.c +++ b/hw/uefi/var-service-vars.c @@ -702,12 +702,14 @@ uint32_t uefi_vars_mm_vars_proto(uefi_vars_state *uv) case SMM_VARIABLE_FUNCTION_READY_TO_BOOT: trace_uefi_event("ready-to-boot"); uv->ready_to_boot = true; + mvar->status = EFI_SUCCESS; length = 0; break; case SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE: trace_uefi_event("exit-boot-service"); uv->exit_boot_service = true; + mvar->status = EFI_SUCCESS; length = 0; break; From fc8ee8fe58ad410f27fca64e4ad212c5a3eabe00 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 11 Aug 2025 15:01:09 +0200 Subject: [PATCH 0245/1794] hw/uefi: check access for first variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When listing variables (via get-next-variable-name) only the names of variables which can be accessed will be returned. That check was missing for the first variable though. Add it. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Gerd Hoffmann Message-ID: <20250811130110.820958-3-kraxel@redhat.com> --- hw/uefi/var-service-vars.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/uefi/var-service-vars.c b/hw/uefi/var-service-vars.c index cbeccdbd26643..8533533ea5c8c 100644 --- a/hw/uefi/var-service-vars.c +++ b/hw/uefi/var-service-vars.c @@ -357,6 +357,9 @@ uefi_vars_mm_get_next_variable(uefi_vars_state *uv, mm_header *mhdr, if (uefi_strlen(name, nv->name_size) == 0) { /* empty string -> first */ var = QTAILQ_FIRST(&uv->variables); + while (var && !check_access(uv, var)) { + var = QTAILQ_NEXT(var, next); + } if (!var) { return uefi_vars_mm_error(mhdr, mvar, EFI_NOT_FOUND); } From 040237436f423253f3397547aa78d449394dfbca Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 11 Aug 2025 15:01:10 +0200 Subject: [PATCH 0246/1794] hw/uefi: open json file in binary mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes file length discrepancies due to line ending conversions on windows hosts. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3058 Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Gerd Hoffmann Message-ID: <20250811130110.820958-4-kraxel@redhat.com> --- hw/uefi/var-service-json.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/uefi/var-service-json.c b/hw/uefi/var-service-json.c index ad3462cd15575..f5f155683334c 100644 --- a/hw/uefi/var-service-json.c +++ b/hw/uefi/var-service-json.c @@ -172,7 +172,7 @@ static GString *uefi_vars_to_json(uefi_vars_state *uv) void uefi_vars_json_init(uefi_vars_state *uv, Error **errp) { if (uv->jsonfile) { - uv->jsonfd = qemu_create(uv->jsonfile, O_RDWR, 0666, errp); + uv->jsonfd = qemu_create(uv->jsonfile, O_RDWR | O_BINARY, 0666, errp); } } From 65677b7aad05edd20a8d2fe1c27b944f1ff9a002 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 11 Aug 2025 16:29:23 +0200 Subject: [PATCH 0247/1794] configure: Don't disable Rust for too old meson version If the user explicitly specified --enable-rust, don't just fail if meson is too old for Rust support, but do the same thing as if meson was too old for the C code: Just download a newer one. In order to avoid the additional download for people who aren't intentionally opting in to Rust, keep the automatic disabling based on the meson version as the default if neither --enable-rust nor --disable-rust were given. Signed-off-by: Kevin Wolf Message-ID: <20250811142923.89983-1-kwolf@redhat.com> Reviewed-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- configure | 8 +++++--- pythondeps.toml | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/configure b/configure index 825057ebf1551..274a7787642e2 100755 --- a/configure +++ b/configure @@ -1186,10 +1186,12 @@ fi meson_version=$($meson --version) if test "$rust" != disabled && ! version_ge "$meson_version" 1.8.1; then if test "$rust" = enabled; then - error_exit "Rust support needs Meson 1.8.1 or newer" + $mkvenv ensuregroup --dir "${source_path}/python/wheels" \ + ${source_path}/pythondeps.toml meson-rust || exit 1 + else + echo "Rust needs Meson 1.8.1, disabling" 2>&1 + rust=disabled fi - echo "Rust needs Meson 1.8.1, disabling" 2>&1 - rust=disabled fi if test "$rust" != disabled && has "$rustc" && $rustc -vV > "${TMPDIR1}/${TMPB}.out"; then rust_host_triple=$(sed -n 's/^host: //p' "${TMPDIR1}/${TMPB}.out") diff --git a/pythondeps.toml b/pythondeps.toml index b2eec940ce5af..d0f52b14f7928 100644 --- a/pythondeps.toml +++ b/pythondeps.toml @@ -22,6 +22,10 @@ meson = { accepted = ">=1.5.0", installed = "1.8.1", canary = "meson" } pycotap = { accepted = ">=1.1.0", installed = "1.3.1" } +[meson-rust] +# The install key should match the version in python/wheels/ +meson = { accepted = ">=1.8.1", installed = "1.8.1", canary = "meson" } + [docs] # Please keep the installed versions in sync with docs/requirements.txt sphinx = { accepted = ">=3.4.3", installed = "6.2.1", canary = "sphinx-build" } From 4af976ef398e4e823addc00bf1c58787ba4952fe Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 11 Aug 2025 15:40:10 +0200 Subject: [PATCH 0248/1794] rbd: Fix .bdrv_get_specific_info implementation qemu_rbd_get_specific_info() has at least two problems: The first is that it issues a blocking rbd_read() call in order to probe the encryption format for the image while querying the node. This means that if the connection to the server goes down, not only I/O is stuck (which is unavoidable), but query-names-block-nodes will actually make the whole QEMU instance unresponsive. .bdrv_get_specific_info implementations shouldn't perform blocking operations, but only return what is already known. The second is that the information returned isn't even correct. If the image is already opened with encryption enabled at the RBD level, we'll probe for "double encryption", i.e. if the encrypted data contains another encryption header. If it doesn't (which is the normal case), we won't return the encryption format. If it does, we return misleading information because it looks like we're talking about the outer level (the encryption format of the image itself) while the information is about an encryption header in the guest data. Fix this by storing the encryption format in BDRVRBDState when the image is opened (and we do blocking operations anyway) and returning only the stored information in qemu_rbd_get_specific_info(). The information we'll store is either the actual encryption format that we enabled on the RBD level, or if the image is unencrypted, the result of the same probing as we previously did when querying the node. Probing image formats based on content that can be modified by the guest has long been known as problematic, but as long as we only output it to the user instead of making decisions based on it, it should be okay. It is undoubtedly useful in the context of 'qemu-img info' when you're trying to figure out which encryption options you have to use to open the image successfully. Fixes: 42e4ac9ef5a6 ("block/rbd: Add support for rbd image encryption") Buglink: https://issues.redhat.com/browse/RHEL-105440 Signed-off-by: Kevin Wolf Message-ID: <20250811134010.81787-1-kwolf@redhat.com> Reviewed-by: Hanna Czenczek Signed-off-by: Kevin Wolf --- block/rbd.c | 104 ++++++++++++++++++++++++++++--------------- qapi/block-core.json | 9 +++- 2 files changed, 76 insertions(+), 37 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index 951cd63f9aedf..3611dc81cf178 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -99,6 +99,14 @@ typedef struct BDRVRBDState { char *namespace; uint64_t image_size; uint64_t object_size; + + /* + * If @bs->encrypted is true, this is the encryption format actually loaded + * at the librbd level. If it is false, it is the result of probing. + * RBD_IMAGE_ENCRYPTION_FORMAT__MAX means that encryption is not enabled and + * probing didn't find any known encryption header either. + */ + RbdImageEncryptionFormat encryption_format; } BDRVRBDState; typedef struct RBDTask { @@ -470,10 +478,12 @@ static int qemu_rbd_encryption_format(rbd_image_t image, return 0; } -static int qemu_rbd_encryption_load(rbd_image_t image, +static int qemu_rbd_encryption_load(BlockDriverState *bs, + rbd_image_t image, RbdEncryptionOptions *encrypt, Error **errp) { + BDRVRBDState *s = bs->opaque; int r = 0; g_autofree char *passphrase = NULL; rbd_encryption_luks1_format_options_t luks_opts; @@ -544,15 +554,19 @@ static int qemu_rbd_encryption_load(rbd_image_t image, error_setg_errno(errp, -r, "encryption load fail"); return r; } + bs->encrypted = true; + s->encryption_format = encrypt->format; return 0; } #ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2 -static int qemu_rbd_encryption_load2(rbd_image_t image, +static int qemu_rbd_encryption_load2(BlockDriverState *bs, + rbd_image_t image, RbdEncryptionOptions *encrypt, Error **errp) { + BDRVRBDState *s = bs->opaque; int r = 0; int encrypt_count = 1; int i; @@ -638,6 +652,8 @@ static int qemu_rbd_encryption_load2(rbd_image_t image, error_setg_errno(errp, -r, "layered encryption load fail"); goto exit; } + bs->encrypted = true; + s->encryption_format = encrypt->format; exit: for (i = 0; i < encrypt_count; ++i) { @@ -671,6 +687,45 @@ static int qemu_rbd_encryption_load2(rbd_image_t image, #endif #endif +/* + * For an image without encryption enabled on the rbd layer, probe the start of + * the image if it could be opened as an encrypted image so that we can display + * it when the user queries the node (most importantly in qemu-img). + * + * If the guest writes an encryption header to its disk after this probing, this + * won't be reflected when queried, but that's okay. There is no reason why the + * user should want to apply encryption at the rbd level while the image is + * still in use. This is just guest data. + */ +static void qemu_rbd_encryption_probe(BlockDriverState *bs) +{ + BDRVRBDState *s = bs->opaque; + char buf[RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {0}; + int r; + + assert(s->encryption_format == RBD_IMAGE_ENCRYPTION_FORMAT__MAX); + + r = rbd_read(s->image, 0, + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN, buf); + if (r < RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) { + return; + } + + if (memcmp(buf, rbd_luks_header_verification, + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { + s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS; + } else if (memcmp(buf, rbd_luks2_header_verification, + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { + s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2; + } else if (memcmp(buf, rbd_layered_luks_header_verification, + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { + s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS; + } else if (memcmp(buf, rbd_layered_luks2_header_verification, + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { + s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2; + } +} + /* FIXME Deprecate and remove keypairs or make it available in QMP. */ static int qemu_rbd_do_create(BlockdevCreateOptions *options, const char *keypairs, const char *password_secret, @@ -1133,17 +1188,18 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, goto failed_open; } + s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT__MAX; if (opts->encrypt) { #ifdef LIBRBD_SUPPORTS_ENCRYPTION if (opts->encrypt->parent) { #ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2 - r = qemu_rbd_encryption_load2(s->image, opts->encrypt, errp); + r = qemu_rbd_encryption_load2(bs, s->image, opts->encrypt, errp); #else r = -ENOTSUP; error_setg(errp, "RBD library does not support layered encryption"); #endif } else { - r = qemu_rbd_encryption_load(s->image, opts->encrypt, errp); + r = qemu_rbd_encryption_load(bs, s->image, opts->encrypt, errp); } if (r < 0) { goto failed_post_open; @@ -1153,6 +1209,8 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, error_setg(errp, "RBD library does not support image encryption"); goto failed_post_open; #endif + } else { + qemu_rbd_encryption_probe(bs); } r = rbd_stat(s->image, &info, sizeof(info)); @@ -1412,17 +1470,6 @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs, { BDRVRBDState *s = bs->opaque; ImageInfoSpecific *spec_info; - char buf[RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {0}; - int r; - - if (s->image_size >= RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) { - r = rbd_read(s->image, 0, - RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN, buf); - if (r < 0) { - error_setg_errno(errp, -r, "cannot read image start for probe"); - return NULL; - } - } spec_info = g_new(ImageInfoSpecific, 1); *spec_info = (ImageInfoSpecific){ @@ -1430,28 +1477,13 @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs, .u.rbd.data = g_new0(ImageInfoSpecificRbd, 1), }; - if (memcmp(buf, rbd_luks_header_verification, - RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { - spec_info->u.rbd.data->encryption_format = - RBD_IMAGE_ENCRYPTION_FORMAT_LUKS; - spec_info->u.rbd.data->has_encryption_format = true; - } else if (memcmp(buf, rbd_luks2_header_verification, - RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { - spec_info->u.rbd.data->encryption_format = - RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2; - spec_info->u.rbd.data->has_encryption_format = true; - } else if (memcmp(buf, rbd_layered_luks_header_verification, - RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { - spec_info->u.rbd.data->encryption_format = - RBD_IMAGE_ENCRYPTION_FORMAT_LUKS; - spec_info->u.rbd.data->has_encryption_format = true; - } else if (memcmp(buf, rbd_layered_luks2_header_verification, - RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { - spec_info->u.rbd.data->encryption_format = - RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2; - spec_info->u.rbd.data->has_encryption_format = true; + if (s->encryption_format == RBD_IMAGE_ENCRYPTION_FORMAT__MAX) { + assert(!bs->encrypted); } else { - spec_info->u.rbd.data->has_encryption_format = false; + ImageInfoSpecificRbd *rbd_info = spec_info->u.rbd.data; + + rbd_info->has_encryption_format = true; + rbd_info->encryption_format = s->encryption_format; } return spec_info; diff --git a/qapi/block-core.json b/qapi/block-core.json index ebbe95b3d82b0..dc6eb4ae23dd1 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -159,7 +159,14 @@ ## # @ImageInfoSpecificRbd: # -# @encryption-format: Image encryption format +# @encryption-format: Image encryption format. If encryption is enabled for the +# image (see encrypted in BlockNodeInfo), this is the actual format in which the +# image is accessed. If encryption is not enabled, this is the result of +# probing when the image was opened, to give a suggestion which encryption +# format could be enabled. Note that probing results can be changed by the +# guest by writing a (possibly partial) encryption format header to the +# image, so don't treat this information as trusted if the guest is not +# trusted. # # Since: 6.1 ## From c0df98ab1f3d348bc05f09d1c093abc529f2b530 Mon Sep 17 00:00:00 2001 From: Werner Fink Date: Wed, 6 Aug 2025 08:54:51 +0200 Subject: [PATCH 0249/1794] qemu-iotests: Ignore indentation in Killed messages New bash 5.3 uses a different padding for reporting job status. Resolves: boo#1246830 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3050 Signed-off-by: Werner Fink Message-ID: Reviewed-by: Kevin Wolf Tested-by: Martin Kletzander Signed-off-by: Kevin Wolf --- tests/qemu-iotests/039.out | 10 +++++----- tests/qemu-iotests/061.out | 4 ++-- tests/qemu-iotests/137.out | 2 +- tests/qemu-iotests/common.filter | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/qemu-iotests/039.out b/tests/qemu-iotests/039.out index e52484d4be1bd..8fdbcc528aaa1 100644 --- a/tests/qemu-iotests/039.out +++ b/tests/qemu-iotests/039.out @@ -11,7 +11,7 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) +./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) incompatible_features [0] ERROR cluster 5 refcount=0 reference=1 ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0 @@ -46,7 +46,7 @@ read 512/512 bytes at offset 0 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) +./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) incompatible_features [0] ERROR cluster 5 refcount=0 reference=1 Rebuilding refcount structure @@ -60,7 +60,7 @@ incompatible_features [] Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) +./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) incompatible_features [] No errors were found on the image. @@ -79,7 +79,7 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) +./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) incompatible_features [0] ERROR cluster 5 refcount=0 reference=1 ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0 @@ -89,7 +89,7 @@ Data may be corrupted, or further writes to the image may corrupt it. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) +./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) incompatible_features [] No errors were found on the image. *** done diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out index 24c33add7ce60..951c6bf3e62c2 100644 --- a/tests/qemu-iotests/061.out +++ b/tests/qemu-iotests/061.out @@ -118,7 +118,7 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) +./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) magic 0x514649fb version 3 backing_file_offset 0x0 @@ -304,7 +304,7 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) +./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) magic 0x514649fb version 3 backing_file_offset 0x0 diff --git a/tests/qemu-iotests/137.out b/tests/qemu-iotests/137.out index 86377c80cde6c..e19df5b6ba82a 100644 --- a/tests/qemu-iotests/137.out +++ b/tests/qemu-iotests/137.out @@ -35,7 +35,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) +./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) OK: Dirty bit not set Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: Parameter 'lazy-refcounts' expects 'on' or 'off' diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 67f819d866a86..511a55b1e88e2 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -74,7 +74,7 @@ _filter_qemu_io() { _filter_win32 | \ gsed -e "s/[0-9]* ops\; [0-9/:. sec]* ([0-9/.inf]* [EPTGMKiBbytes]*\/sec and [0-9/.inf]* ops\/sec)/X ops\; XX:XX:XX.X (XXX YYY\/sec and XXX ops\/sec)/" \ - -e "s/: line [0-9][0-9]*: *[0-9][0-9]*\( Aborted\| Killed\)/:\1/" \ + -e "s/: line [0-9][0-9]*: *[0-9][0-9]*\( Aborted\| Killed\) \{2,\}/:\1 /" \ -e "s/qemu-io> //g" } From e262646e12acd6c1132e03d57fea20680a503251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Aug 2025 14:57:44 +0200 Subject: [PATCH 0250/1794] hw/sd/ssi-sd: Return noise (dummy byte) when no card connected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 1585ab9f1ba ("hw/sd/sdcard: Fill SPI response bits in card code") exposed a bug in the SPI adapter: if no SD card is plugged, we are returning "there is a card with an error". This is wrong, we shouldn't return any particular packet response, but the noise shifted on the MISO line. Return the dummy byte, otherwise we get: qemu-system-riscv64: ../hw/sd/ssi-sd.c:160: ssi_sd_transfer: Assertion `s->arglen > 0' failed. Reported-by: Guenter Roeck Fixes: 775616c3ae8 ("Partial SD card SPI mode support") Signed-off-by: Philippe Mathieu-Daudé Tested-by: Guenter Roeck Reviewed-by: Alex Bennée Reviewed-by: Gustavo Romero Tested-by: Alex Bennée Message-Id: <20250812140415.70153-2-philmd@linaro.org> --- hw/sd/ssi-sd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 594dead19eea1..3aacbd0387153 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -89,6 +89,10 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) SDRequest request; uint8_t longresp[5]; + if (!sdbus_get_inserted(&s->sdbus)) { + return SSI_DUMMY; + } + /* * Special case: allow CMD12 (STOP TRANSMISSION) while reading data. * From 7db162fa013878b06a528686ece79ad99f699c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Aug 2025 15:45:34 +0200 Subject: [PATCH 0251/1794] tests/functional: Test SPI-SD adapter without SD card connected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SPI-SD adapter should be usable, even without any SD card wired. Refactor test_riscv64_sifive_u_mmc_spi() to make it more generic and add another test, inspired by this report: https://lore.kernel.org/qemu-devel/5b2dc427-f0db-4332-a997-fe0c82415acd@roeck-us.net/ Inspired-by: Guenter Roeck Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Gustavo Romero Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-Id: <20250812140415.70153-3-philmd@linaro.org> --- tests/functional/test_riscv64_sifive_u.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/functional/test_riscv64_sifive_u.py b/tests/functional/test_riscv64_sifive_u.py index dc4cb8a4a96f3..358ff0d1f6090 100755 --- a/tests/functional/test_riscv64_sifive_u.py +++ b/tests/functional/test_riscv64_sifive_u.py @@ -27,25 +27,37 @@ class SifiveU(LinuxKernelTest): 'rootfs.ext2.gz'), 'b6ed95610310b7956f9bf20c4c9c0c05fea647900df441da9dfe767d24e8b28b') - def test_riscv64_sifive_u_mmc_spi(self): + def do_test_riscv64_sifive_u_mmc_spi(self, connect_card): self.set_machine('sifive_u') kernel_path = self.ASSET_KERNEL.fetch() rootfs_path = self.uncompress(self.ASSET_ROOTFS) self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'root=/dev/mmcblk0 rootwait ' 'earlycon=sbi console=ttySIF0 ' - 'panic=-1 noreboot') + 'root=/dev/mmcblk0 ') self.vm.add_args('-kernel', kernel_path, - '-drive', f'file={rootfs_path},if=sd,format=raw', '-append', kernel_command_line, '-no-reboot') + if connect_card: + kernel_command_line += 'panic=-1 noreboot rootwait ' + self.vm.add_args('-drive', f'file={rootfs_path},if=sd,format=raw') + pattern = 'Boot successful.' + else: + kernel_command_line += 'panic=0 noreboot ' + pattern = 'Cannot open root device "mmcblk0" or unknown-block(0,0)' + self.vm.launch() - self.wait_for_console_pattern('Boot successful.') + self.wait_for_console_pattern(pattern) os.remove(rootfs_path) + def test_riscv64_sifive_u_nommc_spi(self): + self.do_test_riscv64_sifive_u_mmc_spi(False) + + def test_riscv64_sifive_u_mmc_spi(self): + self.do_test_riscv64_sifive_u_mmc_spi(True) + if __name__ == '__main__': LinuxKernelTest.main() From 6ad034e71232c2929ed546304c9d249312bb632f Mon Sep 17 00:00:00 2001 From: "Sv. Lockal" Date: Mon, 11 Aug 2025 15:01:59 -0400 Subject: [PATCH 0252/1794] mkvenv: Support pip 25.2 Fix compilation with pip-25.2 due to missing distlib.version Bug: https://gitlab.com/qemu-project/qemu/-/issues/3062 Signed-off-by: Sv. Lockal [Edits: Type "safety" whackamole --js] Signed-off-by: John Snow Message-ID: <20250811190159.237321-1-jsnow@redhat.com> Signed-off-by: Stefan Hajnoczi --- python/scripts/mkvenv.py | 64 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/python/scripts/mkvenv.py b/python/scripts/mkvenv.py index 8ac5b0b2a05c9..f102527c4de49 100644 --- a/python/scripts/mkvenv.py +++ b/python/scripts/mkvenv.py @@ -84,6 +84,7 @@ Sequence, Tuple, Union, + cast, ) import venv @@ -94,17 +95,39 @@ HAVE_DISTLIB = True try: import distlib.scripts - import distlib.version except ImportError: try: # Reach into pip's cookie jar. pylint and flake8 don't understand # that these imports will be used via distlib.xxx. from pip._vendor import distlib import pip._vendor.distlib.scripts # noqa, pylint: disable=unused-import - import pip._vendor.distlib.version # noqa, pylint: disable=unused-import except ImportError: HAVE_DISTLIB = False +# pip 25.2 does not vendor distlib.version, but it uses vendored +# packaging.version +HAVE_DISTLIB_VERSION = True +try: + import distlib.version # pylint: disable=ungrouped-imports +except ImportError: + try: + # pylint: disable=unused-import,ungrouped-imports + import pip._vendor.distlib.version # noqa + except ImportError: + HAVE_DISTLIB_VERSION = False + +HAVE_PACKAGING_VERSION = True +try: + # Do not bother importing non-vendored packaging, because it is not + # in stdlib. + from pip._vendor import packaging + # pylint: disable=unused-import + import pip._vendor.packaging.requirements # noqa + import pip._vendor.packaging.version # noqa +except ImportError: + HAVE_PACKAGING_VERSION = False + + # Try to load tomllib, with a fallback to tomli. # HAVE_TOMLLIB is checked below, just-in-time, so that mkvenv does not fail # outside the venv or before a potential call to ensurepip in checkpip(). @@ -133,6 +156,39 @@ class Ouch(RuntimeError): """An Exception class we can't confuse with a builtin.""" +class Matcher: + """Compatibility appliance for version/requirement string parsing.""" + def __init__(self, name_and_constraint: str): + """Create a matcher from a requirement-like string.""" + if HAVE_DISTLIB_VERSION: + self._m = distlib.version.LegacyMatcher(name_and_constraint) + elif HAVE_PACKAGING_VERSION: + self._m = packaging.requirements.Requirement(name_and_constraint) + else: + raise Ouch("found neither distlib.version nor packaging.version") + self.name = self._m.name + + def match(self, version_str: str) -> bool: + """Return True if `version` satisfies the stored constraint.""" + if HAVE_DISTLIB_VERSION: + return cast( + bool, + self._m.match(distlib.version.LegacyVersion(version_str)) + ) + + assert HAVE_PACKAGING_VERSION + return cast( + bool, + self._m.specifier.contains( + packaging.version.Version(version_str), prereleases=True + ) + ) + + def __repr__(self) -> str: + """Stable debug representation delegated to the backend.""" + return repr(self._m) + + class QemuEnvBuilder(venv.EnvBuilder): """ An extension of venv.EnvBuilder for building QEMU's configure-time venv. @@ -669,7 +725,7 @@ def _do_ensure( canary = None for name, info in group.items(): constraint = _make_version_constraint(info, False) - matcher = distlib.version.LegacyMatcher(name + constraint) + matcher = Matcher(name + constraint) print(f"mkvenv: checking for {matcher}", file=sys.stderr) dist: Optional[Distribution] = None @@ -683,7 +739,7 @@ def _do_ensure( # Always pass installed package to pip, so that they can be # updated if the requested version changes or not _is_system_package(dist) - or not matcher.match(distlib.version.LegacyVersion(dist.version)) + or not matcher.match(dist.version) ): absent.append(name + _make_version_constraint(info, True)) if len(absent) == 1: From b2a948220763b0d5184285a6b72d7eb13d51aad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 4 Aug 2025 17:29:59 +0100 Subject: [PATCH 0253/1794] readthedocs: don't build extra formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't build the PDFs ourselves for the hosted docs and it looks like rtd can't manage building PDFs now they have gone over a certain size. Disable the extra formats so we can at least have the online stuff again. Signed-off-by: Alex Bennée Reviewed-by: Daniel P. Berrangé Message-ID: <20250804162959.330060-1-alex.bennee@linaro.org> Signed-off-by: Stefan Hajnoczi --- .readthedocs.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 0b262469ce693..639f628612c88 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -21,5 +21,3 @@ python: install: - requirements: docs/requirements.txt -# We want all the document formats -formats: all From 0c0729b46a3680c233e0d45647d5193c5c5083f9 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Fri, 18 Jul 2025 16:40:39 -0700 Subject: [PATCH 0254/1794] ui/spice: Destroy the temporary egl fb after the blit is submitted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The temporary egl fb scanout_tex_fb is only needed to facilitate the blit to the display surface's texture (ssd->ds->texture). Therefore, destroy it after the blit is submitted. And, also make sure that it is empty initialized before it is actually used. Fixes: f851cd65 ("ui/spice: Blit the scanout texture if its memory layout is not linear") Reported-by: Peter Maydell Cc: Marc-André Lureau Signed-off-by: Vivek Kasireddy Reviewed-by: Marc-André Lureau Message-ID: <20250718234039.2266704-1-vivek.kasireddy@intel.com> Signed-off-by: Stefan Hajnoczi --- ui/spice-display.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ui/spice-display.c b/ui/spice-display.c index 9ce622cefce23..669832c5612a0 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -1183,20 +1183,20 @@ static void qemu_spice_gl_release_dmabuf(DisplayChangeListener *dcl, egl_dmabuf_release_texture(dmabuf); } -static bool spice_gl_blit_scanout_texture(SimpleSpiceDisplay *ssd, - egl_fb *scanout_tex_fb) +static bool spice_gl_blit_scanout_texture(SimpleSpiceDisplay *ssd) { uint32_t offsets[DMABUF_MAX_PLANES], strides[DMABUF_MAX_PLANES]; int fds[DMABUF_MAX_PLANES], num_planes, fourcc; + egl_fb scanout_tex_fb = {}; uint64_t modifier; bool ret; - egl_fb_destroy(scanout_tex_fb); - egl_fb_setup_for_tex(scanout_tex_fb, + egl_fb_setup_for_tex(&scanout_tex_fb, surface_width(ssd->ds), surface_height(ssd->ds), ssd->ds->texture, false); - egl_fb_blit(scanout_tex_fb, &ssd->guest_fb, false); + egl_fb_blit(&scanout_tex_fb, &ssd->guest_fb, false); glFlush(); + egl_fb_destroy(&scanout_tex_fb); if (!ssd->new_scanout_texture) { return true; @@ -1330,9 +1330,7 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, } if (spice_remote_client && ssd->blit_scanout_texture) { - egl_fb scanout_tex_fb; - - ret = spice_gl_blit_scanout_texture(ssd, &scanout_tex_fb); + ret = spice_gl_blit_scanout_texture(ssd); if (!ret) { return; } From de784dc0a0128146a88437d57ea27a58af507de0 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 12 Aug 2025 16:26:39 -0400 Subject: [PATCH 0255/1794] Update version for the v10.1.0-rc3 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index c08f99e23ed2e..d53c9cff2743b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.0.92 +10.0.93 From faaaf017d5b9c9f84fb86dcee016944176eee0d9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 Aug 2025 17:02:35 +0200 Subject: [PATCH 0256/1794] Revert "i386/cpu: Warn about why CPUID_EXT_PDCM is not available" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 00268e00027459abede448662f8794d78eb4b0a4. (The only conflict is in the !is_tdx_vm() part of the condition, which is safe to keep). mark_unavailable_features() actively blocks usage of the feature, so it is a functional change, not merely a emitting warning. The commit was intended to merely warn if PDCM was enabled when the performance counters are not, so revert it. Reported-by: Christian A. Ehrhardt Analyzed-by: Daniel P. Berrangé Analyzed-by: Xiaoyao Li Signed-off-by: Paolo Bonzini Reviewed-by: Daniel P. Berrangé Message-ID: <20250819150235.785559-1-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- target/i386/cpu.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 673f8583c8095..6d85149e6e1b5 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8946,9 +8946,6 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) /* PDCM is fixed1 bit for TDX */ if (!cpu->enable_pmu && !is_tdx_vm()) { - mark_unavailable_features(cpu, FEAT_1_ECX, - env->user_features[FEAT_1_ECX] & CPUID_EXT_PDCM, - "This feature is not available due to PMU being disabled"); env->features[FEAT_1_ECX] &= ~CPUID_EXT_PDCM; } From 88f72048d2f5835a1b9eaba690c7861393aef283 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 19 Aug 2025 10:39:59 -0400 Subject: [PATCH 0257/1794] Update version for the v10.1.0-rc4 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index d53c9cff2743b..dadcbd47d3c22 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.0.93 +10.0.94 From 27535e9ccae89db5856bfb5e3357f44645812143 Mon Sep 17 00:00:00 2001 From: Xin Wang Date: Tue, 19 Aug 2025 22:58:34 +0800 Subject: [PATCH 0258/1794] target/i386: Add support for save/load of exception error code For now, qemu save/load CPU exception info(such as exception_nr and has_error_code), while the exception error_code is ignored. This will cause the dest hypervisor reinject a vCPU exception with error_code(0), potentially causing a guest kernel panic. For instance, if src VM stopped with an user-mode write #PF (error_code 6), the dest hypervisor will reinject an #PF with error_code(0) when vCPU resume, then guest kernel panic as: BUG: unable to handle page fault for address: 00007f80319cb010 #PF: supervisor read access in user mode #PF: error_code(0x0000) - not-present page RIP: 0033:0x40115d To fix it, support save/load exception error_code. Signed-off-by: Xin Wang Link: https://lore.kernel.org/r/20250819145834.3998-1-wangxinxin.wang@huawei.com Signed-off-by: Paolo Bonzini --- target/i386/machine.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/target/i386/machine.c b/target/i386/machine.c index dd2dac1d443c8..45b7cea80aa7f 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -462,6 +462,24 @@ static const VMStateDescription vmstate_exception_info = { } }; +static bool cpu_errcode_needed(void *opaque) +{ + X86CPU *cpu = opaque; + + return cpu->env.has_error_code != 0; +} + +static const VMStateDescription vmstate_error_code = { + .name = "cpu/error_code", + .version_id = 1, + .minimum_version_id = 1, + .needed = cpu_errcode_needed, + .fields = (const VMStateField[]) { + VMSTATE_INT32(env.error_code, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + /* Poll control MSR enabled by default */ static bool poll_control_msr_needed(void *opaque) { @@ -1746,6 +1764,7 @@ const VMStateDescription vmstate_x86_cpu = { }, .subsections = (const VMStateDescription * const []) { &vmstate_exception_info, + &vmstate_error_code, &vmstate_async_pf_msr, &vmstate_async_pf_int_msr, &vmstate_pv_eoi_msr, From a9292b24c35ca40da5bc4b2fd7fcf898b08dcce9 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Wed, 20 Aug 2025 23:41:56 +0300 Subject: [PATCH 0259/1794] scripts/minikconf.py: fix invalid attribute access Fix parse method to use `defconfig` global variable instead of the non-existent KconfigParser class attribute Fixes: f349474920d80838ecea3d421531fdb0660b8740 ("minikconfig: implement allnoconfig and defconfig modes") Signed-off-by: Manos Pitsidianakis Link: https://lore.kernel.org/r/20250820-scripts-minikconf-fixes-v1-1-252041a9125e@linaro.org Signed-off-by: Paolo Bonzini --- scripts/minikconf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/minikconf.py b/scripts/minikconf.py index 6f7f43b291816..2a4694fb6a389 100644 --- a/scripts/minikconf.py +++ b/scripts/minikconf.py @@ -340,7 +340,7 @@ class KconfigParser: @classmethod def parse(self, fp, mode=None): - data = KconfigData(mode or KconfigParser.defconfig) + data = KconfigData(mode or defconfig) parser = KconfigParser(data) parser.parse_file(fp) return data From d84082cc1a7a7cac361094fc9b3165df7c697a01 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Wed, 20 Aug 2025 23:41:57 +0300 Subject: [PATCH 0260/1794] scripts/minikconf.py: s/Error/KconfigParserError Error is not defined in this script, raise KconfigParserError instead. Fixes: 82f5181777ebe04b550fd94a1d04c49dd3f012dc ("kconfig: introduce kconfig files") Signed-off-by: Manos Pitsidianakis Link: https://lore.kernel.org/r/20250820-scripts-minikconf-fixes-v1-2-252041a9125e@linaro.org Signed-off-by: Paolo Bonzini --- scripts/minikconf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/minikconf.py b/scripts/minikconf.py index 2a4694fb6a389..4de5aeed11a22 100644 --- a/scripts/minikconf.py +++ b/scripts/minikconf.py @@ -363,7 +363,9 @@ def parse_file(self, fp): def do_assignment(self, var, val): if not var.startswith("CONFIG_"): - raise Error('assigned variable should start with CONFIG_') + raise KconfigParserError( + self, "assigned variable should start with CONFIG_" + ) var = self.data.do_var(var[7:]) self.data.do_assignment(var, val) From ab85146ac4c6527d6d975afbd3157488cb42147f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 22 Aug 2025 10:46:05 +0200 Subject: [PATCH 0261/1794] python: mkvenv: fix messages printed by mkvenv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new Matcher class does not have a __str__ implementation, and therefore it prints the debugging representation of the internal object: $ ../configure --enable-rust && make qemu-system-arm --enable-download python determined to be '/usr/bin/python3' python version: Python 3.13.6 mkvenv: Creating non-isolated virtual environment at 'pyvenv' mkvenv: checking for LegacyMatcher('meson>=1.5.0') mkvenv: checking for LegacyMatcher('pycotap>=1.1.0') Add the method to print the nicer mkvenv: checking for meson>=1.5.0 mkvenv: checking for pycotap>=1.1.0 Cc: qemu-stable@nongnu.org Cc: John Snow Reviewed-by: Manos Pitsidianakis Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- python/scripts/mkvenv.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/scripts/mkvenv.py b/python/scripts/mkvenv.py index f102527c4de49..9aed266df1ba8 100644 --- a/python/scripts/mkvenv.py +++ b/python/scripts/mkvenv.py @@ -184,6 +184,10 @@ def match(self, version_str: str) -> bool: ) ) + def __str__(self) -> str: + """String representation delegated to the backend.""" + return str(self._m) + def __repr__(self) -> str: """Stable debug representation delegated to the backend.""" return repr(self._m) From 6f8924163ff1fb4bd19e0cd9dc7910bb540486f3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 Aug 2025 17:42:16 +0200 Subject: [PATCH 0262/1794] MAINTAINERS: add a few more files to "Top Level Makefile and configure" A few files in scripts, and the list of packages in pythondeps.toml, are strictly related to the toplevel build scripts. Add them to the MAINTAINERS file stanza. Signed-off-by: Paolo Bonzini --- MAINTAINERS | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index a07086ed76213..0f3e55b51e8ee 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4392,7 +4392,6 @@ R: Philippe Mathieu-Daudé S: Maintained F: meson.build F: meson_options.txt -F: scripts/meson-buildoptions.* F: scripts/check_sparse.py F: scripts/symlink-install-tree.py @@ -4403,6 +4402,9 @@ R: Thomas Huth S: Maintained F: Makefile F: configure +F: pythondeps.toml +F: scripts/git-submodule.sh +F: scripts/meson-buildoptions.* F: scripts/mtest2make.py F: tests/Makefile.include From f8b2f64e2336a28bf0d50b6ef8a7d8c013e9bcf3 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 26 Aug 2025 11:23:27 -0400 Subject: [PATCH 0263/1794] Update version for the v10.1.0 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index dadcbd47d3c22..4149c39eec6fa 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.0.94 +10.1.0 From e771ba98de25c9f43959f79fc7099cf7fbba44cc Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 26 Aug 2025 14:10:25 -0400 Subject: [PATCH 0264/1794] Open 10.2 development tree Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 4149c39eec6fa..9856be5dd9873 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.1.0 +10.1.50 From d96519c6bd1e197f24a157ec761ff69cf54779d3 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Tue, 5 Aug 2025 11:56:16 +0200 Subject: [PATCH 0265/1794] hw: add compat machines for 10.2 Add 10.2 machine types for arm/i440fx/m68k/q35/s390x/spapr. Signed-off-by: Cornelia Huck Reviewed-by: Thomas Huth Acked-by: Michael S. Tsirkin Message-ID: <20250805095616.1168905-1-cohuck@redhat.com> Signed-off-by: Thomas Huth --- hw/arm/virt.c | 9 ++++++++- hw/core/machine.c | 3 +++ hw/i386/pc.c | 3 +++ hw/i386/pc_piix.c | 13 +++++++++++-- hw/i386/pc_q35.c | 13 +++++++++++-- hw/m68k/virt.c | 9 ++++++++- hw/ppc/spapr.c | 15 +++++++++++++-- hw/s390x/s390-virtio-ccw.c | 14 +++++++++++++- include/hw/boards.h | 3 +++ include/hw/i386/pc.h | 3 +++ 10 files changed, 76 insertions(+), 9 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index ef6be3660f5fb..9326cfc895f7b 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -3455,10 +3455,17 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); +static void virt_machine_10_2_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(10, 2) + static void virt_machine_10_1_options(MachineClass *mc) { + virt_machine_10_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_1, hw_compat_10_1_len); } -DEFINE_VIRT_MACHINE_AS_LATEST(10, 1) +DEFINE_VIRT_MACHINE(10, 1) static void virt_machine_10_0_options(MachineClass *mc) { diff --git a/hw/core/machine.c b/hw/core/machine.c index bd47527479a79..38c949c4f2ce4 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -37,6 +37,9 @@ #include "hw/virtio/virtio-iommu.h" #include "audio/audio.h" +GlobalProperty hw_compat_10_1[] = {}; +const size_t hw_compat_10_1_len = G_N_ELEMENTS(hw_compat_10_1); + GlobalProperty hw_compat_10_0[] = { { "scsi-hd", "dpofua", "off" }, { "vfio-pci", "x-migration-load-config-after-iter", "off" }, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 2f58e73d33473..bc048a6d13743 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -81,6 +81,9 @@ { "qemu64-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\ { "athlon-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, }, +GlobalProperty pc_compat_10_1[] = {}; +const size_t pc_compat_10_1_len = G_N_ELEMENTS(pc_compat_10_1); + GlobalProperty pc_compat_10_0[] = { { TYPE_X86_CPU, "x-consistent-cache", "false" }, { TYPE_X86_CPU, "x-vendor-cpuid-only-v2", "false" }, diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index c03324281bdb3..d165ac72ed75c 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -504,12 +504,21 @@ static void pc_i440fx_machine_options(MachineClass *m) pc_piix_compat_defaults, pc_piix_compat_defaults_len); } -static void pc_i440fx_machine_10_1_options(MachineClass *m) +static void pc_i440fx_machine_10_2_options(MachineClass *m) { pc_i440fx_machine_options(m); } -DEFINE_I440FX_MACHINE_AS_LATEST(10, 1); +DEFINE_I440FX_MACHINE_AS_LATEST(10, 2); + +static void pc_i440fx_machine_10_1_options(MachineClass *m) +{ + pc_i440fx_machine_10_2_options(m); + compat_props_add(m->compat_props, hw_compat_10_1, hw_compat_10_1_len); + compat_props_add(m->compat_props, pc_compat_10_1, pc_compat_10_1_len); +} + +DEFINE_I440FX_MACHINE(10, 1); static void pc_i440fx_machine_10_0_options(MachineClass *m) { diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index b309b2b378db4..e89951285e58d 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -374,12 +374,21 @@ static void pc_q35_machine_options(MachineClass *m) pc_q35_compat_defaults, pc_q35_compat_defaults_len); } -static void pc_q35_machine_10_1_options(MachineClass *m) +static void pc_q35_machine_10_2_options(MachineClass *m) { pc_q35_machine_options(m); } -DEFINE_Q35_MACHINE_AS_LATEST(10, 1); +DEFINE_Q35_MACHINE_AS_LATEST(10, 2); + +static void pc_q35_machine_10_1_options(MachineClass *m) +{ + pc_q35_machine_10_2_options(m); + compat_props_add(m->compat_props, hw_compat_10_1, hw_compat_10_1_len); + compat_props_add(m->compat_props, pc_compat_10_1, pc_compat_10_1_len); +} + +DEFINE_Q35_MACHINE(10, 1); static void pc_q35_machine_10_0_options(MachineClass *m) { diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 875fd00ef8d28..98cfe43c73ae2 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -367,10 +367,17 @@ type_init(virt_machine_register_types) #define DEFINE_VIRT_MACHINE(major, minor) \ DEFINE_VIRT_MACHINE_IMPL(false, major, minor) +static void virt_machine_10_2_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(10, 2) + static void virt_machine_10_1_options(MachineClass *mc) { + virt_machine_10_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_1, hw_compat_10_1_len); } -DEFINE_VIRT_MACHINE_AS_LATEST(10, 1) +DEFINE_VIRT_MACHINE(10, 1) static void virt_machine_10_0_options(MachineClass *mc) { diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 1855a3cd8d035..eb22333404d37 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4761,15 +4761,26 @@ static void spapr_machine_latest_class_options(MachineClass *mc) #define DEFINE_SPAPR_MACHINE(major, minor) \ DEFINE_SPAPR_MACHINE_IMPL(false, major, minor) +/* + * pseries-10.2 + */ +static void spapr_machine_10_2_class_options(MachineClass *mc) +{ + /* Defaults for the latest behaviour inherited from the base class */ +} + +DEFINE_SPAPR_MACHINE_AS_LATEST(10, 2); + /* * pseries-10.1 */ static void spapr_machine_10_1_class_options(MachineClass *mc) { - /* Defaults for the latest behaviour inherited from the base class */ + spapr_machine_10_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_1, hw_compat_10_1_len); } -DEFINE_SPAPR_MACHINE_AS_LATEST(10, 1); +DEFINE_SPAPR_MACHINE(10, 1); /* * pseries-10.0 diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index a79bd13275b9b..d0c6e80cb0507 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -911,14 +911,26 @@ static const TypeInfo ccw_machine_info = { DEFINE_CCW_MACHINE_IMPL(false, major, minor) +static void ccw_machine_10_2_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_10_2_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE_AS_LATEST(10, 2); + static void ccw_machine_10_1_instance_options(MachineState *machine) { + ccw_machine_10_2_instance_options(machine); } static void ccw_machine_10_1_class_options(MachineClass *mc) { + ccw_machine_10_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_1, hw_compat_10_1_len); } -DEFINE_CCW_MACHINE_AS_LATEST(10, 1); +DEFINE_CCW_MACHINE(10, 1); static void ccw_machine_10_0_instance_options(MachineState *machine) { diff --git a/include/hw/boards.h b/include/hw/boards.h index f94713e6e29a6..665b6201214c7 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -779,6 +779,9 @@ struct MachineState { } \ type_init(machine_initfn##_register_types) +extern GlobalProperty hw_compat_10_1[]; +extern const size_t hw_compat_10_1_len; + extern GlobalProperty hw_compat_10_0[]; extern const size_t hw_compat_10_0_len; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 79b72c54dd3f8..e83157ab358f0 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -214,6 +214,9 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size); /* sgx.c */ void pc_machine_init_sgx_epc(PCMachineState *pcms); +extern GlobalProperty pc_compat_10_1[]; +extern const size_t pc_compat_10_1_len; + extern GlobalProperty pc_compat_10_0[]; extern const size_t pc_compat_10_0_len; From fe26463d7b3e0f65de4b11377d762d5dcc03bd24 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Wed, 6 Aug 2025 22:09:09 +0200 Subject: [PATCH 0266/1794] CI: Use mingw-w64-x86_64-curl-winssl instead of mingw-w64-x86_64-curl for Windows build mingw-w64-x86_64-curl-winssl is required for https connections. Signed-off-by: Stefan Weil Reviewed-by: Pierrick Bouvier Message-ID: <20250806200909.507803-1-sw@weilnetz.de> Signed-off-by: Thomas Huth --- .gitlab-ci.d/windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index 45ed0c96feaa8..beac39e5bde81 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -77,7 +77,7 @@ msys2-64bit: git grep make sed mingw-w64-x86_64-binutils mingw-w64-x86_64-ccache - mingw-w64-x86_64-curl + mingw-w64-x86_64-curl-winssl mingw-w64-x86_64-gcc mingw-w64-x86_64-glib2 mingw-w64-x86_64-libnfs From 19ae064832dd915e14306c03b94a505abc13b873 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 26 Aug 2025 13:34:55 +0100 Subject: [PATCH 0267/1794] tests/functional/test_aarch64_virt_gpu: Skip test if EGL won't initialize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If you are using the Nvidia drivers and have installed new versions of those packages but have not yet rebooted the host kernel, attempting to use the egl-headless display will cause QEMU to fail to start with $ qemu-system-aarch64 -M virt -display egl-headless qemu-system-aarch64: egl: eglInitialize failed: EGL_NOT_INITIALIZED qemu-system-aarch64: egl: render node init failed together with this complaint in the host kernel dmesg: [7874777.555649] NVRM: API mismatch: the client has the version 535.247.01, but NVRM: this kernel module has the version 535.230.02. Please NVRM: make sure that this kernel module and all NVIDIA driver NVRM: components have the same version. This isn't a problem with QEMU itself, so reporting this as a test failure is misleading. Instead skip the tests, as we already do for various other kinds of "host system can't actually run the EGL display" situation. Signed-off-by: Peter Maydell Message-ID: <20250826123455.2856988-1-peter.maydell@linaro.org> Reviewed-by: Manos Pitsidianakis Acked-by: Alex Bennée Acked-by: Dmitry Osipenko Signed-off-by: Thomas Huth --- tests/functional/test_aarch64_virt_gpu.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/functional/test_aarch64_virt_gpu.py b/tests/functional/test_aarch64_virt_gpu.py index 38447278579e3..4e50887c3e9fe 100755 --- a/tests/functional/test_aarch64_virt_gpu.py +++ b/tests/functional/test_aarch64_virt_gpu.py @@ -76,6 +76,8 @@ def _launch_virt_gpu(self, gpu_device): self.skipTest("egl-headless support is not available") elif "'type' does not accept value 'dbus'" in excp.output: self.skipTest("dbus display support is not available") + elif "eglInitialize failed: EGL_NOT_INITIALIZED" in excp.output: + self.skipTest("EGL failed to initialize on this host") else: self.log.info("unhandled launch failure: %s", excp.output) raise excp From 36fb9796662e8d1f8626b1cacb1a6d5e35a8bd00 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Wed, 27 Aug 2025 00:10:08 +0000 Subject: [PATCH 0268/1794] tests/functional: Fix reverse_debugging asset precaching This commit fixes the asset precaching in the reverse_debugging test on aarch64. QemuBaseTest.main() precaches assets (kernel, rootfs, DT blobs, etc.) that are defined in variables with the ASSET_ prefix. This works because it ultimately calls Asset.precache_test(), which relies on introspection to locate these variables. If an asset variable is not named with the ASSET_ prefix, precache_test cannot find the asset and precaching silently fails. Hence, fix the asset precaching by fixing the asset variable name. Signed-off-by: Gustavo Romero Reviewed-by: Richard Henderson Reviewed-by: Manos Pitsidianakis Message-ID: <20250827001008.22112-1-gustavo.romero@linaro.org> Signed-off-by: Thomas Huth --- tests/functional/test_aarch64_reverse_debug.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/test_aarch64_reverse_debug.py b/tests/functional/test_aarch64_reverse_debug.py index 58d45328350f5..8bc91ccfde711 100755 --- a/tests/functional/test_aarch64_reverse_debug.py +++ b/tests/functional/test_aarch64_reverse_debug.py @@ -21,7 +21,7 @@ class ReverseDebugging_AArch64(ReverseDebugging): REG_PC = 32 - KERNEL_ASSET = Asset( + ASSET_KERNEL = Asset( ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' 'releases/29/Everything/aarch64/os/images/pxeboot/vmlinuz'), '7e1430b81c26bdd0da025eeb8fbd77b5dc961da4364af26e771bd39f379cbbf7') @@ -30,7 +30,7 @@ class ReverseDebugging_AArch64(ReverseDebugging): def test_aarch64_virt(self): self.set_machine('virt') self.cpu = 'cortex-a53' - kernel_path = self.KERNEL_ASSET.fetch() + kernel_path = self.ASSET_KERNEL.fetch() self.reverse_debugging(args=('-kernel', kernel_path)) From 0cdabf8adbaca4b370301366faab2d01121289c7 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 21 Aug 2025 11:47:35 +0200 Subject: [PATCH 0269/1794] tests/functional: Use more fine-grained locking when looking for free ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, we have one lock that is held while a test is looking for free ports. However, we are also using different ranges for looking for free ports nowadays (PORTS_START is based on the PID of the process), so instead of using only one lock, we should rather use a lock per range instead. This should help to allow running more tests in parallel. While we're at it, also create the lock files without executable bit (mode is 0o777 by default). Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth Message-ID: <20250821094735.804210-1-thuth@redhat.com> --- tests/functional/qemu_test/ports.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/functional/qemu_test/ports.py b/tests/functional/qemu_test/ports.py index 631b77abf6bc2..81174a6153280 100644 --- a/tests/functional/qemu_test/ports.py +++ b/tests/functional/qemu_test/ports.py @@ -23,8 +23,9 @@ class Ports(): PORTS_END = PORTS_START + PORTS_RANGE_SIZE def __enter__(self): - lock_file = os.path.join(BUILD_DIR, "tests", "functional", "port_lock") - self.lock_fh = os.open(lock_file, os.O_CREAT) + lock_file = os.path.join(BUILD_DIR, "tests", "functional", + f".port_lock.{self.PORTS_START}") + self.lock_fh = os.open(lock_file, os.O_CREAT, mode=0o666) fcntl.flock(self.lock_fh, fcntl.LOCK_EX) return self From 9845740a162f2d9a4c852068155e94f1a3c487f9 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:38 +0200 Subject: [PATCH 0270/1794] tests/functional: Rework the migration test to have target-specific files We are going to move the tests for each target into separate subdirectories. The migration test does not fit quite into this scheme, since it works for multiple targets, but not all. Rework the test to have a common test class, and target specific files with a target specific class, so that this will fit better into the new scheme. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-2-thuth@redhat.com> --- MAINTAINERS | 2 +- tests/functional/meson.build | 22 ++++++------ .../{test_migration.py => migration.py} | 35 +++++-------------- tests/functional/test_aarch64_migration.py | 26 ++++++++++++++ tests/functional/test_alpha_migration.py | 26 ++++++++++++++ tests/functional/test_arm_migration.py | 26 ++++++++++++++ tests/functional/test_i386_migration.py | 26 ++++++++++++++ tests/functional/test_ppc64_migration.py | 26 ++++++++++++++ tests/functional/test_ppc_migration.py | 26 ++++++++++++++ tests/functional/test_riscv32_migration.py | 26 ++++++++++++++ tests/functional/test_riscv64_migration.py | 26 ++++++++++++++ tests/functional/test_sparc64_migration.py | 26 ++++++++++++++ tests/functional/test_sparc_migration.py | 26 ++++++++++++++ tests/functional/test_x86_64_migration.py | 26 ++++++++++++++ 14 files changed, 306 insertions(+), 39 deletions(-) rename tests/functional/{test_migration.py => migration.py} (74%) mode change 100755 => 100644 create mode 100755 tests/functional/test_aarch64_migration.py create mode 100755 tests/functional/test_alpha_migration.py create mode 100755 tests/functional/test_arm_migration.py create mode 100755 tests/functional/test_i386_migration.py create mode 100755 tests/functional/test_ppc64_migration.py create mode 100755 tests/functional/test_ppc_migration.py create mode 100755 tests/functional/test_riscv32_migration.py create mode 100755 tests/functional/test_riscv64_migration.py create mode 100755 tests/functional/test_sparc64_migration.py create mode 100755 tests/functional/test_sparc_migration.py create mode 100755 tests/functional/test_x86_64_migration.py diff --git a/MAINTAINERS b/MAINTAINERS index a07086ed76213..56c1fe676907a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3597,7 +3597,7 @@ F: include/migration/ F: include/qemu/userfaultfd.h F: migration/ F: scripts/vmstate-static-checker.py -F: tests/functional/test_migration.py +F: tests/functional/*migration.py F: tests/vmstate-static-checker-data/ F: tests/qtest/migration/ F: tests/qtest/migration-* diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 311c6f1806508..c32436d99ad5a 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -80,7 +80,7 @@ tests_generic_bsduser = [ ] tests_aarch64_system_quick = [ - 'migration', + 'aarch64_migration', ] tests_aarch64_system_thorough = [ @@ -110,7 +110,7 @@ tests_aarch64_system_thorough = [ ] tests_alpha_system_quick = [ - 'migration', + 'alpha_migration', ] tests_alpha_system_thorough = [ @@ -119,7 +119,7 @@ tests_alpha_system_thorough = [ ] tests_arm_system_quick = [ - 'migration', + 'arm_migration', ] tests_arm_system_thorough = [ @@ -168,7 +168,7 @@ tests_hppa_system_quick = [ ] tests_i386_system_quick = [ - 'migration', + 'i386_migration', ] tests_i386_system_thorough = [ @@ -228,7 +228,7 @@ tests_or1k_system_thorough = [ ] tests_ppc_system_quick = [ - 'migration', + 'ppc_migration', 'ppc_74xx', ] @@ -245,7 +245,7 @@ tests_ppc_system_thorough = [ ] tests_ppc64_system_quick = [ - 'migration', + 'ppc64_migration', ] tests_ppc64_system_thorough = [ @@ -260,7 +260,7 @@ tests_ppc64_system_thorough = [ ] tests_riscv32_system_quick = [ - 'migration', + 'riscv32_migration', 'riscv_opensbi', ] @@ -269,7 +269,7 @@ tests_riscv32_system_thorough = [ ] tests_riscv64_system_quick = [ - 'migration', + 'riscv64_migration', 'riscv_opensbi', ] @@ -300,7 +300,7 @@ tests_sh4eb_system_thorough = [ ] tests_sparc_system_quick = [ - 'migration', + 'sparc_migration', ] tests_sparc_system_thorough = [ @@ -309,7 +309,7 @@ tests_sparc_system_thorough = [ ] tests_sparc64_system_quick = [ - 'migration', + 'sparc64_migration', ] tests_sparc64_system_thorough = [ @@ -320,7 +320,7 @@ tests_sparc64_system_thorough = [ tests_x86_64_system_quick = [ 'cpu_queries', 'mem_addr_space', - 'migration', + 'x86_64_migration', 'pc_cpu_hotplug_props', 'virtio_version', 'x86_cpu_model_versions', diff --git a/tests/functional/test_migration.py b/tests/functional/migration.py old mode 100755 new mode 100644 similarity index 74% rename from tests/functional/test_migration.py rename to tests/functional/migration.py index c4393c3543469..073955448328d --- a/tests/functional/test_migration.py +++ b/tests/functional/migration.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later # -# Migration test +# Migration test base class # # Copyright (c) 2019 Red Hat, Inc. # @@ -14,7 +14,7 @@ import tempfile import time -from qemu_test import QemuSystemTest, skipIfMissingCommands +from qemu_test import QemuSystemTest, which from qemu_test.ports import Ports @@ -41,24 +41,7 @@ def assert_migration(self, src_vm, dst_vm): self.assertEqual(dst_vm.cmd('query-status')['status'], 'running') self.assertEqual(src_vm.cmd('query-status')['status'],'postmigrate') - def select_machine(self): - target_machine = { - 'aarch64': 'quanta-gsj', - 'alpha': 'clipper', - 'arm': 'npcm750-evb', - 'i386': 'isapc', - 'ppc': 'sam460ex', - 'ppc64': 'mac99', - 'riscv32': 'spike', - 'riscv64': 'virt', - 'sparc': 'SS-4', - 'sparc64': 'sun4u', - 'x86_64': 'microvm', - } - self.set_machine(target_machine[self.arch]) - def do_migrate(self, dest_uri, src_uri=None): - self.select_machine() dest_vm = self.get_vm('-incoming', dest_uri, name="dest-qemu") dest_vm.add_args('-nodefaults') dest_vm.launch() @@ -76,23 +59,21 @@ def _get_free_port(self, ports): self.skipTest('Failed to find a free port') return port - def test_migration_with_tcp_localhost(self): + def migration_with_tcp_localhost(self): with Ports() as ports: dest_uri = 'tcp:localhost:%u' % self._get_free_port(ports) self.do_migrate(dest_uri) - def test_migration_with_unix(self): + def migration_with_unix(self): with tempfile.TemporaryDirectory(prefix='socket_') as socket_path: dest_uri = 'unix:%s/qemu-test.sock' % socket_path self.do_migrate(dest_uri) - @skipIfMissingCommands('ncat') - def test_migration_with_exec(self): + def migration_with_exec(self): + if not which('ncat'): + self.skipTest('ncat is not available') with Ports() as ports: free_port = self._get_free_port(ports) dest_uri = 'exec:ncat -l localhost %u' % free_port src_uri = 'exec:ncat localhost %u' % free_port self.do_migrate(dest_uri, src_uri) - -if __name__ == '__main__': - QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_migration.py b/tests/functional/test_aarch64_migration.py new file mode 100755 index 0000000000000..70267e756d9e9 --- /dev/null +++ b/tests/functional/test_aarch64_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# aarch64 migration test + +from migration import MigrationTest + + +class Aarch64MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('quanta-gsj') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('quanta-gsj') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('quanta-gsj') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_alpha_migration.py b/tests/functional/test_alpha_migration.py new file mode 100755 index 0000000000000..f11b523ec9e7d --- /dev/null +++ b/tests/functional/test_alpha_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Alpha migration test + +from migration import MigrationTest + + +class AlphaMigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('clipper') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('clipper') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('clipper') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_arm_migration.py b/tests/functional/test_arm_migration.py new file mode 100755 index 0000000000000..0aa89f4f61a20 --- /dev/null +++ b/tests/functional/test_arm_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# arm migration test + +from migration import MigrationTest + + +class ArmMigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('npcm750-evb') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('npcm750-evb') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('npcm750-evb') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_i386_migration.py b/tests/functional/test_i386_migration.py new file mode 100755 index 0000000000000..a57f316404476 --- /dev/null +++ b/tests/functional/test_i386_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# i386 migration test + +from migration import MigrationTest + + +class I386MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('isapc') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('isapc') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('isapc') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_ppc64_migration.py b/tests/functional/test_ppc64_migration.py new file mode 100755 index 0000000000000..5dfdaaf709ac7 --- /dev/null +++ b/tests/functional/test_ppc64_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# ppc migration test + +from migration import MigrationTest + + +class PpcMigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('mac99') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('mac99') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('mac99') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_ppc_migration.py b/tests/functional/test_ppc_migration.py new file mode 100755 index 0000000000000..a8692826d35e4 --- /dev/null +++ b/tests/functional/test_ppc_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# ppc migration test + +from migration import MigrationTest + + +class PpcMigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('sam460ex') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('sam460ex') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('sam460ex') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_riscv32_migration.py b/tests/functional/test_riscv32_migration.py new file mode 100755 index 0000000000000..30acbbe69f93d --- /dev/null +++ b/tests/functional/test_riscv32_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# riscv32 migration test + +from migration import MigrationTest + + +class Rv32MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('spike') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('virt') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('spike') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_riscv64_migration.py b/tests/functional/test_riscv64_migration.py new file mode 100755 index 0000000000000..2d613a29ec4f5 --- /dev/null +++ b/tests/functional/test_riscv64_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# riscv64 migration test + +from migration import MigrationTest + + +class Rv64MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('virt') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('spike') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('virt') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_sparc64_migration.py b/tests/functional/test_sparc64_migration.py new file mode 100755 index 0000000000000..a8a6c73c3544d --- /dev/null +++ b/tests/functional/test_sparc64_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Sparc64 migration test + +from migration import MigrationTest + + +class Sparc64MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('sun4u') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('sun4u') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('sun4u') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_sparc_migration.py b/tests/functional/test_sparc_migration.py new file mode 100755 index 0000000000000..dd6d5783b11a5 --- /dev/null +++ b/tests/functional/test_sparc_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Sparc migration test + +from migration import MigrationTest + + +class SparcMigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('SS-4') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('SS-5') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('SS-4') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_x86_64_migration.py b/tests/functional/test_x86_64_migration.py new file mode 100755 index 0000000000000..f3a517ae1f620 --- /dev/null +++ b/tests/functional/test_x86_64_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# x86_64 migration test + +from migration import MigrationTest + + +class X8664MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('microvm') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('microvm') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('microvm') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() From b19c560e4ac730799f45730020fae63e4727a8e0 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:39 +0200 Subject: [PATCH 0271/1794] tests/functional: Rework the multiprocess test to have target-specific files We are going to move the tests for each target into separate subdirectories. The multiprocess test currently contains code for both, x86 and aarch64, so it does not quite fit into this scheme. Rework the test to have a common test class, and target specific files with a target specific class, so that this will fit better into the new scheme. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-3-thuth@redhat.com> --- MAINTAINERS | 2 +- tests/functional/meson.build | 4 +- .../{test_multiprocess.py => multiprocess.py} | 40 +------------------ tests/functional/test_aarch64_multiprocess.py | 31 ++++++++++++++ tests/functional/test_x86_64_multiprocess.py | 31 ++++++++++++++ 5 files changed, 67 insertions(+), 41 deletions(-) rename tests/functional/{test_multiprocess.py => multiprocess.py} (58%) mode change 100755 => 100644 create mode 100755 tests/functional/test_aarch64_multiprocess.py create mode 100755 tests/functional/test_x86_64_multiprocess.py diff --git a/MAINTAINERS b/MAINTAINERS index 56c1fe676907a..adbed9df2fcd6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4286,7 +4286,7 @@ F: hw/remote/vfio-user-obj.c F: include/hw/remote/vfio-user-obj.h F: hw/remote/iommu.c F: include/hw/remote/iommu.h -F: tests/functional/test_multiprocess.py +F: tests/functional/*multiprocess.py VFIO-USER: M: John Levon diff --git a/tests/functional/meson.build b/tests/functional/meson.build index c32436d99ad5a..38ae0d6cd3b48 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -90,6 +90,7 @@ tests_aarch64_system_thorough = [ 'aarch64_hotplug_pci', 'aarch64_imx8mp_evk', 'aarch64_kvm', + 'aarch64_multiprocess', 'aarch64_raspi3', 'aarch64_raspi4', 'aarch64_replay', @@ -106,7 +107,6 @@ tests_aarch64_system_thorough = [ 'aarch64_virt_gpu', 'aarch64_xen', 'aarch64_xlnx_versal', - 'multiprocess', ] tests_alpha_system_quick = [ @@ -332,7 +332,7 @@ tests_x86_64_system_thorough = [ 'acpi_bits', 'intel_iommu', 'linux_initrd', - 'multiprocess', + 'x86_64_multiprocess', 'netdev_ethtool', 'virtio_balloon', 'virtio_gpu', diff --git a/tests/functional/test_multiprocess.py b/tests/functional/multiprocess.py old mode 100755 new mode 100644 similarity index 58% rename from tests/functional/test_multiprocess.py rename to tests/functional/multiprocess.py index 92d5207b0eb20..6a06c1eda19a8 --- a/tests/functional/test_multiprocess.py +++ b/tests/functional/multiprocess.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later # # Test for multiprocess qemu # @@ -9,33 +9,13 @@ import os import socket -from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern +from qemu_test import QemuSystemTest, wait_for_console_pattern from qemu_test import exec_command, exec_command_and_wait_for_pattern class Multiprocess(QemuSystemTest): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - ASSET_KERNEL_X86 = Asset( - ('https://archives.fedoraproject.org/pub/archive/fedora/linux' - '/releases/31/Everything/x86_64/os/images/pxeboot/vmlinuz'), - 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') - - ASSET_INITRD_X86 = Asset( - ('https://archives.fedoraproject.org/pub/archive/fedora/linux' - '/releases/31/Everything/x86_64/os/images/pxeboot/initrd.img'), - '3b6cb5c91a14c42e2f61520f1689264d865e772a1f0069e660a800d31dd61fb9') - - ASSET_KERNEL_AARCH64 = Asset( - ('https://archives.fedoraproject.org/pub/archive/fedora/linux' - '/releases/31/Everything/aarch64/os/images/pxeboot/vmlinuz'), - '3ae07fcafbfc8e4abeb693035a74fe10698faae15e9ccd48882a9167800c1527') - - ASSET_INITRD_AARCH64 = Asset( - ('https://archives.fedoraproject.org/pub/archive/fedora/linux' - '/releases/31/Everything/aarch64/os/images/pxeboot/initrd.img'), - '9fd230cab10b1dafea41cf00150e6669d37051fad133bd618d2130284e16d526') - def do_test(self, kernel_asset, initrd_asset, kernel_command_line, machine_type): """Main test method""" @@ -85,19 +65,3 @@ def do_test(self, kernel_asset, initrd_asset, proxy_sock.close() remote_sock.close() - - def test_multiprocess(self): - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE - if self.arch == 'x86_64': - kernel_command_line += 'console=ttyS0 rdinit=/bin/bash' - self.do_test(self.ASSET_KERNEL_X86, self.ASSET_INITRD_X86, - kernel_command_line, 'pc') - elif self.arch == 'aarch64': - kernel_command_line += 'rdinit=/bin/bash console=ttyAMA0' - self.do_test(self.ASSET_KERNEL_AARCH64, self.ASSET_INITRD_AARCH64, - kernel_command_line, 'virt,gic-version=3') - else: - assert False - -if __name__ == '__main__': - QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_multiprocess.py b/tests/functional/test_aarch64_multiprocess.py new file mode 100755 index 0000000000000..1c6e45ecb6764 --- /dev/null +++ b/tests/functional/test_aarch64_multiprocess.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Test for multiprocess qemu on aarch64 + +from multiprocess import Multiprocess +from qemu_test import Asset + + +class Aarch64Multiprocess(Multiprocess): + + ASSET_KERNEL_AARCH64 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/31/Everything/aarch64/os/images/pxeboot/vmlinuz'), + '3ae07fcafbfc8e4abeb693035a74fe10698faae15e9ccd48882a9167800c1527') + + ASSET_INITRD_AARCH64 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/31/Everything/aarch64/os/images/pxeboot/initrd.img'), + '9fd230cab10b1dafea41cf00150e6669d37051fad133bd618d2130284e16d526') + + def test_multiprocess(self): + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'rdinit=/bin/bash console=ttyAMA0') + self.do_test(self.ASSET_KERNEL_AARCH64, self.ASSET_INITRD_AARCH64, + kernel_command_line, 'virt,gic-version=3') + + +if __name__ == '__main__': + Multiprocess.main() diff --git a/tests/functional/test_x86_64_multiprocess.py b/tests/functional/test_x86_64_multiprocess.py new file mode 100755 index 0000000000000..756629dd446bb --- /dev/null +++ b/tests/functional/test_x86_64_multiprocess.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Test for multiprocess qemu on x86 + +from multiprocess import Multiprocess +from qemu_test import Asset + + +class X86Multiprocess(Multiprocess): + + ASSET_KERNEL_X86 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/31/Everything/x86_64/os/images/pxeboot/vmlinuz'), + 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') + + ASSET_INITRD_X86 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/31/Everything/x86_64/os/images/pxeboot/initrd.img'), + '3b6cb5c91a14c42e2f61520f1689264d865e772a1f0069e660a800d31dd61fb9') + + def test_multiprocess(self): + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 rdinit=/bin/bash') + self.do_test(self.ASSET_KERNEL_X86, self.ASSET_INITRD_X86, + kernel_command_line, 'pc') + + +if __name__ == '__main__': + Multiprocess.main() From b8103f56d97c2e6554af2c365b366989005a666c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:40 +0200 Subject: [PATCH 0272/1794] tests/functional/meson.build: Split timeout settings by target We are going to move these settings into target-specific subfolders. As a first step, split the big test_timeouts array up into individual ones. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-4-thuth@redhat.com> --- tests/functional/meson.build | 50 +++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 38ae0d6cd3b48..356aad12deebe 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -10,7 +10,7 @@ if get_option('tcg_interpreter') endif # Timeouts for individual tests that can be slow e.g. with debugging enabled -test_timeouts = { +test_aarch64_timeouts = { 'aarch64_aspeed_ast2700' : 600, 'aarch64_aspeed_ast2700fc' : 600, 'aarch64_device_passthrough' : 720, @@ -25,7 +25,9 @@ test_timeouts = { 'aarch64_tuxrun' : 240, 'aarch64_virt' : 360, 'aarch64_virt_gpu' : 480, - 'acpi_bits' : 420, +} + +test_arm_timeouts = { 'arm_aspeed_palmetto' : 120, 'arm_aspeed_romulus' : 120, 'arm_aspeed_witherspoon' : 120, @@ -44,24 +46,55 @@ test_timeouts = { 'arm_replay' : 240, 'arm_tuxrun' : 240, 'arm_sx1' : 360, - 'intel_iommu': 300, +} + +test_mips_timeouts = { 'mips_malta' : 480, +} + +test_mipsel_timeouts = { 'mipsel_malta' : 420, 'mipsel_replay' : 480, +} + +test_mips64_timeouts = { 'mips64_malta' : 240, +} + +test_mips64el_timeouts = { 'mips64el_malta' : 420, 'mips64el_replay' : 180, - 'netdev_ethtool' : 180, +} + +test_ppc_timeouts = { 'ppc_40p' : 240, +} + +test_ppc64_timeouts = { 'ppc64_hv' : 1000, 'ppc64_powernv' : 480, 'ppc64_pseries' : 480, 'ppc64_replay' : 210, 'ppc64_tuxrun' : 420, 'ppc64_mac99' : 120, +} + +test_riscv64_timeouts = { 'riscv64_tuxrun' : 120, +} + +test_s390x_timeouts = { 's390x_ccw_virtio' : 420, +} + +test_sh4_timeouts = { 'sh4_tuxrun' : 240, +} + +test_x86_64_timeouts = { + 'acpi_bits' : 420, + 'intel_iommu': 300, + 'netdev_ethtool' : 180, 'virtio_balloon': 120, 'x86_64_kvm_xen' : 180, 'x86_64_replay' : 480, @@ -404,6 +437,11 @@ foreach speed : ['quick', 'thorough'] build_by_default: false, env: test_precache_env) precache_all += precache + if is_variable('test_' + target_base + '_timeouts') + time_out = get_variable('test_' + target_base + '_timeouts').get(test, 90) + else + time_out = 90 + endif # Ideally we would add 'precache' to 'depends' here, such that # 'build_by_default: false' lets the pre-caching automatically @@ -419,8 +457,8 @@ foreach speed : ['quick', 'thorough'] env: test_env, args: [testpath], protocol: 'tap', - timeout: test_timeouts.get(test, 90), - priority: test_timeouts.get(test, 90), + timeout: time_out, + priority: time_out, suite: suites) endforeach endforeach From 9c9efb1e36ddb2e636c7f6aafda49cddaee88a0c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:41 +0200 Subject: [PATCH 0273/1794] tests/functional/meson.build: Allow tests to reside in subfolders We are going to move target-specific tests to subfolders that are named after the target (and generic tests will be put into a "generic" folder), so prepare the meson.build file to allow such locations, too. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-5-thuth@redhat.com> --- tests/functional/meson.build | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 356aad12deebe..8c24ac1cc2da5 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -423,7 +423,13 @@ foreach speed : ['quick', 'thorough'] foreach test : target_tests testname = '@0@-@1@'.format(target_base, test) - testfile = 'test_' + test + '.py' + if fs.exists('test_' + test + '.py') + testfile = 'test_' + test + '.py' + elif fs.exists('generic' / 'test_' + test + '.py') + testfile = 'generic' / 'test_' + test + '.py' + else + testfile = target_base / 'test_' + test + '.py' + endif testpath = meson.current_source_dir() / testfile teststamp = testname + '.tstamp' test_precache_env = environment() From 96ced85b0c5664426518ad43f0e423092fbee933 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:42 +0200 Subject: [PATCH 0274/1794] tests/functional: Move aarch64 tests into architecture specific folder The tests/functional folder has become quite crowded already, some restructuring would be helpful here. Thus move the aarch64 tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-6-thuth@redhat.com> --- MAINTAINERS | 24 +++++----- tests/functional/aarch64/meson.build | 48 +++++++++++++++++++ .../test_aspeed_ast2700.py} | 0 .../test_aspeed_ast2700fc.py} | 0 .../test_device_passthrough.py} | 0 .../test_hotplug_pci.py} | 0 .../test_imx8mp_evk.py} | 0 .../test_kvm.py} | 0 .../test_migration.py} | 0 .../test_multiprocess.py} | 0 .../test_raspi3.py} | 0 .../test_raspi4.py} | 0 .../test_replay.py} | 0 .../test_reverse_debug.py} | 0 .../test_rme_sbsaref.py} | 2 +- .../test_rme_virt.py} | 0 .../test_sbsaref.py} | 0 .../test_sbsaref_alpine.py} | 2 +- .../test_sbsaref_freebsd.py} | 2 +- .../test_smmu.py} | 0 .../test_tcg_plugins.py} | 0 .../test_tuxrun.py} | 0 .../test_virt.py} | 0 .../test_virt_gpu.py} | 0 .../test_xen.py} | 0 .../test_xlnx_versal.py} | 0 tests/functional/meson.build | 48 +------------------ 27 files changed, 64 insertions(+), 62 deletions(-) create mode 100644 tests/functional/aarch64/meson.build rename tests/functional/{test_aarch64_aspeed_ast2700.py => aarch64/test_aspeed_ast2700.py} (100%) rename tests/functional/{test_aarch64_aspeed_ast2700fc.py => aarch64/test_aspeed_ast2700fc.py} (100%) rename tests/functional/{test_aarch64_device_passthrough.py => aarch64/test_device_passthrough.py} (100%) rename tests/functional/{test_aarch64_hotplug_pci.py => aarch64/test_hotplug_pci.py} (100%) rename tests/functional/{test_aarch64_imx8mp_evk.py => aarch64/test_imx8mp_evk.py} (100%) rename tests/functional/{test_aarch64_kvm.py => aarch64/test_kvm.py} (100%) rename tests/functional/{test_aarch64_migration.py => aarch64/test_migration.py} (100%) rename tests/functional/{test_aarch64_multiprocess.py => aarch64/test_multiprocess.py} (100%) rename tests/functional/{test_aarch64_raspi3.py => aarch64/test_raspi3.py} (100%) rename tests/functional/{test_aarch64_raspi4.py => aarch64/test_raspi4.py} (100%) rename tests/functional/{test_aarch64_replay.py => aarch64/test_replay.py} (100%) rename tests/functional/{test_aarch64_reverse_debug.py => aarch64/test_reverse_debug.py} (100%) rename tests/functional/{test_aarch64_rme_sbsaref.py => aarch64/test_rme_sbsaref.py} (98%) rename tests/functional/{test_aarch64_rme_virt.py => aarch64/test_rme_virt.py} (100%) rename tests/functional/{test_aarch64_sbsaref.py => aarch64/test_sbsaref.py} (100%) rename tests/functional/{test_aarch64_sbsaref_alpine.py => aarch64/test_sbsaref_alpine.py} (97%) rename tests/functional/{test_aarch64_sbsaref_freebsd.py => aarch64/test_sbsaref_freebsd.py} (97%) rename tests/functional/{test_aarch64_smmu.py => aarch64/test_smmu.py} (100%) rename tests/functional/{test_aarch64_tcg_plugins.py => aarch64/test_tcg_plugins.py} (100%) rename tests/functional/{test_aarch64_tuxrun.py => aarch64/test_tuxrun.py} (100%) rename tests/functional/{test_aarch64_virt.py => aarch64/test_virt.py} (100%) rename tests/functional/{test_aarch64_virt_gpu.py => aarch64/test_virt_gpu.py} (100%) rename tests/functional/{test_aarch64_xen.py => aarch64/test_xen.py} (100%) rename tests/functional/{test_aarch64_xlnx_versal.py => aarch64/test_xlnx_versal.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index adbed9df2fcd6..a2a5ccea7b6e7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -212,7 +212,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/smmu* F: include/hw/arm/smmu* -F: tests/functional/test_aarch64_smmu.py +F: tests/functional/aarch64/test_smmu.py AVR TCG CPUs M: Michael Rolnik @@ -874,7 +874,7 @@ F: include/hw/arm/fsl-imx8mp.h F: include/hw/misc/imx8mp_*.h F: include/hw/pci-host/fsl_imx8m_phy.h F: docs/system/arm/imx8mp-evk.rst -F: tests/functional/test_aarch64_imx8mp_evk.py +F: tests/functional/aarch64/test_imx8mp_evk.py F: tests/qtest/rs5c372-test.c MPS2 / MPS3 @@ -952,8 +952,7 @@ F: include/hw/arm/rasp* F: include/hw/*/bcm283* F: docs/system/arm/raspi.rst F: tests/functional/test_arm_raspi2.py -F: tests/functional/test_aarch64_raspi3.py -F: tests/functional/test_aarch64_raspi4.py +F: tests/functional/aarch64/test_raspi*.py Real View M: Peter Maydell @@ -993,7 +992,7 @@ F: hw/misc/sbsa_ec.c F: hw/watchdog/sbsa_gwdt.c F: include/hw/watchdog/sbsa_gwdt.h F: docs/system/arm/sbsa.rst -F: tests/functional/test_aarch64_*sbsaref*.py +F: tests/functional/aarch64/test_*sbsaref*.py Sharp SL-5500 (Collie) PDA M: Peter Maydell @@ -1063,8 +1062,8 @@ S: Maintained F: hw/arm/virt* F: include/hw/arm/virt.h F: docs/system/arm/virt.rst -F: tests/functional/test_aarch64_*virt*.py -F: tests/functional/test_aarch64_tuxrun.py +F: tests/functional/aarch64/test_*virt*.py +F: tests/functional/aarch64/test_tuxrun.py F: tests/functional/test_arm_tuxrun.py F: tests/functional/test_arm_virt.py @@ -1096,7 +1095,7 @@ F: hw/display/dpcd.c F: include/hw/display/dpcd.h F: docs/system/arm/xlnx-versal-virt.rst F: docs/system/arm/xlnx-zcu102.rst -F: tests/functional/test_aarch64_xlnx_versal.py +F: tests/functional/aarch64/test_xlnx_versal.py Xilinx Versal OSPI M: Francisco Iglesias @@ -2109,7 +2108,7 @@ ARM PCI Hotplug M: Gustavo Romero L: qemu-arm@nongnu.org S: Supported -F: tests/functional/test_aarch64_hotplug_pci.py +F: tests/functional/aarch64/test_hotplug_pci.py ACPI/SMBIOS M: Michael S. Tsirkin @@ -2263,6 +2262,7 @@ F: util/vfio-helpers.c F: include/hw/vfio/ F: docs/devel/migration/vfio.rst F: qapi/vfio.json +F: tests/functional/aarch64/test_device_passthrough.py vfio-igd M: Alex Williamson @@ -2638,7 +2638,7 @@ M: Alex Bennée S: Maintained F: hw/core/guest-loader.c F: docs/system/guest-loader.rst -F: tests/functional/test_aarch64_xen.py +F: tests/functional/aarch64/test_xen.py Intel Hexadecimal Object File Loader M: Su Hang @@ -2707,7 +2707,7 @@ F: hw/display/virtio-gpu* F: hw/display/virtio-vga.* F: include/hw/virtio/virtio-gpu.h F: docs/system/devices/virtio-gpu.rst -F: tests/functional/test_aarch64_virt_gpu.py +F: tests/functional/aarch64/test_virt_gpu.py vhost-user-blk M: Raphael Norwitz @@ -3933,7 +3933,7 @@ S: Maintained F: docs/devel/tcg-plugins.rst F: plugins/ F: tests/tcg/plugins/ -F: tests/functional/test_aarch64_tcg_plugins.py +F: tests/functional/aarch64/test_tcg_plugins.py F: contrib/plugins/ F: scripts/qemu-plugin-symbols.py diff --git a/tests/functional/aarch64/meson.build b/tests/functional/aarch64/meson.build new file mode 100644 index 0000000000000..04846c6eb1802 --- /dev/null +++ b/tests/functional/aarch64/meson.build @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_aarch64_timeouts = { + 'aspeed_ast2700' : 600, + 'aspeed_ast2700fc' : 600, + 'device_passthrough' : 720, + 'imx8mp_evk' : 240, + 'raspi4' : 480, + 'reverse_debug' : 180, + 'rme_virt' : 1200, + 'rme_sbsaref' : 1200, + 'sbsaref_alpine' : 1200, + 'sbsaref_freebsd' : 720, + 'smmu' : 720, + 'tuxrun' : 240, + 'virt' : 360, + 'virt_gpu' : 480, +} + +tests_aarch64_system_quick = [ + 'migration', +] + +tests_aarch64_system_thorough = [ + 'aspeed_ast2700', + 'aspeed_ast2700fc', + 'device_passthrough', + 'hotplug_pci', + 'imx8mp_evk', + 'kvm', + 'multiprocess', + 'raspi3', + 'raspi4', + 'replay', + 'reverse_debug', + 'rme_virt', + 'rme_sbsaref', + 'sbsaref', + 'sbsaref_alpine', + 'sbsaref_freebsd', + 'smmu', + 'tcg_plugins', + 'tuxrun', + 'virt', + 'virt_gpu', + 'xen', + 'xlnx_versal', +] diff --git a/tests/functional/test_aarch64_aspeed_ast2700.py b/tests/functional/aarch64/test_aspeed_ast2700.py similarity index 100% rename from tests/functional/test_aarch64_aspeed_ast2700.py rename to tests/functional/aarch64/test_aspeed_ast2700.py diff --git a/tests/functional/test_aarch64_aspeed_ast2700fc.py b/tests/functional/aarch64/test_aspeed_ast2700fc.py similarity index 100% rename from tests/functional/test_aarch64_aspeed_ast2700fc.py rename to tests/functional/aarch64/test_aspeed_ast2700fc.py diff --git a/tests/functional/test_aarch64_device_passthrough.py b/tests/functional/aarch64/test_device_passthrough.py similarity index 100% rename from tests/functional/test_aarch64_device_passthrough.py rename to tests/functional/aarch64/test_device_passthrough.py diff --git a/tests/functional/test_aarch64_hotplug_pci.py b/tests/functional/aarch64/test_hotplug_pci.py similarity index 100% rename from tests/functional/test_aarch64_hotplug_pci.py rename to tests/functional/aarch64/test_hotplug_pci.py diff --git a/tests/functional/test_aarch64_imx8mp_evk.py b/tests/functional/aarch64/test_imx8mp_evk.py similarity index 100% rename from tests/functional/test_aarch64_imx8mp_evk.py rename to tests/functional/aarch64/test_imx8mp_evk.py diff --git a/tests/functional/test_aarch64_kvm.py b/tests/functional/aarch64/test_kvm.py similarity index 100% rename from tests/functional/test_aarch64_kvm.py rename to tests/functional/aarch64/test_kvm.py diff --git a/tests/functional/test_aarch64_migration.py b/tests/functional/aarch64/test_migration.py similarity index 100% rename from tests/functional/test_aarch64_migration.py rename to tests/functional/aarch64/test_migration.py diff --git a/tests/functional/test_aarch64_multiprocess.py b/tests/functional/aarch64/test_multiprocess.py similarity index 100% rename from tests/functional/test_aarch64_multiprocess.py rename to tests/functional/aarch64/test_multiprocess.py diff --git a/tests/functional/test_aarch64_raspi3.py b/tests/functional/aarch64/test_raspi3.py similarity index 100% rename from tests/functional/test_aarch64_raspi3.py rename to tests/functional/aarch64/test_raspi3.py diff --git a/tests/functional/test_aarch64_raspi4.py b/tests/functional/aarch64/test_raspi4.py similarity index 100% rename from tests/functional/test_aarch64_raspi4.py rename to tests/functional/aarch64/test_raspi4.py diff --git a/tests/functional/test_aarch64_replay.py b/tests/functional/aarch64/test_replay.py similarity index 100% rename from tests/functional/test_aarch64_replay.py rename to tests/functional/aarch64/test_replay.py diff --git a/tests/functional/test_aarch64_reverse_debug.py b/tests/functional/aarch64/test_reverse_debug.py similarity index 100% rename from tests/functional/test_aarch64_reverse_debug.py rename to tests/functional/aarch64/test_reverse_debug.py diff --git a/tests/functional/test_aarch64_rme_sbsaref.py b/tests/functional/aarch64/test_rme_sbsaref.py similarity index 98% rename from tests/functional/test_aarch64_rme_sbsaref.py rename to tests/functional/aarch64/test_rme_sbsaref.py index 746770e776df9..100f1c7738bcf 100755 --- a/tests/functional/test_aarch64_rme_sbsaref.py +++ b/tests/functional/aarch64/test_rme_sbsaref.py @@ -13,7 +13,7 @@ from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern from qemu_test import exec_command_and_wait_for_pattern -from test_aarch64_rme_virt import test_realms_guest +from test_rme_virt import test_realms_guest class Aarch64RMESbsaRefMachine(QemuSystemTest): diff --git a/tests/functional/test_aarch64_rme_virt.py b/tests/functional/aarch64/test_rme_virt.py similarity index 100% rename from tests/functional/test_aarch64_rme_virt.py rename to tests/functional/aarch64/test_rme_virt.py diff --git a/tests/functional/test_aarch64_sbsaref.py b/tests/functional/aarch64/test_sbsaref.py similarity index 100% rename from tests/functional/test_aarch64_sbsaref.py rename to tests/functional/aarch64/test_sbsaref.py diff --git a/tests/functional/test_aarch64_sbsaref_alpine.py b/tests/functional/aarch64/test_sbsaref_alpine.py similarity index 97% rename from tests/functional/test_aarch64_sbsaref_alpine.py rename to tests/functional/aarch64/test_sbsaref_alpine.py index 877699938319a..abb8f5114bd59 100755 --- a/tests/functional/test_aarch64_sbsaref_alpine.py +++ b/tests/functional/aarch64/test_sbsaref_alpine.py @@ -12,7 +12,7 @@ from qemu_test import QemuSystemTest, Asset, skipSlowTest from qemu_test import wait_for_console_pattern -from test_aarch64_sbsaref import fetch_firmware +from test_sbsaref import fetch_firmware class Aarch64SbsarefAlpine(QemuSystemTest): diff --git a/tests/functional/test_aarch64_sbsaref_freebsd.py b/tests/functional/aarch64/test_sbsaref_freebsd.py similarity index 97% rename from tests/functional/test_aarch64_sbsaref_freebsd.py rename to tests/functional/aarch64/test_sbsaref_freebsd.py index 7ef016fba6286..3b942f7795cde 100755 --- a/tests/functional/test_aarch64_sbsaref_freebsd.py +++ b/tests/functional/aarch64/test_sbsaref_freebsd.py @@ -12,7 +12,7 @@ from qemu_test import QemuSystemTest, Asset, skipSlowTest from qemu_test import wait_for_console_pattern -from test_aarch64_sbsaref import fetch_firmware +from test_sbsaref import fetch_firmware class Aarch64SbsarefFreeBSD(QemuSystemTest): diff --git a/tests/functional/test_aarch64_smmu.py b/tests/functional/aarch64/test_smmu.py similarity index 100% rename from tests/functional/test_aarch64_smmu.py rename to tests/functional/aarch64/test_smmu.py diff --git a/tests/functional/test_aarch64_tcg_plugins.py b/tests/functional/aarch64/test_tcg_plugins.py similarity index 100% rename from tests/functional/test_aarch64_tcg_plugins.py rename to tests/functional/aarch64/test_tcg_plugins.py diff --git a/tests/functional/test_aarch64_tuxrun.py b/tests/functional/aarch64/test_tuxrun.py similarity index 100% rename from tests/functional/test_aarch64_tuxrun.py rename to tests/functional/aarch64/test_tuxrun.py diff --git a/tests/functional/test_aarch64_virt.py b/tests/functional/aarch64/test_virt.py similarity index 100% rename from tests/functional/test_aarch64_virt.py rename to tests/functional/aarch64/test_virt.py diff --git a/tests/functional/test_aarch64_virt_gpu.py b/tests/functional/aarch64/test_virt_gpu.py similarity index 100% rename from tests/functional/test_aarch64_virt_gpu.py rename to tests/functional/aarch64/test_virt_gpu.py diff --git a/tests/functional/test_aarch64_xen.py b/tests/functional/aarch64/test_xen.py similarity index 100% rename from tests/functional/test_aarch64_xen.py rename to tests/functional/aarch64/test_xen.py diff --git a/tests/functional/test_aarch64_xlnx_versal.py b/tests/functional/aarch64/test_xlnx_versal.py similarity index 100% rename from tests/functional/test_aarch64_xlnx_versal.py rename to tests/functional/aarch64/test_xlnx_versal.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 8c24ac1cc2da5..9cb6325360fd4 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -9,23 +9,7 @@ if get_option('tcg_interpreter') subdir_done() endif -# Timeouts for individual tests that can be slow e.g. with debugging enabled -test_aarch64_timeouts = { - 'aarch64_aspeed_ast2700' : 600, - 'aarch64_aspeed_ast2700fc' : 600, - 'aarch64_device_passthrough' : 720, - 'aarch64_imx8mp_evk' : 240, - 'aarch64_raspi4' : 480, - 'aarch64_reverse_debug' : 180, - 'aarch64_rme_virt' : 1200, - 'aarch64_rme_sbsaref' : 1200, - 'aarch64_sbsaref_alpine' : 1200, - 'aarch64_sbsaref_freebsd' : 720, - 'aarch64_smmu' : 720, - 'aarch64_tuxrun' : 240, - 'aarch64_virt' : 360, - 'aarch64_virt_gpu' : 480, -} +subdir('aarch64') test_arm_timeouts = { 'arm_aspeed_palmetto' : 120, @@ -112,36 +96,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_aarch64_system_quick = [ - 'aarch64_migration', -] - -tests_aarch64_system_thorough = [ - 'aarch64_aspeed_ast2700', - 'aarch64_aspeed_ast2700fc', - 'aarch64_device_passthrough', - 'aarch64_hotplug_pci', - 'aarch64_imx8mp_evk', - 'aarch64_kvm', - 'aarch64_multiprocess', - 'aarch64_raspi3', - 'aarch64_raspi4', - 'aarch64_replay', - 'aarch64_reverse_debug', - 'aarch64_rme_virt', - 'aarch64_rme_sbsaref', - 'aarch64_sbsaref', - 'aarch64_sbsaref_alpine', - 'aarch64_sbsaref_freebsd', - 'aarch64_smmu', - 'aarch64_tcg_plugins', - 'aarch64_tuxrun', - 'aarch64_virt', - 'aarch64_virt_gpu', - 'aarch64_xen', - 'aarch64_xlnx_versal', -] - tests_alpha_system_quick = [ 'alpha_migration', ] From e534eee5ba6aefd905c16bcb714d1ff8156a0c45 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:43 +0200 Subject: [PATCH 0275/1794] tests/functional: Move alpha tests into architecture specific folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded already, some restructuring would be helpful here. Thus move the alpha tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-7-thuth@redhat.com> --- MAINTAINERS | 3 ++- tests/functional/alpha/meson.build | 10 ++++++++++ .../{test_alpha_clipper.py => alpha/test_clipper.py} | 0 .../test_migration.py} | 0 .../{test_alpha_replay.py => alpha/test_replay.py} | 0 tests/functional/meson.build | 10 +--------- 6 files changed, 13 insertions(+), 10 deletions(-) create mode 100644 tests/functional/alpha/meson.build rename tests/functional/{test_alpha_clipper.py => alpha/test_clipper.py} (100%) rename tests/functional/{test_alpha_migration.py => alpha/test_migration.py} (100%) rename tests/functional/{test_alpha_replay.py => alpha/test_replay.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index a2a5ccea7b6e7..8115aae618300 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -189,6 +189,7 @@ M: Richard Henderson S: Maintained F: target/alpha/ F: tests/tcg/alpha/ +F: tests/functional/alpha/ F: disas/alpha.c ARM TCG CPUs @@ -656,7 +657,7 @@ S: Maintained F: hw/alpha/ F: hw/isa/smc37c669-superio.c F: tests/tcg/alpha/system/ -F: tests/functional/test_alpha_clipper.py +F: tests/functional/alpha/test_clipper.py ARM Machines ------------ diff --git a/tests/functional/alpha/meson.build b/tests/functional/alpha/meson.build new file mode 100644 index 0000000000000..26a5b3f2e4b9c --- /dev/null +++ b/tests/functional/alpha/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_alpha_system_quick = [ + 'migration', +] + +tests_alpha_system_thorough = [ + 'clipper', + 'replay', +] diff --git a/tests/functional/test_alpha_clipper.py b/tests/functional/alpha/test_clipper.py similarity index 100% rename from tests/functional/test_alpha_clipper.py rename to tests/functional/alpha/test_clipper.py diff --git a/tests/functional/test_alpha_migration.py b/tests/functional/alpha/test_migration.py similarity index 100% rename from tests/functional/test_alpha_migration.py rename to tests/functional/alpha/test_migration.py diff --git a/tests/functional/test_alpha_replay.py b/tests/functional/alpha/test_replay.py similarity index 100% rename from tests/functional/test_alpha_replay.py rename to tests/functional/alpha/test_replay.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 9cb6325360fd4..a7f8c88a078fd 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -10,6 +10,7 @@ if get_option('tcg_interpreter') endif subdir('aarch64') +subdir('alpha') test_arm_timeouts = { 'arm_aspeed_palmetto' : 120, @@ -96,15 +97,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_alpha_system_quick = [ - 'alpha_migration', -] - -tests_alpha_system_thorough = [ - 'alpha_clipper', - 'alpha_replay', -] - tests_arm_system_quick = [ 'arm_migration', ] From 8de42cb748287d8dcc521d057b107f32d98e5edd Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:44 +0200 Subject: [PATCH 0276/1794] tests/functional: Move arm tests into architecture specific folder The tests/functional folder has become quite crowded, thus move the arm tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-8-thuth@redhat.com> --- MAINTAINERS | 38 ++++++------ tests/functional/arm/meson.build | 62 +++++++++++++++++++ .../test_aspeed_ast1030.py} | 0 .../test_aspeed_ast2500.py} | 0 .../test_aspeed_ast2600.py} | 0 .../test_aspeed_bletchley.py} | 0 .../test_aspeed_catalina.py} | 0 .../test_aspeed_gb200nvl_bmc.py} | 0 .../test_aspeed_palmetto.py} | 0 .../test_aspeed_rainier.py} | 0 .../test_aspeed_romulus.py} | 0 .../test_aspeed_witherspoon.py} | 0 .../{test_arm_bflt.py => arm/test_bflt.py} | 0 .../test_bpim2u.py} | 0 .../test_canona1100.py} | 0 .../test_collie.py} | 0 .../test_cubieboard.py} | 0 .../test_emcraft_sf2.py} | 0 .../test_integratorcp.py} | 0 .../test_max78000fthr.py} | 0 .../test_microbit.py} | 0 .../test_migration.py} | 0 .../test_orangepi.py} | 0 .../test_quanta_gsj.py} | 0 .../test_raspi2.py} | 0 .../test_realview.py} | 0 .../test_replay.py} | 0 .../test_smdkc210.py} | 0 .../test_stellaris.py} | 0 .../{test_arm_sx1.py => arm/test_sx1.py} | 0 .../test_tuxrun.py} | 0 .../test_vexpress.py} | 0 .../{test_arm_virt.py => arm/test_virt.py} | 0 tests/functional/meson.build | 62 +------------------ 34 files changed, 83 insertions(+), 79 deletions(-) create mode 100644 tests/functional/arm/meson.build rename tests/functional/{test_arm_aspeed_ast1030.py => arm/test_aspeed_ast1030.py} (100%) rename tests/functional/{test_arm_aspeed_ast2500.py => arm/test_aspeed_ast2500.py} (100%) rename tests/functional/{test_arm_aspeed_ast2600.py => arm/test_aspeed_ast2600.py} (100%) rename tests/functional/{test_arm_aspeed_bletchley.py => arm/test_aspeed_bletchley.py} (100%) mode change 100644 => 100755 rename tests/functional/{test_arm_aspeed_catalina.py => arm/test_aspeed_catalina.py} (100%) rename tests/functional/{test_arm_aspeed_gb200nvl_bmc.py => arm/test_aspeed_gb200nvl_bmc.py} (100%) mode change 100644 => 100755 rename tests/functional/{test_arm_aspeed_palmetto.py => arm/test_aspeed_palmetto.py} (100%) rename tests/functional/{test_arm_aspeed_rainier.py => arm/test_aspeed_rainier.py} (100%) rename tests/functional/{test_arm_aspeed_romulus.py => arm/test_aspeed_romulus.py} (100%) rename tests/functional/{test_arm_aspeed_witherspoon.py => arm/test_aspeed_witherspoon.py} (100%) mode change 100644 => 100755 rename tests/functional/{test_arm_bflt.py => arm/test_bflt.py} (100%) rename tests/functional/{test_arm_bpim2u.py => arm/test_bpim2u.py} (100%) rename tests/functional/{test_arm_canona1100.py => arm/test_canona1100.py} (100%) rename tests/functional/{test_arm_collie.py => arm/test_collie.py} (100%) rename tests/functional/{test_arm_cubieboard.py => arm/test_cubieboard.py} (100%) rename tests/functional/{test_arm_emcraft_sf2.py => arm/test_emcraft_sf2.py} (100%) rename tests/functional/{test_arm_integratorcp.py => arm/test_integratorcp.py} (100%) rename tests/functional/{test_arm_max78000fthr.py => arm/test_max78000fthr.py} (100%) rename tests/functional/{test_arm_microbit.py => arm/test_microbit.py} (100%) rename tests/functional/{test_arm_migration.py => arm/test_migration.py} (100%) rename tests/functional/{test_arm_orangepi.py => arm/test_orangepi.py} (100%) rename tests/functional/{test_arm_quanta_gsj.py => arm/test_quanta_gsj.py} (100%) rename tests/functional/{test_arm_raspi2.py => arm/test_raspi2.py} (100%) rename tests/functional/{test_arm_realview.py => arm/test_realview.py} (100%) rename tests/functional/{test_arm_replay.py => arm/test_replay.py} (100%) rename tests/functional/{test_arm_smdkc210.py => arm/test_smdkc210.py} (100%) rename tests/functional/{test_arm_stellaris.py => arm/test_stellaris.py} (100%) rename tests/functional/{test_arm_sx1.py => arm/test_sx1.py} (100%) rename tests/functional/{test_arm_tuxrun.py => arm/test_tuxrun.py} (100%) rename tests/functional/{test_arm_vexpress.py => arm/test_vexpress.py} (100%) rename tests/functional/{test_arm_virt.py => arm/test_virt.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 8115aae618300..1eb964feca49a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -444,6 +444,7 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: target/arm/kvm.c +F: tests/functional/aarch64/test_kvm.py MIPS KVM CPUs M: Huacai Chen @@ -673,7 +674,7 @@ F: include/hw/*/allwinner* F: hw/arm/cubieboard.c F: docs/system/arm/cubieboard.rst F: hw/misc/axp209.c -F: tests/functional/test_arm_cubieboard.py +F: tests/functional/arm/test_cubieboard.py Allwinner-h3 M: Niek Linnenbank @@ -683,7 +684,7 @@ F: hw/*/allwinner-h3* F: include/hw/*/allwinner-h3* F: hw/arm/orangepi.c F: docs/system/arm/orangepi.rst -F: tests/functional/test_arm_orangepi.py +F: tests/functional/arm/test_orangepi.py ARM PrimeCell and CMSDK devices M: Peter Maydell @@ -753,7 +754,7 @@ F: docs/system/arm/bananapi_m2u.rst F: hw/*/allwinner-r40*.c F: hw/arm/bananapi_m2u.c F: include/hw/*/allwinner-r40*.h -F: tests/functional/test_arm_bpim2u.py +F: tests/functional/arm/test_bpim2u.py B-L475E-IOT01A IoT Node M: Samuel Tardieu @@ -771,7 +772,7 @@ S: Odd Fixes F: hw/*/exynos* F: include/hw/*/exynos* F: docs/system/arm/exynos.rst -F: tests/functional/test_arm_smdkc210.py +F: tests/functional/arm/test_smdkc210.py Calxeda Highbank M: Rob Herring @@ -790,7 +791,7 @@ S: Odd Fixes F: include/hw/arm/digic.h F: hw/*/digic* F: include/hw/*/digic* -F: tests/functional/test_arm_canona1100.py +F: tests/functional/arm/test_canona1100.py F: docs/system/arm/digic.rst Goldfish RTC @@ -833,7 +834,7 @@ S: Odd Fixes F: hw/arm/integratorcp.c F: hw/misc/arm_integrator_debug.c F: include/hw/misc/arm_integrator_debug.h -F: tests/functional/test_arm_integratorcp.py +F: tests/functional/arm/test_integratorcp.py F: docs/system/arm/integratorcp.rst MCIMX6UL EVK / i.MX6ul @@ -939,7 +940,7 @@ F: pc-bios/npcm7xx_bootrom.bin F: pc-bios/npcm8xx_bootrom.bin F: roms/vbootrom F: docs/system/arm/nuvoton.rst -F: tests/functional/test_arm_quanta_gsj.py +F: tests/functional/arm/test_quanta_gsj.py Raspberry Pi M: Peter Maydell @@ -952,7 +953,7 @@ F: hw/*/bcm283* F: include/hw/arm/rasp* F: include/hw/*/bcm283* F: docs/system/arm/raspi.rst -F: tests/functional/test_arm_raspi2.py +F: tests/functional/arm/test_raspi2.py F: tests/functional/aarch64/test_raspi*.py Real View @@ -964,7 +965,7 @@ F: hw/cpu/realview_mpcore.c F: hw/intc/realview_gic.c F: include/hw/intc/realview_gic.h F: docs/system/arm/realview.rst -F: tests/functional/test_arm_realview.py +F: tests/functional/arm/test_realview.py SABRELITE / i.MX6 M: Peter Maydell @@ -1004,7 +1005,7 @@ F: hw/arm/strongarm* F: hw/gpio/zaurus.c F: include/hw/arm/sharpsl.h F: docs/system/arm/collie.rst -F: tests/functional/test_arm_collie.py +F: tests/functional/arm/test_collie.py Stellaris M: Peter Maydell @@ -1015,7 +1016,7 @@ F: hw/display/ssd03* F: include/hw/input/stellaris_gamepad.h F: include/hw/timer/stellaris-gptm.h F: docs/system/arm/stellaris.rst -F: tests/functional/test_arm_stellaris.py +F: tests/functional/arm/test_stellaris.py STM32L4x5 SoC Family M: Samuel Tardieu @@ -1044,7 +1045,7 @@ S: Odd Fixes F: hw/arm/vexpress.c F: hw/display/sii9022.c F: docs/system/arm/vexpress.rst -F: tests/functional/test_arm_vexpress.py +F: tests/functional/arm/test_vexpress.py Versatile PB M: Peter Maydell @@ -1065,8 +1066,8 @@ F: include/hw/arm/virt.h F: docs/system/arm/virt.rst F: tests/functional/aarch64/test_*virt*.py F: tests/functional/aarch64/test_tuxrun.py -F: tests/functional/test_arm_tuxrun.py -F: tests/functional/test_arm_virt.py +F: tests/functional/arm/test_tuxrun.py +F: tests/functional/arm/test_virt.py Xilinx Zynq M: Edgar E. Iglesias @@ -1187,7 +1188,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/msf2-som.c F: docs/system/arm/emcraft-sf2.rst -F: tests/functional/test_arm_emcraft_sf2.py +F: tests/functional/arm/test_emcraft_sf2.py ASPEED BMCs M: Cédric Le Goater @@ -1205,6 +1206,7 @@ F: hw/net/ftgmac100.c F: include/hw/net/ftgmac100.h F: docs/system/arm/aspeed.rst F: docs/system/arm/fby35.rst +F: tests/functional/*/*aspeed* F: tests/*/*aspeed* F: tests/*/*ast2700* F: hw/arm/fby35.c @@ -1220,7 +1222,7 @@ F: hw/*/microbit*.c F: include/hw/*/nrf51*.h F: include/hw/*/microbit*.h F: tests/qtest/microbit-test.c -F: tests/functional/test_arm_microbit.py +F: tests/functional/arm/test_microbit.py F: docs/system/arm/nrf.rst ARM PL011 Rust device @@ -2077,7 +2079,7 @@ S: Odd Fixes F: hw/*/omap* F: include/hw/arm/omap.h F: docs/system/arm/sx1.rst -F: tests/functional/test_arm_sx1.py +F: tests/functional/arm/test_sx1.py IPack M: Alberto Garcia @@ -3914,7 +3916,7 @@ F: configs/targets/*linux-user.mak F: scripts/qemu-binfmt-conf.sh F: scripts/update-syscalltbl.sh F: scripts/update-mips-syscall-args.sh -F: tests/functional/test_arm_bflt.py +F: tests/functional/arm/test_bflt.py Tiny Code Generator (TCG) ------------------------- diff --git a/tests/functional/arm/meson.build b/tests/functional/arm/meson.build new file mode 100644 index 0000000000000..e4e7dba8d087a --- /dev/null +++ b/tests/functional/arm/meson.build @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_arm_timeouts = { + 'aspeed_palmetto' : 120, + 'aspeed_romulus' : 120, + 'aspeed_witherspoon' : 120, + 'aspeed_ast2500' : 720, + 'aspeed_ast2600' : 1200, + 'aspeed_bletchley' : 480, + 'aspeed_catalina' : 480, + 'aspeed_gb200nvl_bmc' : 480, + 'aspeed_rainier' : 480, + 'bpim2u' : 500, + 'collie' : 180, + 'cubieboard' : 360, + 'orangepi' : 540, + 'quanta_gsj' : 240, + 'raspi2' : 120, + 'replay' : 240, + 'tuxrun' : 240, + 'sx1' : 360, +} + +tests_arm_system_quick = [ + 'migration', +] + +tests_arm_system_thorough = [ + 'aspeed_ast1030', + 'aspeed_palmetto', + 'aspeed_romulus', + 'aspeed_witherspoon', + 'aspeed_ast2500', + 'aspeed_ast2600', + 'aspeed_bletchley', + 'aspeed_catalina', + 'aspeed_gb200nvl_bmc', + 'aspeed_rainier', + 'bpim2u', + 'canona1100', + 'collie', + 'cubieboard', + 'emcraft_sf2', + 'integratorcp', + 'max78000fthr', + 'microbit', + 'orangepi', + 'quanta_gsj', + 'raspi2', + 'realview', + 'replay', + 'smdkc210', + 'stellaris', + 'sx1', + 'vexpress', + 'virt', + 'tuxrun', +] + +tests_arm_linuxuser_thorough = [ + 'bflt', +] diff --git a/tests/functional/test_arm_aspeed_ast1030.py b/tests/functional/arm/test_aspeed_ast1030.py similarity index 100% rename from tests/functional/test_arm_aspeed_ast1030.py rename to tests/functional/arm/test_aspeed_ast1030.py diff --git a/tests/functional/test_arm_aspeed_ast2500.py b/tests/functional/arm/test_aspeed_ast2500.py similarity index 100% rename from tests/functional/test_arm_aspeed_ast2500.py rename to tests/functional/arm/test_aspeed_ast2500.py diff --git a/tests/functional/test_arm_aspeed_ast2600.py b/tests/functional/arm/test_aspeed_ast2600.py similarity index 100% rename from tests/functional/test_arm_aspeed_ast2600.py rename to tests/functional/arm/test_aspeed_ast2600.py diff --git a/tests/functional/test_arm_aspeed_bletchley.py b/tests/functional/arm/test_aspeed_bletchley.py old mode 100644 new mode 100755 similarity index 100% rename from tests/functional/test_arm_aspeed_bletchley.py rename to tests/functional/arm/test_aspeed_bletchley.py diff --git a/tests/functional/test_arm_aspeed_catalina.py b/tests/functional/arm/test_aspeed_catalina.py similarity index 100% rename from tests/functional/test_arm_aspeed_catalina.py rename to tests/functional/arm/test_aspeed_catalina.py diff --git a/tests/functional/test_arm_aspeed_gb200nvl_bmc.py b/tests/functional/arm/test_aspeed_gb200nvl_bmc.py old mode 100644 new mode 100755 similarity index 100% rename from tests/functional/test_arm_aspeed_gb200nvl_bmc.py rename to tests/functional/arm/test_aspeed_gb200nvl_bmc.py diff --git a/tests/functional/test_arm_aspeed_palmetto.py b/tests/functional/arm/test_aspeed_palmetto.py similarity index 100% rename from tests/functional/test_arm_aspeed_palmetto.py rename to tests/functional/arm/test_aspeed_palmetto.py diff --git a/tests/functional/test_arm_aspeed_rainier.py b/tests/functional/arm/test_aspeed_rainier.py similarity index 100% rename from tests/functional/test_arm_aspeed_rainier.py rename to tests/functional/arm/test_aspeed_rainier.py diff --git a/tests/functional/test_arm_aspeed_romulus.py b/tests/functional/arm/test_aspeed_romulus.py similarity index 100% rename from tests/functional/test_arm_aspeed_romulus.py rename to tests/functional/arm/test_aspeed_romulus.py diff --git a/tests/functional/test_arm_aspeed_witherspoon.py b/tests/functional/arm/test_aspeed_witherspoon.py old mode 100644 new mode 100755 similarity index 100% rename from tests/functional/test_arm_aspeed_witherspoon.py rename to tests/functional/arm/test_aspeed_witherspoon.py diff --git a/tests/functional/test_arm_bflt.py b/tests/functional/arm/test_bflt.py similarity index 100% rename from tests/functional/test_arm_bflt.py rename to tests/functional/arm/test_bflt.py diff --git a/tests/functional/test_arm_bpim2u.py b/tests/functional/arm/test_bpim2u.py similarity index 100% rename from tests/functional/test_arm_bpim2u.py rename to tests/functional/arm/test_bpim2u.py diff --git a/tests/functional/test_arm_canona1100.py b/tests/functional/arm/test_canona1100.py similarity index 100% rename from tests/functional/test_arm_canona1100.py rename to tests/functional/arm/test_canona1100.py diff --git a/tests/functional/test_arm_collie.py b/tests/functional/arm/test_collie.py similarity index 100% rename from tests/functional/test_arm_collie.py rename to tests/functional/arm/test_collie.py diff --git a/tests/functional/test_arm_cubieboard.py b/tests/functional/arm/test_cubieboard.py similarity index 100% rename from tests/functional/test_arm_cubieboard.py rename to tests/functional/arm/test_cubieboard.py diff --git a/tests/functional/test_arm_emcraft_sf2.py b/tests/functional/arm/test_emcraft_sf2.py similarity index 100% rename from tests/functional/test_arm_emcraft_sf2.py rename to tests/functional/arm/test_emcraft_sf2.py diff --git a/tests/functional/test_arm_integratorcp.py b/tests/functional/arm/test_integratorcp.py similarity index 100% rename from tests/functional/test_arm_integratorcp.py rename to tests/functional/arm/test_integratorcp.py diff --git a/tests/functional/test_arm_max78000fthr.py b/tests/functional/arm/test_max78000fthr.py similarity index 100% rename from tests/functional/test_arm_max78000fthr.py rename to tests/functional/arm/test_max78000fthr.py diff --git a/tests/functional/test_arm_microbit.py b/tests/functional/arm/test_microbit.py similarity index 100% rename from tests/functional/test_arm_microbit.py rename to tests/functional/arm/test_microbit.py diff --git a/tests/functional/test_arm_migration.py b/tests/functional/arm/test_migration.py similarity index 100% rename from tests/functional/test_arm_migration.py rename to tests/functional/arm/test_migration.py diff --git a/tests/functional/test_arm_orangepi.py b/tests/functional/arm/test_orangepi.py similarity index 100% rename from tests/functional/test_arm_orangepi.py rename to tests/functional/arm/test_orangepi.py diff --git a/tests/functional/test_arm_quanta_gsj.py b/tests/functional/arm/test_quanta_gsj.py similarity index 100% rename from tests/functional/test_arm_quanta_gsj.py rename to tests/functional/arm/test_quanta_gsj.py diff --git a/tests/functional/test_arm_raspi2.py b/tests/functional/arm/test_raspi2.py similarity index 100% rename from tests/functional/test_arm_raspi2.py rename to tests/functional/arm/test_raspi2.py diff --git a/tests/functional/test_arm_realview.py b/tests/functional/arm/test_realview.py similarity index 100% rename from tests/functional/test_arm_realview.py rename to tests/functional/arm/test_realview.py diff --git a/tests/functional/test_arm_replay.py b/tests/functional/arm/test_replay.py similarity index 100% rename from tests/functional/test_arm_replay.py rename to tests/functional/arm/test_replay.py diff --git a/tests/functional/test_arm_smdkc210.py b/tests/functional/arm/test_smdkc210.py similarity index 100% rename from tests/functional/test_arm_smdkc210.py rename to tests/functional/arm/test_smdkc210.py diff --git a/tests/functional/test_arm_stellaris.py b/tests/functional/arm/test_stellaris.py similarity index 100% rename from tests/functional/test_arm_stellaris.py rename to tests/functional/arm/test_stellaris.py diff --git a/tests/functional/test_arm_sx1.py b/tests/functional/arm/test_sx1.py similarity index 100% rename from tests/functional/test_arm_sx1.py rename to tests/functional/arm/test_sx1.py diff --git a/tests/functional/test_arm_tuxrun.py b/tests/functional/arm/test_tuxrun.py similarity index 100% rename from tests/functional/test_arm_tuxrun.py rename to tests/functional/arm/test_tuxrun.py diff --git a/tests/functional/test_arm_vexpress.py b/tests/functional/arm/test_vexpress.py similarity index 100% rename from tests/functional/test_arm_vexpress.py rename to tests/functional/arm/test_vexpress.py diff --git a/tests/functional/test_arm_virt.py b/tests/functional/arm/test_virt.py similarity index 100% rename from tests/functional/test_arm_virt.py rename to tests/functional/arm/test_virt.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index a7f8c88a078fd..6989446d1c47f 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -11,27 +11,7 @@ endif subdir('aarch64') subdir('alpha') - -test_arm_timeouts = { - 'arm_aspeed_palmetto' : 120, - 'arm_aspeed_romulus' : 120, - 'arm_aspeed_witherspoon' : 120, - 'arm_aspeed_ast2500' : 720, - 'arm_aspeed_ast2600' : 1200, - 'arm_aspeed_bletchley' : 480, - 'arm_aspeed_catalina' : 480, - 'arm_aspeed_gb200nvl_bmc' : 480, - 'arm_aspeed_rainier' : 480, - 'arm_bpim2u' : 500, - 'arm_collie' : 180, - 'arm_cubieboard' : 360, - 'arm_orangepi' : 540, - 'arm_quanta_gsj' : 240, - 'arm_raspi2' : 120, - 'arm_replay' : 240, - 'arm_tuxrun' : 240, - 'arm_sx1' : 360, -} +subdir('arm') test_mips_timeouts = { 'mips_malta' : 480, @@ -97,46 +77,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_arm_system_quick = [ - 'arm_migration', -] - -tests_arm_system_thorough = [ - 'arm_aspeed_ast1030', - 'arm_aspeed_palmetto', - 'arm_aspeed_romulus', - 'arm_aspeed_witherspoon', - 'arm_aspeed_ast2500', - 'arm_aspeed_ast2600', - 'arm_aspeed_bletchley', - 'arm_aspeed_catalina', - 'arm_aspeed_gb200nvl_bmc', - 'arm_aspeed_rainier', - 'arm_bpim2u', - 'arm_canona1100', - 'arm_collie', - 'arm_cubieboard', - 'arm_emcraft_sf2', - 'arm_integratorcp', - 'arm_max78000fthr', - 'arm_microbit', - 'arm_orangepi', - 'arm_quanta_gsj', - 'arm_raspi2', - 'arm_realview', - 'arm_replay', - 'arm_smdkc210', - 'arm_stellaris', - 'arm_sx1', - 'arm_vexpress', - 'arm_virt', - 'arm_tuxrun', -] - -tests_arm_linuxuser_thorough = [ - 'arm_bflt', -] - tests_avr_system_thorough = [ 'avr_mega2560', 'avr_uno', From dc8f7a1bf724cb90d37c5bdfcdb80a5c74befc11 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:45 +0200 Subject: [PATCH 0277/1794] tests/functional: Move avr tests into architecture specific folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the avr tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-9-thuth@redhat.com> --- MAINTAINERS | 4 ++-- tests/functional/avr/meson.build | 6 ++++++ .../{test_avr_mega2560.py => avr/test_mega2560.py} | 0 tests/functional/{test_avr_uno.py => avr/test_uno.py} | 0 tests/functional/meson.build | 6 +----- 5 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 tests/functional/avr/meson.build rename tests/functional/{test_avr_mega2560.py => avr/test_mega2560.py} (100%) rename tests/functional/{test_avr_uno.py => avr/test_uno.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 1eb964feca49a..d01afcbea6d2f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -221,7 +221,7 @@ S: Maintained F: docs/system/target-avr.rst F: gdb-xml/avr-cpu.xml F: target/avr/ -F: tests/functional/test_avr_*.py +F: tests/functional/avr/ Hexagon TCG CPUs M: Brian Cain @@ -1249,7 +1249,7 @@ Arduino M: Philippe Mathieu-Daudé S: Maintained F: hw/avr/arduino.c -F: tests/functional/test_avr_uno.py +F: tests/functional/avr/test_uno.py HP-PARISC Machines ------------------ diff --git a/tests/functional/avr/meson.build b/tests/functional/avr/meson.build new file mode 100644 index 0000000000000..7a2cb7099e75b --- /dev/null +++ b/tests/functional/avr/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_avr_system_thorough = [ + 'mega2560', + 'uno', +] diff --git a/tests/functional/test_avr_mega2560.py b/tests/functional/avr/test_mega2560.py similarity index 100% rename from tests/functional/test_avr_mega2560.py rename to tests/functional/avr/test_mega2560.py diff --git a/tests/functional/test_avr_uno.py b/tests/functional/avr/test_uno.py similarity index 100% rename from tests/functional/test_avr_uno.py rename to tests/functional/avr/test_uno.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 6989446d1c47f..81eaa9c218c77 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -12,6 +12,7 @@ endif subdir('aarch64') subdir('alpha') subdir('arm') +subdir('avr') test_mips_timeouts = { 'mips_malta' : 480, @@ -77,11 +78,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_avr_system_thorough = [ - 'avr_mega2560', - 'avr_uno', -] - tests_hppa_system_quick = [ 'hppa_seabios', ] From e538303d31bb0b537285571b9b56674e7c90b120 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:46 +0200 Subject: [PATCH 0278/1794] tests/functional: Move hppa tests into architecture specific folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the avr tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-10-thuth@redhat.com> --- MAINTAINERS | 2 +- tests/functional/hppa/meson.build | 5 +++++ .../{test_hppa_seabios.py => hppa/test_seabios.py} | 0 tests/functional/meson.build | 5 +---- 4 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 tests/functional/hppa/meson.build rename tests/functional/{test_hppa_seabios.py => hppa/test_seabios.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index d01afcbea6d2f..2e1754912f65f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1273,7 +1273,7 @@ F: include/hw/pci-host/astro.h F: include/hw/pci-host/dino.h F: pc-bios/hppa-firmware.img F: roms/seabios-hppa/ -F: tests/functional/test_hppa_seabios.py +F: tests/functional/hppa/test_seabios.py LoongArch Machines ------------------ diff --git a/tests/functional/hppa/meson.build b/tests/functional/hppa/meson.build new file mode 100644 index 0000000000000..a334837088409 --- /dev/null +++ b/tests/functional/hppa/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_hppa_system_quick = [ + 'seabios', +] diff --git a/tests/functional/test_hppa_seabios.py b/tests/functional/hppa/test_seabios.py similarity index 100% rename from tests/functional/test_hppa_seabios.py rename to tests/functional/hppa/test_seabios.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 81eaa9c218c77..8f85c13d3d142 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -13,6 +13,7 @@ subdir('aarch64') subdir('alpha') subdir('arm') subdir('avr') +subdir('hppa') test_mips_timeouts = { 'mips_malta' : 480, @@ -78,10 +79,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_hppa_system_quick = [ - 'hppa_seabios', -] - tests_i386_system_quick = [ 'i386_migration', ] From 84b30c4587719297955e1fe2a22face5cc84587c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:47 +0200 Subject: [PATCH 0279/1794] tests/functional: Move i386 tests into architecture specific folder The tests/functional folder has become quite crowded, thus move the i386 tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-11-thuth@redhat.com> --- MAINTAINERS | 3 ++- tests/functional/i386/meson.build | 10 ++++++++++ .../{test_i386_migration.py => i386/test_migration.py} | 0 .../{test_i386_replay.py => i386/test_replay.py} | 0 .../{test_i386_tuxrun.py => i386/test_tuxrun.py} | 0 tests/functional/meson.build | 10 +--------- 6 files changed, 13 insertions(+), 10 deletions(-) create mode 100644 tests/functional/i386/meson.build rename tests/functional/{test_i386_migration.py => i386/test_migration.py} (100%) rename tests/functional/{test_i386_replay.py => i386/test_replay.py} (100%) rename tests/functional/{test_i386_tuxrun.py => i386/test_tuxrun.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 2e1754912f65f..2b109ecc18cd5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -146,6 +146,7 @@ F: target/i386/*.[ch] F: target/i386/Kconfig F: target/i386/meson.build F: tools/i386/ +F: tests/functional/i386/ Guest CPU cores (TCG) --------------------- @@ -1891,7 +1892,7 @@ F: hw/isa/apm.c F: include/hw/isa/apm.h F: tests/unit/test-x86-topo.c F: tests/qtest/test-x86-cpuid-compat.c -F: tests/functional/test_i386_tuxrun.py +F: tests/functional/i386/test_tuxrun.py F: tests/functional/test_linux_initrd.py F: tests/functional/test_mem_addr_space.py F: tests/functional/test_pc_cpu_hotplug_props.py diff --git a/tests/functional/i386/meson.build b/tests/functional/i386/meson.build new file mode 100644 index 0000000000000..23d8c216be7b3 --- /dev/null +++ b/tests/functional/i386/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_i386_system_quick = [ + 'migration', +] + +tests_i386_system_thorough = [ + 'replay', + 'tuxrun', +] diff --git a/tests/functional/test_i386_migration.py b/tests/functional/i386/test_migration.py similarity index 100% rename from tests/functional/test_i386_migration.py rename to tests/functional/i386/test_migration.py diff --git a/tests/functional/test_i386_replay.py b/tests/functional/i386/test_replay.py similarity index 100% rename from tests/functional/test_i386_replay.py rename to tests/functional/i386/test_replay.py diff --git a/tests/functional/test_i386_tuxrun.py b/tests/functional/i386/test_tuxrun.py similarity index 100% rename from tests/functional/test_i386_tuxrun.py rename to tests/functional/i386/test_tuxrun.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 8f85c13d3d142..f1fc01717c387 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -14,6 +14,7 @@ subdir('alpha') subdir('arm') subdir('avr') subdir('hppa') +subdir('i386') test_mips_timeouts = { 'mips_malta' : 480, @@ -79,15 +80,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_i386_system_quick = [ - 'i386_migration', -] - -tests_i386_system_thorough = [ - 'i386_replay', - 'i386_tuxrun', -] - tests_loongarch64_system_thorough = [ 'loongarch64_virt', ] From 6bd38ef0f9af38364509005385ff2ac897ad3913 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:48 +0200 Subject: [PATCH 0280/1794] tests/functional: Move loongarch64 tests into architecture specific folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the loongarch64 tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-12-thuth@redhat.com> --- MAINTAINERS | 2 +- tests/functional/loongarch64/meson.build | 5 +++++ .../{test_loongarch64_virt.py => loongarch64/test_virt.py} | 0 tests/functional/meson.build | 5 +---- 4 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 tests/functional/loongarch64/meson.build rename tests/functional/{test_loongarch64_virt.py => loongarch64/test_virt.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 2b109ecc18cd5..716127e831d4f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -258,7 +258,7 @@ M: Song Gao S: Maintained F: target/loongarch/ F: tests/tcg/loongarch64/ -F: tests/functional/test_loongarch64_virt.py +F: tests/functional/loongarch64/test_virt.py M68K TCG CPUs M: Laurent Vivier diff --git a/tests/functional/loongarch64/meson.build b/tests/functional/loongarch64/meson.build new file mode 100644 index 0000000000000..d1687176a3d3f --- /dev/null +++ b/tests/functional/loongarch64/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_loongarch64_system_thorough = [ + 'virt', +] diff --git a/tests/functional/test_loongarch64_virt.py b/tests/functional/loongarch64/test_virt.py similarity index 100% rename from tests/functional/test_loongarch64_virt.py rename to tests/functional/loongarch64/test_virt.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index f1fc01717c387..e2e66dcf5238f 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -15,6 +15,7 @@ subdir('arm') subdir('avr') subdir('hppa') subdir('i386') +subdir('loongarch64') test_mips_timeouts = { 'mips_malta' : 480, @@ -80,10 +81,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_loongarch64_system_thorough = [ - 'loongarch64_virt', -] - tests_m68k_system_thorough = [ 'm68k_mcf5208evb', 'm68k_nextcube', From 0ae63d0e19b22d45cd354b428b4b530223f53d7b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:49 +0200 Subject: [PATCH 0281/1794] tests/functional: Move m68k tests into architecture specific folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the m68k tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-13-thuth@redhat.com> --- MAINTAINERS | 8 ++++---- tests/functional/m68k/meson.build | 9 +++++++++ .../{test_m68k_mcf5208evb.py => m68k/test_mcf5208evb.py} | 0 .../{test_m68k_nextcube.py => m68k/test_nextcube.py} | 0 .../functional/{test_m68k_q800.py => m68k/test_q800.py} | 0 .../{test_m68k_replay.py => m68k/test_replay.py} | 0 .../{test_m68k_tuxrun.py => m68k/test_tuxrun.py} | 0 tests/functional/meson.build | 9 +-------- 8 files changed, 14 insertions(+), 12 deletions(-) create mode 100644 tests/functional/m68k/meson.build rename tests/functional/{test_m68k_mcf5208evb.py => m68k/test_mcf5208evb.py} (100%) rename tests/functional/{test_m68k_nextcube.py => m68k/test_nextcube.py} (100%) rename tests/functional/{test_m68k_q800.py => m68k/test_q800.py} (100%) rename tests/functional/{test_m68k_replay.py => m68k/test_replay.py} (100%) rename tests/functional/{test_m68k_tuxrun.py => m68k/test_tuxrun.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 716127e831d4f..e188de813fbc2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1312,7 +1312,7 @@ F: hw/m68k/mcf_intc.c F: hw/char/mcf_uart.c F: hw/net/mcf_fec.c F: include/hw/m68k/mcf*.h -F: tests/functional/test_m68k_mcf5208evb.py +F: tests/functional/m68k/test_mcf5208evb.py NeXTcube M: Thomas Huth @@ -1320,7 +1320,7 @@ S: Odd Fixes F: hw/m68k/next-*.c F: hw/display/next-fb.c F: include/hw/m68k/next-cube.h -F: tests/functional/test_m68k_nextcube.py +F: tests/functional/m68k/test_nextcube.py q800 M: Laurent Vivier @@ -1346,7 +1346,7 @@ F: include/hw/m68k/q800-glue.h F: include/hw/misc/djmemc.h F: include/hw/misc/iosb.h F: include/hw/audio/asc.h -F: tests/functional/test_m68k_q800.py +F: tests/functional/m68k/test_q800.py virt M: Laurent Vivier @@ -1361,7 +1361,7 @@ F: include/hw/intc/goldfish_pic.h F: include/hw/intc/m68k_irqc.h F: include/hw/misc/virt_ctrl.h F: docs/specs/virt-ctlr.rst -F: tests/functional/test_m68k_tuxrun.py +F: tests/functional/m68k/test_tuxrun.py MicroBlaze Machines ------------------- diff --git a/tests/functional/m68k/meson.build b/tests/functional/m68k/meson.build new file mode 100644 index 0000000000000..e29044a6d7354 --- /dev/null +++ b/tests/functional/m68k/meson.build @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_m68k_system_thorough = [ + 'mcf5208evb', + 'nextcube', + 'replay', + 'q800', + 'tuxrun', +] diff --git a/tests/functional/test_m68k_mcf5208evb.py b/tests/functional/m68k/test_mcf5208evb.py similarity index 100% rename from tests/functional/test_m68k_mcf5208evb.py rename to tests/functional/m68k/test_mcf5208evb.py diff --git a/tests/functional/test_m68k_nextcube.py b/tests/functional/m68k/test_nextcube.py similarity index 100% rename from tests/functional/test_m68k_nextcube.py rename to tests/functional/m68k/test_nextcube.py diff --git a/tests/functional/test_m68k_q800.py b/tests/functional/m68k/test_q800.py similarity index 100% rename from tests/functional/test_m68k_q800.py rename to tests/functional/m68k/test_q800.py diff --git a/tests/functional/test_m68k_replay.py b/tests/functional/m68k/test_replay.py similarity index 100% rename from tests/functional/test_m68k_replay.py rename to tests/functional/m68k/test_replay.py diff --git a/tests/functional/test_m68k_tuxrun.py b/tests/functional/m68k/test_tuxrun.py similarity index 100% rename from tests/functional/test_m68k_tuxrun.py rename to tests/functional/m68k/test_tuxrun.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index e2e66dcf5238f..d32dd4a371f9a 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -16,6 +16,7 @@ subdir('avr') subdir('hppa') subdir('i386') subdir('loongarch64') +subdir('m68k') test_mips_timeouts = { 'mips_malta' : 480, @@ -81,14 +82,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_m68k_system_thorough = [ - 'm68k_mcf5208evb', - 'm68k_nextcube', - 'm68k_replay', - 'm68k_q800', - 'm68k_tuxrun', -] - tests_microblaze_system_thorough = [ 'microblaze_replay', 'microblaze_s3adsp1800' From 67adbc8b6f40f6a2370d4a0ba479d3778d147cbc Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:50 +0200 Subject: [PATCH 0282/1794] tests/functional: Move microblaze tests into architecture specific folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the microblaze tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-14-thuth@redhat.com> --- MAINTAINERS | 2 +- tests/functional/meson.build | 11 ++--------- tests/functional/microblaze/meson.build | 6 ++++++ .../test_replay.py} | 0 .../test_s3adsp1800.py} | 0 tests/functional/microblazeel/meson.build | 5 +++++ .../test_s3adsp1800.py} | 2 +- 7 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 tests/functional/microblaze/meson.build rename tests/functional/{test_microblaze_replay.py => microblaze/test_replay.py} (100%) rename tests/functional/{test_microblaze_s3adsp1800.py => microblaze/test_s3adsp1800.py} (100%) create mode 100644 tests/functional/microblazeel/meson.build rename tests/functional/{test_microblazeel_s3adsp1800.py => microblazeel/test_s3adsp1800.py} (92%) diff --git a/MAINTAINERS b/MAINTAINERS index e188de813fbc2..b6a835777bb07 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1370,7 +1370,7 @@ M: Edgar E. Iglesias S: Maintained F: hw/microblaze/petalogix_s3adsp1800_mmu.c F: include/hw/char/xilinx_uartlite.h -F: tests/functional/test_microblaze*.py +F: tests/functional/microblaze*/test_s3adsp1800.py petalogix_ml605 M: Edgar E. Iglesias diff --git a/tests/functional/meson.build b/tests/functional/meson.build index d32dd4a371f9a..fee680561458e 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -17,6 +17,8 @@ subdir('hppa') subdir('i386') subdir('loongarch64') subdir('m68k') +subdir('microblaze') +subdir('microblazeel') test_mips_timeouts = { 'mips_malta' : 480, @@ -82,15 +84,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_microblaze_system_thorough = [ - 'microblaze_replay', - 'microblaze_s3adsp1800' -] - -tests_microblazeel_system_thorough = [ - 'microblazeel_s3adsp1800' -] - tests_mips_system_thorough = [ 'mips_malta', 'mips_replay', diff --git a/tests/functional/microblaze/meson.build b/tests/functional/microblaze/meson.build new file mode 100644 index 0000000000000..8069ca9be6014 --- /dev/null +++ b/tests/functional/microblaze/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_microblaze_system_thorough = [ + 'replay', + 's3adsp1800' +] diff --git a/tests/functional/test_microblaze_replay.py b/tests/functional/microblaze/test_replay.py similarity index 100% rename from tests/functional/test_microblaze_replay.py rename to tests/functional/microblaze/test_replay.py diff --git a/tests/functional/test_microblaze_s3adsp1800.py b/tests/functional/microblaze/test_s3adsp1800.py similarity index 100% rename from tests/functional/test_microblaze_s3adsp1800.py rename to tests/functional/microblaze/test_s3adsp1800.py diff --git a/tests/functional/microblazeel/meson.build b/tests/functional/microblazeel/meson.build new file mode 100644 index 0000000000000..27619dc5a9a63 --- /dev/null +++ b/tests/functional/microblazeel/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_microblazeel_system_thorough = [ + 's3adsp1800' +] diff --git a/tests/functional/test_microblazeel_s3adsp1800.py b/tests/functional/microblazeel/test_s3adsp1800.py similarity index 92% rename from tests/functional/test_microblazeel_s3adsp1800.py rename to tests/functional/microblazeel/test_s3adsp1800.py index 915902d48bdef..75ce8856ed151 100755 --- a/tests/functional/test_microblazeel_s3adsp1800.py +++ b/tests/functional/microblazeel/test_s3adsp1800.py @@ -7,7 +7,7 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from test_microblaze_s3adsp1800 import MicroblazeMachine +from microblaze.test_s3adsp1800 import MicroblazeMachine class MicroblazeLittleEndianMachine(MicroblazeMachine): From f227c45e5b6e928a413fcf7176b59aac4e430c82 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:51 +0200 Subject: [PATCH 0283/1794] tests/functional: Move mips tests into target-specific folders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the mips tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-15-thuth@redhat.com> --- MAINTAINERS | 8 ++-- tests/functional/meson.build | 47 ++----------------- tests/functional/mips/meson.build | 11 +++++ .../test_malta.py} | 0 .../test_replay.py} | 0 .../test_tuxrun.py} | 0 tests/functional/mips64/meson.build | 10 ++++ .../test_malta.py} | 2 +- .../test_tuxrun.py} | 0 tests/functional/mips64el/meson.build | 14 ++++++ .../test_fuloong2e.py} | 0 .../test_loongson3v.py} | 0 .../test_malta.py} | 4 +- .../test_replay.py} | 0 .../test_tuxrun.py} | 0 tests/functional/mipsel/meson.build | 12 +++++ .../test_malta.py} | 2 +- .../test_replay.py} | 0 .../test_tuxrun.py} | 0 19 files changed, 59 insertions(+), 51 deletions(-) create mode 100644 tests/functional/mips/meson.build rename tests/functional/{test_mips_malta.py => mips/test_malta.py} (100%) rename tests/functional/{test_mips_replay.py => mips/test_replay.py} (100%) rename tests/functional/{test_mips_tuxrun.py => mips/test_tuxrun.py} (100%) create mode 100644 tests/functional/mips64/meson.build rename tests/functional/{test_mips64_malta.py => mips64/test_malta.py} (96%) rename tests/functional/{test_mips64_tuxrun.py => mips64/test_tuxrun.py} (100%) create mode 100644 tests/functional/mips64el/meson.build rename tests/functional/{test_mips64el_fuloong2e.py => mips64el/test_fuloong2e.py} (100%) rename tests/functional/{test_mips64el_loongson3v.py => mips64el/test_loongson3v.py} (100%) rename tests/functional/{test_mips64el_malta.py => mips64el/test_malta.py} (98%) rename tests/functional/{test_mips64el_replay.py => mips64el/test_replay.py} (100%) rename tests/functional/{test_mips64el_tuxrun.py => mips64el/test_tuxrun.py} (100%) create mode 100644 tests/functional/mipsel/meson.build rename tests/functional/{test_mipsel_malta.py => mipsel/test_malta.py} (98%) rename tests/functional/{test_mipsel_replay.py => mipsel/test_replay.py} (100%) mode change 100644 => 100755 rename tests/functional/{test_mipsel_tuxrun.py => mipsel/test_tuxrun.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index b6a835777bb07..b8f0ce33605bd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1406,8 +1406,8 @@ F: hw/acpi/piix4.c F: hw/mips/malta.c F: hw/pci-host/gt64120.c F: include/hw/southbridge/piix.h -F: tests/functional/test_mips*_malta.py -F: tests/functional/test_mips*_tuxrun.py +F: tests/functional/mips*/test_malta.py +F: tests/functional/mips*/test_tuxrun.py Mipssim R: Aleksandar Rikalo @@ -1423,7 +1423,7 @@ S: Odd Fixes F: hw/mips/fuloong2e.c F: hw/pci-host/bonito.c F: include/hw/pci-host/bonito.h -F: tests/functional/test_mips64el_fuloong2e.py +F: tests/functional/mips64el/test_fuloong2e.py Loongson-3 virtual platforms M: Huacai Chen @@ -1438,7 +1438,7 @@ F: hw/mips/loongson3_virt.c F: include/hw/intc/loongson_ipi_common.h F: include/hw/intc/loongson_ipi.h F: include/hw/intc/loongson_liointc.h -F: tests/functional/test_mips64el_loongson3v.py +F: tests/functional/mips64el/test_loongson3v.py Boston M: Paul Burton diff --git a/tests/functional/meson.build b/tests/functional/meson.build index fee680561458e..52969a3ff8720 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -19,24 +19,10 @@ subdir('loongarch64') subdir('m68k') subdir('microblaze') subdir('microblazeel') - -test_mips_timeouts = { - 'mips_malta' : 480, -} - -test_mipsel_timeouts = { - 'mipsel_malta' : 420, - 'mipsel_replay' : 480, -} - -test_mips64_timeouts = { - 'mips64_malta' : 240, -} - -test_mips64el_timeouts = { - 'mips64el_malta' : 420, - 'mips64el_replay' : 180, -} +subdir('mips') +subdir('mipsel') +subdir('mips64') +subdir('mips64el') test_ppc_timeouts = { 'ppc_40p' : 240, @@ -84,31 +70,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_mips_system_thorough = [ - 'mips_malta', - 'mips_replay', - 'mips_tuxrun', -] - -tests_mipsel_system_thorough = [ - 'mipsel_malta', - 'mipsel_replay', - 'mipsel_tuxrun', -] - -tests_mips64_system_thorough = [ - 'mips64_malta', - 'mips64_tuxrun', -] - -tests_mips64el_system_thorough = [ - 'mips64el_fuloong2e', - 'mips64el_loongson3v', - 'mips64el_malta', - 'mips64el_replay', - 'mips64el_tuxrun', -] - tests_or1k_system_thorough = [ 'or1k_replay', 'or1k_sim', diff --git a/tests/functional/mips/meson.build b/tests/functional/mips/meson.build new file mode 100644 index 0000000000000..49aaf53b02db2 --- /dev/null +++ b/tests/functional/mips/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_mips_timeouts = { + 'malta' : 480, +} + +tests_mips_system_thorough = [ + 'malta', + 'replay', + 'tuxrun', +] diff --git a/tests/functional/test_mips_malta.py b/tests/functional/mips/test_malta.py similarity index 100% rename from tests/functional/test_mips_malta.py rename to tests/functional/mips/test_malta.py diff --git a/tests/functional/test_mips_replay.py b/tests/functional/mips/test_replay.py similarity index 100% rename from tests/functional/test_mips_replay.py rename to tests/functional/mips/test_replay.py diff --git a/tests/functional/test_mips_tuxrun.py b/tests/functional/mips/test_tuxrun.py similarity index 100% rename from tests/functional/test_mips_tuxrun.py rename to tests/functional/mips/test_tuxrun.py diff --git a/tests/functional/mips64/meson.build b/tests/functional/mips64/meson.build new file mode 100644 index 0000000000000..3ff2118987927 --- /dev/null +++ b/tests/functional/mips64/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_mips64_timeouts = { + 'malta' : 240, +} + +tests_mips64_system_thorough = [ + 'malta', + 'tuxrun', +] diff --git a/tests/functional/test_mips64_malta.py b/tests/functional/mips64/test_malta.py similarity index 96% rename from tests/functional/test_mips64_malta.py rename to tests/functional/mips64/test_malta.py index 53c3e0c122195..a553d3c5bc736 100755 --- a/tests/functional/test_mips64_malta.py +++ b/tests/functional/mips64/test_malta.py @@ -5,7 +5,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later from qemu_test import LinuxKernelTest, Asset -from test_mips_malta import mips_check_wheezy +from mips.test_malta import mips_check_wheezy class MaltaMachineConsole(LinuxKernelTest): diff --git a/tests/functional/test_mips64_tuxrun.py b/tests/functional/mips64/test_tuxrun.py similarity index 100% rename from tests/functional/test_mips64_tuxrun.py rename to tests/functional/mips64/test_tuxrun.py diff --git a/tests/functional/mips64el/meson.build b/tests/functional/mips64el/meson.build new file mode 100644 index 0000000000000..69ec50174c451 --- /dev/null +++ b/tests/functional/mips64el/meson.build @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_mips64el_timeouts = { + 'malta' : 420, + 'replay' : 180, +} + +tests_mips64el_system_thorough = [ + 'fuloong2e', + 'loongson3v', + 'malta', + 'replay', + 'tuxrun', +] diff --git a/tests/functional/test_mips64el_fuloong2e.py b/tests/functional/mips64el/test_fuloong2e.py similarity index 100% rename from tests/functional/test_mips64el_fuloong2e.py rename to tests/functional/mips64el/test_fuloong2e.py diff --git a/tests/functional/test_mips64el_loongson3v.py b/tests/functional/mips64el/test_loongson3v.py similarity index 100% rename from tests/functional/test_mips64el_loongson3v.py rename to tests/functional/mips64el/test_loongson3v.py diff --git a/tests/functional/test_mips64el_malta.py b/tests/functional/mips64el/test_malta.py similarity index 98% rename from tests/functional/test_mips64el_malta.py rename to tests/functional/mips64el/test_malta.py index 3cc79b74c181a..8fdc49b300562 100755 --- a/tests/functional/test_mips64el_malta.py +++ b/tests/functional/mips64el/test_malta.py @@ -16,7 +16,7 @@ from qemu_test import exec_command_and_wait_for_pattern from qemu_test import skipIfMissingImports, skipFlakyTest, skipUntrustedTest -from test_mips_malta import mips_check_wheezy +from mips.test_malta import mips_check_wheezy class MaltaMachineConsole(LinuxKernelTest): @@ -191,7 +191,7 @@ def test_mips_malta_i6400_framebuffer_logo_8cores(self): self.do_test_i6400_framebuffer_logo(8) -from test_mipsel_malta import MaltaMachineYAMON +from mipsel.test_malta import MaltaMachineYAMON if __name__ == '__main__': LinuxKernelTest.main() diff --git a/tests/functional/test_mips64el_replay.py b/tests/functional/mips64el/test_replay.py similarity index 100% rename from tests/functional/test_mips64el_replay.py rename to tests/functional/mips64el/test_replay.py diff --git a/tests/functional/test_mips64el_tuxrun.py b/tests/functional/mips64el/test_tuxrun.py similarity index 100% rename from tests/functional/test_mips64el_tuxrun.py rename to tests/functional/mips64el/test_tuxrun.py diff --git a/tests/functional/mipsel/meson.build b/tests/functional/mipsel/meson.build new file mode 100644 index 0000000000000..8bfdf0649b548 --- /dev/null +++ b/tests/functional/mipsel/meson.build @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_mipsel_timeouts = { + 'malta' : 420, + 'replay' : 480, +} + +tests_mipsel_system_thorough = [ + 'malta', + 'replay', + 'tuxrun', +] diff --git a/tests/functional/test_mipsel_malta.py b/tests/functional/mipsel/test_malta.py similarity index 98% rename from tests/functional/test_mipsel_malta.py rename to tests/functional/mipsel/test_malta.py index 9ee2884da8e0d..427e163d19d7f 100755 --- a/tests/functional/test_mipsel_malta.py +++ b/tests/functional/mipsel/test_malta.py @@ -13,7 +13,7 @@ from qemu_test import interrupt_interactive_console_until_pattern from qemu_test import wait_for_console_pattern -from test_mips_malta import mips_check_wheezy +from mips.test_malta import mips_check_wheezy class MaltaMachineConsole(LinuxKernelTest): diff --git a/tests/functional/test_mipsel_replay.py b/tests/functional/mipsel/test_replay.py old mode 100644 new mode 100755 similarity index 100% rename from tests/functional/test_mipsel_replay.py rename to tests/functional/mipsel/test_replay.py diff --git a/tests/functional/test_mipsel_tuxrun.py b/tests/functional/mipsel/test_tuxrun.py similarity index 100% rename from tests/functional/test_mipsel_tuxrun.py rename to tests/functional/mipsel/test_tuxrun.py From ff94df87caeecf3fe2b77e06d2ffe483725bcb51 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:52 +0200 Subject: [PATCH 0284/1794] tests/functional: Move or1k tests into target-specific folders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the openrisc tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-16-thuth@redhat.com> --- MAINTAINERS | 2 +- tests/functional/meson.build | 6 +----- tests/functional/or1k/meson.build | 6 ++++++ .../functional/{test_or1k_replay.py => or1k/test_replay.py} | 0 tests/functional/{test_or1k_sim.py => or1k/test_sim.py} | 0 5 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 tests/functional/or1k/meson.build rename tests/functional/{test_or1k_replay.py => or1k/test_replay.py} (100%) rename tests/functional/{test_or1k_sim.py => or1k/test_sim.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index b8f0ce33605bd..56ba9b02c394b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1457,7 +1457,7 @@ S: Maintained F: docs/system/openrisc/or1k-sim.rst F: hw/intc/ompic.c F: hw/openrisc/openrisc_sim.c -F: tests/functional/test_or1k_sim.py +F: tests/functional/or1k/test_sim.py PowerPC Machines ---------------- diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 52969a3ff8720..397303ec6fbb0 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -23,6 +23,7 @@ subdir('mips') subdir('mipsel') subdir('mips64') subdir('mips64el') +subdir('or1k') test_ppc_timeouts = { 'ppc_40p' : 240, @@ -70,11 +71,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_or1k_system_thorough = [ - 'or1k_replay', - 'or1k_sim', -] - tests_ppc_system_quick = [ 'ppc_migration', 'ppc_74xx', diff --git a/tests/functional/or1k/meson.build b/tests/functional/or1k/meson.build new file mode 100644 index 0000000000000..e246e2ab08dd0 --- /dev/null +++ b/tests/functional/or1k/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_or1k_system_thorough = [ + 'replay', + 'sim', +] diff --git a/tests/functional/test_or1k_replay.py b/tests/functional/or1k/test_replay.py similarity index 100% rename from tests/functional/test_or1k_replay.py rename to tests/functional/or1k/test_replay.py diff --git a/tests/functional/test_or1k_sim.py b/tests/functional/or1k/test_sim.py similarity index 100% rename from tests/functional/test_or1k_sim.py rename to tests/functional/or1k/test_sim.py From 1cdb63218c975b20699714449131c6745a512a1f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:53 +0200 Subject: [PATCH 0285/1794] tests/functional: Move ppc/ppc64 tests into target-specific folders The tests/functional folder has become quite crowded, thus move the ppc and ppc64 tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-17-thuth@redhat.com> --- MAINTAINERS | 32 ++++++------- tests/functional/meson.build | 47 +------------------ tests/functional/ppc/meson.build | 22 +++++++++ .../{test_ppc_40p.py => ppc/test_40p.py} | 0 .../{test_ppc_74xx.py => ppc/test_74xx.py} | 0 .../{test_ppc_amiga.py => ppc/test_amiga.py} | 0 .../test_bamboo.py} | 0 .../{test_ppc_mac.py => ppc/test_mac.py} | 0 .../test_migration.py} | 0 .../test_mpc8544ds.py} | 0 .../test_replay.py} | 0 .../test_sam460ex.py} | 0 .../test_tuxrun.py} | 0 .../test_virtex_ml507.py} | 0 tests/functional/ppc64/meson.build | 25 ++++++++++ .../test_e500.py} | 0 .../{test_ppc64_hv.py => ppc64/test_hv.py} | 0 .../test_mac99.py} | 0 .../test_migration.py} | 0 .../test_powernv.py} | 0 .../test_pseries.py} | 0 .../test_replay.py} | 0 .../test_reverse_debug.py} | 0 .../test_tuxrun.py} | 0 24 files changed, 65 insertions(+), 61 deletions(-) create mode 100644 tests/functional/ppc/meson.build rename tests/functional/{test_ppc_40p.py => ppc/test_40p.py} (100%) rename tests/functional/{test_ppc_74xx.py => ppc/test_74xx.py} (100%) rename tests/functional/{test_ppc_amiga.py => ppc/test_amiga.py} (100%) rename tests/functional/{test_ppc_bamboo.py => ppc/test_bamboo.py} (100%) rename tests/functional/{test_ppc_mac.py => ppc/test_mac.py} (100%) rename tests/functional/{test_ppc_migration.py => ppc/test_migration.py} (100%) rename tests/functional/{test_ppc_mpc8544ds.py => ppc/test_mpc8544ds.py} (100%) rename tests/functional/{test_ppc_replay.py => ppc/test_replay.py} (100%) rename tests/functional/{test_ppc_sam460ex.py => ppc/test_sam460ex.py} (100%) mode change 100644 => 100755 rename tests/functional/{test_ppc_tuxrun.py => ppc/test_tuxrun.py} (100%) rename tests/functional/{test_ppc_virtex_ml507.py => ppc/test_virtex_ml507.py} (100%) create mode 100644 tests/functional/ppc64/meson.build rename tests/functional/{test_ppc64_e500.py => ppc64/test_e500.py} (100%) rename tests/functional/{test_ppc64_hv.py => ppc64/test_hv.py} (100%) rename tests/functional/{test_ppc64_mac99.py => ppc64/test_mac99.py} (100%) rename tests/functional/{test_ppc64_migration.py => ppc64/test_migration.py} (100%) rename tests/functional/{test_ppc64_powernv.py => ppc64/test_powernv.py} (100%) rename tests/functional/{test_ppc64_pseries.py => ppc64/test_pseries.py} (100%) rename tests/functional/{test_ppc64_replay.py => ppc64/test_replay.py} (100%) rename tests/functional/{test_ppc64_reverse_debug.py => ppc64/test_reverse_debug.py} (100%) rename tests/functional/{test_ppc64_tuxrun.py => ppc64/test_tuxrun.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 56ba9b02c394b..b0d440cf75112 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -310,7 +310,7 @@ F: configs/devices/ppc* F: docs/system/ppc/embedded.rst F: docs/system/target-ppc.rst F: tests/tcg/ppc*/* -F: tests/functional/test_ppc_74xx.py +F: tests/functional/ppc/test_74xx.py RISC-V TCG CPUs M: Palmer Dabbelt @@ -1466,7 +1466,7 @@ L: qemu-ppc@nongnu.org S: Orphan F: hw/ppc/ppc440_bamboo.c F: hw/pci-host/ppc4xx_pci.c -F: tests/functional/test_ppc_bamboo.py +F: tests/functional/ppc/test_bamboo.py e500 M: Bernhard Beschow @@ -1484,8 +1484,8 @@ F: pc-bios/u-boot.e500 F: hw/intc/openpic_kvm.c F: include/hw/ppc/openpic_kvm.h F: docs/system/ppc/ppce500.rst -F: tests/functional/test_ppc64_e500.py -F: tests/functional/test_ppc_tuxrun.py +F: tests/functional/ppc64/test_e500.py +F: tests/functional/ppc/test_tuxrun.py mpc8544ds M: Bernhard Beschow @@ -1493,7 +1493,7 @@ L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/mpc8544ds.c F: hw/ppc/mpc8544_guts.c -F: tests/functional/test_ppc_mpc8544ds.py +F: tests/functional/ppc/test_mpc8544ds.py New World (mac99) M: Mark Cave-Ayland @@ -1515,8 +1515,8 @@ F: include/hw/ppc/mac_dbdma.h F: include/hw/pci-host/uninorth.h F: include/hw/input/adb* F: pc-bios/qemu_vga.ndrv -F: tests/functional/test_ppc_mac.py -F: tests/functional/test_ppc64_mac99.py +F: tests/functional/ppc/test_mac.py +F: tests/functional/ppc64/test_mac99.py Old World (g3beige) M: Mark Cave-Ayland @@ -1532,7 +1532,7 @@ F: include/hw/intc/heathrow_pic.h F: include/hw/input/adb* F: include/hw/pci-host/grackle.h F: pc-bios/qemu_vga.ndrv -F: tests/functional/test_ppc_mac.py +F: tests/functional/ppc/test_mac.py PReP M: Hervé Poussineau @@ -1549,7 +1549,7 @@ F: hw/dma/i82374.c F: hw/rtc/m48t59-isa.c F: include/hw/isa/pc87312.h F: include/hw/rtc/m48t59.h -F: tests/functional/test_ppc_40p.py +F: tests/functional/ppc/test_40p.py sPAPR (pseries) M: Nicholas Piggin @@ -1572,9 +1572,9 @@ F: tests/qtest/spapr* F: tests/qtest/libqos/*spapr* F: tests/qtest/rtas* F: tests/qtest/libqos/rtas* -F: tests/functional/test_ppc64_pseries.py -F: tests/functional/test_ppc64_hv.py -F: tests/functional/test_ppc64_tuxrun.py +F: tests/functional/ppc64/test_pseries.py +F: tests/functional/ppc64/test_hv.py +F: tests/functional/ppc64/test_tuxrun.py PowerNV (Non-Virtualized) M: Nicholas Piggin @@ -1593,7 +1593,7 @@ F: include/hw/ssi/pnv_spi* F: pc-bios/skiboot.lid F: pc-bios/pnv-pnor.bin F: tests/qtest/pnv* -F: tests/functional/test_ppc64_powernv.py +F: tests/functional/ppc64/test_powernv.py pca955x M: Glenn Miles @@ -1608,7 +1608,7 @@ M: Edgar E. Iglesias L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/virtex_ml507.c -F: tests/functional/test_ppc_virtex_ml507.py +F: tests/functional/ppc/test_virtex_ml507.py sam460ex M: BALATON Zoltan @@ -1624,7 +1624,7 @@ F: pc-bios/dtb/canyonlands.dt[sb] F: pc-bios/u-boot-sam460ex-20100605.bin F: roms/u-boot-sam460ex F: docs/system/ppc/amigang.rst -F: tests/functional/test_ppc_sam460ex.py +F: tests/functional/ppc/test_sam460ex.py pegasos2 M: BALATON Zoltan @@ -1642,7 +1642,7 @@ S: Maintained F: hw/ppc/amigaone.c F: hw/pci-host/articia.c F: include/hw/pci-host/articia.h -F: tests/functional/test_ppc_amiga.py +F: tests/functional/ppc/test_amiga.py Virtual Open Firmware (VOF) M: Alexey Kardashevskiy diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 397303ec6fbb0..3caeea5ebddd4 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -24,19 +24,8 @@ subdir('mipsel') subdir('mips64') subdir('mips64el') subdir('or1k') - -test_ppc_timeouts = { - 'ppc_40p' : 240, -} - -test_ppc64_timeouts = { - 'ppc64_hv' : 1000, - 'ppc64_powernv' : 480, - 'ppc64_pseries' : 480, - 'ppc64_replay' : 210, - 'ppc64_tuxrun' : 420, - 'ppc64_mac99' : 120, -} +subdir('ppc') +subdir('ppc64') test_riscv64_timeouts = { 'riscv64_tuxrun' : 120, @@ -71,38 +60,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_ppc_system_quick = [ - 'ppc_migration', - 'ppc_74xx', -] - -tests_ppc_system_thorough = [ - 'ppc_40p', - 'ppc_amiga', - 'ppc_bamboo', - 'ppc_mac', - 'ppc_mpc8544ds', - 'ppc_replay', - 'ppc_sam460ex', - 'ppc_tuxrun', - 'ppc_virtex_ml507', -] - -tests_ppc64_system_quick = [ - 'ppc64_migration', -] - -tests_ppc64_system_thorough = [ - 'ppc64_e500', - 'ppc64_hv', - 'ppc64_powernv', - 'ppc64_pseries', - 'ppc64_replay', - 'ppc64_reverse_debug', - 'ppc64_tuxrun', - 'ppc64_mac99', -] - tests_riscv32_system_quick = [ 'riscv32_migration', 'riscv_opensbi', diff --git a/tests/functional/ppc/meson.build b/tests/functional/ppc/meson.build new file mode 100644 index 0000000000000..3d562010d8cb0 --- /dev/null +++ b/tests/functional/ppc/meson.build @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_ppc_timeouts = { + '40p' : 240, +} + +tests_ppc_system_quick = [ + 'migration', + '74xx', +] + +tests_ppc_system_thorough = [ + '40p', + 'amiga', + 'bamboo', + 'mac', + 'mpc8544ds', + 'replay', + 'sam460ex', + 'tuxrun', + 'virtex_ml507', +] diff --git a/tests/functional/test_ppc_40p.py b/tests/functional/ppc/test_40p.py similarity index 100% rename from tests/functional/test_ppc_40p.py rename to tests/functional/ppc/test_40p.py diff --git a/tests/functional/test_ppc_74xx.py b/tests/functional/ppc/test_74xx.py similarity index 100% rename from tests/functional/test_ppc_74xx.py rename to tests/functional/ppc/test_74xx.py diff --git a/tests/functional/test_ppc_amiga.py b/tests/functional/ppc/test_amiga.py similarity index 100% rename from tests/functional/test_ppc_amiga.py rename to tests/functional/ppc/test_amiga.py diff --git a/tests/functional/test_ppc_bamboo.py b/tests/functional/ppc/test_bamboo.py similarity index 100% rename from tests/functional/test_ppc_bamboo.py rename to tests/functional/ppc/test_bamboo.py diff --git a/tests/functional/test_ppc_mac.py b/tests/functional/ppc/test_mac.py similarity index 100% rename from tests/functional/test_ppc_mac.py rename to tests/functional/ppc/test_mac.py diff --git a/tests/functional/test_ppc_migration.py b/tests/functional/ppc/test_migration.py similarity index 100% rename from tests/functional/test_ppc_migration.py rename to tests/functional/ppc/test_migration.py diff --git a/tests/functional/test_ppc_mpc8544ds.py b/tests/functional/ppc/test_mpc8544ds.py similarity index 100% rename from tests/functional/test_ppc_mpc8544ds.py rename to tests/functional/ppc/test_mpc8544ds.py diff --git a/tests/functional/test_ppc_replay.py b/tests/functional/ppc/test_replay.py similarity index 100% rename from tests/functional/test_ppc_replay.py rename to tests/functional/ppc/test_replay.py diff --git a/tests/functional/test_ppc_sam460ex.py b/tests/functional/ppc/test_sam460ex.py old mode 100644 new mode 100755 similarity index 100% rename from tests/functional/test_ppc_sam460ex.py rename to tests/functional/ppc/test_sam460ex.py diff --git a/tests/functional/test_ppc_tuxrun.py b/tests/functional/ppc/test_tuxrun.py similarity index 100% rename from tests/functional/test_ppc_tuxrun.py rename to tests/functional/ppc/test_tuxrun.py diff --git a/tests/functional/test_ppc_virtex_ml507.py b/tests/functional/ppc/test_virtex_ml507.py similarity index 100% rename from tests/functional/test_ppc_virtex_ml507.py rename to tests/functional/ppc/test_virtex_ml507.py diff --git a/tests/functional/ppc64/meson.build b/tests/functional/ppc64/meson.build new file mode 100644 index 0000000000000..842fe0fc715f6 --- /dev/null +++ b/tests/functional/ppc64/meson.build @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_ppc64_timeouts = { + 'hv' : 1000, + 'mac99' : 120, + 'powernv' : 480, + 'pseries' : 480, + 'replay' : 210, + 'tuxrun' : 420, +} + +tests_ppc64_system_quick = [ + 'migration', +] + +tests_ppc64_system_thorough = [ + 'e500', + 'hv', + 'mac99', + 'powernv', + 'pseries', + 'replay', + 'reverse_debug', + 'tuxrun', +] diff --git a/tests/functional/test_ppc64_e500.py b/tests/functional/ppc64/test_e500.py similarity index 100% rename from tests/functional/test_ppc64_e500.py rename to tests/functional/ppc64/test_e500.py diff --git a/tests/functional/test_ppc64_hv.py b/tests/functional/ppc64/test_hv.py similarity index 100% rename from tests/functional/test_ppc64_hv.py rename to tests/functional/ppc64/test_hv.py diff --git a/tests/functional/test_ppc64_mac99.py b/tests/functional/ppc64/test_mac99.py similarity index 100% rename from tests/functional/test_ppc64_mac99.py rename to tests/functional/ppc64/test_mac99.py diff --git a/tests/functional/test_ppc64_migration.py b/tests/functional/ppc64/test_migration.py similarity index 100% rename from tests/functional/test_ppc64_migration.py rename to tests/functional/ppc64/test_migration.py diff --git a/tests/functional/test_ppc64_powernv.py b/tests/functional/ppc64/test_powernv.py similarity index 100% rename from tests/functional/test_ppc64_powernv.py rename to tests/functional/ppc64/test_powernv.py diff --git a/tests/functional/test_ppc64_pseries.py b/tests/functional/ppc64/test_pseries.py similarity index 100% rename from tests/functional/test_ppc64_pseries.py rename to tests/functional/ppc64/test_pseries.py diff --git a/tests/functional/test_ppc64_replay.py b/tests/functional/ppc64/test_replay.py similarity index 100% rename from tests/functional/test_ppc64_replay.py rename to tests/functional/ppc64/test_replay.py diff --git a/tests/functional/test_ppc64_reverse_debug.py b/tests/functional/ppc64/test_reverse_debug.py similarity index 100% rename from tests/functional/test_ppc64_reverse_debug.py rename to tests/functional/ppc64/test_reverse_debug.py diff --git a/tests/functional/test_ppc64_tuxrun.py b/tests/functional/ppc64/test_tuxrun.py similarity index 100% rename from tests/functional/test_ppc64_tuxrun.py rename to tests/functional/ppc64/test_tuxrun.py From e1a8572a8d7a11627db22366d356d0daf0f6d559 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:54 +0200 Subject: [PATCH 0286/1794] tests/functional: Move riscv32/riscv64 tests into target-specific folders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The opensbi test is used for both, riscv32 and riscv64. Copy the main test to the riscv64 folder and add a simple wrapper to the riscv32 folder to be able to run it for that target, too. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-18-thuth@redhat.com> --- MAINTAINERS | 3 ++- tests/functional/meson.build | 25 ++----------------- tests/functional/riscv32/meson.build | 10 ++++++++ .../test_migration.py} | 0 tests/functional/riscv32/test_opensbi.py | 10 ++++++++ .../test_tuxrun.py} | 0 tests/functional/riscv64/meson.build | 15 +++++++++++ .../test_migration.py} | 0 .../test_opensbi.py} | 0 .../test_sifive_u.py} | 0 .../test_tuxrun.py} | 0 11 files changed, 39 insertions(+), 24 deletions(-) create mode 100644 tests/functional/riscv32/meson.build rename tests/functional/{test_riscv32_migration.py => riscv32/test_migration.py} (100%) create mode 100755 tests/functional/riscv32/test_opensbi.py rename tests/functional/{test_riscv32_tuxrun.py => riscv32/test_tuxrun.py} (100%) create mode 100644 tests/functional/riscv64/meson.build rename tests/functional/{test_riscv64_migration.py => riscv64/test_migration.py} (100%) rename tests/functional/{test_riscv_opensbi.py => riscv64/test_opensbi.py} (100%) rename tests/functional/{test_riscv64_sifive_u.py => riscv64/test_sifive_u.py} (100%) rename tests/functional/{test_riscv64_tuxrun.py => riscv64/test_tuxrun.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index b0d440cf75112..81262546c4b7a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -332,7 +332,8 @@ F: include/hw/riscv/ F: linux-user/host/riscv32/ F: linux-user/host/riscv64/ F: common-user/host/riscv* -F: tests/functional/test_riscv* +F: tests/functional/riscv32 +F: tests/functional/riscv64 F: tests/tcg/riscv64/ RISC-V XThead* extensions diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 3caeea5ebddd4..2d8f67fd94f2d 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -26,10 +26,8 @@ subdir('mips64el') subdir('or1k') subdir('ppc') subdir('ppc64') - -test_riscv64_timeouts = { - 'riscv64_tuxrun' : 120, -} +subdir('riscv32') +subdir('riscv64') test_s390x_timeouts = { 's390x_ccw_virtio' : 420, @@ -60,25 +58,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_riscv32_system_quick = [ - 'riscv32_migration', - 'riscv_opensbi', -] - -tests_riscv32_system_thorough = [ - 'riscv32_tuxrun', -] - -tests_riscv64_system_quick = [ - 'riscv64_migration', - 'riscv_opensbi', -] - -tests_riscv64_system_thorough = [ - 'riscv64_sifive_u', - 'riscv64_tuxrun', -] - tests_rx_system_thorough = [ 'rx_gdbsim', ] diff --git a/tests/functional/riscv32/meson.build b/tests/functional/riscv32/meson.build new file mode 100644 index 0000000000000..f3ebbb8db5d00 --- /dev/null +++ b/tests/functional/riscv32/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_riscv32_system_quick = [ + 'migration', + 'opensbi', +] + +tests_riscv32_system_thorough = [ + 'tuxrun', +] diff --git a/tests/functional/test_riscv32_migration.py b/tests/functional/riscv32/test_migration.py similarity index 100% rename from tests/functional/test_riscv32_migration.py rename to tests/functional/riscv32/test_migration.py diff --git a/tests/functional/riscv32/test_opensbi.py b/tests/functional/riscv32/test_opensbi.py new file mode 100755 index 0000000000000..d1ac706f0bb9f --- /dev/null +++ b/tests/functional/riscv32/test_opensbi.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Reuse the 64-bit OpenSBI test for RISC-V 32-bit machines + +from riscv64.test_opensbi import RiscvOpenSBI + +if __name__ == '__main__': + RiscvOpenSBI.main() diff --git a/tests/functional/test_riscv32_tuxrun.py b/tests/functional/riscv32/test_tuxrun.py similarity index 100% rename from tests/functional/test_riscv32_tuxrun.py rename to tests/functional/riscv32/test_tuxrun.py diff --git a/tests/functional/riscv64/meson.build b/tests/functional/riscv64/meson.build new file mode 100644 index 0000000000000..c1704d92751be --- /dev/null +++ b/tests/functional/riscv64/meson.build @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_riscv64_timeouts = { + 'tuxrun' : 120, +} + +tests_riscv64_system_quick = [ + 'migration', + 'opensbi', +] + +tests_riscv64_system_thorough = [ + 'sifive_u', + 'tuxrun', +] diff --git a/tests/functional/test_riscv64_migration.py b/tests/functional/riscv64/test_migration.py similarity index 100% rename from tests/functional/test_riscv64_migration.py rename to tests/functional/riscv64/test_migration.py diff --git a/tests/functional/test_riscv_opensbi.py b/tests/functional/riscv64/test_opensbi.py similarity index 100% rename from tests/functional/test_riscv_opensbi.py rename to tests/functional/riscv64/test_opensbi.py diff --git a/tests/functional/test_riscv64_sifive_u.py b/tests/functional/riscv64/test_sifive_u.py similarity index 100% rename from tests/functional/test_riscv64_sifive_u.py rename to tests/functional/riscv64/test_sifive_u.py diff --git a/tests/functional/test_riscv64_tuxrun.py b/tests/functional/riscv64/test_tuxrun.py similarity index 100% rename from tests/functional/test_riscv64_tuxrun.py rename to tests/functional/riscv64/test_tuxrun.py From bbb43ea3be12f09c284954b7b51ae8fea85ebe33 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:55 +0200 Subject: [PATCH 0287/1794] tests/functional: Move rx test into target-specific folders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the architecture specific test into an architecture specific subdirectory. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-19-thuth@redhat.com> --- MAINTAINERS | 2 +- tests/functional/meson.build | 5 +---- tests/functional/rx/meson.build | 5 +++++ tests/functional/{test_rx_gdbsim.py => rx/test_gdbsim.py} | 0 4 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 tests/functional/rx/meson.build rename tests/functional/{test_rx_gdbsim.py => rx/test_gdbsim.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 81262546c4b7a..c6410a5f5fdba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1721,7 +1721,7 @@ R: Yoshinori Sato S: Orphan F: docs/system/target-rx.rst F: hw/rx/rx-gdbsim.c -F: tests/functional/test_rx_gdbsim.py +F: tests/functional/rx/test_gdbsim.py SH4 Machines ------------ diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 2d8f67fd94f2d..7e7a6aa0c930f 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -28,6 +28,7 @@ subdir('ppc') subdir('ppc64') subdir('riscv32') subdir('riscv64') +subdir('rx') test_s390x_timeouts = { 's390x_ccw_virtio' : 420, @@ -58,10 +59,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_rx_system_thorough = [ - 'rx_gdbsim', -] - tests_s390x_system_thorough = [ 's390x_ccw_virtio', 's390x_pxelinux', diff --git a/tests/functional/rx/meson.build b/tests/functional/rx/meson.build new file mode 100644 index 0000000000000..6af83a9f23fb5 --- /dev/null +++ b/tests/functional/rx/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_rx_system_thorough = [ + 'gdbsim', +] diff --git a/tests/functional/test_rx_gdbsim.py b/tests/functional/rx/test_gdbsim.py similarity index 100% rename from tests/functional/test_rx_gdbsim.py rename to tests/functional/rx/test_gdbsim.py From 5e50a3c9c0f31daf53962914855accdc97a3109b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:56 +0200 Subject: [PATCH 0288/1794] tests/functional: Move s390x tests into target-specific folders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the s390x tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-20-thuth@redhat.com> --- MAINTAINERS | 6 +++--- tests/functional/meson.build | 13 +------------ tests/functional/s390x/meson.build | 13 +++++++++++++ .../test_ccw_virtio.py} | 0 .../test_pxelinux.py} | 0 .../{test_s390x_replay.py => s390x/test_replay.py} | 0 .../test_topology.py} | 0 .../{test_s390x_tuxrun.py => s390x/test_tuxrun.py} | 0 8 files changed, 17 insertions(+), 15 deletions(-) create mode 100644 tests/functional/s390x/meson.build rename tests/functional/{test_s390x_ccw_virtio.py => s390x/test_ccw_virtio.py} (100%) rename tests/functional/{test_s390x_pxelinux.py => s390x/test_pxelinux.py} (100%) rename tests/functional/{test_s390x_replay.py => s390x/test_replay.py} (100%) rename tests/functional/{test_s390x_topology.py => s390x/test_topology.py} (100%) rename tests/functional/{test_s390x_tuxrun.py => s390x/test_tuxrun.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index c6410a5f5fdba..4a55a20f6a587 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1797,7 +1797,7 @@ S: Supported F: hw/s390x/ F: include/hw/s390x/ F: configs/devices/s390x-softmmu/default.mak -F: tests/functional/test_s390x_* +F: tests/functional/s390x T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org @@ -1811,7 +1811,7 @@ F: hw/s390x/ipl.* F: pc-bios/s390-ccw/ F: pc-bios/s390-ccw.img F: docs/devel/s390-dasd-ipl.rst -F: tests/functional/test_s390x_pxelinux.py +F: tests/functional/s390x/test_pxelinux.py T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org @@ -1865,7 +1865,7 @@ F: hw/s390x/cpu-topology.c F: target/s390x/kvm/stsi-topology.c F: docs/devel/s390-cpu-topology.rst F: docs/system/s390x/cpu-topology.rst -F: tests/functional/test_s390x_topology.py +F: tests/functional/s390x/test_topology.py X86 Machines ------------ diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 7e7a6aa0c930f..abaa4e00fcadf 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -29,10 +29,7 @@ subdir('ppc64') subdir('riscv32') subdir('riscv64') subdir('rx') - -test_s390x_timeouts = { - 's390x_ccw_virtio' : 420, -} +subdir('s390x') test_sh4_timeouts = { 'sh4_tuxrun' : 240, @@ -59,14 +56,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_s390x_system_thorough = [ - 's390x_ccw_virtio', - 's390x_pxelinux', - 's390x_replay', - 's390x_topology', - 's390x_tuxrun', -] - tests_sh4_system_thorough = [ 'sh4_r2d', 'sh4_tuxrun', diff --git a/tests/functional/s390x/meson.build b/tests/functional/s390x/meson.build new file mode 100644 index 0000000000000..030b116039cad --- /dev/null +++ b/tests/functional/s390x/meson.build @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_s390x_timeouts = { + 'ccw_virtio' : 420, +} + +tests_s390x_system_thorough = [ + 'ccw_virtio', + 'pxelinux', + 'replay', + 'topology', + 'tuxrun', +] diff --git a/tests/functional/test_s390x_ccw_virtio.py b/tests/functional/s390x/test_ccw_virtio.py similarity index 100% rename from tests/functional/test_s390x_ccw_virtio.py rename to tests/functional/s390x/test_ccw_virtio.py diff --git a/tests/functional/test_s390x_pxelinux.py b/tests/functional/s390x/test_pxelinux.py similarity index 100% rename from tests/functional/test_s390x_pxelinux.py rename to tests/functional/s390x/test_pxelinux.py diff --git a/tests/functional/test_s390x_replay.py b/tests/functional/s390x/test_replay.py similarity index 100% rename from tests/functional/test_s390x_replay.py rename to tests/functional/s390x/test_replay.py diff --git a/tests/functional/test_s390x_topology.py b/tests/functional/s390x/test_topology.py similarity index 100% rename from tests/functional/test_s390x_topology.py rename to tests/functional/s390x/test_topology.py diff --git a/tests/functional/test_s390x_tuxrun.py b/tests/functional/s390x/test_tuxrun.py similarity index 100% rename from tests/functional/test_s390x_tuxrun.py rename to tests/functional/s390x/test_tuxrun.py From 58e95a05dec8bb0c739a2b7192b6d0a96d1a99a5 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:57 +0200 Subject: [PATCH 0289/1794] tests/functional: Move sh4/sh4eb tests into target-specific folders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the sh4 tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-21-thuth@redhat.com> --- MAINTAINERS | 4 ++-- tests/functional/meson.build | 15 ++------------- tests/functional/sh4/meson.build | 10 ++++++++++ .../{test_sh4_r2d.py => sh4/test_r2d.py} | 0 .../{test_sh4_tuxrun.py => sh4/test_tuxrun.py} | 0 tests/functional/sh4eb/meson.build | 5 +++++ .../{test_sh4eb_r2d.py => sh4eb/test_r2d.py} | 0 7 files changed, 19 insertions(+), 15 deletions(-) create mode 100644 tests/functional/sh4/meson.build rename tests/functional/{test_sh4_r2d.py => sh4/test_r2d.py} (100%) rename tests/functional/{test_sh4_tuxrun.py => sh4/test_tuxrun.py} (100%) create mode 100644 tests/functional/sh4eb/meson.build rename tests/functional/{test_sh4eb_r2d.py => sh4eb/test_r2d.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 4a55a20f6a587..eddec0058e0ad 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1736,8 +1736,8 @@ F: hw/pci-host/sh_pci.c F: hw/timer/sh_timer.c F: include/hw/sh4/sh_intc.h F: include/hw/timer/tmu012.h -F: tests/functional/test_sh4*_r2d.py -F: tests/functional/test_sh4_tuxrun.py +F: tests/functional/sh4*/test_r2d.py +F: tests/functional/sh4/test_tuxrun.py SPARC Machines -------------- diff --git a/tests/functional/meson.build b/tests/functional/meson.build index abaa4e00fcadf..ce713509e329f 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -30,10 +30,8 @@ subdir('riscv32') subdir('riscv64') subdir('rx') subdir('s390x') - -test_sh4_timeouts = { - 'sh4_tuxrun' : 240, -} +subdir('sh4') +subdir('sh4eb') test_x86_64_timeouts = { 'acpi_bits' : 420, @@ -56,15 +54,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_sh4_system_thorough = [ - 'sh4_r2d', - 'sh4_tuxrun', -] - -tests_sh4eb_system_thorough = [ - 'sh4eb_r2d', -] - tests_sparc_system_quick = [ 'sparc_migration', ] diff --git a/tests/functional/sh4/meson.build b/tests/functional/sh4/meson.build new file mode 100644 index 0000000000000..56f824e1e717a --- /dev/null +++ b/tests/functional/sh4/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_sh4_timeouts = { + 'tuxrun' : 240, +} + +tests_sh4_system_thorough = [ + 'r2d', + 'tuxrun', +] diff --git a/tests/functional/test_sh4_r2d.py b/tests/functional/sh4/test_r2d.py similarity index 100% rename from tests/functional/test_sh4_r2d.py rename to tests/functional/sh4/test_r2d.py diff --git a/tests/functional/test_sh4_tuxrun.py b/tests/functional/sh4/test_tuxrun.py similarity index 100% rename from tests/functional/test_sh4_tuxrun.py rename to tests/functional/sh4/test_tuxrun.py diff --git a/tests/functional/sh4eb/meson.build b/tests/functional/sh4eb/meson.build new file mode 100644 index 0000000000000..25e9a6e40410f --- /dev/null +++ b/tests/functional/sh4eb/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_sh4eb_system_thorough = [ + 'r2d', +] diff --git a/tests/functional/test_sh4eb_r2d.py b/tests/functional/sh4eb/test_r2d.py similarity index 100% rename from tests/functional/test_sh4eb_r2d.py rename to tests/functional/sh4eb/test_r2d.py From e76291a65457b1fe18b6bf7a90b1d022fa15bf9d Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:58 +0200 Subject: [PATCH 0290/1794] tests/functional: Move sparc/sparc64 tests into target-specific folders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the sparc tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-22-thuth@redhat.com> --- MAINTAINERS | 6 +++--- tests/functional/meson.build | 20 ++----------------- tests/functional/sparc/meson.build | 10 ++++++++++ .../test_migration.py} | 0 .../test_replay.py} | 0 .../test_sun4m.py} | 0 tests/functional/sparc64/meson.build | 10 ++++++++++ .../test_migration.py} | 0 .../test_sun4u.py} | 0 .../test_tuxrun.py} | 0 10 files changed, 25 insertions(+), 21 deletions(-) create mode 100644 tests/functional/sparc/meson.build rename tests/functional/{test_sparc_migration.py => sparc/test_migration.py} (100%) rename tests/functional/{test_sparc_replay.py => sparc/test_replay.py} (100%) rename tests/functional/{test_sparc_sun4m.py => sparc/test_sun4m.py} (100%) create mode 100644 tests/functional/sparc64/meson.build rename tests/functional/{test_sparc64_migration.py => sparc64/test_migration.py} (100%) rename tests/functional/{test_sparc64_sun4u.py => sparc64/test_sun4u.py} (100%) rename tests/functional/{test_sparc64_tuxrun.py => sparc64/test_tuxrun.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index eddec0058e0ad..b46445ff5c054 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1755,7 +1755,7 @@ F: include/hw/nvram/sun_nvram.h F: include/hw/sparc/sparc32_dma.h F: include/hw/sparc/sun4m_iommu.h F: pc-bios/openbios-sparc32 -F: tests/functional/test_sparc_sun4m.py +F: tests/functional/sparc/test_sun4m.py Sun4u M: Mark Cave-Ayland @@ -1768,8 +1768,8 @@ F: include/hw/pci-host/sabre.h F: hw/pci-bridge/simba.c F: include/hw/pci-bridge/simba.h F: pc-bios/openbios-sparc64 -F: tests/functional/test_sparc64_sun4u.py -F: tests/functional/test_sparc64_tuxrun.py +F: tests/functional/sparc64/test_sun4u.py +F: tests/functional/sparc64/test_tuxrun.py Sun4v M: Artyom Tarasenko diff --git a/tests/functional/meson.build b/tests/functional/meson.build index ce713509e329f..00d18dba3cec6 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -32,6 +32,8 @@ subdir('rx') subdir('s390x') subdir('sh4') subdir('sh4eb') +subdir('sparc') +subdir('sparc64') test_x86_64_timeouts = { 'acpi_bits' : 420, @@ -54,24 +56,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_sparc_system_quick = [ - 'sparc_migration', -] - -tests_sparc_system_thorough = [ - 'sparc_replay', - 'sparc_sun4m', -] - -tests_sparc64_system_quick = [ - 'sparc64_migration', -] - -tests_sparc64_system_thorough = [ - 'sparc64_sun4u', - 'sparc64_tuxrun', -] - tests_x86_64_system_quick = [ 'cpu_queries', 'mem_addr_space', diff --git a/tests/functional/sparc/meson.build b/tests/functional/sparc/meson.build new file mode 100644 index 0000000000000..88732becd8123 --- /dev/null +++ b/tests/functional/sparc/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_sparc_system_quick = [ + 'migration', +] + +tests_sparc_system_thorough = [ + 'replay', + 'sun4m', +] diff --git a/tests/functional/test_sparc_migration.py b/tests/functional/sparc/test_migration.py similarity index 100% rename from tests/functional/test_sparc_migration.py rename to tests/functional/sparc/test_migration.py diff --git a/tests/functional/test_sparc_replay.py b/tests/functional/sparc/test_replay.py similarity index 100% rename from tests/functional/test_sparc_replay.py rename to tests/functional/sparc/test_replay.py diff --git a/tests/functional/test_sparc_sun4m.py b/tests/functional/sparc/test_sun4m.py similarity index 100% rename from tests/functional/test_sparc_sun4m.py rename to tests/functional/sparc/test_sun4m.py diff --git a/tests/functional/sparc64/meson.build b/tests/functional/sparc64/meson.build new file mode 100644 index 0000000000000..2e04e7d4f3dfa --- /dev/null +++ b/tests/functional/sparc64/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_sparc64_system_quick = [ + 'migration', +] + +tests_sparc64_system_thorough = [ + 'sun4u', + 'tuxrun', +] diff --git a/tests/functional/test_sparc64_migration.py b/tests/functional/sparc64/test_migration.py similarity index 100% rename from tests/functional/test_sparc64_migration.py rename to tests/functional/sparc64/test_migration.py diff --git a/tests/functional/test_sparc64_sun4u.py b/tests/functional/sparc64/test_sun4u.py similarity index 100% rename from tests/functional/test_sparc64_sun4u.py rename to tests/functional/sparc64/test_sun4u.py diff --git a/tests/functional/test_sparc64_tuxrun.py b/tests/functional/sparc64/test_tuxrun.py similarity index 100% rename from tests/functional/test_sparc64_tuxrun.py rename to tests/functional/sparc64/test_tuxrun.py From 1917d47dd78adbd30abd462712458d0a0b583308 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:59 +0200 Subject: [PATCH 0291/1794] tests/functional: Move x86_64 tests into target-specific folder The tests/functional folder has become quite crowded, thus move the x86_64 tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-23-thuth@redhat.com> --- MAINTAINERS | 32 ++++++++-------- tests/functional/meson.build | 37 +------------------ tests/functional/x86_64/meson.build | 37 +++++++++++++++++++ .../functional/{ => x86_64}/test_acpi_bits.py | 0 .../test_cpu_model_versions.py} | 0 .../{ => x86_64}/test_cpu_queries.py | 0 .../test_hotplug_blk.py} | 0 .../test_hotplug_cpu.py} | 0 .../{ => x86_64}/test_intel_iommu.py | 0 .../test_kvm_xen.py} | 0 .../{ => x86_64}/test_linux_initrd.py | 0 .../{ => x86_64}/test_mem_addr_space.py | 0 tests/functional/{ => x86_64}/test_memlock.py | 0 .../test_migration.py} | 0 .../test_multiprocess.py} | 0 .../{ => x86_64}/test_netdev_ethtool.py | 0 .../{ => x86_64}/test_pc_cpu_hotplug_props.py | 0 .../test_replay.py} | 0 .../test_reverse_debug.py} | 0 .../test_tuxrun.py} | 0 .../{ => x86_64}/test_virtio_balloon.py | 0 .../{ => x86_64}/test_virtio_gpu.py | 0 .../{ => x86_64}/test_virtio_version.py | 0 23 files changed, 55 insertions(+), 51 deletions(-) create mode 100644 tests/functional/x86_64/meson.build rename tests/functional/{ => x86_64}/test_acpi_bits.py (100%) rename tests/functional/{test_x86_cpu_model_versions.py => x86_64/test_cpu_model_versions.py} (100%) rename tests/functional/{ => x86_64}/test_cpu_queries.py (100%) rename tests/functional/{test_x86_64_hotplug_blk.py => x86_64/test_hotplug_blk.py} (100%) rename tests/functional/{test_x86_64_hotplug_cpu.py => x86_64/test_hotplug_cpu.py} (100%) rename tests/functional/{ => x86_64}/test_intel_iommu.py (100%) rename tests/functional/{test_x86_64_kvm_xen.py => x86_64/test_kvm_xen.py} (100%) rename tests/functional/{ => x86_64}/test_linux_initrd.py (100%) rename tests/functional/{ => x86_64}/test_mem_addr_space.py (100%) rename tests/functional/{ => x86_64}/test_memlock.py (100%) rename tests/functional/{test_x86_64_migration.py => x86_64/test_migration.py} (100%) rename tests/functional/{test_x86_64_multiprocess.py => x86_64/test_multiprocess.py} (100%) rename tests/functional/{ => x86_64}/test_netdev_ethtool.py (100%) rename tests/functional/{ => x86_64}/test_pc_cpu_hotplug_props.py (100%) rename tests/functional/{test_x86_64_replay.py => x86_64/test_replay.py} (100%) rename tests/functional/{test_x86_64_reverse_debug.py => x86_64/test_reverse_debug.py} (100%) rename tests/functional/{test_x86_64_tuxrun.py => x86_64/test_tuxrun.py} (100%) rename tests/functional/{ => x86_64}/test_virtio_balloon.py (100%) rename tests/functional/{ => x86_64}/test_virtio_gpu.py (100%) rename tests/functional/{ => x86_64}/test_virtio_version.py (100%) diff --git a/MAINTAINERS b/MAINTAINERS index b46445ff5c054..7b1a94f696cba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -147,6 +147,7 @@ F: target/i386/Kconfig F: target/i386/meson.build F: tools/i386/ F: tests/functional/i386/ +F: tests/functional/x86_64/ Guest CPU cores (TCG) --------------------- @@ -483,7 +484,7 @@ F: docs/system/i386/sgx.rst F: target/i386/kvm/ F: target/i386/sev* F: scripts/kvm/vmxcap -F: tests/functional/test_x86_64_hotplug_cpu.py +F: tests/functional/x86_64/test_hotplug_cpu.py Xen emulation on X86 KVM CPUs M: David Woodhouse @@ -492,7 +493,7 @@ S: Supported F: include/system/kvm_xen.h F: target/i386/kvm/xen* F: hw/i386/kvm/xen* -F: tests/functional/test_x86_64_kvm_xen.py +F: tests/functional/x86_64/test_kvm_xen.py Guest CPU Cores (other accelerators) ------------------------------------ @@ -1894,11 +1895,11 @@ F: include/hw/isa/apm.h F: tests/unit/test-x86-topo.c F: tests/qtest/test-x86-cpuid-compat.c F: tests/functional/i386/test_tuxrun.py -F: tests/functional/test_linux_initrd.py -F: tests/functional/test_mem_addr_space.py -F: tests/functional/test_pc_cpu_hotplug_props.py -F: tests/functional/test_x86_64_tuxrun.py -F: tests/functional/test_x86_cpu_model_versions.py +F: tests/functional/x86_64/test_linux_initrd.py +F: tests/functional/x86_64/test_mem_addr_space.py +F: tests/functional/x86_64/test_pc_cpu_hotplug_props.py +F: tests/functional/x86_64/test_tuxrun.py +F: tests/functional/x86_64/test_cpu_model_versions.py PC Chipset M: Michael S. Tsirkin @@ -1974,7 +1975,7 @@ F: include/hw/boards.h F: include/hw/core/cpu.h F: include/hw/cpu/cluster.h F: include/system/numa.h -F: tests/functional/test_cpu_queries.py +F: tests/functional/x86_64/test_cpu_queries.py F: tests/functional/test_empty_cpu_model.py F: tests/unit/test-smp-parse.c T: git https://gitlab.com/ehabkost/qemu.git machine-next @@ -2159,7 +2160,7 @@ M: Ani Sinha M: Michael S. Tsirkin S: Supported F: tests/functional/acpi-bits/* -F: tests/functional/test_acpi_bits.py +F: tests/functional/x86_64/test_acpi_bits.py F: docs/devel/testing/acpi-bits.rst ACPI/HEST/GHES @@ -2345,7 +2346,7 @@ F: net/vhost-user.c F: include/hw/virtio/ F: docs/devel/virtio* F: docs/devel/migration/virtio.rst -F: tests/functional/test_virtio_version.py +F: tests/functional/x86_64/test_virtio_version.py virtio-balloon M: Michael S. Tsirkin @@ -2357,7 +2358,7 @@ F: include/hw/virtio/virtio-balloon.h F: system/balloon.c F: include/system/balloon.h F: tests/qtest/virtio-balloon-test.c -F: tests/functional/test_virtio_balloon.py +F: tests/functional/x86_64/test_virtio_balloon.py virtio-9p M: Christian Schoenebeck @@ -2380,7 +2381,7 @@ F: hw/block/virtio-blk.c F: hw/block/dataplane/* F: include/hw/virtio/virtio-blk-common.h F: tests/qtest/virtio-blk-test.c -F: tests/functional/test_x86_64_hotplug_blk.py +F: tests/functional/x86_64/test_hotplug_blk.py T: git https://github.com/stefanha/qemu.git block virtio-ccw @@ -2604,7 +2605,7 @@ R: Sriram Yagnaraman S: Odd Fixes F: docs/system/devices/igb.rst F: hw/net/igb* -F: tests/functional/test_netdev_ethtool.py +F: tests/functional/x86_64/test_netdev_ethtool.py F: tests/qtest/igb-test.c F: tests/qtest/libqos/igb.c @@ -2713,6 +2714,7 @@ F: hw/display/virtio-vga.* F: include/hw/virtio/virtio-gpu.h F: docs/system/devices/virtio-gpu.rst F: tests/functional/aarch64/test_virt_gpu.py +F: tests/functional/x86_64/test_virtio_gpu.py vhost-user-blk M: Raphael Norwitz @@ -3856,7 +3858,7 @@ S: Supported F: hw/i386/intel_iommu.c F: hw/i386/intel_iommu_internal.h F: include/hw/i386/intel_iommu.h -F: tests/functional/test_intel_iommu.py +F: tests/functional/x86_64/test_intel_iommu.py F: tests/qtest/intel-iommu-test.c AMD-Vi Emulation @@ -4330,7 +4332,7 @@ F: scripts/ci/ F: tests/docker/ F: tests/vm/ F: tests/lcitool/ -F: tests/functional/test_*_tuxrun.py +F: tests/functional/*/test_tuxrun.py F: scripts/archive-source.sh F: docs/devel/testing/ci* F: docs/devel/testing/main.rst diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 00d18dba3cec6..34e30239a6b2b 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -34,15 +34,7 @@ subdir('sh4') subdir('sh4eb') subdir('sparc') subdir('sparc64') - -test_x86_64_timeouts = { - 'acpi_bits' : 420, - 'intel_iommu': 300, - 'netdev_ethtool' : 180, - 'virtio_balloon': 120, - 'x86_64_kvm_xen' : 180, - 'x86_64_replay' : 480, -} +subdir('x86_64') tests_generic_system = [ 'empty_cpu_model', @@ -56,33 +48,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_x86_64_system_quick = [ - 'cpu_queries', - 'mem_addr_space', - 'x86_64_migration', - 'pc_cpu_hotplug_props', - 'virtio_version', - 'x86_cpu_model_versions', - 'vnc', - 'memlock', -] - -tests_x86_64_system_thorough = [ - 'acpi_bits', - 'intel_iommu', - 'linux_initrd', - 'x86_64_multiprocess', - 'netdev_ethtool', - 'virtio_balloon', - 'virtio_gpu', - 'x86_64_hotplug_blk', - 'x86_64_hotplug_cpu', - 'x86_64_kvm_xen', - 'x86_64_replay', - 'x86_64_reverse_debug', - 'x86_64_tuxrun', -] - tests_xtensa_system_thorough = [ 'xtensa_lx60', 'xtensa_replay', diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build new file mode 100644 index 0000000000000..696a9ecab4281 --- /dev/null +++ b/tests/functional/x86_64/meson.build @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_x86_64_timeouts = { + 'acpi_bits' : 420, + 'intel_iommu': 300, + 'kvm_xen' : 180, + 'netdev_ethtool' : 180, + 'replay' : 480, + 'virtio_balloon': 120, +} + +tests_x86_64_system_quick = [ + 'cpu_model_versions', + 'cpu_queries', + 'mem_addr_space', + 'migration', + 'pc_cpu_hotplug_props', + 'virtio_version', + 'vnc', + 'memlock', +] + +tests_x86_64_system_thorough = [ + 'acpi_bits', + 'hotplug_blk', + 'hotplug_cpu', + 'intel_iommu', + 'kvm_xen', + 'linux_initrd', + 'multiprocess', + 'netdev_ethtool', + 'replay', + 'reverse_debug', + 'tuxrun', + 'virtio_balloon', + 'virtio_gpu', +] diff --git a/tests/functional/test_acpi_bits.py b/tests/functional/x86_64/test_acpi_bits.py similarity index 100% rename from tests/functional/test_acpi_bits.py rename to tests/functional/x86_64/test_acpi_bits.py diff --git a/tests/functional/test_x86_cpu_model_versions.py b/tests/functional/x86_64/test_cpu_model_versions.py similarity index 100% rename from tests/functional/test_x86_cpu_model_versions.py rename to tests/functional/x86_64/test_cpu_model_versions.py diff --git a/tests/functional/test_cpu_queries.py b/tests/functional/x86_64/test_cpu_queries.py similarity index 100% rename from tests/functional/test_cpu_queries.py rename to tests/functional/x86_64/test_cpu_queries.py diff --git a/tests/functional/test_x86_64_hotplug_blk.py b/tests/functional/x86_64/test_hotplug_blk.py similarity index 100% rename from tests/functional/test_x86_64_hotplug_blk.py rename to tests/functional/x86_64/test_hotplug_blk.py diff --git a/tests/functional/test_x86_64_hotplug_cpu.py b/tests/functional/x86_64/test_hotplug_cpu.py similarity index 100% rename from tests/functional/test_x86_64_hotplug_cpu.py rename to tests/functional/x86_64/test_hotplug_cpu.py diff --git a/tests/functional/test_intel_iommu.py b/tests/functional/x86_64/test_intel_iommu.py similarity index 100% rename from tests/functional/test_intel_iommu.py rename to tests/functional/x86_64/test_intel_iommu.py diff --git a/tests/functional/test_x86_64_kvm_xen.py b/tests/functional/x86_64/test_kvm_xen.py similarity index 100% rename from tests/functional/test_x86_64_kvm_xen.py rename to tests/functional/x86_64/test_kvm_xen.py diff --git a/tests/functional/test_linux_initrd.py b/tests/functional/x86_64/test_linux_initrd.py similarity index 100% rename from tests/functional/test_linux_initrd.py rename to tests/functional/x86_64/test_linux_initrd.py diff --git a/tests/functional/test_mem_addr_space.py b/tests/functional/x86_64/test_mem_addr_space.py similarity index 100% rename from tests/functional/test_mem_addr_space.py rename to tests/functional/x86_64/test_mem_addr_space.py diff --git a/tests/functional/test_memlock.py b/tests/functional/x86_64/test_memlock.py similarity index 100% rename from tests/functional/test_memlock.py rename to tests/functional/x86_64/test_memlock.py diff --git a/tests/functional/test_x86_64_migration.py b/tests/functional/x86_64/test_migration.py similarity index 100% rename from tests/functional/test_x86_64_migration.py rename to tests/functional/x86_64/test_migration.py diff --git a/tests/functional/test_x86_64_multiprocess.py b/tests/functional/x86_64/test_multiprocess.py similarity index 100% rename from tests/functional/test_x86_64_multiprocess.py rename to tests/functional/x86_64/test_multiprocess.py diff --git a/tests/functional/test_netdev_ethtool.py b/tests/functional/x86_64/test_netdev_ethtool.py similarity index 100% rename from tests/functional/test_netdev_ethtool.py rename to tests/functional/x86_64/test_netdev_ethtool.py diff --git a/tests/functional/test_pc_cpu_hotplug_props.py b/tests/functional/x86_64/test_pc_cpu_hotplug_props.py similarity index 100% rename from tests/functional/test_pc_cpu_hotplug_props.py rename to tests/functional/x86_64/test_pc_cpu_hotplug_props.py diff --git a/tests/functional/test_x86_64_replay.py b/tests/functional/x86_64/test_replay.py similarity index 100% rename from tests/functional/test_x86_64_replay.py rename to tests/functional/x86_64/test_replay.py diff --git a/tests/functional/test_x86_64_reverse_debug.py b/tests/functional/x86_64/test_reverse_debug.py similarity index 100% rename from tests/functional/test_x86_64_reverse_debug.py rename to tests/functional/x86_64/test_reverse_debug.py diff --git a/tests/functional/test_x86_64_tuxrun.py b/tests/functional/x86_64/test_tuxrun.py similarity index 100% rename from tests/functional/test_x86_64_tuxrun.py rename to tests/functional/x86_64/test_tuxrun.py diff --git a/tests/functional/test_virtio_balloon.py b/tests/functional/x86_64/test_virtio_balloon.py similarity index 100% rename from tests/functional/test_virtio_balloon.py rename to tests/functional/x86_64/test_virtio_balloon.py diff --git a/tests/functional/test_virtio_gpu.py b/tests/functional/x86_64/test_virtio_gpu.py similarity index 100% rename from tests/functional/test_virtio_gpu.py rename to tests/functional/x86_64/test_virtio_gpu.py diff --git a/tests/functional/test_virtio_version.py b/tests/functional/x86_64/test_virtio_version.py similarity index 100% rename from tests/functional/test_virtio_version.py rename to tests/functional/x86_64/test_virtio_version.py From e365d26e42281aae3dcb47aa63c862892efadb0c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:24:00 +0200 Subject: [PATCH 0292/1794] tests/functional: Move xtensa tests into target-specific folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the xtensa tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-24-thuth@redhat.com> --- MAINTAINERS | 2 +- tests/functional/meson.build | 6 +----- tests/functional/xtensa/meson.build | 6 ++++++ .../functional/{test_xtensa_lx60.py => xtensa/test_lx60.py} | 0 .../{test_xtensa_replay.py => xtensa/test_replay.py} | 0 5 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 tests/functional/xtensa/meson.build rename tests/functional/{test_xtensa_lx60.py => xtensa/test_lx60.py} (100%) rename tests/functional/{test_xtensa_replay.py => xtensa/test_replay.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 7b1a94f696cba..792d2d6f2def9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2005,7 +2005,7 @@ S: Maintained F: hw/xtensa/xtfpga.c F: hw/net/opencores_eth.c F: include/hw/xtensa/mx_pic.h -F: tests/functional/test_xtensa_lx60.py +F: tests/functional/xtensa/test_lx60.py Devices ------- diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 34e30239a6b2b..b1eec16add81c 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -35,6 +35,7 @@ subdir('sh4eb') subdir('sparc') subdir('sparc64') subdir('x86_64') +subdir('xtensa') tests_generic_system = [ 'empty_cpu_model', @@ -48,11 +49,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_xtensa_system_thorough = [ - 'xtensa_lx60', - 'xtensa_replay', -] - precache_all = [] foreach speed : ['quick', 'thorough'] foreach dir : target_dirs diff --git a/tests/functional/xtensa/meson.build b/tests/functional/xtensa/meson.build new file mode 100644 index 0000000000000..d61d82a1356f9 --- /dev/null +++ b/tests/functional/xtensa/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_xtensa_system_thorough = [ + 'lx60', + 'replay', +] diff --git a/tests/functional/test_xtensa_lx60.py b/tests/functional/xtensa/test_lx60.py similarity index 100% rename from tests/functional/test_xtensa_lx60.py rename to tests/functional/xtensa/test_lx60.py diff --git a/tests/functional/test_xtensa_replay.py b/tests/functional/xtensa/test_replay.py similarity index 100% rename from tests/functional/test_xtensa_replay.py rename to tests/functional/xtensa/test_replay.py From 0137f60b37c5678e9fa4971fd7e4f07afed33294 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:24:01 +0200 Subject: [PATCH 0293/1794] tests/functional: Move the generic tests to a subfolder This also removes the line for using tests from the main folder since we do not have any tests left here. And while we're at it, also mark the vnc test as generic now since it is not specific to x86. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-25-thuth@redhat.com> --- MAINTAINERS | 8 ++++---- tests/functional/generic/meson.build | 14 ++++++++++++++ .../{ => generic}/test_empty_cpu_model.py | 0 .../{ => generic}/test_info_usernet.py | 0 tests/functional/{ => generic}/test_version.py | 0 tests/functional/{ => generic}/test_vnc.py | 0 tests/functional/meson.build | 17 ++--------------- tests/functional/x86_64/meson.build | 1 - 8 files changed, 20 insertions(+), 20 deletions(-) create mode 100644 tests/functional/generic/meson.build rename tests/functional/{ => generic}/test_empty_cpu_model.py (100%) rename tests/functional/{ => generic}/test_info_usernet.py (100%) rename tests/functional/{ => generic}/test_version.py (100%) rename tests/functional/{ => generic}/test_vnc.py (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 792d2d6f2def9..625fe67b41d90 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1976,7 +1976,7 @@ F: include/hw/core/cpu.h F: include/hw/cpu/cluster.h F: include/system/numa.h F: tests/functional/x86_64/test_cpu_queries.py -F: tests/functional/test_empty_cpu_model.py +F: tests/functional/generic/test_empty_cpu_model.py F: tests/unit/test-smp-parse.c T: git https://gitlab.com/ehabkost/qemu.git machine-next @@ -2197,7 +2197,7 @@ S: Odd Fixes F: hw/net/ F: include/hw/net/ F: tests/qtest/virtio-net-test.c -F: tests/functional/test_info_usernet.py +F: tests/functional/generic/test_info_usernet.py F: docs/system/virtio-net-failover.rst T: git https://github.com/jasowang/qemu.git net @@ -3134,7 +3134,7 @@ S: Supported F: include/qemu/option.h F: tests/unit/test-keyval.c F: tests/unit/test-qemu-opts.c -F: tests/functional/test_version.py +F: tests/functional/generic/test_version.py F: util/keyval.c F: util/qemu-option.c @@ -3252,7 +3252,7 @@ F: include/ui/ F: qapi/ui.json F: util/drm.c F: docs/devel/ui.rst -F: tests/functional/test_vnc.py +F: tests/functional/generic/test_vnc.py Cocoa graphics M: Peter Maydell diff --git a/tests/functional/generic/meson.build b/tests/functional/generic/meson.build new file mode 100644 index 0000000000000..013cc96fbf889 --- /dev/null +++ b/tests/functional/generic/meson.build @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_generic_system = [ + 'empty_cpu_model', + 'info_usernet', + 'version', + 'vnc', +] + +tests_generic_linuxuser = [ +] + +tests_generic_bsduser = [ +] diff --git a/tests/functional/test_empty_cpu_model.py b/tests/functional/generic/test_empty_cpu_model.py similarity index 100% rename from tests/functional/test_empty_cpu_model.py rename to tests/functional/generic/test_empty_cpu_model.py diff --git a/tests/functional/test_info_usernet.py b/tests/functional/generic/test_info_usernet.py similarity index 100% rename from tests/functional/test_info_usernet.py rename to tests/functional/generic/test_info_usernet.py diff --git a/tests/functional/test_version.py b/tests/functional/generic/test_version.py similarity index 100% rename from tests/functional/test_version.py rename to tests/functional/generic/test_version.py diff --git a/tests/functional/test_vnc.py b/tests/functional/generic/test_vnc.py similarity index 100% rename from tests/functional/test_vnc.py rename to tests/functional/generic/test_vnc.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index b1eec16add81c..2a0c5aa141807 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -36,18 +36,7 @@ subdir('sparc') subdir('sparc64') subdir('x86_64') subdir('xtensa') - -tests_generic_system = [ - 'empty_cpu_model', - 'info_usernet', - 'version', -] - -tests_generic_linuxuser = [ -] - -tests_generic_bsduser = [ -] +subdir('generic') precache_all = [] foreach speed : ['quick', 'thorough'] @@ -90,9 +79,7 @@ foreach speed : ['quick', 'thorough'] foreach test : target_tests testname = '@0@-@1@'.format(target_base, test) - if fs.exists('test_' + test + '.py') - testfile = 'test_' + test + '.py' - elif fs.exists('generic' / 'test_' + test + '.py') + if fs.exists('generic' / 'test_' + test + '.py') testfile = 'generic' / 'test_' + test + '.py' else testfile = target_base / 'test_' + test + '.py' diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build index 696a9ecab4281..d0b4667bb8abf 100644 --- a/tests/functional/x86_64/meson.build +++ b/tests/functional/x86_64/meson.build @@ -16,7 +16,6 @@ tests_x86_64_system_quick = [ 'migration', 'pc_cpu_hotplug_props', 'virtio_version', - 'vnc', 'memlock', ] From 45d34fa8a4f7fed96f7fbf2ae42b33382bf25d0d Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:24:02 +0200 Subject: [PATCH 0294/1794] MAINTAINERS: Adjust wildcards for the migration, multiprocess and replay tests Now that we moved the tests into subfolders, we have to adjust the wildcards accordingly. Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-26-thuth@redhat.com> --- MAINTAINERS | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 625fe67b41d90..a64b5b849b515 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3604,7 +3604,8 @@ F: include/migration/ F: include/qemu/userfaultfd.h F: migration/ F: scripts/vmstate-static-checker.py -F: tests/functional/*migration.py +F: tests/functional/migration.py +F: tests/functional/*/*migration.py F: tests/vmstate-static-checker-data/ F: tests/qtest/migration/ F: tests/qtest/migration-* @@ -3773,8 +3774,10 @@ F: include/system/replay.h F: docs/devel/replay.rst F: docs/system/replay.rst F: stubs/replay.c -F: tests/functional/*reverse_debug*.py -F: tests/functional/*replay*.py +F: tests/functional/replay_kernel.py +F: tests/functional/reverse_debugging.py +F: tests/functional/*/*replay*.py +F: tests/functional/*/*reverse_debug*.py F: qapi/replay.json IOVA Tree @@ -4293,7 +4296,8 @@ F: hw/remote/vfio-user-obj.c F: include/hw/remote/vfio-user-obj.h F: hw/remote/iommu.c F: include/hw/remote/iommu.h -F: tests/functional/*multiprocess.py +F: tests/functional/multiprocess.py +F: tests/functional/*/*multiprocess.py VFIO-USER: M: John Levon From 0ac3c314130eff8e3ea9860fe3202908a7746225 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Tue, 19 Aug 2025 14:39:15 +0000 Subject: [PATCH 0295/1794] tests/functional: Mark main in QemuBaseTest class as a static method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The main() method in the QemuBaseTest class has no parameters but is defined as a regular method. Currently, this does not cause any issues because in the functional tests main() is always called directly from QemuBaseTest (never from instances), but the way this method is defined makes its signature wrong, implying a 'self'. Hence, it's best practice to define such a method as a static method, so decorate it with @staticmethod. Signed-off-by: Gustavo Romero Message-ID: <20250819143916.4138035-4-gustavo.romero@linaro.org> Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 5caf7b13fe36b..fbeb171058721 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -235,6 +235,7 @@ def tearDown(self): self.log.removeHandler(self._log_fh) self._log_fh.close() + @staticmethod def main(): warnings.simplefilter("default") os.environ["PYTHONWARNINGS"] = "default" From a7542a38f399c50337e10aadd60513a400c45013 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 14 Aug 2025 17:21:11 +0800 Subject: [PATCH 0296/1794] x86/loader: Don't update kernel header for CoCo VMs Update the header makes it different from the original kernel that user provides via "-kernel", which leads to a different hash and breaks the attestation, e.g., for TDX. We already skip it for SEV VMs. Instead of adding another check of is_tdx_vm() to cover the TDX case, check machine->cgs to cover all the confidential computing case for x86. Reported-by: Vikrant Garg Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250814092111.2353598-1-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/x86-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index b1b5f11e73964..7512be64d67b9 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -952,7 +952,7 @@ void x86_load_linux(X86MachineState *x86ms, * kernel on the other side of the fw_cfg interface matches the hash of the * file the user passed in. */ - if (!sev_enabled() && protocol > 0) { + if (!MACHINE(x86ms)->cgs && protocol > 0) { memcpy(setup, header, MIN(sizeof(header), setup_size)); } From c12cbaa007c9da97a11e74119ea3aed9fcc3ac4c Mon Sep 17 00:00:00 2001 From: Zero Tang Date: Mon, 18 Aug 2025 12:16:47 +0200 Subject: [PATCH 0297/1794] i386/tcg/svm: fix incorrect canonicalization For all 32-bit systems and 64-bit Windows systems, "long" is 4 bytes long. Due to using "long" for a linear address, svm_canonicalization would set all high bits to 1 when (assuming 48-bit linear address) the segment base is bigger than 0x7FFF. This fixes booting guests under TCG when the guest IDT and GDT bases are above 0x7FFF, thereby resulting in incorrect bases. When an interrupt arrives, it would trigger a #PF exception; the #PF would trigger again, resulting in a #DF exception; the #PF would trigger for the third time, resulting in triple-fault, and eventually causes a shutdown VM-Exit to the hypervisor right after guest boot. Cc: qemu-stable@nongnu.org Signed-off-by: Zero Tang --- target/i386/tcg/system/svm_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/tcg/system/svm_helper.c b/target/i386/tcg/system/svm_helper.c index b27049b9ed138..dea039b87a645 100644 --- a/target/i386/tcg/system/svm_helper.c +++ b/target/i386/tcg/system/svm_helper.c @@ -49,7 +49,7 @@ static void svm_save_seg(CPUX86State *env, int mmu_idx, hwaddr addr, static inline void svm_canonicalization(CPUX86State *env, target_ulong *seg_base) { uint16_t shift_amt = 64 - cpu_x86_virtual_addr_width(env); - *seg_base = ((((long) *seg_base) << shift_amt) >> shift_amt); + *seg_base = (((int64_t) *seg_base) << shift_amt) >> shift_amt; } static void svm_load_seg(CPUX86State *env, int mmu_idx, hwaddr addr, From 8e98961f6eb691b59ff0230ee22917061bfae5f8 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Fri, 15 Aug 2025 12:24:45 +0530 Subject: [PATCH 0298/1794] kvm/kvm-all: make kvm_park/unpark_vcpu local to kvm-all.c kvm_park_vcpu() and kvm_unpark_vcpu() is only used in kvm-all.c. Declare it static, remove it from common header file and make it local to kvm-all.c Signed-off-by: Ani Sinha Reviewed-by: Igor Mammedov Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250815065445.8978-1-anisinha@redhat.com Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 4 ++-- include/system/kvm.h | 17 ----------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 890d5ea9f8652..f36dfe3349236 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -414,7 +414,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo return ret; } -void kvm_park_vcpu(CPUState *cpu) +static void kvm_park_vcpu(CPUState *cpu) { struct KVMParkedVcpu *vcpu; @@ -426,7 +426,7 @@ void kvm_park_vcpu(CPUState *cpu) QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node); } -int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id) +static int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id) { struct KVMParkedVcpu *cpu; int kvm_fd = -ENOENT; diff --git a/include/system/kvm.h b/include/system/kvm.h index 3c7d31473663b..4fc09e3891005 100644 --- a/include/system/kvm.h +++ b/include/system/kvm.h @@ -317,23 +317,6 @@ int kvm_create_device(KVMState *s, uint64_t type, bool test); */ bool kvm_device_supported(int vmfd, uint64_t type); -/** - * kvm_park_vcpu - Park QEMU KVM vCPU context - * @cpu: QOM CPUState object for which QEMU KVM vCPU context has to be parked. - * - * @returns: none - */ -void kvm_park_vcpu(CPUState *cpu); - -/** - * kvm_unpark_vcpu - unpark QEMU KVM vCPU context - * @s: KVM State - * @vcpu_id: Architecture vCPU ID of the parked vCPU - * - * @returns: KVM fd - */ -int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id); - /** * kvm_create_and_park_vcpu - Create and park a KVM vCPU * @cpu: QOM CPUState object for which KVM vCPU has to be created and parked. From db62680edd04c78eada3cf67f27d8825e08feb9a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 31 Jul 2025 19:36:38 +0200 Subject: [PATCH 0299/1794] rust: disable borrow_as_ptr warning This is pretty noisy, but it was not visible until now because it only shows up if the rust-version has "&raw const". Signed-off-by: Paolo Bonzini --- rust/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 0868e1b426808..0a83db1535626 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -53,7 +53,6 @@ as_ptr_cast_mut = "deny" as_underscore = "deny" assertions_on_result_states = "deny" bool_to_int_with_if = "deny" -borrow_as_ptr = "deny" cast_lossless = "deny" dbg_macro = "deny" debug_assert_with_mut_call = "deny" From 2102780c5523c240c66ba52ea1629353a7518072 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 16 Jul 2025 13:17:09 +0200 Subject: [PATCH 0300/1794] rust: qemu-api-macros: support matching more than one error Signed-off-by: Paolo Bonzini --- rust/qemu-api-macros/src/tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs index d6dcd62fcf680..6028cdbc4c331 100644 --- a/rust/qemu-api-macros/src/tests.rs +++ b/rust/qemu-api-macros/src/tests.rs @@ -7,9 +7,9 @@ use quote::quote; use super::*; macro_rules! derive_compile_fail { - ($derive_fn:ident, $input:expr, $error_msg:expr) => {{ + ($derive_fn:ident, $input:expr, $($error_msg:expr),+ $(,)?) => {{ let input: proc_macro2::TokenStream = $input; - let error_msg: &str = $error_msg; + let error_msg = &[$( quote! { ::core::compile_error! { $error_msg } } ),*]; let derive_fn: fn(input: syn::DeriveInput) -> Result = $derive_fn; @@ -18,7 +18,7 @@ macro_rules! derive_compile_fail { let err = result.unwrap_err().into_compile_error(); assert_eq!( err.to_string(), - quote! { ::core::compile_error! { #error_msg } }.to_string() + quote! { #(#error_msg)* }.to_string() ); }}; } From 9a6d6ae8afb18e18eacb94e105722c08e84fe9fd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 17 Jul 2025 08:09:26 +0200 Subject: [PATCH 0301/1794] subprojects: update proc-macro2 and syn syn 2.0.69 adds Punctuated::get(). The serde and attrs crate also need a newer version. Signed-off-by: Paolo Bonzini --- rust/Cargo.lock | 8 ++++---- subprojects/packagefiles/proc-macro2-1-rs/meson.build | 2 +- subprojects/packagefiles/syn-2-rs/meson.build | 2 +- subprojects/proc-macro2-1-rs.wrap | 8 ++++---- subprojects/syn-2-rs.wrap | 8 ++++---- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index b785c718f315b..4baf6ba663cd5 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -118,9 +118,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -155,9 +155,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", diff --git a/subprojects/packagefiles/proc-macro2-1-rs/meson.build b/subprojects/packagefiles/proc-macro2-1-rs/meson.build index 5759df3ecc9b0..ba7de07029271 100644 --- a/subprojects/packagefiles/proc-macro2-1-rs/meson.build +++ b/subprojects/packagefiles/proc-macro2-1-rs/meson.build @@ -1,6 +1,6 @@ project('proc-macro2-1-rs', 'rust', meson_version: '>=1.5.0', - version: '1.0.84', + version: '1.0.95', license: 'MIT OR Apache-2.0', default_options: []) diff --git a/subprojects/packagefiles/syn-2-rs/meson.build b/subprojects/packagefiles/syn-2-rs/meson.build index a0094174084ec..3e6dc318a9c7b 100644 --- a/subprojects/packagefiles/syn-2-rs/meson.build +++ b/subprojects/packagefiles/syn-2-rs/meson.build @@ -1,6 +1,6 @@ project('syn-2-rs', 'rust', meson_version: '>=1.5.0', - version: '2.0.66', + version: '2.0.104', license: 'MIT OR Apache-2.0', default_options: []) diff --git a/subprojects/proc-macro2-1-rs.wrap b/subprojects/proc-macro2-1-rs.wrap index 6c9369f0df3f3..0f06cd8e111f8 100644 --- a/subprojects/proc-macro2-1-rs.wrap +++ b/subprojects/proc-macro2-1-rs.wrap @@ -1,8 +1,8 @@ [wrap-file] -directory = proc-macro2-1.0.84 -source_url = https://crates.io/api/v1/crates/proc-macro2/1.0.84/download -source_filename = proc-macro2-1.0.84.0.tar.gz -source_hash = ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6 +directory = proc-macro2-1.0.95 +source_url = https://crates.io/api/v1/crates/proc-macro2/1.0.95/download +source_filename = proc-macro2-1.0.95.0.tar.gz +source_hash = 02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778 #method = cargo patch_directory = proc-macro2-1-rs diff --git a/subprojects/syn-2-rs.wrap b/subprojects/syn-2-rs.wrap index d79cf750fb492..1e5e9d9fb6e32 100644 --- a/subprojects/syn-2-rs.wrap +++ b/subprojects/syn-2-rs.wrap @@ -1,8 +1,8 @@ [wrap-file] -directory = syn-2.0.66 -source_url = https://crates.io/api/v1/crates/syn/2.0.66/download -source_filename = syn-2.0.66.0.tar.gz -source_hash = c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5 +directory = syn-2.0.104 +source_url = https://crates.io/api/v1/crates/syn/2.0.104/download +source_filename = syn-2.0.104.0.tar.gz +source_hash = 17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40 #method = cargo patch_directory = syn-2-rs From 96f2c80fed20790fec0b35b774af676d5068077b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 26 Aug 2025 17:31:32 +0400 Subject: [PATCH 0302/1794] rust/qemu-api-macros: make derive(Object) friendly when missing parent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250826133132.4064478-5-marcandre.lureau@redhat.com Signed-off-by: Paolo Bonzini --- rust/qemu-api-macros/src/lib.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index b525d89c09e49..a6147418891a6 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -85,7 +85,15 @@ fn derive_object_or_error(input: DeriveInput) -> Result Date: Mon, 28 Jul 2025 14:48:23 +0300 Subject: [PATCH 0303/1794] rust: declare self as qemu_api for proc-macros Fix an outstanding TODO. Declaring `extern crate self as qemu_api` allows use of `qemu_api` within the qemu_api crate; this allows the Wrapper derive macro and future proc macros to be used interchangeably in the qemu_api crate and other crates. This is not required currently and is only for future-proofing. Signed-off-by: Manos Pitsidianakis Link: https://lore.kernel.org/r/20250728-self-as-qemu_api-v1-1-001c339cccc8@linaro.org Signed-off-by: Paolo Bonzini --- rust/qemu-api-macros/src/lib.rs | 14 ++++++-------- rust/qemu-api/src/lib.rs | 4 ++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index a6147418891a6..959726efe6dc5 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -123,23 +123,21 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result::Wrapped; + unsafe impl ::qemu_api::cell::Wrapper for #name { + type Wrapped = <#typ as ::qemu_api::cell::Wrapper>::Wrapped; } impl #name { - pub unsafe fn from_raw<'a>(ptr: *mut ::Wrapped) -> &'a Self { + pub unsafe fn from_raw<'a>(ptr: *mut ::Wrapped) -> &'a Self { let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::(); unsafe { ptr.as_ref() } } - pub const fn as_mut_ptr(&self) -> *mut ::Wrapped { + pub const fn as_mut_ptr(&self) -> *mut ::Wrapped { self.0.as_mut_ptr() } - pub const fn as_ptr(&self) -> *const ::Wrapped { + pub const fn as_ptr(&self) -> *const ::Wrapped { self.0.as_ptr() } @@ -147,7 +145,7 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result *mut ::Wrapped { + pub const fn raw_get(slot: *mut Self) -> *mut ::Wrapped { slot.cast() } } diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 86dcd8ef17a9e..bcb51c7986a2c 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -32,6 +32,10 @@ pub mod uninit; pub mod vmstate; pub mod zeroable; +// Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this +// crate). +extern crate self as qemu_api; + use std::{ alloc::{GlobalAlloc, Layout}, ffi::c_void, From 92dedaf169ddcf8c81fa6d21c86c60f3b82458e5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 22 Aug 2025 12:07:44 +0200 Subject: [PATCH 0304/1794] rust: move dependencies to rust/Cargo.toml As more crates start using the same dependencies, it's better to not repeat the versions and move the dependency declarations to the workspace. Reviewed-by: Manos Pitsidianakis Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/Cargo.toml | 5 +++++ rust/qemu-api/Cargo.toml | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 0a83db1535626..6f8884eb30b42 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -15,6 +15,11 @@ license = "GPL-2.0-or-later" repository = "https://gitlab.com/qemu-project/qemu/" rust-version = "1.77.0" +[workspace.dependencies] +anyhow = "~1.0" +foreign = "~0.3.1" +libc = "0.2.162" + [workspace.lints.rust] unexpected_cfgs = { level = "deny", check-cfg = [ 'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)', diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index db7000dee4410..c07a17a28b014 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -15,9 +15,9 @@ rust-version.workspace = true [dependencies] qemu_api_macros = { path = "../qemu-api-macros" } -anyhow = "~1.0" -libc = "0.2.162" -foreign = "~0.3.1" +anyhow = { workspace = true } +foreign = { workspace = true } +libc = { workspace = true } [features] default = ["debug_cell"] From 05c84cf8df23b8dc81317ee0cea748e6199637f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 2 Aug 2025 10:12:01 +1000 Subject: [PATCH 0305/1794] semihosting: Retrieve stack top from image_info Remove the write-once field TaskState.stack_base, and use the same value from struct image_info. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/cpu_loop.c | 1 - linux-user/arm/cpu_loop.c | 1 - linux-user/m68k/cpu_loop.c | 1 - linux-user/qemu.h | 1 - linux-user/riscv/cpu_loop.c | 1 - semihosting/arm-compat-semi.c | 6 +++++- 6 files changed, 5 insertions(+), 6 deletions(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index fea43cefa6bc9..b65999a75bff1 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -168,7 +168,6 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) qemu_guest_getrandom_nofail(&env->keys, sizeof(env->keys)); } - ts->stack_base = info->start_stack; ts->heap_base = info->brk; /* This will be filled in on the first SYS_HEAPINFO call. */ ts->heap_limit = 0; diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 33f63951a958a..e40d6beafa279 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -504,7 +504,6 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) arm_rebuild_hflags(env); #endif - ts->stack_base = info->start_stack; ts->heap_base = info->brk; /* This will be filled in on the first SYS_HEAPINFO call. */ ts->heap_limit = 0; diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index 5da91b997ae38..3aaaf02ca4ee9 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -117,7 +117,6 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) env->aregs[7] = regs->usp; env->sr = regs->sr; - ts->stack_base = info->start_stack; ts->heap_base = info->brk; /* This will be filled in on the first SYS_HEAPINFO call. */ ts->heap_limit = 0; diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 0b19fa43e6511..b6621536b36e2 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -127,7 +127,6 @@ struct TaskState { abi_ulong heap_base; abi_ulong heap_limit; #endif - abi_ulong stack_base; int used; /* non zero if used */ struct image_info *info; struct linux_binprm *bprm; diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index 3ac8bbfec1f49..541de765ffae4 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -109,7 +109,6 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) exit(EXIT_FAILURE); } - ts->stack_base = info->start_stack; ts->heap_base = info->brk; /* This will be filled in on the first SYS_HEAPINFO call. */ ts->heap_limit = 0; diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 86e5260e504be..bc04b02eba8e0 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -696,7 +696,11 @@ void do_common_semihosting(CPUState *cs) retvals[0] = ts->heap_base; retvals[1] = ts->heap_limit; - retvals[2] = ts->stack_base; + /* + * Note that semihosting is *not* thread aware. + * Always return the stack base of the main thread. + */ + retvals[2] = ts->info->start_stack; retvals[3] = 0; /* Stack limit. */ #else retvals[0] = info.heapbase; /* Heap Base */ From 7adf9ebb0ac72637833f61e24e44def6228b4484 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 2 Aug 2025 10:25:12 +1000 Subject: [PATCH 0306/1794] semihosting: Initialize heap once per process While semihosting isn't really thread aware, the current implementation allocates space for the heap per-thread. Remove the heap_base and heap_limit fields from TaskState. Replace with static variables within do_common_semihosting. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/cpu_loop.c | 7 ------- linux-user/arm/cpu_loop.c | 25 +++++++++++-------------- linux-user/m68k/cpu_loop.c | 8 -------- linux-user/qemu.h | 5 ----- linux-user/riscv/cpu_loop.c | 4 ---- semihosting/arm-compat-semi.c | 22 +++++++++------------- 6 files changed, 20 insertions(+), 51 deletions(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index b65999a75bff1..030a630c936e7 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -140,9 +140,6 @@ void cpu_loop(CPUARMState *env) void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { ARMCPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - TaskState *ts = get_task_state(cs); - struct image_info *info = ts->info; int i; if (!(arm_feature(env, ARM_FEATURE_AARCH64))) { @@ -167,8 +164,4 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) if (cpu_isar_feature(aa64_pauth, cpu)) { qemu_guest_getrandom_nofail(&env->keys, sizeof(env->keys)); } - - ts->heap_base = info->brk; - /* This will be filled in on the first SYS_HEAPINFO call. */ - ts->heap_limit = 0; } diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index e40d6beafa279..9d54422736c5f 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -492,19 +492,16 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) for(i = 0; i < 16; i++) { env->regs[i] = regs->uregs[i]; } -#if TARGET_BIG_ENDIAN - /* Enable BE8. */ - if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 - && (info->elf_flags & EF_ARM_BE8)) { - env->uncached_cpsr |= CPSR_E; - env->cp15.sctlr_el[1] |= SCTLR_E0E; - } else { - env->cp15.sctlr_el[1] |= SCTLR_B; - } - arm_rebuild_hflags(env); -#endif - ts->heap_base = info->brk; - /* This will be filled in on the first SYS_HEAPINFO call. */ - ts->heap_limit = 0; + if (TARGET_BIG_ENDIAN) { + /* Enable BE8. */ + if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 + && (info->elf_flags & EF_ARM_BE8)) { + env->uncached_cpsr |= CPSR_E; + env->cp15.sctlr_el[1] |= SCTLR_E0E; + } else { + env->cp15.sctlr_el[1] |= SCTLR_B; + } + arm_rebuild_hflags(env); + } } diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index 3aaaf02ca4ee9..23693f33582ef 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -94,10 +94,6 @@ void cpu_loop(CPUM68KState *env) void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { - CPUState *cpu = env_cpu(env); - TaskState *ts = get_task_state(cpu); - struct image_info *info = ts->info; - env->pc = regs->pc; env->dregs[0] = regs->d0; env->dregs[1] = regs->d1; @@ -116,8 +112,4 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) env->aregs[6] = regs->a6; env->aregs[7] = regs->usp; env->sr = regs->sr; - - ts->heap_base = info->brk; - /* This will be filled in on the first SYS_HEAPINFO call. */ - ts->heap_limit = 0; } diff --git a/linux-user/qemu.h b/linux-user/qemu.h index b6621536b36e2..4d6fad28c63c2 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -121,11 +121,6 @@ struct TaskState { abi_ulong child_tidptr; #ifdef TARGET_M68K abi_ulong tp_value; -#endif -#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_RISCV) - /* Extra fields for semihosted binaries. */ - abi_ulong heap_base; - abi_ulong heap_limit; #endif int used; /* non zero if used */ struct image_info *info; diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index 541de765ffae4..2dd30c7b28886 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -108,8 +108,4 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) error_report("Incompatible ELF: RVE cpu requires RVE ABI binary"); exit(EXIT_FAILURE); } - - ts->heap_base = info->brk; - /* This will be filled in on the first SYS_HEAPINFO call. */ - ts->heap_limit = 0; } diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index bc04b02eba8e0..bcd13cd6dfd5a 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -666,7 +666,7 @@ void do_common_semihosting(CPUState *cs) int i; #ifdef CONFIG_USER_ONLY TaskState *ts = get_task_state(cs); - target_ulong limit; + static abi_ulong heapbase, heaplimit; #else LayoutInfo info = common_semi_find_bases(cs); #endif @@ -678,24 +678,20 @@ void do_common_semihosting(CPUState *cs) * Some C libraries assume the heap immediately follows .bss, so * allocate it using sbrk. */ - if (!ts->heap_limit) { - abi_ulong ret; - - ts->heap_base = do_brk(0); - limit = ts->heap_base + COMMON_SEMI_HEAP_SIZE; + if (!heaplimit) { + heapbase = do_brk(0); /* Try a big heap, and reduce the size if that fails. */ - for (;;) { - ret = do_brk(limit); + for (abi_ulong size = COMMON_SEMI_HEAP_SIZE; ; size >>= 1) { + abi_ulong limit = heapbase + size; + abi_ulong ret = do_brk(limit); if (ret >= limit) { + heaplimit = limit; break; } - limit = (ts->heap_base >> 1) + (limit >> 1); } - ts->heap_limit = limit; } - - retvals[0] = ts->heap_base; - retvals[1] = ts->heap_limit; + retvals[0] = heapbase; + retvals[1] = heaplimit; /* * Note that semihosting is *not* thread aware. * Always return the stack base of the main thread. From 58afe4cfe93be0e283e7a0fcd9a50fd52fc44169 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 08:03:41 -1000 Subject: [PATCH 0307/1794] linux-user: Create target/elfload.c files Prepare to split the main linux-user/elfload.c. Create empty files for each target, and add the common build rule. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/elfload.c | 1 + linux-user/alpha/elfload.c | 1 + linux-user/arm/elfload.c | 1 + linux-user/hexagon/elfload.c | 1 + linux-user/hppa/elfload.c | 1 + linux-user/i386/elfload.c | 1 + linux-user/loongarch64/elfload.c | 1 + linux-user/m68k/elfload.c | 1 + linux-user/microblaze/elfload.c | 1 + linux-user/mips/elfload.c | 1 + linux-user/mips64/elfload.c | 1 + linux-user/openrisc/elfload.c | 1 + linux-user/ppc/elfload.c | 1 + linux-user/riscv/elfload.c | 1 + linux-user/s390x/elfload.c | 1 + linux-user/sh4/elfload.c | 1 + linux-user/sparc/elfload.c | 1 + linux-user/x86_64/elfload.c | 1 + linux-user/xtensa/elfload.c | 1 + meson.build | 6 +++++- 20 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 linux-user/aarch64/elfload.c create mode 100644 linux-user/alpha/elfload.c create mode 100644 linux-user/arm/elfload.c create mode 100644 linux-user/hexagon/elfload.c create mode 100644 linux-user/hppa/elfload.c create mode 100644 linux-user/i386/elfload.c create mode 100644 linux-user/loongarch64/elfload.c create mode 100644 linux-user/m68k/elfload.c create mode 100644 linux-user/microblaze/elfload.c create mode 100644 linux-user/mips/elfload.c create mode 100644 linux-user/mips64/elfload.c create mode 100644 linux-user/openrisc/elfload.c create mode 100644 linux-user/ppc/elfload.c create mode 100644 linux-user/riscv/elfload.c create mode 100644 linux-user/s390x/elfload.c create mode 100644 linux-user/sh4/elfload.c create mode 100644 linux-user/sparc/elfload.c create mode 100644 linux-user/x86_64/elfload.c create mode 100644 linux-user/xtensa/elfload.c diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/aarch64/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/alpha/elfload.c b/linux-user/alpha/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/alpha/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/arm/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/hexagon/elfload.c b/linux-user/hexagon/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/hexagon/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/hppa/elfload.c b/linux-user/hppa/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/hppa/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/i386/elfload.c b/linux-user/i386/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/i386/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/loongarch64/elfload.c b/linux-user/loongarch64/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/loongarch64/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/m68k/elfload.c b/linux-user/m68k/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/m68k/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/microblaze/elfload.c b/linux-user/microblaze/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/microblaze/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/mips/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/mips64/elfload.c b/linux-user/mips64/elfload.c new file mode 100644 index 0000000000000..b719555e6599a --- /dev/null +++ b/linux-user/mips64/elfload.c @@ -0,0 +1 @@ +#include "../mips/elfload.c" diff --git a/linux-user/openrisc/elfload.c b/linux-user/openrisc/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/openrisc/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/ppc/elfload.c b/linux-user/ppc/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/ppc/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/riscv/elfload.c b/linux-user/riscv/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/riscv/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/s390x/elfload.c b/linux-user/s390x/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/s390x/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/sh4/elfload.c b/linux-user/sh4/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/sh4/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/sparc/elfload.c b/linux-user/sparc/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/sparc/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/x86_64/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/xtensa/elfload.c b/linux-user/xtensa/elfload.c new file mode 100644 index 0000000000000..73fa78ef14ea5 --- /dev/null +++ b/linux-user/xtensa/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/meson.build b/meson.build index 50c774a19557a..0d42de61ae6c6 100644 --- a/meson.build +++ b/meson.build @@ -4327,7 +4327,11 @@ foreach target : target_dirs ) if 'CONFIG_LINUX_USER' in config_target dir = base_dir / abi - arch_srcs += files(dir / 'signal.c', dir / 'cpu_loop.c') + arch_srcs += files( + dir / 'cpu_loop.c', + dir / 'elfload.c', + dir / 'signal.c', + ) if config_target.has_key('TARGET_SYSTBL_ABI') arch_srcs += \ syscall_nr_generators[abi].process(base_dir / abi / config_target['TARGET_SYSTBL'], From a859022ceaba12444f1a8f5a5efc5e76c7d5dc13 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 13:40:15 -1000 Subject: [PATCH 0308/1794] linux-user: Move ppc uabi/asm/elf.h workaround to osdep.h Move the workaround out of linux-user/elfload.c, so that we don't have to replicate it in many places. Place it immediately after the include of , which draws in the relevant symbols. Note that ARCH_DLINFO is not defined by the kernel header, and so there's no need to undef it either. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- hw/core/loader.c | 4 ---- include/qemu/osdep.h | 8 ++++++++ linux-user/elfload.c | 10 ---------- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index e7056ba4bd3b7..524af6f14a095 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -295,10 +295,6 @@ static void *load_at(int fd, off_t offset, size_t size) return ptr; } -#ifdef ELF_CLASS -#undef ELF_CLASS -#endif - #define ELF_CLASS ELFCLASS32 #include "elf.h" diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 96fe51bc39046..be3460b32f2d8 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -133,6 +133,14 @@ QEMU_EXTERN_C int daemon(int, int); #include #include +/* + * Avoid conflict with linux/arch/powerpc/include/uapi/asm/elf.h, included + * from , but we might as well do this unconditionally. + */ +#undef ELF_CLASS +#undef ELF_DATA +#undef ELF_ARCH + #ifdef CONFIG_IOVEC #include #endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index ea214105ff86b..4ca8c39dc26b2 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -35,16 +35,6 @@ #include "target/arm/cpu-features.h" #endif -#ifdef _ARCH_PPC64 -#undef ARCH_DLINFO -#undef ELF_PLATFORM -#undef ELF_HWCAP -#undef ELF_HWCAP2 -#undef ELF_CLASS -#undef ELF_DATA -#undef ELF_ARCH -#endif - #ifndef TARGET_ARCH_HAS_SIGTRAMP_PAGE #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 #endif From af880af8d4624b619c9d44ff5d27440ae064f99a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:09:57 -1000 Subject: [PATCH 0309/1794] linux-user: Move get_elf_cpu_model to target/elfload.c Rename from cpu_get_model to emphasize that this is an elf-specific function. Declare the function once in loader.h. This frees up target_elf.h for other uses. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/elfload.c | 10 +++++++ linux-user/aarch64/target_elf.h | 5 +--- linux-user/alpha/elfload.c | 10 +++++++ linux-user/alpha/target_elf.h | 5 +--- linux-user/arm/elfload.c | 10 +++++++ linux-user/arm/target_elf.h | 5 +--- linux-user/hexagon/elfload.c | 34 +++++++++++++++++++++++ linux-user/hexagon/target_elf.h | 29 ------------------- linux-user/hppa/elfload.c | 10 +++++++ linux-user/hppa/target_elf.h | 5 +--- linux-user/i386/elfload.c | 10 +++++++ linux-user/i386/target_elf.h | 5 +--- linux-user/loader.h | 3 ++ linux-user/loongarch64/elfload.c | 10 +++++++ linux-user/loongarch64/target_elf.h | 5 +--- linux-user/m68k/elfload.c | 17 ++++++++++++ linux-user/m68k/target_elf.h | 9 ------ linux-user/main.c | 3 +- linux-user/microblaze/elfload.c | 10 +++++++ linux-user/microblaze/target_elf.h | 5 +--- linux-user/mips/elfload.c | 43 +++++++++++++++++++++++++++++ linux-user/mips/target_elf.h | 11 +------- linux-user/mips64/target_elf.h | 27 +----------------- linux-user/openrisc/elfload.c | 10 +++++++ linux-user/openrisc/target_elf.h | 5 +--- linux-user/ppc/elfload.c | 14 ++++++++++ linux-user/ppc/target_elf.h | 9 +----- linux-user/riscv/elfload.c | 10 +++++++ linux-user/riscv/target_elf.h | 5 +--- linux-user/s390x/elfload.c | 10 +++++++ linux-user/s390x/target_elf.h | 5 +--- linux-user/sh4/elfload.c | 10 +++++++ linux-user/sh4/target_elf.h | 5 +--- linux-user/sparc/elfload.c | 14 ++++++++++ linux-user/sparc/target_elf.h | 9 +----- linux-user/x86_64/elfload.c | 10 +++++++ linux-user/x86_64/target_elf.h | 5 +--- linux-user/xtensa/elfload.c | 10 +++++++ linux-user/xtensa/target_elf.h | 5 ---- 39 files changed, 272 insertions(+), 145 deletions(-) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 73fa78ef14ea5..b92442dfeb2a8 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "any"; +} diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index a7eb962fba754..d955b3d07f901 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -7,8 +7,5 @@ #ifndef AARCH64_TARGET_ELF_H #define AARCH64_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "any"; -} + #endif diff --git a/linux-user/alpha/elfload.c b/linux-user/alpha/elfload.c index 73fa78ef14ea5..1e44475c47586 100644 --- a/linux-user/alpha/elfload.c +++ b/linux-user/alpha/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "ev67"; +} diff --git a/linux-user/alpha/target_elf.h b/linux-user/alpha/target_elf.h index b77d638f6d4a8..52b68680ad35b 100644 --- a/linux-user/alpha/target_elf.h +++ b/linux-user/alpha/target_elf.h @@ -7,8 +7,5 @@ #ifndef ALPHA_TARGET_ELF_H #define ALPHA_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "ev67"; -} + #endif diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index 73fa78ef14ea5..b92442dfeb2a8 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "any"; +} diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index 58ff6a0986fbe..2abb27a733775 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -7,8 +7,5 @@ #ifndef ARM_TARGET_ELF_H #define ARM_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "any"; -} + #endif diff --git a/linux-user/hexagon/elfload.c b/linux-user/hexagon/elfload.c index 73fa78ef14ea5..d8b545032ab61 100644 --- a/linux-user/hexagon/elfload.c +++ b/linux-user/hexagon/elfload.c @@ -1 +1,35 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + static char buf[32]; + int err; + + /* For now, treat anything newer than v5 as a v73 */ + /* FIXME - Disable instructions that are newer than the specified arch */ + if (eflags == 0x04 || /* v5 */ + eflags == 0x05 || /* v55 */ + eflags == 0x60 || /* v60 */ + eflags == 0x61 || /* v61 */ + eflags == 0x62 || /* v62 */ + eflags == 0x65 || /* v65 */ + eflags == 0x66 || /* v66 */ + eflags == 0x67 || /* v67 */ + eflags == 0x8067 || /* v67t */ + eflags == 0x68 || /* v68 */ + eflags == 0x69 || /* v69 */ + eflags == 0x71 || /* v71 */ + eflags == 0x8071 || /* v71t */ + eflags == 0x73 /* v73 */ + ) { + return "v73"; + } + + err = snprintf(buf, sizeof(buf), "unknown (0x%x)", eflags); + return err >= 0 && err < sizeof(buf) ? buf : "unknown"; +} diff --git a/linux-user/hexagon/target_elf.h b/linux-user/hexagon/target_elf.h index 36056fc9f016e..eccf207f6b5b2 100644 --- a/linux-user/hexagon/target_elf.h +++ b/linux-user/hexagon/target_elf.h @@ -18,33 +18,4 @@ #ifndef HEXAGON_TARGET_ELF_H #define HEXAGON_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - static char buf[32]; - int err; - - /* For now, treat anything newer than v5 as a v73 */ - /* FIXME - Disable instructions that are newer than the specified arch */ - if (eflags == 0x04 || /* v5 */ - eflags == 0x05 || /* v55 */ - eflags == 0x60 || /* v60 */ - eflags == 0x61 || /* v61 */ - eflags == 0x62 || /* v62 */ - eflags == 0x65 || /* v65 */ - eflags == 0x66 || /* v66 */ - eflags == 0x67 || /* v67 */ - eflags == 0x8067 || /* v67t */ - eflags == 0x68 || /* v68 */ - eflags == 0x69 || /* v69 */ - eflags == 0x71 || /* v71 */ - eflags == 0x8071 || /* v71t */ - eflags == 0x73 /* v73 */ - ) { - return "v73"; - } - - err = snprintf(buf, sizeof(buf), "unknown (0x%x)", eflags); - return err >= 0 && err < sizeof(buf) ? buf : "unknown"; -} - #endif diff --git a/linux-user/hppa/elfload.c b/linux-user/hppa/elfload.c index 73fa78ef14ea5..2274fcbde435d 100644 --- a/linux-user/hppa/elfload.c +++ b/linux-user/hppa/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "hppa"; +} diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h index 19cae8bd65ddc..5826ca2cd22f6 100644 --- a/linux-user/hppa/target_elf.h +++ b/linux-user/hppa/target_elf.h @@ -7,8 +7,5 @@ #ifndef HPPA_TARGET_ELF_H #define HPPA_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "hppa"; -} + #endif diff --git a/linux-user/i386/elfload.c b/linux-user/i386/elfload.c index 73fa78ef14ea5..f92adb73085b9 100644 --- a/linux-user/i386/elfload.c +++ b/linux-user/i386/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "max"; +} diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index 238a9aba738a0..e6f0d8fa4e679 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -7,8 +7,5 @@ #ifndef I386_TARGET_ELF_H #define I386_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "max"; -} + #endif diff --git a/linux-user/loader.h b/linux-user/loader.h index e102e6f4108f4..75ee9975a08d3 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -98,6 +98,9 @@ abi_long memcpy_to_target(abi_ulong dest, const void *src, extern unsigned long guest_stack_size; +/* Note that Elf32 and Elf64 use uint32_t for e_flags. */ +const char *get_elf_cpu_model(uint32_t eflags); + #if defined(TARGET_S390X) || defined(TARGET_AARCH64) || defined(TARGET_ARM) uint32_t get_elf_hwcap(void); const char *elf_hwcap_str(uint32_t bit); diff --git a/linux-user/loongarch64/elfload.c b/linux-user/loongarch64/elfload.c index 73fa78ef14ea5..874dc4c230477 100644 --- a/linux-user/loongarch64/elfload.c +++ b/linux-user/loongarch64/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "la464"; +} diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index 95c3f05a46d87..39a08d35d9ba2 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -5,8 +5,5 @@ #ifndef LOONGARCH_TARGET_ELF_H #define LOONGARCH_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "la464"; -} + #endif diff --git a/linux-user/m68k/elfload.c b/linux-user/m68k/elfload.c index 73fa78ef14ea5..561ac5b3b3a7d 100644 --- a/linux-user/m68k/elfload.c +++ b/linux-user/m68k/elfload.c @@ -1 +1,18 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + if (eflags == 0 || (eflags & EF_M68K_M68000)) { + /* 680x0 */ + return "m68040"; + } + + /* Coldfire */ + return "any"; +} diff --git a/linux-user/m68k/target_elf.h b/linux-user/m68k/target_elf.h index 998fe0fe2f322..62ff9d38d4cdb 100644 --- a/linux-user/m68k/target_elf.h +++ b/linux-user/m68k/target_elf.h @@ -7,14 +7,5 @@ #ifndef M68K_TARGET_ELF_H #define M68K_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - if (eflags == 0 || (eflags & EF_M68K_M68000)) { - /* 680x0 */ - return "m68040"; - } - /* Coldfire */ - return "any"; -} #endif diff --git a/linux-user/main.c b/linux-user/main.c index 68972f00a155a..ad1a29d1989d4 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -49,7 +49,6 @@ #include "qemu/guest-random.h" #include "elf.h" #include "trace/control.h" -#include "target_elf.h" #include "user/cpu_loop.h" #include "crypto/init.h" #include "fd-trans.h" @@ -809,7 +808,7 @@ int main(int argc, char **argv, char **envp) } if (cpu_model == NULL) { - cpu_model = cpu_get_model(get_elf_eflags(execfd)); + cpu_model = get_elf_cpu_model(get_elf_eflags(execfd)); } cpu_type = parse_cpu_option(cpu_model); diff --git a/linux-user/microblaze/elfload.c b/linux-user/microblaze/elfload.c index 73fa78ef14ea5..b92442dfeb2a8 100644 --- a/linux-user/microblaze/elfload.c +++ b/linux-user/microblaze/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "any"; +} diff --git a/linux-user/microblaze/target_elf.h b/linux-user/microblaze/target_elf.h index 8a8f1debff9e7..bfe2997fd237c 100644 --- a/linux-user/microblaze/target_elf.h +++ b/linux-user/microblaze/target_elf.h @@ -7,8 +7,5 @@ #ifndef MICROBLAZE_TARGET_ELF_H #define MICROBLAZE_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "any"; -} + #endif diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c index 73fa78ef14ea5..04e3b7674010b 100644 --- a/linux-user/mips/elfload.c +++ b/linux-user/mips/elfload.c @@ -1 +1,44 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ +#ifdef TARGET_MIPS64 + switch (eflags & EF_MIPS_MACH) { + case EF_MIPS_MACH_OCTEON: + case EF_MIPS_MACH_OCTEON2: + case EF_MIPS_MACH_OCTEON3: + return "Octeon68XX"; + case EF_MIPS_MACH_LS2E: + return "Loongson-2E"; + case EF_MIPS_MACH_LS2F: + return "Loongson-2F"; + case EF_MIPS_MACH_LS3A: + return "Loongson-3A1000"; + default: + break; + } + switch (eflags & EF_MIPS_ARCH) { + case EF_MIPS_ARCH_64R6: + return "I6400"; + case EF_MIPS_ARCH_64R2: + return "MIPS64R2-generic"; + default: + break; + } + return "5KEf"; +#else + if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { + return "mips32r6-generic"; + } + if (eflags & EF_MIPS_NAN2008) { + return "P5600"; + } + return "24Kf"; +#endif +} diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index 71a32315a858a..febf710c7aeb4 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -7,14 +7,5 @@ #ifndef MIPS_TARGET_ELF_H #define MIPS_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { - return "mips32r6-generic"; - } - if (eflags & EF_MIPS_NAN2008) { - return "P5600"; - } - return "24Kf"; -} + #endif diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index 502af9d2781d5..02e6d14840aed 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -7,30 +7,5 @@ #ifndef MIPS64_TARGET_ELF_H #define MIPS64_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - switch (eflags & EF_MIPS_MACH) { - case EF_MIPS_MACH_OCTEON: - case EF_MIPS_MACH_OCTEON2: - case EF_MIPS_MACH_OCTEON3: - return "Octeon68XX"; - case EF_MIPS_MACH_LS2E: - return "Loongson-2E"; - case EF_MIPS_MACH_LS2F: - return "Loongson-2F"; - case EF_MIPS_MACH_LS3A: - return "Loongson-3A1000"; - default: - break; - } - switch (eflags & EF_MIPS_ARCH) { - case EF_MIPS_ARCH_64R6: - return "I6400"; - case EF_MIPS_ARCH_64R2: - return "MIPS64R2-generic"; - default: - break; - } - return "5KEf"; -} + #endif diff --git a/linux-user/openrisc/elfload.c b/linux-user/openrisc/elfload.c index 73fa78ef14ea5..b92442dfeb2a8 100644 --- a/linux-user/openrisc/elfload.c +++ b/linux-user/openrisc/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "any"; +} diff --git a/linux-user/openrisc/target_elf.h b/linux-user/openrisc/target_elf.h index 265ecd30794d4..b34f2ff672da7 100644 --- a/linux-user/openrisc/target_elf.h +++ b/linux-user/openrisc/target_elf.h @@ -7,8 +7,5 @@ #ifndef OPENRISC_TARGET_ELF_H #define OPENRISC_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "any"; -} + #endif diff --git a/linux-user/ppc/elfload.c b/linux-user/ppc/elfload.c index 73fa78ef14ea5..7775dc06fac08 100644 --- a/linux-user/ppc/elfload.c +++ b/linux-user/ppc/elfload.c @@ -1 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ +#ifdef TARGET_PPC64 + return "POWER9"; +#else + return "750"; +#endif +} diff --git a/linux-user/ppc/target_elf.h b/linux-user/ppc/target_elf.h index 061661885423f..8c0a8ea431268 100644 --- a/linux-user/ppc/target_elf.h +++ b/linux-user/ppc/target_elf.h @@ -7,12 +7,5 @@ #ifndef PPC_TARGET_ELF_H #define PPC_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ -#ifdef TARGET_PPC64 - return "POWER9"; -#else - return "750"; -#endif -} + #endif diff --git a/linux-user/riscv/elfload.c b/linux-user/riscv/elfload.c index 73fa78ef14ea5..f92adb73085b9 100644 --- a/linux-user/riscv/elfload.c +++ b/linux-user/riscv/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "max"; +} diff --git a/linux-user/riscv/target_elf.h b/linux-user/riscv/target_elf.h index dedd5956f3afe..bfe86105d0322 100644 --- a/linux-user/riscv/target_elf.h +++ b/linux-user/riscv/target_elf.h @@ -7,8 +7,5 @@ #ifndef RISCV_TARGET_ELF_H #define RISCV_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "max"; -} + #endif diff --git a/linux-user/s390x/elfload.c b/linux-user/s390x/elfload.c index 73fa78ef14ea5..989953a247ed5 100644 --- a/linux-user/s390x/elfload.c +++ b/linux-user/s390x/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "qemu"; +} diff --git a/linux-user/s390x/target_elf.h b/linux-user/s390x/target_elf.h index 8114b59c1d67a..e51b053339f90 100644 --- a/linux-user/s390x/target_elf.h +++ b/linux-user/s390x/target_elf.h @@ -7,8 +7,5 @@ #ifndef S390X_TARGET_ELF_H #define S390X_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "qemu"; -} + #endif diff --git a/linux-user/sh4/elfload.c b/linux-user/sh4/elfload.c index 73fa78ef14ea5..546034ec07ec8 100644 --- a/linux-user/sh4/elfload.c +++ b/linux-user/sh4/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "sh7785"; +} diff --git a/linux-user/sh4/target_elf.h b/linux-user/sh4/target_elf.h index f485e0cef279b..d17011bd752ef 100644 --- a/linux-user/sh4/target_elf.h +++ b/linux-user/sh4/target_elf.h @@ -7,8 +7,5 @@ #ifndef SH4_TARGET_ELF_H #define SH4_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "sh7785"; -} + #endif diff --git a/linux-user/sparc/elfload.c b/linux-user/sparc/elfload.c index 73fa78ef14ea5..243e6f9b66a6e 100644 --- a/linux-user/sparc/elfload.c +++ b/linux-user/sparc/elfload.c @@ -1 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ +#ifdef TARGET_SPARC64 + return "TI UltraSparc II"; +#else + return "Fujitsu MB86904"; +#endif +} diff --git a/linux-user/sparc/target_elf.h b/linux-user/sparc/target_elf.h index a510ceb6129ce..7e46748d261f9 100644 --- a/linux-user/sparc/target_elf.h +++ b/linux-user/sparc/target_elf.h @@ -7,12 +7,5 @@ #ifndef SPARC_TARGET_ELF_H #define SPARC_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ -#ifdef TARGET_SPARC64 - return "TI UltraSparc II"; -#else - return "Fujitsu MB86904"; -#endif -} + #endif diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c index 73fa78ef14ea5..f92adb73085b9 100644 --- a/linux-user/x86_64/elfload.c +++ b/linux-user/x86_64/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "max"; +} diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index 3f628f8d66197..5849f96350161 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -7,8 +7,5 @@ #ifndef X86_64_TARGET_ELF_H #define X86_64_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "max"; -} + #endif diff --git a/linux-user/xtensa/elfload.c b/linux-user/xtensa/elfload.c index 73fa78ef14ea5..e35ba69a109a3 100644 --- a/linux-user/xtensa/elfload.c +++ b/linux-user/xtensa/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return XTENSA_DEFAULT_CPU_MODEL; +} diff --git a/linux-user/xtensa/target_elf.h b/linux-user/xtensa/target_elf.h index a9a3fabd89bea..2c55c22e14b61 100644 --- a/linux-user/xtensa/target_elf.h +++ b/linux-user/xtensa/target_elf.h @@ -8,9 +8,4 @@ #ifndef XTENSA_TARGET_ELF_H #define XTENSA_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return XTENSA_DEFAULT_CPU_MODEL; -} - #endif From 39476538942f0ae1eff5e03f08399ef1eeca0cc0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 20:34:19 -1000 Subject: [PATCH 0310/1794] linux-user: Move get_elf_hwcap to {i386,x86_64}/elfload.c Change the return type to abi_ulong, and pass in the cpu. Duplicate the one line function between i386 and x86_64, as most other additions to elfload.c won't be common. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 9 +-------- linux-user/i386/elfload.c | 5 +++++ linux-user/i386/target_elf.h | 2 ++ linux-user/loader.h | 3 +++ linux-user/x86_64/elfload.c | 5 +++++ linux-user/x86_64/target_elf.h | 2 ++ 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 4ca8c39dc26b2..0c62c249e9f1e 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -148,14 +148,7 @@ typedef abi_int target_pid_t; #ifdef TARGET_I386 -#define ELF_HWCAP get_elf_hwcap() - -static uint32_t get_elf_hwcap(void) -{ - X86CPU *cpu = X86_CPU(thread_cpu); - - return cpu->env.features[FEAT_1_EDX]; -} +#define ELF_HWCAP get_elf_hwcap(thread_cpu) #ifdef TARGET_X86_64 #define ELF_CLASS ELFCLASS64 diff --git a/linux-user/i386/elfload.c b/linux-user/i386/elfload.c index f92adb73085b9..f99336e73c09c 100644 --- a/linux-user/i386/elfload.c +++ b/linux-user/i386/elfload.c @@ -9,3 +9,8 @@ const char *get_elf_cpu_model(uint32_t eflags) { return "max"; } + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + return cpu_env(cs)->features[FEAT_1_EDX]; +} diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index e6f0d8fa4e679..802395af3a041 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -8,4 +8,6 @@ #ifndef I386_TARGET_ELF_H #define I386_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif diff --git a/linux-user/loader.h b/linux-user/loader.h index 75ee9975a08d3..457bb36daa4d3 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -101,6 +101,9 @@ extern unsigned long guest_stack_size; /* Note that Elf32 and Elf64 use uint32_t for e_flags. */ const char *get_elf_cpu_model(uint32_t eflags); +#if defined(TARGET_I386) || defined(TARGET_X86_64) +abi_ulong get_elf_hwcap(CPUState *cs); +#endif #if defined(TARGET_S390X) || defined(TARGET_AARCH64) || defined(TARGET_ARM) uint32_t get_elf_hwcap(void); const char *elf_hwcap_str(uint32_t bit); diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c index f92adb73085b9..f99336e73c09c 100644 --- a/linux-user/x86_64/elfload.c +++ b/linux-user/x86_64/elfload.c @@ -9,3 +9,8 @@ const char *get_elf_cpu_model(uint32_t eflags) { return "max"; } + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + return cpu_env(cs)->features[FEAT_1_EDX]; +} diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index 5849f96350161..03483bad57360 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -8,4 +8,6 @@ #ifndef X86_64_TARGET_ELF_H #define X86_64_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif From 2d0687a514c63956de6215978acdda4f55c190dd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 20:52:01 -1000 Subject: [PATCH 0311/1794] linux-user: Move hwcap functions to {arm,aarch64}/elfload.c For get_elf_hwcap and get_elf_hwcap2, change the return type to abi_ulong, and pass in the cpu. We must do these targets at the same time because of the ifdef dependency between TARGET_AARCH64 and TARGET_ARM. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/elfload.c | 333 +++++++++++++++++++++ linux-user/aarch64/target_elf.h | 3 + linux-user/arm/elfload.c | 161 ++++++++++ linux-user/arm/target_elf.h | 3 + linux-user/arm/target_proc.h | 4 +- linux-user/elfload.c | 505 +------------------------------- linux-user/loader.h | 10 +- 7 files changed, 510 insertions(+), 509 deletions(-) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index b92442dfeb2a8..92c8ea62c6f37 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -3,9 +3,342 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target/arm/cpu-features.h" const char *get_elf_cpu_model(uint32_t eflags) { return "any"; } + +enum { + ARM_HWCAP_A64_FP = 1 << 0, + ARM_HWCAP_A64_ASIMD = 1 << 1, + ARM_HWCAP_A64_EVTSTRM = 1 << 2, + ARM_HWCAP_A64_AES = 1 << 3, + ARM_HWCAP_A64_PMULL = 1 << 4, + ARM_HWCAP_A64_SHA1 = 1 << 5, + ARM_HWCAP_A64_SHA2 = 1 << 6, + ARM_HWCAP_A64_CRC32 = 1 << 7, + ARM_HWCAP_A64_ATOMICS = 1 << 8, + ARM_HWCAP_A64_FPHP = 1 << 9, + ARM_HWCAP_A64_ASIMDHP = 1 << 10, + ARM_HWCAP_A64_CPUID = 1 << 11, + ARM_HWCAP_A64_ASIMDRDM = 1 << 12, + ARM_HWCAP_A64_JSCVT = 1 << 13, + ARM_HWCAP_A64_FCMA = 1 << 14, + ARM_HWCAP_A64_LRCPC = 1 << 15, + ARM_HWCAP_A64_DCPOP = 1 << 16, + ARM_HWCAP_A64_SHA3 = 1 << 17, + ARM_HWCAP_A64_SM3 = 1 << 18, + ARM_HWCAP_A64_SM4 = 1 << 19, + ARM_HWCAP_A64_ASIMDDP = 1 << 20, + ARM_HWCAP_A64_SHA512 = 1 << 21, + ARM_HWCAP_A64_SVE = 1 << 22, + ARM_HWCAP_A64_ASIMDFHM = 1 << 23, + ARM_HWCAP_A64_DIT = 1 << 24, + ARM_HWCAP_A64_USCAT = 1 << 25, + ARM_HWCAP_A64_ILRCPC = 1 << 26, + ARM_HWCAP_A64_FLAGM = 1 << 27, + ARM_HWCAP_A64_SSBS = 1 << 28, + ARM_HWCAP_A64_SB = 1 << 29, + ARM_HWCAP_A64_PACA = 1 << 30, + ARM_HWCAP_A64_PACG = 1ULL << 31, + ARM_HWCAP_A64_GCS = 1ULL << 32, + ARM_HWCAP_A64_CMPBR = 1ULL << 33, + ARM_HWCAP_A64_FPRCVT = 1ULL << 34, + ARM_HWCAP_A64_F8MM8 = 1ULL << 35, + ARM_HWCAP_A64_F8MM4 = 1ULL << 36, + ARM_HWCAP_A64_SVE_F16MM = 1ULL << 37, + ARM_HWCAP_A64_SVE_ELTPERM = 1ULL << 38, + ARM_HWCAP_A64_SVE_AES2 = 1ULL << 39, + ARM_HWCAP_A64_SVE_BFSCALE = 1ULL << 40, + ARM_HWCAP_A64_SVE2P2 = 1ULL << 41, + ARM_HWCAP_A64_SME2P2 = 1ULL << 42, + ARM_HWCAP_A64_SME_SBITPERM = 1ULL << 43, + ARM_HWCAP_A64_SME_AES = 1ULL << 44, + ARM_HWCAP_A64_SME_SFEXPA = 1ULL << 45, + ARM_HWCAP_A64_SME_STMOP = 1ULL << 46, + ARM_HWCAP_A64_SME_SMOP4 = 1ULL << 47, + + ARM_HWCAP2_A64_DCPODP = 1 << 0, + ARM_HWCAP2_A64_SVE2 = 1 << 1, + ARM_HWCAP2_A64_SVEAES = 1 << 2, + ARM_HWCAP2_A64_SVEPMULL = 1 << 3, + ARM_HWCAP2_A64_SVEBITPERM = 1 << 4, + ARM_HWCAP2_A64_SVESHA3 = 1 << 5, + ARM_HWCAP2_A64_SVESM4 = 1 << 6, + ARM_HWCAP2_A64_FLAGM2 = 1 << 7, + ARM_HWCAP2_A64_FRINT = 1 << 8, + ARM_HWCAP2_A64_SVEI8MM = 1 << 9, + ARM_HWCAP2_A64_SVEF32MM = 1 << 10, + ARM_HWCAP2_A64_SVEF64MM = 1 << 11, + ARM_HWCAP2_A64_SVEBF16 = 1 << 12, + ARM_HWCAP2_A64_I8MM = 1 << 13, + ARM_HWCAP2_A64_BF16 = 1 << 14, + ARM_HWCAP2_A64_DGH = 1 << 15, + ARM_HWCAP2_A64_RNG = 1 << 16, + ARM_HWCAP2_A64_BTI = 1 << 17, + ARM_HWCAP2_A64_MTE = 1 << 18, + ARM_HWCAP2_A64_ECV = 1 << 19, + ARM_HWCAP2_A64_AFP = 1 << 20, + ARM_HWCAP2_A64_RPRES = 1 << 21, + ARM_HWCAP2_A64_MTE3 = 1 << 22, + ARM_HWCAP2_A64_SME = 1 << 23, + ARM_HWCAP2_A64_SME_I16I64 = 1 << 24, + ARM_HWCAP2_A64_SME_F64F64 = 1 << 25, + ARM_HWCAP2_A64_SME_I8I32 = 1 << 26, + ARM_HWCAP2_A64_SME_F16F32 = 1 << 27, + 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, + ARM_HWCAP2_A64_SVE_B16B16 = 1ULL << 45, + ARM_HWCAP2_A64_LRCPC3 = 1ULL << 46, + ARM_HWCAP2_A64_LSE128 = 1ULL << 47, + ARM_HWCAP2_A64_FPMR = 1ULL << 48, + ARM_HWCAP2_A64_LUT = 1ULL << 49, + ARM_HWCAP2_A64_FAMINMAX = 1ULL << 50, + ARM_HWCAP2_A64_F8CVT = 1ULL << 51, + ARM_HWCAP2_A64_F8FMA = 1ULL << 52, + ARM_HWCAP2_A64_F8DP4 = 1ULL << 53, + ARM_HWCAP2_A64_F8DP2 = 1ULL << 54, + ARM_HWCAP2_A64_F8E4M3 = 1ULL << 55, + ARM_HWCAP2_A64_F8E5M2 = 1ULL << 56, + ARM_HWCAP2_A64_SME_LUTV2 = 1ULL << 57, + ARM_HWCAP2_A64_SME_F8F16 = 1ULL << 58, + ARM_HWCAP2_A64_SME_F8F32 = 1ULL << 59, + ARM_HWCAP2_A64_SME_SF8FMA = 1ULL << 60, + ARM_HWCAP2_A64_SME_SF8DP4 = 1ULL << 61, + ARM_HWCAP2_A64_SME_SF8DP2 = 1ULL << 62, + ARM_HWCAP2_A64_POE = 1ULL << 63, +}; + +#define GET_FEATURE_ID(feat, hwcap) \ + do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + abi_ulong hwcaps = 0; + + hwcaps |= ARM_HWCAP_A64_FP; + hwcaps |= ARM_HWCAP_A64_ASIMD; + hwcaps |= ARM_HWCAP_A64_CPUID; + + /* probe for the extra features */ + + GET_FEATURE_ID(aa64_aes, ARM_HWCAP_A64_AES); + GET_FEATURE_ID(aa64_pmull, ARM_HWCAP_A64_PMULL); + GET_FEATURE_ID(aa64_sha1, ARM_HWCAP_A64_SHA1); + GET_FEATURE_ID(aa64_sha256, ARM_HWCAP_A64_SHA2); + GET_FEATURE_ID(aa64_sha512, ARM_HWCAP_A64_SHA512); + GET_FEATURE_ID(aa64_crc32, ARM_HWCAP_A64_CRC32); + GET_FEATURE_ID(aa64_sha3, ARM_HWCAP_A64_SHA3); + GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3); + GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4); + GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); + GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS); + GET_FEATURE_ID(aa64_lse2, ARM_HWCAP_A64_USCAT); + GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM); + GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP); + GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA); + GET_FEATURE_ID(aa64_sve, ARM_HWCAP_A64_SVE); + GET_FEATURE_ID(aa64_pauth, ARM_HWCAP_A64_PACA | ARM_HWCAP_A64_PACG); + GET_FEATURE_ID(aa64_fhm, ARM_HWCAP_A64_ASIMDFHM); + GET_FEATURE_ID(aa64_dit, ARM_HWCAP_A64_DIT); + GET_FEATURE_ID(aa64_jscvt, ARM_HWCAP_A64_JSCVT); + GET_FEATURE_ID(aa64_sb, ARM_HWCAP_A64_SB); + GET_FEATURE_ID(aa64_condm_4, ARM_HWCAP_A64_FLAGM); + GET_FEATURE_ID(aa64_dcpop, ARM_HWCAP_A64_DCPOP); + GET_FEATURE_ID(aa64_rcpc_8_3, ARM_HWCAP_A64_LRCPC); + GET_FEATURE_ID(aa64_rcpc_8_4, ARM_HWCAP_A64_ILRCPC); + + return hwcaps; +} + +abi_ulong get_elf_hwcap2(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + abi_ulong hwcaps = 0; + + GET_FEATURE_ID(aa64_dcpodp, ARM_HWCAP2_A64_DCPODP); + GET_FEATURE_ID(aa64_sve2, ARM_HWCAP2_A64_SVE2); + GET_FEATURE_ID(aa64_sve2_aes, ARM_HWCAP2_A64_SVEAES); + GET_FEATURE_ID(aa64_sve2_pmull128, ARM_HWCAP2_A64_SVEPMULL); + GET_FEATURE_ID(aa64_sve2_bitperm, ARM_HWCAP2_A64_SVEBITPERM); + GET_FEATURE_ID(aa64_sve2_sha3, ARM_HWCAP2_A64_SVESHA3); + GET_FEATURE_ID(aa64_sve2_sm4, ARM_HWCAP2_A64_SVESM4); + GET_FEATURE_ID(aa64_condm_5, ARM_HWCAP2_A64_FLAGM2); + GET_FEATURE_ID(aa64_frint, ARM_HWCAP2_A64_FRINT); + GET_FEATURE_ID(aa64_sve_i8mm, ARM_HWCAP2_A64_SVEI8MM); + GET_FEATURE_ID(aa64_sve_f32mm, ARM_HWCAP2_A64_SVEF32MM); + GET_FEATURE_ID(aa64_sve_f64mm, ARM_HWCAP2_A64_SVEF64MM); + GET_FEATURE_ID(aa64_sve_bf16, ARM_HWCAP2_A64_SVEBF16); + GET_FEATURE_ID(aa64_i8mm, ARM_HWCAP2_A64_I8MM); + GET_FEATURE_ID(aa64_bf16, ARM_HWCAP2_A64_BF16); + GET_FEATURE_ID(aa64_rndr, ARM_HWCAP2_A64_RNG); + GET_FEATURE_ID(aa64_bti, ARM_HWCAP2_A64_BTI); + GET_FEATURE_ID(aa64_mte, ARM_HWCAP2_A64_MTE); + GET_FEATURE_ID(aa64_mte3, ARM_HWCAP2_A64_MTE3); + GET_FEATURE_ID(aa64_sme, (ARM_HWCAP2_A64_SME | + ARM_HWCAP2_A64_SME_F32F32 | + ARM_HWCAP2_A64_SME_B16F32 | + ARM_HWCAP2_A64_SME_F16F32 | + ARM_HWCAP2_A64_SME_I8I32)); + 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); + GET_FEATURE_ID(aa64_mops, ARM_HWCAP2_A64_MOPS); + GET_FEATURE_ID(aa64_sve2p1, ARM_HWCAP2_A64_SVE2P1); + GET_FEATURE_ID(aa64_sme2, (ARM_HWCAP2_A64_SME2 | + ARM_HWCAP2_A64_SME_I16I32 | + ARM_HWCAP2_A64_SME_BI32I32)); + GET_FEATURE_ID(aa64_sme2p1, ARM_HWCAP2_A64_SME2P1); + GET_FEATURE_ID(aa64_sme_b16b16, ARM_HWCAP2_A64_SME_B16B16); + GET_FEATURE_ID(aa64_sme_f16f16, ARM_HWCAP2_A64_SME_F16F16); + GET_FEATURE_ID(aa64_sve_b16b16, ARM_HWCAP2_A64_SVE_B16B16); + + return hwcaps; +} + +const char *elf_hwcap_str(uint32_t bit) +{ + static const char * const hwcap_str[] = { + [__builtin_ctz(ARM_HWCAP_A64_FP )] = "fp", + [__builtin_ctz(ARM_HWCAP_A64_ASIMD )] = "asimd", + [__builtin_ctz(ARM_HWCAP_A64_EVTSTRM )] = "evtstrm", + [__builtin_ctz(ARM_HWCAP_A64_AES )] = "aes", + [__builtin_ctz(ARM_HWCAP_A64_PMULL )] = "pmull", + [__builtin_ctz(ARM_HWCAP_A64_SHA1 )] = "sha1", + [__builtin_ctz(ARM_HWCAP_A64_SHA2 )] = "sha2", + [__builtin_ctz(ARM_HWCAP_A64_CRC32 )] = "crc32", + [__builtin_ctz(ARM_HWCAP_A64_ATOMICS )] = "atomics", + [__builtin_ctz(ARM_HWCAP_A64_FPHP )] = "fphp", + [__builtin_ctz(ARM_HWCAP_A64_ASIMDHP )] = "asimdhp", + [__builtin_ctz(ARM_HWCAP_A64_CPUID )] = "cpuid", + [__builtin_ctz(ARM_HWCAP_A64_ASIMDRDM)] = "asimdrdm", + [__builtin_ctz(ARM_HWCAP_A64_JSCVT )] = "jscvt", + [__builtin_ctz(ARM_HWCAP_A64_FCMA )] = "fcma", + [__builtin_ctz(ARM_HWCAP_A64_LRCPC )] = "lrcpc", + [__builtin_ctz(ARM_HWCAP_A64_DCPOP )] = "dcpop", + [__builtin_ctz(ARM_HWCAP_A64_SHA3 )] = "sha3", + [__builtin_ctz(ARM_HWCAP_A64_SM3 )] = "sm3", + [__builtin_ctz(ARM_HWCAP_A64_SM4 )] = "sm4", + [__builtin_ctz(ARM_HWCAP_A64_ASIMDDP )] = "asimddp", + [__builtin_ctz(ARM_HWCAP_A64_SHA512 )] = "sha512", + [__builtin_ctz(ARM_HWCAP_A64_SVE )] = "sve", + [__builtin_ctz(ARM_HWCAP_A64_ASIMDFHM)] = "asimdfhm", + [__builtin_ctz(ARM_HWCAP_A64_DIT )] = "dit", + [__builtin_ctz(ARM_HWCAP_A64_USCAT )] = "uscat", + [__builtin_ctz(ARM_HWCAP_A64_ILRCPC )] = "ilrcpc", + [__builtin_ctz(ARM_HWCAP_A64_FLAGM )] = "flagm", + [__builtin_ctz(ARM_HWCAP_A64_SSBS )] = "ssbs", + [__builtin_ctz(ARM_HWCAP_A64_SB )] = "sb", + [__builtin_ctz(ARM_HWCAP_A64_PACA )] = "paca", + [__builtin_ctz(ARM_HWCAP_A64_PACG )] = "pacg", + [__builtin_ctzll(ARM_HWCAP_A64_GCS )] = "gcs", + [__builtin_ctzll(ARM_HWCAP_A64_CMPBR )] = "cmpbr", + [__builtin_ctzll(ARM_HWCAP_A64_FPRCVT)] = "fprcvt", + [__builtin_ctzll(ARM_HWCAP_A64_F8MM8 )] = "f8mm8", + [__builtin_ctzll(ARM_HWCAP_A64_F8MM4 )] = "f8mm4", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_F16MM)] = "svef16mm", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_ELTPERM)] = "sveeltperm", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_AES2)] = "sveaes2", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_BFSCALE)] = "svebfscale", + [__builtin_ctzll(ARM_HWCAP_A64_SVE2P2)] = "sve2p2", + [__builtin_ctzll(ARM_HWCAP_A64_SME2P2)] = "sme2p2", + [__builtin_ctzll(ARM_HWCAP_A64_SME_SBITPERM)] = "smesbitperm", + [__builtin_ctzll(ARM_HWCAP_A64_SME_AES)] = "smeaes", + [__builtin_ctzll(ARM_HWCAP_A64_SME_SFEXPA)] = "smesfexpa", + [__builtin_ctzll(ARM_HWCAP_A64_SME_STMOP)] = "smestmop", + [__builtin_ctzll(ARM_HWCAP_A64_SME_SMOP4)] = "smesmop4", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} + +const char *elf_hwcap2_str(uint32_t bit) +{ + static const char * const hwcap_str[] = { + [__builtin_ctz(ARM_HWCAP2_A64_DCPODP )] = "dcpodp", + [__builtin_ctz(ARM_HWCAP2_A64_SVE2 )] = "sve2", + [__builtin_ctz(ARM_HWCAP2_A64_SVEAES )] = "sveaes", + [__builtin_ctz(ARM_HWCAP2_A64_SVEPMULL )] = "svepmull", + [__builtin_ctz(ARM_HWCAP2_A64_SVEBITPERM )] = "svebitperm", + [__builtin_ctz(ARM_HWCAP2_A64_SVESHA3 )] = "svesha3", + [__builtin_ctz(ARM_HWCAP2_A64_SVESM4 )] = "svesm4", + [__builtin_ctz(ARM_HWCAP2_A64_FLAGM2 )] = "flagm2", + [__builtin_ctz(ARM_HWCAP2_A64_FRINT )] = "frint", + [__builtin_ctz(ARM_HWCAP2_A64_SVEI8MM )] = "svei8mm", + [__builtin_ctz(ARM_HWCAP2_A64_SVEF32MM )] = "svef32mm", + [__builtin_ctz(ARM_HWCAP2_A64_SVEF64MM )] = "svef64mm", + [__builtin_ctz(ARM_HWCAP2_A64_SVEBF16 )] = "svebf16", + [__builtin_ctz(ARM_HWCAP2_A64_I8MM )] = "i8mm", + [__builtin_ctz(ARM_HWCAP2_A64_BF16 )] = "bf16", + [__builtin_ctz(ARM_HWCAP2_A64_DGH )] = "dgh", + [__builtin_ctz(ARM_HWCAP2_A64_RNG )] = "rng", + [__builtin_ctz(ARM_HWCAP2_A64_BTI )] = "bti", + [__builtin_ctz(ARM_HWCAP2_A64_MTE )] = "mte", + [__builtin_ctz(ARM_HWCAP2_A64_ECV )] = "ecv", + [__builtin_ctz(ARM_HWCAP2_A64_AFP )] = "afp", + [__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 )] = "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", + [__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", + [__builtin_ctzll(ARM_HWCAP2_A64_SVE_B16B16 )] = "sveb16b16", + [__builtin_ctzll(ARM_HWCAP2_A64_LRCPC3 )] = "lrcpc3", + [__builtin_ctzll(ARM_HWCAP2_A64_LSE128 )] = "lse128", + [__builtin_ctzll(ARM_HWCAP2_A64_FPMR )] = "fpmr", + [__builtin_ctzll(ARM_HWCAP2_A64_LUT )] = "lut", + [__builtin_ctzll(ARM_HWCAP2_A64_FAMINMAX )] = "faminmax", + [__builtin_ctzll(ARM_HWCAP2_A64_F8CVT )] = "f8cvt", + [__builtin_ctzll(ARM_HWCAP2_A64_F8FMA )] = "f8fma", + [__builtin_ctzll(ARM_HWCAP2_A64_F8DP4 )] = "f8dp4", + [__builtin_ctzll(ARM_HWCAP2_A64_F8DP2 )] = "f8dp2", + [__builtin_ctzll(ARM_HWCAP2_A64_F8E4M3 )] = "f8e4m3", + [__builtin_ctzll(ARM_HWCAP2_A64_F8E5M2 )] = "f8e5m2", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_LUTV2 )] = "smelutv2", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_F8F16 )] = "smef8f16", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_F8F32 )] = "smef8f32", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_SF8DP4 )] = "smesf8dp4", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_SF8DP2 )] = "smesf8dp2", + [__builtin_ctzll(ARM_HWCAP2_A64_POE )] = "poe", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index d955b3d07f901..77108f3cb0cda 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -8,4 +8,7 @@ #ifndef AARCH64_TARGET_ELF_H #define AARCH64_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_HWCAP2 1 + #endif diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index b92442dfeb2a8..c7561b005b483 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -3,9 +3,170 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target/arm/cpu-features.h" const char *get_elf_cpu_model(uint32_t eflags) { return "any"; } + +enum +{ + ARM_HWCAP_ARM_SWP = 1 << 0, + ARM_HWCAP_ARM_HALF = 1 << 1, + ARM_HWCAP_ARM_THUMB = 1 << 2, + ARM_HWCAP_ARM_26BIT = 1 << 3, + ARM_HWCAP_ARM_FAST_MULT = 1 << 4, + ARM_HWCAP_ARM_FPA = 1 << 5, + ARM_HWCAP_ARM_VFP = 1 << 6, + ARM_HWCAP_ARM_EDSP = 1 << 7, + ARM_HWCAP_ARM_JAVA = 1 << 8, + ARM_HWCAP_ARM_IWMMXT = 1 << 9, + ARM_HWCAP_ARM_CRUNCH = 1 << 10, + ARM_HWCAP_ARM_THUMBEE = 1 << 11, + ARM_HWCAP_ARM_NEON = 1 << 12, + ARM_HWCAP_ARM_VFPv3 = 1 << 13, + ARM_HWCAP_ARM_VFPv3D16 = 1 << 14, + ARM_HWCAP_ARM_TLS = 1 << 15, + ARM_HWCAP_ARM_VFPv4 = 1 << 16, + ARM_HWCAP_ARM_IDIVA = 1 << 17, + ARM_HWCAP_ARM_IDIVT = 1 << 18, + 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 { + ARM_HWCAP2_ARM_AES = 1 << 0, + ARM_HWCAP2_ARM_PMULL = 1 << 1, + 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, +}; + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + abi_ulong hwcaps = 0; + + hwcaps |= ARM_HWCAP_ARM_SWP; + hwcaps |= ARM_HWCAP_ARM_HALF; + hwcaps |= ARM_HWCAP_ARM_THUMB; + hwcaps |= ARM_HWCAP_ARM_FAST_MULT; + + /* probe for the extra features */ +#define GET_FEATURE(feat, hwcap) \ + do { if (arm_feature(&cpu->env, feat)) { hwcaps |= hwcap; } } while (0) + +#define GET_FEATURE_ID(feat, hwcap) \ + do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) + + /* EDSP is in v5TE and above, but all our v5 CPUs are v5TE */ + GET_FEATURE(ARM_FEATURE_V5, ARM_HWCAP_ARM_EDSP); + GET_FEATURE(ARM_FEATURE_IWMMXT, ARM_HWCAP_ARM_IWMMXT); + GET_FEATURE(ARM_FEATURE_THUMB2EE, ARM_HWCAP_ARM_THUMBEE); + GET_FEATURE(ARM_FEATURE_NEON, ARM_HWCAP_ARM_NEON); + GET_FEATURE(ARM_FEATURE_V6K, ARM_HWCAP_ARM_TLS); + GET_FEATURE(ARM_FEATURE_LPAE, ARM_HWCAP_ARM_LPAE); + GET_FEATURE_ID(aa32_arm_div, ARM_HWCAP_ARM_IDIVA); + GET_FEATURE_ID(aa32_thumb_div, ARM_HWCAP_ARM_IDIVT); + GET_FEATURE_ID(aa32_vfp, ARM_HWCAP_ARM_VFP); + + if (cpu_isar_feature(aa32_fpsp_v3, cpu) || + cpu_isar_feature(aa32_fpdp_v3, cpu)) { + hwcaps |= ARM_HWCAP_ARM_VFPv3; + if (cpu_isar_feature(aa32_simd_r32, cpu)) { + hwcaps |= ARM_HWCAP_ARM_VFPD32; + } else { + hwcaps |= ARM_HWCAP_ARM_VFPv3D16; + } + } + 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; +} + +abi_ulong get_elf_hwcap2(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + abi_ulong hwcaps = 0; + + GET_FEATURE_ID(aa32_aes, ARM_HWCAP2_ARM_AES); + GET_FEATURE_ID(aa32_pmull, ARM_HWCAP2_ARM_PMULL); + 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; +} + +const char *elf_hwcap_str(uint32_t bit) +{ + static const char *hwcap_str[] = { + [__builtin_ctz(ARM_HWCAP_ARM_SWP )] = "swp", + [__builtin_ctz(ARM_HWCAP_ARM_HALF )] = "half", + [__builtin_ctz(ARM_HWCAP_ARM_THUMB )] = "thumb", + [__builtin_ctz(ARM_HWCAP_ARM_26BIT )] = "26bit", + [__builtin_ctz(ARM_HWCAP_ARM_FAST_MULT)] = "fast_mult", + [__builtin_ctz(ARM_HWCAP_ARM_FPA )] = "fpa", + [__builtin_ctz(ARM_HWCAP_ARM_VFP )] = "vfp", + [__builtin_ctz(ARM_HWCAP_ARM_EDSP )] = "edsp", + [__builtin_ctz(ARM_HWCAP_ARM_JAVA )] = "java", + [__builtin_ctz(ARM_HWCAP_ARM_IWMMXT )] = "iwmmxt", + [__builtin_ctz(ARM_HWCAP_ARM_CRUNCH )] = "crunch", + [__builtin_ctz(ARM_HWCAP_ARM_THUMBEE )] = "thumbee", + [__builtin_ctz(ARM_HWCAP_ARM_NEON )] = "neon", + [__builtin_ctz(ARM_HWCAP_ARM_VFPv3 )] = "vfpv3", + [__builtin_ctz(ARM_HWCAP_ARM_VFPv3D16 )] = "vfpv3d16", + [__builtin_ctz(ARM_HWCAP_ARM_TLS )] = "tls", + [__builtin_ctz(ARM_HWCAP_ARM_VFPv4 )] = "vfpv4", + [__builtin_ctz(ARM_HWCAP_ARM_IDIVA )] = "idiva", + [__builtin_ctz(ARM_HWCAP_ARM_IDIVT )] = "idivt", + [__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; +} + +const char *elf_hwcap2_str(uint32_t bit) +{ + static const char *hwcap_str[] = { + [__builtin_ctz(ARM_HWCAP2_ARM_AES )] = "aes", + [__builtin_ctz(ARM_HWCAP2_ARM_PMULL)] = "pmull", + [__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; +} diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index 2abb27a733775..90470bd87b6dd 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -8,4 +8,7 @@ #ifndef ARM_TARGET_ELF_H #define ARM_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_HWCAP2 1 + #endif diff --git a/linux-user/arm/target_proc.h b/linux-user/arm/target_proc.h index ac75af9ca6498..a4cd6948c60d5 100644 --- a/linux-user/arm/target_proc.h +++ b/linux-user/arm/target_proc.h @@ -10,8 +10,8 @@ static int open_cpuinfo(CPUArchState *cpu_env, int fd) { ARMCPU *cpu = env_archcpu(cpu_env); int arch, midr_rev, midr_part, midr_var, midr_impl; - target_ulong elf_hwcap = get_elf_hwcap(); - target_ulong elf_hwcap2 = get_elf_hwcap2(); + target_ulong elf_hwcap = get_elf_hwcap(env_cpu(cpu_env)); + target_ulong elf_hwcap2 = get_elf_hwcap2(env_cpu(cpu_env)); const char *elf_name; int num_cpus, len_part, len_var; diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0c62c249e9f1e..149d1313c0a70 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -401,48 +401,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUARMState *en #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -enum -{ - ARM_HWCAP_ARM_SWP = 1 << 0, - ARM_HWCAP_ARM_HALF = 1 << 1, - ARM_HWCAP_ARM_THUMB = 1 << 2, - ARM_HWCAP_ARM_26BIT = 1 << 3, - ARM_HWCAP_ARM_FAST_MULT = 1 << 4, - ARM_HWCAP_ARM_FPA = 1 << 5, - ARM_HWCAP_ARM_VFP = 1 << 6, - ARM_HWCAP_ARM_EDSP = 1 << 7, - ARM_HWCAP_ARM_JAVA = 1 << 8, - ARM_HWCAP_ARM_IWMMXT = 1 << 9, - ARM_HWCAP_ARM_CRUNCH = 1 << 10, - ARM_HWCAP_ARM_THUMBEE = 1 << 11, - ARM_HWCAP_ARM_NEON = 1 << 12, - ARM_HWCAP_ARM_VFPv3 = 1 << 13, - ARM_HWCAP_ARM_VFPv3D16 = 1 << 14, - ARM_HWCAP_ARM_TLS = 1 << 15, - ARM_HWCAP_ARM_VFPv4 = 1 << 16, - ARM_HWCAP_ARM_IDIVA = 1 << 17, - ARM_HWCAP_ARM_IDIVT = 1 << 18, - 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 { - ARM_HWCAP2_ARM_AES = 1 << 0, - ARM_HWCAP2_ARM_PMULL = 1 << 1, - 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 */ #define HI_COMMPAGE (intptr_t)0xffff0f00u @@ -491,129 +449,8 @@ static bool init_guest_commpage(void) return true; } -#define ELF_HWCAP get_elf_hwcap() -#define ELF_HWCAP2 get_elf_hwcap2() - -uint32_t get_elf_hwcap(void) -{ - ARMCPU *cpu = ARM_CPU(thread_cpu); - uint32_t hwcaps = 0; - - hwcaps |= ARM_HWCAP_ARM_SWP; - hwcaps |= ARM_HWCAP_ARM_HALF; - hwcaps |= ARM_HWCAP_ARM_THUMB; - hwcaps |= ARM_HWCAP_ARM_FAST_MULT; - - /* probe for the extra features */ -#define GET_FEATURE(feat, hwcap) \ - do { if (arm_feature(&cpu->env, feat)) { hwcaps |= hwcap; } } while (0) - -#define GET_FEATURE_ID(feat, hwcap) \ - do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) - - /* EDSP is in v5TE and above, but all our v5 CPUs are v5TE */ - GET_FEATURE(ARM_FEATURE_V5, ARM_HWCAP_ARM_EDSP); - GET_FEATURE(ARM_FEATURE_IWMMXT, ARM_HWCAP_ARM_IWMMXT); - GET_FEATURE(ARM_FEATURE_THUMB2EE, ARM_HWCAP_ARM_THUMBEE); - GET_FEATURE(ARM_FEATURE_NEON, ARM_HWCAP_ARM_NEON); - GET_FEATURE(ARM_FEATURE_V6K, ARM_HWCAP_ARM_TLS); - GET_FEATURE(ARM_FEATURE_LPAE, ARM_HWCAP_ARM_LPAE); - GET_FEATURE_ID(aa32_arm_div, ARM_HWCAP_ARM_IDIVA); - GET_FEATURE_ID(aa32_thumb_div, ARM_HWCAP_ARM_IDIVT); - GET_FEATURE_ID(aa32_vfp, ARM_HWCAP_ARM_VFP); - - if (cpu_isar_feature(aa32_fpsp_v3, cpu) || - cpu_isar_feature(aa32_fpdp_v3, cpu)) { - hwcaps |= ARM_HWCAP_ARM_VFPv3; - if (cpu_isar_feature(aa32_simd_r32, cpu)) { - hwcaps |= ARM_HWCAP_ARM_VFPD32; - } else { - hwcaps |= ARM_HWCAP_ARM_VFPv3D16; - } - } - 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; -} - -uint64_t get_elf_hwcap2(void) -{ - ARMCPU *cpu = ARM_CPU(thread_cpu); - uint64_t hwcaps = 0; - - GET_FEATURE_ID(aa32_aes, ARM_HWCAP2_ARM_AES); - GET_FEATURE_ID(aa32_pmull, ARM_HWCAP2_ARM_PMULL); - 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; -} - -const char *elf_hwcap_str(uint32_t bit) -{ - static const char *hwcap_str[] = { - [__builtin_ctz(ARM_HWCAP_ARM_SWP )] = "swp", - [__builtin_ctz(ARM_HWCAP_ARM_HALF )] = "half", - [__builtin_ctz(ARM_HWCAP_ARM_THUMB )] = "thumb", - [__builtin_ctz(ARM_HWCAP_ARM_26BIT )] = "26bit", - [__builtin_ctz(ARM_HWCAP_ARM_FAST_MULT)] = "fast_mult", - [__builtin_ctz(ARM_HWCAP_ARM_FPA )] = "fpa", - [__builtin_ctz(ARM_HWCAP_ARM_VFP )] = "vfp", - [__builtin_ctz(ARM_HWCAP_ARM_EDSP )] = "edsp", - [__builtin_ctz(ARM_HWCAP_ARM_JAVA )] = "java", - [__builtin_ctz(ARM_HWCAP_ARM_IWMMXT )] = "iwmmxt", - [__builtin_ctz(ARM_HWCAP_ARM_CRUNCH )] = "crunch", - [__builtin_ctz(ARM_HWCAP_ARM_THUMBEE )] = "thumbee", - [__builtin_ctz(ARM_HWCAP_ARM_NEON )] = "neon", - [__builtin_ctz(ARM_HWCAP_ARM_VFPv3 )] = "vfpv3", - [__builtin_ctz(ARM_HWCAP_ARM_VFPv3D16 )] = "vfpv3d16", - [__builtin_ctz(ARM_HWCAP_ARM_TLS )] = "tls", - [__builtin_ctz(ARM_HWCAP_ARM_VFPv4 )] = "vfpv4", - [__builtin_ctz(ARM_HWCAP_ARM_IDIVA )] = "idiva", - [__builtin_ctz(ARM_HWCAP_ARM_IDIVT )] = "idivt", - [__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; -} - -const char *elf_hwcap2_str(uint32_t bit) -{ - static const char *hwcap_str[] = { - [__builtin_ctz(ARM_HWCAP2_ARM_AES )] = "aes", - [__builtin_ctz(ARM_HWCAP2_ARM_PMULL)] = "pmull", - [__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; -} - -#undef GET_FEATURE -#undef GET_FEATURE_ID +#define ELF_HWCAP get_elf_hwcap(thread_cpu) +#define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) #define ELF_PLATFORM get_elf_platform() @@ -702,342 +539,8 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -enum { - ARM_HWCAP_A64_FP = 1 << 0, - ARM_HWCAP_A64_ASIMD = 1 << 1, - ARM_HWCAP_A64_EVTSTRM = 1 << 2, - ARM_HWCAP_A64_AES = 1 << 3, - ARM_HWCAP_A64_PMULL = 1 << 4, - ARM_HWCAP_A64_SHA1 = 1 << 5, - ARM_HWCAP_A64_SHA2 = 1 << 6, - ARM_HWCAP_A64_CRC32 = 1 << 7, - ARM_HWCAP_A64_ATOMICS = 1 << 8, - ARM_HWCAP_A64_FPHP = 1 << 9, - ARM_HWCAP_A64_ASIMDHP = 1 << 10, - ARM_HWCAP_A64_CPUID = 1 << 11, - ARM_HWCAP_A64_ASIMDRDM = 1 << 12, - ARM_HWCAP_A64_JSCVT = 1 << 13, - ARM_HWCAP_A64_FCMA = 1 << 14, - ARM_HWCAP_A64_LRCPC = 1 << 15, - ARM_HWCAP_A64_DCPOP = 1 << 16, - ARM_HWCAP_A64_SHA3 = 1 << 17, - ARM_HWCAP_A64_SM3 = 1 << 18, - ARM_HWCAP_A64_SM4 = 1 << 19, - ARM_HWCAP_A64_ASIMDDP = 1 << 20, - ARM_HWCAP_A64_SHA512 = 1 << 21, - ARM_HWCAP_A64_SVE = 1 << 22, - ARM_HWCAP_A64_ASIMDFHM = 1 << 23, - ARM_HWCAP_A64_DIT = 1 << 24, - ARM_HWCAP_A64_USCAT = 1 << 25, - ARM_HWCAP_A64_ILRCPC = 1 << 26, - ARM_HWCAP_A64_FLAGM = 1 << 27, - ARM_HWCAP_A64_SSBS = 1 << 28, - ARM_HWCAP_A64_SB = 1 << 29, - ARM_HWCAP_A64_PACA = 1 << 30, - ARM_HWCAP_A64_PACG = 1ULL << 31, - ARM_HWCAP_A64_GCS = 1ULL << 32, - ARM_HWCAP_A64_CMPBR = 1ULL << 33, - ARM_HWCAP_A64_FPRCVT = 1ULL << 34, - ARM_HWCAP_A64_F8MM8 = 1ULL << 35, - ARM_HWCAP_A64_F8MM4 = 1ULL << 36, - ARM_HWCAP_A64_SVE_F16MM = 1ULL << 37, - ARM_HWCAP_A64_SVE_ELTPERM = 1ULL << 38, - ARM_HWCAP_A64_SVE_AES2 = 1ULL << 39, - ARM_HWCAP_A64_SVE_BFSCALE = 1ULL << 40, - ARM_HWCAP_A64_SVE2P2 = 1ULL << 41, - ARM_HWCAP_A64_SME2P2 = 1ULL << 42, - ARM_HWCAP_A64_SME_SBITPERM = 1ULL << 43, - ARM_HWCAP_A64_SME_AES = 1ULL << 44, - ARM_HWCAP_A64_SME_SFEXPA = 1ULL << 45, - ARM_HWCAP_A64_SME_STMOP = 1ULL << 46, - ARM_HWCAP_A64_SME_SMOP4 = 1ULL << 47, - - ARM_HWCAP2_A64_DCPODP = 1 << 0, - ARM_HWCAP2_A64_SVE2 = 1 << 1, - ARM_HWCAP2_A64_SVEAES = 1 << 2, - ARM_HWCAP2_A64_SVEPMULL = 1 << 3, - ARM_HWCAP2_A64_SVEBITPERM = 1 << 4, - ARM_HWCAP2_A64_SVESHA3 = 1 << 5, - ARM_HWCAP2_A64_SVESM4 = 1 << 6, - ARM_HWCAP2_A64_FLAGM2 = 1 << 7, - ARM_HWCAP2_A64_FRINT = 1 << 8, - ARM_HWCAP2_A64_SVEI8MM = 1 << 9, - ARM_HWCAP2_A64_SVEF32MM = 1 << 10, - ARM_HWCAP2_A64_SVEF64MM = 1 << 11, - ARM_HWCAP2_A64_SVEBF16 = 1 << 12, - ARM_HWCAP2_A64_I8MM = 1 << 13, - ARM_HWCAP2_A64_BF16 = 1 << 14, - ARM_HWCAP2_A64_DGH = 1 << 15, - ARM_HWCAP2_A64_RNG = 1 << 16, - ARM_HWCAP2_A64_BTI = 1 << 17, - ARM_HWCAP2_A64_MTE = 1 << 18, - ARM_HWCAP2_A64_ECV = 1 << 19, - ARM_HWCAP2_A64_AFP = 1 << 20, - ARM_HWCAP2_A64_RPRES = 1 << 21, - ARM_HWCAP2_A64_MTE3 = 1 << 22, - ARM_HWCAP2_A64_SME = 1 << 23, - ARM_HWCAP2_A64_SME_I16I64 = 1 << 24, - ARM_HWCAP2_A64_SME_F64F64 = 1 << 25, - ARM_HWCAP2_A64_SME_I8I32 = 1 << 26, - ARM_HWCAP2_A64_SME_F16F32 = 1 << 27, - 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, - ARM_HWCAP2_A64_SVE_B16B16 = 1ULL << 45, - ARM_HWCAP2_A64_LRCPC3 = 1ULL << 46, - ARM_HWCAP2_A64_LSE128 = 1ULL << 47, - ARM_HWCAP2_A64_FPMR = 1ULL << 48, - ARM_HWCAP2_A64_LUT = 1ULL << 49, - ARM_HWCAP2_A64_FAMINMAX = 1ULL << 50, - ARM_HWCAP2_A64_F8CVT = 1ULL << 51, - ARM_HWCAP2_A64_F8FMA = 1ULL << 52, - ARM_HWCAP2_A64_F8DP4 = 1ULL << 53, - ARM_HWCAP2_A64_F8DP2 = 1ULL << 54, - ARM_HWCAP2_A64_F8E4M3 = 1ULL << 55, - ARM_HWCAP2_A64_F8E5M2 = 1ULL << 56, - ARM_HWCAP2_A64_SME_LUTV2 = 1ULL << 57, - ARM_HWCAP2_A64_SME_F8F16 = 1ULL << 58, - ARM_HWCAP2_A64_SME_F8F32 = 1ULL << 59, - ARM_HWCAP2_A64_SME_SF8FMA = 1ULL << 60, - ARM_HWCAP2_A64_SME_SF8DP4 = 1ULL << 61, - ARM_HWCAP2_A64_SME_SF8DP2 = 1ULL << 62, - ARM_HWCAP2_A64_POE = 1ULL << 63, -}; - -#define ELF_HWCAP get_elf_hwcap() -#define ELF_HWCAP2 get_elf_hwcap2() - -#define GET_FEATURE_ID(feat, hwcap) \ - do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) - -uint32_t get_elf_hwcap(void) -{ - ARMCPU *cpu = ARM_CPU(thread_cpu); - uint32_t hwcaps = 0; - - hwcaps |= ARM_HWCAP_A64_FP; - hwcaps |= ARM_HWCAP_A64_ASIMD; - hwcaps |= ARM_HWCAP_A64_CPUID; - - /* probe for the extra features */ - - GET_FEATURE_ID(aa64_aes, ARM_HWCAP_A64_AES); - GET_FEATURE_ID(aa64_pmull, ARM_HWCAP_A64_PMULL); - GET_FEATURE_ID(aa64_sha1, ARM_HWCAP_A64_SHA1); - GET_FEATURE_ID(aa64_sha256, ARM_HWCAP_A64_SHA2); - GET_FEATURE_ID(aa64_sha512, ARM_HWCAP_A64_SHA512); - GET_FEATURE_ID(aa64_crc32, ARM_HWCAP_A64_CRC32); - GET_FEATURE_ID(aa64_sha3, ARM_HWCAP_A64_SHA3); - GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3); - GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4); - GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); - GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS); - GET_FEATURE_ID(aa64_lse2, ARM_HWCAP_A64_USCAT); - GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM); - GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP); - GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA); - GET_FEATURE_ID(aa64_sve, ARM_HWCAP_A64_SVE); - GET_FEATURE_ID(aa64_pauth, ARM_HWCAP_A64_PACA | ARM_HWCAP_A64_PACG); - GET_FEATURE_ID(aa64_fhm, ARM_HWCAP_A64_ASIMDFHM); - GET_FEATURE_ID(aa64_dit, ARM_HWCAP_A64_DIT); - GET_FEATURE_ID(aa64_jscvt, ARM_HWCAP_A64_JSCVT); - GET_FEATURE_ID(aa64_sb, ARM_HWCAP_A64_SB); - GET_FEATURE_ID(aa64_condm_4, ARM_HWCAP_A64_FLAGM); - GET_FEATURE_ID(aa64_dcpop, ARM_HWCAP_A64_DCPOP); - GET_FEATURE_ID(aa64_rcpc_8_3, ARM_HWCAP_A64_LRCPC); - GET_FEATURE_ID(aa64_rcpc_8_4, ARM_HWCAP_A64_ILRCPC); - - return hwcaps; -} - -uint64_t get_elf_hwcap2(void) -{ - ARMCPU *cpu = ARM_CPU(thread_cpu); - uint64_t hwcaps = 0; - - GET_FEATURE_ID(aa64_dcpodp, ARM_HWCAP2_A64_DCPODP); - GET_FEATURE_ID(aa64_sve2, ARM_HWCAP2_A64_SVE2); - GET_FEATURE_ID(aa64_sve2_aes, ARM_HWCAP2_A64_SVEAES); - GET_FEATURE_ID(aa64_sve2_pmull128, ARM_HWCAP2_A64_SVEPMULL); - GET_FEATURE_ID(aa64_sve2_bitperm, ARM_HWCAP2_A64_SVEBITPERM); - GET_FEATURE_ID(aa64_sve2_sha3, ARM_HWCAP2_A64_SVESHA3); - GET_FEATURE_ID(aa64_sve2_sm4, ARM_HWCAP2_A64_SVESM4); - GET_FEATURE_ID(aa64_condm_5, ARM_HWCAP2_A64_FLAGM2); - GET_FEATURE_ID(aa64_frint, ARM_HWCAP2_A64_FRINT); - GET_FEATURE_ID(aa64_sve_i8mm, ARM_HWCAP2_A64_SVEI8MM); - GET_FEATURE_ID(aa64_sve_f32mm, ARM_HWCAP2_A64_SVEF32MM); - GET_FEATURE_ID(aa64_sve_f64mm, ARM_HWCAP2_A64_SVEF64MM); - GET_FEATURE_ID(aa64_sve_bf16, ARM_HWCAP2_A64_SVEBF16); - GET_FEATURE_ID(aa64_i8mm, ARM_HWCAP2_A64_I8MM); - GET_FEATURE_ID(aa64_bf16, ARM_HWCAP2_A64_BF16); - GET_FEATURE_ID(aa64_rndr, ARM_HWCAP2_A64_RNG); - GET_FEATURE_ID(aa64_bti, ARM_HWCAP2_A64_BTI); - GET_FEATURE_ID(aa64_mte, ARM_HWCAP2_A64_MTE); - GET_FEATURE_ID(aa64_mte3, ARM_HWCAP2_A64_MTE3); - GET_FEATURE_ID(aa64_sme, (ARM_HWCAP2_A64_SME | - ARM_HWCAP2_A64_SME_F32F32 | - ARM_HWCAP2_A64_SME_B16F32 | - ARM_HWCAP2_A64_SME_F16F32 | - ARM_HWCAP2_A64_SME_I8I32)); - 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); - GET_FEATURE_ID(aa64_mops, ARM_HWCAP2_A64_MOPS); - GET_FEATURE_ID(aa64_sve2p1, ARM_HWCAP2_A64_SVE2P1); - GET_FEATURE_ID(aa64_sme2, (ARM_HWCAP2_A64_SME2 | - ARM_HWCAP2_A64_SME_I16I32 | - ARM_HWCAP2_A64_SME_BI32I32)); - GET_FEATURE_ID(aa64_sme2p1, ARM_HWCAP2_A64_SME2P1); - GET_FEATURE_ID(aa64_sme_b16b16, ARM_HWCAP2_A64_SME_B16B16); - GET_FEATURE_ID(aa64_sme_f16f16, ARM_HWCAP2_A64_SME_F16F16); - GET_FEATURE_ID(aa64_sve_b16b16, ARM_HWCAP2_A64_SVE_B16B16); - - return hwcaps; -} - -const char *elf_hwcap_str(uint32_t bit) -{ - static const char * const hwcap_str[] = { - [__builtin_ctz(ARM_HWCAP_A64_FP )] = "fp", - [__builtin_ctz(ARM_HWCAP_A64_ASIMD )] = "asimd", - [__builtin_ctz(ARM_HWCAP_A64_EVTSTRM )] = "evtstrm", - [__builtin_ctz(ARM_HWCAP_A64_AES )] = "aes", - [__builtin_ctz(ARM_HWCAP_A64_PMULL )] = "pmull", - [__builtin_ctz(ARM_HWCAP_A64_SHA1 )] = "sha1", - [__builtin_ctz(ARM_HWCAP_A64_SHA2 )] = "sha2", - [__builtin_ctz(ARM_HWCAP_A64_CRC32 )] = "crc32", - [__builtin_ctz(ARM_HWCAP_A64_ATOMICS )] = "atomics", - [__builtin_ctz(ARM_HWCAP_A64_FPHP )] = "fphp", - [__builtin_ctz(ARM_HWCAP_A64_ASIMDHP )] = "asimdhp", - [__builtin_ctz(ARM_HWCAP_A64_CPUID )] = "cpuid", - [__builtin_ctz(ARM_HWCAP_A64_ASIMDRDM)] = "asimdrdm", - [__builtin_ctz(ARM_HWCAP_A64_JSCVT )] = "jscvt", - [__builtin_ctz(ARM_HWCAP_A64_FCMA )] = "fcma", - [__builtin_ctz(ARM_HWCAP_A64_LRCPC )] = "lrcpc", - [__builtin_ctz(ARM_HWCAP_A64_DCPOP )] = "dcpop", - [__builtin_ctz(ARM_HWCAP_A64_SHA3 )] = "sha3", - [__builtin_ctz(ARM_HWCAP_A64_SM3 )] = "sm3", - [__builtin_ctz(ARM_HWCAP_A64_SM4 )] = "sm4", - [__builtin_ctz(ARM_HWCAP_A64_ASIMDDP )] = "asimddp", - [__builtin_ctz(ARM_HWCAP_A64_SHA512 )] = "sha512", - [__builtin_ctz(ARM_HWCAP_A64_SVE )] = "sve", - [__builtin_ctz(ARM_HWCAP_A64_ASIMDFHM)] = "asimdfhm", - [__builtin_ctz(ARM_HWCAP_A64_DIT )] = "dit", - [__builtin_ctz(ARM_HWCAP_A64_USCAT )] = "uscat", - [__builtin_ctz(ARM_HWCAP_A64_ILRCPC )] = "ilrcpc", - [__builtin_ctz(ARM_HWCAP_A64_FLAGM )] = "flagm", - [__builtin_ctz(ARM_HWCAP_A64_SSBS )] = "ssbs", - [__builtin_ctz(ARM_HWCAP_A64_SB )] = "sb", - [__builtin_ctz(ARM_HWCAP_A64_PACA )] = "paca", - [__builtin_ctz(ARM_HWCAP_A64_PACG )] = "pacg", - [__builtin_ctzll(ARM_HWCAP_A64_GCS )] = "gcs", - [__builtin_ctzll(ARM_HWCAP_A64_CMPBR )] = "cmpbr", - [__builtin_ctzll(ARM_HWCAP_A64_FPRCVT)] = "fprcvt", - [__builtin_ctzll(ARM_HWCAP_A64_F8MM8 )] = "f8mm8", - [__builtin_ctzll(ARM_HWCAP_A64_F8MM4 )] = "f8mm4", - [__builtin_ctzll(ARM_HWCAP_A64_SVE_F16MM)] = "svef16mm", - [__builtin_ctzll(ARM_HWCAP_A64_SVE_ELTPERM)] = "sveeltperm", - [__builtin_ctzll(ARM_HWCAP_A64_SVE_AES2)] = "sveaes2", - [__builtin_ctzll(ARM_HWCAP_A64_SVE_BFSCALE)] = "svebfscale", - [__builtin_ctzll(ARM_HWCAP_A64_SVE2P2)] = "sve2p2", - [__builtin_ctzll(ARM_HWCAP_A64_SME2P2)] = "sme2p2", - [__builtin_ctzll(ARM_HWCAP_A64_SME_SBITPERM)] = "smesbitperm", - [__builtin_ctzll(ARM_HWCAP_A64_SME_AES)] = "smeaes", - [__builtin_ctzll(ARM_HWCAP_A64_SME_SFEXPA)] = "smesfexpa", - [__builtin_ctzll(ARM_HWCAP_A64_SME_STMOP)] = "smestmop", - [__builtin_ctzll(ARM_HWCAP_A64_SME_SMOP4)] = "smesmop4", - }; - - return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; -} - -const char *elf_hwcap2_str(uint32_t bit) -{ - static const char * const hwcap_str[] = { - [__builtin_ctz(ARM_HWCAP2_A64_DCPODP )] = "dcpodp", - [__builtin_ctz(ARM_HWCAP2_A64_SVE2 )] = "sve2", - [__builtin_ctz(ARM_HWCAP2_A64_SVEAES )] = "sveaes", - [__builtin_ctz(ARM_HWCAP2_A64_SVEPMULL )] = "svepmull", - [__builtin_ctz(ARM_HWCAP2_A64_SVEBITPERM )] = "svebitperm", - [__builtin_ctz(ARM_HWCAP2_A64_SVESHA3 )] = "svesha3", - [__builtin_ctz(ARM_HWCAP2_A64_SVESM4 )] = "svesm4", - [__builtin_ctz(ARM_HWCAP2_A64_FLAGM2 )] = "flagm2", - [__builtin_ctz(ARM_HWCAP2_A64_FRINT )] = "frint", - [__builtin_ctz(ARM_HWCAP2_A64_SVEI8MM )] = "svei8mm", - [__builtin_ctz(ARM_HWCAP2_A64_SVEF32MM )] = "svef32mm", - [__builtin_ctz(ARM_HWCAP2_A64_SVEF64MM )] = "svef64mm", - [__builtin_ctz(ARM_HWCAP2_A64_SVEBF16 )] = "svebf16", - [__builtin_ctz(ARM_HWCAP2_A64_I8MM )] = "i8mm", - [__builtin_ctz(ARM_HWCAP2_A64_BF16 )] = "bf16", - [__builtin_ctz(ARM_HWCAP2_A64_DGH )] = "dgh", - [__builtin_ctz(ARM_HWCAP2_A64_RNG )] = "rng", - [__builtin_ctz(ARM_HWCAP2_A64_BTI )] = "bti", - [__builtin_ctz(ARM_HWCAP2_A64_MTE )] = "mte", - [__builtin_ctz(ARM_HWCAP2_A64_ECV )] = "ecv", - [__builtin_ctz(ARM_HWCAP2_A64_AFP )] = "afp", - [__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 )] = "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", - [__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", - [__builtin_ctzll(ARM_HWCAP2_A64_SVE_B16B16 )] = "sveb16b16", - [__builtin_ctzll(ARM_HWCAP2_A64_LRCPC3 )] = "lrcpc3", - [__builtin_ctzll(ARM_HWCAP2_A64_LSE128 )] = "lse128", - [__builtin_ctzll(ARM_HWCAP2_A64_FPMR )] = "fpmr", - [__builtin_ctzll(ARM_HWCAP2_A64_LUT )] = "lut", - [__builtin_ctzll(ARM_HWCAP2_A64_FAMINMAX )] = "faminmax", - [__builtin_ctzll(ARM_HWCAP2_A64_F8CVT )] = "f8cvt", - [__builtin_ctzll(ARM_HWCAP2_A64_F8FMA )] = "f8fma", - [__builtin_ctzll(ARM_HWCAP2_A64_F8DP4 )] = "f8dp4", - [__builtin_ctzll(ARM_HWCAP2_A64_F8DP2 )] = "f8dp2", - [__builtin_ctzll(ARM_HWCAP2_A64_F8E4M3 )] = "f8e4m3", - [__builtin_ctzll(ARM_HWCAP2_A64_F8E5M2 )] = "f8e5m2", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_LUTV2 )] = "smelutv2", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_F8F16 )] = "smef8f16", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_F8F32 )] = "smef8f32", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_SF8DP4 )] = "smesf8dp4", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_SF8DP2 )] = "smesf8dp2", - [__builtin_ctzll(ARM_HWCAP2_A64_POE )] = "poe", - }; - - return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; -} - -#undef GET_FEATURE_ID +#define ELF_HWCAP get_elf_hwcap(thread_cpu) +#define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) #if TARGET_BIG_ENDIAN # define VDSO_HEADER "vdso-be.c.inc" diff --git a/linux-user/loader.h b/linux-user/loader.h index 457bb36daa4d3..151a06f5db5aa 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -101,16 +101,14 @@ extern unsigned long guest_stack_size; /* Note that Elf32 and Elf64 use uint32_t for e_flags. */ const char *get_elf_cpu_model(uint32_t eflags); -#if defined(TARGET_I386) || defined(TARGET_X86_64) +#if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) abi_ulong get_elf_hwcap(CPUState *cs); +abi_ulong get_elf_hwcap2(CPUState *cs); #endif -#if defined(TARGET_S390X) || defined(TARGET_AARCH64) || defined(TARGET_ARM) +#if defined(TARGET_S390X) uint32_t get_elf_hwcap(void); -const char *elf_hwcap_str(uint32_t bit); #endif -#if defined(TARGET_AARCH64) || defined(TARGET_ARM) -uint64_t get_elf_hwcap2(void); +const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); -#endif #endif /* LINUX_USER_LOADER_H */ From e0f62c5a5b5c80d0a6b163a8113e5938b0b40d54 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 21:03:12 -1000 Subject: [PATCH 0312/1794] linux-user: Move get_elf_hwcap to sparc/elfload.c Change the return type to abi_ulong, and pass in the cpu. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 30 +----------------------------- linux-user/loader.h | 3 ++- linux-user/sparc/elfload.c | 27 +++++++++++++++++++++++++++ linux-user/sparc/target_elf.h | 2 ++ 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 149d1313c0a70..16709865f788c 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -565,35 +565,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, # define ELF_ARCH EM_SPARCV9 #endif -#include "elf.h" - -#define ELF_HWCAP get_elf_hwcap() - -static uint32_t get_elf_hwcap(void) -{ - /* There are not many sparc32 hwcap bits -- we have all of them. */ - uint32_t r = HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | - HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV; - -#ifdef TARGET_SPARC64 - CPUSPARCState *env = cpu_env(thread_cpu); - uint32_t features = env->def.features; - - r |= HWCAP_SPARC_V9 | HWCAP_SPARC_V8PLUS; - /* 32x32 multiply and divide are efficient. */ - r |= HWCAP_SPARC_MUL32 | HWCAP_SPARC_DIV32; - /* We don't have an internal feature bit for this. */ - r |= HWCAP_SPARC_POPC; - r |= features & CPU_FEATURE_FSMULD ? HWCAP_SPARC_FSMULD : 0; - r |= features & CPU_FEATURE_VIS1 ? HWCAP_SPARC_VIS : 0; - r |= features & CPU_FEATURE_VIS2 ? HWCAP_SPARC_VIS2 : 0; - r |= features & CPU_FEATURE_FMAF ? HWCAP_SPARC_FMAF : 0; - r |= features & CPU_FEATURE_VIS3 ? HWCAP_SPARC_VIS3 : 0; - r |= features & CPU_FEATURE_IMA ? HWCAP_SPARC_IMA : 0; -#endif - - return r; -} +#define ELF_HWCAP get_elf_hwcap(thread_cpu) static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) diff --git a/linux-user/loader.h b/linux-user/loader.h index 151a06f5db5aa..2c8414e0e53d1 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -101,7 +101,8 @@ extern unsigned long guest_stack_size; /* Note that Elf32 and Elf64 use uint32_t for e_flags. */ const char *get_elf_cpu_model(uint32_t eflags); -#if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) +#if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) \ + || defined(TARGET_SPARC) abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); #endif diff --git a/linux-user/sparc/elfload.c b/linux-user/sparc/elfload.c index 243e6f9b66a6e..32ca1b05b1a3f 100644 --- a/linux-user/sparc/elfload.c +++ b/linux-user/sparc/elfload.c @@ -3,6 +3,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -13,3 +14,29 @@ const char *get_elf_cpu_model(uint32_t eflags) return "Fujitsu MB86904"; #endif } + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + /* There are not many sparc32 hwcap bits -- we have all of them. */ + uint32_t r = HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | + HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV; + +#ifdef TARGET_SPARC64 + CPUSPARCState *env = cpu_env(cs); + uint32_t features = env->def.features; + + r |= HWCAP_SPARC_V9 | HWCAP_SPARC_V8PLUS; + /* 32x32 multiply and divide are efficient. */ + r |= HWCAP_SPARC_MUL32 | HWCAP_SPARC_DIV32; + /* We don't have an internal feature bit for this. */ + r |= HWCAP_SPARC_POPC; + r |= features & CPU_FEATURE_FSMULD ? HWCAP_SPARC_FSMULD : 0; + r |= features & CPU_FEATURE_VIS1 ? HWCAP_SPARC_VIS : 0; + r |= features & CPU_FEATURE_VIS2 ? HWCAP_SPARC_VIS2 : 0; + r |= features & CPU_FEATURE_FMAF ? HWCAP_SPARC_FMAF : 0; + r |= features & CPU_FEATURE_VIS3 ? HWCAP_SPARC_VIS3 : 0; + r |= features & CPU_FEATURE_IMA ? HWCAP_SPARC_IMA : 0; +#endif + + return r; +} diff --git a/linux-user/sparc/target_elf.h b/linux-user/sparc/target_elf.h index 7e46748d261f9..b7544db0a1cb0 100644 --- a/linux-user/sparc/target_elf.h +++ b/linux-user/sparc/target_elf.h @@ -8,4 +8,6 @@ #ifndef SPARC_TARGET_ELF_H #define SPARC_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif From ea9d69159e013a5757e20bea0266b7c17303d961 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 21:12:54 -1000 Subject: [PATCH 0313/1794] linux-user: Move hwcap functions to ppc/elfload.c Change the return type to abi_ulong, and pass in the cpu. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 116 +----------------------------------- linux-user/loader.h | 2 +- linux-user/ppc/elfload.c | 116 ++++++++++++++++++++++++++++++++++++ linux-user/ppc/target_elf.h | 3 + 4 files changed, 122 insertions(+), 115 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 16709865f788c..843b1f7b6ccfd 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -598,120 +598,8 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_ARCH EM_PPC -/* Feature masks for the Aux Vector Hardware Capabilities (AT_HWCAP). - See arch/powerpc/include/asm/cputable.h. */ -enum { - QEMU_PPC_FEATURE_32 = 0x80000000, - QEMU_PPC_FEATURE_64 = 0x40000000, - QEMU_PPC_FEATURE_601_INSTR = 0x20000000, - QEMU_PPC_FEATURE_HAS_ALTIVEC = 0x10000000, - QEMU_PPC_FEATURE_HAS_FPU = 0x08000000, - QEMU_PPC_FEATURE_HAS_MMU = 0x04000000, - QEMU_PPC_FEATURE_HAS_4xxMAC = 0x02000000, - QEMU_PPC_FEATURE_UNIFIED_CACHE = 0x01000000, - QEMU_PPC_FEATURE_HAS_SPE = 0x00800000, - QEMU_PPC_FEATURE_HAS_EFP_SINGLE = 0x00400000, - QEMU_PPC_FEATURE_HAS_EFP_DOUBLE = 0x00200000, - QEMU_PPC_FEATURE_NO_TB = 0x00100000, - QEMU_PPC_FEATURE_POWER4 = 0x00080000, - QEMU_PPC_FEATURE_POWER5 = 0x00040000, - QEMU_PPC_FEATURE_POWER5_PLUS = 0x00020000, - QEMU_PPC_FEATURE_CELL = 0x00010000, - QEMU_PPC_FEATURE_BOOKE = 0x00008000, - QEMU_PPC_FEATURE_SMT = 0x00004000, - QEMU_PPC_FEATURE_ICACHE_SNOOP = 0x00002000, - QEMU_PPC_FEATURE_ARCH_2_05 = 0x00001000, - QEMU_PPC_FEATURE_PA6T = 0x00000800, - QEMU_PPC_FEATURE_HAS_DFP = 0x00000400, - QEMU_PPC_FEATURE_POWER6_EXT = 0x00000200, - QEMU_PPC_FEATURE_ARCH_2_06 = 0x00000100, - QEMU_PPC_FEATURE_HAS_VSX = 0x00000080, - QEMU_PPC_FEATURE_PSERIES_PERFMON_COMPAT = 0x00000040, - - QEMU_PPC_FEATURE_TRUE_LE = 0x00000002, - QEMU_PPC_FEATURE_PPC_LE = 0x00000001, - - /* Feature definitions in AT_HWCAP2. */ - QEMU_PPC_FEATURE2_ARCH_2_07 = 0x80000000, /* ISA 2.07 */ - QEMU_PPC_FEATURE2_HAS_HTM = 0x40000000, /* Hardware Transactional Memory */ - QEMU_PPC_FEATURE2_HAS_DSCR = 0x20000000, /* Data Stream Control Register */ - QEMU_PPC_FEATURE2_HAS_EBB = 0x10000000, /* Event Base Branching */ - QEMU_PPC_FEATURE2_HAS_ISEL = 0x08000000, /* Integer Select */ - QEMU_PPC_FEATURE2_HAS_TAR = 0x04000000, /* Target Address Register */ - QEMU_PPC_FEATURE2_VEC_CRYPTO = 0x02000000, - QEMU_PPC_FEATURE2_HTM_NOSC = 0x01000000, - QEMU_PPC_FEATURE2_ARCH_3_00 = 0x00800000, /* ISA 3.00 */ - QEMU_PPC_FEATURE2_HAS_IEEE128 = 0x00400000, /* VSX IEEE Bin Float 128-bit */ - QEMU_PPC_FEATURE2_DARN = 0x00200000, /* darn random number insn */ - QEMU_PPC_FEATURE2_SCV = 0x00100000, /* scv syscall */ - QEMU_PPC_FEATURE2_HTM_NO_SUSPEND = 0x00080000, /* TM w/o suspended state */ - QEMU_PPC_FEATURE2_ARCH_3_1 = 0x00040000, /* ISA 3.1 */ - QEMU_PPC_FEATURE2_MMA = 0x00020000, /* Matrix-Multiply Assist */ -}; - -#define ELF_HWCAP get_elf_hwcap() - -static uint32_t get_elf_hwcap(void) -{ - PowerPCCPU *cpu = POWERPC_CPU(thread_cpu); - uint32_t features = 0; - - /* We don't have to be terribly complete here; the high points are - Altivec/FP/SPE support. Anything else is just a bonus. */ -#define GET_FEATURE(flag, feature) \ - do { if (cpu->env.insns_flags & flag) { features |= feature; } } while (0) -#define GET_FEATURE2(flags, feature) \ - do { \ - if ((cpu->env.insns_flags2 & flags) == flags) { \ - features |= feature; \ - } \ - } while (0) - GET_FEATURE(PPC_64B, QEMU_PPC_FEATURE_64); - GET_FEATURE(PPC_FLOAT, QEMU_PPC_FEATURE_HAS_FPU); - GET_FEATURE(PPC_ALTIVEC, QEMU_PPC_FEATURE_HAS_ALTIVEC); - GET_FEATURE(PPC_SPE, QEMU_PPC_FEATURE_HAS_SPE); - GET_FEATURE(PPC_SPE_SINGLE, QEMU_PPC_FEATURE_HAS_EFP_SINGLE); - GET_FEATURE(PPC_SPE_DOUBLE, QEMU_PPC_FEATURE_HAS_EFP_DOUBLE); - GET_FEATURE(PPC_BOOKE, QEMU_PPC_FEATURE_BOOKE); - GET_FEATURE(PPC_405_MAC, QEMU_PPC_FEATURE_HAS_4xxMAC); - GET_FEATURE2(PPC2_DFP, QEMU_PPC_FEATURE_HAS_DFP); - GET_FEATURE2(PPC2_VSX, QEMU_PPC_FEATURE_HAS_VSX); - GET_FEATURE2((PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | PPC2_ATOMIC_ISA206 | - PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206), - QEMU_PPC_FEATURE_ARCH_2_06); -#undef GET_FEATURE -#undef GET_FEATURE2 - - return features; -} - -#define ELF_HWCAP2 get_elf_hwcap2() - -static uint32_t get_elf_hwcap2(void) -{ - PowerPCCPU *cpu = POWERPC_CPU(thread_cpu); - uint32_t features = 0; - -#define GET_FEATURE(flag, feature) \ - do { if (cpu->env.insns_flags & flag) { features |= feature; } } while (0) -#define GET_FEATURE2(flag, feature) \ - do { if (cpu->env.insns_flags2 & flag) { features |= feature; } } while (0) - - GET_FEATURE(PPC_ISEL, QEMU_PPC_FEATURE2_HAS_ISEL); - GET_FEATURE2(PPC2_BCTAR_ISA207, QEMU_PPC_FEATURE2_HAS_TAR); - GET_FEATURE2((PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | - PPC2_ISA207S), QEMU_PPC_FEATURE2_ARCH_2_07 | - QEMU_PPC_FEATURE2_VEC_CRYPTO); - GET_FEATURE2(PPC2_ISA300, QEMU_PPC_FEATURE2_ARCH_3_00 | - QEMU_PPC_FEATURE2_DARN | QEMU_PPC_FEATURE2_HAS_IEEE128); - GET_FEATURE2(PPC2_ISA310, QEMU_PPC_FEATURE2_ARCH_3_1 | - QEMU_PPC_FEATURE2_MMA); - -#undef GET_FEATURE -#undef GET_FEATURE2 - - return features; -} +#define ELF_HWCAP get_elf_hwcap(thread_cpu) +#define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) /* * The requirements here are: diff --git a/linux-user/loader.h b/linux-user/loader.h index 2c8414e0e53d1..818c5e6d7d770 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -102,7 +102,7 @@ extern unsigned long guest_stack_size; const char *get_elf_cpu_model(uint32_t eflags); #if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) \ - || defined(TARGET_SPARC) + || defined(TARGET_SPARC) || defined(TARGET_PPC) abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); #endif diff --git a/linux-user/ppc/elfload.c b/linux-user/ppc/elfload.c index 7775dc06fac08..a214675650414 100644 --- a/linux-user/ppc/elfload.c +++ b/linux-user/ppc/elfload.c @@ -13,3 +13,119 @@ const char *get_elf_cpu_model(uint32_t eflags) return "750"; #endif } + +/* + * Feature masks for the Aux Vector Hardware Capabilities (AT_HWCAP). + * See arch/powerpc/include/asm/cputable.h. + */ +enum { + QEMU_PPC_FEATURE_32 = 0x80000000, + QEMU_PPC_FEATURE_64 = 0x40000000, + QEMU_PPC_FEATURE_601_INSTR = 0x20000000, + QEMU_PPC_FEATURE_HAS_ALTIVEC = 0x10000000, + QEMU_PPC_FEATURE_HAS_FPU = 0x08000000, + QEMU_PPC_FEATURE_HAS_MMU = 0x04000000, + QEMU_PPC_FEATURE_HAS_4xxMAC = 0x02000000, + QEMU_PPC_FEATURE_UNIFIED_CACHE = 0x01000000, + QEMU_PPC_FEATURE_HAS_SPE = 0x00800000, + QEMU_PPC_FEATURE_HAS_EFP_SINGLE = 0x00400000, + QEMU_PPC_FEATURE_HAS_EFP_DOUBLE = 0x00200000, + QEMU_PPC_FEATURE_NO_TB = 0x00100000, + QEMU_PPC_FEATURE_POWER4 = 0x00080000, + QEMU_PPC_FEATURE_POWER5 = 0x00040000, + QEMU_PPC_FEATURE_POWER5_PLUS = 0x00020000, + QEMU_PPC_FEATURE_CELL = 0x00010000, + QEMU_PPC_FEATURE_BOOKE = 0x00008000, + QEMU_PPC_FEATURE_SMT = 0x00004000, + QEMU_PPC_FEATURE_ICACHE_SNOOP = 0x00002000, + QEMU_PPC_FEATURE_ARCH_2_05 = 0x00001000, + QEMU_PPC_FEATURE_PA6T = 0x00000800, + QEMU_PPC_FEATURE_HAS_DFP = 0x00000400, + QEMU_PPC_FEATURE_POWER6_EXT = 0x00000200, + QEMU_PPC_FEATURE_ARCH_2_06 = 0x00000100, + QEMU_PPC_FEATURE_HAS_VSX = 0x00000080, + QEMU_PPC_FEATURE_PSERIES_PERFMON_COMPAT = 0x00000040, + + QEMU_PPC_FEATURE_TRUE_LE = 0x00000002, + QEMU_PPC_FEATURE_PPC_LE = 0x00000001, + + /* Feature definitions in AT_HWCAP2. */ + QEMU_PPC_FEATURE2_ARCH_2_07 = 0x80000000, /* ISA 2.07 */ + QEMU_PPC_FEATURE2_HAS_HTM = 0x40000000, /* Hardware Transactional Memory */ + QEMU_PPC_FEATURE2_HAS_DSCR = 0x20000000, /* Data Stream Control Register */ + QEMU_PPC_FEATURE2_HAS_EBB = 0x10000000, /* Event Base Branching */ + QEMU_PPC_FEATURE2_HAS_ISEL = 0x08000000, /* Integer Select */ + QEMU_PPC_FEATURE2_HAS_TAR = 0x04000000, /* Target Address Register */ + QEMU_PPC_FEATURE2_VEC_CRYPTO = 0x02000000, + QEMU_PPC_FEATURE2_HTM_NOSC = 0x01000000, + QEMU_PPC_FEATURE2_ARCH_3_00 = 0x00800000, /* ISA 3.00 */ + QEMU_PPC_FEATURE2_HAS_IEEE128 = 0x00400000, /* VSX IEEE Bin Float 128-bit */ + QEMU_PPC_FEATURE2_DARN = 0x00200000, /* darn random number insn */ + QEMU_PPC_FEATURE2_SCV = 0x00100000, /* scv syscall */ + QEMU_PPC_FEATURE2_HTM_NO_SUSPEND = 0x00080000, /* TM w/o suspended state */ + QEMU_PPC_FEATURE2_ARCH_3_1 = 0x00040000, /* ISA 3.1 */ + QEMU_PPC_FEATURE2_MMA = 0x00020000, /* Matrix-Multiply Assist */ +}; + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + uint32_t features = 0; + + /* + * We don't have to be terribly complete here; the high points are + * Altivec/FP/SPE support. Anything else is just a bonus. + */ +#define GET_FEATURE(flag, feature) \ + do { if (cpu->env.insns_flags & flag) { features |= feature; } } while (0) +#define GET_FEATURE2(flags, feature) \ + do { \ + if ((cpu->env.insns_flags2 & flags) == flags) { \ + features |= feature; \ + } \ + } while (0) + GET_FEATURE(PPC_64B, QEMU_PPC_FEATURE_64); + GET_FEATURE(PPC_FLOAT, QEMU_PPC_FEATURE_HAS_FPU); + GET_FEATURE(PPC_ALTIVEC, QEMU_PPC_FEATURE_HAS_ALTIVEC); + GET_FEATURE(PPC_SPE, QEMU_PPC_FEATURE_HAS_SPE); + GET_FEATURE(PPC_SPE_SINGLE, QEMU_PPC_FEATURE_HAS_EFP_SINGLE); + GET_FEATURE(PPC_SPE_DOUBLE, QEMU_PPC_FEATURE_HAS_EFP_DOUBLE); + GET_FEATURE(PPC_BOOKE, QEMU_PPC_FEATURE_BOOKE); + GET_FEATURE(PPC_405_MAC, QEMU_PPC_FEATURE_HAS_4xxMAC); + GET_FEATURE2(PPC2_DFP, QEMU_PPC_FEATURE_HAS_DFP); + GET_FEATURE2(PPC2_VSX, QEMU_PPC_FEATURE_HAS_VSX); + GET_FEATURE2((PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | PPC2_ATOMIC_ISA206 | + PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206), + QEMU_PPC_FEATURE_ARCH_2_06); + +#undef GET_FEATURE +#undef GET_FEATURE2 + + return features; +} + +abi_ulong get_elf_hwcap2(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + uint32_t features = 0; + +#define GET_FEATURE(flag, feature) \ + do { if (cpu->env.insns_flags & flag) { features |= feature; } } while (0) +#define GET_FEATURE2(flag, feature) \ + do { if (cpu->env.insns_flags2 & flag) { features |= feature; } } while (0) + + GET_FEATURE(PPC_ISEL, QEMU_PPC_FEATURE2_HAS_ISEL); + GET_FEATURE2(PPC2_BCTAR_ISA207, QEMU_PPC_FEATURE2_HAS_TAR); + GET_FEATURE2((PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | + PPC2_ISA207S), QEMU_PPC_FEATURE2_ARCH_2_07 | + QEMU_PPC_FEATURE2_VEC_CRYPTO); + GET_FEATURE2(PPC2_ISA300, QEMU_PPC_FEATURE2_ARCH_3_00 | + QEMU_PPC_FEATURE2_DARN | QEMU_PPC_FEATURE2_HAS_IEEE128); + GET_FEATURE2(PPC2_ISA310, QEMU_PPC_FEATURE2_ARCH_3_1 | + QEMU_PPC_FEATURE2_MMA); + +#undef GET_FEATURE +#undef GET_FEATURE2 + + return features; +} diff --git a/linux-user/ppc/target_elf.h b/linux-user/ppc/target_elf.h index 8c0a8ea431268..4203a89d665f8 100644 --- a/linux-user/ppc/target_elf.h +++ b/linux-user/ppc/target_elf.h @@ -8,4 +8,7 @@ #ifndef PPC_TARGET_ELF_H #define PPC_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_HWCAP2 1 + #endif From 184e74b236df9ad42ddbf92011713259cd17caf1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 21:17:08 -1000 Subject: [PATCH 0314/1794] linux-user: Move get_elf_hwcap to loongarch64/elfload.c Change the return type to abi_ulong, and pass in the cpu. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 49 +---------------------------- linux-user/loader.h | 3 +- linux-user/loongarch64/elfload.c | 47 +++++++++++++++++++++++++++ linux-user/loongarch64/target_elf.h | 2 ++ 4 files changed, 52 insertions(+), 49 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 843b1f7b6ccfd..574b37a22c197 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -725,54 +725,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#define ELF_HWCAP get_elf_hwcap() - -/* See arch/loongarch/include/uapi/asm/hwcap.h */ -enum { - HWCAP_LOONGARCH_CPUCFG = (1 << 0), - HWCAP_LOONGARCH_LAM = (1 << 1), - HWCAP_LOONGARCH_UAL = (1 << 2), - HWCAP_LOONGARCH_FPU = (1 << 3), - HWCAP_LOONGARCH_LSX = (1 << 4), - HWCAP_LOONGARCH_LASX = (1 << 5), - HWCAP_LOONGARCH_CRC32 = (1 << 6), - HWCAP_LOONGARCH_COMPLEX = (1 << 7), - HWCAP_LOONGARCH_CRYPTO = (1 << 8), - HWCAP_LOONGARCH_LVZ = (1 << 9), - HWCAP_LOONGARCH_LBT_X86 = (1 << 10), - HWCAP_LOONGARCH_LBT_ARM = (1 << 11), - HWCAP_LOONGARCH_LBT_MIPS = (1 << 12), -}; - -static uint32_t get_elf_hwcap(void) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(thread_cpu); - uint32_t hwcaps = 0; - - hwcaps |= HWCAP_LOONGARCH_CRC32; - - if (FIELD_EX32(cpu->env.cpucfg[1], CPUCFG1, UAL)) { - hwcaps |= HWCAP_LOONGARCH_UAL; - } - - if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, FP)) { - hwcaps |= HWCAP_LOONGARCH_FPU; - } - - if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LAM)) { - hwcaps |= HWCAP_LOONGARCH_LAM; - } - - if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { - hwcaps |= HWCAP_LOONGARCH_LSX; - } - - if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) { - hwcaps |= HWCAP_LOONGARCH_LASX; - } - - return hwcaps; -} +#define ELF_HWCAP get_elf_hwcap(thread_cpu) #define ELF_PLATFORM "loongarch" diff --git a/linux-user/loader.h b/linux-user/loader.h index 818c5e6d7d770..92b6d41145e91 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -102,7 +102,8 @@ extern unsigned long guest_stack_size; const char *get_elf_cpu_model(uint32_t eflags); #if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) \ - || defined(TARGET_SPARC) || defined(TARGET_PPC) + || defined(TARGET_SPARC) || defined(TARGET_PPC) \ + || defined(TARGET_LOONGARCH64) abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); #endif diff --git a/linux-user/loongarch64/elfload.c b/linux-user/loongarch64/elfload.c index 874dc4c230477..ee4a85b8d6c42 100644 --- a/linux-user/loongarch64/elfload.c +++ b/linux-user/loongarch64/elfload.c @@ -9,3 +9,50 @@ const char *get_elf_cpu_model(uint32_t eflags) { return "la464"; } + +/* See arch/loongarch/include/uapi/asm/hwcap.h */ +enum { + HWCAP_LOONGARCH_CPUCFG = (1 << 0), + HWCAP_LOONGARCH_LAM = (1 << 1), + HWCAP_LOONGARCH_UAL = (1 << 2), + HWCAP_LOONGARCH_FPU = (1 << 3), + HWCAP_LOONGARCH_LSX = (1 << 4), + HWCAP_LOONGARCH_LASX = (1 << 5), + HWCAP_LOONGARCH_CRC32 = (1 << 6), + HWCAP_LOONGARCH_COMPLEX = (1 << 7), + HWCAP_LOONGARCH_CRYPTO = (1 << 8), + HWCAP_LOONGARCH_LVZ = (1 << 9), + HWCAP_LOONGARCH_LBT_X86 = (1 << 10), + HWCAP_LOONGARCH_LBT_ARM = (1 << 11), + HWCAP_LOONGARCH_LBT_MIPS = (1 << 12), +}; + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + abi_ulong hwcaps = 0; + + hwcaps |= HWCAP_LOONGARCH_CRC32; + + if (FIELD_EX32(cpu->env.cpucfg[1], CPUCFG1, UAL)) { + hwcaps |= HWCAP_LOONGARCH_UAL; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, FP)) { + hwcaps |= HWCAP_LOONGARCH_FPU; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LAM)) { + hwcaps |= HWCAP_LOONGARCH_LAM; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { + hwcaps |= HWCAP_LOONGARCH_LSX; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) { + hwcaps |= HWCAP_LOONGARCH_LASX; + } + + return hwcaps; +} diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index 39a08d35d9ba2..037740d36f24a 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -6,4 +6,6 @@ #ifndef LOONGARCH_TARGET_ELF_H #define LOONGARCH_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif From 48004ab0586ba01387efc37e7ada26607a1c183e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 21:23:05 -1000 Subject: [PATCH 0315/1794] linux-user: Move get_elf_hwcap to mips/elfload.c Change the return type to abi_ulong, and pass in the cpu. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 52 +--------------------------------- linux-user/loader.h | 2 +- linux-user/mips/elfload.c | 50 ++++++++++++++++++++++++++++++++ linux-user/mips/target_elf.h | 2 ++ linux-user/mips64/target_elf.h | 2 ++ 5 files changed, 56 insertions(+), 52 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 574b37a22c197..dc3f502277a56 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -834,57 +834,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMIPSState *e #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -/* See arch/mips/include/uapi/asm/hwcap.h. */ -enum { - HWCAP_MIPS_R6 = (1 << 0), - HWCAP_MIPS_MSA = (1 << 1), - HWCAP_MIPS_CRC32 = (1 << 2), - HWCAP_MIPS_MIPS16 = (1 << 3), - HWCAP_MIPS_MDMX = (1 << 4), - HWCAP_MIPS_MIPS3D = (1 << 5), - HWCAP_MIPS_SMARTMIPS = (1 << 6), - HWCAP_MIPS_DSP = (1 << 7), - HWCAP_MIPS_DSP2 = (1 << 8), - HWCAP_MIPS_DSP3 = (1 << 9), - HWCAP_MIPS_MIPS16E2 = (1 << 10), - HWCAP_LOONGSON_MMI = (1 << 11), - HWCAP_LOONGSON_EXT = (1 << 12), - HWCAP_LOONGSON_EXT2 = (1 << 13), - HWCAP_LOONGSON_CPUCFG = (1 << 14), -}; - -#define ELF_HWCAP get_elf_hwcap() - -#define GET_FEATURE_INSN(_flag, _hwcap) \ - do { if (cpu->env.insn_flags & (_flag)) { hwcaps |= _hwcap; } } while (0) - -#define GET_FEATURE_REG_SET(_reg, _mask, _hwcap) \ - do { if (cpu->env._reg & (_mask)) { hwcaps |= _hwcap; } } while (0) - -#define GET_FEATURE_REG_EQU(_reg, _start, _length, _val, _hwcap) \ - do { \ - if (extract32(cpu->env._reg, (_start), (_length)) == (_val)) { \ - hwcaps |= _hwcap; \ - } \ - } while (0) - -static uint32_t get_elf_hwcap(void) -{ - MIPSCPU *cpu = MIPS_CPU(thread_cpu); - uint32_t hwcaps = 0; - - GET_FEATURE_REG_EQU(CP0_Config0, CP0C0_AR, CP0C0_AR_LENGTH, - 2, HWCAP_MIPS_R6); - GET_FEATURE_REG_SET(CP0_Config3, 1 << CP0C3_MSAP, HWCAP_MIPS_MSA); - GET_FEATURE_INSN(ASE_LMMI, HWCAP_LOONGSON_MMI); - GET_FEATURE_INSN(ASE_LEXT, HWCAP_LOONGSON_EXT); - - return hwcaps; -} - -#undef GET_FEATURE_REG_EQU -#undef GET_FEATURE_REG_SET -#undef GET_FEATURE_INSN +#define ELF_HWCAP get_elf_hwcap(thread_cpu) #endif /* TARGET_MIPS */ diff --git a/linux-user/loader.h b/linux-user/loader.h index 92b6d41145e91..04457737dd4c0 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -103,7 +103,7 @@ const char *get_elf_cpu_model(uint32_t eflags); #if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) \ || defined(TARGET_SPARC) || defined(TARGET_PPC) \ - || defined(TARGET_LOONGARCH64) + || defined(TARGET_LOONGARCH64) || defined(TARGET_MIPS) abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); #endif diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c index 04e3b7674010b..739f71c21b11a 100644 --- a/linux-user/mips/elfload.c +++ b/linux-user/mips/elfload.c @@ -42,3 +42,53 @@ const char *get_elf_cpu_model(uint32_t eflags) return "24Kf"; #endif } + +/* See arch/mips/include/uapi/asm/hwcap.h. */ +enum { + HWCAP_MIPS_R6 = (1 << 0), + HWCAP_MIPS_MSA = (1 << 1), + HWCAP_MIPS_CRC32 = (1 << 2), + HWCAP_MIPS_MIPS16 = (1 << 3), + HWCAP_MIPS_MDMX = (1 << 4), + HWCAP_MIPS_MIPS3D = (1 << 5), + HWCAP_MIPS_SMARTMIPS = (1 << 6), + HWCAP_MIPS_DSP = (1 << 7), + HWCAP_MIPS_DSP2 = (1 << 8), + HWCAP_MIPS_DSP3 = (1 << 9), + HWCAP_MIPS_MIPS16E2 = (1 << 10), + HWCAP_LOONGSON_MMI = (1 << 11), + HWCAP_LOONGSON_EXT = (1 << 12), + HWCAP_LOONGSON_EXT2 = (1 << 13), + HWCAP_LOONGSON_CPUCFG = (1 << 14), +}; + +#define GET_FEATURE_INSN(_flag, _hwcap) \ + do { if (cpu->env.insn_flags & (_flag)) { hwcaps |= _hwcap; } } while (0) + +#define GET_FEATURE_REG_SET(_reg, _mask, _hwcap) \ + do { if (cpu->env._reg & (_mask)) { hwcaps |= _hwcap; } } while (0) + +#define GET_FEATURE_REG_EQU(_reg, _start, _length, _val, _hwcap) \ + do { \ + if (extract32(cpu->env._reg, (_start), (_length)) == (_val)) { \ + hwcaps |= _hwcap; \ + } \ + } while (0) + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + MIPSCPU *cpu = MIPS_CPU(cs); + abi_ulong hwcaps = 0; + + GET_FEATURE_REG_EQU(CP0_Config0, CP0C0_AR, CP0C0_AR_LENGTH, + 2, HWCAP_MIPS_R6); + GET_FEATURE_REG_SET(CP0_Config3, 1 << CP0C3_MSAP, HWCAP_MIPS_MSA); + GET_FEATURE_INSN(ASE_LMMI, HWCAP_LOONGSON_MMI); + GET_FEATURE_INSN(ASE_LEXT, HWCAP_LOONGSON_EXT); + + return hwcaps; +} + +#undef GET_FEATURE_REG_EQU +#undef GET_FEATURE_REG_SET +#undef GET_FEATURE_INSN diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index febf710c7aeb4..877f8347d70f0 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -8,4 +8,6 @@ #ifndef MIPS_TARGET_ELF_H #define MIPS_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index 02e6d14840aed..c0347e5cb6e1c 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -8,4 +8,6 @@ #ifndef MIPS64_TARGET_ELF_H #define MIPS64_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif From 92c9983c06fa0a55f120c71dda03e38d5220fccc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 21:28:05 -1000 Subject: [PATCH 0316/1794] linux-user: Move get_elf_hwcap to sh4/elfload.c Change the return type to abi_ulong, and pass in the cpu. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 29 +---------------------------- linux-user/loader.h | 3 ++- linux-user/sh4/elfload.c | 27 +++++++++++++++++++++++++++ linux-user/sh4/target_elf.h | 2 ++ 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index dc3f502277a56..7e1c11c39f24e 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -963,34 +963,7 @@ static inline void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -enum { - SH_CPU_HAS_FPU = 0x0001, /* Hardware FPU support */ - SH_CPU_HAS_P2_FLUSH_BUG = 0x0002, /* Need to flush the cache in P2 area */ - SH_CPU_HAS_MMU_PAGE_ASSOC = 0x0004, /* SH3: TLB way selection bit support */ - SH_CPU_HAS_DSP = 0x0008, /* SH-DSP: DSP support */ - SH_CPU_HAS_PERF_COUNTER = 0x0010, /* Hardware performance counters */ - SH_CPU_HAS_PTEA = 0x0020, /* PTEA register */ - SH_CPU_HAS_LLSC = 0x0040, /* movli.l/movco.l */ - SH_CPU_HAS_L2_CACHE = 0x0080, /* Secondary cache / URAM */ - SH_CPU_HAS_OP32 = 0x0100, /* 32-bit instruction support */ - SH_CPU_HAS_PTEAEX = 0x0200, /* PTE ASID Extension support */ -}; - -#define ELF_HWCAP get_elf_hwcap() - -static uint32_t get_elf_hwcap(void) -{ - SuperHCPU *cpu = SUPERH_CPU(thread_cpu); - uint32_t hwcap = 0; - - hwcap |= SH_CPU_HAS_FPU; - - if (cpu->env.features & SH_FEATURE_SH4A) { - hwcap |= SH_CPU_HAS_LLSC; - } - - return hwcap; -} +#define ELF_HWCAP get_elf_hwcap(thread_cpu) #endif diff --git a/linux-user/loader.h b/linux-user/loader.h index 04457737dd4c0..d8a9399807665 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -103,7 +103,8 @@ const char *get_elf_cpu_model(uint32_t eflags); #if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) \ || defined(TARGET_SPARC) || defined(TARGET_PPC) \ - || defined(TARGET_LOONGARCH64) || defined(TARGET_MIPS) + || defined(TARGET_LOONGARCH64) || defined(TARGET_MIPS) \ + || defined(TARGET_SH4) abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); #endif diff --git a/linux-user/sh4/elfload.c b/linux-user/sh4/elfload.c index 546034ec07ec8..99ad4f6334c79 100644 --- a/linux-user/sh4/elfload.c +++ b/linux-user/sh4/elfload.c @@ -9,3 +9,30 @@ const char *get_elf_cpu_model(uint32_t eflags) { return "sh7785"; } + +enum { + SH_CPU_HAS_FPU = 0x0001, /* Hardware FPU support */ + SH_CPU_HAS_P2_FLUSH_BUG = 0x0002, /* Need to flush the cache in P2 area */ + SH_CPU_HAS_MMU_PAGE_ASSOC = 0x0004, /* SH3: TLB way selection bit support */ + SH_CPU_HAS_DSP = 0x0008, /* SH-DSP: DSP support */ + SH_CPU_HAS_PERF_COUNTER = 0x0010, /* Hardware performance counters */ + SH_CPU_HAS_PTEA = 0x0020, /* PTEA register */ + SH_CPU_HAS_LLSC = 0x0040, /* movli.l/movco.l */ + SH_CPU_HAS_L2_CACHE = 0x0080, /* Secondary cache / URAM */ + SH_CPU_HAS_OP32 = 0x0100, /* 32-bit instruction support */ + SH_CPU_HAS_PTEAEX = 0x0200, /* PTE ASID Extension support */ +}; + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + SuperHCPU *cpu = SUPERH_CPU(cs); + abi_ulong hwcap = 0; + + hwcap |= SH_CPU_HAS_FPU; + + if (cpu->env.features & SH_FEATURE_SH4A) { + hwcap |= SH_CPU_HAS_LLSC; + } + + return hwcap; +} diff --git a/linux-user/sh4/target_elf.h b/linux-user/sh4/target_elf.h index d17011bd752ef..badd0f5371fb3 100644 --- a/linux-user/sh4/target_elf.h +++ b/linux-user/sh4/target_elf.h @@ -8,4 +8,6 @@ #ifndef SH4_TARGET_ELF_H #define SH4_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif From 1d4774b60e3dfdc9295e36c5fa298b457d10db10 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 21:39:23 -1000 Subject: [PATCH 0317/1794] linux-user: Move hwcap functions to s390x/elfload.c For get_elf_hwcap, change the return type to abi_ulong and pass in the cpu. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 60 +--------------------------------- linux-user/loader.h | 5 +-- linux-user/s390x/elfload.c | 57 ++++++++++++++++++++++++++++++++ linux-user/s390x/target_elf.h | 2 ++ linux-user/s390x/target_proc.h | 2 +- 5 files changed, 62 insertions(+), 64 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 7e1c11c39f24e..ba8593368daa5 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1039,65 +1039,7 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_S390 -#include "elf.h" - -#define ELF_HWCAP get_elf_hwcap() - -#define GET_FEATURE(_feat, _hwcap) \ - do { if (s390_has_feat(_feat)) { hwcap |= _hwcap; } } while (0) - -uint32_t get_elf_hwcap(void) -{ - /* - * Let's assume we always have esan3 and zarch. - * 31-bit processes can use 64-bit registers (high gprs). - */ - uint32_t hwcap = HWCAP_S390_ESAN3 | HWCAP_S390_ZARCH | HWCAP_S390_HIGH_GPRS; - - GET_FEATURE(S390_FEAT_STFLE, HWCAP_S390_STFLE); - GET_FEATURE(S390_FEAT_MSA, HWCAP_S390_MSA); - GET_FEATURE(S390_FEAT_LONG_DISPLACEMENT, HWCAP_S390_LDISP); - GET_FEATURE(S390_FEAT_EXTENDED_IMMEDIATE, HWCAP_S390_EIMM); - if (s390_has_feat(S390_FEAT_EXTENDED_TRANSLATION_3) && - s390_has_feat(S390_FEAT_ETF3_ENH)) { - hwcap |= HWCAP_S390_ETF3EH; - } - GET_FEATURE(S390_FEAT_VECTOR, HWCAP_S390_VXRS); - GET_FEATURE(S390_FEAT_VECTOR_ENH, HWCAP_S390_VXRS_EXT); - GET_FEATURE(S390_FEAT_VECTOR_ENH2, HWCAP_S390_VXRS_EXT2); - - return hwcap; -} - -const char *elf_hwcap_str(uint32_t bit) -{ - static const char *hwcap_str[] = { - [HWCAP_S390_NR_ESAN3] = "esan3", - [HWCAP_S390_NR_ZARCH] = "zarch", - [HWCAP_S390_NR_STFLE] = "stfle", - [HWCAP_S390_NR_MSA] = "msa", - [HWCAP_S390_NR_LDISP] = "ldisp", - [HWCAP_S390_NR_EIMM] = "eimm", - [HWCAP_S390_NR_DFP] = "dfp", - [HWCAP_S390_NR_HPAGE] = "edat", - [HWCAP_S390_NR_ETF3EH] = "etf3eh", - [HWCAP_S390_NR_HIGH_GPRS] = "highgprs", - [HWCAP_S390_NR_TE] = "te", - [HWCAP_S390_NR_VXRS] = "vx", - [HWCAP_S390_NR_VXRS_BCD] = "vxd", - [HWCAP_S390_NR_VXRS_EXT] = "vxe", - [HWCAP_S390_NR_GS] = "gs", - [HWCAP_S390_NR_VXRS_EXT2] = "vxe2", - [HWCAP_S390_NR_VXRS_PDE] = "vxp", - [HWCAP_S390_NR_SORT] = "sort", - [HWCAP_S390_NR_DFLT] = "dflt", - [HWCAP_S390_NR_NNPA] = "nnpa", - [HWCAP_S390_NR_PCI_MIO] = "pcimio", - [HWCAP_S390_NR_SIE] = "sie", - }; - - return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; -} +#define ELF_HWCAP get_elf_hwcap(thread_cpu) static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { diff --git a/linux-user/loader.h b/linux-user/loader.h index d8a9399807665..c14e69f551055 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -104,13 +104,10 @@ const char *get_elf_cpu_model(uint32_t eflags); #if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) \ || defined(TARGET_SPARC) || defined(TARGET_PPC) \ || defined(TARGET_LOONGARCH64) || defined(TARGET_MIPS) \ - || defined(TARGET_SH4) + || defined(TARGET_SH4) || defined(TARGET_S390X) abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); #endif -#if defined(TARGET_S390X) -uint32_t get_elf_hwcap(void); -#endif const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); diff --git a/linux-user/s390x/elfload.c b/linux-user/s390x/elfload.c index 989953a247ed5..79ceaba51d19a 100644 --- a/linux-user/s390x/elfload.c +++ b/linux-user/s390x/elfload.c @@ -3,9 +3,66 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "elf.h" const char *get_elf_cpu_model(uint32_t eflags) { return "qemu"; } + +#define GET_FEATURE(_feat, _hwcap) \ + do { if (s390_has_feat(_feat)) { hwcap |= _hwcap; } } while (0) + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + /* + * Let's assume we always have esan3 and zarch. + * 31-bit processes can use 64-bit registers (high gprs). + */ + uint32_t hwcap = HWCAP_S390_ESAN3 | HWCAP_S390_ZARCH | HWCAP_S390_HIGH_GPRS; + + GET_FEATURE(S390_FEAT_STFLE, HWCAP_S390_STFLE); + GET_FEATURE(S390_FEAT_MSA, HWCAP_S390_MSA); + GET_FEATURE(S390_FEAT_LONG_DISPLACEMENT, HWCAP_S390_LDISP); + GET_FEATURE(S390_FEAT_EXTENDED_IMMEDIATE, HWCAP_S390_EIMM); + if (s390_has_feat(S390_FEAT_EXTENDED_TRANSLATION_3) && + s390_has_feat(S390_FEAT_ETF3_ENH)) { + hwcap |= HWCAP_S390_ETF3EH; + } + GET_FEATURE(S390_FEAT_VECTOR, HWCAP_S390_VXRS); + GET_FEATURE(S390_FEAT_VECTOR_ENH, HWCAP_S390_VXRS_EXT); + GET_FEATURE(S390_FEAT_VECTOR_ENH2, HWCAP_S390_VXRS_EXT2); + + return hwcap; +} + +const char *elf_hwcap_str(uint32_t bit) +{ + static const char *hwcap_str[] = { + [HWCAP_S390_NR_ESAN3] = "esan3", + [HWCAP_S390_NR_ZARCH] = "zarch", + [HWCAP_S390_NR_STFLE] = "stfle", + [HWCAP_S390_NR_MSA] = "msa", + [HWCAP_S390_NR_LDISP] = "ldisp", + [HWCAP_S390_NR_EIMM] = "eimm", + [HWCAP_S390_NR_DFP] = "dfp", + [HWCAP_S390_NR_HPAGE] = "edat", + [HWCAP_S390_NR_ETF3EH] = "etf3eh", + [HWCAP_S390_NR_HIGH_GPRS] = "highgprs", + [HWCAP_S390_NR_TE] = "te", + [HWCAP_S390_NR_VXRS] = "vx", + [HWCAP_S390_NR_VXRS_BCD] = "vxd", + [HWCAP_S390_NR_VXRS_EXT] = "vxe", + [HWCAP_S390_NR_GS] = "gs", + [HWCAP_S390_NR_VXRS_EXT2] = "vxe2", + [HWCAP_S390_NR_VXRS_PDE] = "vxp", + [HWCAP_S390_NR_SORT] = "sort", + [HWCAP_S390_NR_DFLT] = "dflt", + [HWCAP_S390_NR_NNPA] = "nnpa", + [HWCAP_S390_NR_PCI_MIO] = "pcimio", + [HWCAP_S390_NR_SIE] = "sie", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} diff --git a/linux-user/s390x/target_elf.h b/linux-user/s390x/target_elf.h index e51b053339f90..cebace949a52b 100644 --- a/linux-user/s390x/target_elf.h +++ b/linux-user/s390x/target_elf.h @@ -8,4 +8,6 @@ #ifndef S390X_TARGET_ELF_H #define S390X_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif diff --git a/linux-user/s390x/target_proc.h b/linux-user/s390x/target_proc.h index a4a4821ea5cff..60cc22d3b462f 100644 --- a/linux-user/s390x/target_proc.h +++ b/linux-user/s390x/target_proc.h @@ -48,7 +48,7 @@ static void show_cpu_summary(CPUArchState *cpu_env, int fd) { S390CPUModel *model = env_archcpu(cpu_env)->model; int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); - uint32_t elf_hwcap = get_elf_hwcap(); + uint32_t elf_hwcap = get_elf_hwcap(env_cpu(cpu_env)); const char *hwcap_str; int i; From 50e59ad0b7b1c71e858939504a749bf1a1313e2e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 21:44:02 -1000 Subject: [PATCH 0318/1794] linux-user: Move get_elf_hwcap to riscv/elfload.c Change the return type to abi_ulong, and pass in the cpu. As this is the last instance of get_elf_hwcap to be converted, remove the ifdef around the declaration in loader.h. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 14 +------------- linux-user/loader.h | 5 ----- linux-user/riscv/elfload.c | 12 ++++++++++++ linux-user/riscv/target_elf.h | 2 ++ 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index ba8593368daa5..ce4055b0e9637 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1099,19 +1099,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define VDSO_HEADER "vdso-64.c.inc" #endif -#define ELF_HWCAP get_elf_hwcap() - -static uint32_t get_elf_hwcap(void) -{ -#define MISA_BIT(EXT) (1 << (EXT - 'A')) - RISCVCPU *cpu = RISCV_CPU(thread_cpu); - uint32_t mask = MISA_BIT('I') | MISA_BIT('M') | MISA_BIT('A') - | MISA_BIT('F') | MISA_BIT('D') | MISA_BIT('C') - | MISA_BIT('V'); - - return cpu->env.misa_ext & mask; -#undef MISA_BIT -} +#define ELF_HWCAP get_elf_hwcap(thread_cpu) static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) diff --git a/linux-user/loader.h b/linux-user/loader.h index c14e69f551055..729723cc06bcd 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -101,13 +101,8 @@ extern unsigned long guest_stack_size; /* Note that Elf32 and Elf64 use uint32_t for e_flags. */ const char *get_elf_cpu_model(uint32_t eflags); -#if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) \ - || defined(TARGET_SPARC) || defined(TARGET_PPC) \ - || defined(TARGET_LOONGARCH64) || defined(TARGET_MIPS) \ - || defined(TARGET_SH4) || defined(TARGET_S390X) abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); -#endif const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); diff --git a/linux-user/riscv/elfload.c b/linux-user/riscv/elfload.c index f92adb73085b9..2e7d622232360 100644 --- a/linux-user/riscv/elfload.c +++ b/linux-user/riscv/elfload.c @@ -9,3 +9,15 @@ const char *get_elf_cpu_model(uint32_t eflags) { return "max"; } + +abi_ulong get_elf_hwcap(CPUState *cs) +{ +#define MISA_BIT(EXT) (1 << (EXT - 'A')) + RISCVCPU *cpu = RISCV_CPU(cs); + uint32_t mask = MISA_BIT('I') | MISA_BIT('M') | MISA_BIT('A') + | MISA_BIT('F') | MISA_BIT('D') | MISA_BIT('C') + | MISA_BIT('V'); + + return cpu->env.misa_ext & mask; +#undef MISA_BIT +} diff --git a/linux-user/riscv/target_elf.h b/linux-user/riscv/target_elf.h index bfe86105d0322..48d9af557bfae 100644 --- a/linux-user/riscv/target_elf.h +++ b/linux-user/riscv/target_elf.h @@ -8,4 +8,6 @@ #ifndef RISCV_TARGET_ELF_H #define RISCV_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif From 0dbb0ba870a11366b061905b1a06baa973b642d6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 2 Aug 2025 09:05:41 +1000 Subject: [PATCH 0319/1794] linux-user: Remove ELF_HWCAP All real definitions of ELF_HWCAP are now identical, and the stub definitions are 0. Provide zero stub as a fallback definition. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index ce4055b0e9637..88d439f3486e2 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -28,6 +28,7 @@ #include "qemu/lockable.h" #include "qapi/error.h" #include "qemu/error-report.h" +#include "target_elf.h" #include "target_signal.h" #include "tcg/debuginfo.h" @@ -148,8 +149,6 @@ typedef abi_int target_pid_t; #ifdef TARGET_I386 -#define ELF_HWCAP get_elf_hwcap(thread_cpu) - #ifdef TARGET_X86_64 #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 @@ -449,7 +448,6 @@ static bool init_guest_commpage(void) return true; } -#define ELF_HWCAP get_elf_hwcap(thread_cpu) #define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) #define ELF_PLATFORM get_elf_platform() @@ -539,7 +537,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#define ELF_HWCAP get_elf_hwcap(thread_cpu) #define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) #if TARGET_BIG_ENDIAN @@ -565,8 +562,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, # define ELF_ARCH EM_SPARCV9 #endif -#define ELF_HWCAP get_elf_hwcap(thread_cpu) - static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { @@ -598,7 +593,6 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_ARCH EM_PPC -#define ELF_HWCAP get_elf_hwcap(thread_cpu) #define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) /* @@ -725,8 +719,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#define ELF_HWCAP get_elf_hwcap(thread_cpu) - #define ELF_PLATFORM "loongarch" #endif /* TARGET_LOONGARCH64 */ @@ -834,8 +826,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMIPSState *e #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#define ELF_HWCAP get_elf_hwcap(thread_cpu) - #endif /* TARGET_MIPS */ #ifdef TARGET_MICROBLAZE @@ -909,7 +899,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, (*regs)[32] = tswapreg(env->pc); (*regs)[33] = tswapreg(cpu_get_sr(env)); } -#define ELF_HWCAP 0 + #define ELF_PLATFORM NULL #endif /* TARGET_OPENRISC */ @@ -963,8 +953,6 @@ static inline void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#define ELF_HWCAP get_elf_hwcap(thread_cpu) - #endif #ifdef TARGET_M68K @@ -1039,8 +1027,6 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_S390 -#define ELF_HWCAP get_elf_hwcap(thread_cpu) - static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { regs->psw.addr = infop->entry; @@ -1099,8 +1085,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define VDSO_HEADER "vdso-64.c.inc" #endif -#define ELF_HWCAP get_elf_hwcap(thread_cpu) - static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { @@ -1268,10 +1252,6 @@ static inline void init_thread(struct target_pt_regs *regs, #define elf_check_abi(x) (1) #endif -#ifndef ELF_HWCAP -#define ELF_HWCAP 0 -#endif - #ifndef STACK_GROWS_DOWN #define STACK_GROWS_DOWN 1 #endif @@ -1291,6 +1271,15 @@ static inline void init_thread(struct target_pt_regs *regs, #define EXSTACK_DEFAULT false #endif +/* + * Provide fallback definitions that the target may omit. + * One way or another, we'll get a link error if the setting of + * HAVE_* doesn't match the implementation. + */ +#ifndef HAVE_ELF_HWCAP +abi_ulong get_elf_hwcap(CPUState *cs) { return 0; } +#endif + #include "elf.h" /* We must delay the following stanzas until after "elf.h". */ @@ -1868,7 +1857,7 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, NEW_AUX_ENT(AT_EUID, (abi_ulong) geteuid()); NEW_AUX_ENT(AT_GID, (abi_ulong) getgid()); NEW_AUX_ENT(AT_EGID, (abi_ulong) getegid()); - NEW_AUX_ENT(AT_HWCAP, (abi_ulong) ELF_HWCAP); + NEW_AUX_ENT(AT_HWCAP, get_elf_hwcap(thread_cpu)); NEW_AUX_ENT(AT_CLKTCK, (abi_ulong) sysconf(_SC_CLK_TCK)); NEW_AUX_ENT(AT_RANDOM, (abi_ulong) u_rand_bytes); NEW_AUX_ENT(AT_SECURE, (abi_ulong) qemu_getauxval(AT_SECURE)); From fcac98d0ba8b5f4c311c1059d897d74d1276af9d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 2 Aug 2025 09:13:23 +1000 Subject: [PATCH 0320/1794] linux-user: Remove ELF_HWCAP2 All definitions of ELF_HWCAP2 are now identical. Provide a not-reached stub as a fallback definition of get_elf_hwcap2. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 88d439f3486e2..7a41917b49dc6 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -448,8 +448,6 @@ static bool init_guest_commpage(void) return true; } -#define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) - #define ELF_PLATFORM get_elf_platform() static const char *get_elf_platform(void) @@ -537,8 +535,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) - #if TARGET_BIG_ENDIAN # define VDSO_HEADER "vdso-be.c.inc" #else @@ -593,8 +589,6 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_ARCH EM_PPC -#define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) - /* * The requirements here are: * - keep the final alignment of sp (sp & 0xf) @@ -1279,6 +1273,10 @@ static inline void init_thread(struct target_pt_regs *regs, #ifndef HAVE_ELF_HWCAP abi_ulong get_elf_hwcap(CPUState *cs) { return 0; } #endif +#ifndef HAVE_ELF_HWCAP2 +abi_ulong get_elf_hwcap2(CPUState *cs) { g_assert_not_reached(); } +#define HAVE_ELF_HWCAP2 0 +#endif #include "elf.h" @@ -1801,9 +1799,9 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, #ifdef DLINFO_ARCH_ITEMS size += DLINFO_ARCH_ITEMS * 2; #endif -#ifdef ELF_HWCAP2 - size += 2; -#endif + if (HAVE_ELF_HWCAP2) { + size += 2; + } info->auxv_len = size * n; size += envc + argc + 2; @@ -1863,10 +1861,9 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, NEW_AUX_ENT(AT_SECURE, (abi_ulong) qemu_getauxval(AT_SECURE)); NEW_AUX_ENT(AT_EXECFN, info->file_string); -#ifdef ELF_HWCAP2 - NEW_AUX_ENT(AT_HWCAP2, (abi_ulong) ELF_HWCAP2); -#endif - + if (HAVE_ELF_HWCAP2) { + NEW_AUX_ENT(AT_HWCAP2, get_elf_hwcap(thread_cpu)); + } if (u_base_platform) { NEW_AUX_ENT(AT_BASE_PLATFORM, u_base_platform); } From 3c907dec45b66fff084fbddf18ee5bb84ddc5314 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 08:41:43 -1000 Subject: [PATCH 0321/1794] linux-user: Move get_elf_platform to {i386,x86_64}/elfload.c Move get_elf_platform to i386/elfload.c; pass in CPUState. Create a simple get_elf_platform for x86_64. Introduce HAVE_ELF_PLATFORM. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 18 ++---------------- linux-user/i386/elfload.c | 13 +++++++++++++ linux-user/i386/target_elf.h | 1 + linux-user/loader.h | 3 +++ linux-user/x86_64/elfload.c | 5 +++++ linux-user/x86_64/target_elf.h | 1 + 6 files changed, 25 insertions(+), 16 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 7a41917b49dc6..e6e509c0a603d 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -149,12 +149,12 @@ typedef abi_int target_pid_t; #ifdef TARGET_I386 +#define ELF_PLATFORM get_elf_platform(thread_cpu) + #ifdef TARGET_X86_64 #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 -#define ELF_PLATFORM "x86_64" - static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { regs->rax = 0; @@ -237,22 +237,8 @@ static bool init_guest_commpage(void) #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_386 -#define ELF_PLATFORM get_elf_platform() #define EXSTACK_DEFAULT true -static const char *get_elf_platform(void) -{ - static char elf_platform[] = "i386"; - int family = object_property_get_int(OBJECT(thread_cpu), "family", NULL); - if (family > 6) { - family = 6; - } - if (family >= 3) { - elf_platform[1] = '0' + family; - } - return elf_platform; -} - static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { diff --git a/linux-user/i386/elfload.c b/linux-user/i386/elfload.c index f99336e73c09c..1b759098ca0e6 100644 --- a/linux-user/i386/elfload.c +++ b/linux-user/i386/elfload.c @@ -14,3 +14,16 @@ abi_ulong get_elf_hwcap(CPUState *cs) { return cpu_env(cs)->features[FEAT_1_EDX]; } + +const char *get_elf_platform(CPUState *cs) +{ + static char elf_platform[] = "i386"; + int family = object_property_get_int(OBJECT(cs), "family", NULL); + if (family > 6) { + family = 6; + } + if (family >= 3) { + elf_platform[1] = '0' + family; + } + return elf_platform; +} diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index 802395af3a041..44dde1ac4a7ca 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -9,5 +9,6 @@ #define I386_TARGET_ELF_H #define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_PLATFORM 1 #endif diff --git a/linux-user/loader.h b/linux-user/loader.h index 729723cc06bcd..44bb4cbfd3fbc 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -105,5 +105,8 @@ abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); +#if defined(TARGET_I386) +const char *get_elf_platform(CPUState *cs); +#endif #endif /* LINUX_USER_LOADER_H */ diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c index f99336e73c09c..88541ea45e689 100644 --- a/linux-user/x86_64/elfload.c +++ b/linux-user/x86_64/elfload.c @@ -14,3 +14,8 @@ abi_ulong get_elf_hwcap(CPUState *cs) { return cpu_env(cs)->features[FEAT_1_EDX]; } + +const char *get_elf_platform(CPUState *cs) +{ + return "x86_64"; +} diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index 03483bad57360..498c3f7e4ec13 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -9,5 +9,6 @@ #define X86_64_TARGET_ELF_H #define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_PLATFORM 1 #endif From e03b6ad483c087c8cf0357b7859d98bd2b109071 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 08:43:35 -1000 Subject: [PATCH 0322/1794] linux-user/i386: Return const data from get_elf_platform Rather than modify a static buffer, index into an array of const data. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/i386/elfload.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/linux-user/i386/elfload.c b/linux-user/i386/elfload.c index 1b759098ca0e6..ef3a6c35d20e9 100644 --- a/linux-user/i386/elfload.c +++ b/linux-user/i386/elfload.c @@ -17,13 +17,9 @@ abi_ulong get_elf_hwcap(CPUState *cs) const char *get_elf_platform(CPUState *cs) { - static char elf_platform[] = "i386"; + static const char elf_platform[4][5] = { "i386", "i486", "i586", "i686" }; int family = object_property_get_int(OBJECT(cs), "family", NULL); - if (family > 6) { - family = 6; - } - if (family >= 3) { - elf_platform[1] = '0' + family; - } - return elf_platform; + + family = MAX(MIN(family, 6), 3); + return elf_platform[family - 3]; } From d6b8c5dbd9b0fd05c3f8ac9427729082eecb9fbb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 08:47:57 -1000 Subject: [PATCH 0323/1794] linux-user: Move get_elf_platform to arm/elfload.c Move the aarch32 get_elf_platform to arm/elfload.c; pass in CPUState. Create a simple version in aarch64/elfload.c, which we must do at the same time because of the ifdef dependency between TARGET_AARCH64 and TARGET_ARM. Since all versions of get_elf_platform now have the same signature, remove the ifdef from the declaration in loader.h. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/elfload.c | 5 +++++ linux-user/aarch64/target_elf.h | 1 + linux-user/arm/elfload.c | 29 +++++++++++++++++++++++++ linux-user/arm/target_elf.h | 1 + linux-user/elfload.c | 38 ++------------------------------- linux-user/loader.h | 2 -- 6 files changed, 38 insertions(+), 38 deletions(-) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 92c8ea62c6f37..1030cb8094722 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -342,3 +342,8 @@ const char *elf_hwcap2_str(uint32_t bit) return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; } + +const char *get_elf_platform(CPUState *cs) +{ + return TARGET_BIG_ENDIAN ? "aarch64_be" : "aarch64"; +} diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index 77108f3cb0cda..dee79ce0c60ec 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -10,5 +10,6 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 +#define HAVE_ELF_PLATFORM 1 #endif diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index c7561b005b483..7de1f13f4be25 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -170,3 +170,32 @@ const char *elf_hwcap2_str(uint32_t bit) return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; } + +const char *get_elf_platform(CPUState *cs) +{ + CPUARMState *env = cpu_env(cs); + +#if TARGET_BIG_ENDIAN +# define END "b" +#else +# define END "l" +#endif + + if (arm_feature(env, ARM_FEATURE_V8)) { + return "v8" END; + } else if (arm_feature(env, ARM_FEATURE_V7)) { + if (arm_feature(env, ARM_FEATURE_M)) { + return "v7m" END; + } else { + return "v7" END; + } + } else if (arm_feature(env, ARM_FEATURE_V6)) { + return "v6" END; + } else if (arm_feature(env, ARM_FEATURE_V5)) { + return "v5" END; + } else { + return "v4" END; + } + +#undef END +} diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index 90470bd87b6dd..856ca41b165b2 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -10,5 +10,6 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 +#define HAVE_ELF_PLATFORM 1 #endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index e6e509c0a603d..0e41737cf1bfd 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -309,6 +309,8 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en #ifdef TARGET_ARM +#define ELF_PLATFORM get_elf_platform(thread_cpu) + #ifndef TARGET_AARCH64 /* 32 bit ARM definitions */ @@ -434,37 +436,6 @@ static bool init_guest_commpage(void) return true; } -#define ELF_PLATFORM get_elf_platform() - -static const char *get_elf_platform(void) -{ - CPUARMState *env = cpu_env(thread_cpu); - -#if TARGET_BIG_ENDIAN -# define END "b" -#else -# define END "l" -#endif - - if (arm_feature(env, ARM_FEATURE_V8)) { - return "v8" END; - } else if (arm_feature(env, ARM_FEATURE_V7)) { - if (arm_feature(env, ARM_FEATURE_M)) { - return "v7m" END; - } else { - return "v7" END; - } - } else if (arm_feature(env, ARM_FEATURE_V6)) { - return "v6" END; - } else if (arm_feature(env, ARM_FEATURE_V5)) { - return "v5" END; - } else { - return "v4" END; - } - -#undef END -} - #if TARGET_BIG_ENDIAN #include "elf.h" #include "vdso-be8.c.inc" @@ -487,11 +458,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_ARCH EM_AARCH64 #define ELF_CLASS ELFCLASS64 -#if TARGET_BIG_ENDIAN -# define ELF_PLATFORM "aarch64_be" -#else -# define ELF_PLATFORM "aarch64" -#endif static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) diff --git a/linux-user/loader.h b/linux-user/loader.h index 44bb4cbfd3fbc..440871466c0af 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -105,8 +105,6 @@ abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); -#if defined(TARGET_I386) const char *get_elf_platform(CPUState *cs); -#endif #endif /* LINUX_USER_LOADER_H */ From 084b3247a08da16c541efba059c308b0d9299bdc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 09:00:52 -1000 Subject: [PATCH 0324/1794] linux-user/loongarch64: Create get_elf_platform Move the string literal to a new function. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 2 +- linux-user/loongarch64/elfload.c | 5 +++++ linux-user/loongarch64/target_elf.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0e41737cf1bfd..9d61feae3016f 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -665,7 +665,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#define ELF_PLATFORM "loongarch" +#define ELF_PLATFORM get_elf_platform(thread_cpu) #endif /* TARGET_LOONGARCH64 */ diff --git a/linux-user/loongarch64/elfload.c b/linux-user/loongarch64/elfload.c index ee4a85b8d6c42..911352840f710 100644 --- a/linux-user/loongarch64/elfload.c +++ b/linux-user/loongarch64/elfload.c @@ -56,3 +56,8 @@ abi_ulong get_elf_hwcap(CPUState *cs) return hwcaps; } + +const char *get_elf_platform(CPUState *cs) +{ + return "loongarch"; +} diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index 037740d36f24a..eb179273255a7 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -7,5 +7,6 @@ #define LOONGARCH_TARGET_ELF_H #define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_PLATFORM 1 #endif From eaf983e04b342ab4c3401ad8add649ec626a0744 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 09:06:39 -1000 Subject: [PATCH 0325/1794] linux-user/hppa: Create get_elf_platform Move the string literal to a new function. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 2 +- linux-user/hppa/elfload.c | 5 +++++ linux-user/hppa/target_elf.h | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 9d61feae3016f..83cb6731ec8af 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1046,7 +1046,7 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_PARISC -#define ELF_PLATFORM "PARISC" +#define ELF_PLATFORM get_elf_platform(thread_cpu) #define STACK_GROWS_DOWN 0 #define STACK_ALIGNMENT 64 diff --git a/linux-user/hppa/elfload.c b/linux-user/hppa/elfload.c index 2274fcbde435d..9dd3fe092a82e 100644 --- a/linux-user/hppa/elfload.c +++ b/linux-user/hppa/elfload.c @@ -9,3 +9,8 @@ const char *get_elf_cpu_model(uint32_t eflags) { return "hppa"; } + +const char *get_elf_platform(CPUState *cs) +{ + return "PARISC"; +} diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h index 5826ca2cd22f6..85be00584d8ea 100644 --- a/linux-user/hppa/target_elf.h +++ b/linux-user/hppa/target_elf.h @@ -8,4 +8,6 @@ #ifndef HPPA_TARGET_ELF_H #define HPPA_TARGET_ELF_H +#define HAVE_ELF_PLATFORM 1 + #endif From f10c3d90849da63916905f7bd155d5737aa4378f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 09:18:03 -1000 Subject: [PATCH 0326/1794] linux-user: Remove ELF_PLATFORM All real definitions of ELF_PLATFORM are now identical, and the stub definitions are NULL. Use HAVE_ELF_PLATFORM and provide a stub as a fallback definition of get_elf_platform. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 83cb6731ec8af..d2d73b06fc005 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -149,8 +149,6 @@ typedef abi_int target_pid_t; #ifdef TARGET_I386 -#define ELF_PLATFORM get_elf_platform(thread_cpu) - #ifdef TARGET_X86_64 #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 @@ -309,8 +307,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en #ifdef TARGET_ARM -#define ELF_PLATFORM get_elf_platform(thread_cpu) - #ifndef TARGET_AARCH64 /* 32 bit ARM definitions */ @@ -665,8 +661,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#define ELF_PLATFORM get_elf_platform(thread_cpu) - #endif /* TARGET_LOONGARCH64 */ #ifdef TARGET_MIPS @@ -846,8 +840,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, (*regs)[33] = tswapreg(cpu_get_sr(env)); } -#define ELF_PLATFORM NULL - #endif /* TARGET_OPENRISC */ #ifdef TARGET_SH4 @@ -1046,7 +1038,6 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_PARISC -#define ELF_PLATFORM get_elf_platform(thread_cpu) #define STACK_GROWS_DOWN 0 #define STACK_ALIGNMENT 64 @@ -1182,10 +1173,6 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_BASE_PLATFORM (NULL) #endif -#ifndef ELF_PLATFORM -#define ELF_PLATFORM (NULL) -#endif - #ifndef ELF_MACHINE #define ELF_MACHINE ELF_ARCH #endif @@ -1229,6 +1216,9 @@ abi_ulong get_elf_hwcap(CPUState *cs) { return 0; } abi_ulong get_elf_hwcap2(CPUState *cs) { g_assert_not_reached(); } #define HAVE_ELF_HWCAP2 0 #endif +#ifndef HAVE_ELF_PLATFORM +const char *get_elf_platform(CPUState *cs) { return NULL; } +#endif #include "elf.h" @@ -1699,7 +1689,7 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, } u_platform = 0; - k_platform = ELF_PLATFORM; + k_platform = get_elf_platform(thread_cpu); if (k_platform) { size_t len = strlen(k_platform) + 1; if (STACK_GROWS_DOWN) { From f8498e084eaa7f78d455d5f2f7a308a4d0d5f39b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 09:24:31 -1000 Subject: [PATCH 0327/1794] linux-user: Move get_elf_base_platform to mips/elfload.c Pass in CPUState; define HAVE_ELF_BASE_PLATFORM. Since this was the only instance of ELF_BASE_PLATFORM, go ahead and provide the stub definition for other platforms. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 40 ++++------------------------------ linux-user/loader.h | 1 + linux-user/mips/elfload.c | 30 +++++++++++++++++++++++++ linux-user/mips/target_elf.h | 1 + linux-user/mips64/target_elf.h | 1 + 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index d2d73b06fc005..4facaa7e27867 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -679,37 +679,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) #endif -#define ELF_BASE_PLATFORM get_elf_base_platform() - -#define MATCH_PLATFORM_INSN(_flags, _base_platform) \ - do { if ((cpu->env.insn_flags & (_flags)) == _flags) \ - { return _base_platform; } } while (0) - -static const char *get_elf_base_platform(void) -{ - MIPSCPU *cpu = MIPS_CPU(thread_cpu); - - /* 64 bit ISAs goes first */ - MATCH_PLATFORM_INSN(CPU_MIPS64R6, "mips64r6"); - MATCH_PLATFORM_INSN(CPU_MIPS64R5, "mips64r5"); - MATCH_PLATFORM_INSN(CPU_MIPS64R2, "mips64r2"); - MATCH_PLATFORM_INSN(CPU_MIPS64R1, "mips64"); - MATCH_PLATFORM_INSN(CPU_MIPS5, "mips5"); - MATCH_PLATFORM_INSN(CPU_MIPS4, "mips4"); - MATCH_PLATFORM_INSN(CPU_MIPS3, "mips3"); - - /* 32 bit ISAs */ - MATCH_PLATFORM_INSN(CPU_MIPS32R6, "mips32r6"); - MATCH_PLATFORM_INSN(CPU_MIPS32R5, "mips32r5"); - MATCH_PLATFORM_INSN(CPU_MIPS32R2, "mips32r2"); - MATCH_PLATFORM_INSN(CPU_MIPS32R1, "mips32"); - MATCH_PLATFORM_INSN(CPU_MIPS2, "mips2"); - - /* Fallback */ - return "mips"; -} -#undef MATCH_PLATFORM_INSN - static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { @@ -1169,10 +1138,6 @@ static inline void init_thread(struct target_pt_regs *regs, #endif /* TARGET_HEXAGON */ -#ifndef ELF_BASE_PLATFORM -#define ELF_BASE_PLATFORM (NULL) -#endif - #ifndef ELF_MACHINE #define ELF_MACHINE ELF_ARCH #endif @@ -1219,6 +1184,9 @@ abi_ulong get_elf_hwcap2(CPUState *cs) { g_assert_not_reached(); } #ifndef HAVE_ELF_PLATFORM const char *get_elf_platform(CPUState *cs) { return NULL; } #endif +#ifndef HAVE_ELF_BASE_PLATFORM +const char *get_elf_base_platform(CPUState *cs) { return NULL; } +#endif #include "elf.h" @@ -1673,7 +1641,7 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, } u_base_platform = 0; - k_base_platform = ELF_BASE_PLATFORM; + k_base_platform = get_elf_base_platform(thread_cpu); if (k_base_platform) { size_t len = strlen(k_base_platform) + 1; if (STACK_GROWS_DOWN) { diff --git a/linux-user/loader.h b/linux-user/loader.h index 440871466c0af..42cba90deacf8 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -106,5 +106,6 @@ abi_ulong get_elf_hwcap2(CPUState *cs); const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); const char *get_elf_platform(CPUState *cs); +const char *get_elf_base_platform(CPUState *cs); #endif /* LINUX_USER_LOADER_H */ diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c index 739f71c21b11a..c353ccc1ad1a7 100644 --- a/linux-user/mips/elfload.c +++ b/linux-user/mips/elfload.c @@ -92,3 +92,33 @@ abi_ulong get_elf_hwcap(CPUState *cs) #undef GET_FEATURE_REG_EQU #undef GET_FEATURE_REG_SET #undef GET_FEATURE_INSN + +#define MATCH_PLATFORM_INSN(_flags, _base_platform) \ + do { if ((cpu->env.insn_flags & (_flags)) == _flags) \ + { return _base_platform; } } while (0) + +const char *get_elf_base_platform(CPUState *cs) +{ + MIPSCPU *cpu = MIPS_CPU(cs); + + /* 64 bit ISAs goes first */ + MATCH_PLATFORM_INSN(CPU_MIPS64R6, "mips64r6"); + MATCH_PLATFORM_INSN(CPU_MIPS64R5, "mips64r5"); + MATCH_PLATFORM_INSN(CPU_MIPS64R2, "mips64r2"); + MATCH_PLATFORM_INSN(CPU_MIPS64R1, "mips64"); + MATCH_PLATFORM_INSN(CPU_MIPS5, "mips5"); + MATCH_PLATFORM_INSN(CPU_MIPS4, "mips4"); + MATCH_PLATFORM_INSN(CPU_MIPS3, "mips3"); + + /* 32 bit ISAs */ + MATCH_PLATFORM_INSN(CPU_MIPS32R6, "mips32r6"); + MATCH_PLATFORM_INSN(CPU_MIPS32R5, "mips32r5"); + MATCH_PLATFORM_INSN(CPU_MIPS32R2, "mips32r2"); + MATCH_PLATFORM_INSN(CPU_MIPS32R1, "mips32"); + MATCH_PLATFORM_INSN(CPU_MIPS2, "mips2"); + + /* Fallback */ + return "mips"; +} + +#undef MATCH_PLATFORM_INSN diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index 877f8347d70f0..08e699c08554e 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -9,5 +9,6 @@ #define MIPS_TARGET_ELF_H #define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_BASE_PLATFORM 1 #endif diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index c0347e5cb6e1c..24bb7fcd3ff92 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -9,5 +9,6 @@ #define MIPS64_TARGET_ELF_H #define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_BASE_PLATFORM 1 #endif From d8329660b2e103ac2bbbf9ae933c0c742d44864e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 09:49:39 -1000 Subject: [PATCH 0328/1794] linux-user: Move target_cpu_copy_regs decl to qemu.h The function is not used by bsd-user, so placement within include/user/cpu_loop.h is not ideal. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- include/user/cpu_loop.h | 4 ---- linux-user/qemu.h | 3 +++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/user/cpu_loop.h b/include/user/cpu_loop.h index ad8a1d711f0bd..346e37ede8bea 100644 --- a/include/user/cpu_loop.h +++ b/include/user/cpu_loop.h @@ -81,8 +81,4 @@ void target_exception_dump(CPUArchState *env, const char *fmt, int code); #define EXCP_DUMP(env, fmt, code) \ target_exception_dump(env, fmt, code) -typedef struct target_pt_regs target_pt_regs; - -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs); - #endif diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 4d6fad28c63c2..0c3cfe93a14b5 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -359,4 +359,7 @@ void *lock_user_string(abi_ulong guest_addr); /* Clone cpu state */ CPUArchState *cpu_copy(CPUArchState *env); +typedef struct target_pt_regs target_pt_regs; +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs); + #endif /* QEMU_H */ From 1f2f4c0fbcc527f47e2f9d5708ae7df824bd574f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 10:00:36 -1000 Subject: [PATCH 0329/1794] linux-user: Create do_init_main_thread Provide a unified function to initialize the main thread. Keep target_pt_regs isolated to this function. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 +++++-- linux-user/linuxload.c | 6 ++---- linux-user/loader.h | 5 ++--- linux-user/main.c | 10 +++------- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 4facaa7e27867..6fce74f45a435 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -3619,7 +3619,10 @@ static int elf_core_dump(int signr, const CPUArchState *env) } #endif /* USE_ELF_CORE_DUMP */ -void do_init_thread(struct target_pt_regs *regs, struct image_info *infop) +void do_init_main_thread(CPUState *cs, struct image_info *infop) { - init_thread(regs, infop); + target_pt_regs regs = { }; + + init_thread(®s, infop); + target_cpu_copy_regs(cpu_env(cs), ®s); } diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c index 37f132be4aff0..85d700953e233 100644 --- a/linux-user/linuxload.c +++ b/linux-user/linuxload.c @@ -139,8 +139,7 @@ abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, } int loader_exec(int fdexec, const char *filename, char **argv, char **envp, - struct target_pt_regs *regs, struct image_info *infop, - struct linux_binprm *bprm) + struct image_info *infop, struct linux_binprm *bprm) { int retval; @@ -175,8 +174,7 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, return retval; } - /* Success. Initialize important registers. */ - do_init_thread(regs, infop); + /* Success. */ return 0; } diff --git a/linux-user/loader.h b/linux-user/loader.h index 42cba90deacf8..e0291cc3b0f58 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -82,12 +82,11 @@ struct linux_binprm { int (*core_dump)(int, const CPUArchState *); /* coredump routine */ }; -void do_init_thread(struct target_pt_regs *regs, struct image_info *infop); +void do_init_main_thread(CPUState *cs, struct image_info *infop); abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, abi_ulong stringp, int push_ptr); int loader_exec(int fdexec, const char *filename, char **argv, char **envp, - struct target_pt_regs *regs, struct image_info *infop, - struct linux_binprm *); + struct image_info *infop, struct linux_binprm *); uint32_t get_elf_eflags(int fd); int load_elf_binary(struct linux_binprm *bprm, struct image_info *info); diff --git a/linux-user/main.c b/linux-user/main.c index ad1a29d1989d4..e21842bde904d 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -696,7 +696,6 @@ static int parse_args(int argc, char **argv) int main(int argc, char **argv, char **envp) { - struct target_pt_regs regs1, *regs = ®s1; struct image_info info1, *info = &info1; struct linux_binprm bprm; TaskState *ts; @@ -762,9 +761,6 @@ int main(int argc, char **argv, char **envp) trace_init_file(); qemu_plugin_load_list(&plugins, &error_fatal); - /* Zero out regs */ - memset(regs, 0, sizeof(struct target_pt_regs)); - /* Zero out image_info */ memset(info, 0, sizeof(struct image_info)); @@ -988,8 +984,8 @@ int main(int argc, char **argv, char **envp) fd_trans_init(); - ret = loader_exec(execfd, exec_path, target_argv, target_environ, regs, - info, &bprm); + ret = loader_exec(execfd, exec_path, target_argv, target_environ, + info, &bprm); if (ret != 0) { printf("Error while loading %s: %s\n", exec_path, strerror(-ret)); _exit(EXIT_FAILURE); @@ -1041,7 +1037,7 @@ int main(int argc, char **argv, char **envp) the real value of GUEST_BASE into account. */ tcg_prologue_init(); - target_cpu_copy_regs(env, regs); + do_init_main_thread(cpu, info); if (gdbstub) { gdbserver_start(gdbstub, &error_fatal); From 8d4020dd025dc37d9003c6a514e758d8c6ef99b9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 10:17:44 -1000 Subject: [PATCH 0330/1794] linux-user/i386: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Temporarily introduce HAVE_INIT_MAIN_THREAD during conversion. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 29 +++++------------------ linux-user/i386/cpu_loop.c | 48 +++++++++++++++++--------------------- linux-user/qemu.h | 1 + 3 files changed, 29 insertions(+), 49 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 6fce74f45a435..89f39722539a7 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -149,17 +149,12 @@ typedef abi_int target_pid_t; #ifdef TARGET_I386 +#define HAVE_INIT_MAIN_THREAD + #ifdef TARGET_X86_64 #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 -static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) -{ - regs->rax = 0; - regs->rsp = infop->start_stack; - regs->rip = infop->entry; -} - #define ELF_NREG 27 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -237,22 +232,6 @@ static bool init_guest_commpage(void) #define EXSTACK_DEFAULT true -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->esp = infop->start_stack; - regs->eip = infop->entry; - - /* SVR4/i386 ABI (pages 3-31, 3-32) says that when the program - starts %edx contains a pointer to a function which might be - registered using `atexit'. This provides a mean for the - dynamic linker to call DT_FINI functions for shared libraries - that have been loaded before the code runs. - - A value of 0 tells we have no such handler. */ - regs->edx = 0; -} - #define ELF_NREG 17 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -3621,8 +3600,12 @@ static int elf_core_dump(int signr, const CPUArchState *env) void do_init_main_thread(CPUState *cs, struct image_info *infop) { +#ifdef HAVE_INIT_MAIN_THREAD + init_main_thread(cs, infop); +#else target_pt_regs regs = { }; init_thread(®s, infop); target_cpu_copy_regs(cpu_env(cs), ®s); +#endif } diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c index d96d5553fafc9..7b2d8b03d8413 100644 --- a/linux-user/i386/cpu_loop.c +++ b/linux-user/i386/cpu_loop.c @@ -331,11 +331,10 @@ static void target_cpu_free(void *obj) g_free(obj); } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cpu, struct image_info *info) { - CPUState *cpu = env_cpu(env); + CPUArchState *env = cpu_env(cpu); bool is64 = (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) != 0; - int i; OBJECT(cpu)->free = target_cpu_free; env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK; @@ -361,28 +360,25 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) /* flags setup : we activate the IRQs by default as in user mode */ env->eflags |= IF_MASK; - /* linux register setup */ -#ifndef TARGET_ABI32 - env->regs[R_EAX] = regs->rax; - env->regs[R_EBX] = regs->rbx; - env->regs[R_ECX] = regs->rcx; - env->regs[R_EDX] = regs->rdx; - env->regs[R_ESI] = regs->rsi; - env->regs[R_EDI] = regs->rdi; - env->regs[R_EBP] = regs->rbp; - env->regs[R_ESP] = regs->rsp; - env->eip = regs->rip; -#else - env->regs[R_EAX] = regs->eax; - env->regs[R_EBX] = regs->ebx; - env->regs[R_ECX] = regs->ecx; - env->regs[R_EDX] = regs->edx; - env->regs[R_ESI] = regs->esi; - env->regs[R_EDI] = regs->edi; - env->regs[R_EBP] = regs->ebp; - env->regs[R_ESP] = regs->esp; - env->eip = regs->eip; -#endif + /* + * Linux register setup. + * + * SVR4/i386 ABI (pages 3-31, 3-32) says that when the program + * starts %edx contains a pointer to a function which might be + * registered using `atexit'. This provides a mean for the + * dynamic linker to call DT_FINI functions for shared libraries + * that have been loaded before the code runs. + * A value of 0 tells we have no such handler. + * + * This applies to x86_64 as well as i386. + * + * That said, the kernel's ELF_PLAT_INIT simply zeros all of the general + * registers. Note that x86_cpu_reset_hold will set %edx to cpuid_version; + * clear all general registers defensively. + */ + memset(env->regs, 0, sizeof(env->regs)); + env->regs[R_ESP] = info->start_stack; + env->eip = info->entry; /* linux interrupt setup */ #ifndef TARGET_ABI32 @@ -394,7 +390,7 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); idt_table = g2h_untagged(env->idt.base); - for (i = 0; i < 20; i++) { + for (int i = 0; i < 20; i++) { set_idt(i, 0, is64); } set_idt(3, 3, is64); diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 0c3cfe93a14b5..8a9500d4f45e3 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -361,5 +361,6 @@ CPUArchState *cpu_copy(CPUArchState *env); typedef struct target_pt_regs target_pt_regs; void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs); +void init_main_thread(CPUState *cs, struct image_info *info); #endif /* QEMU_H */ From ea8683d2b3777fcb934a075df8b63d45642097d6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 10:43:26 -1000 Subject: [PATCH 0331/1794] linux-user/arm: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/arm/cpu_loop.c | 53 +++++++++++++++++++++++++++++++-------- linux-user/elfload.c | 41 +----------------------------- 2 files changed, 44 insertions(+), 50 deletions(-) diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 9d54422736c5f..739e1607e3d80 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -480,17 +480,50 @@ void cpu_loop(CPUARMState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - CPUState *cpu = env_cpu(env); - TaskState *ts = get_task_state(cpu); - struct image_info *info = ts->info; - int i; - - cpsr_write(env, regs->uregs[16], CPSR_USER | CPSR_EXEC, - CPSRWriteByInstr); - for(i = 0; i < 16; i++) { - env->regs[i] = regs->uregs[i]; + CPUARMState *env = cpu_env(cs); + abi_ptr stack = info->start_stack; + abi_ptr entry = info->entry; + + cpsr_write(env, ARM_CPU_MODE_USR | (entry & 1 ? CPSR_T : 0), + CPSR_USER | CPSR_EXEC, CPSRWriteByInstr); + + env->regs[15] = entry & 0xfffffffe; + env->regs[13] = stack; + + /* FIXME - what to for failure of get_user()? */ + get_user_ual(env->regs[2], stack + 8); /* envp */ + get_user_ual(env->regs[1], stack + 4); /* envp */ + + /* + * Per the SVR4 ABI, r0 contains a pointer to a function to be + * registered with atexit. A value of 0 means we have no such handler. + */ + env->regs[0] = 0; + + /* For uClinux PIC binaries. */ + /* XXX: Linux does this only on ARM with no MMU (do we care?) */ + env->regs[10] = info->start_data; + + /* Support ARM FDPIC. */ + if (info_is_fdpic(info)) { + /* + * As described in the ABI document, r7 points to the loadmap info + * prepared by the kernel. If an interpreter is needed, r8 points + * to the interpreter loadmap and r9 points to the interpreter + * PT_DYNAMIC info. If no interpreter is needed, r8 is zero, and + * r9 points to the main program PT_DYNAMIC info. + */ + env->regs[7] = info->loadmap_addr; + if (info->interpreter_loadmap_addr) { + /* Executable is dynamically loaded. */ + env->regs[8] = info->interpreter_loadmap_addr; + env->regs[9] = info->interpreter_pt_dynamic_addr; + } else { + env->regs[8] = 0; + env->regs[9] = info->pt_dynamic_addr; + } } if (TARGET_BIG_ENDIAN) { diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 89f39722539a7..95868739546ff 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -293,46 +293,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en #define ELF_CLASS ELFCLASS32 #define EXSTACK_DEFAULT true -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - abi_long stack = infop->start_stack; - memset(regs, 0, sizeof(*regs)); - - regs->uregs[16] = ARM_CPU_MODE_USR; - if (infop->entry & 1) { - regs->uregs[16] |= CPSR_T; - } - regs->uregs[15] = infop->entry & 0xfffffffe; - regs->uregs[13] = infop->start_stack; - /* FIXME - what to for failure of get_user()? */ - get_user_ual(regs->uregs[2], stack + 8); /* envp */ - get_user_ual(regs->uregs[1], stack + 4); /* envp */ - /* XXX: it seems that r0 is zeroed after ! */ - regs->uregs[0] = 0; - /* For uClinux PIC binaries. */ - /* XXX: Linux does this only on ARM with no MMU (do we care ?) */ - regs->uregs[10] = infop->start_data; - - /* Support ARM FDPIC. */ - if (info_is_fdpic(infop)) { - /* As described in the ABI document, r7 points to the loadmap info - * prepared by the kernel. If an interpreter is needed, r8 points - * to the interpreter loadmap and r9 points to the interpreter - * PT_DYNAMIC info. If no interpreter is needed, r8 is zero, and - * r9 points to the main program PT_DYNAMIC info. - */ - regs->uregs[7] = infop->loadmap_addr; - if (infop->interpreter_loadmap_addr) { - /* Executable is dynamically loaded. */ - regs->uregs[8] = infop->interpreter_loadmap_addr; - regs->uregs[9] = infop->interpreter_pt_dynamic_addr; - } else { - regs->uregs[8] = 0; - regs->uregs[9] = infop->pt_dynamic_addr; - } - } -} +#define HAVE_INIT_MAIN_THREAD #define ELF_NREG 18 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; From e872342837ed61af5f2ed15541375adf0c23e0eb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 3 Aug 2025 07:51:51 +1000 Subject: [PATCH 0332/1794] linux-user/arm: Remove a.out startup remenents The setting of r1/r2 was removed in kernel commit acfdd4b1f7590d0 ("ARM: 7791/1: a.out: remove partial a.out support"), and the kernel commit message explains the history. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/arm/cpu_loop.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 739e1607e3d80..9aeb9b0087f5f 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -492,10 +492,6 @@ void init_main_thread(CPUState *cs, struct image_info *info) env->regs[15] = entry & 0xfffffffe; env->regs[13] = stack; - /* FIXME - what to for failure of get_user()? */ - get_user_ual(env->regs[2], stack + 8); /* envp */ - get_user_ual(env->regs[1], stack + 4); /* envp */ - /* * Per the SVR4 ABI, r0 contains a pointer to a function to be * registered with atexit. A value of 0 means we have no such handler. From 2eaaf04ad7f9eb5bd578f3f225d118c9eb0bef1d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 11:04:34 -1000 Subject: [PATCH 0333/1794] linux-user/aarch64: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/cpu_loop.c | 14 ++++++-------- linux-user/elfload.c | 10 +--------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 030a630c936e7..4c4921152e84a 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -137,10 +137,10 @@ void cpu_loop(CPUARMState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { + CPUARMState *env = cpu_env(cs); ARMCPU *cpu = env_archcpu(env); - int i; if (!(arm_feature(env, ARM_FEATURE_AARCH64))) { fprintf(stderr, @@ -148,14 +148,12 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) exit(EXIT_FAILURE); } - for (i = 0; i < 31; i++) { - env->xregs[i] = regs->regs[i]; - } - env->pc = regs->pc; - env->xregs[31] = regs->sp; + env->pc = info->entry & ~0x3ULL; + env->xregs[31] = info->start_stack; + #if TARGET_BIG_ENDIAN env->cp15.sctlr_el[1] |= SCTLR_E0E; - for (i = 1; i < 4; ++i) { + for (int i = 1; i < 4; ++i) { env->cp15.sctlr_el[i] |= SCTLR_EE; } arm_rebuild_hflags(env); diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 95868739546ff..f93afbdcea37b 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -395,15 +395,7 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_ARCH EM_AARCH64 #define ELF_CLASS ELFCLASS64 -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - abi_long stack = infop->start_stack; - memset(regs, 0, sizeof(*regs)); - - regs->pc = infop->entry & ~0x3ULL; - regs->sp = stack; -} +#define HAVE_INIT_MAIN_THREAD #define ELF_NREG 34 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; From 7b9efb7aaef9b43b22fce7051861f59fe15ff51a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 12:29:11 -1000 Subject: [PATCH 0334/1794] linux-user/sparc: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 12 ++---------- linux-user/sparc/cpu_loop.c | 16 +++++++--------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index f93afbdcea37b..887a3a1cb2504 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -438,16 +438,8 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, # define ELF_ARCH EM_SPARCV9 #endif -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - /* Note that target_cpu_copy_regs does not read psr/tstate. */ - regs->pc = infop->entry; - regs->npc = regs->pc + 4; - regs->y = 0; - regs->u_regs[14] = (infop->start_stack - 16 * sizeof(abi_ulong) - - TARGET_STACK_BIAS); -} +#define HAVE_INIT_MAIN_THREAD + #endif /* TARGET_SPARC */ #ifdef TARGET_PPC diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index 68f1e8ecd43bf..7d30cd1ff22e6 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -357,14 +357,12 @@ void cpu_loop (CPUSPARCState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; - env->pc = regs->pc; - env->npc = regs->npc; - env->y = regs->y; - for(i = 0; i < 8; i++) - env->gregs[i] = regs->u_regs[i]; - for(i = 0; i < 8; i++) - env->regwptr[i] = regs->u_regs[i + 8]; + CPUArchState *env = cpu_env(cs); + + env->pc = info->entry; + env->npc = env->pc + 4; + env->regwptr[WREG_SP] = (info->start_stack - 16 * sizeof(abi_ulong) + - TARGET_STACK_BIAS); } From 88c9adef2b0fff84f0e9cbc728e860cfbad52008 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 13:09:29 -1000 Subject: [PATCH 0335/1794] linux-user/ppc: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 17 +---------------- linux-user/ppc/cpu_loop.c | 26 ++++++++++++++++++-------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 887a3a1cb2504..a30431c7a2dcf 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -485,22 +485,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ } while (0) -static inline void init_thread(struct target_pt_regs *_regs, struct image_info *infop) -{ - _regs->gpr[1] = infop->start_stack; -#if defined(TARGET_PPC64) - if (get_ppc64_abi(infop) < 2) { - uint64_t val; - get_user_u64(val, infop->entry + 8); - _regs->gpr[2] = val + infop->load_bias; - get_user_u64(val, infop->entry); - infop->entry = val + infop->load_bias; - } else { - _regs->gpr[12] = infop->entry; /* r12 set to global entry address */ - } -#endif - _regs->nip = infop->entry; -} +#define HAVE_INIT_MAIN_THREAD /* See linux kernel: arch/powerpc/include/asm/elf.h. */ #define ELF_NREG 48 diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index 2a0efaffcd6a5..22885ffd90606 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -378,21 +378,31 @@ void cpu_loop(CPUPPCState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; + CPUArchState *env = cpu_env(cs); + abi_ptr entry = info->entry; + + env->gpr[1] = info->start_stack; + +#ifdef TARGET_PPC64 + if (get_ppc64_abi(info) < 2) { + uint64_t val; + get_user_u64(val, entry + 8); + env->gpr[2] = val + info->load_bias; + get_user_u64(val, entry); + entry = val + info->load_bias; + } else { + env->gpr[12] = entry; /* r12 set to global entry address */ + } -#if defined(TARGET_PPC64) int flag = (env->insns_flags2 & PPC2_BOOKE206) ? MSR_CM : MSR_SF; #if defined(TARGET_ABI32) ppc_store_msr(env, env->msr & ~((target_ulong)1 << flag)); #else ppc_store_msr(env, env->msr | (target_ulong)1 << flag); #endif -#endif +#endif /* TARGET_PPC64 */ - env->nip = regs->nip; - for(i = 0; i < 32; i++) { - env->gpr[i] = regs->gpr[i]; - } + env->nip = entry; } From a2c83f5156324b88cf5c92ed777648fc1aaccf7f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 13:22:48 -1000 Subject: [PATCH 0336/1794] linux-user/loongarch64: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Note that init_thread had set crmd in target_pt_regs, but target_cpu_copy_regs did not copy to env. This turns out to be ok because loongarch_cpu_reset_hold initializes CRMD properly. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 9 +-------- linux-user/loongarch64/cpu_loop.c | 11 ++++------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index a30431c7a2dcf..0feccfbe9166c 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -533,14 +533,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en #define VDSO_HEADER "vdso.c.inc" -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - /*Set crmd PG,DA = 1,0 */ - regs->csr.crmd = 2 << 3; - regs->csr.era = infop->entry; - regs->regs[3] = infop->start_stack; -} +#define HAVE_INIT_MAIN_THREAD /* See linux kernel: arch/loongarch/include/asm/elf.h */ #define ELF_NREG 45 diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c index ec8a06c88c79e..a0a4cbb7cc312 100644 --- a/linux-user/loongarch64/cpu_loop.c +++ b/linux-user/loongarch64/cpu_loop.c @@ -120,13 +120,10 @@ void cpu_loop(CPULoongArchState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; - - for (i = 0; i < 32; i++) { - env->gpr[i] = regs->regs[i]; - } - env->pc = regs->csr.era; + CPUArchState *env = cpu_env(cs); + env->pc = info->entry; + env->gpr[3] = info->start_stack; } From e17cc00f7195f387720cd839dcb83fd17c2147c9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 13:33:18 -1000 Subject: [PATCH 0337/1794] linux-user/mips: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Note that init_thread had set cp0_status in target_pt_regs, but target_cpu_copy_regs did not copy to env. This turns out to be ok because mips_cpu_reset_hold initializes CP0_Status properly. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 8 +------- linux-user/mips/cpu_loop.c | 16 ++++++---------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0feccfbe9166c..ac96755b06c64 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -581,13 +581,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) #endif -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->cp0_status = 2 << CP0St_KSU; - regs->cp0_epc = infop->entry; - regs->regs[29] = infop->start_stack; -} +#define HAVE_INIT_MAIN_THREAD /* See linux kernel: arch/mips/include/asm/elf.h. */ #define ELF_NREG 45 diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index 6405806eb0267..e67b8a2e46320 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -211,12 +211,9 @@ void cpu_loop(CPUMIPSState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - CPUState *cpu = env_cpu(env); - TaskState *ts = get_task_state(cpu); - struct image_info *info = ts->info; - int i; + CPUArchState *env = cpu_env(cs); struct mode_req { bool single; @@ -245,12 +242,11 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) struct mode_req prog_req; struct mode_req interp_req; + target_ulong entry = info->entry; - for(i = 0; i < 32; i++) { - env->active_tc.gpr[i] = regs->regs[i]; - } - env->active_tc.PC = regs->cp0_epc & ~(target_ulong)1; - if (regs->cp0_epc & 1) { + env->active_tc.gpr[29] = info->start_stack; + env->active_tc.PC = entry & ~(target_ulong)1; + if (entry & 1) { env->hflags |= MIPS_HFLAG_M16; } From 2ffaa3f70dfbf07a3bdb1035fb4ba4e0f7f94de1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 21:44:06 -1000 Subject: [PATCH 0338/1794] linux-user/microblaze: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 8 +------ linux-user/microblaze/cpu_loop.c | 39 ++++---------------------------- 2 files changed, 6 insertions(+), 41 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index ac96755b06c64..3f9ec4935952e 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -640,13 +640,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMIPSState *e #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_MICROBLAZE -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->pc = infop->entry; - regs->r1 = infop->start_stack; - -} +#define HAVE_INIT_MAIN_THREAD #define ELF_EXEC_PAGESIZE 4096 diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c index 87236c166f2b7..d8277961c7368 100644 --- a/linux-user/microblaze/cpu_loop.c +++ b/linux-user/microblaze/cpu_loop.c @@ -127,39 +127,10 @@ void cpu_loop(CPUMBState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - env->regs[0] = regs->r0; - env->regs[1] = regs->r1; - env->regs[2] = regs->r2; - env->regs[3] = regs->r3; - env->regs[4] = regs->r4; - env->regs[5] = regs->r5; - env->regs[6] = regs->r6; - env->regs[7] = regs->r7; - env->regs[8] = regs->r8; - env->regs[9] = regs->r9; - env->regs[10] = regs->r10; - env->regs[11] = regs->r11; - env->regs[12] = regs->r12; - env->regs[13] = regs->r13; - env->regs[14] = regs->r14; - env->regs[15] = regs->r15; - env->regs[16] = regs->r16; - env->regs[17] = regs->r17; - env->regs[18] = regs->r18; - env->regs[19] = regs->r19; - env->regs[20] = regs->r20; - env->regs[21] = regs->r21; - env->regs[22] = regs->r22; - env->regs[23] = regs->r23; - env->regs[24] = regs->r24; - env->regs[25] = regs->r25; - env->regs[26] = regs->r26; - env->regs[27] = regs->r27; - env->regs[28] = regs->r28; - env->regs[29] = regs->r29; - env->regs[30] = regs->r30; - env->regs[31] = regs->r31; - env->pc = regs->pc; + CPUArchState *env = cpu_env(cs); + + env->pc = info->entry; + env->regs[1] = info->start_stack; } From 83411d840cfd80d7288ea096d905413754a87c66 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 21:48:48 -1000 Subject: [PATCH 0339/1794] linux-user/openrisc: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 +------ linux-user/openrisc/cpu_loop.c | 11 ++++------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 3f9ec4935952e..03c95397744ed 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -673,12 +673,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMBState *env #define ELF_CLASS ELFCLASS32 #define ELF_DATA ELFDATA2MSB -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->pc = infop->entry; - regs->gpr[1] = infop->start_stack; -} +#define HAVE_INIT_MAIN_THREAD #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 diff --git a/linux-user/openrisc/cpu_loop.c b/linux-user/openrisc/cpu_loop.c index 306b4f8eb4345..8c72347a99a43 100644 --- a/linux-user/openrisc/cpu_loop.c +++ b/linux-user/openrisc/cpu_loop.c @@ -83,13 +83,10 @@ void cpu_loop(CPUOpenRISCState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; + CPUArchState *env = cpu_env(cs); - for (i = 0; i < 32; i++) { - cpu_set_gpr(env, i, regs->gpr[i]); - } - env->pc = regs->pc; - cpu_set_sr(env, regs->sr); + env->pc = info->entry; + cpu_set_gpr(env, 1, info->start_stack); } From 011480ff599d76e62a0e49ddd21ba9023cdb0ec3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 21:51:02 -1000 Subject: [PATCH 0340/1794] linux-user/sh4: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 8 +------- linux-user/sh4/cpu_loop.c | 10 ++++------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 03c95397744ed..8604308a310a7 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -701,13 +701,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_SH -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - /* Check other registers XXXXX */ - regs->pc = infop->entry; - regs->regs[15] = infop->start_stack; -} +#define HAVE_INIT_MAIN_THREAD /* See linux kernel: arch/sh/include/asm/elf.h. */ #define ELF_NREG 23 diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c index ee9eff3428a92..259ea1cc8bb47 100644 --- a/linux-user/sh4/cpu_loop.c +++ b/linux-user/sh4/cpu_loop.c @@ -81,12 +81,10 @@ void cpu_loop(CPUSH4State *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; + CPUArchState *env = cpu_env(cs); - for(i = 0; i < 16; i++) { - env->gregs[i] = regs->regs[i]; - } - env->pc = regs->pc; + env->pc = info->entry; + env->gregs[15] = info->start_stack; } From ff053f9c5a4df1f0fb49ab7d7a0886a43f18a4f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 21:53:30 -1000 Subject: [PATCH 0341/1794] linux-user/m68k: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 11 +---------- linux-user/m68k/cpu_loop.c | 25 ++++++------------------- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8604308a310a7..46150586aff29 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -746,16 +746,7 @@ static inline void elf_core_copy_regs(target_elf_gregset_t *regs, #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_68K -/* ??? Does this need to do anything? - #define ELF_PLAT_INIT(_r) */ - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->usp = infop->start_stack; - regs->sr = 0; - regs->pc = infop->entry; -} +#define HAVE_INIT_MAIN_THREAD /* See linux kernel: arch/m68k/include/asm/elf.h. */ #define ELF_NREG 20 diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index 23693f33582ef..aca0bf23dc66e 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -92,24 +92,11 @@ void cpu_loop(CPUM68KState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - env->pc = regs->pc; - env->dregs[0] = regs->d0; - env->dregs[1] = regs->d1; - env->dregs[2] = regs->d2; - env->dregs[3] = regs->d3; - env->dregs[4] = regs->d4; - env->dregs[5] = regs->d5; - env->dregs[6] = regs->d6; - env->dregs[7] = regs->d7; - env->aregs[0] = regs->a0; - env->aregs[1] = regs->a1; - env->aregs[2] = regs->a2; - env->aregs[3] = regs->a3; - env->aregs[4] = regs->a4; - env->aregs[5] = regs->a5; - env->aregs[6] = regs->a6; - env->aregs[7] = regs->usp; - env->sr = regs->sr; + CPUArchState *env = cpu_env(cs); + + env->pc = info->entry; + env->aregs[7] = info->start_stack; + env->sr = 0; } From 6b523112baac10747446af9074d1281974767004 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 21:59:47 -1000 Subject: [PATCH 0342/1794] linux-user/alpha: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Note that init_thread had set ps in target_pt_regs, but target_cpu_copy_regs did not copy to env. This turns out to be ok because alpha_cpu_initfn initializes flags properly. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/alpha/cpu_loop.c | 11 ++++------- linux-user/elfload.c | 8 +------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c index 80ad536c5f235..728b64906d945 100644 --- a/linux-user/alpha/cpu_loop.c +++ b/linux-user/alpha/cpu_loop.c @@ -173,13 +173,10 @@ void cpu_loop(CPUAlphaState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; + CPUArchState *env = cpu_env(cs); - for(i = 0; i < 28; i++) { - env->ir[i] = ((abi_ulong *)regs)[i]; - } - env->ir[IR_SP] = regs->usp; - env->pc = regs->pc; + env->pc = info->entry; + env->ir[IR_SP] = info->start_stack; } diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 46150586aff29..a7de852d4dd05 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -786,13 +786,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUM68KState *e #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_ALPHA -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->pc = infop->entry; - regs->ps = 8; - regs->usp = infop->start_stack; -} +#define HAVE_INIT_MAIN_THREAD #define ELF_EXEC_PAGESIZE 8192 From 592f36d13dd32d0a90f71266931a68f522617026 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 22:03:03 -1000 Subject: [PATCH 0343/1794] linux-user/s390x: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 9 +-------- linux-user/s390x/cpu_loop.c | 15 ++++++++------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index a7de852d4dd05..16aa09214e8c0 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -798,14 +798,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUM68KState *e #define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_S390 -static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) -{ - regs->psw.addr = infop->entry; - regs->psw.mask = PSW_MASK_DAT | PSW_MASK_IO | PSW_MASK_EXT | \ - PSW_MASK_MCHECK | PSW_MASK_PSTATE | PSW_MASK_64 | \ - PSW_MASK_32; - regs->gprs[15] = infop->start_stack; -} +#define HAVE_INIT_MAIN_THREAD /* See linux kernel: arch/s390/include/uapi/asm/ptrace.h (s390_regs). */ #define ELF_NREG 27 diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c index c9124444ed207..49e44548f85a0 100644 --- a/linux-user/s390x/cpu_loop.c +++ b/linux-user/s390x/cpu_loop.c @@ -180,12 +180,13 @@ void cpu_loop(CPUS390XState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; - for (i = 0; i < 16; i++) { - env->regs[i] = regs->gprs[i]; - } - env->psw.mask = regs->psw.mask; - env->psw.addr = regs->psw.addr; + CPUArchState *env = cpu_env(cs); + + env->psw.addr = info->entry; + env->psw.mask = PSW_MASK_DAT | PSW_MASK_IO | PSW_MASK_EXT | + PSW_MASK_MCHECK | PSW_MASK_PSTATE | PSW_MASK_64 | + PSW_MASK_32; + env->regs[15] = info->start_stack; } From a56cf00bc06a11627ec7b695618532d0d58cf8de Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 22:06:12 -1000 Subject: [PATCH 0344/1794] linux-user/riscv: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 +------ linux-user/riscv/cpu_loop.c | 10 ++++------ 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 16aa09214e8c0..556f11d720dc1 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -849,12 +849,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define VDSO_HEADER "vdso-64.c.inc" #endif -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->sepc = infop->entry; - regs->sp = infop->start_stack; -} +#define HAVE_INIT_MAIN_THREAD #define ELF_EXEC_PAGESIZE 4096 diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index 2dd30c7b28886..b3162815320b9 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -94,14 +94,12 @@ void cpu_loop(CPURISCVState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - CPUState *cpu = env_cpu(env); - TaskState *ts = get_task_state(cpu); - struct image_info *info = ts->info; + CPUArchState *env = cpu_env(cs); - env->pc = regs->sepc; - env->gpr[xSP] = regs->sp; + env->pc = info->entry; + env->gpr[xSP] = info->start_stack; env->elf_flags = info->elf_flags; if ((env->misa_ext & RVE) && !(env->elf_flags & EF_RISCV_RVE)) { From 1aec088719c5646b3117fd3b9c4507830f99f432 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 22:09:19 -1000 Subject: [PATCH 0345/1794] linux-user/hppa: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 13 +------------ linux-user/hppa/cpu_loop.c | 18 +++++++++++------- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 556f11d720dc1..4876e4b0a80d3 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -864,18 +864,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define VDSO_HEADER "vdso.c.inc" -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->iaoq[0] = infop->entry | PRIV_USER; - regs->iaoq[1] = regs->iaoq[0] + 4; - regs->gr[23] = 0; - regs->gr[24] = infop->argv; - regs->gr[25] = infop->argc; - /* The top-of-stack contains a linkage buffer. */ - regs->gr[30] = infop->start_stack + 64; - regs->gr[31] = infop->entry; -} +#define HAVE_INIT_MAIN_THREAD #define LO_COMMPAGE 0 diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c index 9abaad5ef81d0..3af50653bb775 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -196,12 +196,16 @@ void cpu_loop(CPUHPPAState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; - for (i = 1; i < 32; i++) { - env->gr[i] = regs->gr[i]; - } - env->iaoq_f = regs->iaoq[0]; - env->iaoq_b = regs->iaoq[1]; + CPUArchState *env = cpu_env(cs); + + env->iaoq_f = info->entry | PRIV_USER; + env->iaoq_b = env->iaoq_f + 4; + env->gr[23] = 0; + env->gr[24] = info->argv; + env->gr[25] = info->argc; + /* The top-of-stack contains a linkage buffer. */ + env->gr[30] = info->start_stack + 64; + env->gr[31] = info->entry; } From ceff7f9ae996ec9bb78d463f054241c338dc979c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 22:16:22 -1000 Subject: [PATCH 0346/1794] linux-user/xtensa: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 18 +----------------- linux-user/xtensa/cpu_loop.c | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 4876e4b0a80d3..447a9be11d7f9 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -905,23 +905,7 @@ static bool init_guest_commpage(void) #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_XTENSA -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->windowbase = 0; - regs->windowstart = 1; - regs->areg[1] = infop->start_stack; - regs->pc = infop->entry; - if (info_is_fdpic(infop)) { - regs->areg[4] = infop->loadmap_addr; - regs->areg[5] = infop->interpreter_loadmap_addr; - if (infop->interpreter_loadmap_addr) { - regs->areg[6] = infop->interpreter_pt_dynamic_addr; - } else { - regs->areg[6] = infop->pt_dynamic_addr; - } - } -} +#define HAVE_INIT_MAIN_THREAD /* See linux kernel: arch/xtensa/include/asm/elf.h. */ #define ELF_NREG 128 diff --git a/linux-user/xtensa/cpu_loop.c b/linux-user/xtensa/cpu_loop.c index c0fcf743e7013..43a194fc4a4ad 100644 --- a/linux-user/xtensa/cpu_loop.c +++ b/linux-user/xtensa/cpu_loop.c @@ -238,12 +238,22 @@ void cpu_loop(CPUXtensaState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; - for (i = 0; i < 16; ++i) { - env->regs[i] = regs->areg[i]; + CPUArchState *env = cpu_env(cs); + + env->sregs[WINDOW_BASE] = 0; + env->sregs[WINDOW_START] = 1; + env->regs[1] = info->start_stack; + env->pc = info->entry; + + if (info_is_fdpic(info)) { + env->regs[4] = info->loadmap_addr; + env->regs[5] = info->interpreter_loadmap_addr; + if (info->interpreter_loadmap_addr) { + env->regs[6] = info->interpreter_pt_dynamic_addr; + } else { + env->regs[6] = info->pt_dynamic_addr; + } } - env->sregs[WINDOW_START] = regs->windowstart; - env->pc = regs->pc; } From f61c88c571e5e68cdf1d935d7524493a8f404556 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 22:19:49 -1000 Subject: [PATCH 0347/1794] linux-user/hexagon: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 +------ linux-user/hexagon/cpu_loop.c | 8 +++++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 447a9be11d7f9..4417c2d99ad9c 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -954,12 +954,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_HEXAGON -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->sepc = infop->entry; - regs->sp = infop->start_stack; -} +#define HAVE_INIT_MAIN_THREAD #endif /* TARGET_HEXAGON */ diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index e18a0183b5016..25c97edcaef0c 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -79,9 +79,11 @@ void cpu_loop(CPUHexagonState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - env->gpr[HEX_REG_PC] = regs->sepc; - env->gpr[HEX_REG_SP] = regs->sp; + CPUArchState *env = cpu_env(cs); + + env->gpr[HEX_REG_PC] = info->entry; + env->gpr[HEX_REG_SP] = info->start_stack; env->gpr[HEX_REG_USR] = 0x56000; } From e191623fb0694a08c372fb68c5634969addbcb7b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 22:24:19 -1000 Subject: [PATCH 0348/1794] linux-user: Remove do_init_main_thread All targets have been converted, so we can call init_main_thread directly. Remove do_init_main_thread and HAVE_INIT_MAIN_THREAD. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 46 -------------------------------------------- linux-user/loader.h | 1 - linux-user/main.c | 2 +- linux-user/qemu.h | 2 -- 4 files changed, 1 insertion(+), 50 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 4417c2d99ad9c..fce4c05674398 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -149,8 +149,6 @@ typedef abi_int target_pid_t; #ifdef TARGET_I386 -#define HAVE_INIT_MAIN_THREAD - #ifdef TARGET_X86_64 #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 @@ -293,8 +291,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en #define ELF_CLASS ELFCLASS32 #define EXSTACK_DEFAULT true -#define HAVE_INIT_MAIN_THREAD - #define ELF_NREG 18 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -395,8 +391,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_ARCH EM_AARCH64 #define ELF_CLASS ELFCLASS64 -#define HAVE_INIT_MAIN_THREAD - #define ELF_NREG 34 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -438,8 +432,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, # define ELF_ARCH EM_SPARCV9 #endif -#define HAVE_INIT_MAIN_THREAD - #endif /* TARGET_SPARC */ #ifdef TARGET_PPC @@ -485,8 +477,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ } while (0) -#define HAVE_INIT_MAIN_THREAD - /* See linux kernel: arch/powerpc/include/asm/elf.h. */ #define ELF_NREG 48 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -533,8 +523,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en #define VDSO_HEADER "vdso.c.inc" -#define HAVE_INIT_MAIN_THREAD - /* See linux kernel: arch/loongarch/include/asm/elf.h */ #define ELF_NREG 45 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -581,8 +569,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) #endif -#define HAVE_INIT_MAIN_THREAD - /* See linux kernel: arch/mips/include/asm/elf.h. */ #define ELF_NREG 45 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -640,8 +626,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMIPSState *e #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_MICROBLAZE -#define HAVE_INIT_MAIN_THREAD - #define ELF_EXEC_PAGESIZE 4096 #define USE_ELF_CORE_DUMP @@ -673,8 +657,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMBState *env #define ELF_CLASS ELFCLASS32 #define ELF_DATA ELFDATA2MSB -#define HAVE_INIT_MAIN_THREAD - #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 @@ -701,8 +683,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_SH -#define HAVE_INIT_MAIN_THREAD - /* See linux kernel: arch/sh/include/asm/elf.h. */ #define ELF_NREG 23 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -746,8 +726,6 @@ static inline void elf_core_copy_regs(target_elf_gregset_t *regs, #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_68K -#define HAVE_INIT_MAIN_THREAD - /* See linux kernel: arch/m68k/include/asm/elf.h. */ #define ELF_NREG 20 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -786,8 +764,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUM68KState *e #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_ALPHA -#define HAVE_INIT_MAIN_THREAD - #define ELF_EXEC_PAGESIZE 8192 #endif /* TARGET_ALPHA */ @@ -798,8 +774,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUM68KState *e #define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_S390 -#define HAVE_INIT_MAIN_THREAD - /* See linux kernel: arch/s390/include/uapi/asm/ptrace.h (s390_regs). */ #define ELF_NREG 27 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -849,8 +823,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define VDSO_HEADER "vdso-64.c.inc" #endif -#define HAVE_INIT_MAIN_THREAD - #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_RISCV */ @@ -864,8 +836,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define VDSO_HEADER "vdso.c.inc" -#define HAVE_INIT_MAIN_THREAD - #define LO_COMMPAGE 0 static bool init_guest_commpage(void) @@ -905,8 +875,6 @@ static bool init_guest_commpage(void) #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_XTENSA -#define HAVE_INIT_MAIN_THREAD - /* See linux kernel: arch/xtensa/include/asm/elf.h. */ #define ELF_NREG 128 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -954,8 +922,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_HEXAGON -#define HAVE_INIT_MAIN_THREAD - #endif /* TARGET_HEXAGON */ #ifndef ELF_MACHINE @@ -3438,15 +3404,3 @@ static int elf_core_dump(int signr, const CPUArchState *env) return ret; } #endif /* USE_ELF_CORE_DUMP */ - -void do_init_main_thread(CPUState *cs, struct image_info *infop) -{ -#ifdef HAVE_INIT_MAIN_THREAD - init_main_thread(cs, infop); -#else - target_pt_regs regs = { }; - - init_thread(®s, infop); - target_cpu_copy_regs(cpu_env(cs), ®s); -#endif -} diff --git a/linux-user/loader.h b/linux-user/loader.h index e0291cc3b0f58..6482c7c90ca4f 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -82,7 +82,6 @@ struct linux_binprm { int (*core_dump)(int, const CPUArchState *); /* coredump routine */ }; -void do_init_main_thread(CPUState *cs, struct image_info *infop); abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, abi_ulong stringp, int push_ptr); int loader_exec(int fdexec, const char *filename, char **argv, char **envp, diff --git a/linux-user/main.c b/linux-user/main.c index e21842bde904d..6edeeecef3858 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1037,7 +1037,7 @@ int main(int argc, char **argv, char **envp) the real value of GUEST_BASE into account. */ tcg_prologue_init(); - do_init_main_thread(cpu, info); + init_main_thread(cpu, info); if (gdbstub) { gdbserver_start(gdbstub, &error_fatal); diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 8a9500d4f45e3..e4dca0c20f082 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -359,8 +359,6 @@ void *lock_user_string(abi_ulong guest_addr); /* Clone cpu state */ CPUArchState *cpu_copy(CPUArchState *env); -typedef struct target_pt_regs target_pt_regs; -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs); void init_main_thread(CPUState *cs, struct image_info *info); #endif /* QEMU_H */ From f91563d011a0439cd6709e169cdfac268779d562 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 26 Aug 2025 15:33:40 +0930 Subject: [PATCH 0349/1794] linux-user: Add strace for rseq build/qemu-riscv64 -cpu rv64,v=on -d strace build/tests/tcg/riscv64-linux-user/test-vstart-overflow 1118081 riscv_hwprobe(0xffffbc038200,1,0,0,0,0) = 0 1118081 brk(NULL) = 0x0000000000085000 1118081 brk(0x0000000000085b00) = 0x0000000000085b00 1118081 set_tid_address(0x850f0) = 1118081 1118081 set_robust_list(0x85100,24) = -1 errno=38 (Function not implemented) 1118081 rseq(0x857c0,32,0,0xf1401073) = -1 errno=38 (Function not implemented) Signed-off-by: Joel Stanley Signed-off-by: Richard Henderson Reviewed-by: Richard Henderson Message-ID: <20250826060341.1118670-1-joel@jms.id.au> --- linux-user/strace.list | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux-user/strace.list b/linux-user/strace.list index ab818352a90cd..51b5ead9696c9 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -1719,3 +1719,6 @@ #ifdef TARGET_NR_riscv_hwprobe { TARGET_NR_riscv_hwprobe, "riscv_hwprobe" , "%s(%p,%d,%d,%d,%d,%d)", NULL, NULL }, #endif +#ifdef TARGET_NR_rseq +{ TARGET_NR_rseq, "rseq" , "%s(%p,%u,%d,%#x)", NULL, NULL }, +#endif From a5fbf1c617c5b51082d317601e0d4cf5eea5c140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Stelmach?= Date: Wed, 27 Aug 2025 11:54:12 +0200 Subject: [PATCH 0350/1794] linux-user: do not print IP socket options by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IP protocols should not be printed unless the socket is an IPv4 or IPv6 one. Current arrangement erroneously prints IPPROTO_IP for Unix domain sockets. Signed-off-by: Łukasz Stelmach Signed-off-by: Richard Henderson Reviewed-by: Richard Henderson Message-ID: <20250827095412.2348821-1-l.stelmach@samsung.com> --- linux-user/strace.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/linux-user/strace.c b/linux-user/strace.c index 3b744ccd4a721..786354627aa4e 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -586,23 +586,27 @@ print_socket_protocol(int domain, int type, int protocol) return; } - switch (protocol) { - case IPPROTO_IP: - qemu_log("IPPROTO_IP"); - break; - case IPPROTO_TCP: - qemu_log("IPPROTO_TCP"); - break; - case IPPROTO_UDP: - qemu_log("IPPROTO_UDP"); - break; - case IPPROTO_RAW: - qemu_log("IPPROTO_RAW"); - break; - default: - qemu_log("%d", protocol); - break; + if (domain == AF_INET || domain == AF_INET6) { + switch (protocol) { + case IPPROTO_IP: + qemu_log("IPPROTO_IP"); + break; + case IPPROTO_TCP: + qemu_log("IPPROTO_TCP"); + break; + case IPPROTO_UDP: + qemu_log("IPPROTO_UDP"); + break; + case IPPROTO_RAW: + qemu_log("IPPROTO_RAW"); + break; + default: + qemu_log("%d", protocol); + break; + } + return; } + qemu_log("%d", protocol); } From 96e7448c1f820c56caea8447c01f5227b0c95c79 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Fri, 25 Jul 2025 11:12:32 +0800 Subject: [PATCH 0351/1794] target/loongarch: Guard 64-bit-only insn translation with TRANS64 macro This patch replaces uses of the generic TRANS macro with TRANS64 for instructions that are only valid when 64-bit support is available. This improves correctness and avoids potential assertion failures or undefined behavior during translation on 32-bit-only configurations. Signed-off-by: WANG Rui Reviewed-by: Bibo Mao Reviewed-by: Song Gao Signed-off-by: Song Gao --- .../tcg/insn_trans/trans_atomic.c.inc | 36 +++++++++---------- .../tcg/insn_trans/trans_extra.c.inc | 8 +++-- .../tcg/insn_trans/trans_farith.c.inc | 8 ++--- .../loongarch/tcg/insn_trans/trans_fcnv.c.inc | 4 +-- .../tcg/insn_trans/trans_fmemory.c.inc | 16 ++++----- .../tcg/insn_trans/trans_privileged.c.inc | 4 +-- .../tcg/insn_trans/trans_shift.c.inc | 4 +-- target/loongarch/translate.h | 4 +++ 8 files changed, 46 insertions(+), 38 deletions(-) diff --git a/target/loongarch/tcg/insn_trans/trans_atomic.c.inc b/target/loongarch/tcg/insn_trans/trans_atomic.c.inc index 3d70d75941715..77eeedbc42b17 100644 --- a/target/loongarch/tcg/insn_trans/trans_atomic.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_atomic.c.inc @@ -74,38 +74,38 @@ TRANS(sc_w, ALL, gen_sc, MO_TESL) TRANS(ll_d, 64, gen_ll, MO_TEUQ) TRANS(sc_d, 64, gen_sc, MO_TEUQ) TRANS(amswap_w, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TESL) -TRANS(amswap_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) +TRANS64(amswap_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) TRANS(amadd_w, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TESL) -TRANS(amadd_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) +TRANS64(amadd_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) TRANS(amand_w, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TESL) -TRANS(amand_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) +TRANS64(amand_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) TRANS(amor_w, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TESL) -TRANS(amor_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) +TRANS64(amor_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) TRANS(amxor_w, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TESL) -TRANS(amxor_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) +TRANS64(amxor_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) TRANS(ammax_w, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TESL) -TRANS(ammax_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) +TRANS64(ammax_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) TRANS(ammin_w, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TESL) -TRANS(ammin_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) +TRANS64(ammin_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) TRANS(ammax_wu, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TESL) -TRANS(ammax_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) +TRANS64(ammax_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) TRANS(ammin_wu, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TESL) -TRANS(ammin_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) +TRANS64(ammin_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) TRANS(amswap_db_w, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TESL) -TRANS(amswap_db_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) +TRANS64(amswap_db_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) TRANS(amadd_db_w, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TESL) -TRANS(amadd_db_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) +TRANS64(amadd_db_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) TRANS(amand_db_w, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TESL) -TRANS(amand_db_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) +TRANS64(amand_db_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) TRANS(amor_db_w, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TESL) -TRANS(amor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) +TRANS64(amor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) TRANS(amxor_db_w, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TESL) -TRANS(amxor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) +TRANS64(amxor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) TRANS(ammax_db_w, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TESL) -TRANS(ammax_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) +TRANS64(ammax_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) TRANS(ammin_db_w, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TESL) -TRANS(ammin_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) +TRANS64(ammin_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) TRANS(ammax_db_wu, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TESL) -TRANS(ammax_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) +TRANS64(ammax_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) TRANS(ammin_db_wu, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TESL) -TRANS(ammin_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) +TRANS64(ammin_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) diff --git a/target/loongarch/tcg/insn_trans/trans_extra.c.inc b/target/loongarch/tcg/insn_trans/trans_extra.c.inc index eda3d6e56111b..298a80cff55e0 100644 --- a/target/loongarch/tcg/insn_trans/trans_extra.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_extra.c.inc @@ -69,6 +69,10 @@ static bool trans_rdtimeh_w(DisasContext *ctx, arg_rdtimeh_w *a) static bool trans_rdtime_d(DisasContext *ctx, arg_rdtime_d *a) { + if (!avail_64(ctx)) { + return false; + } + return gen_rdtime(ctx, a, 0, 0); } @@ -100,8 +104,8 @@ static bool gen_crc(DisasContext *ctx, arg_rrr *a, TRANS(crc_w_b_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(1)) TRANS(crc_w_h_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(2)) TRANS(crc_w_w_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(4)) -TRANS(crc_w_d_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(8)) +TRANS64(crc_w_d_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(8)) TRANS(crcc_w_b_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(1)) TRANS(crcc_w_h_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(2)) TRANS(crcc_w_w_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(4)) -TRANS(crcc_w_d_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(8)) +TRANS64(crcc_w_d_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(8)) diff --git a/target/loongarch/tcg/insn_trans/trans_farith.c.inc b/target/loongarch/tcg/insn_trans/trans_farith.c.inc index f4a0dea72701a..ff6cf3448e725 100644 --- a/target/loongarch/tcg/insn_trans/trans_farith.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_farith.c.inc @@ -183,16 +183,16 @@ TRANS(fmaxa_s, FP_SP, gen_fff, gen_helper_fmaxa_s) TRANS(fmaxa_d, FP_DP, gen_fff, gen_helper_fmaxa_d) TRANS(fmina_s, FP_SP, gen_fff, gen_helper_fmina_s) TRANS(fmina_d, FP_DP, gen_fff, gen_helper_fmina_d) -TRANS(fscaleb_s, FP_SP, gen_fff, gen_helper_fscaleb_s) -TRANS(fscaleb_d, FP_DP, gen_fff, gen_helper_fscaleb_d) +TRANS64(fscaleb_s, FP_SP, gen_fff, gen_helper_fscaleb_s) +TRANS64(fscaleb_d, FP_DP, gen_fff, gen_helper_fscaleb_d) TRANS(fsqrt_s, FP_SP, gen_ff, gen_helper_fsqrt_s) TRANS(fsqrt_d, FP_DP, gen_ff, gen_helper_fsqrt_d) TRANS(frecip_s, FP_SP, gen_ff, gen_helper_frecip_s) TRANS(frecip_d, FP_DP, gen_ff, gen_helper_frecip_d) TRANS(frsqrt_s, FP_SP, gen_ff, gen_helper_frsqrt_s) TRANS(frsqrt_d, FP_DP, gen_ff, gen_helper_frsqrt_d) -TRANS(flogb_s, FP_SP, gen_ff, gen_helper_flogb_s) -TRANS(flogb_d, FP_DP, gen_ff, gen_helper_flogb_d) +TRANS64(flogb_s, FP_SP, gen_ff, gen_helper_flogb_s) +TRANS64(flogb_d, FP_DP, gen_ff, gen_helper_flogb_d) TRANS(fclass_s, FP_SP, gen_ff, gen_helper_fclass_s) TRANS(fclass_d, FP_DP, gen_ff, gen_helper_fclass_d) TRANS(fmadd_s, FP_SP, gen_muladd, gen_helper_fmuladd_s, 0) diff --git a/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc b/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc index 833c059d6da31..ca1d76a3661c1 100644 --- a/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc @@ -29,5 +29,5 @@ TRANS(ffint_s_w, FP_SP, gen_ff, gen_helper_ffint_s_w) TRANS(ffint_s_l, FP_SP, gen_ff, gen_helper_ffint_s_l) TRANS(ffint_d_w, FP_DP, gen_ff, gen_helper_ffint_d_w) TRANS(ffint_d_l, FP_DP, gen_ff, gen_helper_ffint_d_l) -TRANS(frint_s, FP_SP, gen_ff, gen_helper_frint_s) -TRANS(frint_d, FP_DP, gen_ff, gen_helper_frint_d) +TRANS64(frint_s, FP_SP, gen_ff, gen_helper_frint_s) +TRANS64(frint_d, FP_DP, gen_ff, gen_helper_frint_d) diff --git a/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc b/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc index 13452bc7e56ae..79da4718a5606 100644 --- a/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc @@ -148,11 +148,11 @@ TRANS(fldx_s, FP_SP, gen_floadx, MO_TEUL) TRANS(fldx_d, FP_DP, gen_floadx, MO_TEUQ) TRANS(fstx_s, FP_SP, gen_fstorex, MO_TEUL) TRANS(fstx_d, FP_DP, gen_fstorex, MO_TEUQ) -TRANS(fldgt_s, FP_SP, gen_fload_gt, MO_TEUL) -TRANS(fldgt_d, FP_DP, gen_fload_gt, MO_TEUQ) -TRANS(fldle_s, FP_SP, gen_fload_le, MO_TEUL) -TRANS(fldle_d, FP_DP, gen_fload_le, MO_TEUQ) -TRANS(fstgt_s, FP_SP, gen_fstore_gt, MO_TEUL) -TRANS(fstgt_d, FP_DP, gen_fstore_gt, MO_TEUQ) -TRANS(fstle_s, FP_SP, gen_fstore_le, MO_TEUL) -TRANS(fstle_d, FP_DP, gen_fstore_le, MO_TEUQ) +TRANS64(fldgt_s, FP_SP, gen_fload_gt, MO_TEUL) +TRANS64(fldgt_d, FP_DP, gen_fload_gt, MO_TEUQ) +TRANS64(fldle_s, FP_SP, gen_fload_le, MO_TEUL) +TRANS64(fldle_d, FP_DP, gen_fload_le, MO_TEUQ) +TRANS64(fstgt_s, FP_SP, gen_fstore_gt, MO_TEUL) +TRANS64(fstgt_d, FP_DP, gen_fstore_gt, MO_TEUQ) +TRANS64(fstle_s, FP_SP, gen_fstore_le, MO_TEUL) +TRANS64(fstle_d, FP_DP, gen_fstore_le, MO_TEUQ) diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc index ecbfe23b6362b..34cfab8879072 100644 --- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc @@ -233,11 +233,11 @@ static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a, TRANS(iocsrrd_b, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_b) TRANS(iocsrrd_h, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_h) TRANS(iocsrrd_w, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_w) -TRANS(iocsrrd_d, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_d) +TRANS64(iocsrrd_d, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_d) TRANS(iocsrwr_b, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_b) TRANS(iocsrwr_h, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_h) TRANS(iocsrwr_w, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_w) -TRANS(iocsrwr_d, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_d) +TRANS64(iocsrwr_d, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_d) static void check_mmu_idx(DisasContext *ctx) { diff --git a/target/loongarch/tcg/insn_trans/trans_shift.c.inc b/target/loongarch/tcg/insn_trans/trans_shift.c.inc index 377307785aab4..136c4c845527f 100644 --- a/target/loongarch/tcg/insn_trans/trans_shift.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_shift.c.inc @@ -78,7 +78,7 @@ TRANS(sra_w, ALL, gen_rrr, EXT_SIGN, EXT_NONE, EXT_SIGN, gen_sra_w) TRANS(sll_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sll_d) TRANS(srl_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_srl_d) TRANS(sra_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sra_d) -TRANS(rotr_w, 64, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_rotr_w) +TRANS(rotr_w, ALL, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_rotr_w) TRANS(rotr_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rotr_d) TRANS(slli_w, ALL, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_shli_tl) TRANS(slli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shli_tl) @@ -86,5 +86,5 @@ TRANS(srli_w, ALL, gen_rri_c, EXT_ZERO, EXT_SIGN, tcg_gen_shri_tl) TRANS(srli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shri_tl) TRANS(srai_w, ALL, gen_rri_c, EXT_NONE, EXT_NONE, gen_sari_w) TRANS(srai_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_sari_tl) -TRANS(rotri_w, 64, gen_rri_v, EXT_NONE, EXT_NONE, gen_rotr_w) +TRANS(rotri_w, ALL, gen_rri_v, EXT_NONE, EXT_NONE, gen_rotr_w) TRANS(rotri_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_rotri_tl) diff --git a/target/loongarch/translate.h b/target/loongarch/translate.h index 018dc5eb171bb..bbe015ba579f5 100644 --- a/target/loongarch/translate.h +++ b/target/loongarch/translate.h @@ -14,6 +14,10 @@ static bool trans_##NAME(DisasContext *ctx, arg_##NAME * a) \ { return avail_##AVAIL(ctx) && FUNC(ctx, a, __VA_ARGS__); } +#define TRANS64(NAME, AVAIL, FUNC, ...) \ + static bool trans_##NAME(DisasContext *ctx, arg_##NAME * a) \ + { return avail_64(ctx) && avail_##AVAIL(ctx) && FUNC(ctx, a, __VA_ARGS__); } + #define avail_ALL(C) true #define avail_64(C) (FIELD_EX32((C)->cpucfg1, CPUCFG1, ARCH) == \ CPUCFG1_ARCH_LA64) From 86bca40402316891b8b9a920c2e3bf8cf37ba9a4 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 1 Aug 2025 08:01:52 +0200 Subject: [PATCH 0352/1794] hw/intc/loongarch_pch_pic: Fix ubsan warning and endianness issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When booting the Linux kernel from tests/functional/test_loongarch64_virt.py with a QEMU that has been compiled with --enable-ubsan, there is a warning like this: .../hw/intc/loongarch_pch_pic.c:171:46: runtime error: index 512 out of bounds for type 'uint8_t[64]' (aka 'unsigned char[64]') SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior .../hw/intc/loongarch_pch_pic.c:171:46 .../hw/intc/loongarch_pch_pic.c:175:45: runtime error: index 256 out of bounds for type 'uint8_t[64]' (aka 'unsigned char[64]') SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior .../hw/intc/loongarch_pch_pic.c:175:45 It happens because "addr" is added first before substracting the base (PCH_PIC_HTMSI_VEC or PCH_PIC_ROUTE_ENTRY). Additionally, this code looks like it is not endianness safe, since it uses a 64-bit pointer to write values into an array of 8-bit values. Thus rework the code to use the stq_le_p / ldq_le_p helpers here and make sure that we do not create pointers with undefined behavior by accident. Signed-off-by: Thomas Huth Reviewed-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Tested-by: Song Gao Signed-off-by: Song Gao --- hw/intc/loongarch_pch_pic.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index c4b242dbf4169..32f01aabf0ed5 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -110,10 +110,10 @@ static uint64_t pch_pic_read(void *opaque, hwaddr addr, uint64_t field_mask) val = s->int_polarity; break; case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END: - val = *(uint64_t *)(s->htmsi_vector + addr - PCH_PIC_HTMSI_VEC); + val = ldq_le_p(&s->htmsi_vector[addr - PCH_PIC_HTMSI_VEC]); break; case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END: - val = *(uint64_t *)(s->route_entry + addr - PCH_PIC_ROUTE_ENTRY); + val = ldq_le_p(&s->route_entry[addr - PCH_PIC_ROUTE_ENTRY]); break; default: qemu_log_mask(LOG_GUEST_ERROR, @@ -129,7 +129,8 @@ static void pch_pic_write(void *opaque, hwaddr addr, uint64_t value, { LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); uint32_t offset; - uint64_t old, mask, data, *ptemp; + uint64_t old, mask, data; + void *ptemp; offset = addr & 7; addr -= offset; @@ -168,12 +169,12 @@ static void pch_pic_write(void *opaque, hwaddr addr, uint64_t value, s->int_polarity = (s->int_polarity & ~mask) | data; break; case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END: - ptemp = (uint64_t *)(s->htmsi_vector + addr - PCH_PIC_HTMSI_VEC); - *ptemp = (*ptemp & ~mask) | data; + ptemp = &s->htmsi_vector[addr - PCH_PIC_HTMSI_VEC]; + stq_le_p(ptemp, (ldq_le_p(ptemp) & ~mask) | data); break; case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END: - ptemp = (uint64_t *)(s->route_entry + addr - PCH_PIC_ROUTE_ENTRY); - *ptemp = (*ptemp & ~mask) | data; + ptemp = (uint64_t *)&s->route_entry[addr - PCH_PIC_ROUTE_ENTRY]; + stq_le_p(ptemp, (ldq_le_p(ptemp) & ~mask) | data); break; default: qemu_log_mask(LOG_GUEST_ERROR, From de3c9f552d8d6af5e1a5f28eb93c836080715796 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 8 Jul 2025 15:22:37 +0800 Subject: [PATCH 0353/1794] target/loongarch: Move some function definition to kvm directory Move function definition specified with kvm to the corresponding directory. Also remove header file "cpu.h" including outside of macro QEMU_KVM_LOONGARCH_H. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- hw/loongarch/virt.c | 1 + target/loongarch/cpu.h | 9 --------- target/loongarch/kvm/kvm_loongarch.h | 4 ++-- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index b15ada20787ba..31215b778543c 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -46,6 +46,7 @@ #include "hw/block/flash.h" #include "hw/virtio/virtio-iommu.h" #include "qemu/error-report.h" +#include "kvm/kvm_loongarch.h" static void virt_get_veiointc(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 9538e8d61d75a..bbe6db33f1c37 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -496,13 +496,4 @@ static inline void set_pc(CPULoongArchState *env, uint64_t value) void loongarch_cpu_post_init(Object *obj); -#ifdef CONFIG_KVM -void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu); -#else -static inline void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu) -{ -} -#endif -void kvm_loongarch_init_irq_routing(void); - #endif /* LOONGARCH_CPU_H */ diff --git a/target/loongarch/kvm/kvm_loongarch.h b/target/loongarch/kvm/kvm_loongarch.h index 1051a341ec263..51475675d6f6b 100644 --- a/target/loongarch/kvm/kvm_loongarch.h +++ b/target/loongarch/kvm/kvm_loongarch.h @@ -5,11 +5,11 @@ * Copyright (c) 2023 Loongson Technology Corporation Limited */ -#include "cpu.h" - #ifndef QEMU_KVM_LOONGARCH_H #define QEMU_KVM_LOONGARCH_H +void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu); +void kvm_loongarch_init_irq_routing(void); int kvm_loongarch_set_interrupt(LoongArchCPU *cpu, int irq, int level); void kvm_arch_reset_vcpu(CPUState *cs); From 982d7674ff4ed1affc4dadba2c6f6ab1b1df4e97 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 8 Jul 2025 15:35:12 +0800 Subject: [PATCH 0354/1794] target/loongarch: Define function loongarch_cpu_post_init as static Function loongarch_cpu_post_init() is implemented and used in the same file target/loongarch/cpu.c, it can be defined as static function. This patch moves implementation about function loongarch_cpu_post_init() before it is referenced. And it is only code movement, no function change. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu.c | 180 ++++++++++++++++++++--------------------- target/loongarch/cpu.h | 2 - 2 files changed, 90 insertions(+), 92 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index abad84c054707..b96429ffb1926 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -422,6 +422,96 @@ static void loongarch_la464_init_csr(Object *obj) #endif } +static bool loongarch_get_lsx(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->lsx != ON_OFF_AUTO_OFF; +} + +static void loongarch_set_lsx(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + uint32_t val; + + cpu->lsx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + if (cpu->lsx == ON_OFF_AUTO_OFF) { + cpu->lasx = ON_OFF_AUTO_OFF; + if (cpu->lasx == ON_OFF_AUTO_ON) { + error_setg(errp, "Failed to disable LSX since LASX is enabled"); + return; + } + } + + if (kvm_enabled()) { + /* kvm feature detection in function kvm_arch_init_vcpu */ + return; + } + + /* LSX feature detection in TCG mode */ + val = cpu->env.cpucfg[2]; + if (cpu->lsx == ON_OFF_AUTO_ON) { + if (FIELD_EX32(val, CPUCFG2, LSX) == 0) { + error_setg(errp, "Failed to enable LSX in TCG mode"); + return; + } + } else { + cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, 0); + val = cpu->env.cpucfg[2]; + } + + cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LSX, value); +} + +static bool loongarch_get_lasx(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->lasx != ON_OFF_AUTO_OFF; +} + +static void loongarch_set_lasx(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + uint32_t val; + + cpu->lasx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + if ((cpu->lsx == ON_OFF_AUTO_OFF) && (cpu->lasx == ON_OFF_AUTO_ON)) { + error_setg(errp, "Failed to enable LASX since lSX is disabled"); + return; + } + + if (kvm_enabled()) { + /* kvm feature detection in function kvm_arch_init_vcpu */ + return; + } + + /* LASX feature detection in TCG mode */ + val = cpu->env.cpucfg[2]; + if (cpu->lasx == ON_OFF_AUTO_ON) { + if (FIELD_EX32(val, CPUCFG2, LASX) == 0) { + error_setg(errp, "Failed to enable LASX in TCG mode"); + return; + } + } + + cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, value); +} + +static void loongarch_cpu_post_init(Object *obj) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu->lbt = ON_OFF_AUTO_OFF; + cpu->pmu = ON_OFF_AUTO_OFF; + cpu->lsx = ON_OFF_AUTO_AUTO; + cpu->lasx = ON_OFF_AUTO_AUTO; + object_property_add_bool(obj, "lsx", loongarch_get_lsx, + loongarch_set_lsx); + object_property_add_bool(obj, "lasx", loongarch_get_lasx, + loongarch_set_lasx); + /* lbt is enabled only in kvm mode, not supported in tcg mode */ + if (kvm_enabled()) { + kvm_loongarch_cpu_post_init(cpu); + } +} + static void loongarch_la464_initfn(Object *obj) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); @@ -683,96 +773,6 @@ static void loongarch_cpu_unrealizefn(DeviceState *dev) lacc->parent_unrealize(dev); } -static bool loongarch_get_lsx(Object *obj, Error **errp) -{ - return LOONGARCH_CPU(obj)->lsx != ON_OFF_AUTO_OFF; -} - -static void loongarch_set_lsx(Object *obj, bool value, Error **errp) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(obj); - uint32_t val; - - cpu->lsx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; - if (cpu->lsx == ON_OFF_AUTO_OFF) { - cpu->lasx = ON_OFF_AUTO_OFF; - if (cpu->lasx == ON_OFF_AUTO_ON) { - error_setg(errp, "Failed to disable LSX since LASX is enabled"); - return; - } - } - - if (kvm_enabled()) { - /* kvm feature detection in function kvm_arch_init_vcpu */ - return; - } - - /* LSX feature detection in TCG mode */ - val = cpu->env.cpucfg[2]; - if (cpu->lsx == ON_OFF_AUTO_ON) { - if (FIELD_EX32(val, CPUCFG2, LSX) == 0) { - error_setg(errp, "Failed to enable LSX in TCG mode"); - return; - } - } else { - cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, 0); - val = cpu->env.cpucfg[2]; - } - - cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LSX, value); -} - -static bool loongarch_get_lasx(Object *obj, Error **errp) -{ - return LOONGARCH_CPU(obj)->lasx != ON_OFF_AUTO_OFF; -} - -static void loongarch_set_lasx(Object *obj, bool value, Error **errp) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(obj); - uint32_t val; - - cpu->lasx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; - if ((cpu->lsx == ON_OFF_AUTO_OFF) && (cpu->lasx == ON_OFF_AUTO_ON)) { - error_setg(errp, "Failed to enable LASX since lSX is disabled"); - return; - } - - if (kvm_enabled()) { - /* kvm feature detection in function kvm_arch_init_vcpu */ - return; - } - - /* LASX feature detection in TCG mode */ - val = cpu->env.cpucfg[2]; - if (cpu->lasx == ON_OFF_AUTO_ON) { - if (FIELD_EX32(val, CPUCFG2, LASX) == 0) { - error_setg(errp, "Failed to enable LASX in TCG mode"); - return; - } - } - - cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, value); -} - -void loongarch_cpu_post_init(Object *obj) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(obj); - - cpu->lbt = ON_OFF_AUTO_OFF; - cpu->pmu = ON_OFF_AUTO_OFF; - cpu->lsx = ON_OFF_AUTO_AUTO; - cpu->lasx = ON_OFF_AUTO_AUTO; - object_property_add_bool(obj, "lsx", loongarch_get_lsx, - loongarch_set_lsx); - object_property_add_bool(obj, "lasx", loongarch_get_lasx, - loongarch_set_lasx); - /* lbt is enabled only in kvm mode, not supported in tcg mode */ - if (kvm_enabled()) { - kvm_loongarch_cpu_post_init(cpu); - } -} - static void loongarch_cpu_init(Object *obj) { #ifndef CONFIG_USER_ONLY diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index bbe6db33f1c37..7731f6acdc990 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -494,6 +494,4 @@ static inline void set_pc(CPULoongArchState *env, uint64_t value) #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU -void loongarch_cpu_post_init(Object *obj); - #endif /* LOONGARCH_CPU_H */ From 198c827ca5d3bee6aa0c498f25f5ea6928f57de2 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 16 Jul 2025 09:45:47 +0800 Subject: [PATCH 0355/1794] target/loongarch: Set page size in TLB entry with STLB With VTLB different TLB entry may have different page size, and page size is set in PS field of TLB entry. However with STLB, all the TLB entries have the same page size, page size comes from register CSR_STLBPS, PS field of TLB entry is not used. Here PS field of TLB entry is used with all TLB entries, even with STLB. It is convenient with TLB maintainance operation. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- target/loongarch/tcg/tlb_helper.c | 41 ++++++++----------------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 8872593ff0105..3ea0e153b152c 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -110,11 +110,8 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index) if (!tlb_e) { return; } - if (index >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } + + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); pagesize = MAKE_64BIT_MASK(tlb_ps, 1); mask = MAKE_64BIT_MASK(0, tlb_ps + 1); @@ -173,11 +170,8 @@ static void fill_tlb_entry(CPULoongArchState *env, int index) lo1 = env->CSR_TLBELO1; } - /* Only MTLB has the ps fields */ - if (index >= LOONGARCH_STLB) { - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps); - } - + /* Store page size in field PS */ + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps); tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn); tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1); csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); @@ -283,12 +277,7 @@ void helper_tlbrd(CPULoongArchState *env) index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); tlb = &env->tlb[index]; - - if (index >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); if (!tlb_e) { @@ -476,11 +465,8 @@ void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info, if (!tlb_e) { continue; } - if (i >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } + + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; @@ -509,11 +495,8 @@ void helper_invtlb_page_asid_or_g(CPULoongArchState *env, if (!tlb_e) { continue; } - if (i >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } + + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; @@ -673,11 +656,7 @@ static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, uint64_t tlb_entry, tlb_ppn; uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; - if (index >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); n = (address >> tlb_ps) & 0x1;/* Odd or even */ tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0; From a3d4bc4845911d82162f5be782f73e9742a41306 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 8 Jul 2025 16:10:20 +0800 Subject: [PATCH 0356/1794] target/loongarch: Add header file cpu-mmu.h New header file cpu-mmu.h is added and move mmu relative function declaration to this file. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu-mmu.h | 30 ++++++++++++++++++++++++++++++ target/loongarch/cpu.c | 1 + target/loongarch/cpu_helper.c | 1 + target/loongarch/internals.h | 20 -------------------- target/loongarch/tcg/csr_helper.c | 1 + target/loongarch/tcg/tlb_helper.c | 1 + 6 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 target/loongarch/cpu-mmu.h diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h new file mode 100644 index 0000000000000..4c5cbd7425e8e --- /dev/null +++ b/target/loongarch/cpu-mmu.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch CPU parameters for QEMU. + * + * Copyright (c) 2025 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_CPU_MMU_H +#define LOONGARCH_CPU_MMU_H + +enum { + TLBRET_MATCH = 0, + TLBRET_BADADDR = 1, + TLBRET_NOMATCH = 2, + TLBRET_INVALID = 3, + TLBRET_DIRTY = 4, + TLBRET_RI = 5, + TLBRET_XI = 6, + TLBRET_PE = 7, +}; + +bool check_ps(CPULoongArchState *ent, uint8_t ps); +int get_physical_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx, int is_debug); +void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, + uint64_t *dir_width, target_ulong level); +hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); + +#endif /* LOONGARCH_CPU_MMU_H */ diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index b96429ffb1926..990985708e6e0 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -17,6 +17,7 @@ #include "hw/qdev-properties.h" #include "exec/translation-block.h" #include "cpu.h" +#include "cpu-mmu.h" #include "internals.h" #include "fpu/softfloat-helpers.h" #include "csr.h" diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index b5f732f15b7bd..418122f447490 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -13,6 +13,7 @@ #include "exec/target_page.h" #include "internals.h" #include "cpu-csr.h" +#include "cpu-mmu.h" #include "tcg/tcg_loongarch.h" void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index a7384b0d31515..e50d109767d49 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -32,19 +32,6 @@ void restore_fp_status(CPULoongArchState *env); #endif #ifndef CONFIG_USER_ONLY -enum { - TLBRET_MATCH = 0, - TLBRET_BADADDR = 1, - TLBRET_NOMATCH = 2, - TLBRET_INVALID = 3, - TLBRET_DIRTY = 4, - TLBRET_RI = 5, - TLBRET_XI = 6, - TLBRET_PE = 7, -}; - -bool check_ps(CPULoongArchState *ent, uint8_t ps); - extern const VMStateDescription vmstate_loongarch_cpu; void loongarch_cpu_set_irq(void *opaque, int irq, int level); @@ -54,13 +41,6 @@ uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu); uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu); void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu, uint64_t value); -int get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx, int is_debug); -void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, - uint64_t *dir_width, target_ulong level); -hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); - #endif /* !CONFIG_USER_ONLY */ uint64_t read_fcc(CPULoongArchState *env); diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c index 28b1bb86bd977..0d99e2c92b636 100644 --- a/target/loongarch/tcg/csr_helper.c +++ b/target/loongarch/tcg/csr_helper.c @@ -16,6 +16,7 @@ #include "accel/tcg/cpu-ldst.h" #include "hw/irq.h" #include "cpu-csr.h" +#include "cpu-mmu.h" target_ulong helper_csrwr_stlbps(CPULoongArchState *env, target_ulong val) { diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 3ea0e153b152c..1f49619e7f811 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -10,6 +10,7 @@ #include "qemu/guest-random.h" #include "cpu.h" +#include "cpu-mmu.h" #include "internals.h" #include "exec/helper-proto.h" #include "exec/cputlb.h" From 912f75eaed5f686fe0f312e8ac797be7c1f43b0e Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 10:44:51 +0800 Subject: [PATCH 0357/1794] target/loongarch: Add enum type TLBRet definition There is mixed usage between enum variable TLBRET_xxx and int type, here add enum type TLBRet definition and replace int type variable with enum type TLBRet in some functions. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu-mmu.h | 27 ++++++++++++++------------- target/loongarch/cpu_helper.c | 26 ++++++++++++++------------ target/loongarch/tcg/tcg_loongarch.h | 7 ++++--- target/loongarch/tcg/tlb_helper.c | 16 ++++++++-------- 4 files changed, 40 insertions(+), 36 deletions(-) diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h index 4c5cbd7425e8e..cbe6f377737a7 100644 --- a/target/loongarch/cpu-mmu.h +++ b/target/loongarch/cpu-mmu.h @@ -8,21 +8,22 @@ #ifndef LOONGARCH_CPU_MMU_H #define LOONGARCH_CPU_MMU_H -enum { - TLBRET_MATCH = 0, - TLBRET_BADADDR = 1, - TLBRET_NOMATCH = 2, - TLBRET_INVALID = 3, - TLBRET_DIRTY = 4, - TLBRET_RI = 5, - TLBRET_XI = 6, - TLBRET_PE = 7, -}; +typedef enum TLBRet { + TLBRET_MATCH, + TLBRET_BADADDR, + TLBRET_NOMATCH, + TLBRET_INVALID, + TLBRET_DIRTY, + TLBRET_RI, + TLBRET_XI, + TLBRET_PE, +} TLBRet; bool check_ps(CPULoongArchState *ent, uint8_t ps); -int get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx, int is_debug); +TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx, + int is_debug); void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, uint64_t *dir_width, target_ulong level); hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 418122f447490..17a0735f5c6da 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -44,8 +44,9 @@ void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, } } -static int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address) +static TLBRet loongarch_page_table_walker(CPULoongArchState *env, + hwaddr *physical, + int *prot, target_ulong address) { CPUState *cs = env_cpu(env); target_ulong index, phys; @@ -116,15 +117,15 @@ static int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, /* mask other attribute bits */ *physical = base & TARGET_PAGE_MASK; - return 0; + return TLBRET_MATCH; } -static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx, - int is_debug) +static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx, + int is_debug) { - int ret; + TLBRet ret; if (tcg_enabled()) { ret = loongarch_get_addr_from_tlb(env, physical, prot, address, @@ -158,9 +159,10 @@ static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, } } -int get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx, int is_debug) +TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx, + int is_debug) { int user_mode = mmu_idx == MMU_USER_IDX; int kernel_mode = mmu_idx == MMU_KERNEL_IDX; @@ -214,7 +216,7 @@ hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) int prot; if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, - cpu_mmu_index(cs, false), 1) != 0) { + cpu_mmu_index(cs, false), 1) != TLBRET_MATCH) { return -1; } return phys_addr; diff --git a/target/loongarch/tcg/tcg_loongarch.h b/target/loongarch/tcg/tcg_loongarch.h index fd4e1160229ed..488700c3c363a 100644 --- a/target/loongarch/tcg/tcg_loongarch.h +++ b/target/loongarch/tcg/tcg_loongarch.h @@ -7,6 +7,7 @@ #ifndef TARGET_LOONGARCH_TCG_LOONGARCH_H #define TARGET_LOONGARCH_TCG_LOONGARCH_H #include "cpu.h" +#include "cpu-mmu.h" void loongarch_csr_translate_init(void); @@ -14,8 +15,8 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); -int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx); +TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx); #endif /* TARGET_LOONGARCH_TCG_LOONGARCH_H */ diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 1f49619e7f811..4a2a5659851d8 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -30,7 +30,7 @@ bool check_ps(CPULoongArchState *env, uint8_t tlb_ps) } static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, - MMUAccessType access_type, int tlb_error) + MMUAccessType access_type, TLBRet tlb_error) { CPUState *cs = env_cpu(env); @@ -517,7 +517,7 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, CPULoongArchState *env = cpu_env(cs); hwaddr physical; int prot; - int ret; + TLBRet ret; /* Data access */ ret = get_physical_address(env, &physical, &prot, address, @@ -648,9 +648,9 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps); } -static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - int access_type, int index, int mmu_idx) +static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + int access_type, int index, int mmu_idx) { LoongArchTLB *tlb = &env->tlb[index]; uint64_t plv = mmu_idx; @@ -713,9 +713,9 @@ static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, return TLBRET_MATCH; } -int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) +TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx) { int index, match; From 82cd0be29b603ef620f45053a406bdb4a3221563 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 11:29:32 +0800 Subject: [PATCH 0358/1794] target/loongarch: Use vaddr in get_physical_address() Replace target_ulong type with vaddr in function get_physical_address() and the same with its calling functions. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu-mmu.h | 2 +- target/loongarch/cpu_helper.c | 9 ++++----- target/loongarch/tcg/tlb_helper.c | 11 ++++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h index cbe6f377737a7..dffc12820faee 100644 --- a/target/loongarch/cpu-mmu.h +++ b/target/loongarch/cpu-mmu.h @@ -21,7 +21,7 @@ typedef enum TLBRet { bool check_ps(CPULoongArchState *ent, uint8_t ps); TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, + int *prot, vaddr address, MMUAccessType access_type, int mmu_idx, int is_debug); void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 17a0735f5c6da..0c037ef1636d8 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -46,7 +46,7 @@ void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, static TLBRet loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address) + int *prot, vaddr address) { CPUState *cs = env_cpu(env); target_ulong index, phys; @@ -121,7 +121,7 @@ static TLBRet loongarch_page_table_walker(CPULoongArchState *env, } static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, + int *prot, vaddr address, MMUAccessType access_type, int mmu_idx, int is_debug) { @@ -147,8 +147,7 @@ static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, return TLBRET_NOMATCH; } -static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, - target_ulong dmw) +static hwaddr dmw_va2pa(CPULoongArchState *env, vaddr va, target_ulong dmw) { if (is_la64(env)) { return va & TARGET_VIRT_MASK; @@ -160,7 +159,7 @@ static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, } TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, + int *prot, vaddr address, MMUAccessType access_type, int mmu_idx, int is_debug) { diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 4a2a5659851d8..3d09f180208e1 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -29,7 +29,7 @@ bool check_ps(CPULoongArchState *env, uint8_t tlb_ps) return BIT_ULL(tlb_ps) & (env->CSR_PRCFG2); } -static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, +static void raise_mmu_exception(CPULoongArchState *env, vaddr address, MMUAccessType access_type, TLBRet tlb_error) { CPUState *cs = env_cpu(env); @@ -198,7 +198,7 @@ static uint32_t get_random_tlb(uint32_t low, uint32_t high) * field in tlb entry contains bit[47:13], so need adjust. * virt_vpn = vaddr[47:13] */ -static bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, +static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr, int *index) { LoongArchTLB *tlb; @@ -649,8 +649,9 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, } static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - int access_type, int index, int mmu_idx) + int *prot, vaddr address, + int access_type, int index, + int mmu_idx) { LoongArchTLB *tlb = &env->tlb[index]; uint64_t plv = mmu_idx; @@ -714,7 +715,7 @@ static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, } TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, + int *prot, vaddr address, MMUAccessType access_type, int mmu_idx) { int index, match; From e6c855f44ad63b5e94bc3d27adca12c24ce7953d Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 11:38:35 +0800 Subject: [PATCH 0359/1794] target/loongarch: Use MMUAccessType in loongarch_map_tlb_entry() Enum type MMUAccessType is used in function loongarch_map_tlb_entry() rather than int type, and keep consistent with its caller function. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 3d09f180208e1..915b1aadb5be2 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -650,7 +650,7 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, int *prot, vaddr address, - int access_type, int index, + MMUAccessType access_type, int index, int mmu_idx) { LoongArchTLB *tlb = &env->tlb[index]; From 7392cb1c7b24e34987b6f2bbff00c39fe0829cc9 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 12:03:43 +0800 Subject: [PATCH 0360/1794] target/loongarch: Add common function loongarch_check_pte() Common function loongarch_check_pte() is to check tlb entry, return the physical address and access priviledge if found. Also it can be used with page table entry, which is used in page table walker. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu-mmu.h | 10 +++++ target/loongarch/cpu_helper.c | 61 ++++++++++++++++++++++++++++ target/loongarch/tcg/tlb_helper.c | 66 ++++++------------------------- 3 files changed, 83 insertions(+), 54 deletions(-) diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h index dffc12820faee..be3d11d3c1c19 100644 --- a/target/loongarch/cpu-mmu.h +++ b/target/loongarch/cpu-mmu.h @@ -19,7 +19,17 @@ typedef enum TLBRet { TLBRET_PE, } TLBRet; +typedef struct MMUContext { + vaddr addr; + uint64_t pte; + hwaddr physical; + int ps; /* page size shift */ + int prot; +} MMUContext; + bool check_ps(CPULoongArchState *ent, uint8_t ps); +TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context, + MMUAccessType access_type, int mmu_idx); TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, int *prot, vaddr address, MMUAccessType access_type, int mmu_idx, diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 0c037ef1636d8..739cdab5aa895 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -44,6 +44,67 @@ void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, } } +TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context, + MMUAccessType access_type, int mmu_idx) +{ + uint64_t plv = mmu_idx; + uint64_t tlb_entry, tlb_ppn; + uint8_t tlb_ps, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; + + tlb_entry = context->pte; + tlb_ps = context->ps; + tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V); + tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); + tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); + if (is_la64(env)) { + tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); + tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); + tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); + tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); + } else { + tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); + tlb_nx = 0; + tlb_nr = 0; + tlb_rplv = 0; + } + + /* Remove sw bit between bit12 -- bit PS*/ + tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) - 1)); + + /* Check access rights */ + if (!tlb_v) { + return TLBRET_INVALID; + } + + if (access_type == MMU_INST_FETCH && tlb_nx) { + return TLBRET_XI; + } + + if (access_type == MMU_DATA_LOAD && tlb_nr) { + return TLBRET_RI; + } + + if (((tlb_rplv == 0) && (plv > tlb_plv)) || + ((tlb_rplv == 1) && (plv != tlb_plv))) { + return TLBRET_PE; + } + + if ((access_type == MMU_DATA_STORE) && !tlb_d) { + return TLBRET_DIRTY; + } + + context->physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | + (context->addr & MAKE_64BIT_MASK(0, tlb_ps)); + context->prot = PAGE_READ; + if (tlb_d) { + context->prot |= PAGE_WRITE; + } + if (!tlb_nx) { + context->prot |= PAGE_EXEC; + } + return TLBRET_MATCH; +} + static TLBRet loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, int *prot, vaddr address) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 915b1aadb5be2..10322da62e428 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -654,64 +654,22 @@ static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, int mmu_idx) { LoongArchTLB *tlb = &env->tlb[index]; - uint64_t plv = mmu_idx; - uint64_t tlb_entry, tlb_ppn; - uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; + uint8_t tlb_ps, n; + MMUContext context; + TLBRet ret; tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); n = (address >> tlb_ps) & 0x1;/* Odd or even */ + context.pte = n ? tlb->tlb_entry1 : tlb->tlb_entry0; + context.addr = address; + context.ps = tlb_ps; + ret = loongarch_check_pte(env, &context, access_type, mmu_idx); + if (ret == TLBRET_MATCH) { + *physical = context.physical; + *prot = context.prot; + } - tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0; - tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V); - tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); - tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); - if (is_la64(env)) { - tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); - tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); - tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); - tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); - } else { - tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); - tlb_nx = 0; - tlb_nr = 0; - tlb_rplv = 0; - } - - /* Remove sw bit between bit12 -- bit PS*/ - tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) - 1)); - - /* Check access rights */ - if (!tlb_v) { - return TLBRET_INVALID; - } - - if (access_type == MMU_INST_FETCH && tlb_nx) { - return TLBRET_XI; - } - - if (access_type == MMU_DATA_LOAD && tlb_nr) { - return TLBRET_RI; - } - - if (((tlb_rplv == 0) && (plv > tlb_plv)) || - ((tlb_rplv == 1) && (plv != tlb_plv))) { - return TLBRET_PE; - } - - if ((access_type == MMU_DATA_STORE) && !tlb_d) { - return TLBRET_DIRTY; - } - - *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | - (address & MAKE_64BIT_MASK(0, tlb_ps)); - *prot = PAGE_READ; - if (tlb_d) { - *prot |= PAGE_WRITE; - } - if (!tlb_nx) { - *prot |= PAGE_EXEC; - } - return TLBRET_MATCH; + return ret; } TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, From 3dd4a2d0fc339c3cb321d69a0372cb9433786105 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 16:10:02 +0800 Subject: [PATCH 0361/1794] target/loongarch: Use loongarch_check_pte in loongarch_page_table_walker Function loongarch_check_pte() can get physical address and access priviledge, it works on both TLB entry and pte entry. It can be used in function loongarch_page_table_walker() also. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu_helper.c | 42 +++++++++++++---------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 739cdab5aa895..cd61b33ef9556 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -106,15 +106,17 @@ TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context, } static TLBRet loongarch_page_table_walker(CPULoongArchState *env, - hwaddr *physical, - int *prot, vaddr address) + MMUContext *context, + int access_type, int mmu_idx) { CPUState *cs = env_cpu(env); target_ulong index, phys; uint64_t dir_base, dir_width; uint64_t base; int level; + vaddr address; + address = context->addr; if ((address >> 63) & 0x1) { base = env->CSR_PGDH; } else { @@ -156,29 +158,9 @@ static TLBRet loongarch_page_table_walker(CPULoongArchState *env, base = ldq_phys(cs->as, phys); } - /* TODO: check plv and other bits? */ - - /* base is pte, in normal pte format */ - if (!FIELD_EX64(base, TLBENTRY, V)) { - return TLBRET_NOMATCH; - } - - if (!FIELD_EX64(base, TLBENTRY, D)) { - *prot = PAGE_READ; - } else { - *prot = PAGE_READ | PAGE_WRITE; - } - - /* get TARGET_PAGE_SIZE aligned physical address */ - base += (address & TARGET_PHYS_MASK) & ((1 << dir_base) - 1); - /* mask RPLV, NX, NR bits */ - base = FIELD_DP64(base, TLBENTRY_64, RPLV, 0); - base = FIELD_DP64(base, TLBENTRY_64, NX, 0); - base = FIELD_DP64(base, TLBENTRY_64, NR, 0); - /* mask other attribute bits */ - *physical = base & TARGET_PAGE_MASK; - - return TLBRET_MATCH; + context->ps = dir_base; + context->pte = base; + return loongarch_check_pte(env, context, access_type, mmu_idx); } static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, @@ -187,7 +169,9 @@ static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, int is_debug) { TLBRet ret; + MMUContext context; + context.addr = address; if (tcg_enabled()) { ret = loongarch_get_addr_from_tlb(env, physical, prot, address, access_type, mmu_idx); @@ -202,7 +186,13 @@ static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, * legal mapping, even if the mapping is not yet in TLB. return 0 if * there is a valid map, else none zero. */ - return loongarch_page_table_walker(env, physical, prot, address); + ret = loongarch_page_table_walker(env, &context, access_type, mmu_idx); + if (ret == TLBRET_MATCH) { + *physical = context.physical; + *prot = context.prot; + } + + return ret; } return TLBRET_NOMATCH; From 36055cf414b591ce2467631dbd5d8c32d4350263 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 16:40:25 +0800 Subject: [PATCH 0362/1794] target/loongarch: Use MMUConext in loongarch_map_tlb_entry() With function loongarch_map_tlb_entry(), parameter MMUConext is added and remove parameter physical, prot and address. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 33 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 10322da62e428..703ab9c8ca4ca 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -648,28 +648,19 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps); } -static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, - int *prot, vaddr address, +static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, + MMUContext *context, MMUAccessType access_type, int index, int mmu_idx) { LoongArchTLB *tlb = &env->tlb[index]; uint8_t tlb_ps, n; - MMUContext context; - TLBRet ret; tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - n = (address >> tlb_ps) & 0x1;/* Odd or even */ - context.pte = n ? tlb->tlb_entry1 : tlb->tlb_entry0; - context.addr = address; - context.ps = tlb_ps; - ret = loongarch_check_pte(env, &context, access_type, mmu_idx); - if (ret == TLBRET_MATCH) { - *physical = context.physical; - *prot = context.prot; - } - - return ret; + n = (context->addr >> tlb_ps) & 0x1;/* Odd or even */ + context->pte = n ? tlb->tlb_entry1 : tlb->tlb_entry0; + context->ps = tlb_ps; + return loongarch_check_pte(env, context, access_type, mmu_idx); } TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, @@ -677,11 +668,19 @@ TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, MMUAccessType access_type, int mmu_idx) { int index, match; + MMUContext context; + TLBRet ret; + context.addr = address; match = loongarch_tlb_search(env, address, &index); if (match) { - return loongarch_map_tlb_entry(env, physical, prot, - address, access_type, index, mmu_idx); + ret = loongarch_map_tlb_entry(env, &context, access_type, index, + mmu_idx); + if (ret == TLBRET_MATCH) { + *physical = context.physical; + *prot = context.prot; + } + return ret; } return TLBRET_NOMATCH; From 35fc0ec73c3264b67ba2c8d5e39d5897dca2c891 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 17:06:05 +0800 Subject: [PATCH 0363/1794] target/loongarch: Use MMUContext in loongarch_get_addr_from_tlb With function loongarch_get_addr_from_tlb(), parameter MMUContext is added and remove parameter physical, prot and address. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu_helper.c | 7 +++++-- target/loongarch/tcg/tcg_loongarch.h | 4 ++-- target/loongarch/tcg/tlb_helper.c | 18 +++++------------- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index cd61b33ef9556..0cc01a0ca47ec 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -173,9 +173,12 @@ static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, context.addr = address; if (tcg_enabled()) { - ret = loongarch_get_addr_from_tlb(env, physical, prot, address, - access_type, mmu_idx); + ret = loongarch_get_addr_from_tlb(env, &context, access_type, mmu_idx); if (ret != TLBRET_NOMATCH) { + if (ret == TLBRET_MATCH) { + *physical = context.physical; + *prot = context.prot; + } return ret; } } diff --git a/target/loongarch/tcg/tcg_loongarch.h b/target/loongarch/tcg/tcg_loongarch.h index 488700c3c363a..47702893e3248 100644 --- a/target/loongarch/tcg/tcg_loongarch.h +++ b/target/loongarch/tcg/tcg_loongarch.h @@ -15,8 +15,8 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); -TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, +TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, + MMUContext *context, MMUAccessType access_type, int mmu_idx); #endif /* TARGET_LOONGARCH_TCG_LOONGARCH_H */ diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 703ab9c8ca4ca..64a4e82dec265 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -663,24 +663,16 @@ static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, return loongarch_check_pte(env, context, access_type, mmu_idx); } -TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, - int *prot, vaddr address, +TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, + MMUContext *context, MMUAccessType access_type, int mmu_idx) { int index, match; - MMUContext context; - TLBRet ret; - context.addr = address; - match = loongarch_tlb_search(env, address, &index); + match = loongarch_tlb_search(env, context->addr, &index); if (match) { - ret = loongarch_map_tlb_entry(env, &context, access_type, index, - mmu_idx); - if (ret == TLBRET_MATCH) { - *physical = context.physical; - *prot = context.prot; - } - return ret; + return loongarch_map_tlb_entry(env, context, access_type, index, + mmu_idx); } return TLBRET_NOMATCH; From 4817a22edd49fc671f6efdc922826a4ce7f91017 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 17:19:53 +0800 Subject: [PATCH 0364/1794] target/loongarch: Use MMUContext in loongarch_map_address() With function loongarch_map_address(), parameter MMUContext is added and remove parameter address, prot and address. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu_helper.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 0cc01a0ca47ec..225382f70e8a0 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -163,22 +163,16 @@ static TLBRet loongarch_page_table_walker(CPULoongArchState *env, return loongarch_check_pte(env, context, access_type, mmu_idx); } -static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, - int *prot, vaddr address, +static TLBRet loongarch_map_address(CPULoongArchState *env, + MMUContext *context, MMUAccessType access_type, int mmu_idx, int is_debug) { TLBRet ret; - MMUContext context; - context.addr = address; if (tcg_enabled()) { - ret = loongarch_get_addr_from_tlb(env, &context, access_type, mmu_idx); + ret = loongarch_get_addr_from_tlb(env, context, access_type, mmu_idx); if (ret != TLBRET_NOMATCH) { - if (ret == TLBRET_MATCH) { - *physical = context.physical; - *prot = context.prot; - } return ret; } } @@ -189,13 +183,7 @@ static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, * legal mapping, even if the mapping is not yet in TLB. return 0 if * there is a valid map, else none zero. */ - ret = loongarch_page_table_walker(env, &context, access_type, mmu_idx); - if (ret == TLBRET_MATCH) { - *physical = context.physical; - *prot = context.prot; - } - - return ret; + return loongarch_page_table_walker(env, context, access_type, mmu_idx); } return TLBRET_NOMATCH; @@ -223,6 +211,8 @@ TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, int64_t addr_high; uint8_t da = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, DA); uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG); + MMUContext context; + TLBRet ret; /* Check PG and DA */ if (da & !pg) { @@ -258,8 +248,14 @@ TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, } /* Mapped address */ - return loongarch_map_address(env, physical, prot, address, - access_type, mmu_idx, is_debug); + context.addr = address; + ret = loongarch_map_address(env, &context, + access_type, mmu_idx, is_debug); + if (ret == TLBRET_MATCH) { + *physical = context.physical; + *prot = context.prot; + } + return ret; } hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) From f95b9702750507665f90e377b5c6c68274104024 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 17:51:28 +0800 Subject: [PATCH 0365/1794] target/loongarch: Use MMUContext in get_physical_address() With function get_physical_address(), parameter MMUContext is added and remove parameter address, prot and address. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu-mmu.h | 3 +-- target/loongarch/cpu_helper.c | 32 ++++++++++++------------------- target/loongarch/tcg/tlb_helper.c | 8 +++++--- 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h index be3d11d3c1c19..0068d22efcb0c 100644 --- a/target/loongarch/cpu-mmu.h +++ b/target/loongarch/cpu-mmu.h @@ -30,8 +30,7 @@ typedef struct MMUContext { bool check_ps(CPULoongArchState *ent, uint8_t ps); TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context, MMUAccessType access_type, int mmu_idx); -TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, vaddr address, +TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context, MMUAccessType access_type, int mmu_idx, int is_debug); void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 225382f70e8a0..4a9db3ea4c1f0 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -200,8 +200,7 @@ static hwaddr dmw_va2pa(CPULoongArchState *env, vaddr va, target_ulong dmw) } } -TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, vaddr address, +TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context, MMUAccessType access_type, int mmu_idx, int is_debug) { @@ -211,13 +210,13 @@ TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, int64_t addr_high; uint8_t da = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, DA); uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG); - MMUContext context; - TLBRet ret; + vaddr address; /* Check PG and DA */ + address = context->addr; if (da & !pg) { - *physical = address & TARGET_PHYS_MASK; - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + context->physical = address & TARGET_PHYS_MASK; + context->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return TLBRET_MATCH; } @@ -235,8 +234,8 @@ TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_32, VSEG); } if ((plv & env->CSR_DMW[i]) && (base_c == base_v)) { - *physical = dmw_va2pa(env, address, env->CSR_DMW[i]); - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + context->physical = dmw_va2pa(env, address, env->CSR_DMW[i]); + context->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return TLBRET_MATCH; } } @@ -248,25 +247,18 @@ TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, } /* Mapped address */ - context.addr = address; - ret = loongarch_map_address(env, &context, - access_type, mmu_idx, is_debug); - if (ret == TLBRET_MATCH) { - *physical = context.physical; - *prot = context.prot; - } - return ret; + return loongarch_map_address(env, context, access_type, mmu_idx, is_debug); } hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { CPULoongArchState *env = cpu_env(cs); - hwaddr phys_addr; - int prot; + MMUContext context; - if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, + context.addr = addr; + if (get_physical_address(env, &context, MMU_DATA_LOAD, cpu_mmu_index(cs, false), 1) != TLBRET_MATCH) { return -1; } - return phys_addr; + return context.physical; } diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 64a4e82dec265..7d3f98633d548 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -517,13 +517,15 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, CPULoongArchState *env = cpu_env(cs); hwaddr physical; int prot; + MMUContext context; TLBRet ret; /* Data access */ - ret = get_physical_address(env, &physical, &prot, address, - access_type, mmu_idx, 0); - + context.addr = address; + ret = get_physical_address(env, &context, access_type, mmu_idx, 0); if (ret == TLBRET_MATCH) { + physical = context.physical; + prot = context.prot; tlb_set_page(cs, address & TARGET_PAGE_MASK, physical & TARGET_PAGE_MASK, prot, mmu_idx, TARGET_PAGE_SIZE); From cc78259deb21940521a227619eb00a4b8e3e36c2 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 30 Jul 2025 09:47:55 +0800 Subject: [PATCH 0366/1794] target/loongarch: Use correct address when flush tlb With tlb_flush_range_by_mmuidx(), the virtual address is 64 bit. However on LoongArch TLB emulation system, virtual address is 48 bit. It is necessary to signed-extend 48 bit address to 64 bit when flush tlb, also fix address calculation issue with odd page. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 7d3f98633d548..9365860c8c98e 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -115,16 +115,16 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index) tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); pagesize = MAKE_64BIT_MASK(tlb_ps, 1); mask = MAKE_64BIT_MASK(0, tlb_ps + 1); + addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask; + addr = sextract64(addr, 0, TARGET_VIRT_ADDR_SPACE_BITS); if (tlb_v0) { - addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask; /* even */ tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, mmu_idx, TARGET_LONG_BITS); } if (tlb_v1) { - addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & pagesize; /* odd */ - tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, + tlb_flush_range_by_mmuidx(env_cpu(env), addr + pagesize, pagesize, mmu_idx, TARGET_LONG_BITS); } } From e1e2909f8e74051a34a044940f90d4650b6e784a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:44 +0100 Subject: [PATCH 0367/1794] hw/i386/pc_piix.c: restrict isapc machine to 32-bit CPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The isapc machine represents a legacy ISA PC with a 486 CPU. Whilst it is possible to specify any CPU via -cpu on the command line, it makes no sense to allow modern 64-bit CPUs to be used. Restrict the isapc machine to the available 32-bit CPUs, taking care to handle the case where if a user inadvertently uses either -cpu max or -cpu host then the "best" 32-bit CPU is used (in this case the pentium3). Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250828111057.468712-2-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index d165ac72ed75c..8f5fb3cf90b13 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -436,6 +436,31 @@ static void pc_set_south_bridge(Object *obj, int value, Error **errp) #ifdef CONFIG_ISAPC static void pc_init_isa(MachineState *machine) { + /* + * There is a small chance that someone unintentionally passes "-cpu max" + * for the isapc machine, which will provide a much more modern 32-bit + * CPU than would be expected for an ISA-era PC. If the "max" cpu type has + * been specified, choose the "best" 32-bit cpu possible which we consider + * be the pentium3 (deliberately choosing an Intel CPU given that the + * default 486 CPU for the isapc machine is also an Intel CPU). + */ + if (!strcmp(machine->cpu_type, X86_CPU_TYPE_NAME("max"))) { + machine->cpu_type = X86_CPU_TYPE_NAME("pentium3"); + warn_report("-cpu max is invalid for isapc machine, using pentium3"); + } + + /* + * Similarly if someone unintentionally passes "-cpu host" for the isapc + * machine then display a warning and also switch to the "best" 32-bit + * cpu possible which we consider to be the pentium3. This is because any + * host CPU will already be modern than this, but it also ensures any + * newer CPU flags/features are filtered out for older guests. + */ + if (!strcmp(machine->cpu_type, X86_CPU_TYPE_NAME("host"))) { + machine->cpu_type = X86_CPU_TYPE_NAME("pentium3"); + warn_report("-cpu host is invalid for isapc machine, using pentium3"); + } + pc_init1(machine, NULL); } #endif @@ -815,7 +840,20 @@ DEFINE_I440FX_MACHINE(2, 6); #ifdef CONFIG_ISAPC static void isapc_machine_options(MachineClass *m) { + static const char * const valid_cpu_types[] = { + X86_CPU_TYPE_NAME("486"), + X86_CPU_TYPE_NAME("athlon"), + X86_CPU_TYPE_NAME("kvm32"), + X86_CPU_TYPE_NAME("pentium"), + X86_CPU_TYPE_NAME("pentium2"), + X86_CPU_TYPE_NAME("pentium3"), + X86_CPU_TYPE_NAME("qemu32"), + X86_CPU_TYPE_NAME("max"), + X86_CPU_TYPE_NAME("host"), + NULL + }; PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + m->desc = "ISA-only PC"; m->max_cpus = 1; m->option_rom_has_mr = true; @@ -828,6 +866,7 @@ static void isapc_machine_options(MachineClass *m) pcmc->has_reserved_memory = false; m->default_nic = "ne2k_isa"; m->default_cpu_type = X86_CPU_TYPE_NAME("486"); + m->valid_cpu_types = valid_cpu_types; m->no_floppy = !module_object_class_by_name(TYPE_ISA_FDC); m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); } From 483a232e0431f19a4d6596be59c1d51370407249 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:45 +0100 Subject: [PATCH 0368/1794] hw/i386/pc_piix.c: restrict isapc machine to 3.5G memory Since the isapc machine is now limited to using 32-bit CPUs, add a hard restriction so that the machine cannot be started with more than 3.5G memory. This matches the default value for max_ram_below_4g if not specified and provides consistent behaviour betweem TCG and KVM accelerators. Signed-off-by: Mark Cave-Ayland Link: https://lore.kernel.org/r/20250828111057.468712-3-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 8f5fb3cf90b13..9a3b5d88f08b3 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -461,6 +461,12 @@ static void pc_init_isa(MachineState *machine) warn_report("-cpu host is invalid for isapc machine, using pentium3"); } + if (machine->ram_size > 3.5 * GiB) { + error_report("Too much memory for this machine: %" PRId64 " MiB, " + "maximum 3584 MiB", machine->ram_size / MiB); + exit(1); + } + pc_init1(machine, NULL); } #endif From b55eab382cfbeb11f0afe116a06243d3fe5e43d9 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:46 +0100 Subject: [PATCH 0369/1794] hw/i386/pc_piix.c: remove include for loader.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This header is not required since the loader functionality is handled separately by pc_memory_init() in pc.c. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-4-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 9a3b5d88f08b3..351986232d6d3 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -28,7 +28,6 @@ #include "qemu/units.h" #include "hw/char/parallel-isa.h" #include "hw/dma/i8257.h" -#include "hw/loader.h" #include "hw/i386/x86.h" #include "hw/i386/pc.h" #include "hw/i386/apic.h" From 79233a7e600ff26c623aecee81aa9a04cbbc7668 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:47 +0100 Subject: [PATCH 0370/1794] hw/i386/pc_piix.c: inline pc_xen_hvm_init_pci() into pc_xen_hvm_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helps to simplify the initialisation of the Xen hvm machine. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Mark Cave-Ayland Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-5-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 351986232d6d3..8e302dc013bf5 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -471,14 +471,6 @@ static void pc_init_isa(MachineState *machine) #endif #ifdef CONFIG_XEN -static void pc_xen_hvm_init_pci(MachineState *machine) -{ - const char *pci_type = xen_igd_gfx_pt_enabled() ? - TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE : TYPE_I440FX_PCI_DEVICE; - - pc_init1(machine, pci_type); -} - static void pc_xen_hvm_init(MachineState *machine) { PCMachineState *pcms = PC_MACHINE(machine); @@ -488,7 +480,10 @@ static void pc_xen_hvm_init(MachineState *machine) exit(1); } - pc_xen_hvm_init_pci(machine); + pc_init1(machine, xen_igd_gfx_pt_enabled() + ? TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE + : TYPE_I440FX_PCI_DEVICE); + xen_igd_reserve_slot(pcms->pcibus); pci_create_simple(pcms->pcibus, -1, "xen-platform"); } From 469be2f11f7279fe9174199183cf51ba1f557e2d Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:48 +0100 Subject: [PATCH 0371/1794] hw/i386/pc_piix.c: duplicate pc_init1() into pc_isa_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to prepare for splitting the isapc machine into its own separate file. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250828111057.468712-6-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 275 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 274 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 8e302dc013bf5..60bf18c680b05 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -435,6 +435,23 @@ static void pc_set_south_bridge(Object *obj, int value, Error **errp) #ifdef CONFIG_ISAPC static void pc_init_isa(MachineState *machine) { + const char *pci_type = NULL; + PCMachineState *pcms = PC_MACHINE(machine); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + X86MachineState *x86ms = X86_MACHINE(machine); + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *system_io = get_system_io(); + Object *phb = NULL; + ISABus *isa_bus; + Object *piix4_pm = NULL; + qemu_irq smi_irq; + GSIState *gsi_state; + MemoryRegion *ram_memory; + MemoryRegion *pci_memory = NULL; + MemoryRegion *rom_memory = system_memory; + ram_addr_t lowmem; + uint64_t hole64_size = 0; + /* * There is a small chance that someone unintentionally passes "-cpu max" * for the isapc machine, which will provide a much more modern 32-bit @@ -466,7 +483,263 @@ static void pc_init_isa(MachineState *machine) exit(1); } - pc_init1(machine, NULL); + /* + * Calculate ram split, for memory below and above 4G. It's a bit + * complicated for backward compatibility reasons ... + * + * - Traditional split is 3.5G (lowmem = 0xe0000000). This is the + * default value for max_ram_below_4g now. + * + * - Then, to gigabyte align the memory, we move the split to 3G + * (lowmem = 0xc0000000). But only in case we have to split in + * the first place, i.e. ram_size is larger than (traditional) + * lowmem. And for new machine types (gigabyte_align = true) + * only, for live migration compatibility reasons. + * + * - Next the max-ram-below-4g option was added, which allowed to + * reduce lowmem to a smaller value, to allow a larger PCI I/O + * window below 4G. qemu doesn't enforce gigabyte alignment here, + * but prints a warning. + * + * - Finally max-ram-below-4g got updated to also allow raising lowmem, + * so legacy non-PAE guests can get as much memory as possible in + * the 32bit address space below 4G. + * + * - Note that Xen has its own ram setup code in xen_ram_init(), + * called via xen_hvm_init_pc(). + * + * Examples: + * qemu -M pc-1.7 -m 4G (old default) -> 3584M low, 512M high + * qemu -M pc -m 4G (new default) -> 3072M low, 1024M high + * qemu -M pc,max-ram-below-4g=2G -m 4G -> 2048M low, 2048M high + * qemu -M pc,max-ram-below-4g=4G -m 3968M -> 3968M low (=4G-128M) + */ + if (xen_enabled()) { + xen_hvm_init_pc(pcms, &ram_memory); + } else { + ram_memory = machine->ram; + if (!pcms->max_ram_below_4g) { + pcms->max_ram_below_4g = 0xe0000000; /* default: 3.5G */ + } + lowmem = pcms->max_ram_below_4g; + if (machine->ram_size >= pcms->max_ram_below_4g) { + if (pcmc->gigabyte_align) { + if (lowmem > 0xc0000000) { + lowmem = 0xc0000000; + } + if (lowmem & (1 * GiB - 1)) { + warn_report("Large machine and max_ram_below_4g " + "(%" PRIu64 ") not a multiple of 1G; " + "possible bad performance.", + pcms->max_ram_below_4g); + } + } + } + + if (machine->ram_size >= lowmem) { + x86ms->above_4g_mem_size = machine->ram_size - lowmem; + x86ms->below_4g_mem_size = lowmem; + } else { + x86ms->above_4g_mem_size = 0; + x86ms->below_4g_mem_size = machine->ram_size; + } + } + + pc_machine_init_sgx_epc(pcms); + x86_cpus_init(x86ms, pcmc->default_cpu_version); + + if (kvm_enabled()) { + kvmclock_create(pcmc->kvmclock_create_always); + } + + if (pcmc->pci_enabled) { + pci_memory = g_new(MemoryRegion, 1); + memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); + rom_memory = pci_memory; + + phb = OBJECT(qdev_new(TYPE_I440FX_PCI_HOST_BRIDGE)); + object_property_add_child(OBJECT(machine), "i440fx", phb); + object_property_set_link(phb, PCI_HOST_PROP_RAM_MEM, + OBJECT(ram_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_PCI_MEM, + OBJECT(pci_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_SYSTEM_MEM, + OBJECT(system_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_IO_MEM, + OBJECT(system_io), &error_fatal); + object_property_set_uint(phb, PCI_HOST_BELOW_4G_MEM_SIZE, + x86ms->below_4g_mem_size, &error_fatal); + object_property_set_uint(phb, PCI_HOST_ABOVE_4G_MEM_SIZE, + x86ms->above_4g_mem_size, &error_fatal); + object_property_set_str(phb, I440FX_HOST_PROP_PCI_TYPE, pci_type, + &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(phb), &error_fatal); + + pcms->pcibus = PCI_BUS(qdev_get_child_bus(DEVICE(phb), "pci.0")); + pci_bus_map_irqs(pcms->pcibus, + xen_enabled() ? xen_pci_slot_get_pirq + : pc_pci_slot_get_pirq); + + hole64_size = object_property_get_uint(phb, + PCI_HOST_PROP_PCI_HOLE64_SIZE, + &error_abort); + } + + /* allocate ram and load rom/bios */ + if (!xen_enabled()) { + pc_memory_init(pcms, system_memory, rom_memory, hole64_size); + } else { + assert(machine->ram_size == x86ms->below_4g_mem_size + + x86ms->above_4g_mem_size); + + pc_system_flash_cleanup_unused(pcms); + if (machine->kernel_filename != NULL) { + /* For xen HVM direct kernel boot, load linux here */ + xen_load_linux(pcms); + } + } + + gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); + + if (pcmc->pci_enabled) { + PCIDevice *pci_dev; + DeviceState *dev; + size_t i; + + pci_dev = pci_new_multifunction(-1, pcms->south_bridge); + object_property_set_bool(OBJECT(pci_dev), "has-usb", + machine_usb(machine), &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-acpi", + x86_machine_is_acpi_enabled(x86ms), + &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-pic", false, + &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-pit", false, + &error_abort); + qdev_prop_set_uint32(DEVICE(pci_dev), "smb_io_base", 0xb100); + object_property_set_bool(OBJECT(pci_dev), "smm-enabled", + x86_machine_is_smm_enabled(x86ms), + &error_abort); + dev = DEVICE(pci_dev); + for (i = 0; i < ISA_NUM_IRQS; i++) { + qdev_connect_gpio_out_named(dev, "isa-irqs", i, x86ms->gsi[i]); + } + pci_realize_and_unref(pci_dev, pcms->pcibus, &error_fatal); + + if (xen_enabled()) { + pci_device_set_intx_routing_notifier( + pci_dev, piix_intx_routing_notifier_xen); + + /* + * Xen supports additional interrupt routes from the PCI devices to + * the IOAPIC: the four pins of each PCI device on the bus are also + * connected to the IOAPIC directly. + * These additional routes can be discovered through ACPI. + */ + pci_bus_irqs(pcms->pcibus, xen_intx_set_irq, pci_dev, + XEN_IOAPIC_NUM_PIRQS); + } + + isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci_dev), "isa.0")); + x86ms->rtc = ISA_DEVICE(object_resolve_path_component(OBJECT(pci_dev), + "rtc")); + piix4_pm = object_resolve_path_component(OBJECT(pci_dev), "pm"); + dev = DEVICE(object_resolve_path_component(OBJECT(pci_dev), "ide")); + pci_ide_create_devs(PCI_DEVICE(dev)); + pcms->idebus[0] = qdev_get_child_bus(dev, "ide.0"); + pcms->idebus[1] = qdev_get_child_bus(dev, "ide.1"); + } else { + uint32_t irq; + + isa_bus = isa_bus_new(NULL, system_memory, system_io, + &error_abort); + isa_bus_register_input_irqs(isa_bus, x86ms->gsi); + + x86ms->rtc = isa_new(TYPE_MC146818_RTC); + qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); + isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); + irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", + &error_fatal); + isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); + + i8257_dma_init(OBJECT(machine), isa_bus, 0); + pcms->hpet_enabled = false; + } + + if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { + pc_i8259_create(isa_bus, gsi_state->i8259_irq); + } + + if (phb) { + ioapic_init_gsi(gsi_state, phb); + } + + if (tcg_enabled()) { + x86_register_ferr_irq(x86ms->gsi[13]); + } + + pc_vga_init(isa_bus, pcmc->pci_enabled ? pcms->pcibus : NULL); + + /* init basic PC hardware */ + pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, + !MACHINE_CLASS(pcmc)->no_floppy, 0x4); + + pc_nic_init(pcmc, isa_bus, pcms->pcibus); + +#ifdef CONFIG_IDE_ISA + if (!pcmc->pci_enabled) { + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + int i; + + ide_drive_get(hd, ARRAY_SIZE(hd)); + for (i = 0; i < MAX_IDE_BUS; i++) { + ISADevice *dev; + char busname[] = "ide.0"; + dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], + ide_irq[i], + hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); + /* + * The ide bus name is ide.0 for the first bus and ide.1 for the + * second one. + */ + busname[4] = '0' + i; + pcms->idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); + } + } +#endif + + if (piix4_pm) { + smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0); + + qdev_connect_gpio_out_named(DEVICE(piix4_pm), "smi-irq", 0, smi_irq); + pcms->smbus = I2C_BUS(qdev_get_child_bus(DEVICE(piix4_pm), "i2c")); + /* TODO: Populate SPD eeprom data. */ + smbus_eeprom_init(pcms->smbus, 8, NULL, 0); + + object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, + TYPE_HOTPLUG_HANDLER, + (Object **)&x86ms->acpi_dev, + object_property_allow_set_link, + OBJ_PROP_LINK_STRONG); + object_property_set_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, + piix4_pm, &error_abort); + } + + if (machine->nvdimms_state->is_enabled) { + nvdimm_init_acpi_state(machine->nvdimms_state, system_io, + x86_nvdimm_acpi_dsmio, + x86ms->fw_cfg, OBJECT(pcms)); + } + +#if defined(CONFIG_IGVM) + /* Apply guest state from IGVM if supplied */ + if (x86ms->igvm) { + if (IGVM_CFG_GET_CLASS(x86ms->igvm) + ->process(x86ms->igvm, machine->cgs, false, &error_fatal) < 0) { + g_assert_not_reached(); + } + } +#endif } #endif From ba5500e0385fad2dd1d4878872695023e5e32e92 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:49 +0100 Subject: [PATCH 0372/1794] hw/i386/pc_piix.c: remove pcmc->pci_enabled dependent initialisation from pc_init_isa() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCI code will never be used for an isapc machine. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-7-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 120 ++++++---------------------------------------- 1 file changed, 15 insertions(+), 105 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 60bf18c680b05..f1b4468d0a117 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -435,19 +435,17 @@ static void pc_set_south_bridge(Object *obj, int value, Error **errp) #ifdef CONFIG_ISAPC static void pc_init_isa(MachineState *machine) { - const char *pci_type = NULL; PCMachineState *pcms = PC_MACHINE(machine); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); X86MachineState *x86ms = X86_MACHINE(machine); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *system_io = get_system_io(); - Object *phb = NULL; ISABus *isa_bus; Object *piix4_pm = NULL; qemu_irq smi_irq; + uint32_t irq; GSIState *gsi_state; MemoryRegion *ram_memory; - MemoryRegion *pci_memory = NULL; MemoryRegion *rom_memory = system_memory; ram_addr_t lowmem; uint64_t hole64_size = 0; @@ -552,39 +550,6 @@ static void pc_init_isa(MachineState *machine) kvmclock_create(pcmc->kvmclock_create_always); } - if (pcmc->pci_enabled) { - pci_memory = g_new(MemoryRegion, 1); - memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); - rom_memory = pci_memory; - - phb = OBJECT(qdev_new(TYPE_I440FX_PCI_HOST_BRIDGE)); - object_property_add_child(OBJECT(machine), "i440fx", phb); - object_property_set_link(phb, PCI_HOST_PROP_RAM_MEM, - OBJECT(ram_memory), &error_fatal); - object_property_set_link(phb, PCI_HOST_PROP_PCI_MEM, - OBJECT(pci_memory), &error_fatal); - object_property_set_link(phb, PCI_HOST_PROP_SYSTEM_MEM, - OBJECT(system_memory), &error_fatal); - object_property_set_link(phb, PCI_HOST_PROP_IO_MEM, - OBJECT(system_io), &error_fatal); - object_property_set_uint(phb, PCI_HOST_BELOW_4G_MEM_SIZE, - x86ms->below_4g_mem_size, &error_fatal); - object_property_set_uint(phb, PCI_HOST_ABOVE_4G_MEM_SIZE, - x86ms->above_4g_mem_size, &error_fatal); - object_property_set_str(phb, I440FX_HOST_PROP_PCI_TYPE, pci_type, - &error_fatal); - sysbus_realize_and_unref(SYS_BUS_DEVICE(phb), &error_fatal); - - pcms->pcibus = PCI_BUS(qdev_get_child_bus(DEVICE(phb), "pci.0")); - pci_bus_map_irqs(pcms->pcibus, - xen_enabled() ? xen_pci_slot_get_pirq - : pc_pci_slot_get_pirq); - - hole64_size = object_property_get_uint(phb, - PCI_HOST_PROP_PCI_HOLE64_SIZE, - &error_abort); - } - /* allocate ram and load rom/bios */ if (!xen_enabled()) { pc_memory_init(pcms, system_memory, rom_memory, hole64_size); @@ -599,92 +564,37 @@ static void pc_init_isa(MachineState *machine) } } - gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); - - if (pcmc->pci_enabled) { - PCIDevice *pci_dev; - DeviceState *dev; - size_t i; - - pci_dev = pci_new_multifunction(-1, pcms->south_bridge); - object_property_set_bool(OBJECT(pci_dev), "has-usb", - machine_usb(machine), &error_abort); - object_property_set_bool(OBJECT(pci_dev), "has-acpi", - x86_machine_is_acpi_enabled(x86ms), - &error_abort); - object_property_set_bool(OBJECT(pci_dev), "has-pic", false, - &error_abort); - object_property_set_bool(OBJECT(pci_dev), "has-pit", false, - &error_abort); - qdev_prop_set_uint32(DEVICE(pci_dev), "smb_io_base", 0xb100); - object_property_set_bool(OBJECT(pci_dev), "smm-enabled", - x86_machine_is_smm_enabled(x86ms), - &error_abort); - dev = DEVICE(pci_dev); - for (i = 0; i < ISA_NUM_IRQS; i++) { - qdev_connect_gpio_out_named(dev, "isa-irqs", i, x86ms->gsi[i]); - } - pci_realize_and_unref(pci_dev, pcms->pcibus, &error_fatal); - - if (xen_enabled()) { - pci_device_set_intx_routing_notifier( - pci_dev, piix_intx_routing_notifier_xen); - - /* - * Xen supports additional interrupt routes from the PCI devices to - * the IOAPIC: the four pins of each PCI device on the bus are also - * connected to the IOAPIC directly. - * These additional routes can be discovered through ACPI. - */ - pci_bus_irqs(pcms->pcibus, xen_intx_set_irq, pci_dev, - XEN_IOAPIC_NUM_PIRQS); - } - - isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci_dev), "isa.0")); - x86ms->rtc = ISA_DEVICE(object_resolve_path_component(OBJECT(pci_dev), - "rtc")); - piix4_pm = object_resolve_path_component(OBJECT(pci_dev), "pm"); - dev = DEVICE(object_resolve_path_component(OBJECT(pci_dev), "ide")); - pci_ide_create_devs(PCI_DEVICE(dev)); - pcms->idebus[0] = qdev_get_child_bus(dev, "ide.0"); - pcms->idebus[1] = qdev_get_child_bus(dev, "ide.1"); - } else { - uint32_t irq; + gsi_state = pc_gsi_create(&x86ms->gsi, false); - isa_bus = isa_bus_new(NULL, system_memory, system_io, - &error_abort); - isa_bus_register_input_irqs(isa_bus, x86ms->gsi); + isa_bus = isa_bus_new(NULL, system_memory, system_io, + &error_abort); + isa_bus_register_input_irqs(isa_bus, x86ms->gsi); - x86ms->rtc = isa_new(TYPE_MC146818_RTC); - qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); - isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); - irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", - &error_fatal); - isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); + x86ms->rtc = isa_new(TYPE_MC146818_RTC); + qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); + isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); + irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", + &error_fatal); + isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); - i8257_dma_init(OBJECT(machine), isa_bus, 0); - pcms->hpet_enabled = false; - } + i8257_dma_init(OBJECT(machine), isa_bus, 0); + pcms->hpet_enabled = false; if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { pc_i8259_create(isa_bus, gsi_state->i8259_irq); } - if (phb) { - ioapic_init_gsi(gsi_state, phb); - } - if (tcg_enabled()) { x86_register_ferr_irq(x86ms->gsi[13]); } - pc_vga_init(isa_bus, pcmc->pci_enabled ? pcms->pcibus : NULL); + pc_vga_init(isa_bus, NULL); /* init basic PC hardware */ pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, !MACHINE_CLASS(pcmc)->no_floppy, 0x4); - pc_nic_init(pcmc, isa_bus, pcms->pcibus); + pc_nic_init(pcmc, isa_bus, NULL); #ifdef CONFIG_IDE_ISA if (!pcmc->pci_enabled) { From dc58530f0a58fe09862026ab6c26c68c00f4d535 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:50 +0100 Subject: [PATCH 0373/1794] hw/i386/pc_piix.c: remove igvm initialisation from pc_init_isa() According to the QEMU documentation igvm is only supported for the pc and q35 machines so remove igvm support from the isapc machine. Signed-off-by: Mark Cave-Ayland Link: https://lore.kernel.org/r/20250828111057.468712-8-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index f1b4468d0a117..5ae265bd538d1 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -640,16 +640,6 @@ static void pc_init_isa(MachineState *machine) x86_nvdimm_acpi_dsmio, x86ms->fw_cfg, OBJECT(pcms)); } - -#if defined(CONFIG_IGVM) - /* Apply guest state from IGVM if supplied */ - if (x86ms->igvm) { - if (IGVM_CFG_GET_CLASS(x86ms->igvm) - ->process(x86ms->igvm, machine->cgs, false, &error_fatal) < 0) { - g_assert_not_reached(); - } - } -#endif } #endif From ae4199af92d38fffd0d1fb9f02eaeb0632eff6df Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:51 +0100 Subject: [PATCH 0374/1794] hw/i386/pc_piix.c: remove SMI and piix4_pm initialisation from pc_init_isa() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are based upon the PIIX4 PCI chipset and so can never be used on an isapc machine. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-9-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 5ae265bd538d1..57b02da5a82a4 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -441,8 +441,6 @@ static void pc_init_isa(MachineState *machine) MemoryRegion *system_memory = get_system_memory(); MemoryRegion *system_io = get_system_io(); ISABus *isa_bus; - Object *piix4_pm = NULL; - qemu_irq smi_irq; uint32_t irq; GSIState *gsi_state; MemoryRegion *ram_memory; @@ -618,23 +616,6 @@ static void pc_init_isa(MachineState *machine) } #endif - if (piix4_pm) { - smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0); - - qdev_connect_gpio_out_named(DEVICE(piix4_pm), "smi-irq", 0, smi_irq); - pcms->smbus = I2C_BUS(qdev_get_child_bus(DEVICE(piix4_pm), "i2c")); - /* TODO: Populate SPD eeprom data. */ - smbus_eeprom_init(pcms->smbus, 8, NULL, 0); - - object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, - TYPE_HOTPLUG_HANDLER, - (Object **)&x86ms->acpi_dev, - object_property_allow_set_link, - OBJ_PROP_LINK_STRONG); - object_property_set_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, - piix4_pm, &error_abort); - } - if (machine->nvdimms_state->is_enabled) { nvdimm_init_acpi_state(machine->nvdimms_state, system_io, x86_nvdimm_acpi_dsmio, From d7916f6d5ec346d05ef63f5419d97a4d9f7d0a75 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:52 +0100 Subject: [PATCH 0375/1794] hw/i386/pc_piix.c: remove SGX initialisation from pc_init_isa() The Intel SGX instructions only exist on recent CPUs and so would never be available on a CPU from the pre-PCI era. Signed-off-by: Mark Cave-Ayland Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-10-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 57b02da5a82a4..9a2eee8ab0aa9 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -541,7 +541,6 @@ static void pc_init_isa(MachineState *machine) } } - pc_machine_init_sgx_epc(pcms); x86_cpus_init(x86ms, pcmc->default_cpu_version); if (kvm_enabled()) { From 62f8d562bb86cd7e1a0ce06f191c402a1eeba309 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:53 +0100 Subject: [PATCH 0376/1794] hw/i386/pc_piix.c: remove nvdimm initialisation from pc_init_isa() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NVDIMMs cannot be used by PCs from a pre-PCI era. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-11-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 9a2eee8ab0aa9..daf63a326b62e 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -614,12 +614,6 @@ static void pc_init_isa(MachineState *machine) } } #endif - - if (machine->nvdimms_state->is_enabled) { - nvdimm_init_acpi_state(machine->nvdimms_state, system_io, - x86_nvdimm_acpi_dsmio, - x86ms->fw_cfg, OBJECT(pcms)); - } } #endif From f2096fa151cbdf6cd169a6f0be9c5ccb5cd10466 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:54 +0100 Subject: [PATCH 0377/1794] hw/i386/pc_piix.c: simplify RAM size logic in pc_init_isa() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All isapc machines must have 32-bit CPUs and so the RAM split logic can be hardcoded accordingly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250828111057.468712-12-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 58 ++++------------------------------------------- 1 file changed, 4 insertions(+), 54 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index daf63a326b62e..0bc033943ca91 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -445,7 +445,6 @@ static void pc_init_isa(MachineState *machine) GSIState *gsi_state; MemoryRegion *ram_memory; MemoryRegion *rom_memory = system_memory; - ram_addr_t lowmem; uint64_t hole64_size = 0; /* @@ -480,65 +479,16 @@ static void pc_init_isa(MachineState *machine) } /* - * Calculate ram split, for memory below and above 4G. It's a bit - * complicated for backward compatibility reasons ... - * - * - Traditional split is 3.5G (lowmem = 0xe0000000). This is the - * default value for max_ram_below_4g now. - * - * - Then, to gigabyte align the memory, we move the split to 3G - * (lowmem = 0xc0000000). But only in case we have to split in - * the first place, i.e. ram_size is larger than (traditional) - * lowmem. And for new machine types (gigabyte_align = true) - * only, for live migration compatibility reasons. - * - * - Next the max-ram-below-4g option was added, which allowed to - * reduce lowmem to a smaller value, to allow a larger PCI I/O - * window below 4G. qemu doesn't enforce gigabyte alignment here, - * but prints a warning. - * - * - Finally max-ram-below-4g got updated to also allow raising lowmem, - * so legacy non-PAE guests can get as much memory as possible in - * the 32bit address space below 4G. - * - * - Note that Xen has its own ram setup code in xen_ram_init(), - * called via xen_hvm_init_pc(). - * - * Examples: - * qemu -M pc-1.7 -m 4G (old default) -> 3584M low, 512M high - * qemu -M pc -m 4G (new default) -> 3072M low, 1024M high - * qemu -M pc,max-ram-below-4g=2G -m 4G -> 2048M low, 2048M high - * qemu -M pc,max-ram-below-4g=4G -m 3968M -> 3968M low (=4G-128M) + * There is no RAM split for the isapc machine */ if (xen_enabled()) { xen_hvm_init_pc(pcms, &ram_memory); } else { ram_memory = machine->ram; - if (!pcms->max_ram_below_4g) { - pcms->max_ram_below_4g = 0xe0000000; /* default: 3.5G */ - } - lowmem = pcms->max_ram_below_4g; - if (machine->ram_size >= pcms->max_ram_below_4g) { - if (pcmc->gigabyte_align) { - if (lowmem > 0xc0000000) { - lowmem = 0xc0000000; - } - if (lowmem & (1 * GiB - 1)) { - warn_report("Large machine and max_ram_below_4g " - "(%" PRIu64 ") not a multiple of 1G; " - "possible bad performance.", - pcms->max_ram_below_4g); - } - } - } - if (machine->ram_size >= lowmem) { - x86ms->above_4g_mem_size = machine->ram_size - lowmem; - x86ms->below_4g_mem_size = lowmem; - } else { - x86ms->above_4g_mem_size = 0; - x86ms->below_4g_mem_size = machine->ram_size; - } + pcms->max_ram_below_4g = 3.5 * GiB; + x86ms->above_4g_mem_size = 0; + x86ms->below_4g_mem_size = machine->ram_size; } x86_cpus_init(x86ms, pcmc->default_cpu_version); From 20fd284ec3c6ec39fb8e062d52f1ee514e89c554 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:55 +0100 Subject: [PATCH 0378/1794] hw/i386/pc_piix.c: hardcode hole64_size to 0 in pc_init_isa() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All isapc machines must have 32-bit CPUs and have no PCI 64-bit hole so it can be hardcoded to 0. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-13-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 0bc033943ca91..66dc4a5186e67 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -445,7 +445,6 @@ static void pc_init_isa(MachineState *machine) GSIState *gsi_state; MemoryRegion *ram_memory; MemoryRegion *rom_memory = system_memory; - uint64_t hole64_size = 0; /* * There is a small chance that someone unintentionally passes "-cpu max" @@ -499,7 +498,7 @@ static void pc_init_isa(MachineState *machine) /* allocate ram and load rom/bios */ if (!xen_enabled()) { - pc_memory_init(pcms, system_memory, rom_memory, hole64_size); + pc_memory_init(pcms, system_memory, rom_memory, 0); } else { assert(machine->ram_size == x86ms->below_4g_mem_size + x86ms->above_4g_mem_size); From b11ad71e32441baff354cdab1993847b61923570 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:56 +0100 Subject: [PATCH 0379/1794] hw/i386/pc_piix.c: remove pc_system_flash_cleanup_unused() from pc_init_isa() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function contains 'assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled)' and so we can safely assume that it should never be used for the isapc machine. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250828111057.468712-14-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 66dc4a5186e67..fb936748bd191 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -503,7 +503,6 @@ static void pc_init_isa(MachineState *machine) assert(machine->ram_size == x86ms->below_4g_mem_size + x86ms->above_4g_mem_size); - pc_system_flash_cleanup_unused(pcms); if (machine->kernel_filename != NULL) { /* For xen HVM direct kernel boot, load linux here */ xen_load_linux(pcms); From 32c73eb73c7c09290a642c0c3ea541ce00350994 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:57 +0100 Subject: [PATCH 0380/1794] hw/i386/pc_piix.c: always initialise ISA IDE drives in pc_init_isa() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By definition an isapc machine must always use ISA IDE drives so ensure that they are always enabled. At the same time also remove the surrounding CONFIG_IDE_ISA define since it will be enabled via the ISAPC Kconfig. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-15-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index fb936748bd191..72ddd9b149d73 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -445,6 +445,8 @@ static void pc_init_isa(MachineState *machine) GSIState *gsi_state; MemoryRegion *ram_memory; MemoryRegion *rom_memory = system_memory; + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + int i; /* * There is a small chance that someone unintentionally passes "-cpu max" @@ -541,27 +543,20 @@ static void pc_init_isa(MachineState *machine) pc_nic_init(pcmc, isa_bus, NULL); -#ifdef CONFIG_IDE_ISA - if (!pcmc->pci_enabled) { - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - int i; - - ide_drive_get(hd, ARRAY_SIZE(hd)); - for (i = 0; i < MAX_IDE_BUS; i++) { - ISADevice *dev; - char busname[] = "ide.0"; - dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], - ide_irq[i], - hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); - /* - * The ide bus name is ide.0 for the first bus and ide.1 for the - * second one. - */ - busname[4] = '0' + i; - pcms->idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); - } + ide_drive_get(hd, ARRAY_SIZE(hd)); + for (i = 0; i < MAX_IDE_BUS; i++) { + ISADevice *dev; + char busname[] = "ide.0"; + dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], + ide_irq[i], + hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); + /* + * The ide bus name is ide.0 for the first bus and ide.1 for the + * second one. + */ + busname[4] = '0' + i; + pcms->idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); } -#endif } #endif From 99d0630a454581eeb2bfabdc0bc15cc07d145876 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:58 +0100 Subject: [PATCH 0381/1794] hw/i386/pc_piix.c: assume pcmc->pci_enabled is always true in pc_init1() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCI is always enabled on the pc-i440fx machine so hardcode the relevant logic in pc_init1(). Add an assert() to ensure that this is always the case at runtime as already done in pc_q35_init(). Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-16-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 194 ++++++++++++++++++---------------------------- 1 file changed, 77 insertions(+), 117 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 72ddd9b149d73..3ea77b2c44f4f 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -125,6 +125,11 @@ static void pc_init1(MachineState *machine, const char *pci_type) MemoryRegion *rom_memory = system_memory; ram_addr_t lowmem; uint64_t hole64_size = 0; + PCIDevice *pci_dev; + DeviceState *dev; + size_t i; + + assert(pcmc->pci_enabled); /* * Calculate ram split, for memory below and above 4G. It's a bit @@ -195,38 +200,36 @@ static void pc_init1(MachineState *machine, const char *pci_type) kvmclock_create(pcmc->kvmclock_create_always); } - if (pcmc->pci_enabled) { - pci_memory = g_new(MemoryRegion, 1); - memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); - rom_memory = pci_memory; - - phb = OBJECT(qdev_new(TYPE_I440FX_PCI_HOST_BRIDGE)); - object_property_add_child(OBJECT(machine), "i440fx", phb); - object_property_set_link(phb, PCI_HOST_PROP_RAM_MEM, - OBJECT(ram_memory), &error_fatal); - object_property_set_link(phb, PCI_HOST_PROP_PCI_MEM, - OBJECT(pci_memory), &error_fatal); - object_property_set_link(phb, PCI_HOST_PROP_SYSTEM_MEM, - OBJECT(system_memory), &error_fatal); - object_property_set_link(phb, PCI_HOST_PROP_IO_MEM, - OBJECT(system_io), &error_fatal); - object_property_set_uint(phb, PCI_HOST_BELOW_4G_MEM_SIZE, - x86ms->below_4g_mem_size, &error_fatal); - object_property_set_uint(phb, PCI_HOST_ABOVE_4G_MEM_SIZE, - x86ms->above_4g_mem_size, &error_fatal); - object_property_set_str(phb, I440FX_HOST_PROP_PCI_TYPE, pci_type, - &error_fatal); - sysbus_realize_and_unref(SYS_BUS_DEVICE(phb), &error_fatal); - - pcms->pcibus = PCI_BUS(qdev_get_child_bus(DEVICE(phb), "pci.0")); - pci_bus_map_irqs(pcms->pcibus, - xen_enabled() ? xen_pci_slot_get_pirq - : pc_pci_slot_get_pirq); - - hole64_size = object_property_get_uint(phb, - PCI_HOST_PROP_PCI_HOLE64_SIZE, - &error_abort); - } + pci_memory = g_new(MemoryRegion, 1); + memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); + rom_memory = pci_memory; + + phb = OBJECT(qdev_new(TYPE_I440FX_PCI_HOST_BRIDGE)); + object_property_add_child(OBJECT(machine), "i440fx", phb); + object_property_set_link(phb, PCI_HOST_PROP_RAM_MEM, + OBJECT(ram_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_PCI_MEM, + OBJECT(pci_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_SYSTEM_MEM, + OBJECT(system_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_IO_MEM, + OBJECT(system_io), &error_fatal); + object_property_set_uint(phb, PCI_HOST_BELOW_4G_MEM_SIZE, + x86ms->below_4g_mem_size, &error_fatal); + object_property_set_uint(phb, PCI_HOST_ABOVE_4G_MEM_SIZE, + x86ms->above_4g_mem_size, &error_fatal); + object_property_set_str(phb, I440FX_HOST_PROP_PCI_TYPE, pci_type, + &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(phb), &error_fatal); + + pcms->pcibus = PCI_BUS(qdev_get_child_bus(DEVICE(phb), "pci.0")); + pci_bus_map_irqs(pcms->pcibus, + xen_enabled() ? xen_pci_slot_get_pirq + : pc_pci_slot_get_pirq); + + hole64_size = object_property_get_uint(phb, + PCI_HOST_PROP_PCI_HOLE64_SIZE, + &error_abort); /* allocate ram and load rom/bios */ if (!xen_enabled()) { @@ -242,72 +245,51 @@ static void pc_init1(MachineState *machine, const char *pci_type) } } - gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); - - if (pcmc->pci_enabled) { - PCIDevice *pci_dev; - DeviceState *dev; - size_t i; - - pci_dev = pci_new_multifunction(-1, pcms->south_bridge); - object_property_set_bool(OBJECT(pci_dev), "has-usb", - machine_usb(machine), &error_abort); - object_property_set_bool(OBJECT(pci_dev), "has-acpi", - x86_machine_is_acpi_enabled(x86ms), - &error_abort); - object_property_set_bool(OBJECT(pci_dev), "has-pic", false, - &error_abort); - object_property_set_bool(OBJECT(pci_dev), "has-pit", false, - &error_abort); - qdev_prop_set_uint32(DEVICE(pci_dev), "smb_io_base", 0xb100); - object_property_set_bool(OBJECT(pci_dev), "smm-enabled", - x86_machine_is_smm_enabled(x86ms), - &error_abort); - dev = DEVICE(pci_dev); - for (i = 0; i < ISA_NUM_IRQS; i++) { - qdev_connect_gpio_out_named(dev, "isa-irqs", i, x86ms->gsi[i]); - } - pci_realize_and_unref(pci_dev, pcms->pcibus, &error_fatal); - - if (xen_enabled()) { - pci_device_set_intx_routing_notifier( - pci_dev, piix_intx_routing_notifier_xen); - - /* - * Xen supports additional interrupt routes from the PCI devices to - * the IOAPIC: the four pins of each PCI device on the bus are also - * connected to the IOAPIC directly. - * These additional routes can be discovered through ACPI. - */ - pci_bus_irqs(pcms->pcibus, xen_intx_set_irq, pci_dev, - XEN_IOAPIC_NUM_PIRQS); - } + gsi_state = pc_gsi_create(&x86ms->gsi, true); + + pci_dev = pci_new_multifunction(-1, pcms->south_bridge); + object_property_set_bool(OBJECT(pci_dev), "has-usb", + machine_usb(machine), &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-acpi", + x86_machine_is_acpi_enabled(x86ms), + &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-pic", false, + &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-pit", false, + &error_abort); + qdev_prop_set_uint32(DEVICE(pci_dev), "smb_io_base", 0xb100); + object_property_set_bool(OBJECT(pci_dev), "smm-enabled", + x86_machine_is_smm_enabled(x86ms), + &error_abort); + dev = DEVICE(pci_dev); + for (i = 0; i < ISA_NUM_IRQS; i++) { + qdev_connect_gpio_out_named(dev, "isa-irqs", i, x86ms->gsi[i]); + } + pci_realize_and_unref(pci_dev, pcms->pcibus, &error_fatal); - isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci_dev), "isa.0")); - x86ms->rtc = ISA_DEVICE(object_resolve_path_component(OBJECT(pci_dev), - "rtc")); - piix4_pm = object_resolve_path_component(OBJECT(pci_dev), "pm"); - dev = DEVICE(object_resolve_path_component(OBJECT(pci_dev), "ide")); - pci_ide_create_devs(PCI_DEVICE(dev)); - pcms->idebus[0] = qdev_get_child_bus(dev, "ide.0"); - pcms->idebus[1] = qdev_get_child_bus(dev, "ide.1"); - } else { - uint32_t irq; + if (xen_enabled()) { + pci_device_set_intx_routing_notifier( + pci_dev, piix_intx_routing_notifier_xen); - isa_bus = isa_bus_new(NULL, system_memory, system_io, - &error_abort); - isa_bus_register_input_irqs(isa_bus, x86ms->gsi); + /* + * Xen supports additional interrupt routes from the PCI devices to + * the IOAPIC: the four pins of each PCI device on the bus are also + * connected to the IOAPIC directly. + * These additional routes can be discovered through ACPI. + */ + pci_bus_irqs(pcms->pcibus, xen_intx_set_irq, pci_dev, + XEN_IOAPIC_NUM_PIRQS); + } - x86ms->rtc = isa_new(TYPE_MC146818_RTC); - qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); - isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); - irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", - &error_fatal); - isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); + isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci_dev), "isa.0")); + x86ms->rtc = ISA_DEVICE(object_resolve_path_component(OBJECT(pci_dev), + "rtc")); + piix4_pm = object_resolve_path_component(OBJECT(pci_dev), "pm"); + dev = DEVICE(object_resolve_path_component(OBJECT(pci_dev), "ide")); + pci_ide_create_devs(PCI_DEVICE(dev)); + pcms->idebus[0] = qdev_get_child_bus(dev, "ide.0"); + pcms->idebus[1] = qdev_get_child_bus(dev, "ide.1"); - i8257_dma_init(OBJECT(machine), isa_bus, 0); - pcms->hpet_enabled = false; - } if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { pc_i8259_create(isa_bus, gsi_state->i8259_irq); @@ -321,7 +303,7 @@ static void pc_init1(MachineState *machine, const char *pci_type) x86_register_ferr_irq(x86ms->gsi[13]); } - pc_vga_init(isa_bus, pcmc->pci_enabled ? pcms->pcibus : NULL); + pc_vga_init(isa_bus, pcms->pcibus); /* init basic PC hardware */ pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, @@ -329,28 +311,6 @@ static void pc_init1(MachineState *machine, const char *pci_type) pc_nic_init(pcmc, isa_bus, pcms->pcibus); -#ifdef CONFIG_IDE_ISA - if (!pcmc->pci_enabled) { - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - int i; - - ide_drive_get(hd, ARRAY_SIZE(hd)); - for (i = 0; i < MAX_IDE_BUS; i++) { - ISADevice *dev; - char busname[] = "ide.0"; - dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], - ide_irq[i], - hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); - /* - * The ide bus name is ide.0 for the first bus and ide.1 for the - * second one. - */ - busname[4] = '0' + i; - pcms->idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); - } - } -#endif - if (piix4_pm) { smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0); From 3113e7db1d5d683e91cdbb4796e1b154cdda73bf Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:59 +0100 Subject: [PATCH 0382/1794] hw/i386: move isapc machine to separate isapc.c file Now that pc_init_isa() is independent of any PCI initialisation, move it into a separate isapc.c file including the ISA IDE variables which are now no longer needed for the pc-i440fx machine. This enables us to finally fix the dependency of ISAPC on I440FX in hw/i386/Kconfig. Note that as part of the move to a separate file we can see that the licence text is a verbatim copy of the MIT licence. The text originates from commit 1df912cf9e ("VL license of the day is MIT/BSD") so we can be sure that this was the original intent. As a consequence we can update the file header to use a SPDX tag as per the current project contribution guidelines. Signed-off-by: Mark Cave-Ayland Reviewed-by: Bernhard Beschow Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-17-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/Kconfig | 3 - hw/i386/isapc.c | 190 ++++++++++++++++++++++++++++++++++++++++++++ hw/i386/meson.build | 1 + hw/i386/pc_piix.c | 172 --------------------------------------- 4 files changed, 191 insertions(+), 175 deletions(-) create mode 100644 hw/i386/isapc.c diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 3a0e2b8ebbb6f..6a0ab54bea4ab 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -96,9 +96,6 @@ config ISAPC select ISA_BUS select PC select IDE_ISA - # FIXME: it is in the same file as i440fx, and does not compile - # if separated - depends on I440FX config Q35 bool diff --git a/hw/i386/isapc.c b/hw/i386/isapc.c new file mode 100644 index 0000000000000..300d64b7ad2c5 --- /dev/null +++ b/hw/i386/isapc.c @@ -0,0 +1,190 @@ +/* + * QEMU PC System Emulator + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" + +#include "qemu/units.h" +#include "qemu/error-report.h" +#include "hw/char/parallel-isa.h" +#include "hw/dma/i8257.h" +#include "hw/i386/pc.h" +#include "hw/ide/isa.h" +#include "hw/ide/ide-bus.h" +#include "system/kvm.h" +#include "hw/i386/kvm/clock.h" +#include "hw/xen/xen-x86.h" +#include "system/xen.h" +#include "hw/rtc/mc146818rtc.h" +#include "target/i386/cpu.h" + +static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 }; +static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; +static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; + + +static void pc_init_isa(MachineState *machine) +{ + PCMachineState *pcms = PC_MACHINE(machine); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + X86MachineState *x86ms = X86_MACHINE(machine); + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *system_io = get_system_io(); + ISABus *isa_bus; + uint32_t irq; + GSIState *gsi_state; + MemoryRegion *ram_memory; + MemoryRegion *rom_memory = system_memory; + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + int i; + + /* + * There is a small chance that someone unintentionally passes "-cpu max" + * for the isapc machine, which will provide a much more modern 32-bit + * CPU than would be expected for an ISA-era PC. If the "max" cpu type has + * been specified, choose the "best" 32-bit cpu possible which we consider + * be the pentium3 (deliberately choosing an Intel CPU given that the + * default 486 CPU for the isapc machine is also an Intel CPU). + */ + if (!strcmp(machine->cpu_type, X86_CPU_TYPE_NAME("max"))) { + machine->cpu_type = X86_CPU_TYPE_NAME("pentium3"); + warn_report("-cpu max is invalid for isapc machine, using pentium3"); + } + + /* + * Similarly if someone unintentionally passes "-cpu host" for the isapc + * machine then display a warning and also switch to the "best" 32-bit + * cpu possible which we consider to be the pentium3. This is because any + * host CPU will already be modern than this, but it also ensures any + * newer CPU flags/features are filtered out for older guests. + */ + if (!strcmp(machine->cpu_type, X86_CPU_TYPE_NAME("host"))) { + machine->cpu_type = X86_CPU_TYPE_NAME("pentium3"); + warn_report("-cpu host is invalid for isapc machine, using pentium3"); + } + + if (machine->ram_size > 3.5 * GiB) { + error_report("Too much memory for this machine: %" PRId64 " MiB, " + "maximum 3584 MiB", machine->ram_size / MiB); + exit(1); + } + + /* + * There is no RAM split for the isapc machine + */ + if (xen_enabled()) { + xen_hvm_init_pc(pcms, &ram_memory); + } else { + ram_memory = machine->ram; + + pcms->max_ram_below_4g = 3.5 * GiB; + x86ms->above_4g_mem_size = 0; + x86ms->below_4g_mem_size = machine->ram_size; + } + + x86_cpus_init(x86ms, pcmc->default_cpu_version); + + if (kvm_enabled()) { + kvmclock_create(pcmc->kvmclock_create_always); + } + + /* allocate ram and load rom/bios */ + if (!xen_enabled()) { + pc_memory_init(pcms, system_memory, rom_memory, 0); + } else { + assert(machine->ram_size == x86ms->below_4g_mem_size + + x86ms->above_4g_mem_size); + + if (machine->kernel_filename != NULL) { + /* For xen HVM direct kernel boot, load linux here */ + xen_load_linux(pcms); + } + } + + gsi_state = pc_gsi_create(&x86ms->gsi, false); + + isa_bus = isa_bus_new(NULL, system_memory, system_io, + &error_abort); + isa_bus_register_input_irqs(isa_bus, x86ms->gsi); + + x86ms->rtc = isa_new(TYPE_MC146818_RTC); + qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); + isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); + irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", + &error_fatal); + isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); + + i8257_dma_init(OBJECT(machine), isa_bus, 0); + pcms->hpet_enabled = false; + + if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { + pc_i8259_create(isa_bus, gsi_state->i8259_irq); + } + + if (tcg_enabled()) { + x86_register_ferr_irq(x86ms->gsi[13]); + } + + pc_vga_init(isa_bus, NULL); + + /* init basic PC hardware */ + pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, + !MACHINE_CLASS(pcmc)->no_floppy, 0x4); + + pc_nic_init(pcmc, isa_bus, NULL); + + ide_drive_get(hd, ARRAY_SIZE(hd)); + for (i = 0; i < MAX_IDE_BUS; i++) { + ISADevice *dev; + char busname[] = "ide.0"; + dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], + ide_irq[i], + hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); + /* + * The ide bus name is ide.0 for the first bus and ide.1 for the + * second one. + */ + busname[4] = '0' + i; + pcms->idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); + } +} + +static void isapc_machine_options(MachineClass *m) +{ + static const char * const valid_cpu_types[] = { + X86_CPU_TYPE_NAME("486"), + X86_CPU_TYPE_NAME("athlon"), + X86_CPU_TYPE_NAME("kvm32"), + X86_CPU_TYPE_NAME("pentium"), + X86_CPU_TYPE_NAME("pentium2"), + X86_CPU_TYPE_NAME("pentium3"), + X86_CPU_TYPE_NAME("qemu32"), + X86_CPU_TYPE_NAME("max"), + X86_CPU_TYPE_NAME("host"), + NULL + }; + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + + m->desc = "ISA-only PC"; + m->max_cpus = 1; + m->option_rom_has_mr = true; + m->rom_file_has_mr = false; + pcmc->pci_enabled = false; + pcmc->has_acpi_build = false; + pcmc->smbios_defaults = false; + pcmc->gigabyte_align = false; + pcmc->smbios_legacy_mode = true; + pcmc->has_reserved_memory = false; + m->default_nic = "ne2k_isa"; + m->default_cpu_type = X86_CPU_TYPE_NAME("486"); + m->valid_cpu_types = valid_cpu_types; + m->no_floppy = !module_object_class_by_name(TYPE_ISA_FDC); + m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); +} + +DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa, + isapc_machine_options); diff --git a/hw/i386/meson.build b/hw/i386/meson.build index 7896f348cff80..436b3ce52d648 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -14,6 +14,7 @@ i386_ss.add(when: 'CONFIG_X86_IOMMU', if_true: files('x86-iommu.c'), i386_ss.add(when: 'CONFIG_AMD_IOMMU', if_true: files('amd_iommu.c'), if_false: files('amd_iommu-stub.c')) i386_ss.add(when: 'CONFIG_I440FX', if_true: files('pc_piix.c')) +i386_ss.add(when: 'CONFIG_ISAPC', if_true: files('isapc.c')) i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('x86-common.c', 'microvm.c', 'acpi-microvm.c', 'microvm-dt.c')) i386_ss.add(when: 'CONFIG_NITRO_ENCLAVE', if_true: files('nitro_enclave.c')) i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c')) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 3ea77b2c44f4f..988c9edc32cd0 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -71,12 +71,6 @@ #define XEN_IOAPIC_NUM_PIRQS 128ULL -#ifdef CONFIG_IDE_ISA -static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 }; -static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; -static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; -#endif - static GlobalProperty pc_piix_compat_defaults[] = { { TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" }, { TYPE_VFIO_PCI_NOHOTPLUG, "use-legacy-x86-rom", "true" }, @@ -392,134 +386,6 @@ static void pc_set_south_bridge(Object *obj, int value, Error **errp) pcms->south_bridge = PCSouthBridgeOption_lookup.array[value]; } -#ifdef CONFIG_ISAPC -static void pc_init_isa(MachineState *machine) -{ - PCMachineState *pcms = PC_MACHINE(machine); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); - X86MachineState *x86ms = X86_MACHINE(machine); - MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *system_io = get_system_io(); - ISABus *isa_bus; - uint32_t irq; - GSIState *gsi_state; - MemoryRegion *ram_memory; - MemoryRegion *rom_memory = system_memory; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - int i; - - /* - * There is a small chance that someone unintentionally passes "-cpu max" - * for the isapc machine, which will provide a much more modern 32-bit - * CPU than would be expected for an ISA-era PC. If the "max" cpu type has - * been specified, choose the "best" 32-bit cpu possible which we consider - * be the pentium3 (deliberately choosing an Intel CPU given that the - * default 486 CPU for the isapc machine is also an Intel CPU). - */ - if (!strcmp(machine->cpu_type, X86_CPU_TYPE_NAME("max"))) { - machine->cpu_type = X86_CPU_TYPE_NAME("pentium3"); - warn_report("-cpu max is invalid for isapc machine, using pentium3"); - } - - /* - * Similarly if someone unintentionally passes "-cpu host" for the isapc - * machine then display a warning and also switch to the "best" 32-bit - * cpu possible which we consider to be the pentium3. This is because any - * host CPU will already be modern than this, but it also ensures any - * newer CPU flags/features are filtered out for older guests. - */ - if (!strcmp(machine->cpu_type, X86_CPU_TYPE_NAME("host"))) { - machine->cpu_type = X86_CPU_TYPE_NAME("pentium3"); - warn_report("-cpu host is invalid for isapc machine, using pentium3"); - } - - if (machine->ram_size > 3.5 * GiB) { - error_report("Too much memory for this machine: %" PRId64 " MiB, " - "maximum 3584 MiB", machine->ram_size / MiB); - exit(1); - } - - /* - * There is no RAM split for the isapc machine - */ - if (xen_enabled()) { - xen_hvm_init_pc(pcms, &ram_memory); - } else { - ram_memory = machine->ram; - - pcms->max_ram_below_4g = 3.5 * GiB; - x86ms->above_4g_mem_size = 0; - x86ms->below_4g_mem_size = machine->ram_size; - } - - x86_cpus_init(x86ms, pcmc->default_cpu_version); - - if (kvm_enabled()) { - kvmclock_create(pcmc->kvmclock_create_always); - } - - /* allocate ram and load rom/bios */ - if (!xen_enabled()) { - pc_memory_init(pcms, system_memory, rom_memory, 0); - } else { - assert(machine->ram_size == x86ms->below_4g_mem_size + - x86ms->above_4g_mem_size); - - if (machine->kernel_filename != NULL) { - /* For xen HVM direct kernel boot, load linux here */ - xen_load_linux(pcms); - } - } - - gsi_state = pc_gsi_create(&x86ms->gsi, false); - - isa_bus = isa_bus_new(NULL, system_memory, system_io, - &error_abort); - isa_bus_register_input_irqs(isa_bus, x86ms->gsi); - - x86ms->rtc = isa_new(TYPE_MC146818_RTC); - qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); - isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); - irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", - &error_fatal); - isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); - - i8257_dma_init(OBJECT(machine), isa_bus, 0); - pcms->hpet_enabled = false; - - if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { - pc_i8259_create(isa_bus, gsi_state->i8259_irq); - } - - if (tcg_enabled()) { - x86_register_ferr_irq(x86ms->gsi[13]); - } - - pc_vga_init(isa_bus, NULL); - - /* init basic PC hardware */ - pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, - !MACHINE_CLASS(pcmc)->no_floppy, 0x4); - - pc_nic_init(pcmc, isa_bus, NULL); - - ide_drive_get(hd, ARRAY_SIZE(hd)); - for (i = 0; i < MAX_IDE_BUS; i++) { - ISADevice *dev; - char busname[] = "ide.0"; - dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], - ide_irq[i], - hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); - /* - * The ide bus name is ide.0 for the first bus and ide.1 for the - * second one. - */ - busname[4] = '0' + i; - pcms->idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); - } -} -#endif - #ifdef CONFIG_XEN static void pc_xen_hvm_init(MachineState *machine) { @@ -887,44 +753,6 @@ static void pc_i440fx_machine_2_6_options(MachineClass *m) DEFINE_I440FX_MACHINE(2, 6); -#ifdef CONFIG_ISAPC -static void isapc_machine_options(MachineClass *m) -{ - static const char * const valid_cpu_types[] = { - X86_CPU_TYPE_NAME("486"), - X86_CPU_TYPE_NAME("athlon"), - X86_CPU_TYPE_NAME("kvm32"), - X86_CPU_TYPE_NAME("pentium"), - X86_CPU_TYPE_NAME("pentium2"), - X86_CPU_TYPE_NAME("pentium3"), - X86_CPU_TYPE_NAME("qemu32"), - X86_CPU_TYPE_NAME("max"), - X86_CPU_TYPE_NAME("host"), - NULL - }; - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - m->desc = "ISA-only PC"; - m->max_cpus = 1; - m->option_rom_has_mr = true; - m->rom_file_has_mr = false; - pcmc->pci_enabled = false; - pcmc->has_acpi_build = false; - pcmc->smbios_defaults = false; - pcmc->gigabyte_align = false; - pcmc->smbios_legacy_mode = true; - pcmc->has_reserved_memory = false; - m->default_nic = "ne2k_isa"; - m->default_cpu_type = X86_CPU_TYPE_NAME("486"); - m->valid_cpu_types = valid_cpu_types; - m->no_floppy = !module_object_class_by_name(TYPE_ISA_FDC); - m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); -} - -DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa, - isapc_machine_options); -#endif - #ifdef CONFIG_XEN static void xenfv_machine_4_2_options(MachineClass *m) { From d773f9689109eb00ba6627c2264af949c66897fb Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:10:00 +0100 Subject: [PATCH 0383/1794] hw/i386/pc_piix.c: remove unused headers after isapc machine split MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The headers for isapc-only devices can be removed from pc_piix.c since they are no longer used by the i440fx-pc machine. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-18-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 988c9edc32cd0..627de09c7041b 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -27,19 +27,16 @@ #include "qemu/units.h" #include "hw/char/parallel-isa.h" -#include "hw/dma/i8257.h" #include "hw/i386/x86.h" #include "hw/i386/pc.h" #include "hw/i386/apic.h" #include "hw/pci-host/i440fx.h" -#include "hw/rtc/mc146818rtc.h" #include "hw/southbridge/piix.h" #include "hw/display/ramfb.h" #include "hw/pci/pci.h" #include "hw/pci/pci_ids.h" #include "hw/usb.h" #include "net/net.h" -#include "hw/ide/isa.h" #include "hw/ide/pci.h" #include "hw/irq.h" #include "system/kvm.h" From 523a64f388a689a1e9bc593ca8768fe1449613db Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:10:01 +0100 Subject: [PATCH 0384/1794] hw/i386/pc_piix.c: replace rom_memory with pci_memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we can guarantee the i440fx-pc machine will always have a PCI bus, any instances of rom_memory can be replaced by pci_memory and rom_memory removed completely. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-19-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 627de09c7041b..7e78b6daa6dd5 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -113,7 +113,6 @@ static void pc_init1(MachineState *machine, const char *pci_type) GSIState *gsi_state; MemoryRegion *ram_memory; MemoryRegion *pci_memory = NULL; - MemoryRegion *rom_memory = system_memory; ram_addr_t lowmem; uint64_t hole64_size = 0; PCIDevice *pci_dev; @@ -193,7 +192,6 @@ static void pc_init1(MachineState *machine, const char *pci_type) pci_memory = g_new(MemoryRegion, 1); memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); - rom_memory = pci_memory; phb = OBJECT(qdev_new(TYPE_I440FX_PCI_HOST_BRIDGE)); object_property_add_child(OBJECT(machine), "i440fx", phb); @@ -224,7 +222,7 @@ static void pc_init1(MachineState *machine, const char *pci_type) /* allocate ram and load rom/bios */ if (!xen_enabled()) { - pc_memory_init(pcms, system_memory, rom_memory, hole64_size); + pc_memory_init(pcms, system_memory, pci_memory, hole64_size); } else { assert(machine->ram_size == x86ms->below_4g_mem_size + x86ms->above_4g_mem_size); From d8701867d12241f53f3b17973e7fd533c764c76a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:10:02 +0100 Subject: [PATCH 0385/1794] hw/i386/isapc.c: replace rom_memory with system_memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we can guarantee the isapc machine will never have a PCI bus, any instances of rom_memory can be replaced by system_memory and rom_memory removed completely. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-20-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/isapc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/i386/isapc.c b/hw/i386/isapc.c index 300d64b7ad2c5..44f4a44672439 100644 --- a/hw/i386/isapc.c +++ b/hw/i386/isapc.c @@ -38,7 +38,6 @@ static void pc_init_isa(MachineState *machine) uint32_t irq; GSIState *gsi_state; MemoryRegion *ram_memory; - MemoryRegion *rom_memory = system_memory; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; int i; @@ -94,7 +93,7 @@ static void pc_init_isa(MachineState *machine) /* allocate ram and load rom/bios */ if (!xen_enabled()) { - pc_memory_init(pcms, system_memory, rom_memory, 0); + pc_memory_init(pcms, system_memory, system_memory, 0); } else { assert(machine->ram_size == x86ms->below_4g_mem_size + x86ms->above_4g_mem_size); From b8217bbaf2bafef1a4f54082a3548613eeef8f2b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 10:46:48 +0200 Subject: [PATCH 0386/1794] user-exec: ensure interrupt_request is not used cpu_interrupt() is not called anymore except by ARM but even there it is dead code; disentangling the various cpregs accessors from user-mode emulation is a work in progress. Signed-off-by: Paolo Bonzini --- accel/tcg/cpu-exec.c | 6 ++++-- accel/tcg/user-exec.c | 4 +--- include/hw/core/cpu.h | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 713bdb2056480..b44dd1e8205b7 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -778,6 +778,9 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, */ qatomic_set_mb(&cpu->neg.icount_decr.u16.high, 0); +#ifdef CONFIG_USER_ONLY + g_assert(!qatomic_read(&cpu->interrupt_request)); +#else if (unlikely(qatomic_read(&cpu->interrupt_request))) { int interrupt_request; bql_lock(); @@ -792,7 +795,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, bql_unlock(); return true; } -#if !defined(CONFIG_USER_ONLY) if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) { /* Do nothing */ } else if (interrupt_request & CPU_INTERRUPT_HALT) { @@ -840,7 +842,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, * reload the 'interrupt_request' value */ interrupt_request = cpu->interrupt_request; } -#endif /* !CONFIG_USER_ONLY */ if (interrupt_request & CPU_INTERRUPT_EXITTB) { cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB; /* ensure that no TB jump will be modified as @@ -851,6 +852,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, /* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */ bql_unlock(); } +#endif /* !CONFIG_USER_ONLY */ /* Finally, check if we need to exit to the main loop. */ if (unlikely(qatomic_read(&cpu->exit_request)) || icount_exit_request(cpu)) { diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index f25d80e2dc2c1..748bfab04a70c 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -48,9 +48,7 @@ __thread uintptr_t helper_retaddr; void cpu_interrupt(CPUState *cpu, int mask) { - g_assert(bql_locked()); - cpu->interrupt_request |= mask; - qatomic_set(&cpu->neg.icount_decr.u16.high, -1); + g_assert_not_reached(); } /* diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 5eaf41a566f37..f73b4357c7bc7 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -423,6 +423,7 @@ struct qemu_work_item; * @created: Indicates whether the CPU thread has been successfully created. * @halt_cond: condition variable sleeping threads can wait on. * @interrupt_request: Indicates a pending interrupt request. + * Only used by system emulation. * @halted: Nonzero if the CPU is in suspended state. * @stop: Indicates a pending stop request. * @stopped: Indicates the CPU has been artificially stopped. From 87511341c30d8c9c77178db16491a0ccacc5d64b Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 21 Aug 2025 17:56:03 +0200 Subject: [PATCH 0387/1794] add cpu_test_interrupt()/cpu_set_interrupt() helpers and use them tree wide The helpers form load-acquire/store-release pair and ensure that appropriate barriers are in place in case checks happen outside of BQL. Use them to replace open-coded checkers/setters across the code, to make sure that barriers are not missed. Helpers also make code a bit more readable. Signed-off-by: Igor Mammedov Reviewed-by: Peter Xu Reviewed-by: Jason J. Herne Link: https://lore.kernel.org/r/20250821155603.2422553-1-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- accel/tcg/cpu-exec.c | 12 +++++----- accel/tcg/tcg-accel-ops.c | 2 +- hw/intc/s390_flic.c | 2 +- hw/openrisc/cputimer.c | 2 +- include/hw/core/cpu.h | 22 +++++++++++++++++++ system/cpus.c | 9 +++++++- target/alpha/cpu.c | 8 +++---- target/arm/cpu.c | 20 ++++++++--------- target/arm/helper.c | 18 +++++++-------- target/arm/hvf/hvf.c | 6 ++--- target/avr/cpu.c | 2 +- target/hppa/cpu.c | 2 +- target/i386/hvf/hvf.c | 4 ++-- target/i386/hvf/x86hvf.c | 21 +++++++++--------- target/i386/kvm/kvm.c | 34 ++++++++++++++--------------- target/i386/nvmm/nvmm-all.c | 24 ++++++++++---------- target/i386/tcg/system/seg_helper.c | 2 +- target/i386/tcg/system/svm_helper.c | 2 +- target/i386/whpx/whpx-all.c | 34 ++++++++++++++--------------- target/loongarch/cpu.c | 2 +- target/m68k/cpu.c | 2 +- target/microblaze/cpu.c | 2 +- target/mips/cpu.c | 6 ++--- target/mips/kvm.c | 2 +- target/openrisc/cpu.c | 3 +-- target/ppc/cpu_init.c | 2 +- target/ppc/kvm.c | 2 +- target/rx/cpu.c | 3 +-- target/rx/helper.c | 2 +- target/s390x/cpu-system.c | 2 +- target/sh4/cpu.c | 2 +- target/sh4/helper.c | 2 +- target/sparc/cpu.c | 2 +- target/sparc/int64_helper.c | 4 ++-- 34 files changed, 145 insertions(+), 119 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index b44dd1e8205b7..96c124aa72018 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -779,9 +779,9 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, qatomic_set_mb(&cpu->neg.icount_decr.u16.high, 0); #ifdef CONFIG_USER_ONLY - g_assert(!qatomic_read(&cpu->interrupt_request)); + assert(!cpu_test_interrupt(cpu, ~0)); #else - if (unlikely(qatomic_read(&cpu->interrupt_request))) { + if (unlikely(cpu_test_interrupt(cpu, ~0))) { int interrupt_request; bql_lock(); interrupt_request = cpu->interrupt_request; @@ -789,7 +789,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, /* Mask out external interrupts for this step. */ interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK; } - if (interrupt_request & CPU_INTERRUPT_DEBUG) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_DEBUG)) { cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG; cpu->exception_index = EXCP_DEBUG; bql_unlock(); @@ -797,7 +797,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, } if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) { /* Do nothing */ - } else if (interrupt_request & CPU_INTERRUPT_HALT) { + } else if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HALT)) { replay_interrupt(); cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; cpu->halted = 1; @@ -807,7 +807,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, } else { const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; - if (interrupt_request & CPU_INTERRUPT_RESET) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_RESET)) { replay_interrupt(); tcg_ops->cpu_exec_reset(cpu); bql_unlock(); @@ -842,7 +842,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, * reload the 'interrupt_request' value */ interrupt_request = cpu->interrupt_request; } - if (interrupt_request & CPU_INTERRUPT_EXITTB) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_EXITTB)) { cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB; /* ensure that no TB jump will be modified as the program flow was changed */ diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 3b0d7d298e61f..9c37266c1e046 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -97,7 +97,7 @@ static void tcg_cpu_reset_hold(CPUState *cpu) /* mask must never be zero, except for A20 change call */ void tcg_handle_interrupt(CPUState *cpu, int mask) { - cpu->interrupt_request |= mask; + cpu_set_interrupt(cpu, mask); /* * If called from iothread context, wake the target cpu in diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 8f4c9fd52e864..1eed5125d17b6 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -190,7 +190,7 @@ static void qemu_s390_flic_notify(uint32_t type) CPU_FOREACH(cs) { S390CPU *cpu = S390_CPU(cs); - cs->interrupt_request |= CPU_INTERRUPT_HARD; + cpu_set_interrupt(cs, CPU_INTERRUPT_HARD); /* ignore CPUs that are not sleeping */ if (s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING && diff --git a/hw/openrisc/cputimer.c b/hw/openrisc/cputimer.c index 6331997d56ba0..51da226fcdeb2 100644 --- a/hw/openrisc/cputimer.c +++ b/hw/openrisc/cputimer.c @@ -105,7 +105,7 @@ static void openrisc_timer_cb(void *opaque) CPUState *cs = CPU(cpu); cpu->env.ttmr |= TTMR_IP; - cs->interrupt_request |= CPU_INTERRUPT_TIMER; + cpu_set_interrupt(cs, CPU_INTERRUPT_TIMER); } switch (cpu->env.ttmr & TTMR_M) { diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index f73b4357c7bc7..b01a0cffd6460 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -943,6 +943,28 @@ CPUState *cpu_by_arch_id(int64_t id); void cpu_interrupt(CPUState *cpu, int mask); +/** + * cpu_test_interrupt: + * @cpu: The CPU to check interrupt(s) on. + * @mask: The interrupts to check. + * + * Checks if any of interrupts in @mask are pending on @cpu. + */ +static inline bool cpu_test_interrupt(CPUState *cpu, int mask) +{ + return qatomic_load_acquire(&cpu->interrupt_request) & mask; +} + +/** + * cpu_set_interrupt: + * @cpu: The CPU to set pending interrupt(s) on. + * @mask: The interrupts to set. + * + * Sets interrupts in @mask as pending on @cpu. Unlike @cpu_interrupt, + * this does not kick the vCPU. + */ +void cpu_set_interrupt(CPUState *cpu, int mask); + /** * cpu_set_pc: * @cpu: The CPU to set the program counter for. diff --git a/system/cpus.c b/system/cpus.c index 256723558d0c8..437848b5eb448 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -254,9 +254,16 @@ int64_t cpus_get_elapsed_ticks(void) return cpu_get_ticks(); } +void cpu_set_interrupt(CPUState *cpu, int mask) +{ + /* Pairs with cpu_test_interrupt(). */ + qatomic_store_release(&cpu->interrupt_request, + cpu->interrupt_request | mask); +} + void generic_handle_interrupt(CPUState *cpu, int mask) { - cpu->interrupt_request |= mask; + cpu_set_interrupt(cpu, mask); if (!qemu_cpu_is_self(cpu)) { qemu_cpu_kick(cpu); diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index bf1787a69ddc8..932cddac055ce 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -86,10 +86,10 @@ static bool alpha_cpu_has_work(CPUState *cs) assume that if a CPU really wants to stay asleep, it will mask interrupts at the chipset level, which will prevent these bits from being set in the first place. */ - return cs->interrupt_request & (CPU_INTERRUPT_HARD - | CPU_INTERRUPT_TIMER - | CPU_INTERRUPT_SMP - | CPU_INTERRUPT_MCHK); + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD + | CPU_INTERRUPT_TIMER + | CPU_INTERRUPT_SMP + | CPU_INTERRUPT_MCHK); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/arm/cpu.c b/target/arm/cpu.c index e2b2337399cf5..a29c3facbfd42 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -142,11 +142,11 @@ static bool arm_cpu_has_work(CPUState *cs) ARMCPU *cpu = ARM_CPU(cs); return (cpu->power_state != PSCI_OFF) - && cs->interrupt_request & - (CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD - | CPU_INTERRUPT_NMI | CPU_INTERRUPT_VINMI | CPU_INTERRUPT_VFNMI - | CPU_INTERRUPT_VFIQ | CPU_INTERRUPT_VIRQ | CPU_INTERRUPT_VSERR - | CPU_INTERRUPT_EXITTB); + && cpu_test_interrupt(cs, + CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD + | CPU_INTERRUPT_NMI | CPU_INTERRUPT_VINMI | CPU_INTERRUPT_VFNMI + | CPU_INTERRUPT_VFIQ | CPU_INTERRUPT_VIRQ | CPU_INTERRUPT_VSERR + | CPU_INTERRUPT_EXITTB); } #endif /* !CONFIG_USER_ONLY */ @@ -958,7 +958,7 @@ void arm_cpu_update_virq(ARMCPU *cpu) !(arm_hcrx_el2_eff(env) & HCRX_VINMI)) || (env->irq_line_state & CPU_INTERRUPT_VIRQ); - if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VIRQ) != 0)) { + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VIRQ)) { if (new_state) { cpu_interrupt(cs, CPU_INTERRUPT_VIRQ); } else { @@ -980,7 +980,7 @@ void arm_cpu_update_vfiq(ARMCPU *cpu) !(arm_hcrx_el2_eff(env) & HCRX_VFNMI)) || (env->irq_line_state & CPU_INTERRUPT_VFIQ); - if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VFIQ) != 0)) { + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VFIQ)) { if (new_state) { cpu_interrupt(cs, CPU_INTERRUPT_VFIQ); } else { @@ -1002,7 +1002,7 @@ void arm_cpu_update_vinmi(ARMCPU *cpu) (arm_hcrx_el2_eff(env) & HCRX_VINMI)) || (env->irq_line_state & CPU_INTERRUPT_VINMI); - if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VINMI) != 0)) { + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VINMI)) { if (new_state) { cpu_interrupt(cs, CPU_INTERRUPT_VINMI); } else { @@ -1022,7 +1022,7 @@ void arm_cpu_update_vfnmi(ARMCPU *cpu) bool new_state = (arm_hcr_el2_eff(env) & HCR_VF) && (arm_hcrx_el2_eff(env) & HCRX_VFNMI); - if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VFNMI) != 0)) { + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VFNMI)) { if (new_state) { cpu_interrupt(cs, CPU_INTERRUPT_VFNMI); } else { @@ -1041,7 +1041,7 @@ void arm_cpu_update_vserr(ARMCPU *cpu) bool new_state = env->cp15.hcr_el2 & HCR_VSE; - if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VSERR) != 0)) { + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VSERR)) { if (new_state) { cpu_interrupt(cs, CPU_INTERRUPT_VSERR); } else { diff --git a/target/arm/helper.c b/target/arm/helper.c index 0c1299ff841f3..4cd36e950aa7e 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -833,40 +833,40 @@ static uint64_t isr_read(CPUARMState *env, const ARMCPRegInfo *ri) uint64_t ret = 0; if (hcr_el2 & HCR_IMO) { - if (cs->interrupt_request & CPU_INTERRUPT_VIRQ) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_VIRQ)) { ret |= CPSR_I; } - if (cs->interrupt_request & CPU_INTERRUPT_VINMI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_VINMI)) { ret |= ISR_IS; ret |= CPSR_I; } } else { - if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD)) { ret |= CPSR_I; } - if (cs->interrupt_request & CPU_INTERRUPT_NMI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { ret |= ISR_IS; ret |= CPSR_I; } } if (hcr_el2 & HCR_FMO) { - if (cs->interrupt_request & CPU_INTERRUPT_VFIQ) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_VFIQ)) { ret |= CPSR_F; } - if (cs->interrupt_request & CPU_INTERRUPT_VFNMI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_VFNMI)) { ret |= ISR_FS; ret |= CPSR_F; } } else { - if (cs->interrupt_request & CPU_INTERRUPT_FIQ) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_FIQ)) { ret |= CPSR_F; } } if (hcr_el2 & HCR_AMO) { - if (cs->interrupt_request & CPU_INTERRUPT_VSERR) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_VSERR)) { ret |= CPSR_A; } } @@ -9147,7 +9147,7 @@ void arm_cpu_do_interrupt(CPUState *cs) arm_call_el_change_hook(cpu); if (!kvm_enabled()) { - cs->interrupt_request |= CPU_INTERRUPT_EXITTB; + cpu_set_interrupt(cs, CPU_INTERRUPT_EXITTB); } } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 47b0cd3a351c9..b77db99079e53 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1782,13 +1782,13 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) static int hvf_inject_interrupts(CPUState *cpu) { - if (cpu->interrupt_request & CPU_INTERRUPT_FIQ) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_FIQ)) { trace_hvf_inject_fiq(); hv_vcpu_set_pending_interrupt(cpu->accel->fd, HV_INTERRUPT_TYPE_FIQ, true); } - if (cpu->interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { trace_hvf_inject_irq(); hv_vcpu_set_pending_interrupt(cpu->accel->fd, HV_INTERRUPT_TYPE_IRQ, true); @@ -1840,7 +1840,7 @@ static void hvf_wfi(CPUState *cpu) uint64_t nanos; uint32_t cntfrq; - if (cpu->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIQ)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIQ)) { /* Interrupt pending, no need to wait */ return; } diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 6995de6a12b7e..a6df71d020574 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -45,7 +45,7 @@ static vaddr avr_cpu_get_pc(CPUState *cs) static bool avr_cpu_has_work(CPUState *cs) { - return (cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_RESET)) + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_RESET) && cpu_interrupts_enabled(cpu_env(cs)); } diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 24777727e6203..0ca79ee5e23a5 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -135,7 +135,7 @@ static void hppa_restore_state_to_opc(CPUState *cs, #ifndef CONFIG_USER_ONLY static bool hppa_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 818b50419f40d..8445cadecec3c 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -773,9 +773,9 @@ int hvf_vcpu_exec(CPUState *cpu) switch (exit_reason) { case EXIT_REASON_HLT: { macvm_set_rip(cpu, rip + ins_len); - if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + if (!(cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) - && !(cpu->interrupt_request & CPU_INTERRUPT_NMI) && + && !cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI) && !(idtvec_info & VMCS_IDT_VEC_VALID)) { cpu->halted = 1; ret = EXCP_HLT; diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index 17fce1d3cdd2c..9e05e0e5765bf 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -395,7 +395,7 @@ bool hvf_inject_interrupts(CPUState *cs) }; } - if (cs->interrupt_request & CPU_INTERRUPT_NMI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { if (!(env->hflags2 & HF2_NMI_MASK) && !(info & VMCS_INTR_VALID)) { cs->interrupt_request &= ~CPU_INTERRUPT_NMI; info = VMCS_INTR_VALID | VMCS_INTR_T_NMI | EXCP02_NMI; @@ -406,7 +406,7 @@ bool hvf_inject_interrupts(CPUState *cs) } if (!(env->hflags & HF_INHIBIT_IRQ_MASK) && - (cs->interrupt_request & CPU_INTERRUPT_HARD) && + cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK) && !(info & VMCS_INTR_VALID)) { int line = cpu_get_pic_interrupt(env); cs->interrupt_request &= ~CPU_INTERRUPT_HARD; @@ -415,11 +415,10 @@ bool hvf_inject_interrupts(CPUState *cs) VMCS_INTR_VALID | VMCS_INTR_T_HWINTR); } } - if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD)) { vmx_set_int_window_exiting(cs); } - return (cs->interrupt_request - & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)); + return cpu_test_interrupt(cs, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR); } int hvf_process_events(CPUState *cs) @@ -432,25 +431,25 @@ int hvf_process_events(CPUState *cs) env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); } - if (cs->interrupt_request & CPU_INTERRUPT_INIT) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_INIT)) { cpu_synchronize_state(cs); do_cpu_init(cpu); } - if (cs->interrupt_request & CPU_INTERRUPT_POLL) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_POLL)) { cs->interrupt_request &= ~CPU_INTERRUPT_POLL; apic_poll_irq(cpu->apic_state); } - if (((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if ((cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) || - (cs->interrupt_request & CPU_INTERRUPT_NMI)) { + cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { cs->halted = 0; } - if (cs->interrupt_request & CPU_INTERRUPT_SIPI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_SIPI)) { cpu_synchronize_state(cs); do_cpu_sipi(cpu); } - if (cs->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_TPR)) { cs->interrupt_request &= ~CPU_INTERRUPT_TPR; cpu_synchronize_state(cs); apic_handle_tpr_access_report(cpu->apic_state, env->eip, diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 369626f8c8d78..a7b5c8f81bcd1 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5453,8 +5453,8 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) int ret; /* Inject NMI */ - if (cpu->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { - if (cpu->interrupt_request & CPU_INTERRUPT_NMI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { bql_lock(); cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; bql_unlock(); @@ -5465,7 +5465,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) strerror(-ret)); } } - if (cpu->interrupt_request & CPU_INTERRUPT_SMI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SMI)) { bql_lock(); cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; bql_unlock(); @@ -5486,12 +5486,12 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) * or (for userspace APIC, but it is cheap to combine the checks here) * pending TPR access reports. */ - if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { - if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { cpu->exit_request = 1; } - if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { cpu->exit_request = 1; } } @@ -5499,7 +5499,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) if (!kvm_pic_in_kernel()) { /* Try to inject an interrupt if the guest can accept it */ if (run->ready_for_interrupt_injection && - (cpu->interrupt_request & CPU_INTERRUPT_HARD) && + cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) { int irq; @@ -5523,7 +5523,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) * interrupt, request an interrupt window exit. This will * cause a return to userspace as soon as the guest is ready to * receive interrupts. */ - if ((cpu->interrupt_request & CPU_INTERRUPT_HARD)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { run->request_interrupt_window = 1; } else { run->request_interrupt_window = 0; @@ -5595,7 +5595,7 @@ int kvm_arch_process_async_events(CPUState *cs) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - if (cs->interrupt_request & CPU_INTERRUPT_MCE) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_MCE)) { /* We must not raise CPU_INTERRUPT_MCE if it's not supported. */ assert(env->mcg_cap); @@ -5618,7 +5618,7 @@ int kvm_arch_process_async_events(CPUState *cs) } } - if ((cs->interrupt_request & CPU_INTERRUPT_INIT) && + if (cpu_test_interrupt(cs, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { kvm_cpu_synchronize_state(cs); do_cpu_init(cpu); @@ -5628,20 +5628,20 @@ int kvm_arch_process_async_events(CPUState *cs) return 0; } - if (cs->interrupt_request & CPU_INTERRUPT_POLL) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_POLL)) { cs->interrupt_request &= ~CPU_INTERRUPT_POLL; apic_poll_irq(cpu->apic_state); } - if (((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if ((cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) || - (cs->interrupt_request & CPU_INTERRUPT_NMI)) { + cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { cs->halted = 0; } - if (cs->interrupt_request & CPU_INTERRUPT_SIPI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_SIPI)) { kvm_cpu_synchronize_state(cs); do_cpu_sipi(cpu); } - if (cs->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_TPR)) { cs->interrupt_request &= ~CPU_INTERRUPT_TPR; kvm_cpu_synchronize_state(cs); apic_handle_tpr_access_report(cpu->apic_state, env->eip, @@ -5656,9 +5656,9 @@ static int kvm_handle_halt(X86CPU *cpu) CPUState *cs = CPU(cpu); CPUX86State *env = &cpu->env; - if (!((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if (!(cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) && - !(cs->interrupt_request & CPU_INTERRUPT_NMI)) { + !cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { cs->halted = 1; return EXCP_HLT; } diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 92e3b8b2f45f5..c1ac74c4f04dd 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -413,11 +413,11 @@ nvmm_vcpu_pre_run(CPUState *cpu) * Force the VCPU out of its inner loop to process any INIT requests * or commit pending TPR access. */ - if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { cpu->exit_request = 1; } - if (!has_event && (cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + if (!has_event && cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { if (nvmm_can_take_nmi(cpu)) { cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; event->type = NVMM_VCPU_EVENT_INTR; @@ -426,7 +426,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) } } - if (!has_event && (cpu->interrupt_request & CPU_INTERRUPT_HARD)) { + if (!has_event && cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { if (nvmm_can_take_int(cpu)) { cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; event->type = NVMM_VCPU_EVENT_INTR; @@ -436,7 +436,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) } /* Don't want SMIs. */ - if (cpu->interrupt_request & CPU_INTERRUPT_SMI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SMI)) { cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; } @@ -651,9 +651,9 @@ nvmm_handle_halted(struct nvmm_machine *mach, CPUState *cpu, bql_lock(); - if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + if (!(cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (cpu_env(cpu)->eflags & IF_MASK)) && - !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + !cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { cpu->exception_index = EXCP_HLT; cpu->halted = true; ret = 1; @@ -691,25 +691,25 @@ nvmm_vcpu_loop(CPUState *cpu) * Some asynchronous events must be handled outside of the inner * VCPU loop. They are handled here. */ - if (cpu->interrupt_request & CPU_INTERRUPT_INIT) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT)) { nvmm_cpu_synchronize_state(cpu); do_cpu_init(x86_cpu); /* set int/nmi windows back to the reset state */ } - if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_POLL)) { cpu->interrupt_request &= ~CPU_INTERRUPT_POLL; apic_poll_irq(x86_cpu->apic_state); } - if (((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + if ((cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) || - (cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { cpu->halted = false; } - if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SIPI)) { nvmm_cpu_synchronize_state(cpu); do_cpu_sipi(x86_cpu); } - if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { cpu->interrupt_request &= ~CPU_INTERRUPT_TPR; nvmm_cpu_synchronize_state(cpu); apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, diff --git a/target/i386/tcg/system/seg_helper.c b/target/i386/tcg/system/seg_helper.c index d4ea890c12440..794a23ddfc4b0 100644 --- a/target/i386/tcg/system/seg_helper.c +++ b/target/i386/tcg/system/seg_helper.c @@ -133,7 +133,7 @@ bool x86_cpu_exec_halt(CPUState *cpu) X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; - if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_POLL)) { bql_lock(); apic_poll_irq(x86_cpu->apic_state); cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); diff --git a/target/i386/tcg/system/svm_helper.c b/target/i386/tcg/system/svm_helper.c index dea039b87a645..3569196bddadb 100644 --- a/target/i386/tcg/system/svm_helper.c +++ b/target/i386/tcg/system/svm_helper.c @@ -403,7 +403,7 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) env->hflags2 |= HF2_GIF_MASK; if (ctl_has_irq(env)) { - cs->interrupt_request |= CPU_INTERRUPT_VIRQ; + cpu_set_interrupt(cs, CPU_INTERRUPT_VIRQ); } if (virtual_gif_set(env)) { diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index b72dcff3c8d46..878cdd1668c5e 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -1436,9 +1436,9 @@ static int whpx_handle_halt(CPUState *cpu) int ret = 0; bql_lock(); - if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + if (!(cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (cpu_env(cpu)->eflags & IF_MASK)) && - !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + !cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { cpu->exception_index = EXCP_HLT; cpu->halted = true; ret = 1; @@ -1469,15 +1469,15 @@ static void whpx_vcpu_pre_run(CPUState *cpu) /* Inject NMI */ if (!vcpu->interruption_pending && - cpu->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { - if (cpu->interrupt_request & CPU_INTERRUPT_NMI) { + cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; vcpu->interruptable = false; new_int.InterruptionType = WHvX64PendingNmi; new_int.InterruptionPending = 1; new_int.InterruptionVector = 2; } - if (cpu->interrupt_request & CPU_INTERRUPT_SMI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SMI)) { cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; } } @@ -1486,12 +1486,12 @@ static void whpx_vcpu_pre_run(CPUState *cpu) * Force the VCPU out of its inner loop to process any INIT requests or * commit pending TPR access. */ - if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { - if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { cpu->exit_request = 1; } - if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { cpu->exit_request = 1; } } @@ -1501,7 +1501,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) if (!vcpu->interruption_pending && vcpu->interruptable && (env->eflags & IF_MASK)) { assert(!new_int.InterruptionPending); - if (cpu->interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; irq = cpu_get_pic_interrupt(env); if (irq >= 0) { @@ -1519,7 +1519,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) reg_count += 1; } } else if (vcpu->ready_for_pic_interrupt && - (cpu->interrupt_request & CPU_INTERRUPT_HARD)) { + cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; irq = cpu_get_pic_interrupt(env); if (irq >= 0) { @@ -1546,7 +1546,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) /* Update the state of the interrupt delivery notification */ if (!vcpu->window_registered && - cpu->interrupt_request & CPU_INTERRUPT_HARD) { + cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { reg_values[reg_count].DeliverabilityNotifications = (WHV_X64_DELIVERABILITY_NOTIFICATIONS_REGISTER) { .InterruptNotification = 1 @@ -1599,30 +1599,30 @@ static void whpx_vcpu_process_async_events(CPUState *cpu) CPUX86State *env = &x86_cpu->env; AccelCPUState *vcpu = cpu->accel; - if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { whpx_cpu_synchronize_state(cpu); do_cpu_init(x86_cpu); vcpu->interruptable = true; } - if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_POLL)) { cpu->interrupt_request &= ~CPU_INTERRUPT_POLL; apic_poll_irq(x86_cpu->apic_state); } - if (((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + if ((cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) || - (cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { cpu->halted = false; } - if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SIPI)) { whpx_cpu_synchronize_state(cpu); do_cpu_sipi(x86_cpu); } - if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { cpu->interrupt_request &= ~CPU_INTERRUPT_TPR; whpx_cpu_synchronize_state(cpu); apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index abad84c054707..3a7621c0ea08b 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -376,7 +376,7 @@ static bool loongarch_cpu_has_work(CPUState *cs) { bool has_work = false; - if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && cpu_loongarch_hw_interrupts_pending(cpu_env(cs))) { has_work = true; } diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 6a09db3a6f6df..f1b673119d6b4 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -74,7 +74,7 @@ static void m68k_restore_state_to_opc(CPUState *cs, #ifndef CONFIG_USER_ONLY static bool m68k_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & CPU_INTERRUPT_HARD; + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index ee0a869a94aac..22231f09e602a 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -129,7 +129,7 @@ static void mb_restore_state_to_opc(CPUState *cs, #ifndef CONFIG_USER_ONLY static bool mb_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 1f6c41fd3401e..5989c3ba17782 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -145,7 +145,7 @@ static bool mips_cpu_has_work(CPUState *cs) * check for interrupts that can be taken. For pre-release 6 CPUs, * check for CP0 Config7 'Wait IE ignore' bit. */ - if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && cpu_mips_hw_interrupts_pending(env)) { if (cpu_mips_hw_interrupts_enabled(env) || (env->CP0_Config7 & (1 << CP0C7_WII)) || @@ -160,7 +160,7 @@ static bool mips_cpu_has_work(CPUState *cs) * The QEMU model will issue an _WAKE request whenever the CPUs * should be woken up. */ - if (cs->interrupt_request & CPU_INTERRUPT_WAKE) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_WAKE)) { has_work = true; } @@ -170,7 +170,7 @@ static bool mips_cpu_has_work(CPUState *cs) } /* MIPS Release 6 has the ability to halt the CPU. */ if (env->CP0_Config5 & (1 << CP0C5_VP)) { - if (cs->interrupt_request & CPU_INTERRUPT_WAKE) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_WAKE)) { has_work = true; } if (!mips_vp_active(env)) { diff --git a/target/mips/kvm.c b/target/mips/kvm.c index ec53acb51a1fd..450947c3fa56e 100644 --- a/target/mips/kvm.c +++ b/target/mips/kvm.c @@ -144,7 +144,7 @@ void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) bql_lock(); - if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && cpu_mips_io_interrupts_pending(cpu)) { intr.cpu = -1; intr.irq = 2; diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index dfbb2df643a09..9bbfe22ed3a85 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -78,8 +78,7 @@ static void openrisc_restore_state_to_opc(CPUState *cs, #ifndef CONFIG_USER_ONLY static bool openrisc_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & (CPU_INTERRUPT_HARD | - CPU_INTERRUPT_TIMER); + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_TIMER); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index a0e77f2673e56..db841f126039f 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7225,7 +7225,7 @@ static int ppc_cpu_mmu_index(CPUState *cs, bool ifetch) #ifndef CONFIG_USER_ONLY static bool ppc_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & CPU_INTERRUPT_HARD; + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 015658049e612..d145774b09ae6 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -1354,7 +1354,7 @@ static int kvmppc_handle_halt(PowerPCCPU *cpu) CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - if (!(cs->interrupt_request & CPU_INTERRUPT_HARD) && + if (!cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && FIELD_EX64(env->msr, MSR, EE)) { cs->halted = 1; cs->exception_index = EXCP_HLT; diff --git a/target/rx/cpu.c b/target/rx/cpu.c index c6dd5d6f832ee..da02ae7bf888c 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -75,8 +75,7 @@ static void rx_restore_state_to_opc(CPUState *cs, static bool rx_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & - (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIR); + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIR); } static int rx_cpu_mmu_index(CPUState *cs, bool ifunc) diff --git a/target/rx/helper.c b/target/rx/helper.c index 0640ab322b587..ce003af421900 100644 --- a/target/rx/helper.c +++ b/target/rx/helper.c @@ -44,7 +44,7 @@ void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte) void rx_cpu_do_interrupt(CPUState *cs) { CPURXState *env = cpu_env(cs); - int do_irq = cs->interrupt_request & INT_FLAGS; + int do_irq = cpu_test_interrupt(cs, INT_FLAGS); uint32_t save_psw; env->in_sleep = 0; diff --git a/target/s390x/cpu-system.c b/target/s390x/cpu-system.c index 709ccd52992ce..f3a9ffb2a2772 100644 --- a/target/s390x/cpu-system.c +++ b/target/s390x/cpu-system.c @@ -49,7 +49,7 @@ bool s390_cpu_has_work(CPUState *cs) return false; } - if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) { + if (!cpu_test_interrupt(cs, CPU_INTERRUPT_HARD)) { return false; } diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 4f561e8c912f3..21ccb86df48ad 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -108,7 +108,7 @@ static bool superh_io_recompile_replay_branch(CPUState *cs, static bool superh_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & CPU_INTERRUPT_HARD; + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/sh4/helper.c b/target/sh4/helper.c index fb7642bda1b74..1744ef0e6d876 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -58,7 +58,7 @@ int cpu_sh4_is_cached(CPUSH4State *env, target_ulong addr) void superh_cpu_do_interrupt(CPUState *cs) { CPUSH4State *env = cpu_env(cs); - int do_irq = cs->interrupt_request & CPU_INTERRUPT_HARD; + int do_irq = cpu_test_interrupt(cs, CPU_INTERRUPT_HARD); int do_exp, irq_vector = cs->exception_index; /* prioritize exceptions over interrupts */ diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 245caf2de0218..c9773f15401a9 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -783,7 +783,7 @@ static void sparc_restore_state_to_opc(CPUState *cs, #ifndef CONFIG_USER_ONLY static bool sparc_cpu_has_work(CPUState *cs) { - return (cs->interrupt_request & CPU_INTERRUPT_HARD) && + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && cpu_interrupts_enabled(cpu_env(cs)); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/sparc/int64_helper.c b/target/sparc/int64_helper.c index bd14c7a0db9ae..49e4e51c6dcc6 100644 --- a/target/sparc/int64_helper.c +++ b/target/sparc/int64_helper.c @@ -89,7 +89,7 @@ void cpu_check_irqs(CPUSPARCState *env) * the next bit is (2 << psrpil). */ if (pil < (2 << env->psrpil)) { - if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD)) { trace_sparc64_cpu_check_irqs_reset_irq(env->interrupt_index); env->interrupt_index = 0; cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); @@ -120,7 +120,7 @@ void cpu_check_irqs(CPUSPARCState *env) break; } } - } else if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + } else if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD)) { trace_sparc64_cpu_check_irqs_disabled(pil, env->pil_in, env->softint, env->interrupt_index); env->interrupt_index = 0; From 73c520b088878682e2d3b7fa19a6366ec8d39829 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Aug 2025 18:05:53 +0200 Subject: [PATCH 0388/1794] memory: reintroduce BQL-free fine-grained PIO/MMIO This patch brings back Jan's idea [1] of BQL-free IO access This will let us make access to ACPI PM/HPET timers cheaper, and prevent BQL contention in case of workload that heavily uses the timers with a lot of vCPUs. 1) 196ea13104f (memory: Add global-locking property to memory regions) ... de7ea885c539 (kvm: Switch to unlocked MMIO) Signed-off-by: Igor Mammedov Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20250814160600.2327672-2-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- include/system/memory.h | 12 ++++++++++++ system/memory.c | 15 +++++++++++++++ system/physmem.c | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/include/system/memory.h b/include/system/memory.h index e2cd6ed126144..aa85fc27a1023 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -833,6 +833,7 @@ struct MemoryRegion { bool nonvolatile; bool rom_device; bool flush_coalesced_mmio; + bool lockless_io; bool unmergeable; uint8_t dirty_log_mask; bool is_iommu; @@ -2341,6 +2342,17 @@ void memory_region_set_flush_coalesced(MemoryRegion *mr); */ void memory_region_clear_flush_coalesced(MemoryRegion *mr); +/** + * memory_region_enable_lockless_io: Enable lockless (BQL free) acceess. + * + * Enable BQL-free access for devices that are well prepared to handle + * locking during I/O themselves: either by doing fine grained locking or + * by providing lock-free I/O schemes. + * + * @mr: the memory region to be updated. + */ +void memory_region_enable_lockless_io(MemoryRegion *mr); + /** * memory_region_add_eventfd: Request an eventfd to be triggered when a word * is written to a location. diff --git a/system/memory.c b/system/memory.c index 56465479406f4..44701c465c1e9 100644 --- a/system/memory.c +++ b/system/memory.c @@ -2546,6 +2546,21 @@ void memory_region_clear_flush_coalesced(MemoryRegion *mr) } } +void memory_region_enable_lockless_io(MemoryRegion *mr) +{ + mr->lockless_io = true; + /* + * reentrancy_guard has per device scope, that when enabled + * will effectively prevent concurrent access to device's IO + * MemoryRegion(s) by not calling accessor callback. + * + * Turn it off for lock-less IO enabled devices, to allow + * concurrent IO. + * TODO: remove this when reentrancy_guard becomes per transaction. + */ + mr->disable_reentrancy_guard = true; +} + void memory_region_add_eventfd(MemoryRegion *mr, hwaddr addr, unsigned size, diff --git a/system/physmem.c b/system/physmem.c index e5dd760e0bcae..f498572fc821f 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -2900,7 +2900,7 @@ bool prepare_mmio_access(MemoryRegion *mr) { bool release_lock = false; - if (!bql_locked()) { + if (!bql_locked() && !mr->lockless_io) { bql_lock(); release_lock = true; } From 4ae5e2b2bf65452538511a2895d7a4e2115058a5 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Aug 2025 18:05:54 +0200 Subject: [PATCH 0389/1794] acpi: mark PMTIMER as unlocked Reading QEMU_CLOCK_VIRTUAL is thread-safe, write access is NOP. This makes possible to boot Windows with large vCPUs count when hv-time is not used. Reproducer: -M q35,hpet=off -cpu host -enable-kvm -smp 240,sockets=4 -m 8G WS2025.img fails to boot within 30min. With this fix it boots within 2-1min. Signed-off-by: Igor Mammedov Reviewed-by: Peter Xu Acked-by: Michael S. Tsirkin Link: https://lore.kernel.org/r/20250814160600.2327672-3-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- hw/acpi/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/acpi/core.c b/hw/acpi/core.c index 58f8964e13090..ff1658280338a 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -547,6 +547,7 @@ void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci, ar->tmr.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, acpi_pm_tmr_timer, ar); memory_region_init_io(&ar->tmr.io, memory_region_owner(parent), &acpi_pm_tmr_ops, ar, "acpi-tmr", 4); + memory_region_enable_lockless_io(&ar->tmr.io); memory_region_add_subregion(parent, 8, &ar->tmr.io); } From 7defb58bafac8dcb23c06be5e4f2d1a33d8392fd Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Aug 2025 18:05:55 +0200 Subject: [PATCH 0390/1794] hpet: switch to fine-grained device locking as a step towards lock-less HPET counter read, use per device locking instead of BQL. Signed-off-by: Igor Mammedov Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20250814160600.2327672-4-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- hw/timer/hpet.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index cb48cc151f13b..ab5aa59ae4e9b 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -38,6 +38,7 @@ #include "hw/timer/i8254.h" #include "system/address-spaces.h" #include "qom/object.h" +#include "qemu/lockable.h" #include "trace.h" struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX}; @@ -69,6 +70,7 @@ struct HPETState { SysBusDevice parent_obj; /*< public >*/ + QemuMutex lock; MemoryRegion iomem; uint64_t hpet_offset; bool hpet_offset_saved; @@ -428,6 +430,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, trace_hpet_ram_read(addr); addr &= ~4; + QEMU_LOCK_GUARD(&s->lock); /*address range of all global regs*/ if (addr <= 0xff) { switch (addr) { @@ -482,6 +485,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, int len = MIN(size * 8, 64 - shift); uint64_t old_val, new_val, cleared; + QEMU_LOCK_GUARD(&s->lock); trace_hpet_ram_write(addr, value); addr &= ~4; @@ -679,8 +683,10 @@ static void hpet_init(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); HPETState *s = HPET(obj); + qemu_mutex_init(&s->lock); /* HPET Area */ memory_region_init_io(&s->iomem, obj, &hpet_ram_ops, s, "hpet", HPET_LEN); + memory_region_enable_lockless_io(&s->iomem); sysbus_init_mmio(sbd, &s->iomem); } From a453bf0354412592362139bdf4df0d4900ec0686 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Aug 2025 18:05:56 +0200 Subject: [PATCH 0391/1794] hpet: move out main counter read into a separate block Follow up patche will switch main counter read to lock-less mode. As preparation for that move relevant branch into a separate top level block to make followup patch cleaner/simplier by reducing contextual noise when lock-less read is introduced. no functional changes. Signed-off-by: Igor Mammedov Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20250814160600.2327672-5-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- hw/timer/hpet.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index ab5aa59ae4e9b..c776afc0f2d1c 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -431,6 +431,16 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, addr &= ~4; QEMU_LOCK_GUARD(&s->lock); + if (addr == HPET_COUNTER) { + if (hpet_enabled(s)) { + cur_tick = hpet_get_ticks(s); + } else { + cur_tick = s->hpet_counter; + } + trace_hpet_ram_read_reading_counter(addr & 4, cur_tick); + return cur_tick >> shift; + } + /*address range of all global regs*/ if (addr <= 0xff) { switch (addr) { @@ -438,14 +448,6 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, return s->capability >> shift; case HPET_CFG: return s->config >> shift; - case HPET_COUNTER: - if (hpet_enabled(s)) { - cur_tick = hpet_get_ticks(s); - } else { - cur_tick = s->hpet_counter; - } - trace_hpet_ram_read_reading_counter(addr & 4, cur_tick); - return cur_tick >> shift; case HPET_STATUS: return s->isr >> shift; default: From 20c2345290f34aac434284cf9a242c7904d39a27 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Aug 2025 18:05:57 +0200 Subject: [PATCH 0392/1794] hpet: make main counter read lock-less Make access to main HPET counter lock-less. In unlikely event of an update in progress, readers will busy wait untill update is finished. As result micro benchmark of concurrent reading of HPET counter with large number of vCPU shows over 80% better (less) latency. Signed-off-by: Igor Mammedov Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20250814160600.2327672-6-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- hw/timer/hpet.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index c776afc0f2d1c..789a31d0a08a2 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -39,6 +39,7 @@ #include "system/address-spaces.h" #include "qom/object.h" #include "qemu/lockable.h" +#include "qemu/seqlock.h" #include "trace.h" struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX}; @@ -74,6 +75,7 @@ struct HPETState { MemoryRegion iomem; uint64_t hpet_offset; bool hpet_offset_saved; + QemuSeqLock state_version; qemu_irq irqs[HPET_NUM_IRQ_ROUTES]; uint32_t flags; uint8_t rtc_irq_level; @@ -430,17 +432,25 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, trace_hpet_ram_read(addr); addr &= ~4; - QEMU_LOCK_GUARD(&s->lock); if (addr == HPET_COUNTER) { - if (hpet_enabled(s)) { - cur_tick = hpet_get_ticks(s); - } else { - cur_tick = s->hpet_counter; - } + unsigned version; + + /* + * Write update is rare, so busywait here is unlikely to happen + */ + do { + version = seqlock_read_begin(&s->state_version); + if (unlikely(!hpet_enabled(s))) { + cur_tick = s->hpet_counter; + } else { + cur_tick = hpet_get_ticks(s); + } + } while (seqlock_read_retry(&s->state_version, version)); trace_hpet_ram_read_reading_counter(addr & 4, cur_tick); return cur_tick >> shift; } + QEMU_LOCK_GUARD(&s->lock); /*address range of all global regs*/ if (addr <= 0xff) { switch (addr) { @@ -500,6 +510,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, old_val = s->config; new_val = deposit64(old_val, shift, len, value); new_val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); + seqlock_write_begin(&s->state_version); s->config = new_val; if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) { /* Enable main counter and interrupt generation. */ @@ -518,6 +529,8 @@ static void hpet_ram_write(void *opaque, hwaddr addr, hpet_del_timer(&s->timer[i]); } } + seqlock_write_end(&s->state_version); + /* i8254 and RTC output pins are disabled * when HPET is in legacy mode */ if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) { @@ -686,6 +699,7 @@ static void hpet_init(Object *obj) HPETState *s = HPET(obj); qemu_mutex_init(&s->lock); + seqlock_init(&s->state_version); /* HPET Area */ memory_region_init_io(&s->iomem, obj, &hpet_ram_ops, s, "hpet", HPET_LEN); memory_region_enable_lockless_io(&s->iomem); From 17e645c6f17999cd0306c4d18d6f6cb3db55756d Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Aug 2025 18:05:59 +0200 Subject: [PATCH 0393/1794] kvm: i386: irqchip: take BQL only if there is an interrupt when kernel-irqchip=split is used, QEMU still hits BQL contention issue when reading ACPI PM/HPET timers (despite of timer[s] access being lock-less). So Windows with more than 255 cpus is still not able to boot (since it requires iommu -> split irqchip). Problematic path is in kvm_arch_pre_run() where BQL is taken unconditionally when split irqchip is in use. There are a few parts that BQL protects there: 1. interrupt check and injecting however we do not take BQL when checking for pending interrupt (even within the same function), so the patch takes the same approach for cpu->interrupt_request checks and takes BQL only if there is a job to do. 2. request_interrupt_window access CPUState::kvm_run::request_interrupt_window doesn't need BQL as it's accessed by its own vCPU thread. 3. cr8/cpu_get_apic_tpr access the same (as #2) applies to CPUState::kvm_run::cr8, and APIC registers are also cached/synced (get/put) within the vCPU thread it belongs to. Taking BQL only when is necessary, eleminates BQL bottleneck on IO/MMIO only exit path, improoving latency by 80% on HPET micro benchmark. This lets Windows to boot succesfully (in case hv-time isn't used) when more than 255 vCPUs are in use. Signed-off-by: Igor Mammedov Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20250814160600.2327672-8-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index a7b5c8f81bcd1..306430a052133 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5478,9 +5478,6 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) } } - if (!kvm_pic_in_kernel()) { - bql_lock(); - } /* Force the VCPU out of its inner loop to process any INIT requests * or (for userspace APIC, but it is cheap to combine the checks here) @@ -5489,10 +5486,10 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, 1); } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, 1); } } @@ -5503,6 +5500,8 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) (env->eflags & IF_MASK)) { int irq; + bql_lock(); + cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; irq = cpu_get_pic_interrupt(env); if (irq >= 0) { @@ -5517,6 +5516,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) strerror(-ret)); } } + bql_unlock(); } /* If we have an interrupt but the guest is not ready to receive an @@ -5531,8 +5531,6 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) DPRINTF("setting tpr\n"); run->cr8 = cpu_get_apic_tpr(x86_cpu->apic_state); - - bql_unlock(); } } From 83bd8e65bc70cef03a207df315004f8b1301dc53 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Aug 2025 18:06:00 +0200 Subject: [PATCH 0394/1794] tcg: move interrupt caching and single step masking closer to user in cpu_handle_interrupt() the only place where cached interrupt_request might have effect is when CPU_INTERRUPT_SSTEP_MASK applied and cached interrupt_request handed over to cpu_exec_interrupt() and need_replay_interrupt(). Simplify code by moving interrupt_request caching and CPU_INTERRUPT_SSTEP_MASK masking into the block where it actually matters and drop reloading cached value from CPUState:interrupt_request as the rest of the code directly uses CPUState:interrupt_request. Signed-off-by: Igor Mammedov Link: https://lore.kernel.org/r/20250814160600.2327672-9-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- accel/tcg/cpu-exec.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 96c124aa72018..8491e5badd18d 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -782,13 +782,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, assert(!cpu_test_interrupt(cpu, ~0)); #else if (unlikely(cpu_test_interrupt(cpu, ~0))) { - int interrupt_request; bql_lock(); - interrupt_request = cpu->interrupt_request; - if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) { - /* Mask out external interrupts for this step. */ - interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK; - } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_DEBUG)) { cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG; cpu->exception_index = EXCP_DEBUG; @@ -806,6 +800,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, return true; } else { const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; + int interrupt_request = cpu->interrupt_request; if (cpu_test_interrupt(cpu, CPU_INTERRUPT_RESET)) { replay_interrupt(); @@ -814,6 +809,11 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, return true; } + if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) { + /* Mask out external interrupts for this step. */ + interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK; + } + /* * The target hook has 3 exit conditions: * False when the interrupt isn't processed, @@ -838,9 +838,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, cpu->exception_index = -1; *last_tb = NULL; } - /* The target hook may have updated the 'cpu->interrupt_request'; - * reload the 'interrupt_request' value */ - interrupt_request = cpu->interrupt_request; } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_EXITTB)) { cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB; From f3d9393791e6c02bae99f920d350e65cd299fed1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Aug 2025 15:27:50 +1000 Subject: [PATCH 0395/1794] hw/core: Dump cpu_reset in the reset.exit phase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During reset.hold, the cpu is in an inconsistent state, where the leaf class has not had a chance to initialize state at all. This is visible as a SIGSEGV in "qemu-system-sparc64 -d cpu_reset". Move the dump to the exit phase, where all initialization is certain to be complete. Reported-by: Henk van der Laak Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/core/cpu-common.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 39e674aca2154..26321be785c24 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -119,11 +119,6 @@ static void cpu_common_reset_hold(Object *obj, ResetType type) { CPUState *cpu = CPU(obj); - if (qemu_loglevel_mask(CPU_LOG_RESET)) { - qemu_log("CPU Reset (CPU %d)\n", cpu->cpu_index); - log_cpu_state(cpu, cpu->cc->reset_dump_flags); - } - cpu->interrupt_request = 0; cpu->halted = cpu->start_powered_off; cpu->mem_io_pc = 0; @@ -137,6 +132,16 @@ static void cpu_common_reset_hold(Object *obj, ResetType type) cpu_exec_reset_hold(cpu); } +static void cpu_common_reset_exit(Object *obj, ResetType type) +{ + if (qemu_loglevel_mask(CPU_LOG_RESET)) { + CPUState *cpu = CPU(obj); + + qemu_log("CPU Reset (CPU %d)\n", cpu->cpu_index); + log_cpu_state(cpu, cpu->cc->reset_dump_flags); + } +} + ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model) { ObjectClass *oc; @@ -380,6 +385,7 @@ static void cpu_common_class_init(ObjectClass *klass, const void *data) dc->realize = cpu_common_realizefn; dc->unrealize = cpu_common_unrealizefn; rc->phases.hold = cpu_common_reset_hold; + rc->phases.exit = cpu_common_reset_exit; cpu_class_init_props(dc); /* * Reason: CPUs still need special care by board code: wiring up From fd5c5032df6bea4bbf56752d8885c8a9770c4959 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Aug 2025 15:34:35 +1000 Subject: [PATCH 0396/1794] hw/core: Use qemu_log_trylock/unlock in cpu_common_reset_exit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensure that the "CPU Reset" message won't be separated from the cpu_dump_state output. Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/core/cpu-common.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 26321be785c24..259cf2a3c3614 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -135,10 +135,15 @@ static void cpu_common_reset_hold(Object *obj, ResetType type) static void cpu_common_reset_exit(Object *obj, ResetType type) { if (qemu_loglevel_mask(CPU_LOG_RESET)) { - CPUState *cpu = CPU(obj); + FILE *f = qemu_log_trylock(); - qemu_log("CPU Reset (CPU %d)\n", cpu->cpu_index); - log_cpu_state(cpu, cpu->cc->reset_dump_flags); + if (f) { + CPUState *cpu = CPU(obj); + + fprintf(f, "CPU Reset (CPU %d)\n", cpu->cpu_index); + cpu_dump_state(cpu, f, cpu->cc->reset_dump_flags); + qemu_log_unlock(f); + } } } From d7cde4df1c1b1f24dfd7760fe897429b75200b19 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 07:36:25 +1000 Subject: [PATCH 0397/1794] linux-user: Tidy print_socket_protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sink all of the qemu_log calls to the end, collecting only a string for the name, if identified. Merge separate if blocks into one switch. Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/strace.c | 96 +++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/linux-user/strace.c b/linux-user/strace.c index 786354627aa4e..1233ebceb08b1 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -499,116 +499,118 @@ print_socket_type(int type) static void print_socket_protocol(int domain, int type, int protocol) { - if (domain == AF_PACKET || - (domain == AF_INET && type == TARGET_SOCK_PACKET)) { + const char *name = NULL; + + switch (domain) { + case AF_PACKET: switch (protocol) { - case 0x0003: - qemu_log("ETH_P_ALL"); + case 3: + name = "ETH_P_ALL"; break; - default: - qemu_log("%d", protocol); } - return; - } + break; - if (domain == PF_NETLINK) { + case PF_NETLINK: switch (protocol) { case NETLINK_ROUTE: - qemu_log("NETLINK_ROUTE"); + name = "NETLINK_ROUTE"; break; case NETLINK_UNUSED: - qemu_log("NETLINK_UNUSED"); + name = "NETLINK_UNUSED"; break; case NETLINK_USERSOCK: - qemu_log("NETLINK_USERSOCK"); + name = "NETLINK_USERSOCK"; break; case NETLINK_FIREWALL: - qemu_log("NETLINK_FIREWALL"); + name = "NETLINK_FIREWALL"; break; case NETLINK_SOCK_DIAG: - qemu_log("NETLINK_SOCK_DIAG"); + name = "NETLINK_SOCK_DIAG"; break; case NETLINK_NFLOG: - qemu_log("NETLINK_NFLOG"); + name = "NETLINK_NFLOG"; break; case NETLINK_XFRM: - qemu_log("NETLINK_XFRM"); + name = "NETLINK_XFRM"; break; case NETLINK_SELINUX: - qemu_log("NETLINK_SELINUX"); + name = "NETLINK_SELINUX"; break; case NETLINK_ISCSI: - qemu_log("NETLINK_ISCSI"); + name = "NETLINK_ISCSI"; break; case NETLINK_AUDIT: - qemu_log("NETLINK_AUDIT"); + name = "NETLINK_AUDIT"; break; case NETLINK_FIB_LOOKUP: - qemu_log("NETLINK_FIB_LOOKUP"); + name = "NETLINK_FIB_LOOKUP"; break; case NETLINK_CONNECTOR: - qemu_log("NETLINK_CONNECTOR"); + name = "NETLINK_CONNECTOR"; break; case NETLINK_NETFILTER: - qemu_log("NETLINK_NETFILTER"); + name = "NETLINK_NETFILTER"; break; case NETLINK_IP6_FW: - qemu_log("NETLINK_IP6_FW"); + name = "NETLINK_IP6_FW"; break; case NETLINK_DNRTMSG: - qemu_log("NETLINK_DNRTMSG"); + name = "NETLINK_DNRTMSG"; break; case NETLINK_KOBJECT_UEVENT: - qemu_log("NETLINK_KOBJECT_UEVENT"); + name = "NETLINK_KOBJECT_UEVENT"; break; case NETLINK_GENERIC: - qemu_log("NETLINK_GENERIC"); + name = "NETLINK_GENERIC"; break; case NETLINK_SCSITRANSPORT: - qemu_log("NETLINK_SCSITRANSPORT"); + name = "NETLINK_SCSITRANSPORT"; break; case NETLINK_ECRYPTFS: - qemu_log("NETLINK_ECRYPTFS"); + name = "NETLINK_ECRYPTFS"; break; case NETLINK_RDMA: - qemu_log("NETLINK_RDMA"); + name = "NETLINK_RDMA"; break; case NETLINK_CRYPTO: - qemu_log("NETLINK_CRYPTO"); + name = "NETLINK_CRYPTO"; break; case NETLINK_SMC: - qemu_log("NETLINK_SMC"); - break; - default: - qemu_log("%d", protocol); + name = "NETLINK_SMC"; break; } - return; - } + break; - if (domain == AF_INET || domain == AF_INET6) { + case AF_INET: + case AF_INET6: switch (protocol) { + case 3: + if (domain == AF_INET && type == TARGET_SOCK_PACKET) { + name = "ETH_P_ALL"; + } + break; case IPPROTO_IP: - qemu_log("IPPROTO_IP"); + name = "IPPROTO_IP"; break; case IPPROTO_TCP: - qemu_log("IPPROTO_TCP"); + name = "IPPROTO_TCP"; break; case IPPROTO_UDP: - qemu_log("IPPROTO_UDP"); + name = "IPPROTO_UDP"; break; case IPPROTO_RAW: - qemu_log("IPPROTO_RAW"); - break; - default: - qemu_log("%d", protocol); + name = "IPPROTO_RAW"; break; } - return; + break; } - qemu_log("%d", protocol); -} + if (name) { + qemu_log("%s", name); + } else { + qemu_log("%d", protocol); + } +} #ifdef TARGET_NR__newselect static void From e61df9176d7a0107069de1a77b6f502b24b34f58 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Aug 2025 17:20:12 +0100 Subject: [PATCH 0398/1794] linux-user: Drop deprecated -p option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The user-mode '-p' option has been deprecated since 9.0 and doesn't do anything except emit a warning. We are well past our minimum deprecation period, so drop the option. Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Signed-off-by: Peter Maydell Signed-off-by: Richard Henderson Message-ID: <20250828162012.3307647-1-peter.maydell@linaro.org> --- bsd-user/main.c | 8 -------- docs/about/deprecated.rst | 10 ---------- docs/about/removed-features.rst | 8 ++++++++ docs/user/main.rst | 3 --- linux-user/main.c | 12 ------------ 5 files changed, 8 insertions(+), 33 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index 7e5d4bbce0942..9ba69642f500d 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -367,14 +367,6 @@ int main(int argc, char **argv) } } else if (!strcmp(r, "L")) { interp_prefix = argv[optind++]; - } else if (!strcmp(r, "p")) { - unsigned size, want = qemu_real_host_page_size(); - - r = argv[optind++]; - if (qemu_strtoui(r, NULL, 10, &size) || size != want) { - warn_report("Deprecated page size option cannot " - "change host page size (%u)", want); - } } else if (!strcmp(r, "g")) { gdbstub = g_strdup(argv[optind++]); } else if (!strcmp(r, "r")) { diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index d50645a07117b..5d1579dcf8296 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -81,16 +81,6 @@ kernel since 2001. None of the board types QEMU supports need ``param_struct`` support, so this option has been deprecated and will be removed in a future QEMU version. -User-mode emulator command line arguments ------------------------------------------ - -``-p`` (since 9.0) -'''''''''''''''''' - -The ``-p`` option pretends to control the host page size. However, -it is not possible to change the host page size, and using the -option only causes failures. - QEMU Machine Protocol (QMP) commands ------------------------------------ diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index d7c2113fc3eb2..25a904032c558 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -571,6 +571,14 @@ The ``-singlestep`` option has been given a name that better reflects what it actually does. For both linux-user and bsd-user, use the ``-one-insn-per-tb`` option instead. +``-p`` (removed in 10.2) +'''''''''''''''''''''''' + +The ``-p`` option pretends to control the host page size. However, +it is not possible to change the host page size; we stopped trying +to do anything with the option except print a warning from 9.0, +and now the option is removed entirely. + QEMU Machine Protocol (QMP) commands ------------------------------------ diff --git a/docs/user/main.rst b/docs/user/main.rst index 347bdfabf8cf6..a8ddf9142495e 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -262,9 +262,6 @@ Debug options: Activate logging of the specified items (use '-d help' for a list of log items) -``-p pagesize`` - Act as if the host page size was 'pagesize' bytes - ``-one-insn-per-tb`` Run the emulation with one guest instruction per translation block. This slows down emulation a lot, but can be useful in some situations, diff --git a/linux-user/main.c b/linux-user/main.c index 6edeeecef3858..7b0ccb6fd6090 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -340,16 +340,6 @@ static void handle_arg_ld_prefix(const char *arg) interp_prefix = strdup(arg); } -static void handle_arg_pagesize(const char *arg) -{ - unsigned size, want = qemu_real_host_page_size(); - - if (qemu_strtoui(arg, NULL, 10, &size) || size != want) { - warn_report("Deprecated page size option cannot " - "change host page size (%u)", want); - } -} - static void handle_arg_seed(const char *arg) { seed_optarg = arg; @@ -522,8 +512,6 @@ static const struct qemu_argument arg_table[] = { "range[,...]","filter logging based on address range"}, {"D", "QEMU_LOG_FILENAME", true, handle_arg_log_filename, "logfile", "write logs to 'logfile' (default stderr)"}, - {"p", "QEMU_PAGESIZE", true, handle_arg_pagesize, - "pagesize", "deprecated change to host page size"}, {"one-insn-per-tb", "QEMU_ONE_INSN_PER_TB", false, handle_arg_one_insn_per_tb, "", "run with one guest instruction per emulated TB"}, From 720dbc31cb424ba6364e99a340fca805d1837bfa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 07:55:10 +1000 Subject: [PATCH 0399/1794] linux-user/x86_64: Convert target_elf_gregset_t to a struct A structure typedef may be abstract, while an array typedef cannot. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 62 +++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index fce4c05674398..ba205c5a19477 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -154,7 +154,9 @@ typedef abi_int target_pid_t; #define ELF_ARCH EM_X86_64 #define ELF_NREG 27 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; /* * Note that ELF_NREG should be 29 as there should be place for @@ -163,35 +165,35 @@ typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; * * See linux kernel: arch/x86/include/asm/elf.h */ -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *env) -{ - (*regs)[0] = tswapreg(env->regs[15]); - (*regs)[1] = tswapreg(env->regs[14]); - (*regs)[2] = tswapreg(env->regs[13]); - (*regs)[3] = tswapreg(env->regs[12]); - (*regs)[4] = tswapreg(env->regs[R_EBP]); - (*regs)[5] = tswapreg(env->regs[R_EBX]); - (*regs)[6] = tswapreg(env->regs[11]); - (*regs)[7] = tswapreg(env->regs[10]); - (*regs)[8] = tswapreg(env->regs[9]); - (*regs)[9] = tswapreg(env->regs[8]); - (*regs)[10] = tswapreg(env->regs[R_EAX]); - (*regs)[11] = tswapreg(env->regs[R_ECX]); - (*regs)[12] = tswapreg(env->regs[R_EDX]); - (*regs)[13] = tswapreg(env->regs[R_ESI]); - (*regs)[14] = tswapreg(env->regs[R_EDI]); - (*regs)[15] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); - (*regs)[16] = tswapreg(env->eip); - (*regs)[17] = tswapreg(env->segs[R_CS].selector & 0xffff); - (*regs)[18] = tswapreg(env->eflags); - (*regs)[19] = tswapreg(env->regs[R_ESP]); - (*regs)[20] = tswapreg(env->segs[R_SS].selector & 0xffff); - (*regs)[21] = tswapreg(env->segs[R_FS].selector & 0xffff); - (*regs)[22] = tswapreg(env->segs[R_GS].selector & 0xffff); - (*regs)[23] = tswapreg(env->segs[R_DS].selector & 0xffff); - (*regs)[24] = tswapreg(env->segs[R_ES].selector & 0xffff); - (*regs)[25] = tswapreg(env->segs[R_FS].selector & 0xffff); - (*regs)[26] = tswapreg(env->segs[R_GS].selector & 0xffff); +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) +{ + r->regs[0] = tswapreg(env->regs[15]); + r->regs[1] = tswapreg(env->regs[14]); + r->regs[2] = tswapreg(env->regs[13]); + r->regs[3] = tswapreg(env->regs[12]); + r->regs[4] = tswapreg(env->regs[R_EBP]); + r->regs[5] = tswapreg(env->regs[R_EBX]); + r->regs[6] = tswapreg(env->regs[11]); + r->regs[7] = tswapreg(env->regs[10]); + r->regs[8] = tswapreg(env->regs[9]); + r->regs[9] = tswapreg(env->regs[8]); + r->regs[10] = tswapreg(env->regs[R_EAX]); + r->regs[11] = tswapreg(env->regs[R_ECX]); + r->regs[12] = tswapreg(env->regs[R_EDX]); + r->regs[13] = tswapreg(env->regs[R_ESI]); + r->regs[14] = tswapreg(env->regs[R_EDI]); + r->regs[15] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); + r->regs[16] = tswapreg(env->eip); + r->regs[17] = tswapreg(env->segs[R_CS].selector & 0xffff); + r->regs[18] = tswapreg(env->eflags); + r->regs[19] = tswapreg(env->regs[R_ESP]); + r->regs[20] = tswapreg(env->segs[R_SS].selector & 0xffff); + r->regs[21] = tswapreg(env->segs[R_FS].selector & 0xffff); + r->regs[22] = tswapreg(env->segs[R_GS].selector & 0xffff); + r->regs[23] = tswapreg(env->segs[R_DS].selector & 0xffff); + r->regs[24] = tswapreg(env->segs[R_ES].selector & 0xffff); + r->regs[25] = tswapreg(env->segs[R_FS].selector & 0xffff); + r->regs[26] = tswapreg(env->segs[R_GS].selector & 0xffff); } #if ULONG_MAX > UINT32_MAX From 4d8db2fa6f8e1e77fd88b1c3a4b0409adad630c0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 07:57:31 +1000 Subject: [PATCH 0400/1794] linux-user/i386: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index ba205c5a19477..e8a7f040ed41a 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -233,7 +233,9 @@ static bool init_guest_commpage(void) #define EXSTACK_DEFAULT true #define ELF_NREG 17 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; /* * Note that ELF_NREG should be 19 as there should be place for @@ -242,25 +244,25 @@ typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; * * See linux kernel: arch/x86/include/asm/elf.h */ -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *env) -{ - (*regs)[0] = tswapreg(env->regs[R_EBX]); - (*regs)[1] = tswapreg(env->regs[R_ECX]); - (*regs)[2] = tswapreg(env->regs[R_EDX]); - (*regs)[3] = tswapreg(env->regs[R_ESI]); - (*regs)[4] = tswapreg(env->regs[R_EDI]); - (*regs)[5] = tswapreg(env->regs[R_EBP]); - (*regs)[6] = tswapreg(env->regs[R_EAX]); - (*regs)[7] = tswapreg(env->segs[R_DS].selector & 0xffff); - (*regs)[8] = tswapreg(env->segs[R_ES].selector & 0xffff); - (*regs)[9] = tswapreg(env->segs[R_FS].selector & 0xffff); - (*regs)[10] = tswapreg(env->segs[R_GS].selector & 0xffff); - (*regs)[11] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); - (*regs)[12] = tswapreg(env->eip); - (*regs)[13] = tswapreg(env->segs[R_CS].selector & 0xffff); - (*regs)[14] = tswapreg(env->eflags); - (*regs)[15] = tswapreg(env->regs[R_ESP]); - (*regs)[16] = tswapreg(env->segs[R_SS].selector & 0xffff); +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) +{ + r->regs[0] = tswapreg(env->regs[R_EBX]); + r->regs[1] = tswapreg(env->regs[R_ECX]); + r->regs[2] = tswapreg(env->regs[R_EDX]); + r->regs[3] = tswapreg(env->regs[R_ESI]); + r->regs[4] = tswapreg(env->regs[R_EDI]); + r->regs[5] = tswapreg(env->regs[R_EBP]); + r->regs[6] = tswapreg(env->regs[R_EAX]); + r->regs[7] = tswapreg(env->segs[R_DS].selector & 0xffff); + r->regs[8] = tswapreg(env->segs[R_ES].selector & 0xffff); + r->regs[9] = tswapreg(env->segs[R_FS].selector & 0xffff); + r->regs[10] = tswapreg(env->segs[R_GS].selector & 0xffff); + r->regs[11] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); + r->regs[12] = tswapreg(env->eip); + r->regs[13] = tswapreg(env->segs[R_CS].selector & 0xffff); + r->regs[14] = tswapreg(env->eflags); + r->regs[15] = tswapreg(env->regs[R_ESP]); + r->regs[16] = tswapreg(env->segs[R_SS].selector & 0xffff); } /* From ea37ee2b2659a7fa4b1161112c0be3559180ccdd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:02:18 +1000 Subject: [PATCH 0401/1794] linux-user/arm: Convert target_elf_gregset_t to a struct While we're at it, loop over the general registers rather than open-code them. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index e8a7f040ed41a..0180f6063f12b 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -296,29 +296,17 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) #define EXSTACK_DEFAULT true #define ELF_NREG 18 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUARMState *env) -{ - (*regs)[0] = tswapreg(env->regs[0]); - (*regs)[1] = tswapreg(env->regs[1]); - (*regs)[2] = tswapreg(env->regs[2]); - (*regs)[3] = tswapreg(env->regs[3]); - (*regs)[4] = tswapreg(env->regs[4]); - (*regs)[5] = tswapreg(env->regs[5]); - (*regs)[6] = tswapreg(env->regs[6]); - (*regs)[7] = tswapreg(env->regs[7]); - (*regs)[8] = tswapreg(env->regs[8]); - (*regs)[9] = tswapreg(env->regs[9]); - (*regs)[10] = tswapreg(env->regs[10]); - (*regs)[11] = tswapreg(env->regs[11]); - (*regs)[12] = tswapreg(env->regs[12]); - (*regs)[13] = tswapreg(env->regs[13]); - (*regs)[14] = tswapreg(env->regs[14]); - (*regs)[15] = tswapreg(env->regs[15]); - - (*regs)[16] = tswapreg(cpsr_read((CPUARMState *)env)); - (*regs)[17] = tswapreg(env->regs[0]); /* XXX */ +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) +{ + for (int i = 0; i < 16; ++i) { + r->regs[i] = tswapreg(env->regs[i]); + } + r->regs[16] = tswapreg(cpsr_read((CPUARMState *)env)); + r->regs[17] = tswapreg(env->regs[0]); /* XXX */ } #define USE_ELF_CORE_DUMP From 0b3357425cef78233ec0c574990a4e70e53c30e6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:03:45 +1000 Subject: [PATCH 0402/1794] linux-user/aarch64: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0180f6063f12b..da57c6c2ce7f6 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -384,18 +384,17 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_CLASS ELFCLASS64 #define ELF_NREG 34 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *regs, - const CPUARMState *env) +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) { - int i; - - for (i = 0; i < 32; i++) { - (*regs)[i] = tswapreg(env->xregs[i]); + for (int i = 0; i < 32; i++) { + r->regs[i] = tswapreg(env->xregs[i]); } - (*regs)[32] = tswapreg(env->pc); - (*regs)[33] = tswapreg(pstate_read((CPUARMState *)env)); + r->regs[32] = tswapreg(env->pc); + r->regs[33] = tswapreg(pstate_read((CPUARMState *)env)); } #define USE_ELF_CORE_DUMP From 544843f2e76b2d40535b8b3d14007371cbd868f8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:05:47 +1000 Subject: [PATCH 0403/1794] linux-user/ppc: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index da57c6c2ce7f6..0dd76937f968c 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -470,25 +470,27 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) /* See linux kernel: arch/powerpc/include/asm/elf.h. */ #define ELF_NREG 48 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *env) +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) { int i; target_ulong ccr = 0; for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - (*regs)[i] = tswapreg(env->gpr[i]); + r->regs[i] = tswapreg(env->gpr[i]); } - (*regs)[32] = tswapreg(env->nip); - (*regs)[33] = tswapreg(env->msr); - (*regs)[35] = tswapreg(env->ctr); - (*regs)[36] = tswapreg(env->lr); - (*regs)[37] = tswapreg(cpu_read_xer(env)); + r->regs[32] = tswapreg(env->nip); + r->regs[33] = tswapreg(env->msr); + r->regs[35] = tswapreg(env->ctr); + r->regs[36] = tswapreg(env->lr); + r->regs[37] = tswapreg(cpu_read_xer(env)); ccr = ppc_get_cr(env); - (*regs)[38] = tswapreg(ccr); + r->regs[38] = tswapreg(ccr); } #define USE_ELF_CORE_DUMP From 68bb444d358c8cbe7bf4d8f942491ff01f132443 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:07:32 +1000 Subject: [PATCH 0404/1794] linux-user/loongarch64: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0dd76937f968c..1e59399afaf25 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -518,7 +518,9 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) /* See linux kernel: arch/loongarch/include/asm/elf.h */ #define ELF_NREG 45 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; enum { TARGET_EF_R0 = 0, @@ -526,19 +528,17 @@ enum { TARGET_EF_CSR_BADV = TARGET_EF_R0 + 34, }; -static void elf_core_copy_regs(target_elf_gregset_t *regs, +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPULoongArchState *env) { - int i; - - (*regs)[TARGET_EF_R0] = 0; + r->regs[TARGET_EF_R0] = 0; - for (i = 1; i < ARRAY_SIZE(env->gpr); i++) { - (*regs)[TARGET_EF_R0 + i] = tswapreg(env->gpr[i]); + for (int i = 1; i < ARRAY_SIZE(env->gpr); i++) { + r->regs[TARGET_EF_R0 + i] = tswapreg(env->gpr[i]); } - (*regs)[TARGET_EF_CSR_ERA] = tswapreg(env->pc); - (*regs)[TARGET_EF_CSR_BADV] = tswapreg(env->CSR_BADV); + r->regs[TARGET_EF_CSR_ERA] = tswapreg(env->pc); + r->regs[TARGET_EF_CSR_BADV] = tswapreg(env->CSR_BADV); } #define USE_ELF_CORE_DUMP From 5c86402eef21c09c65c23158ddebd791fdf603c9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:10:05 +1000 Subject: [PATCH 0405/1794] linux-user/mips: Convert target_elf_gregset_t to a struct While we're at it, merge the store of TARGET_EF_R0 into the loop over all R0 registers. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 1e59399afaf25..8fcdb0569bdc0 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -564,7 +564,9 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, /* See linux kernel: arch/mips/include/asm/elf.h. */ #define ELF_NREG 45 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; /* See linux kernel: arch/mips/include/asm/reg.h. */ enum { @@ -584,27 +586,25 @@ enum { }; /* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMIPSState *env) +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) { int i; - for (i = 0; i < TARGET_EF_R0; i++) { - (*regs)[i] = 0; + for (i = 0; i <= TARGET_EF_R0; i++) { + r->regs[i] = 0; } - (*regs)[TARGET_EF_R0] = 0; - for (i = 1; i < ARRAY_SIZE(env->active_tc.gpr); i++) { - (*regs)[TARGET_EF_R0 + i] = tswapreg(env->active_tc.gpr[i]); + r->regs[TARGET_EF_R0 + i] = tswapreg(env->active_tc.gpr[i]); } - (*regs)[TARGET_EF_R26] = 0; - (*regs)[TARGET_EF_R27] = 0; - (*regs)[TARGET_EF_LO] = tswapreg(env->active_tc.LO[0]); - (*regs)[TARGET_EF_HI] = tswapreg(env->active_tc.HI[0]); - (*regs)[TARGET_EF_CP0_EPC] = tswapreg(env->active_tc.PC); - (*regs)[TARGET_EF_CP0_BADVADDR] = tswapreg(env->CP0_BadVAddr); - (*regs)[TARGET_EF_CP0_STATUS] = tswapreg(env->CP0_Status); - (*regs)[TARGET_EF_CP0_CAUSE] = tswapreg(env->CP0_Cause); + r->regs[TARGET_EF_R26] = 0; + r->regs[TARGET_EF_R27] = 0; + r->regs[TARGET_EF_LO] = tswapreg(env->active_tc.LO[0]); + r->regs[TARGET_EF_HI] = tswapreg(env->active_tc.HI[0]); + r->regs[TARGET_EF_CP0_EPC] = tswapreg(env->active_tc.PC); + r->regs[TARGET_EF_CP0_BADVADDR] = tswapreg(env->CP0_BadVAddr); + r->regs[TARGET_EF_CP0_STATUS] = tswapreg(env->CP0_Status); + r->regs[TARGET_EF_CP0_CAUSE] = tswapreg(env->CP0_Cause); } #define USE_ELF_CORE_DUMP From b4ad80d6d3355f2ab7f02381f8eb697609b89e93 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:13:46 +1000 Subject: [PATCH 0406/1794] linux-user/microblaze: Convert target_elf_gregset_t to a struct While we're at it, drop "pos++" and simply open-code indexes. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8fcdb0569bdc0..40a5bcccab6db 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -623,23 +623,23 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) #define USE_ELF_CORE_DUMP #define ELF_NREG 38 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; /* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMBState *env) +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) { - int i, pos = 0; - - for (i = 0; i < 32; i++) { - (*regs)[pos++] = tswapreg(env->regs[i]); + for (int i = 0; i < 32; i++) { + r->regs[i] = tswapreg(env->regs[i]); } - (*regs)[pos++] = tswapreg(env->pc); - (*regs)[pos++] = tswapreg(mb_cpu_read_msr(env)); - (*regs)[pos++] = 0; - (*regs)[pos++] = tswapreg(env->ear); - (*regs)[pos++] = 0; - (*regs)[pos++] = tswapreg(env->esr); + r->regs[32] = tswapreg(env->pc); + r->regs[33] = tswapreg(mb_cpu_read_msr(env)); + r->regs[34] = 0; + r->regs[35] = tswapreg(env->ear); + r->regs[36] = 0; + r->regs[37] = tswapreg(env->esr); } #endif /* TARGET_MICROBLAZE */ From aebdd808e6288d78d7393bf1d4b49aaea93f786c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:15:44 +1000 Subject: [PATCH 0407/1794] linux-user/openrisc: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 40a5bcccab6db..da034e5a76e38 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -655,18 +655,18 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) /* See linux kernel arch/openrisc/include/asm/elf.h. */ #define ELF_NREG 34 /* gprs and pc, sr */ -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *regs, +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUOpenRISCState *env) { - int i; - - for (i = 0; i < 32; i++) { - (*regs)[i] = tswapreg(cpu_get_gpr(env, i)); + for (int i = 0; i < 32; i++) { + r->regs[i] = tswapreg(cpu_get_gpr(env, i)); } - (*regs)[32] = tswapreg(env->pc); - (*regs)[33] = tswapreg(cpu_get_sr(env)); + r->regs[32] = tswapreg(env->pc); + r->regs[33] = tswapreg(cpu_get_sr(env)); } #endif /* TARGET_OPENRISC */ From 7bd01645d66c23ef5494dcc8ea13d15bc90d82f5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:17:40 +1000 Subject: [PATCH 0408/1794] linux-user/sh4: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index da034e5a76e38..cc9140bf32fea 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -678,7 +678,9 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, /* See linux kernel: arch/sh/include/asm/elf.h. */ #define ELF_NREG 23 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; /* See linux kernel: arch/sh/include/asm/ptrace.h. */ enum { @@ -691,22 +693,19 @@ enum { TARGET_REG_SYSCALL = 22 }; -static inline void elf_core_copy_regs(target_elf_gregset_t *regs, - const CPUSH4State *env) +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) { - int i; - - for (i = 0; i < 16; i++) { - (*regs)[i] = tswapreg(env->gregs[i]); + for (int i = 0; i < 16; i++) { + r->regs[i] = tswapreg(env->gregs[i]); } - (*regs)[TARGET_REG_PC] = tswapreg(env->pc); - (*regs)[TARGET_REG_PR] = tswapreg(env->pr); - (*regs)[TARGET_REG_SR] = tswapreg(env->sr); - (*regs)[TARGET_REG_GBR] = tswapreg(env->gbr); - (*regs)[TARGET_REG_MACH] = tswapreg(env->mach); - (*regs)[TARGET_REG_MACL] = tswapreg(env->macl); - (*regs)[TARGET_REG_SYSCALL] = 0; /* FIXME */ + r->regs[TARGET_REG_PC] = tswapreg(env->pc); + r->regs[TARGET_REG_PR] = tswapreg(env->pr); + r->regs[TARGET_REG_SR] = tswapreg(env->sr); + r->regs[TARGET_REG_GBR] = tswapreg(env->gbr); + r->regs[TARGET_REG_MACH] = tswapreg(env->mach); + r->regs[TARGET_REG_MACL] = tswapreg(env->macl); + r->regs[TARGET_REG_SYSCALL] = 0; /* FIXME */ } #define USE_ELF_CORE_DUMP From a9caaa69f081c6ae0a1adead578a757be5dec98b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:19:30 +1000 Subject: [PATCH 0409/1794] linux-user/m68k: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 48 +++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index cc9140bf32fea..63376fa1d658b 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -720,30 +720,32 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) /* See linux kernel: arch/m68k/include/asm/elf.h. */ #define ELF_NREG 20 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUM68KState *env) -{ - (*regs)[0] = tswapreg(env->dregs[1]); - (*regs)[1] = tswapreg(env->dregs[2]); - (*regs)[2] = tswapreg(env->dregs[3]); - (*regs)[3] = tswapreg(env->dregs[4]); - (*regs)[4] = tswapreg(env->dregs[5]); - (*regs)[5] = tswapreg(env->dregs[6]); - (*regs)[6] = tswapreg(env->dregs[7]); - (*regs)[7] = tswapreg(env->aregs[0]); - (*regs)[8] = tswapreg(env->aregs[1]); - (*regs)[9] = tswapreg(env->aregs[2]); - (*regs)[10] = tswapreg(env->aregs[3]); - (*regs)[11] = tswapreg(env->aregs[4]); - (*regs)[12] = tswapreg(env->aregs[5]); - (*regs)[13] = tswapreg(env->aregs[6]); - (*regs)[14] = tswapreg(env->dregs[0]); - (*regs)[15] = tswapreg(env->aregs[7]); - (*regs)[16] = tswapreg(env->dregs[0]); /* FIXME: orig_d0 */ - (*regs)[17] = tswapreg(env->sr); - (*regs)[18] = tswapreg(env->pc); - (*regs)[19] = 0; /* FIXME: regs->format | regs->vector */ +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) +{ + r->regs[0] = tswapreg(env->dregs[1]); + r->regs[1] = tswapreg(env->dregs[2]); + r->regs[2] = tswapreg(env->dregs[3]); + r->regs[3] = tswapreg(env->dregs[4]); + r->regs[4] = tswapreg(env->dregs[5]); + r->regs[5] = tswapreg(env->dregs[6]); + r->regs[6] = tswapreg(env->dregs[7]); + r->regs[7] = tswapreg(env->aregs[0]); + r->regs[8] = tswapreg(env->aregs[1]); + r->regs[9] = tswapreg(env->aregs[2]); + r->regs[10] = tswapreg(env->aregs[3]); + r->regs[11] = tswapreg(env->aregs[4]); + r->regs[12] = tswapreg(env->aregs[5]); + r->regs[13] = tswapreg(env->aregs[6]); + r->regs[14] = tswapreg(env->dregs[0]); + r->regs[15] = tswapreg(env->aregs[7]); + r->regs[16] = tswapreg(env->dregs[0]); /* FIXME: orig_d0 */ + r->regs[17] = tswapreg(env->sr); + r->regs[18] = tswapreg(env->pc); + r->regs[19] = 0; /* FIXME: regs->format | regs->vector */ } #define USE_ELF_CORE_DUMP From f2accdfac4ca431dd58054a82c2312bc58e984a2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:30:25 +1000 Subject: [PATCH 0410/1794] linux-user/s390x: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 63376fa1d658b..98c17d32e60be 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -770,7 +770,9 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) /* See linux kernel: arch/s390/include/uapi/asm/ptrace.h (s390_regs). */ #define ELF_NREG 27 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; enum { TARGET_REG_PSWM = 0, @@ -780,22 +782,22 @@ enum { TARGET_REG_ORIG_R2 = 26, }; -static void elf_core_copy_regs(target_elf_gregset_t *regs, +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUS390XState *env) { int i; uint32_t *aregs; - (*regs)[TARGET_REG_PSWM] = tswapreg(env->psw.mask); - (*regs)[TARGET_REG_PSWA] = tswapreg(env->psw.addr); + r->regs[TARGET_REG_PSWM] = tswapreg(env->psw.mask); + r->regs[TARGET_REG_PSWA] = tswapreg(env->psw.addr); for (i = 0; i < 16; i++) { - (*regs)[TARGET_REG_GPRS + i] = tswapreg(env->regs[i]); + r->regs[TARGET_REG_GPRS + i] = tswapreg(env->regs[i]); } - aregs = (uint32_t *)&((*regs)[TARGET_REG_ARS]); + aregs = (uint32_t *)&(r->regs[TARGET_REG_ARS]); for (i = 0; i < 16; i++) { aregs[i] = tswap32(env->aregs[i]); } - (*regs)[TARGET_REG_ORIG_R2] = 0; + r->regs[TARGET_REG_ORIG_R2] = 0; } #define USE_ELF_CORE_DUMP From 8caa6621109ea1fb71870aa52c688de1f7dc64e9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:32:11 +1000 Subject: [PATCH 0411/1794] linux-user/xtensa: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 98c17d32e60be..930701f08fd2f 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -873,7 +873,9 @@ static bool init_guest_commpage(void) /* See linux kernel: arch/xtensa/include/asm/elf.h. */ #define ELF_NREG 128 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; enum { TARGET_REG_PC, @@ -888,23 +890,23 @@ enum { TARGET_REG_AR0 = 64, }; -static void elf_core_copy_regs(target_elf_gregset_t *regs, +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUXtensaState *env) { unsigned i; - (*regs)[TARGET_REG_PC] = tswapreg(env->pc); - (*regs)[TARGET_REG_PS] = tswapreg(env->sregs[PS] & ~PS_EXCM); - (*regs)[TARGET_REG_LBEG] = tswapreg(env->sregs[LBEG]); - (*regs)[TARGET_REG_LEND] = tswapreg(env->sregs[LEND]); - (*regs)[TARGET_REG_LCOUNT] = tswapreg(env->sregs[LCOUNT]); - (*regs)[TARGET_REG_SAR] = tswapreg(env->sregs[SAR]); - (*regs)[TARGET_REG_WINDOWSTART] = tswapreg(env->sregs[WINDOW_START]); - (*regs)[TARGET_REG_WINDOWBASE] = tswapreg(env->sregs[WINDOW_BASE]); - (*regs)[TARGET_REG_THREADPTR] = tswapreg(env->uregs[THREADPTR]); + r->regs[TARGET_REG_PC] = tswapreg(env->pc); + r->regs[TARGET_REG_PS] = tswapreg(env->sregs[PS] & ~PS_EXCM); + r->regs[TARGET_REG_LBEG] = tswapreg(env->sregs[LBEG]); + r->regs[TARGET_REG_LEND] = tswapreg(env->sregs[LEND]); + r->regs[TARGET_REG_LCOUNT] = tswapreg(env->sregs[LCOUNT]); + r->regs[TARGET_REG_SAR] = tswapreg(env->sregs[SAR]); + r->regs[TARGET_REG_WINDOWSTART] = tswapreg(env->sregs[WINDOW_START]); + r->regs[TARGET_REG_WINDOWBASE] = tswapreg(env->sregs[WINDOW_BASE]); + r->regs[TARGET_REG_THREADPTR] = tswapreg(env->uregs[THREADPTR]); xtensa_sync_phys_from_window((CPUXtensaState *)env); for (i = 0; i < env->config->nareg; ++i) { - (*regs)[TARGET_REG_AR0 + i] = tswapreg(env->phys_regs[i]); + r->regs[TARGET_REG_AR0 + i] = tswapreg(env->phys_regs[i]); } } From 1b32b3d7d8d438ee46313312aa62ae8b89c90bcb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:37:17 +1000 Subject: [PATCH 0412/1794] linux-user: Update comment for target_elf_gregset_t The only thing now used by generic core dump code is target_elf_gregset_t; ELF_NREG and target_elf_greg_t are now private to the implementation. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 930701f08fd2f..74f88dfa686dd 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2859,12 +2859,8 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) * * #define USE_ELF_CORE_DUMP * - * Next you define type of register set used for dumping. ELF specification - * says that it needs to be array of elf_greg_t that has size of ELF_NREG. - * - * typedef target_elf_greg_t; - * #define ELF_NREG - * typedef taret_elf_greg_t target_elf_gregset_t[ELF_NREG]; + * Next you define type of register set used for dumping: + * typedef struct target_elf_gregset_t { ... } target_elf_gregset_t; * * Last step is to implement target specific function that copies registers * from given cpu into just specified register set. Prototype is: From 553bf7dbd466794955f93c07b5c6fc0a7f65abb4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 2 Aug 2025 19:38:28 +1000 Subject: [PATCH 0413/1794] linux-user: Declare elf_core_copy_regs in loader.h Drop the static from all implementations. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 33 ++++++++++++++------------------- linux-user/loader.h | 3 +++ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 74f88dfa686dd..5ed5b3c544638 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -165,7 +165,7 @@ typedef struct target_elf_gregset_t { * * See linux kernel: arch/x86/include/asm/elf.h */ -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) { r->regs[0] = tswapreg(env->regs[15]); r->regs[1] = tswapreg(env->regs[14]); @@ -244,7 +244,7 @@ typedef struct target_elf_gregset_t { * * See linux kernel: arch/x86/include/asm/elf.h */ -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) { r->regs[0] = tswapreg(env->regs[R_EBX]); r->regs[1] = tswapreg(env->regs[R_ECX]); @@ -300,7 +300,7 @@ typedef struct target_elf_gregset_t { target_elf_greg_t regs[ELF_NREG]; } target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) { for (int i = 0; i < 16; ++i) { r->regs[i] = tswapreg(env->regs[i]); @@ -388,7 +388,7 @@ typedef struct target_elf_gregset_t { target_elf_greg_t regs[ELF_NREG]; } target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) { for (int i = 0; i < 32; i++) { r->regs[i] = tswapreg(env->xregs[i]); @@ -474,7 +474,7 @@ typedef struct target_elf_gregset_t { target_elf_greg_t regs[ELF_NREG]; } target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) { int i; target_ulong ccr = 0; @@ -528,8 +528,7 @@ enum { TARGET_EF_CSR_BADV = TARGET_EF_R0 + 34, }; -static void elf_core_copy_regs(target_elf_gregset_t *r, - const CPULoongArchState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPULoongArchState *env) { r->regs[TARGET_EF_R0] = 0; @@ -586,7 +585,7 @@ enum { }; /* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) { int i; @@ -628,7 +627,7 @@ typedef struct target_elf_gregset_t { } target_elf_gregset_t; /* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) { for (int i = 0; i < 32; i++) { r->regs[i] = tswapreg(env->regs[i]); @@ -659,8 +658,7 @@ typedef struct target_elf_gregset_t { target_elf_greg_t regs[ELF_NREG]; } target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *r, - const CPUOpenRISCState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUOpenRISCState *env) { for (int i = 0; i < 32; i++) { r->regs[i] = tswapreg(cpu_get_gpr(env, i)); @@ -693,7 +691,7 @@ enum { TARGET_REG_SYSCALL = 22 }; -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) { for (int i = 0; i < 16; i++) { r->regs[i] = tswapreg(env->gregs[i]); @@ -724,7 +722,7 @@ typedef struct target_elf_gregset_t { target_elf_greg_t regs[ELF_NREG]; } target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) { r->regs[0] = tswapreg(env->dregs[1]); r->regs[1] = tswapreg(env->dregs[2]); @@ -782,8 +780,7 @@ enum { TARGET_REG_ORIG_R2 = 26, }; -static void elf_core_copy_regs(target_elf_gregset_t *r, - const CPUS390XState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUS390XState *env) { int i; uint32_t *aregs; @@ -890,8 +887,7 @@ enum { TARGET_REG_AR0 = 64, }; -static void elf_core_copy_regs(target_elf_gregset_t *r, - const CPUXtensaState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUXtensaState *env) { unsigned i; @@ -2865,8 +2861,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) * Last step is to implement target specific function that copies registers * from given cpu into just specified register set. Prototype is: * - * static void elf_core_copy_regs(taret_elf_gregset_t *regs, - * const CPUArchState *env); + * void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUArchState *env); * * Parameters: * regs - copy register values into here (allocated and zeroed by caller) diff --git a/linux-user/loader.h b/linux-user/loader.h index 6482c7c90ca4f..8f4a7f69acbb4 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -106,4 +106,7 @@ const char *elf_hwcap2_str(uint32_t bit); const char *get_elf_platform(CPUState *cs); const char *get_elf_base_platform(CPUState *cs); +struct target_elf_gregset_t; +void elf_core_copy_regs(struct target_elf_gregset_t *, const CPUArchState *); + #endif /* LINUX_USER_LOADER_H */ From bb130c1758863f9d5c825ed7bf21669c15d6deff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 09:46:08 +1000 Subject: [PATCH 0414/1794] linux-user: Rename USE_ELF_CORE_DUMP to HAVE_ELF_CORE_DUMP The other knobs in target_elf.h are all HAVE_*. Rename this USE_ELF_CORE_DUMP to match. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 5ed5b3c544638..af31a345947b0 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -281,7 +281,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) #define VDSO_HEADER "vdso.c.inc" -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_I386 */ @@ -309,7 +309,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) r->regs[17] = tswapreg(env->regs[0]); /* XXX */ } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 /* The commpage only exists for 32 bit kernels */ @@ -397,7 +397,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) r->regs[33] = tswapreg(pstate_read((CPUARMState *)env)); } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #if TARGET_BIG_ENDIAN @@ -493,7 +493,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) r->regs[38] = tswapreg(ccr); } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #ifndef TARGET_PPC64 @@ -540,7 +540,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPULoongArchState *env) r->regs[TARGET_EF_CSR_BADV] = tswapreg(env->CSR_BADV); } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_LOONGARCH64 */ @@ -606,7 +606,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) r->regs[TARGET_EF_CP0_CAUSE] = tswapreg(env->CP0_Cause); } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_MIPS */ @@ -620,7 +620,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) #define ELF_EXEC_PAGESIZE 4096 -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_NREG 38 typedef struct target_elf_gregset_t { target_elf_greg_t regs[ELF_NREG]; @@ -649,7 +649,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) #define ELF_CLASS ELFCLASS32 #define ELF_DATA ELFDATA2MSB -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 /* See linux kernel arch/openrisc/include/asm/elf.h. */ @@ -706,7 +706,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) r->regs[TARGET_REG_SYSCALL] = 0; /* FIXME */ } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif @@ -746,7 +746,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) r->regs[19] = 0; /* FIXME: regs->format | regs->vector */ } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 #endif @@ -797,7 +797,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUS390XState *env) r->regs[TARGET_REG_ORIG_R2] = 0; } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #define VDSO_HEADER "vdso.c.inc" @@ -906,7 +906,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUXtensaState *env) } } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_XTENSA */ @@ -1115,9 +1115,9 @@ static void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags) } #endif -#ifdef USE_ELF_CORE_DUMP +#ifdef HAVE_ELF_CORE_DUMP static int elf_core_dump(int, const CPUArchState *); -#endif /* USE_ELF_CORE_DUMP */ +#endif /* HAVE_ELF_CORE_DUMP */ static void load_symbols(struct elfhdr *hdr, const ImageSource *src, abi_ulong load_bias); @@ -2827,14 +2827,14 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) g_free(elf_interpreter); } -#ifdef USE_ELF_CORE_DUMP +#ifdef HAVE_ELF_CORE_DUMP bprm->core_dump = &elf_core_dump; #endif return 0; } -#ifdef USE_ELF_CORE_DUMP +#ifdef HAVE_ELF_CORE_DUMP /* * Definitions to generate Intel SVR4-like core files. @@ -2850,10 +2850,10 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) * Core dump code is copied from linux kernel (fs/binfmt_elf.c). * * Porting ELF coredump for target is (quite) simple process. First you - * define USE_ELF_CORE_DUMP in target ELF code (where init_thread() for + * define HAVE_ELF_CORE_DUMP in target ELF code (where init_thread() for * the target resides): * - * #define USE_ELF_CORE_DUMP + * #define HAVE_ELF_CORE_DUMP * * Next you define type of register set used for dumping: * typedef struct target_elf_gregset_t { ... } target_elf_gregset_t; @@ -3392,4 +3392,4 @@ static int elf_core_dump(int signr, const CPUArchState *env) } return ret; } -#endif /* USE_ELF_CORE_DUMP */ +#endif /* HAVE_ELF_CORE_DUMP */ From 93c62ca6fe31dadbdb38688f67c741ee00448a24 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 09:48:07 +1000 Subject: [PATCH 0415/1794] linux-user: Move elf_core_copy_regs to {i386,x86_64}/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 77 ---------------------------------- linux-user/i386/elfload.c | 24 +++++++++++ linux-user/i386/target_elf.h | 15 +++++++ linux-user/x86_64/elfload.c | 34 +++++++++++++++ linux-user/x86_64/target_elf.h | 15 +++++++ 5 files changed, 88 insertions(+), 77 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index af31a345947b0..e4f821f8c8180 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -153,49 +153,6 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 -#define ELF_NREG 27 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -/* - * Note that ELF_NREG should be 29 as there should be place for - * TRAPNO and ERR "registers" as well but linux doesn't dump - * those. - * - * See linux kernel: arch/x86/include/asm/elf.h - */ -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) -{ - r->regs[0] = tswapreg(env->regs[15]); - r->regs[1] = tswapreg(env->regs[14]); - r->regs[2] = tswapreg(env->regs[13]); - r->regs[3] = tswapreg(env->regs[12]); - r->regs[4] = tswapreg(env->regs[R_EBP]); - r->regs[5] = tswapreg(env->regs[R_EBX]); - r->regs[6] = tswapreg(env->regs[11]); - r->regs[7] = tswapreg(env->regs[10]); - r->regs[8] = tswapreg(env->regs[9]); - r->regs[9] = tswapreg(env->regs[8]); - r->regs[10] = tswapreg(env->regs[R_EAX]); - r->regs[11] = tswapreg(env->regs[R_ECX]); - r->regs[12] = tswapreg(env->regs[R_EDX]); - r->regs[13] = tswapreg(env->regs[R_ESI]); - r->regs[14] = tswapreg(env->regs[R_EDI]); - r->regs[15] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); - r->regs[16] = tswapreg(env->eip); - r->regs[17] = tswapreg(env->segs[R_CS].selector & 0xffff); - r->regs[18] = tswapreg(env->eflags); - r->regs[19] = tswapreg(env->regs[R_ESP]); - r->regs[20] = tswapreg(env->segs[R_SS].selector & 0xffff); - r->regs[21] = tswapreg(env->segs[R_FS].selector & 0xffff); - r->regs[22] = tswapreg(env->segs[R_GS].selector & 0xffff); - r->regs[23] = tswapreg(env->segs[R_DS].selector & 0xffff); - r->regs[24] = tswapreg(env->segs[R_ES].selector & 0xffff); - r->regs[25] = tswapreg(env->segs[R_FS].selector & 0xffff); - r->regs[26] = tswapreg(env->segs[R_GS].selector & 0xffff); -} - #if ULONG_MAX > UINT32_MAX #define INIT_GUEST_COMMPAGE static bool init_guest_commpage(void) @@ -232,39 +189,6 @@ static bool init_guest_commpage(void) #define EXSTACK_DEFAULT true -#define ELF_NREG 17 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -/* - * Note that ELF_NREG should be 19 as there should be place for - * TRAPNO and ERR "registers" as well but linux doesn't dump - * those. - * - * See linux kernel: arch/x86/include/asm/elf.h - */ -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) -{ - r->regs[0] = tswapreg(env->regs[R_EBX]); - r->regs[1] = tswapreg(env->regs[R_ECX]); - r->regs[2] = tswapreg(env->regs[R_EDX]); - r->regs[3] = tswapreg(env->regs[R_ESI]); - r->regs[4] = tswapreg(env->regs[R_EDI]); - r->regs[5] = tswapreg(env->regs[R_EBP]); - r->regs[6] = tswapreg(env->regs[R_EAX]); - r->regs[7] = tswapreg(env->segs[R_DS].selector & 0xffff); - r->regs[8] = tswapreg(env->segs[R_ES].selector & 0xffff); - r->regs[9] = tswapreg(env->segs[R_FS].selector & 0xffff); - r->regs[10] = tswapreg(env->segs[R_GS].selector & 0xffff); - r->regs[11] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); - r->regs[12] = tswapreg(env->eip); - r->regs[13] = tswapreg(env->segs[R_CS].selector & 0xffff); - r->regs[14] = tswapreg(env->eflags); - r->regs[15] = tswapreg(env->regs[R_ESP]); - r->regs[16] = tswapreg(env->segs[R_SS].selector & 0xffff); -} - /* * i386 is the only target which supplies AT_SYSINFO for the vdso. * All others only supply AT_SYSINFO_EHDR. @@ -281,7 +205,6 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) #define VDSO_HEADER "vdso.c.inc" -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_I386 */ diff --git a/linux-user/i386/elfload.c b/linux-user/i386/elfload.c index ef3a6c35d20e9..279aeb8116d7c 100644 --- a/linux-user/i386/elfload.c +++ b/linux-user/i386/elfload.c @@ -3,6 +3,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -23,3 +24,26 @@ const char *get_elf_platform(CPUState *cs) family = MAX(MIN(family, 6), 3); return elf_platform[family - 3]; } + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) +{ + r->regs[0] = tswapreg(env->regs[R_EBX]); + r->regs[1] = tswapreg(env->regs[R_ECX]); + r->regs[2] = tswapreg(env->regs[R_EDX]); + r->regs[3] = tswapreg(env->regs[R_ESI]); + r->regs[4] = tswapreg(env->regs[R_EDI]); + r->regs[5] = tswapreg(env->regs[R_EBP]); + r->regs[6] = tswapreg(env->regs[R_EAX]); + r->regs[7] = tswapreg(env->segs[R_DS].selector & 0xffff); + r->regs[8] = tswapreg(env->segs[R_ES].selector & 0xffff); + r->regs[9] = tswapreg(env->segs[R_FS].selector & 0xffff); + r->regs[10] = tswapreg(env->segs[R_GS].selector & 0xffff); + r->regs[11] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); + r->regs[12] = tswapreg(env->eip); + r->regs[13] = tswapreg(env->segs[R_CS].selector & 0xffff); + r->regs[14] = tswapreg(env->eflags); + r->regs[15] = tswapreg(env->regs[R_ESP]); + r->regs[16] = tswapreg(env->segs[R_SS].selector & 0xffff); +} diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index 44dde1ac4a7ca..eb286868e116e 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -10,5 +10,20 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +/* + * Note that ELF_NREG should be 19 as there should be place for + * TRAPNO and ERR "registers" as well but linux doesn't dump those. + * + * See linux kernel: arch/x86/include/asm/elf.h + */ +#define ELF_NREG 17 + +typedef abi_ulong target_elf_greg_t; + +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c index 88541ea45e689..76cf5c15098c8 100644 --- a/linux-user/x86_64/elfload.c +++ b/linux-user/x86_64/elfload.c @@ -3,6 +3,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -19,3 +20,36 @@ const char *get_elf_platform(CPUState *cs) { return "x86_64"; } + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) +{ + r->regs[0] = tswapreg(env->regs[15]); + r->regs[1] = tswapreg(env->regs[14]); + r->regs[2] = tswapreg(env->regs[13]); + r->regs[3] = tswapreg(env->regs[12]); + r->regs[4] = tswapreg(env->regs[R_EBP]); + r->regs[5] = tswapreg(env->regs[R_EBX]); + r->regs[6] = tswapreg(env->regs[11]); + r->regs[7] = tswapreg(env->regs[10]); + r->regs[8] = tswapreg(env->regs[9]); + r->regs[9] = tswapreg(env->regs[8]); + r->regs[10] = tswapreg(env->regs[R_EAX]); + r->regs[11] = tswapreg(env->regs[R_ECX]); + r->regs[12] = tswapreg(env->regs[R_EDX]); + r->regs[13] = tswapreg(env->regs[R_ESI]); + r->regs[14] = tswapreg(env->regs[R_EDI]); + r->regs[15] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); + r->regs[16] = tswapreg(env->eip); + r->regs[17] = tswapreg(env->segs[R_CS].selector & 0xffff); + r->regs[18] = tswapreg(env->eflags); + r->regs[19] = tswapreg(env->regs[R_ESP]); + r->regs[20] = tswapreg(env->segs[R_SS].selector & 0xffff); + r->regs[21] = tswapreg(env->segs[R_FS].selector & 0xffff); + r->regs[22] = tswapreg(env->segs[R_GS].selector & 0xffff); + r->regs[23] = tswapreg(env->segs[R_DS].selector & 0xffff); + r->regs[24] = tswapreg(env->segs[R_ES].selector & 0xffff); + r->regs[25] = tswapreg(env->segs[R_FS].selector & 0xffff); + r->regs[26] = tswapreg(env->segs[R_GS].selector & 0xffff); +} diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index 498c3f7e4ec13..74a77d94cdc66 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -10,5 +10,20 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +/* + * Note that ELF_NREG should be 29 as there should be place for + * TRAPNO and ERR "registers" as well but linux doesn't dump those. + * + * See linux kernel: arch/x86/include/asm/elf.h + */ +#define ELF_NREG 27 + +typedef abi_ulong target_elf_greg_t; + +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif From 53c6724cc96acc64bf6213e5820e3cd610e7eaa4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 05:51:30 -1000 Subject: [PATCH 0416/1794] linux-user: Move elf_core_copy_regs to arm/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/arm/elfload.c | 12 ++++++++++++ linux-user/arm/target_elf.h | 8 ++++++++ linux-user/elfload.c | 15 --------------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index 7de1f13f4be25..47fe16a1a650e 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -4,6 +4,7 @@ #include "qemu.h" #include "loader.h" #include "target/arm/cpu-features.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -199,3 +200,14 @@ const char *get_elf_platform(CPUState *cs) #undef END } + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) +{ + for (int i = 0; i < 16; ++i) { + r->regs[i] = tswapreg(env->regs[i]); + } + r->regs[16] = tswapreg(cpsr_read((CPUARMState *)env)); + r->regs[17] = tswapreg(env->regs[0]); /* XXX */ +} diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index 856ca41b165b2..94db3738e8717 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -11,5 +11,13 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +#define ELF_NREG 18 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index e4f821f8c8180..72a291e51f493 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -218,21 +218,6 @@ static bool init_guest_commpage(void) #define ELF_CLASS ELFCLASS32 #define EXSTACK_DEFAULT true -#define ELF_NREG 18 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) -{ - for (int i = 0; i < 16; ++i) { - r->regs[i] = tswapreg(env->regs[i]); - } - r->regs[16] = tswapreg(cpsr_read((CPUARMState *)env)); - r->regs[17] = tswapreg(env->regs[0]); /* XXX */ -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 /* The commpage only exists for 32 bit kernels */ From b71b68b233dd828960e57702580a97b11f3fd26f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 05:53:29 -1000 Subject: [PATCH 0417/1794] linux-user: Move elf_core_copy_regs to aarch64/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/elfload.c | 12 ++++++++++++ linux-user/aarch64/target_elf.h | 8 ++++++++ linux-user/elfload.c | 15 --------------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 1030cb8094722..00550f9fdfae1 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -4,6 +4,7 @@ #include "qemu.h" #include "loader.h" #include "target/arm/cpu-features.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -347,3 +348,14 @@ const char *get_elf_platform(CPUState *cs) { return TARGET_BIG_ENDIAN ? "aarch64_be" : "aarch64"; } + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) +{ + for (int i = 0; i < 32; i++) { + r->regs[i] = tswapreg(env->xregs[i]); + } + r->regs[32] = tswapreg(env->pc); + r->regs[33] = tswapreg(pstate_read((CPUARMState *)env)); +} diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index dee79ce0c60ec..b0728a100824c 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -11,5 +11,13 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +#define ELF_NREG 34 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 72a291e51f493..017346b82d4e8 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -291,21 +291,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_ARCH EM_AARCH64 #define ELF_CLASS ELFCLASS64 -#define ELF_NREG 34 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) -{ - for (int i = 0; i < 32; i++) { - r->regs[i] = tswapreg(env->xregs[i]); - } - r->regs[32] = tswapreg(env->pc); - r->regs[33] = tswapreg(pstate_read((CPUARMState *)env)); -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #if TARGET_BIG_ENDIAN From a67e20d629a849d9d9d5ef527963462cd0718e78 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 05:55:56 -1000 Subject: [PATCH 0418/1794] linux-user: Move elf_core_copy_regs to ppc/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 26 -------------------------- linux-user/ppc/elfload.c | 22 ++++++++++++++++++++++ linux-user/ppc/target_elf.h | 9 +++++++++ 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 017346b82d4e8..d1d0a112fb86d 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -361,32 +361,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ } while (0) -/* See linux kernel: arch/powerpc/include/asm/elf.h. */ -#define ELF_NREG 48 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) -{ - int i; - target_ulong ccr = 0; - - for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - r->regs[i] = tswapreg(env->gpr[i]); - } - - r->regs[32] = tswapreg(env->nip); - r->regs[33] = tswapreg(env->msr); - r->regs[35] = tswapreg(env->ctr); - r->regs[36] = tswapreg(env->lr); - r->regs[37] = tswapreg(cpu_read_xer(env)); - - ccr = ppc_get_cr(env); - r->regs[38] = tswapreg(ccr); -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #ifndef TARGET_PPC64 diff --git a/linux-user/ppc/elfload.c b/linux-user/ppc/elfload.c index a214675650414..114e40a358aae 100644 --- a/linux-user/ppc/elfload.c +++ b/linux-user/ppc/elfload.c @@ -3,6 +3,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -129,3 +130,24 @@ abi_ulong get_elf_hwcap2(CPUState *cs) return features; } + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) +{ + int i; + target_ulong ccr = 0; + + for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { + r->regs[i] = tswapreg(env->gpr[i]); + } + + r->regs[32] = tswapreg(env->nip); + r->regs[33] = tswapreg(env->msr); + r->regs[35] = tswapreg(env->ctr); + r->regs[36] = tswapreg(env->lr); + r->regs[37] = tswapreg(cpu_read_xer(env)); + + ccr = ppc_get_cr(env); + r->regs[38] = tswapreg(ccr); +} diff --git a/linux-user/ppc/target_elf.h b/linux-user/ppc/target_elf.h index 4203a89d665f8..72615553ea53e 100644 --- a/linux-user/ppc/target_elf.h +++ b/linux-user/ppc/target_elf.h @@ -10,5 +10,14 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +/* See linux kernel: arch/powerpc/include/asm/elf.h. */ +#define ELF_NREG 48 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif From cf334829cb2a68ba1582923c773557b6fd20e123 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 05:58:48 -1000 Subject: [PATCH 0419/1794] linux-user: Move elf_core_copy_regs to loongarch64/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 25 ------------------------- linux-user/loongarch64/elfload.c | 21 +++++++++++++++++++++ linux-user/loongarch64/target_elf.h | 9 +++++++++ 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index d1d0a112fb86d..4acd7b9ffe154 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -383,31 +383,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define VDSO_HEADER "vdso.c.inc" -/* See linux kernel: arch/loongarch/include/asm/elf.h */ -#define ELF_NREG 45 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -enum { - TARGET_EF_R0 = 0, - TARGET_EF_CSR_ERA = TARGET_EF_R0 + 33, - TARGET_EF_CSR_BADV = TARGET_EF_R0 + 34, -}; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPULoongArchState *env) -{ - r->regs[TARGET_EF_R0] = 0; - - for (int i = 1; i < ARRAY_SIZE(env->gpr); i++) { - r->regs[TARGET_EF_R0 + i] = tswapreg(env->gpr[i]); - } - - r->regs[TARGET_EF_CSR_ERA] = tswapreg(env->pc); - r->regs[TARGET_EF_CSR_BADV] = tswapreg(env->CSR_BADV); -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_LOONGARCH64 */ diff --git a/linux-user/loongarch64/elfload.c b/linux-user/loongarch64/elfload.c index 911352840f710..832890de1002b 100644 --- a/linux-user/loongarch64/elfload.c +++ b/linux-user/loongarch64/elfload.c @@ -3,6 +3,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -61,3 +62,23 @@ const char *get_elf_platform(CPUState *cs) { return "loongarch"; } + +#define tswapreg(ptr) tswapal(ptr) + +enum { + TARGET_EF_R0 = 0, + TARGET_EF_CSR_ERA = TARGET_EF_R0 + 33, + TARGET_EF_CSR_BADV = TARGET_EF_R0 + 34, +}; + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPULoongArchState *env) +{ + r->regs[TARGET_EF_R0] = 0; + + for (int i = 1; i < ARRAY_SIZE(env->gpr); i++) { + r->regs[TARGET_EF_R0 + i] = tswapreg(env->gpr[i]); + } + + r->regs[TARGET_EF_CSR_ERA] = tswapreg(env->pc); + r->regs[TARGET_EF_CSR_BADV] = tswapreg(env->CSR_BADV); +} diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index eb179273255a7..90bca4499d2a3 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -8,5 +8,14 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +/* See linux kernel: arch/loongarch/include/asm/elf.h */ +#define ELF_NREG 45 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif From a8081da18de8f3558b593e9c1ff12b9319c1d892 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 06:03:38 -1000 Subject: [PATCH 0420/1794] linux-user: Move elf_core_copy_regs to mips/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 46 ---------------------------------- linux-user/mips/elfload.c | 46 ++++++++++++++++++++++++++++++++++ linux-user/mips/target_elf.h | 9 +++++++ linux-user/mips64/target_elf.h | 13 ++++++++++ 4 files changed, 68 insertions(+), 46 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 4acd7b9ffe154..5a3a5cfc39fcf 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -403,52 +403,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) #endif -/* See linux kernel: arch/mips/include/asm/elf.h. */ -#define ELF_NREG 45 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -/* See linux kernel: arch/mips/include/asm/reg.h. */ -enum { -#ifdef TARGET_MIPS64 - TARGET_EF_R0 = 0, -#else - TARGET_EF_R0 = 6, -#endif - TARGET_EF_R26 = TARGET_EF_R0 + 26, - TARGET_EF_R27 = TARGET_EF_R0 + 27, - TARGET_EF_LO = TARGET_EF_R0 + 32, - TARGET_EF_HI = TARGET_EF_R0 + 33, - TARGET_EF_CP0_EPC = TARGET_EF_R0 + 34, - TARGET_EF_CP0_BADVADDR = TARGET_EF_R0 + 35, - TARGET_EF_CP0_STATUS = TARGET_EF_R0 + 36, - TARGET_EF_CP0_CAUSE = TARGET_EF_R0 + 37 -}; - -/* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) -{ - int i; - - for (i = 0; i <= TARGET_EF_R0; i++) { - r->regs[i] = 0; - } - for (i = 1; i < ARRAY_SIZE(env->active_tc.gpr); i++) { - r->regs[TARGET_EF_R0 + i] = tswapreg(env->active_tc.gpr[i]); - } - - r->regs[TARGET_EF_R26] = 0; - r->regs[TARGET_EF_R27] = 0; - r->regs[TARGET_EF_LO] = tswapreg(env->active_tc.LO[0]); - r->regs[TARGET_EF_HI] = tswapreg(env->active_tc.HI[0]); - r->regs[TARGET_EF_CP0_EPC] = tswapreg(env->active_tc.PC); - r->regs[TARGET_EF_CP0_BADVADDR] = tswapreg(env->CP0_BadVAddr); - r->regs[TARGET_EF_CP0_STATUS] = tswapreg(env->CP0_Status); - r->regs[TARGET_EF_CP0_CAUSE] = tswapreg(env->CP0_Cause); -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_MIPS */ diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c index c353ccc1ad1a7..6e884911afa47 100644 --- a/linux-user/mips/elfload.c +++ b/linux-user/mips/elfload.c @@ -4,6 +4,7 @@ #include "qemu.h" #include "loader.h" #include "elf.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -122,3 +123,48 @@ const char *get_elf_base_platform(CPUState *cs) } #undef MATCH_PLATFORM_INSN + +#ifdef TARGET_ABI_MIPSN32 +#define tswapreg(ptr) tswap64(ptr) +#else +#define tswapreg(ptr) tswapal(ptr) +#endif + +/* See linux kernel: arch/mips/include/asm/reg.h. */ +enum { +#ifdef TARGET_MIPS64 + TARGET_EF_R0 = 0, +#else + TARGET_EF_R0 = 6, +#endif + TARGET_EF_R26 = TARGET_EF_R0 + 26, + TARGET_EF_R27 = TARGET_EF_R0 + 27, + TARGET_EF_LO = TARGET_EF_R0 + 32, + TARGET_EF_HI = TARGET_EF_R0 + 33, + TARGET_EF_CP0_EPC = TARGET_EF_R0 + 34, + TARGET_EF_CP0_BADVADDR = TARGET_EF_R0 + 35, + TARGET_EF_CP0_STATUS = TARGET_EF_R0 + 36, + TARGET_EF_CP0_CAUSE = TARGET_EF_R0 + 37 +}; + +/* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) +{ + int i; + + for (i = 0; i <= TARGET_EF_R0; i++) { + r->regs[i] = 0; + } + for (i = 1; i < ARRAY_SIZE(env->active_tc.gpr); i++) { + r->regs[TARGET_EF_R0 + i] = tswapreg(env->active_tc.gpr[i]); + } + + r->regs[TARGET_EF_R26] = 0; + r->regs[TARGET_EF_R27] = 0; + r->regs[TARGET_EF_LO] = tswapreg(env->active_tc.LO[0]); + r->regs[TARGET_EF_HI] = tswapreg(env->active_tc.HI[0]); + r->regs[TARGET_EF_CP0_EPC] = tswapreg(env->active_tc.PC); + r->regs[TARGET_EF_CP0_BADVADDR] = tswapreg(env->CP0_BadVAddr); + r->regs[TARGET_EF_CP0_STATUS] = tswapreg(env->CP0_Status); + r->regs[TARGET_EF_CP0_CAUSE] = tswapreg(env->CP0_Cause); +} diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index 08e699c08554e..f767767eaa82b 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -10,5 +10,14 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_BASE_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +/* See linux kernel: arch/mips/include/asm/elf.h. */ +#define ELF_NREG 45 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index 24bb7fcd3ff92..046a165eef2cd 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -10,5 +10,18 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_BASE_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +#ifdef TARGET_ABI_MIPSN32 +typedef abi_ullong target_elf_greg_t; +#else +typedef abi_ulong target_elf_greg_t; +#endif + +/* See linux kernel: arch/mips/include/asm/elf.h. */ +#define ELF_NREG 45 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif From e06b9c34eaa388e0503426c7831a2db977a472fd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 06:06:03 -1000 Subject: [PATCH 0421/1794] linux-user: Move elf_core_copy_regs to microblaze/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 21 --------------------- linux-user/microblaze/elfload.c | 17 +++++++++++++++++ linux-user/microblaze/target_elf.h | 9 +++++++++ 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 5a3a5cfc39fcf..96ed6b651544f 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -416,27 +416,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_EXEC_PAGESIZE 4096 -#define HAVE_ELF_CORE_DUMP -#define ELF_NREG 38 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -/* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) -{ - for (int i = 0; i < 32; i++) { - r->regs[i] = tswapreg(env->regs[i]); - } - - r->regs[32] = tswapreg(env->pc); - r->regs[33] = tswapreg(mb_cpu_read_msr(env)); - r->regs[34] = 0; - r->regs[35] = tswapreg(env->ear); - r->regs[36] = 0; - r->regs[37] = tswapreg(env->esr); -} - #endif /* TARGET_MICROBLAZE */ #ifdef TARGET_OPENRISC diff --git a/linux-user/microblaze/elfload.c b/linux-user/microblaze/elfload.c index b92442dfeb2a8..89250dbd631d4 100644 --- a/linux-user/microblaze/elfload.c +++ b/linux-user/microblaze/elfload.c @@ -3,9 +3,26 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) { return "any"; } + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) +{ + for (int i = 0; i < 32; i++) { + r->regs[i] = tswapreg(env->regs[i]); + } + + r->regs[32] = tswapreg(env->pc); + r->regs[33] = tswapreg(mb_cpu_read_msr(env)); + r->regs[34] = 0; + r->regs[35] = tswapreg(env->ear); + r->regs[36] = 0; + r->regs[37] = tswapreg(env->esr); +} diff --git a/linux-user/microblaze/target_elf.h b/linux-user/microblaze/target_elf.h index bfe2997fd237c..cc5cc0477e3a4 100644 --- a/linux-user/microblaze/target_elf.h +++ b/linux-user/microblaze/target_elf.h @@ -8,4 +8,13 @@ #ifndef MICROBLAZE_TARGET_ELF_H #define MICROBLAZE_TARGET_ELF_H +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +#define ELF_NREG 38 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; + #endif From 28c7d60b54d7e6e3afa064ceae7a4786375d8b4b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 06:12:01 -1000 Subject: [PATCH 0422/1794] linux-user: Move elf_core_copy_regs to openrisc/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 16 ---------------- linux-user/openrisc/elfload.c | 12 ++++++++++++ linux-user/openrisc/target_elf.h | 10 ++++++++++ 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 96ed6b651544f..8c3ef41312cc9 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -424,24 +424,8 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_CLASS ELFCLASS32 #define ELF_DATA ELFDATA2MSB -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 -/* See linux kernel arch/openrisc/include/asm/elf.h. */ -#define ELF_NREG 34 /* gprs and pc, sr */ -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUOpenRISCState *env) -{ - for (int i = 0; i < 32; i++) { - r->regs[i] = tswapreg(cpu_get_gpr(env, i)); - } - r->regs[32] = tswapreg(env->pc); - r->regs[33] = tswapreg(cpu_get_sr(env)); -} - #endif /* TARGET_OPENRISC */ #ifdef TARGET_SH4 diff --git a/linux-user/openrisc/elfload.c b/linux-user/openrisc/elfload.c index b92442dfeb2a8..bb5ad96711353 100644 --- a/linux-user/openrisc/elfload.c +++ b/linux-user/openrisc/elfload.c @@ -3,9 +3,21 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) { return "any"; } + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUOpenRISCState *env) +{ + for (int i = 0; i < 32; i++) { + r->regs[i] = tswapreg(cpu_get_gpr(env, i)); + } + r->regs[32] = tswapreg(env->pc); + r->regs[33] = tswapreg(cpu_get_sr(env)); +} diff --git a/linux-user/openrisc/target_elf.h b/linux-user/openrisc/target_elf.h index b34f2ff672da7..e97bdc11ed906 100644 --- a/linux-user/openrisc/target_elf.h +++ b/linux-user/openrisc/target_elf.h @@ -8,4 +8,14 @@ #ifndef OPENRISC_TARGET_ELF_H #define OPENRISC_TARGET_ELF_H +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +/* See linux kernel arch/openrisc/include/asm/elf.h. */ +#define ELF_NREG 34 /* gprs and pc, sr */ +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; + #endif From a4ea8c30e7c8ae49e2e50e2c410581ad53c3e1fb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 06:12:27 -1000 Subject: [PATCH 0423/1794] linux-user: Move elf_core_copy_regs to sh4/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 33 --------------------------------- linux-user/sh4/elfload.c | 29 +++++++++++++++++++++++++++++ linux-user/sh4/target_elf.h | 9 +++++++++ 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8c3ef41312cc9..69532faddbb0d 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -433,39 +433,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_SH -/* See linux kernel: arch/sh/include/asm/elf.h. */ -#define ELF_NREG 23 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -/* See linux kernel: arch/sh/include/asm/ptrace.h. */ -enum { - TARGET_REG_PC = 16, - TARGET_REG_PR = 17, - TARGET_REG_SR = 18, - TARGET_REG_GBR = 19, - TARGET_REG_MACH = 20, - TARGET_REG_MACL = 21, - TARGET_REG_SYSCALL = 22 -}; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) -{ - for (int i = 0; i < 16; i++) { - r->regs[i] = tswapreg(env->gregs[i]); - } - - r->regs[TARGET_REG_PC] = tswapreg(env->pc); - r->regs[TARGET_REG_PR] = tswapreg(env->pr); - r->regs[TARGET_REG_SR] = tswapreg(env->sr); - r->regs[TARGET_REG_GBR] = tswapreg(env->gbr); - r->regs[TARGET_REG_MACH] = tswapreg(env->mach); - r->regs[TARGET_REG_MACL] = tswapreg(env->macl); - r->regs[TARGET_REG_SYSCALL] = 0; /* FIXME */ -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif diff --git a/linux-user/sh4/elfload.c b/linux-user/sh4/elfload.c index 99ad4f6334c79..71cae9703e727 100644 --- a/linux-user/sh4/elfload.c +++ b/linux-user/sh4/elfload.c @@ -3,6 +3,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -36,3 +37,31 @@ abi_ulong get_elf_hwcap(CPUState *cs) return hwcap; } + +#define tswapreg(ptr) tswapal(ptr) + +/* See linux kernel: arch/sh/include/asm/ptrace.h. */ +enum { + TARGET_REG_PC = 16, + TARGET_REG_PR = 17, + TARGET_REG_SR = 18, + TARGET_REG_GBR = 19, + TARGET_REG_MACH = 20, + TARGET_REG_MACL = 21, + TARGET_REG_SYSCALL = 22 +}; + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) +{ + for (int i = 0; i < 16; i++) { + r->regs[i] = tswapreg(env->gregs[i]); + } + + r->regs[TARGET_REG_PC] = tswapreg(env->pc); + r->regs[TARGET_REG_PR] = tswapreg(env->pr); + r->regs[TARGET_REG_SR] = tswapreg(env->sr); + r->regs[TARGET_REG_GBR] = tswapreg(env->gbr); + r->regs[TARGET_REG_MACH] = tswapreg(env->mach); + r->regs[TARGET_REG_MACL] = tswapreg(env->macl); + r->regs[TARGET_REG_SYSCALL] = 0; /* FIXME */ +} diff --git a/linux-user/sh4/target_elf.h b/linux-user/sh4/target_elf.h index badd0f5371fb3..f7443ddbacd15 100644 --- a/linux-user/sh4/target_elf.h +++ b/linux-user/sh4/target_elf.h @@ -9,5 +9,14 @@ #define SH4_TARGET_ELF_H #define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +/* See linux kernel: arch/sh/include/asm/elf.h. */ +#define ELF_NREG 23 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif From bcaebf6e5bac13352a17eb4949464787c34829f9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 06:14:26 -1000 Subject: [PATCH 0424/1794] linux-user: Move elf_core_copy_regs to m68k/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 31 ------------------------------- linux-user/m68k/elfload.c | 27 +++++++++++++++++++++++++++ linux-user/m68k/target_elf.h | 10 ++++++++++ 3 files changed, 37 insertions(+), 31 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 69532faddbb0d..e92c424faf159 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -442,37 +442,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_68K -/* See linux kernel: arch/m68k/include/asm/elf.h. */ -#define ELF_NREG 20 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) -{ - r->regs[0] = tswapreg(env->dregs[1]); - r->regs[1] = tswapreg(env->dregs[2]); - r->regs[2] = tswapreg(env->dregs[3]); - r->regs[3] = tswapreg(env->dregs[4]); - r->regs[4] = tswapreg(env->dregs[5]); - r->regs[5] = tswapreg(env->dregs[6]); - r->regs[6] = tswapreg(env->dregs[7]); - r->regs[7] = tswapreg(env->aregs[0]); - r->regs[8] = tswapreg(env->aregs[1]); - r->regs[9] = tswapreg(env->aregs[2]); - r->regs[10] = tswapreg(env->aregs[3]); - r->regs[11] = tswapreg(env->aregs[4]); - r->regs[12] = tswapreg(env->aregs[5]); - r->regs[13] = tswapreg(env->aregs[6]); - r->regs[14] = tswapreg(env->dregs[0]); - r->regs[15] = tswapreg(env->aregs[7]); - r->regs[16] = tswapreg(env->dregs[0]); /* FIXME: orig_d0 */ - r->regs[17] = tswapreg(env->sr); - r->regs[18] = tswapreg(env->pc); - r->regs[19] = 0; /* FIXME: regs->format | regs->vector */ -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 #endif diff --git a/linux-user/m68k/elfload.c b/linux-user/m68k/elfload.c index 561ac5b3b3a7d..2970ff7dec81f 100644 --- a/linux-user/m68k/elfload.c +++ b/linux-user/m68k/elfload.c @@ -4,6 +4,7 @@ #include "qemu.h" #include "loader.h" #include "elf.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -16,3 +17,29 @@ const char *get_elf_cpu_model(uint32_t eflags) /* Coldfire */ return "any"; } + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) +{ + r->regs[0] = tswapreg(env->dregs[1]); + r->regs[1] = tswapreg(env->dregs[2]); + r->regs[2] = tswapreg(env->dregs[3]); + r->regs[3] = tswapreg(env->dregs[4]); + r->regs[4] = tswapreg(env->dregs[5]); + r->regs[5] = tswapreg(env->dregs[6]); + r->regs[6] = tswapreg(env->dregs[7]); + r->regs[7] = tswapreg(env->aregs[0]); + r->regs[8] = tswapreg(env->aregs[1]); + r->regs[9] = tswapreg(env->aregs[2]); + r->regs[10] = tswapreg(env->aregs[3]); + r->regs[11] = tswapreg(env->aregs[4]); + r->regs[12] = tswapreg(env->aregs[5]); + r->regs[13] = tswapreg(env->aregs[6]); + r->regs[14] = tswapreg(env->dregs[0]); + r->regs[15] = tswapreg(env->aregs[7]); + r->regs[16] = tswapreg(env->dregs[0]); /* FIXME: orig_d0 */ + r->regs[17] = tswapreg(env->sr); + r->regs[18] = tswapreg(env->pc); + r->regs[19] = 0; /* FIXME: regs->format | regs->vector */ +} diff --git a/linux-user/m68k/target_elf.h b/linux-user/m68k/target_elf.h index 62ff9d38d4cdb..cd6908ab57a72 100644 --- a/linux-user/m68k/target_elf.h +++ b/linux-user/m68k/target_elf.h @@ -8,4 +8,14 @@ #ifndef M68K_TARGET_ELF_H #define M68K_TARGET_ELF_H +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +/* See linux kernel: arch/m68k/include/asm/elf.h. */ +#define ELF_NREG 20 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; + #endif From 59b51b4e7cacca78d0f60e3817150d2c29423d54 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 06:16:07 -1000 Subject: [PATCH 0425/1794] linux-user: Move elf_core_copy_regs to s390x/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 32 -------------------------------- linux-user/s390x/elfload.c | 28 ++++++++++++++++++++++++++++ linux-user/s390x/target_elf.h | 9 +++++++++ 3 files changed, 37 insertions(+), 32 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index e92c424faf159..7c783b74d4f62 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -461,38 +461,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_S390 -/* See linux kernel: arch/s390/include/uapi/asm/ptrace.h (s390_regs). */ -#define ELF_NREG 27 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -enum { - TARGET_REG_PSWM = 0, - TARGET_REG_PSWA = 1, - TARGET_REG_GPRS = 2, - TARGET_REG_ARS = 18, - TARGET_REG_ORIG_R2 = 26, -}; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUS390XState *env) -{ - int i; - uint32_t *aregs; - - r->regs[TARGET_REG_PSWM] = tswapreg(env->psw.mask); - r->regs[TARGET_REG_PSWA] = tswapreg(env->psw.addr); - for (i = 0; i < 16; i++) { - r->regs[TARGET_REG_GPRS + i] = tswapreg(env->regs[i]); - } - aregs = (uint32_t *)&(r->regs[TARGET_REG_ARS]); - for (i = 0; i < 16; i++) { - aregs[i] = tswap32(env->aregs[i]); - } - r->regs[TARGET_REG_ORIG_R2] = 0; -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #define VDSO_HEADER "vdso.c.inc" diff --git a/linux-user/s390x/elfload.c b/linux-user/s390x/elfload.c index 79ceaba51d19a..4113273b72453 100644 --- a/linux-user/s390x/elfload.c +++ b/linux-user/s390x/elfload.c @@ -4,6 +4,7 @@ #include "qemu.h" #include "loader.h" #include "elf.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -66,3 +67,30 @@ const char *elf_hwcap_str(uint32_t bit) return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; } + +#define tswapreg(ptr) tswapal(ptr) + +enum { + TARGET_REG_PSWM = 0, + TARGET_REG_PSWA = 1, + TARGET_REG_GPRS = 2, + TARGET_REG_ARS = 18, + TARGET_REG_ORIG_R2 = 26, +}; + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUS390XState *env) +{ + int i; + uint32_t *aregs; + + r->regs[TARGET_REG_PSWM] = tswapreg(env->psw.mask); + r->regs[TARGET_REG_PSWA] = tswapreg(env->psw.addr); + for (i = 0; i < 16; i++) { + r->regs[TARGET_REG_GPRS + i] = tswapreg(env->regs[i]); + } + aregs = (uint32_t *)&(r->regs[TARGET_REG_ARS]); + for (i = 0; i < 16; i++) { + aregs[i] = tswap32(env->aregs[i]); + } + r->regs[TARGET_REG_ORIG_R2] = 0; +} diff --git a/linux-user/s390x/target_elf.h b/linux-user/s390x/target_elf.h index cebace949a52b..b7d863ee6661d 100644 --- a/linux-user/s390x/target_elf.h +++ b/linux-user/s390x/target_elf.h @@ -9,5 +9,14 @@ #define S390X_TARGET_ELF_H #define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +/* See linux kernel: arch/s390/include/uapi/asm/ptrace.h (s390_regs). */ +#define ELF_NREG 27 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif From 952916bb8fae405fd9f25be6c3ad0f6f8525ddbf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 06:17:52 -1000 Subject: [PATCH 0426/1794] linux-user: Move elf_core_copy_regs to xtensa/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 39 ---------------------------------- linux-user/xtensa/elfload.c | 35 ++++++++++++++++++++++++++++++ linux-user/xtensa/target_elf.h | 10 +++++++++ 3 files changed, 45 insertions(+), 39 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 7c783b74d4f62..5cdbdc20d9d6f 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -531,45 +531,6 @@ static bool init_guest_commpage(void) #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_XTENSA -/* See linux kernel: arch/xtensa/include/asm/elf.h. */ -#define ELF_NREG 128 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -enum { - TARGET_REG_PC, - TARGET_REG_PS, - TARGET_REG_LBEG, - TARGET_REG_LEND, - TARGET_REG_LCOUNT, - TARGET_REG_SAR, - TARGET_REG_WINDOWSTART, - TARGET_REG_WINDOWBASE, - TARGET_REG_THREADPTR, - TARGET_REG_AR0 = 64, -}; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUXtensaState *env) -{ - unsigned i; - - r->regs[TARGET_REG_PC] = tswapreg(env->pc); - r->regs[TARGET_REG_PS] = tswapreg(env->sregs[PS] & ~PS_EXCM); - r->regs[TARGET_REG_LBEG] = tswapreg(env->sregs[LBEG]); - r->regs[TARGET_REG_LEND] = tswapreg(env->sregs[LEND]); - r->regs[TARGET_REG_LCOUNT] = tswapreg(env->sregs[LCOUNT]); - r->regs[TARGET_REG_SAR] = tswapreg(env->sregs[SAR]); - r->regs[TARGET_REG_WINDOWSTART] = tswapreg(env->sregs[WINDOW_START]); - r->regs[TARGET_REG_WINDOWBASE] = tswapreg(env->sregs[WINDOW_BASE]); - r->regs[TARGET_REG_THREADPTR] = tswapreg(env->uregs[THREADPTR]); - xtensa_sync_phys_from_window((CPUXtensaState *)env); - for (i = 0; i < env->config->nareg; ++i) { - r->regs[TARGET_REG_AR0 + i] = tswapreg(env->phys_regs[i]); - } -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_XTENSA */ diff --git a/linux-user/xtensa/elfload.c b/linux-user/xtensa/elfload.c index e35ba69a109a3..49e709a094fe0 100644 --- a/linux-user/xtensa/elfload.c +++ b/linux-user/xtensa/elfload.c @@ -3,9 +3,44 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) { return XTENSA_DEFAULT_CPU_MODEL; } + +#define tswapreg(ptr) tswapal(ptr) + +enum { + TARGET_REG_PC, + TARGET_REG_PS, + TARGET_REG_LBEG, + TARGET_REG_LEND, + TARGET_REG_LCOUNT, + TARGET_REG_SAR, + TARGET_REG_WINDOWSTART, + TARGET_REG_WINDOWBASE, + TARGET_REG_THREADPTR, + TARGET_REG_AR0 = 64, +}; + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUXtensaState *env) +{ + unsigned i; + + r->regs[TARGET_REG_PC] = tswapreg(env->pc); + r->regs[TARGET_REG_PS] = tswapreg(env->sregs[PS] & ~PS_EXCM); + r->regs[TARGET_REG_LBEG] = tswapreg(env->sregs[LBEG]); + r->regs[TARGET_REG_LEND] = tswapreg(env->sregs[LEND]); + r->regs[TARGET_REG_LCOUNT] = tswapreg(env->sregs[LCOUNT]); + r->regs[TARGET_REG_SAR] = tswapreg(env->sregs[SAR]); + r->regs[TARGET_REG_WINDOWSTART] = tswapreg(env->sregs[WINDOW_START]); + r->regs[TARGET_REG_WINDOWBASE] = tswapreg(env->sregs[WINDOW_BASE]); + r->regs[TARGET_REG_THREADPTR] = tswapreg(env->uregs[THREADPTR]); + xtensa_sync_phys_from_window((CPUXtensaState *)env); + for (i = 0; i < env->config->nareg; ++i) { + r->regs[TARGET_REG_AR0 + i] = tswapreg(env->phys_regs[i]); + } +} diff --git a/linux-user/xtensa/target_elf.h b/linux-user/xtensa/target_elf.h index 2c55c22e14b61..43e241aac1231 100644 --- a/linux-user/xtensa/target_elf.h +++ b/linux-user/xtensa/target_elf.h @@ -8,4 +8,14 @@ #ifndef XTENSA_TARGET_ELF_H #define XTENSA_TARGET_ELF_H +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +/* See linux kernel: arch/xtensa/include/asm/elf.h. */ +#define ELF_NREG 128 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; + #endif From 4540a4e6044870dc98bdaacb1593012aef6f5df9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 10:53:13 +1000 Subject: [PATCH 0427/1794] linux-user: Remove target_elf_greg_t, tswapreg from elfload.c These are no longer used within the generic file. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 5cdbdc20d9d6f..07d83c674d6b5 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,14 +130,6 @@ int info_is_fdpic(struct image_info *info) #define ELF_DATA ELFDATA2LSB #endif -#ifdef TARGET_ABI_MIPSN32 -typedef abi_ullong target_elf_greg_t; -#define tswapreg(ptr) tswap64(ptr) -#else -typedef abi_ulong target_elf_greg_t; -#define tswapreg(ptr) tswapal(ptr) -#endif - #ifdef USE_UID16 typedef abi_ushort target_uid_t; typedef abi_ushort target_gid_t; From c47407ef2f4e6b2965b726618cd600aa24149f13 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 14:19:04 +1000 Subject: [PATCH 0428/1794] linux-user/i386: Create target_ptrace.h Remove the target_pt_regs structure from target_syscall.h. Add target_user_regs_struct to target_ptrace.h, which is what is actually used by ELF_CORE_COPY_REGS; the layout of the two structure definitions is identical. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/i386/target_ptrace.h | 32 ++++++++++++++++++++++++++++++++ linux-user/i386/target_syscall.h | 18 ------------------ 2 files changed, 32 insertions(+), 18 deletions(-) create mode 100644 linux-user/i386/target_ptrace.h diff --git a/linux-user/i386/target_ptrace.h b/linux-user/i386/target_ptrace.h new file mode 100644 index 0000000000000..bc57926f2542a --- /dev/null +++ b/linux-user/i386/target_ptrace.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef I386_TARGET_PTRACE_H +#define I386_TARGET_PTRACE_H + +/* + * Note that arch/x86/include/uapi/asm/ptrace.h (struct pt_regs) and + * arch/x86/include/asm/user_32.h (struct user_regs_struct) have the + * same layout, though the exact types differ (int vs long vs unsigned). + * Define user_regs_struct because that's what's actually used. + */ +struct target_user_regs_struct { + abi_ulong bx; + abi_ulong cx; + abi_ulong dx; + abi_ulong si; + abi_ulong di; + abi_ulong bp; + abi_ulong ax; + abi_ulong ds; + abi_ulong es; + abi_ulong fs; + abi_ulong gs; + abi_ulong orig_ax; + abi_ulong ip; + abi_ulong cs; + abi_ulong flags; + abi_ulong sp; + abi_ulong ss; +}; + +#endif /* I386_TARGET_PTRACE_H */ diff --git a/linux-user/i386/target_syscall.h b/linux-user/i386/target_syscall.h index aaade06b136ac..c214a909a6ff0 100644 --- a/linux-user/i386/target_syscall.h +++ b/linux-user/i386/target_syscall.h @@ -5,24 +5,6 @@ #define __USER_CS (0x23) #define __USER_DS (0x2B) -struct target_pt_regs { - long ebx; - long ecx; - long edx; - long esi; - long edi; - long ebp; - long eax; - int xds; - int xes; - long orig_eax; - long eip; - int xcs; - long eflags; - long esp; - int xss; -}; - /* ioctls */ #define TARGET_LDT_ENTRIES 8192 From ff15e62d3f472890b6c93262f85447bfb9eec642 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 11:09:03 +1000 Subject: [PATCH 0429/1794] linux-user/i386: Expand target_elf_gregset_t The comment re ELF_NREG is incorrect or out-of-date. Make use of the fact that target_elf_gregset_t is a proper structure by using target_user_regs_struct. Drop target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/i386/elfload.c | 36 +++++++++++++++++------------------- linux-user/i386/target_elf.h | 14 +++++--------- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/linux-user/i386/elfload.c b/linux-user/i386/elfload.c index 279aeb8116d7c..26b12001a3e0b 100644 --- a/linux-user/i386/elfload.c +++ b/linux-user/i386/elfload.c @@ -25,25 +25,23 @@ const char *get_elf_platform(CPUState *cs) return elf_platform[family - 3]; } -#define tswapreg(ptr) tswapal(ptr) - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) { - r->regs[0] = tswapreg(env->regs[R_EBX]); - r->regs[1] = tswapreg(env->regs[R_ECX]); - r->regs[2] = tswapreg(env->regs[R_EDX]); - r->regs[3] = tswapreg(env->regs[R_ESI]); - r->regs[4] = tswapreg(env->regs[R_EDI]); - r->regs[5] = tswapreg(env->regs[R_EBP]); - r->regs[6] = tswapreg(env->regs[R_EAX]); - r->regs[7] = tswapreg(env->segs[R_DS].selector & 0xffff); - r->regs[8] = tswapreg(env->segs[R_ES].selector & 0xffff); - r->regs[9] = tswapreg(env->segs[R_FS].selector & 0xffff); - r->regs[10] = tswapreg(env->segs[R_GS].selector & 0xffff); - r->regs[11] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); - r->regs[12] = tswapreg(env->eip); - r->regs[13] = tswapreg(env->segs[R_CS].selector & 0xffff); - r->regs[14] = tswapreg(env->eflags); - r->regs[15] = tswapreg(env->regs[R_ESP]); - r->regs[16] = tswapreg(env->segs[R_SS].selector & 0xffff); + r->pt.bx = tswapal(env->regs[R_EBX]); + r->pt.cx = tswapal(env->regs[R_ECX]); + r->pt.dx = tswapal(env->regs[R_EDX]); + r->pt.si = tswapal(env->regs[R_ESI]); + r->pt.di = tswapal(env->regs[R_EDI]); + r->pt.bp = tswapal(env->regs[R_EBP]); + r->pt.ax = tswapal(env->regs[R_EAX]); + r->pt.ds = tswapal(env->segs[R_DS].selector & 0xffff); + r->pt.es = tswapal(env->segs[R_ES].selector & 0xffff); + r->pt.fs = tswapal(env->segs[R_FS].selector & 0xffff); + r->pt.gs = tswapal(env->segs[R_GS].selector & 0xffff); + r->pt.orig_ax = tswapal(get_task_state(env_cpu_const(env))->orig_ax); + r->pt.ip = tswapal(env->eip); + r->pt.cs = tswapal(env->segs[R_CS].selector & 0xffff); + r->pt.flags = tswapal(env->eflags); + r->pt.sp = tswapal(env->regs[R_ESP]); + r->pt.ss = tswapal(env->segs[R_SS].selector & 0xffff); } diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index eb286868e116e..f89ac0b611a63 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -8,22 +8,18 @@ #ifndef I386_TARGET_ELF_H #define I386_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 /* - * Note that ELF_NREG should be 19 as there should be place for - * TRAPNO and ERR "registers" as well but linux doesn't dump those. - * - * See linux kernel: arch/x86/include/asm/elf.h + * See linux kernel: arch/x86/include/asm/elf.h, where elf_gregset_t + * is mapped to struct user_regs_struct via sizeof. */ -#define ELF_NREG 17 - -typedef abi_ulong target_elf_greg_t; - typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_user_regs_struct pt; } target_elf_gregset_t; #endif From 399313bb6d94e5f3ce4ce533f9a8c5fe504f6cda Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 14:41:40 +1000 Subject: [PATCH 0430/1794] linux-user/x86_64: Create target_ptrace.h Remove the target_pt_regs structure from target_syscall.h. Add target_user_regs_struct to target_ptrace.h, which matches what is actually used on x86_64. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/x86_64/target_ptrace.h | 40 ++++++++++++++++++++++++++++++ linux-user/x86_64/target_syscall.h | 28 --------------------- 2 files changed, 40 insertions(+), 28 deletions(-) create mode 100644 linux-user/x86_64/target_ptrace.h diff --git a/linux-user/x86_64/target_ptrace.h b/linux-user/x86_64/target_ptrace.h new file mode 100644 index 0000000000000..33527127cb200 --- /dev/null +++ b/linux-user/x86_64/target_ptrace.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef X86_64_TARGET_PTRACE_H +#define X86_64_TARGET_PTRACE_H + +/* + * The struct pt_regs in arch/x86/include/uapi/asm/ptrace.h has missing + * register values and is not used. See arch/x86/include/asm/user_64.h. + */ +struct target_user_regs_struct { + abi_ulong r15; + abi_ulong r14; + abi_ulong r13; + abi_ulong r12; + abi_ulong bp; + abi_ulong bx; + abi_ulong r11; + abi_ulong r10; + abi_ulong r9; + abi_ulong r8; + abi_ulong ax; + abi_ulong cx; + abi_ulong dx; + abi_ulong si; + abi_ulong di; + abi_ulong orig_ax; + abi_ulong ip; + abi_ulong cs; + abi_ulong flags; + abi_ulong sp; + abi_ulong ss; + abi_ulong fs_base; + abi_ulong gs_base; + abi_ulong ds; + abi_ulong es; + abi_ulong fs; + abi_ulong gs; +}; + +#endif /* X86_64_TARGET_PTRACE_H */ diff --git a/linux-user/x86_64/target_syscall.h b/linux-user/x86_64/target_syscall.h index fb558345d30bc..68f55f8e7b4f6 100644 --- a/linux-user/x86_64/target_syscall.h +++ b/linux-user/x86_64/target_syscall.h @@ -4,34 +4,6 @@ #define __USER_CS (0x33) #define __USER_DS (0x2B) -struct target_pt_regs { - abi_ulong r15; - abi_ulong r14; - abi_ulong r13; - abi_ulong r12; - abi_ulong rbp; - abi_ulong rbx; -/* arguments: non interrupts/non tracing syscalls only save up to here */ - abi_ulong r11; - abi_ulong r10; - abi_ulong r9; - abi_ulong r8; - abi_ulong rax; - abi_ulong rcx; - abi_ulong rdx; - abi_ulong rsi; - abi_ulong rdi; - abi_ulong orig_rax; -/* end of arguments */ -/* cpu exception frame or undefined */ - abi_ulong rip; - abi_ulong cs; - abi_ulong eflags; - abi_ulong rsp; - abi_ulong ss; -/* top of stack page */ -}; - /* Maximum number of LDT entries supported. */ #define TARGET_LDT_ENTRIES 8192 /* The size of each LDT entry. */ From 9c49798e18c00eb07bf6832aa50a1a889a145ec5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 11:17:02 +1000 Subject: [PATCH 0431/1794] linux-user/x86_64: Expand target_elf_gregset_t The comment re ELF_NREG is incorrect or out-of-date. Make use of the fact that target_elf_gregset_t is a proper structure by using target_user_regs_struct. Drop target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/x86_64/elfload.c | 56 ++++++++++++++++------------------ linux-user/x86_64/target_elf.h | 14 +++------ 2 files changed, 32 insertions(+), 38 deletions(-) diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c index 76cf5c15098c8..18d632ec34d93 100644 --- a/linux-user/x86_64/elfload.c +++ b/linux-user/x86_64/elfload.c @@ -21,35 +21,33 @@ const char *get_elf_platform(CPUState *cs) return "x86_64"; } -#define tswapreg(ptr) tswapal(ptr) - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) { - r->regs[0] = tswapreg(env->regs[15]); - r->regs[1] = tswapreg(env->regs[14]); - r->regs[2] = tswapreg(env->regs[13]); - r->regs[3] = tswapreg(env->regs[12]); - r->regs[4] = tswapreg(env->regs[R_EBP]); - r->regs[5] = tswapreg(env->regs[R_EBX]); - r->regs[6] = tswapreg(env->regs[11]); - r->regs[7] = tswapreg(env->regs[10]); - r->regs[8] = tswapreg(env->regs[9]); - r->regs[9] = tswapreg(env->regs[8]); - r->regs[10] = tswapreg(env->regs[R_EAX]); - r->regs[11] = tswapreg(env->regs[R_ECX]); - r->regs[12] = tswapreg(env->regs[R_EDX]); - r->regs[13] = tswapreg(env->regs[R_ESI]); - r->regs[14] = tswapreg(env->regs[R_EDI]); - r->regs[15] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); - r->regs[16] = tswapreg(env->eip); - r->regs[17] = tswapreg(env->segs[R_CS].selector & 0xffff); - r->regs[18] = tswapreg(env->eflags); - r->regs[19] = tswapreg(env->regs[R_ESP]); - r->regs[20] = tswapreg(env->segs[R_SS].selector & 0xffff); - r->regs[21] = tswapreg(env->segs[R_FS].selector & 0xffff); - r->regs[22] = tswapreg(env->segs[R_GS].selector & 0xffff); - r->regs[23] = tswapreg(env->segs[R_DS].selector & 0xffff); - r->regs[24] = tswapreg(env->segs[R_ES].selector & 0xffff); - r->regs[25] = tswapreg(env->segs[R_FS].selector & 0xffff); - r->regs[26] = tswapreg(env->segs[R_GS].selector & 0xffff); + r->pt.r15 = tswapal(env->regs[15]); + r->pt.r14 = tswapal(env->regs[14]); + r->pt.r13 = tswapal(env->regs[13]); + r->pt.r12 = tswapal(env->regs[12]); + r->pt.bp = tswapal(env->regs[R_EBP]); + r->pt.bx = tswapal(env->regs[R_EBX]); + r->pt.r11 = tswapal(env->regs[11]); + r->pt.r10 = tswapal(env->regs[10]); + r->pt.r9 = tswapal(env->regs[9]); + r->pt.r8 = tswapal(env->regs[8]); + r->pt.ax = tswapal(env->regs[R_EAX]); + r->pt.cx = tswapal(env->regs[R_ECX]); + r->pt.dx = tswapal(env->regs[R_EDX]); + r->pt.si = tswapal(env->regs[R_ESI]); + r->pt.di = tswapal(env->regs[R_EDI]); + r->pt.orig_ax = tswapal(get_task_state(env_cpu_const(env))->orig_ax); + r->pt.ip = tswapal(env->eip); + r->pt.cs = tswapal(env->segs[R_CS].selector & 0xffff); + r->pt.flags = tswapal(env->eflags); + r->pt.sp = tswapal(env->regs[R_ESP]); + r->pt.ss = tswapal(env->segs[R_SS].selector & 0xffff); + r->pt.fs_base = tswapal(env->segs[R_FS].selector & 0xffff); + r->pt.gs_base = tswapal(env->segs[R_GS].selector & 0xffff); + r->pt.ds = tswapal(env->segs[R_DS].selector & 0xffff); + r->pt.es = tswapal(env->segs[R_ES].selector & 0xffff); + r->pt.fs = tswapal(env->segs[R_FS].selector & 0xffff); + r->pt.gs = tswapal(env->segs[R_GS].selector & 0xffff); } diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index 74a77d94cdc66..32a9eec431495 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -8,22 +8,18 @@ #ifndef X86_64_TARGET_ELF_H #define X86_64_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 /* - * Note that ELF_NREG should be 29 as there should be place for - * TRAPNO and ERR "registers" as well but linux doesn't dump those. - * - * See linux kernel: arch/x86/include/asm/elf.h + * See linux kernel: arch/x86/include/asm/elf.h, where + * elf_gregset_t is mapped to struct user_regs_struct via sizeof. */ -#define ELF_NREG 27 - -typedef abi_ulong target_elf_greg_t; - typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_user_regs_struct pt; } target_elf_gregset_t; #endif From 31d0ef2c85ca481266d0a8793afdce38ab0b687c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 14:50:12 +1000 Subject: [PATCH 0432/1794] linux-user/x86_64: Fix dump of fs_base, gs_base We were storing the selector, not the base. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/x86_64/elfload.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c index 18d632ec34d93..12de1c54c7f6d 100644 --- a/linux-user/x86_64/elfload.c +++ b/linux-user/x86_64/elfload.c @@ -44,8 +44,8 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) r->pt.flags = tswapal(env->eflags); r->pt.sp = tswapal(env->regs[R_ESP]); r->pt.ss = tswapal(env->segs[R_SS].selector & 0xffff); - r->pt.fs_base = tswapal(env->segs[R_FS].selector & 0xffff); - r->pt.gs_base = tswapal(env->segs[R_GS].selector & 0xffff); + r->pt.fs_base = tswapal(env->segs[R_FS].base); + r->pt.gs_base = tswapal(env->segs[R_GS].base); r->pt.ds = tswapal(env->segs[R_DS].selector & 0xffff); r->pt.es = tswapal(env->segs[R_ES].selector & 0xffff); r->pt.fs = tswapal(env->segs[R_FS].selector & 0xffff); From 7ee71b02fbc21f9de31140f5237c849e36ee30d5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 14:50:33 +1000 Subject: [PATCH 0433/1794] linux-user/aarch64: Create target_ptrace.h Move the target_pt_regs structure from target_syscall.h, and rename to target_user_pt_regs, to match what's in ptrace.h. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/target_ptrace.h | 14 ++++++++++++++ linux-user/aarch64/target_syscall.h | 7 ------- 2 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 linux-user/aarch64/target_ptrace.h diff --git a/linux-user/aarch64/target_ptrace.h b/linux-user/aarch64/target_ptrace.h new file mode 100644 index 0000000000000..10681338ba45d --- /dev/null +++ b/linux-user/aarch64/target_ptrace.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef AARCH64_TARGET_PTRACE_H +#define AARCH64_TARGET_PTRACE_H + +/* See arch/arm64/include/uapi/asm/ptrace.h. */ +struct target_user_pt_regs { + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; +}; + +#endif /* AARCH64_TARGET_PTRACE_H */ diff --git a/linux-user/aarch64/target_syscall.h b/linux-user/aarch64/target_syscall.h index c055133725eca..bd05f6c7fe5d3 100644 --- a/linux-user/aarch64/target_syscall.h +++ b/linux-user/aarch64/target_syscall.h @@ -1,13 +1,6 @@ #ifndef AARCH64_TARGET_SYSCALL_H #define AARCH64_TARGET_SYSCALL_H -struct target_pt_regs { - uint64_t regs[31]; - uint64_t sp; - uint64_t pc; - uint64_t pstate; -}; - #if TARGET_BIG_ENDIAN #define UNAME_MACHINE "aarch64_be" #else From ff22166d3f68786e5f129d2fb34a38cfdc0631b3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 11:24:05 +1000 Subject: [PATCH 0434/1794] linux-user/aarch64: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure by using target_user_pt_regs. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/elfload.c | 11 +++++------ linux-user/aarch64/target_elf.h | 11 +++++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 00550f9fdfae1..07a0c3f844145 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -349,13 +349,12 @@ const char *get_elf_platform(CPUState *cs) return TARGET_BIG_ENDIAN ? "aarch64_be" : "aarch64"; } -#define tswapreg(ptr) tswapal(ptr) - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) { - for (int i = 0; i < 32; i++) { - r->regs[i] = tswapreg(env->xregs[i]); + for (int i = 0; i < 31; i++) { + r->pt.regs[i] = tswap64(env->xregs[i]); } - r->regs[32] = tswapreg(env->pc); - r->regs[33] = tswapreg(pstate_read((CPUARMState *)env)); + r->pt.sp = tswap64(env->xregs[31]); + r->pt.pc = tswap64(env->pc); + r->pt.pstate = tswap64(pstate_read((CPUARMState *)env)); } diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index b0728a100824c..9eb8bb547e49f 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -8,16 +8,19 @@ #ifndef AARCH64_TARGET_ELF_H #define AARCH64_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - -#define ELF_NREG 34 +/* + * See linux kernel: arch/arm64/include/asm/elf.h, where + * elf_gregset_t is mapped to struct user_pt_regs via sizeof. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_user_pt_regs pt; } target_elf_gregset_t; #endif From db58f1667e72b3fa4c0a9e45f0b6b683e836295b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 14:56:57 +1000 Subject: [PATCH 0435/1794] linux-user/arm: Create target_ptrace.h Move the target_pt_regs structure from target_syscall.h. Replace the array with proper structure fields. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/arm/target_ptrace.h | 16 ++++++++++++++++ linux-user/arm/target_syscall.h | 8 -------- 2 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 linux-user/arm/target_ptrace.h diff --git a/linux-user/arm/target_ptrace.h b/linux-user/arm/target_ptrace.h new file mode 100644 index 0000000000000..1610b8e03c193 --- /dev/null +++ b/linux-user/arm/target_ptrace.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef ARM_TARGET_PTRACE_H +#define ARM_TARGET_PTRACE_H + +/* + * See arch/arm/include/uapi/asm/ptrace.h. + * Instead of an array and ARM_xx defines, use proper fields. + */ +struct target_pt_regs { + abi_ulong regs[16]; + abi_ulong cpsr; + abi_ulong orig_r0; +}; + +#endif /* ARM_TARGET_PTRACE_H */ diff --git a/linux-user/arm/target_syscall.h b/linux-user/arm/target_syscall.h index 412ad434cfc20..8c4ddba717737 100644 --- a/linux-user/arm/target_syscall.h +++ b/linux-user/arm/target_syscall.h @@ -1,14 +1,6 @@ #ifndef ARM_TARGET_SYSCALL_H #define ARM_TARGET_SYSCALL_H -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -/* uregs[0..15] are r0 to r15; uregs[16] is CPSR; uregs[17] is ORIG_r0 */ -struct target_pt_regs { - abi_long uregs[18]; -}; - #define ARM_SYSCALL_BASE 0x900000 #define ARM_THUMB_SYSCALL 0 From af6da9e6b845f31675343c58786b2389b2f4a0ec Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 11:30:59 +1000 Subject: [PATCH 0436/1794] linux-user/arm: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/arm/elfload.c | 8 +++----- linux-user/arm/target_elf.h | 11 +++++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index 47fe16a1a650e..f811c2f07a9c1 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -201,13 +201,11 @@ const char *get_elf_platform(CPUState *cs) #undef END } -#define tswapreg(ptr) tswapal(ptr) - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) { for (int i = 0; i < 16; ++i) { - r->regs[i] = tswapreg(env->regs[i]); + r->pt.regs[i] = tswapal(env->regs[i]); } - r->regs[16] = tswapreg(cpsr_read((CPUARMState *)env)); - r->regs[17] = tswapreg(env->regs[0]); /* XXX */ + r->pt.cpsr = tswapal(cpsr_read((CPUARMState *)env)); + r->pt.orig_r0 = tswapal(env->regs[0]); /* FIXME */ } diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index 94db3738e8717..fa8f8af2f3edc 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -8,16 +8,19 @@ #ifndef ARM_TARGET_ELF_H #define ARM_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - -#define ELF_NREG 18 +/* + * See linux kernel: arch/arm/include/asm/elf.h, where + * elf_gregset_t is mapped to struct pt_regs via sizeof. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_pt_regs pt; } target_elf_gregset_t; #endif From 9308223c714d1efb275cf21c439ee85f5e6cad0d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 15:03:40 +1000 Subject: [PATCH 0437/1794] linux-user/loongarch64: Create target_ptrace.h Remove the target_pt_regs structure from target_syscall.h. Add target_user_pt_regs to target_ptrace.h, which matches what is actually used on loongarch64. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/loongarch64/target_ptrace.h | 15 +++++++++++++++ linux-user/loongarch64/target_syscall.h | 23 ----------------------- 2 files changed, 15 insertions(+), 23 deletions(-) create mode 100644 linux-user/loongarch64/target_ptrace.h diff --git a/linux-user/loongarch64/target_ptrace.h b/linux-user/loongarch64/target_ptrace.h new file mode 100644 index 0000000000000..2578e09207e16 --- /dev/null +++ b/linux-user/loongarch64/target_ptrace.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef LOONGARCH64_TARGET_PTRACE_H +#define LOONGARCH64_TARGET_PTRACE_H + +/* See arch/loongarch/include/uapi/asm/ptrace.h. */ +struct target_user_pt_regs { + abi_ulong regs[32]; + abi_ulong orig_a0; + abi_ulong csr_era; + abi_ulong csr_badv; + abi_ulong reserved[10]; +}; + +#endif /* LOONGARCH64_TARGET_PTRACE_H */ diff --git a/linux-user/loongarch64/target_syscall.h b/linux-user/loongarch64/target_syscall.h index 39f229bb9c881..f7ced7b2be354 100644 --- a/linux-user/loongarch64/target_syscall.h +++ b/linux-user/loongarch64/target_syscall.h @@ -8,29 +8,6 @@ #include "qemu/units.h" -/* - * this struct defines the way the registers are stored on the - * stack during a system call. - */ - -struct target_pt_regs { - /* Saved main processor registers. */ - target_ulong regs[32]; - - /* Saved special registers. */ - struct { - target_ulong era; - target_ulong badv; - target_ulong crmd; - target_ulong prmd; - target_ulong euen; - target_ulong ecfg; - target_ulong estat; - } csr; - target_ulong orig_a0; - target_ulong __last[0]; -}; - #define UNAME_MACHINE "loongarch64" #define UNAME_MINIMUM_RELEASE "5.19.0" From 190cc717f8cf50b7d38b8f9b8a2045540f71c571 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 11:37:08 +1000 Subject: [PATCH 0438/1794] linux-user/loongarch64: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. Note that the kernel's uses an array, and then it has a bunch of defines to create symbolic offsets. Modulo some reserved fields, which we do not implement here, this is the same layout as struct user_pt_regs. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/loongarch64/elfload.c | 14 ++++---------- linux-user/loongarch64/target_elf.h | 7 +++---- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/linux-user/loongarch64/elfload.c b/linux-user/loongarch64/elfload.c index 832890de1002b..ce3bd0c6079c9 100644 --- a/linux-user/loongarch64/elfload.c +++ b/linux-user/loongarch64/elfload.c @@ -65,20 +65,14 @@ const char *get_elf_platform(CPUState *cs) #define tswapreg(ptr) tswapal(ptr) -enum { - TARGET_EF_R0 = 0, - TARGET_EF_CSR_ERA = TARGET_EF_R0 + 33, - TARGET_EF_CSR_BADV = TARGET_EF_R0 + 34, -}; - void elf_core_copy_regs(target_elf_gregset_t *r, const CPULoongArchState *env) { - r->regs[TARGET_EF_R0] = 0; + r->pt.regs[0] = 0; for (int i = 1; i < ARRAY_SIZE(env->gpr); i++) { - r->regs[TARGET_EF_R0 + i] = tswapreg(env->gpr[i]); + r->pt.regs[i] = tswapreg(env->gpr[i]); } - r->regs[TARGET_EF_CSR_ERA] = tswapreg(env->pc); - r->regs[TARGET_EF_CSR_BADV] = tswapreg(env->CSR_BADV); + r->pt.csr_era = tswapreg(env->pc); + r->pt.csr_badv = tswapreg(env->CSR_BADV); } diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index 90bca4499d2a3..1f40419af2485 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -6,16 +6,15 @@ #ifndef LOONGARCH_TARGET_ELF_H #define LOONGARCH_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - /* See linux kernel: arch/loongarch/include/asm/elf.h */ -#define ELF_NREG 45 typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_user_pt_regs pt; } target_elf_gregset_t; #endif From 5c3a0884efe7d9a8651bc847571584804146f3f1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 11:44:40 +1000 Subject: [PATCH 0439/1794] linux-user/m68k: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/m68k/elfload.c | 42 +++++++++++++++++------------------- linux-user/m68k/target_elf.h | 24 ++++++++++++++++----- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/linux-user/m68k/elfload.c b/linux-user/m68k/elfload.c index 2970ff7dec81f..423d1f680af11 100644 --- a/linux-user/m68k/elfload.c +++ b/linux-user/m68k/elfload.c @@ -18,28 +18,26 @@ const char *get_elf_cpu_model(uint32_t eflags) return "any"; } -#define tswapreg(ptr) tswapal(ptr) - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) { - r->regs[0] = tswapreg(env->dregs[1]); - r->regs[1] = tswapreg(env->dregs[2]); - r->regs[2] = tswapreg(env->dregs[3]); - r->regs[3] = tswapreg(env->dregs[4]); - r->regs[4] = tswapreg(env->dregs[5]); - r->regs[5] = tswapreg(env->dregs[6]); - r->regs[6] = tswapreg(env->dregs[7]); - r->regs[7] = tswapreg(env->aregs[0]); - r->regs[8] = tswapreg(env->aregs[1]); - r->regs[9] = tswapreg(env->aregs[2]); - r->regs[10] = tswapreg(env->aregs[3]); - r->regs[11] = tswapreg(env->aregs[4]); - r->regs[12] = tswapreg(env->aregs[5]); - r->regs[13] = tswapreg(env->aregs[6]); - r->regs[14] = tswapreg(env->dregs[0]); - r->regs[15] = tswapreg(env->aregs[7]); - r->regs[16] = tswapreg(env->dregs[0]); /* FIXME: orig_d0 */ - r->regs[17] = tswapreg(env->sr); - r->regs[18] = tswapreg(env->pc); - r->regs[19] = 0; /* FIXME: regs->format | regs->vector */ + r->d1 = tswapal(env->dregs[1]); + r->d2 = tswapal(env->dregs[2]); + r->d3 = tswapal(env->dregs[3]); + r->d4 = tswapal(env->dregs[4]); + r->d5 = tswapal(env->dregs[5]); + r->d6 = tswapal(env->dregs[6]); + r->d7 = tswapal(env->dregs[7]); + r->a0 = tswapal(env->aregs[0]); + r->a1 = tswapal(env->aregs[1]); + r->a2 = tswapal(env->aregs[2]); + r->a3 = tswapal(env->aregs[3]); + r->a4 = tswapal(env->aregs[4]); + r->a5 = tswapal(env->aregs[5]); + r->a6 = tswapal(env->aregs[6]); + r->d0 = tswapal(env->dregs[0]); + r->usp = tswapal(env->aregs[7]); + r->orig_d0 = tswapal(env->dregs[0]); /* FIXME */ + r->sr = tswapal(env->sr); + r->pc = tswapal(env->pc); + /* FIXME: regs->format | regs->vector */ } diff --git a/linux-user/m68k/target_elf.h b/linux-user/m68k/target_elf.h index cd6908ab57a72..0737412cee6cd 100644 --- a/linux-user/m68k/target_elf.h +++ b/linux-user/m68k/target_elf.h @@ -10,12 +10,26 @@ #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - -/* See linux kernel: arch/m68k/include/asm/elf.h. */ -#define ELF_NREG 20 +/* + * See linux kernel: arch/m68k/include/asm/elf.h, where + * elf_gregset_t is mapped to struct user_regs_struct via sizeof. + * + * Note that user_regs_struct has + * short stkadj, sr; + * ... + * short fmtvec, __fill; + * but ELF_CORE_COPY_REGS writes to unsigned longs. + * Therefore adjust the sr and fmtvec fields to match. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + abi_ulong d1, d2, d3, d4, d5, d6, d7; + abi_ulong a0, a1, a2, a3, a4, a5, a6; + abi_ulong d0; + abi_ulong usp; + abi_ulong orig_d0; + abi_ulong sr; + abi_ulong pc; + abi_ulong fmtvec; } target_elf_gregset_t; #endif From cf33264f19b566cda4b50ccfec9e87734b08971f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 15:22:14 +1000 Subject: [PATCH 0440/1794] linux-user/microblaze: Create target_ptrace.h Move the target_pt_regs structure from target_syscall.h. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/microblaze/signal.c | 1 + linux-user/microblaze/target_ptrace.h | 50 ++++++++++++++++++++++++++ linux-user/microblaze/target_syscall.h | 44 ----------------------- 3 files changed, 51 insertions(+), 44 deletions(-) create mode 100644 linux-user/microblaze/target_ptrace.h diff --git a/linux-user/microblaze/signal.c b/linux-user/microblaze/signal.c index f6d47d76ff616..7aef7813143a1 100644 --- a/linux-user/microblaze/signal.c +++ b/linux-user/microblaze/signal.c @@ -21,6 +21,7 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "target_ptrace.h" struct target_sigcontext { struct target_pt_regs regs; /* needs to be first */ diff --git a/linux-user/microblaze/target_ptrace.h b/linux-user/microblaze/target_ptrace.h new file mode 100644 index 0000000000000..a46c8cb7bc181 --- /dev/null +++ b/linux-user/microblaze/target_ptrace.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef MICROBLAZE_TARGET_PTRACE_H +#define MICROBLAZE_TARGET_PTRACE_H + +/* We use microblaze_reg_t to keep things similar to the kernel sources. */ +typedef uint32_t microblaze_reg_t; + +struct target_pt_regs { + microblaze_reg_t r0; + microblaze_reg_t r1; + microblaze_reg_t r2; + microblaze_reg_t r3; + microblaze_reg_t r4; + microblaze_reg_t r5; + microblaze_reg_t r6; + microblaze_reg_t r7; + microblaze_reg_t r8; + microblaze_reg_t r9; + microblaze_reg_t r10; + microblaze_reg_t r11; + microblaze_reg_t r12; + microblaze_reg_t r13; + microblaze_reg_t r14; + microblaze_reg_t r15; + microblaze_reg_t r16; + microblaze_reg_t r17; + microblaze_reg_t r18; + microblaze_reg_t r19; + microblaze_reg_t r20; + microblaze_reg_t r21; + microblaze_reg_t r22; + microblaze_reg_t r23; + microblaze_reg_t r24; + microblaze_reg_t r25; + microblaze_reg_t r26; + microblaze_reg_t r27; + microblaze_reg_t r28; + microblaze_reg_t r29; + microblaze_reg_t r30; + microblaze_reg_t r31; + microblaze_reg_t pc; + microblaze_reg_t msr; + microblaze_reg_t ear; + microblaze_reg_t esr; + microblaze_reg_t fsr; + uint32_t kernel_mode; +}; + +#endif /* MICROBLAZE_TARGET_PTRACE_H */ diff --git a/linux-user/microblaze/target_syscall.h b/linux-user/microblaze/target_syscall.h index 43362a1664caf..66f5a9ebe22e6 100644 --- a/linux-user/microblaze/target_syscall.h +++ b/linux-user/microblaze/target_syscall.h @@ -4,50 +4,6 @@ #define UNAME_MACHINE "microblaze" #define UNAME_MINIMUM_RELEASE "2.6.32" -/* We use microblaze_reg_t to keep things similar to the kernel sources. */ -typedef uint32_t microblaze_reg_t; - -struct target_pt_regs { - microblaze_reg_t r0; - microblaze_reg_t r1; - microblaze_reg_t r2; - microblaze_reg_t r3; - microblaze_reg_t r4; - microblaze_reg_t r5; - microblaze_reg_t r6; - microblaze_reg_t r7; - microblaze_reg_t r8; - microblaze_reg_t r9; - microblaze_reg_t r10; - microblaze_reg_t r11; - microblaze_reg_t r12; - microblaze_reg_t r13; - microblaze_reg_t r14; - microblaze_reg_t r15; - microblaze_reg_t r16; - microblaze_reg_t r17; - microblaze_reg_t r18; - microblaze_reg_t r19; - microblaze_reg_t r20; - microblaze_reg_t r21; - microblaze_reg_t r22; - microblaze_reg_t r23; - microblaze_reg_t r24; - microblaze_reg_t r25; - microblaze_reg_t r26; - microblaze_reg_t r27; - microblaze_reg_t r28; - microblaze_reg_t r29; - microblaze_reg_t r30; - microblaze_reg_t r31; - microblaze_reg_t pc; - microblaze_reg_t msr; - microblaze_reg_t ear; - microblaze_reg_t esr; - microblaze_reg_t fsr; - uint32_t kernel_mode; -}; - #define TARGET_CLONE_BACKWARDS #define TARGET_MCL_CURRENT 1 #define TARGET_MCL_FUTURE 2 From e5c31ef558da16fb9cbb9e3a3c7247b60fdfd05b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 15:28:11 +1000 Subject: [PATCH 0441/1794] linux-user/microblaze: Fold target_pt_regs.r* to an array Separately enumerating all 32 registers is not helpful. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/microblaze/signal.c | 70 +++------------------------ linux-user/microblaze/target_ptrace.h | 34 +------------ 2 files changed, 8 insertions(+), 96 deletions(-) diff --git a/linux-user/microblaze/signal.c b/linux-user/microblaze/signal.c index 7aef7813143a1..e874e4def1e93 100644 --- a/linux-user/microblaze/signal.c +++ b/linux-user/microblaze/signal.c @@ -51,75 +51,17 @@ struct target_rt_sigframe { static void setup_sigcontext(struct target_sigcontext *sc, CPUMBState *env) { - __put_user(env->regs[0], &sc->regs.r0); - __put_user(env->regs[1], &sc->regs.r1); - __put_user(env->regs[2], &sc->regs.r2); - __put_user(env->regs[3], &sc->regs.r3); - __put_user(env->regs[4], &sc->regs.r4); - __put_user(env->regs[5], &sc->regs.r5); - __put_user(env->regs[6], &sc->regs.r6); - __put_user(env->regs[7], &sc->regs.r7); - __put_user(env->regs[8], &sc->regs.r8); - __put_user(env->regs[9], &sc->regs.r9); - __put_user(env->regs[10], &sc->regs.r10); - __put_user(env->regs[11], &sc->regs.r11); - __put_user(env->regs[12], &sc->regs.r12); - __put_user(env->regs[13], &sc->regs.r13); - __put_user(env->regs[14], &sc->regs.r14); - __put_user(env->regs[15], &sc->regs.r15); - __put_user(env->regs[16], &sc->regs.r16); - __put_user(env->regs[17], &sc->regs.r17); - __put_user(env->regs[18], &sc->regs.r18); - __put_user(env->regs[19], &sc->regs.r19); - __put_user(env->regs[20], &sc->regs.r20); - __put_user(env->regs[21], &sc->regs.r21); - __put_user(env->regs[22], &sc->regs.r22); - __put_user(env->regs[23], &sc->regs.r23); - __put_user(env->regs[24], &sc->regs.r24); - __put_user(env->regs[25], &sc->regs.r25); - __put_user(env->regs[26], &sc->regs.r26); - __put_user(env->regs[27], &sc->regs.r27); - __put_user(env->regs[28], &sc->regs.r28); - __put_user(env->regs[29], &sc->regs.r29); - __put_user(env->regs[30], &sc->regs.r30); - __put_user(env->regs[31], &sc->regs.r31); + for (int i = 0; i < 32; ++i) { + __put_user(env->regs[i], &sc->regs.r[i]); + } __put_user(env->pc, &sc->regs.pc); } static void restore_sigcontext(struct target_sigcontext *sc, CPUMBState *env) { - __get_user(env->regs[0], &sc->regs.r0); - __get_user(env->regs[1], &sc->regs.r1); - __get_user(env->regs[2], &sc->regs.r2); - __get_user(env->regs[3], &sc->regs.r3); - __get_user(env->regs[4], &sc->regs.r4); - __get_user(env->regs[5], &sc->regs.r5); - __get_user(env->regs[6], &sc->regs.r6); - __get_user(env->regs[7], &sc->regs.r7); - __get_user(env->regs[8], &sc->regs.r8); - __get_user(env->regs[9], &sc->regs.r9); - __get_user(env->regs[10], &sc->regs.r10); - __get_user(env->regs[11], &sc->regs.r11); - __get_user(env->regs[12], &sc->regs.r12); - __get_user(env->regs[13], &sc->regs.r13); - __get_user(env->regs[14], &sc->regs.r14); - __get_user(env->regs[15], &sc->regs.r15); - __get_user(env->regs[16], &sc->regs.r16); - __get_user(env->regs[17], &sc->regs.r17); - __get_user(env->regs[18], &sc->regs.r18); - __get_user(env->regs[19], &sc->regs.r19); - __get_user(env->regs[20], &sc->regs.r20); - __get_user(env->regs[21], &sc->regs.r21); - __get_user(env->regs[22], &sc->regs.r22); - __get_user(env->regs[23], &sc->regs.r23); - __get_user(env->regs[24], &sc->regs.r24); - __get_user(env->regs[25], &sc->regs.r25); - __get_user(env->regs[26], &sc->regs.r26); - __get_user(env->regs[27], &sc->regs.r27); - __get_user(env->regs[28], &sc->regs.r28); - __get_user(env->regs[29], &sc->regs.r29); - __get_user(env->regs[30], &sc->regs.r30); - __get_user(env->regs[31], &sc->regs.r31); + for (int i = 0; i < 32; ++i) { + __get_user(env->regs[i], &sc->regs.r[i]); + } __get_user(env->pc, &sc->regs.pc); } diff --git a/linux-user/microblaze/target_ptrace.h b/linux-user/microblaze/target_ptrace.h index a46c8cb7bc181..ead913e5a4521 100644 --- a/linux-user/microblaze/target_ptrace.h +++ b/linux-user/microblaze/target_ptrace.h @@ -7,38 +7,8 @@ typedef uint32_t microblaze_reg_t; struct target_pt_regs { - microblaze_reg_t r0; - microblaze_reg_t r1; - microblaze_reg_t r2; - microblaze_reg_t r3; - microblaze_reg_t r4; - microblaze_reg_t r5; - microblaze_reg_t r6; - microblaze_reg_t r7; - microblaze_reg_t r8; - microblaze_reg_t r9; - microblaze_reg_t r10; - microblaze_reg_t r11; - microblaze_reg_t r12; - microblaze_reg_t r13; - microblaze_reg_t r14; - microblaze_reg_t r15; - microblaze_reg_t r16; - microblaze_reg_t r17; - microblaze_reg_t r18; - microblaze_reg_t r19; - microblaze_reg_t r20; - microblaze_reg_t r21; - microblaze_reg_t r22; - microblaze_reg_t r23; - microblaze_reg_t r24; - microblaze_reg_t r25; - microblaze_reg_t r26; - microblaze_reg_t r27; - microblaze_reg_t r28; - microblaze_reg_t r29; - microblaze_reg_t r30; - microblaze_reg_t r31; + /* Note the kernel enumerates all 32 registers. */ + microblaze_reg_t r[32]; microblaze_reg_t pc; microblaze_reg_t msr; microblaze_reg_t ear; From e803a48c1ac72ed47b6490e3d7cf6b0cc7372e85 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 11:55:27 +1000 Subject: [PATCH 0442/1794] linux-user/microblaze: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/microblaze/elfload.c | 14 +++++--------- linux-user/microblaze/target_elf.h | 11 +++++++---- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/linux-user/microblaze/elfload.c b/linux-user/microblaze/elfload.c index 89250dbd631d4..7eb1b26d17099 100644 --- a/linux-user/microblaze/elfload.c +++ b/linux-user/microblaze/elfload.c @@ -11,18 +11,14 @@ const char *get_elf_cpu_model(uint32_t eflags) return "any"; } -#define tswapreg(ptr) tswapal(ptr) - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) { for (int i = 0; i < 32; i++) { - r->regs[i] = tswapreg(env->regs[i]); + r->pt.r[i] = tswapal(env->regs[i]); } - r->regs[32] = tswapreg(env->pc); - r->regs[33] = tswapreg(mb_cpu_read_msr(env)); - r->regs[34] = 0; - r->regs[35] = tswapreg(env->ear); - r->regs[36] = 0; - r->regs[37] = tswapreg(env->esr); + r->pt.pc = tswapal(env->pc); + r->pt.msr = tswapal(mb_cpu_read_msr(env)); + r->pt.ear = tswapal(env->ear); + r->pt.esr = tswapal(env->esr); } diff --git a/linux-user/microblaze/target_elf.h b/linux-user/microblaze/target_elf.h index cc5cc0477e3a4..56de77d4f374b 100644 --- a/linux-user/microblaze/target_elf.h +++ b/linux-user/microblaze/target_elf.h @@ -8,13 +8,16 @@ #ifndef MICROBLAZE_TARGET_ELF_H #define MICROBLAZE_TARGET_ELF_H -#define HAVE_ELF_CORE_DUMP 1 +#include "target_ptrace.h" -typedef abi_ulong target_elf_greg_t; +#define HAVE_ELF_CORE_DUMP 1 -#define ELF_NREG 38 +/* + * See linux kernel: arch/microblaze/include/asm/elf.h, where + * elf_gregset_t is mapped to struct pt_regs via sizeof. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_pt_regs pt; } target_elf_gregset_t; #endif From 0dcef5773000f4d72277c6b41200f35031bdcbb5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 15:34:41 +1000 Subject: [PATCH 0443/1794] linux-user/mips: Create target_ptrace.h Move the target_pt_regs structure from target_syscall.h. Fix the incorrect ordering of the fields. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/mips/target_ptrace.h | 17 +++++++++++++++++ linux-user/mips/target_syscall.h | 19 ------------------- linux-user/mips64/target_ptrace.h | 16 ++++++++++++++++ linux-user/mips64/target_syscall.h | 16 ---------------- 4 files changed, 33 insertions(+), 35 deletions(-) create mode 100644 linux-user/mips/target_ptrace.h create mode 100644 linux-user/mips64/target_ptrace.h diff --git a/linux-user/mips/target_ptrace.h b/linux-user/mips/target_ptrace.h new file mode 100644 index 0000000000000..2f63b27ac49f2 --- /dev/null +++ b/linux-user/mips/target_ptrace.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef MIPS_TARGET_PTRACE_H +#define MIPS_TARGET_PTRACE_H + +struct target_pt_regs { + abi_ulong pad0[6]; + abi_ulong regs[32]; + abi_ulong lo; + abi_ulong hi; + abi_ulong cp0_epc; + abi_ulong cp0_badvaddr; + abi_ulong cp0_status; + abi_ulong cp0_cause; +}; + +#endif /* MIPS_TARGET_PTRACE_H */ diff --git a/linux-user/mips/target_syscall.h b/linux-user/mips/target_syscall.h index 08ead678104fb..dfcdf320b7c1e 100644 --- a/linux-user/mips/target_syscall.h +++ b/linux-user/mips/target_syscall.h @@ -1,25 +1,6 @@ #ifndef MIPS_TARGET_SYSCALL_H #define MIPS_TARGET_SYSCALL_H -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -struct target_pt_regs { - /* Pad bytes for argument save space on the stack. */ - abi_ulong pad0[6]; - - /* Saved main processor registers. */ - abi_ulong regs[32]; - - /* Saved special registers. */ - abi_ulong cp0_status; - abi_ulong lo; - abi_ulong hi; - abi_ulong cp0_badvaddr; - abi_ulong cp0_cause; - abi_ulong cp0_epc; -}; - #define UNAME_MACHINE "mips" #define UNAME_MINIMUM_RELEASE "2.6.32" diff --git a/linux-user/mips64/target_ptrace.h b/linux-user/mips64/target_ptrace.h new file mode 100644 index 0000000000000..41f0bf6c1c253 --- /dev/null +++ b/linux-user/mips64/target_ptrace.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef MIPS64_TARGET_PTRACE_H +#define MIPS64_TARGET_PTRACE_H + +struct target_pt_regs { + target_ulong regs[32]; + target_ulong lo; + target_ulong hi; + target_ulong cp0_epc; + target_ulong cp0_badvaddr; + target_ulong cp0_status; + target_ulong cp0_cause; +}; + +#endif /* MIPS64_TARGET_PTRACE_H */ diff --git a/linux-user/mips64/target_syscall.h b/linux-user/mips64/target_syscall.h index 358dc2d64c998..9135bf5e8b623 100644 --- a/linux-user/mips64/target_syscall.h +++ b/linux-user/mips64/target_syscall.h @@ -1,22 +1,6 @@ #ifndef MIPS64_TARGET_SYSCALL_H #define MIPS64_TARGET_SYSCALL_H -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -struct target_pt_regs { - /* Saved main processor registers. */ - target_ulong regs[32]; - - /* Saved special registers. */ - target_ulong cp0_status; - target_ulong lo; - target_ulong hi; - target_ulong cp0_badvaddr; - target_ulong cp0_cause; - target_ulong cp0_epc; -}; - #define UNAME_MACHINE "mips64" #define UNAME_MINIMUM_RELEASE "2.6.32" From c61b88fbe49e428e39eff501cddbcd53f12486cb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 12:05:05 +1000 Subject: [PATCH 0444/1794] linux-user/mips: Use target_ulong for target_elf_greg_t Make use of the fact that target_elf_gregset_t is a proper structure. The target_ulong type matches the abi_ulong/abi_ullong selection within mips64/target_elf.h. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/mips/elfload.c | 48 +++++++--------------------------- linux-user/mips/target_elf.h | 10 ++++--- linux-user/mips64/target_elf.h | 14 +++++----- 3 files changed, 22 insertions(+), 50 deletions(-) diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c index 6e884911afa47..e0c50f50ed290 100644 --- a/linux-user/mips/elfload.c +++ b/linux-user/mips/elfload.c @@ -124,47 +124,19 @@ const char *get_elf_base_platform(CPUState *cs) #undef MATCH_PLATFORM_INSN -#ifdef TARGET_ABI_MIPSN32 -#define tswapreg(ptr) tswap64(ptr) -#else -#define tswapreg(ptr) tswapal(ptr) -#endif - -/* See linux kernel: arch/mips/include/asm/reg.h. */ -enum { -#ifdef TARGET_MIPS64 - TARGET_EF_R0 = 0, -#else - TARGET_EF_R0 = 6, -#endif - TARGET_EF_R26 = TARGET_EF_R0 + 26, - TARGET_EF_R27 = TARGET_EF_R0 + 27, - TARGET_EF_LO = TARGET_EF_R0 + 32, - TARGET_EF_HI = TARGET_EF_R0 + 33, - TARGET_EF_CP0_EPC = TARGET_EF_R0 + 34, - TARGET_EF_CP0_BADVADDR = TARGET_EF_R0 + 35, - TARGET_EF_CP0_STATUS = TARGET_EF_R0 + 36, - TARGET_EF_CP0_CAUSE = TARGET_EF_R0 + 37 -}; - /* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) { - int i; - - for (i = 0; i <= TARGET_EF_R0; i++) { - r->regs[i] = 0; - } - for (i = 1; i < ARRAY_SIZE(env->active_tc.gpr); i++) { - r->regs[TARGET_EF_R0 + i] = tswapreg(env->active_tc.gpr[i]); + for (int i = 1; i < ARRAY_SIZE(env->active_tc.gpr); i++) { + r->pt.regs[i] = tswapl(env->active_tc.gpr[i]); } - r->regs[TARGET_EF_R26] = 0; - r->regs[TARGET_EF_R27] = 0; - r->regs[TARGET_EF_LO] = tswapreg(env->active_tc.LO[0]); - r->regs[TARGET_EF_HI] = tswapreg(env->active_tc.HI[0]); - r->regs[TARGET_EF_CP0_EPC] = tswapreg(env->active_tc.PC); - r->regs[TARGET_EF_CP0_BADVADDR] = tswapreg(env->CP0_BadVAddr); - r->regs[TARGET_EF_CP0_STATUS] = tswapreg(env->CP0_Status); - r->regs[TARGET_EF_CP0_CAUSE] = tswapreg(env->CP0_Cause); + r->pt.regs[26] = 0; + r->pt.regs[27] = 0; + r->pt.lo = tswapl(env->active_tc.LO[0]); + r->pt.hi = tswapl(env->active_tc.HI[0]); + r->pt.cp0_epc = tswapl(env->active_tc.PC); + r->pt.cp0_badvaddr = tswapl(env->CP0_BadVAddr); + r->pt.cp0_status = tswapl(env->CP0_Status); + r->pt.cp0_cause = tswapl(env->CP0_Cause); } diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index f767767eaa82b..a4b7fadbd698f 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -8,16 +8,18 @@ #ifndef MIPS_TARGET_ELF_H #define MIPS_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_BASE_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - /* See linux kernel: arch/mips/include/asm/elf.h. */ -#define ELF_NREG 45 typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + union { + abi_ulong reserved[45]; + struct target_pt_regs pt; + }; } target_elf_gregset_t; #endif diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index 046a165eef2cd..67bc9631346eb 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -8,20 +8,18 @@ #ifndef MIPS64_TARGET_ELF_H #define MIPS64_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_BASE_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 -#ifdef TARGET_ABI_MIPSN32 -typedef abi_ullong target_elf_greg_t; -#else -typedef abi_ulong target_elf_greg_t; -#endif - /* See linux kernel: arch/mips/include/asm/elf.h. */ -#define ELF_NREG 45 typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + union { + target_ulong reserved[45]; + struct target_pt_regs pt; + }; } target_elf_gregset_t; #endif From 7a4512db0a52b5005999f839cec934a77f32437b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 15:47:43 +1000 Subject: [PATCH 0445/1794] linux-user/openrisc: Create target_ptrace.h Move the target_pt_regs structure from target_syscall.h and rename to target_user_regs_struct, obviating the comment. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/openrisc/signal.c | 3 ++- linux-user/openrisc/target_ptrace.h | 13 +++++++++++++ linux-user/openrisc/target_syscall.h | 11 ----------- 3 files changed, 15 insertions(+), 12 deletions(-) create mode 100644 linux-user/openrisc/target_ptrace.h diff --git a/linux-user/openrisc/signal.c b/linux-user/openrisc/signal.c index cb74a9fe5e2bf..40249095f2301 100644 --- a/linux-user/openrisc/signal.c +++ b/linux-user/openrisc/signal.c @@ -21,9 +21,10 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "target_ptrace.h" typedef struct target_sigcontext { - struct target_pt_regs regs; + struct target_user_regs_struct regs; abi_ulong oldmask; } target_sigcontext; diff --git a/linux-user/openrisc/target_ptrace.h b/linux-user/openrisc/target_ptrace.h new file mode 100644 index 0000000000000..563c64852595a --- /dev/null +++ b/linux-user/openrisc/target_ptrace.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef OPENRISC_TARGET_PTRACE_H +#define OPENRISC_TARGET_PTRACE_H + +/* See arch/openrisc/include/uapi/asm/ptrace.h. */ +struct target_user_regs_struct { + abi_ulong gpr[32]; + abi_ulong pc; + abi_ulong sr; +}; + +#endif /* OPENRISC_TARGET_PTRACE_H */ diff --git a/linux-user/openrisc/target_syscall.h b/linux-user/openrisc/target_syscall.h index 7fe5b73d3bea4..c8394e9dcd7a3 100644 --- a/linux-user/openrisc/target_syscall.h +++ b/linux-user/openrisc/target_syscall.h @@ -1,17 +1,6 @@ #ifndef OPENRISC_TARGET_SYSCALL_H #define OPENRISC_TARGET_SYSCALL_H -/* Note that in linux/arch/openrisc/include/uapi/asm/ptrace.h, - * this is called user_regs_struct. Given that this is what - * is used within struct sigcontext we need this definition. - * However, elfload.c wants this name. - */ -struct target_pt_regs { - abi_ulong gpr[32]; - abi_ulong pc; - abi_ulong sr; -}; - #define UNAME_MACHINE "openrisc" #define UNAME_MINIMUM_RELEASE "2.6.32" From 611dd00a45860027f51281fc31e6428aefc8a003 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 12:09:26 +1000 Subject: [PATCH 0446/1794] linux-user/openrisc: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/openrisc/elfload.c | 8 +++----- linux-user/openrisc/target_elf.h | 12 +++++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/linux-user/openrisc/elfload.c b/linux-user/openrisc/elfload.c index bb5ad96711353..6bf02bf58d7aa 100644 --- a/linux-user/openrisc/elfload.c +++ b/linux-user/openrisc/elfload.c @@ -11,13 +11,11 @@ const char *get_elf_cpu_model(uint32_t eflags) return "any"; } -#define tswapreg(ptr) tswapal(ptr) - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUOpenRISCState *env) { for (int i = 0; i < 32; i++) { - r->regs[i] = tswapreg(cpu_get_gpr(env, i)); + r->pt.gpr[i] = tswapal(cpu_get_gpr(env, i)); } - r->regs[32] = tswapreg(env->pc); - r->regs[33] = tswapreg(cpu_get_sr(env)); + r->pt.pc = tswapal(env->pc); + r->pt.sr = tswapal(cpu_get_sr(env)); } diff --git a/linux-user/openrisc/target_elf.h b/linux-user/openrisc/target_elf.h index e97bdc11ed906..ad80e4b41aedc 100644 --- a/linux-user/openrisc/target_elf.h +++ b/linux-user/openrisc/target_elf.h @@ -8,14 +8,16 @@ #ifndef OPENRISC_TARGET_ELF_H #define OPENRISC_TARGET_ELF_H -#define HAVE_ELF_CORE_DUMP 1 +#include "target_ptrace.h" -typedef abi_ulong target_elf_greg_t; +#define HAVE_ELF_CORE_DUMP 1 -/* See linux kernel arch/openrisc/include/asm/elf.h. */ -#define ELF_NREG 34 /* gprs and pc, sr */ +/* + * See linux kernel: arch/openrisc/include/uapi/asm/elf.h, where + * elf_gregset_t is mapped to struct user_regs_struct via sizeof. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_user_regs_struct pt; } target_elf_gregset_t; #endif From 584c21f34eb0419858d74420df7d0fd5f3ea419a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 21:02:35 +1000 Subject: [PATCH 0447/1794] linux-user/ppc: Create target_ptrace.h Move the target_pt_regs structure from target_syscall.h. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/ppc/target_ptrace.h | 26 ++++++++++++++++++++++++++ linux-user/ppc/target_syscall.h | 28 ---------------------------- 2 files changed, 26 insertions(+), 28 deletions(-) create mode 100644 linux-user/ppc/target_ptrace.h diff --git a/linux-user/ppc/target_ptrace.h b/linux-user/ppc/target_ptrace.h new file mode 100644 index 0000000000000..df77bfde73ee1 --- /dev/null +++ b/linux-user/ppc/target_ptrace.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef PPC_TARGET_PTRACE_H +#define PPC_TARGET_PTRACE_H + +struct target_pt_regs { + abi_ulong gpr[32]; + abi_ulong nip; + abi_ulong msr; + abi_ulong orig_gpr3; /* Used for restarting system calls */ + abi_ulong ctr; + abi_ulong link; + abi_ulong xer; + abi_ulong ccr; +#if defined(TARGET_PPC64) + abi_ulong softe; +#else + abi_ulong mq; /* 601 only (not used at present) */ +#endif + abi_ulong trap; /* Reason for being here */ + abi_ulong dar; /* Fault registers */ + abi_ulong dsisr; + abi_ulong result; /* Result of a system call */ +}; + +#endif /* PPC_TARGET_PTRACE_H */ diff --git a/linux-user/ppc/target_syscall.h b/linux-user/ppc/target_syscall.h index 77b36d0b46e67..976b4bb7e9a2b 100644 --- a/linux-user/ppc/target_syscall.h +++ b/linux-user/ppc/target_syscall.h @@ -20,34 +20,6 @@ #ifndef PPC_TARGET_SYSCALL_H #define PPC_TARGET_SYSCALL_H -/* XXX: ABSOLUTELY BUGGY: - * for now, this is quite just a cut-and-paste from i386 target... - */ - -/* default linux values for the selectors */ -#define __USER_DS (1) - -struct target_pt_regs { - abi_ulong gpr[32]; - abi_ulong nip; - abi_ulong msr; - abi_ulong orig_gpr3; /* Used for restarting system calls */ - abi_ulong ctr; - abi_ulong link; - abi_ulong xer; - abi_ulong ccr; -#if defined(TARGET_PPC64) - abi_ulong softe; -#else - abi_ulong mq; /* 601 only (not used at present) */ -#endif - /* Used on APUS to hold IPL value. */ - abi_ulong trap; /* Reason for being here */ - abi_ulong dar; /* Fault registers */ - abi_ulong dsisr; - abi_ulong result; /* Result of a system call */ -}; - /* ioctls */ struct target_revectored_struct { abi_ulong __map[8]; /* 256 bits */ From 8ad5f12426dd6218db4753722ab240a16a235259 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 12:22:55 +1000 Subject: [PATCH 0448/1794] linux-user/ppc: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/ppc/elfload.c | 23 ++++++++--------------- linux-user/ppc/target_elf.h | 16 +++++++++++----- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/linux-user/ppc/elfload.c b/linux-user/ppc/elfload.c index 114e40a358aae..0d54da980396c 100644 --- a/linux-user/ppc/elfload.c +++ b/linux-user/ppc/elfload.c @@ -131,23 +131,16 @@ abi_ulong get_elf_hwcap2(CPUState *cs) return features; } -#define tswapreg(ptr) tswapal(ptr) - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) { - int i; - target_ulong ccr = 0; - - for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - r->regs[i] = tswapreg(env->gpr[i]); + for (int i = 0; i < ARRAY_SIZE(env->gpr); i++) { + r->pt.gpr[i] = tswapal(env->gpr[i]); } - r->regs[32] = tswapreg(env->nip); - r->regs[33] = tswapreg(env->msr); - r->regs[35] = tswapreg(env->ctr); - r->regs[36] = tswapreg(env->lr); - r->regs[37] = tswapreg(cpu_read_xer(env)); - - ccr = ppc_get_cr(env); - r->regs[38] = tswapreg(ccr); + r->pt.nip = tswapal(env->nip); + r->pt.msr = tswapal(env->msr); + r->pt.ctr = tswapal(env->ctr); + r->pt.link = tswapal(env->lr); + r->pt.xer = tswapal(cpu_read_xer(env)); + r->pt.ccr = tswapal(ppc_get_cr(env)); } diff --git a/linux-user/ppc/target_elf.h b/linux-user/ppc/target_elf.h index 72615553ea53e..2a61cd2896e3d 100644 --- a/linux-user/ppc/target_elf.h +++ b/linux-user/ppc/target_elf.h @@ -8,16 +8,22 @@ #ifndef PPC_TARGET_ELF_H #define PPC_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - -/* See linux kernel: arch/powerpc/include/asm/elf.h. */ -#define ELF_NREG 48 +/* + * The size of 48 words is set in arch/powerpc/include/uapi/asm/elf.h. + * However PPC_ELF_CORE_COPY_REGS in arch/powerpc/include/asm/elf.h + * open-codes a memcpy from struct pt_regs, then zeros the rest. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + union { + struct target_pt_regs pt; + abi_ulong reserved[48]; + }; } target_elf_gregset_t; #endif From 71c2c79815fafe6d509ca1a76bc67d6041aaa37b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 21:13:03 +1000 Subject: [PATCH 0449/1794] linux-user/s390x: Create target_ptrace.h Move target_psw_t to target_ptrace.h. Note that abi_ulong already has an attribute for 8-byte alignment, so there's no need to carry another on target_psw_t. Remove the target_pt_regs; add target_s390x_reg to target_ptrace.h, which matches what is actually used. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/s390x/signal.c | 1 + linux-user/s390x/target_ptrace.h | 18 ++++++++++++++++++ linux-user/s390x/target_syscall.h | 22 ---------------------- 3 files changed, 19 insertions(+), 22 deletions(-) create mode 100644 linux-user/s390x/target_ptrace.h diff --git a/linux-user/s390x/signal.c b/linux-user/s390x/signal.c index df49c2470802b..96d1c8d11ca5d 100644 --- a/linux-user/s390x/signal.c +++ b/linux-user/s390x/signal.c @@ -22,6 +22,7 @@ #include "signal-common.h" #include "linux-user/trace.h" #include "vdso-asmoffset.h" +#include "target_ptrace.h" #define __NUM_GPRS 16 #define __NUM_FPRS 16 diff --git a/linux-user/s390x/target_ptrace.h b/linux-user/s390x/target_ptrace.h new file mode 100644 index 0000000000000..a5ceb75a747a4 --- /dev/null +++ b/linux-user/s390x/target_ptrace.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef S390X_TARGET_PTRACE_H +#define S390X_TARGET_PTRACE_H + +typedef struct { + abi_ulong mask; + abi_ulong addr; +} target_psw_t; + +struct target_s390_regs { + target_psw_t psw; + abi_ulong gprs[16]; + abi_uint acrs[16]; + abi_ulong orig_gpr2; +}; + +#endif /* S390X_TARGET_PTRACE_H */ diff --git a/linux-user/s390x/target_syscall.h b/linux-user/s390x/target_syscall.h index 4018988a25e1b..f01f9a0baa2a4 100644 --- a/linux-user/s390x/target_syscall.h +++ b/linux-user/s390x/target_syscall.h @@ -1,28 +1,6 @@ #ifndef S390X_TARGET_SYSCALL_H #define S390X_TARGET_SYSCALL_H -/* this typedef defines how a Program Status Word looks like */ -typedef struct { - abi_ulong mask; - abi_ulong addr; -} __attribute__ ((aligned(8))) target_psw_t; - -/* - * The pt_regs struct defines the way the registers are stored on - * the stack during a system call. - */ - -#define TARGET_NUM_GPRS 16 - -struct target_pt_regs { - abi_ulong args[1]; - target_psw_t psw; - abi_ulong gprs[TARGET_NUM_GPRS]; - abi_ulong orig_gpr2; - unsigned short ilen; - unsigned short trap; -}; - #define UNAME_MACHINE "s390x" #define UNAME_MINIMUM_RELEASE "2.6.32" From dabda4f36a992dffde252919124a25fe7054fe42 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 12:31:49 +1000 Subject: [PATCH 0450/1794] linux-user/s390x: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. This lets us drop the ugly cast to uint32_t* in the middle. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/s390x/elfload.c | 28 +++++++--------------------- linux-user/s390x/target_elf.h | 12 +++++++----- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/linux-user/s390x/elfload.c b/linux-user/s390x/elfload.c index 4113273b72453..27109279e28cb 100644 --- a/linux-user/s390x/elfload.c +++ b/linux-user/s390x/elfload.c @@ -68,29 +68,15 @@ const char *elf_hwcap_str(uint32_t bit) return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; } -#define tswapreg(ptr) tswapal(ptr) - -enum { - TARGET_REG_PSWM = 0, - TARGET_REG_PSWA = 1, - TARGET_REG_GPRS = 2, - TARGET_REG_ARS = 18, - TARGET_REG_ORIG_R2 = 26, -}; - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUS390XState *env) { - int i; - uint32_t *aregs; - - r->regs[TARGET_REG_PSWM] = tswapreg(env->psw.mask); - r->regs[TARGET_REG_PSWA] = tswapreg(env->psw.addr); - for (i = 0; i < 16; i++) { - r->regs[TARGET_REG_GPRS + i] = tswapreg(env->regs[i]); + r->pt.psw.mask = tswapal(env->psw.mask); + r->pt.psw.addr = tswapal(env->psw.addr); + for (int i = 0; i < 16; i++) { + r->pt.gprs[i] = tswapal(env->regs[i]); } - aregs = (uint32_t *)&(r->regs[TARGET_REG_ARS]); - for (i = 0; i < 16; i++) { - aregs[i] = tswap32(env->aregs[i]); + for (int i = 0; i < 16; i++) { + r->pt.acrs[i] = tswap32(env->aregs[i]); } - r->regs[TARGET_REG_ORIG_R2] = 0; + r->pt.orig_gpr2 = 0; } diff --git a/linux-user/s390x/target_elf.h b/linux-user/s390x/target_elf.h index b7d863ee6661d..670c7b3eed7ed 100644 --- a/linux-user/s390x/target_elf.h +++ b/linux-user/s390x/target_elf.h @@ -8,15 +8,17 @@ #ifndef S390X_TARGET_ELF_H #define S390X_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - -/* See linux kernel: arch/s390/include/uapi/asm/ptrace.h (s390_regs). */ -#define ELF_NREG 27 +/* + * See linux kernel: arch/s390/include/asm/elf.h, where + * elf_gregset_t is typedef'd to struct s390_regs. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_s390_regs pt; } target_elf_gregset_t; #endif From ec12f80d1f2915bbd1c2734f7ce1dcbb3c0ff70b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 21:22:08 +1000 Subject: [PATCH 0451/1794] linux-user/sh4: Create target_ptrace.h Move target_pt_regs to target_ptrace.h. Convert to abi_foo types. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/sh4/target_ptrace.h | 18 ++++++++++++++++++ linux-user/sh4/target_syscall.h | 11 ----------- 2 files changed, 18 insertions(+), 11 deletions(-) create mode 100644 linux-user/sh4/target_ptrace.h diff --git a/linux-user/sh4/target_ptrace.h b/linux-user/sh4/target_ptrace.h new file mode 100644 index 0000000000000..b80218526b110 --- /dev/null +++ b/linux-user/sh4/target_ptrace.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef SH4_TARGET_PTRACE_H +#define SH4_TARGET_PTRACE_H + +/* See arch/sh/include/uapi/asm/ptrace_32.h. */ +struct target_pt_regs { + abi_ulong regs[16]; + abi_ulong pc; + abi_ulong pr; + abi_ulong sr; + abi_ulong gbr; + abi_ulong mach; + abi_ulong macl; + abi_long tra; +}; + +#endif /* SH4_TARGET_PTRACE_H */ diff --git a/linux-user/sh4/target_syscall.h b/linux-user/sh4/target_syscall.h index 148398855dfee..2f3557742d305 100644 --- a/linux-user/sh4/target_syscall.h +++ b/linux-user/sh4/target_syscall.h @@ -1,17 +1,6 @@ #ifndef SH4_TARGET_SYSCALL_H #define SH4_TARGET_SYSCALL_H -struct target_pt_regs { - unsigned long regs[16]; - unsigned long pc; - unsigned long pr; - unsigned long sr; - unsigned long gbr; - unsigned long mach; - unsigned long macl; - long tra; -}; - #define UNAME_MACHINE "sh4" #define UNAME_MINIMUM_RELEASE "2.6.32" From 92e1a92823eaa812451a9499754431b2002503ec Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 13:34:12 +1000 Subject: [PATCH 0452/1794] linux-user/sh4: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/sh4/elfload.c | 28 +++++++--------------------- linux-user/sh4/target_elf.h | 12 +++++++----- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/linux-user/sh4/elfload.c b/linux-user/sh4/elfload.c index 71cae9703e727..ddf2aaaed7ca0 100644 --- a/linux-user/sh4/elfload.c +++ b/linux-user/sh4/elfload.c @@ -38,30 +38,16 @@ abi_ulong get_elf_hwcap(CPUState *cs) return hwcap; } -#define tswapreg(ptr) tswapal(ptr) - -/* See linux kernel: arch/sh/include/asm/ptrace.h. */ -enum { - TARGET_REG_PC = 16, - TARGET_REG_PR = 17, - TARGET_REG_SR = 18, - TARGET_REG_GBR = 19, - TARGET_REG_MACH = 20, - TARGET_REG_MACL = 21, - TARGET_REG_SYSCALL = 22 -}; - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) { for (int i = 0; i < 16; i++) { - r->regs[i] = tswapreg(env->gregs[i]); + r->pt.regs[i] = tswapal(env->gregs[i]); } - r->regs[TARGET_REG_PC] = tswapreg(env->pc); - r->regs[TARGET_REG_PR] = tswapreg(env->pr); - r->regs[TARGET_REG_SR] = tswapreg(env->sr); - r->regs[TARGET_REG_GBR] = tswapreg(env->gbr); - r->regs[TARGET_REG_MACH] = tswapreg(env->mach); - r->regs[TARGET_REG_MACL] = tswapreg(env->macl); - r->regs[TARGET_REG_SYSCALL] = 0; /* FIXME */ + r->pt.pc = tswapal(env->pc); + r->pt.pr = tswapal(env->pr); + r->pt.sr = tswapal(env->sr); + r->pt.gbr = tswapal(env->gbr); + r->pt.mach = tswapal(env->mach); + r->pt.macl = tswapal(env->macl); } diff --git a/linux-user/sh4/target_elf.h b/linux-user/sh4/target_elf.h index f7443ddbacd15..fd3ae68a0140f 100644 --- a/linux-user/sh4/target_elf.h +++ b/linux-user/sh4/target_elf.h @@ -8,15 +8,17 @@ #ifndef SH4_TARGET_ELF_H #define SH4_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - -/* See linux kernel: arch/sh/include/asm/elf.h. */ -#define ELF_NREG 23 +/* + * See linux kernel: arch/sh/include/asm/elf.h, where + * elf_gregset_t is mapped to struct pt_regs via sizeof. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_pt_regs pt; } target_elf_gregset_t; #endif From b90e36861636fc5f021fd2a0676087d076cd0cc9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 21:35:33 +1000 Subject: [PATCH 0453/1794] linux-user/xtensa: Create target_ptrace.h Remove the target_pt_regs; add target_user_pt_regs to target_ptrace.h, which matches what is actually used. Remove xtensa_reg_t and xtregs_opt_t. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/xtensa/target_ptrace.h | 22 +++++++++++++++++++ linux-user/xtensa/target_syscall.h | 35 ------------------------------ 2 files changed, 22 insertions(+), 35 deletions(-) create mode 100644 linux-user/xtensa/target_ptrace.h diff --git a/linux-user/xtensa/target_ptrace.h b/linux-user/xtensa/target_ptrace.h new file mode 100644 index 0000000000000..32443d0deeced --- /dev/null +++ b/linux-user/xtensa/target_ptrace.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef XTENSA_TARGET_PTRACE_H +#define XTENSA_TARGET_PTRACE_H + +/* See arch/xtensa/include/uapi/asm/ptrace.h. */ +struct target_user_pt_regs { + uint32_t pc; + uint32_t ps; + uint32_t lbeg; + uint32_t lend; + uint32_t lcount; + uint32_t sar; + uint32_t windowstart; + uint32_t windowbase; + uint32_t threadptr; + uint32_t syscall; + uint32_t reserved[6 + 48]; + uint32_t a[64]; +}; + +#endif /* XTENSA_TARGET_PTRACE_H */ diff --git a/linux-user/xtensa/target_syscall.h b/linux-user/xtensa/target_syscall.h index afc86a153fc04..5d4352a4d1931 100644 --- a/linux-user/xtensa/target_syscall.h +++ b/linux-user/xtensa/target_syscall.h @@ -8,41 +8,6 @@ #define MMAP_SHIFT TARGET_PAGE_BITS -typedef uint32_t xtensa_reg_t; -typedef struct { -} xtregs_opt_t; /* TODO */ - -struct target_pt_regs { - xtensa_reg_t pc; /* 4 */ - xtensa_reg_t ps; /* 8 */ - xtensa_reg_t depc; /* 12 */ - xtensa_reg_t exccause; /* 16 */ - xtensa_reg_t excvaddr; /* 20 */ - xtensa_reg_t debugcause; /* 24 */ - xtensa_reg_t wmask; /* 28 */ - xtensa_reg_t lbeg; /* 32 */ - xtensa_reg_t lend; /* 36 */ - xtensa_reg_t lcount; /* 40 */ - xtensa_reg_t sar; /* 44 */ - xtensa_reg_t windowbase; /* 48 */ - xtensa_reg_t windowstart; /* 52 */ - xtensa_reg_t syscall; /* 56 */ - xtensa_reg_t icountlevel; /* 60 */ - xtensa_reg_t scompare1; /* 64 */ - xtensa_reg_t threadptr; /* 68 */ - - /* Additional configurable registers that are used by the compiler. */ - xtregs_opt_t xtregs_opt; - - /* Make sure the areg field is 16 bytes aligned. */ - int align[0] __attribute__ ((aligned(16))); - - /* current register frame. - * Note: The ESF for kernel exceptions ends after 16 registers! - */ - xtensa_reg_t areg[16]; -}; - #define TARGET_MCL_CURRENT 1 #define TARGET_MCL_FUTURE 2 #define TARGET_MCL_ONFAULT 4 From abfa6c7c2a0ddc4eaf373de320d758435e7acf9b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 13:44:40 +1000 Subject: [PATCH 0454/1794] linux-user/xtensa: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/xtensa/elfload.c | 39 +++++++++++----------------------- linux-user/xtensa/target_elf.h | 12 ++++++----- 2 files changed, 19 insertions(+), 32 deletions(-) diff --git a/linux-user/xtensa/elfload.c b/linux-user/xtensa/elfload.c index 49e709a094fe0..68aeed855f991 100644 --- a/linux-user/xtensa/elfload.c +++ b/linux-user/xtensa/elfload.c @@ -11,36 +11,21 @@ const char *get_elf_cpu_model(uint32_t eflags) return XTENSA_DEFAULT_CPU_MODEL; } -#define tswapreg(ptr) tswapal(ptr) - -enum { - TARGET_REG_PC, - TARGET_REG_PS, - TARGET_REG_LBEG, - TARGET_REG_LEND, - TARGET_REG_LCOUNT, - TARGET_REG_SAR, - TARGET_REG_WINDOWSTART, - TARGET_REG_WINDOWBASE, - TARGET_REG_THREADPTR, - TARGET_REG_AR0 = 64, -}; - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUXtensaState *env) { - unsigned i; + r->pt.pc = tswap32(env->pc); + r->pt.ps = tswap32(env->sregs[PS] & ~PS_EXCM); + r->pt.lbeg = tswap32(env->sregs[LBEG]); + r->pt.lend = tswap32(env->sregs[LEND]); + r->pt.lcount = tswap32(env->sregs[LCOUNT]); + r->pt.sar = tswap32(env->sregs[SAR]); + r->pt.windowstart = tswap32(env->sregs[WINDOW_START]); + r->pt.windowbase = tswap32(env->sregs[WINDOW_BASE]); + r->pt.threadptr = tswap32(env->uregs[THREADPTR]); - r->regs[TARGET_REG_PC] = tswapreg(env->pc); - r->regs[TARGET_REG_PS] = tswapreg(env->sregs[PS] & ~PS_EXCM); - r->regs[TARGET_REG_LBEG] = tswapreg(env->sregs[LBEG]); - r->regs[TARGET_REG_LEND] = tswapreg(env->sregs[LEND]); - r->regs[TARGET_REG_LCOUNT] = tswapreg(env->sregs[LCOUNT]); - r->regs[TARGET_REG_SAR] = tswapreg(env->sregs[SAR]); - r->regs[TARGET_REG_WINDOWSTART] = tswapreg(env->sregs[WINDOW_START]); - r->regs[TARGET_REG_WINDOWBASE] = tswapreg(env->sregs[WINDOW_BASE]); - r->regs[TARGET_REG_THREADPTR] = tswapreg(env->uregs[THREADPTR]); xtensa_sync_phys_from_window((CPUXtensaState *)env); - for (i = 0; i < env->config->nareg; ++i) { - r->regs[TARGET_REG_AR0 + i] = tswapreg(env->phys_regs[i]); + + for (unsigned i = 0; i < env->config->nareg; ++i) { + r->pt.a[i] = tswap32(env->phys_regs[i]); } } diff --git a/linux-user/xtensa/target_elf.h b/linux-user/xtensa/target_elf.h index 43e241aac1231..850a7206a5091 100644 --- a/linux-user/xtensa/target_elf.h +++ b/linux-user/xtensa/target_elf.h @@ -8,14 +8,16 @@ #ifndef XTENSA_TARGET_ELF_H #define XTENSA_TARGET_ELF_H -#define HAVE_ELF_CORE_DUMP 1 +#include "target_ptrace.h" -typedef abi_ulong target_elf_greg_t; +#define HAVE_ELF_CORE_DUMP 1 -/* See linux kernel: arch/xtensa/include/asm/elf.h. */ -#define ELF_NREG 128 +/* + * See linux kernel: arch/xtensa/include/asm/elf.h, where elf_gregset_t + * is mapped to struct user_pt_regs via typedef and sizeof. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_user_pt_regs pt; } target_elf_gregset_t; #endif From 8d4a6f8e4c95e11a3da2e38682da2819d4e2160c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 09:06:15 -1000 Subject: [PATCH 0455/1794] linux-user: Move init_guest_commpage to x86_64/elfload.c Rename INIT_GUEST_COMMPAGE to HAVE_GUEST_COMMPAGE to match the other HAVE_* defines. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 23 +---------------------- linux-user/loader.h | 3 +++ linux-user/x86_64/elfload.c | 20 ++++++++++++++++++++ linux-user/x86_64/target_elf.h | 1 + 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 07d83c674d6b5..0ba75a83b378b 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -145,27 +145,6 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 -#if ULONG_MAX > UINT32_MAX -#define INIT_GUEST_COMMPAGE -static bool init_guest_commpage(void) -{ - /* - * The vsyscall page is at a high negative address aka kernel space, - * which means that we cannot actually allocate it with target_mmap. - * We still should be able to use page_set_flags, unless the user - * has specified -R reserved_va, which would trigger an assert(). - */ - if (reserved_va != 0 && - TARGET_VSYSCALL_PAGE + TARGET_PAGE_SIZE - 1 > reserved_va) { - error_report("Cannot allocate vsyscall page"); - exit(EXIT_FAILURE); - } - page_set_flags(TARGET_VSYSCALL_PAGE, - TARGET_VSYSCALL_PAGE | ~TARGET_PAGE_MASK, - PAGE_EXEC | PAGE_VALID); - return true; -} -#endif #else /* @@ -1215,7 +1194,7 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, #else #define HI_COMMPAGE 0 #define LO_COMMPAGE -1 -#ifndef INIT_GUEST_COMMPAGE +#ifndef HAVE_GUEST_COMMPAGE #define init_guest_commpage() true #endif #endif diff --git a/linux-user/loader.h b/linux-user/loader.h index 8f4a7f69acbb4..98015fba7d373 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -105,6 +105,9 @@ const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); const char *get_elf_platform(CPUState *cs); const char *get_elf_base_platform(CPUState *cs); +#if defined(TARGET_X86_64) +bool init_guest_commpage(void); +#endif struct target_elf_gregset_t; void elf_core_copy_regs(struct target_elf_gregset_t *, const CPUArchState *); diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c index 12de1c54c7f6d..1e7000c6bcfd7 100644 --- a/linux-user/x86_64/elfload.c +++ b/linux-user/x86_64/elfload.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu.h" #include "loader.h" #include "target_elf.h" @@ -21,6 +22,25 @@ const char *get_elf_platform(CPUState *cs) return "x86_64"; } +bool init_guest_commpage(void) +{ + /* + * The vsyscall page is at a high negative address aka kernel space, + * which means that we cannot actually allocate it with target_mmap. + * We still should be able to use page_set_flags, unless the user + * has specified -R reserved_va, which would trigger an assert(). + */ + if (reserved_va != 0 && + TARGET_VSYSCALL_PAGE + TARGET_PAGE_SIZE - 1 > reserved_va) { + error_report("Cannot allocate vsyscall page"); + exit(EXIT_FAILURE); + } + page_set_flags(TARGET_VSYSCALL_PAGE, + TARGET_VSYSCALL_PAGE | ~TARGET_PAGE_MASK, + PAGE_EXEC | PAGE_VALID); + return true; +} + void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) { r->pt.r15 = tswapal(env->regs[15]); diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index 32a9eec431495..f05b1d4dba48a 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -13,6 +13,7 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 +#define HAVE_GUEST_COMMPAGE 1 /* * See linux kernel: arch/x86/include/asm/elf.h, where From a56ef5e8454e42c0d263a97a1297ae67f4ce19cd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 09:11:12 -1000 Subject: [PATCH 0456/1794] linux-user: Move init_guest_commpage to arm/elfload.c Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/arm/elfload.c | 46 +++++++++++++++++++++++++++++++++++ linux-user/arm/target_elf.h | 2 ++ linux-user/elfload.c | 48 ------------------------------------- linux-user/loader.h | 2 +- 4 files changed, 49 insertions(+), 49 deletions(-) diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index f811c2f07a9c1..1205687976892 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -3,6 +3,8 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "user-internals.h" +#include "target_elf.h" #include "target/arm/cpu-features.h" #include "target_elf.h" @@ -201,6 +203,50 @@ const char *get_elf_platform(CPUState *cs) #undef END } +bool init_guest_commpage(void) +{ + ARMCPU *cpu = ARM_CPU(thread_cpu); + int host_page_size = qemu_real_host_page_size(); + abi_ptr commpage; + void *want; + void *addr; + + /* + * M-profile allocates maximum of 2GB address space, so can never + * allocate the commpage. Skip it. + */ + if (arm_feature(&cpu->env, ARM_FEATURE_M)) { + return true; + } + + commpage = HI_COMMPAGE & -host_page_size; + want = g2h_untagged(commpage); + addr = mmap(want, host_page_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | + (commpage < reserved_va ? MAP_FIXED : MAP_FIXED_NOREPLACE), + -1, 0); + + if (addr == MAP_FAILED) { + perror("Allocating guest commpage"); + exit(EXIT_FAILURE); + } + if (addr != want) { + return false; + } + + /* Set kernel helper versions; rest of page is 0. */ + __put_user(5, (uint32_t *)g2h_untagged(0xffff0ffcu)); + + if (mprotect(addr, host_page_size, PROT_READ)) { + perror("Protecting guest commpage"); + exit(EXIT_FAILURE); + } + + page_set_flags(commpage, commpage | (host_page_size - 1), + PAGE_READ | PAGE_EXEC | PAGE_VALID); + return true; +} + void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) { for (int i = 0; i < 16; ++i) { diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index fa8f8af2f3edc..5f81a43efb23b 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -15,6 +15,8 @@ #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 +#define HI_COMMPAGE ((intptr_t)0xffff0f00u) + /* * See linux kernel: arch/arm/include/asm/elf.h, where * elf_gregset_t is mapped to struct pt_regs via sizeof. diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0ba75a83b378b..2281853c575e6 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -191,54 +191,6 @@ typedef abi_int target_pid_t; #define ELF_EXEC_PAGESIZE 4096 -/* The commpage only exists for 32 bit kernels */ - -#define HI_COMMPAGE (intptr_t)0xffff0f00u - -static bool init_guest_commpage(void) -{ - ARMCPU *cpu = ARM_CPU(thread_cpu); - int host_page_size = qemu_real_host_page_size(); - abi_ptr commpage; - void *want; - void *addr; - - /* - * M-profile allocates maximum of 2GB address space, so can never - * allocate the commpage. Skip it. - */ - if (arm_feature(&cpu->env, ARM_FEATURE_M)) { - return true; - } - - commpage = HI_COMMPAGE & -host_page_size; - want = g2h_untagged(commpage); - addr = mmap(want, host_page_size, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE | - (commpage < reserved_va ? MAP_FIXED : MAP_FIXED_NOREPLACE), - -1, 0); - - if (addr == MAP_FAILED) { - perror("Allocating guest commpage"); - exit(EXIT_FAILURE); - } - if (addr != want) { - return false; - } - - /* Set kernel helper versions; rest of page is 0. */ - __put_user(5, (uint32_t *)g2h_untagged(0xffff0ffcu)); - - if (mprotect(addr, host_page_size, PROT_READ)) { - perror("Protecting guest commpage"); - exit(EXIT_FAILURE); - } - - page_set_flags(commpage, commpage | (host_page_size - 1), - PAGE_READ | PAGE_EXEC | PAGE_VALID); - return true; -} - #if TARGET_BIG_ENDIAN #include "elf.h" #include "vdso-be8.c.inc" diff --git a/linux-user/loader.h b/linux-user/loader.h index 98015fba7d373..0c2cc556c3667 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -105,7 +105,7 @@ const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); const char *get_elf_platform(CPUState *cs); const char *get_elf_base_platform(CPUState *cs); -#if defined(TARGET_X86_64) +#if defined(TARGET_X86_64) || defined(TARGET_ARM) bool init_guest_commpage(void); #endif From 6a91f64ee12fe54d1a9573e9ecbbae037b113097 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 09:13:54 -1000 Subject: [PATCH 0457/1794] linux-user: Move init_guest_commpage to hppa/elfload.c Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 32 -------------------------------- linux-user/hppa/elfload.c | 31 +++++++++++++++++++++++++++++++ linux-user/hppa/target_elf.h | 2 ++ linux-user/loader.h | 2 -- 4 files changed, 33 insertions(+), 34 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 2281853c575e6..25f29e60deec1 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -415,38 +415,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define VDSO_HEADER "vdso.c.inc" -#define LO_COMMPAGE 0 - -static bool init_guest_commpage(void) -{ - /* If reserved_va, then we have already mapped 0 page on the host. */ - if (!reserved_va) { - void *want, *addr; - - want = g2h_untagged(LO_COMMPAGE); - addr = mmap(want, TARGET_PAGE_SIZE, PROT_NONE, - MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0); - if (addr == MAP_FAILED) { - perror("Allocating guest commpage"); - exit(EXIT_FAILURE); - } - if (addr != want) { - return false; - } - } - - /* - * On Linux, page zero is normally marked execute only + gateway. - * Normal read or write is supposed to fail (thus PROT_NONE above), - * but specific offsets have kernel code mapped to raise permissions - * and implement syscalls. Here, simply mark the page executable. - * Special case the entry points during translation (see do_page_zero). - */ - page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK, - PAGE_EXEC | PAGE_VALID); - return true; -} - #endif /* TARGET_HPPA */ #ifdef TARGET_XTENSA diff --git a/linux-user/hppa/elfload.c b/linux-user/hppa/elfload.c index 9dd3fe092a82e..018034f244e58 100644 --- a/linux-user/hppa/elfload.c +++ b/linux-user/hppa/elfload.c @@ -3,6 +3,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -14,3 +15,33 @@ const char *get_elf_platform(CPUState *cs) { return "PARISC"; } + +bool init_guest_commpage(void) +{ + /* If reserved_va, then we have already mapped 0 page on the host. */ + if (!reserved_va) { + void *want, *addr; + + want = g2h_untagged(LO_COMMPAGE); + addr = mmap(want, TARGET_PAGE_SIZE, PROT_NONE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0); + if (addr == MAP_FAILED) { + perror("Allocating guest commpage"); + exit(EXIT_FAILURE); + } + if (addr != want) { + return false; + } + } + + /* + * On Linux, page zero is normally marked execute only + gateway. + * Normal read or write is supposed to fail (thus PROT_NONE above), + * but specific offsets have kernel code mapped to raise permissions + * and implement syscalls. Here, simply mark the page executable. + * Special case the entry points during translation (see do_page_zero). + */ + page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK, + PAGE_EXEC | PAGE_VALID); + return true; +} diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h index 85be00584d8ea..b654758afaa53 100644 --- a/linux-user/hppa/target_elf.h +++ b/linux-user/hppa/target_elf.h @@ -10,4 +10,6 @@ #define HAVE_ELF_PLATFORM 1 +#define LO_COMMPAGE 0 + #endif diff --git a/linux-user/loader.h b/linux-user/loader.h index 0c2cc556c3667..c3b8f92e23840 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -105,9 +105,7 @@ const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); const char *get_elf_platform(CPUState *cs); const char *get_elf_base_platform(CPUState *cs); -#if defined(TARGET_X86_64) || defined(TARGET_ARM) bool init_guest_commpage(void); -#endif struct target_elf_gregset_t; void elf_core_copy_regs(struct target_elf_gregset_t *, const CPUArchState *); From 6e1c4ec4582814537c9a2b4700ff32da44fb27af Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 09:16:37 -1000 Subject: [PATCH 0458/1794] linux-user: Replace init_guest_commpage macro with function Turn the fallback macro into a function. This will produce a link error if the other macros are set up incorrectly. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 25f29e60deec1..81bf05f581ea2 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1115,7 +1115,7 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, #define HI_COMMPAGE 0 #define LO_COMMPAGE -1 #ifndef HAVE_GUEST_COMMPAGE -#define init_guest_commpage() true +bool init_guest_commpage(void) { return true; } #endif #endif From 71cc79a4a172d28638ad27a4e6327a4ce1bcdf2b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 09:33:51 -1000 Subject: [PATCH 0459/1794] linux-user: Move get_vdso_image_info to arm/elfload.c Rename from vdso_image_info to avoid a symbol clash. Define HAVE_VDSO_IMAGE_INFO to signal the external definition exists. Provide fallback versions for other targets. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/arm/elfload.c | 20 ++++++++++++++++++ linux-user/arm/target_elf.h | 1 + linux-user/elfload.c | 41 ++++++++----------------------------- linux-user/loader.h | 12 +++++++++++ 4 files changed, 42 insertions(+), 32 deletions(-) diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index 1205687976892..308ed23fcbd7c 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -7,6 +7,7 @@ #include "target_elf.h" #include "target/arm/cpu-features.h" #include "target_elf.h" +#include "elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -255,3 +256,22 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) r->pt.cpsr = tswapal(cpsr_read((CPUARMState *)env)); r->pt.orig_r0 = tswapal(env->regs[0]); /* FIXME */ } + +#if TARGET_BIG_ENDIAN +# include "vdso-be8.c.inc" +# include "vdso-be32.c.inc" +#else +# include "vdso-le.c.inc" +#endif + +const VdsoImageInfo *get_vdso_image_info(uint32_t elf_flags) +{ +#if TARGET_BIG_ENDIAN + return (EF_ARM_EABI_VERSION(elf_flags) >= EF_ARM_EABI_VER4 + && (elf_flags & EF_ARM_BE8) + ? &vdso_be8_image_info + : &vdso_be32_image_info); +#else + return &vdso_image_info; +#endif +} diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index 5f81a43efb23b..19fdfa2f2c907 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -14,6 +14,7 @@ #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 +#define HAVE_VDSO_IMAGE_INFO 1 #define HI_COMMPAGE ((intptr_t)0xffff0f00u) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 81bf05f581ea2..aed390ebb3e92 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -40,15 +40,6 @@ #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 #endif -typedef struct { - const uint8_t *image; - const uint32_t *relocs; - unsigned image_size; - unsigned reloc_count; - unsigned sigreturn_ofs; - unsigned rt_sigreturn_ofs; -} VdsoImageInfo; - #define ELF_OSABI ELFOSABI_SYSV /* from personality.h */ @@ -191,23 +182,6 @@ typedef abi_int target_pid_t; #define ELF_EXEC_PAGESIZE 4096 -#if TARGET_BIG_ENDIAN -#include "elf.h" -#include "vdso-be8.c.inc" -#include "vdso-be32.c.inc" - -static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) -{ - return (EF_ARM_EABI_VERSION(elf_flags) >= EF_ARM_EABI_VER4 - && (elf_flags & EF_ARM_BE8) - ? &vdso_be8_image_info - : &vdso_be32_image_info); -} -#define vdso_image_info vdso_image_info -#else -# define VDSO_HEADER "vdso-le.c.inc" -#endif - #else /* 64 bit ARM definitions */ @@ -1973,14 +1947,17 @@ static void load_elf_interp(const char *filename, struct image_info *info, load_elf_image(filename, &src, info, &ehdr, NULL); } -#ifndef vdso_image_info +#ifndef HAVE_VDSO_IMAGE_INFO +const VdsoImageInfo *get_vdso_image_info(uint32_t elf_flags) +{ #ifdef VDSO_HEADER #include VDSO_HEADER -#define vdso_image_info(flags) &vdso_image_info + return &vdso_image_info; #else -#define vdso_image_info(flags) NULL -#endif /* VDSO_HEADER */ -#endif /* vdso_image_info */ + return NULL; +#endif +} +#endif /* HAVE_VDSO_IMAGE_INFO */ static void load_elf_vdso(struct image_info *info, const VdsoImageInfo *vdso) { @@ -2311,7 +2288,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) * Load a vdso if available, which will amongst other things contain the * signal trampolines. Otherwise, allocate a separate page for them. */ - const VdsoImageInfo *vdso = vdso_image_info(info->elf_flags); + const VdsoImageInfo *vdso = get_vdso_image_info(info->elf_flags); if (vdso) { load_elf_vdso(&vdso_info, vdso); info->vdso = vdso_info.load_bias; diff --git a/linux-user/loader.h b/linux-user/loader.h index c3b8f92e23840..2175dd4e0af93 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -110,4 +110,16 @@ bool init_guest_commpage(void); struct target_elf_gregset_t; void elf_core_copy_regs(struct target_elf_gregset_t *, const CPUArchState *); +typedef struct { + const uint8_t *image; + const uint32_t *relocs; + unsigned image_size; + unsigned reloc_count; + unsigned sigreturn_ofs; + unsigned rt_sigreturn_ofs; +} VdsoImageInfo; + +/* Note that both Elf32_Word and Elf64_Word are uint32_t. */ +const VdsoImageInfo *get_vdso_image_info(uint32_t elf_flags); + #endif /* LINUX_USER_LOADER_H */ From 9f24d077f1ea76ede5968d68312840c909751b73 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 09:40:47 -1000 Subject: [PATCH 0460/1794] linux-user: Remove ELF_EXEC_PAGESIZE Use TARGET_PAGE_SIZE instead. If the target page size may vary, using a different fixed size is wrong. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index aed390ebb3e92..59e6605e36287 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -167,8 +167,6 @@ typedef abi_int target_pid_t; #define VDSO_HEADER "vdso.c.inc" -#define ELF_EXEC_PAGESIZE 4096 - #endif /* TARGET_I386 */ #ifdef TARGET_ARM @@ -180,16 +178,12 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS32 #define EXSTACK_DEFAULT true -#define ELF_EXEC_PAGESIZE 4096 - #else /* 64 bit ARM definitions */ #define ELF_ARCH EM_AARCH64 #define ELF_CLASS ELFCLASS64 -#define ELF_EXEC_PAGESIZE 4096 - #if TARGET_BIG_ENDIAN # define VDSO_HEADER "vdso-be.c.inc" #else @@ -258,8 +252,6 @@ typedef abi_int target_pid_t; NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ } while (0) -#define ELF_EXEC_PAGESIZE 4096 - #ifndef TARGET_PPC64 # define VDSO_HEADER "vdso-32.c.inc" #elif TARGET_BIG_ENDIAN @@ -280,8 +272,6 @@ typedef abi_int target_pid_t; #define VDSO_HEADER "vdso.c.inc" -#define ELF_EXEC_PAGESIZE 4096 - #endif /* TARGET_LOONGARCH64 */ #ifdef TARGET_MIPS @@ -300,8 +290,6 @@ typedef abi_int target_pid_t; #define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) #endif -#define ELF_EXEC_PAGESIZE 4096 - #endif /* TARGET_MIPS */ #ifdef TARGET_MICROBLAZE @@ -311,8 +299,6 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_MICROBLAZE -#define ELF_EXEC_PAGESIZE 4096 - #endif /* TARGET_MICROBLAZE */ #ifdef TARGET_OPENRISC @@ -321,8 +307,6 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS32 #define ELF_DATA ELFDATA2MSB -#define ELF_EXEC_PAGESIZE 8192 - #endif /* TARGET_OPENRISC */ #ifdef TARGET_SH4 @@ -330,8 +314,6 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_SH -#define ELF_EXEC_PAGESIZE 4096 - #endif #ifdef TARGET_M68K @@ -339,8 +321,6 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_68K -#define ELF_EXEC_PAGESIZE 8192 - #endif #ifdef TARGET_ALPHA @@ -348,8 +328,6 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_ALPHA -#define ELF_EXEC_PAGESIZE 8192 - #endif /* TARGET_ALPHA */ #ifdef TARGET_S390X @@ -358,8 +336,6 @@ typedef abi_int target_pid_t; #define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_S390 -#define ELF_EXEC_PAGESIZE 4096 - #define VDSO_HEADER "vdso.c.inc" #endif /* TARGET_S390X */ @@ -376,8 +352,6 @@ typedef abi_int target_pid_t; #define VDSO_HEADER "vdso-64.c.inc" #endif -#define ELF_EXEC_PAGESIZE 4096 - #endif /* TARGET_RISCV */ #ifdef TARGET_HPPA @@ -396,8 +370,6 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_XTENSA -#define ELF_EXEC_PAGESIZE 4096 - #endif /* TARGET_XTENSA */ #ifdef TARGET_HEXAGON @@ -2697,7 +2669,7 @@ static int wmr_fill_region_phdr(void *opaque, vaddr start, phdr->p_flags = (flags & PAGE_READ ? PF_R : 0) | (flags & PAGE_WRITE_ORG ? PF_W : 0) | (flags & PAGE_EXEC ? PF_X : 0); - phdr->p_align = ELF_EXEC_PAGESIZE; + phdr->p_align = TARGET_PAGE_SIZE; bswap_phdr(phdr, 1); d->phdr = phdr + 1; @@ -2805,7 +2777,7 @@ static int elf_core_dump(int signr, const CPUArchState *env) offset += size_note("CORE", sizeof(struct target_elf_prpsinfo)); offset += size_note("CORE", sizeof(struct target_elf_prstatus)) * cpus; note_size = offset - note_offset; - data_offset = ROUND_UP(offset, ELF_EXEC_PAGESIZE); + data_offset = TARGET_PAGE_ALIGN(offset); /* Do not dump if the corefile size exceeds the limit. */ if (dumpsize.rlim_cur != RLIM_INFINITY From 793ca839186df6cc9dda25121932a25c7d0ff366 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 3 Aug 2025 07:21:46 +1000 Subject: [PATCH 0461/1794] linux-user: Remove redundant ELF_DATA definitons We already provide ELF_DATA based on TARGET_BIG_ENDIAN. Remove the extra definitions from openrisc and s390x. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 59e6605e36287..8ff9f83bb8ab1 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -305,7 +305,6 @@ typedef abi_int target_pid_t; #define ELF_ARCH EM_OPENRISC #define ELF_CLASS ELFCLASS32 -#define ELF_DATA ELFDATA2MSB #endif /* TARGET_OPENRISC */ @@ -333,7 +332,6 @@ typedef abi_int target_pid_t; #ifdef TARGET_S390X #define ELF_CLASS ELFCLASS64 -#define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_S390 #define VDSO_HEADER "vdso.c.inc" From 73addb3ffc2c2252a9ffc55e56d9ed88d1dccd84 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:14:24 -1000 Subject: [PATCH 0462/1794] linux-user: Move elf parameters to {i386,x86_64}/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 39 ---------------------------------- linux-user/i386/target_elf.h | 22 +++++++++++++++++++ linux-user/x86_64/target_elf.h | 4 ++++ 3 files changed, 26 insertions(+), 39 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8ff9f83bb8ab1..73ca6c681e10f 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,45 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_I386 - -#ifdef TARGET_X86_64 -#define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_X86_64 - -#else - -/* - * This is used to ensure we don't load something for the wrong architecture. - */ -#define elf_check_arch(x) ( ((x) == EM_386) || ((x) == EM_486) ) - -/* - * These are used to set parameters in the core dumps. - */ -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_386 - -#define EXSTACK_DEFAULT true - -/* - * i386 is the only target which supplies AT_SYSINFO for the vdso. - * All others only supply AT_SYSINFO_EHDR. - */ -#define DLINFO_ARCH_ITEMS (vdso_info != NULL) -#define ARCH_DLINFO \ - do { \ - if (vdso_info) { \ - NEW_AUX_ENT(AT_SYSINFO, vdso_info->entry); \ - } \ - } while (0) - -#endif /* TARGET_X86_64 */ - -#define VDSO_HEADER "vdso.c.inc" - -#endif /* TARGET_I386 */ - #ifdef TARGET_ARM #ifndef TARGET_AARCH64 diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index f89ac0b611a63..dc58c0017ab50 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -10,6 +10,11 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_386 +#define EXSTACK_DEFAULT true +#define VDSO_HEADER "vdso.c.inc" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 @@ -22,4 +27,21 @@ typedef struct target_elf_gregset_t { struct target_user_regs_struct pt; } target_elf_gregset_t; +/* + * This is used to ensure we don't load something for the wrong architecture. + */ +#define elf_check_arch(x) ((x) == EM_386 || (x) == EM_486) + +/* + * i386 is the only target which supplies AT_SYSINFO for the vdso. + * All others only supply AT_SYSINFO_EHDR. + */ +#define DLINFO_ARCH_ITEMS (vdso_info != NULL) +#define ARCH_DLINFO \ + do { \ + if (vdso_info) { \ + NEW_AUX_ENT(AT_SYSINFO, vdso_info->entry); \ + } \ + } while (0) + #endif diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index f05b1d4dba48a..f3c09bb8da515 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -10,6 +10,10 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS64 +#define ELF_ARCH EM_X86_64 +#define VDSO_HEADER "vdso.c.inc" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 From f8eff2334a3b77d30ddae672edd3c541020cd1ca Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:17:19 -1000 Subject: [PATCH 0463/1794] linux-user: Move elf parameters to {arm,aarch64}/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/target_elf.h | 9 +++++++++ linux-user/arm/target_elf.h | 4 ++++ linux-user/elfload.c | 25 ------------------------- 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index 9eb8bb547e49f..3c9fef93785bb 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -10,6 +10,9 @@ #include "target_ptrace.h" +#define ELF_ARCH EM_AARCH64 +#define ELF_CLASS ELFCLASS64 + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_PLATFORM 1 @@ -23,4 +26,10 @@ typedef struct target_elf_gregset_t { struct target_user_pt_regs pt; } target_elf_gregset_t; +#if TARGET_BIG_ENDIAN +# define VDSO_HEADER "vdso-be.c.inc" +#else +# define VDSO_HEADER "vdso-le.c.inc" +#endif + #endif diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index 19fdfa2f2c907..d871d6d665f3c 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -10,6 +10,10 @@ #include "target_ptrace.h" +#define ELF_ARCH EM_ARM +#define ELF_CLASS ELFCLASS32 +#define EXSTACK_DEFAULT true + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_PLATFORM 1 diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 73ca6c681e10f..838d7199a6050 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,31 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_ARM - -#ifndef TARGET_AARCH64 -/* 32 bit ARM definitions */ - -#define ELF_ARCH EM_ARM -#define ELF_CLASS ELFCLASS32 -#define EXSTACK_DEFAULT true - -#else -/* 64 bit ARM definitions */ - -#define ELF_ARCH EM_AARCH64 -#define ELF_CLASS ELFCLASS64 - -#if TARGET_BIG_ENDIAN -# define VDSO_HEADER "vdso-be.c.inc" -#else -# define VDSO_HEADER "vdso-le.c.inc" -#endif - -#endif /* not TARGET_AARCH64 */ - -#endif /* TARGET_ARM */ - #ifdef TARGET_SPARC #ifndef TARGET_SPARC64 From 7b75b5e611d157ec77e4be3ac7f5ff1301380440 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:19:10 -1000 Subject: [PATCH 0464/1794] linux-user: Move elf parameters to sparc/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 15 --------------- linux-user/sparc/target_elf.h | 11 +++++++++++ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 838d7199a6050..ccdd87aa12f5d 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,21 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_SPARC - -#ifndef TARGET_SPARC64 -# define ELF_CLASS ELFCLASS32 -# define ELF_ARCH EM_SPARC -#elif defined(TARGET_ABI32) -# define ELF_CLASS ELFCLASS32 -# define elf_check_arch(x) ((x) == EM_SPARC32PLUS || (x) == EM_SPARC) -#else -# define ELF_CLASS ELFCLASS64 -# define ELF_ARCH EM_SPARCV9 -#endif - -#endif /* TARGET_SPARC */ - #ifdef TARGET_PPC #define ELF_MACHINE PPC_ELF_MACHINE diff --git a/linux-user/sparc/target_elf.h b/linux-user/sparc/target_elf.h index b7544db0a1cb0..f89c708c462a8 100644 --- a/linux-user/sparc/target_elf.h +++ b/linux-user/sparc/target_elf.h @@ -8,6 +8,17 @@ #ifndef SPARC_TARGET_ELF_H #define SPARC_TARGET_ELF_H +#ifndef TARGET_SPARC64 +# define ELF_CLASS ELFCLASS32 +# define ELF_ARCH EM_SPARC +#elif defined(TARGET_ABI32) +# define ELF_CLASS ELFCLASS32 +# define elf_check_arch(x) ((x) == EM_SPARC32PLUS || (x) == EM_SPARC) +#else +# define ELF_CLASS ELFCLASS64 +# define ELF_ARCH EM_SPARCV9 +#endif + #define HAVE_ELF_HWCAP 1 #endif From 5408b354552180e42d644dc42b8b565648fad1be Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:24:56 -1000 Subject: [PATCH 0465/1794] linux-user: Move elf parameters to ppc/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 53 ------------------------------------- linux-user/ppc/target_elf.h | 43 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 53 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index ccdd87aa12f5d..526c90e2c16eb 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,59 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_PPC - -#define ELF_MACHINE PPC_ELF_MACHINE - -#if defined(TARGET_PPC64) - -#define elf_check_arch(x) ( (x) == EM_PPC64 ) - -#define ELF_CLASS ELFCLASS64 - -#else - -#define ELF_CLASS ELFCLASS32 -#define EXSTACK_DEFAULT true - -#endif - -#define ELF_ARCH EM_PPC - -/* - * The requirements here are: - * - keep the final alignment of sp (sp & 0xf) - * - make sure the 32-bit value at the first 16 byte aligned position of - * AUXV is greater than 16 for glibc compatibility. - * AT_IGNOREPPC is used for that. - * - for compatibility with glibc ARCH_DLINFO must always be defined on PPC, - * even if DLINFO_ARCH_ITEMS goes to zero or is undefined. - */ -#define DLINFO_ARCH_ITEMS 5 -#define ARCH_DLINFO \ - do { \ - PowerPCCPU *cpu = POWERPC_CPU(thread_cpu); \ - /* \ - * Handle glibc compatibility: these magic entries must \ - * be at the lowest addresses in the final auxv. \ - */ \ - NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ - NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ - NEW_AUX_ENT(AT_DCACHEBSIZE, cpu->env.dcache_line_size); \ - NEW_AUX_ENT(AT_ICACHEBSIZE, cpu->env.icache_line_size); \ - NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ - } while (0) - -#ifndef TARGET_PPC64 -# define VDSO_HEADER "vdso-32.c.inc" -#elif TARGET_BIG_ENDIAN -# define VDSO_HEADER "vdso-64.c.inc" -#else -# define VDSO_HEADER "vdso-64le.c.inc" -#endif - -#endif - #ifdef TARGET_LOONGARCH64 #define ELF_CLASS ELFCLASS64 diff --git a/linux-user/ppc/target_elf.h b/linux-user/ppc/target_elf.h index 2a61cd2896e3d..9a47f18fb8820 100644 --- a/linux-user/ppc/target_elf.h +++ b/linux-user/ppc/target_elf.h @@ -10,6 +10,17 @@ #include "target_ptrace.h" +#define ELF_MACHINE PPC_ELF_MACHINE + +#ifdef TARGET_PPC64 +# define elf_check_arch(x) ((x) == EM_PPC64) +# define ELF_CLASS ELFCLASS64 +#else +# define ELF_CLASS ELFCLASS32 +# define EXSTACK_DEFAULT true +#endif +#define ELF_ARCH EM_PPC + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_CORE_DUMP 1 @@ -26,4 +37,36 @@ typedef struct target_elf_gregset_t { }; } target_elf_gregset_t; +#ifndef TARGET_PPC64 +# define VDSO_HEADER "vdso-32.c.inc" +#elif TARGET_BIG_ENDIAN +# define VDSO_HEADER "vdso-64.c.inc" +#else +# define VDSO_HEADER "vdso-64le.c.inc" +#endif + +/* + * The requirements here are: + * - keep the final alignment of sp (sp & 0xf) + * - make sure the 32-bit value at the first 16 byte aligned position of + * AUXV is greater than 16 for glibc compatibility. + * AT_IGNOREPPC is used for that. + * - for compatibility with glibc ARCH_DLINFO must always be defined on PPC, + * even if DLINFO_ARCH_ITEMS goes to zero or is undefined. + */ +#define DLINFO_ARCH_ITEMS 5 +#define ARCH_DLINFO \ + do { \ + PowerPCCPU *cpu = POWERPC_CPU(thread_cpu); \ + /* \ + * Handle glibc compatibility: these magic entries must \ + * be at the lowest addresses in the final auxv. \ + */ \ + NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ + NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ + NEW_AUX_ENT(AT_DCACHEBSIZE, cpu->env.dcache_line_size); \ + NEW_AUX_ENT(AT_ICACHEBSIZE, cpu->env.icache_line_size); \ + NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ + } while (0) + #endif From b38ec953747d8a56f1a6cf72cdecf9b243a55fae Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:26:37 -1000 Subject: [PATCH 0466/1794] linux-user: Move elf parameters to loongarch64/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 12 ------------ linux-user/loongarch64/target_elf.h | 6 ++++++ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 526c90e2c16eb..a4005c44eff10 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,18 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_LOONGARCH64 - -#define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_LOONGARCH -#define EXSTACK_DEFAULT true - -#define elf_check_arch(x) ((x) == EM_LOONGARCH) - -#define VDSO_HEADER "vdso.c.inc" - -#endif /* TARGET_LOONGARCH64 */ - #ifdef TARGET_MIPS #ifdef TARGET_MIPS64 diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index 1f40419af2485..47bf51a41c2a9 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -8,6 +8,12 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS64 +#define ELF_ARCH EM_LOONGARCH +#define EXSTACK_DEFAULT true +#define elf_check_arch(x) ((x) == EM_LOONGARCH) +#define VDSO_HEADER "vdso.c.inc" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 From c33d6652970edcdc6d88e1e4316569f618086a9a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:30:44 -1000 Subject: [PATCH 0467/1794] linux-user: Move elf parameters to {mips,mips64}/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 18 ------------------ linux-user/mips/target_elf.h | 4 ++++ linux-user/mips64/target_elf.h | 10 ++++++++++ 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index a4005c44eff10..a67147d43bca8 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,24 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_MIPS - -#ifdef TARGET_MIPS64 -#define ELF_CLASS ELFCLASS64 -#else -#define ELF_CLASS ELFCLASS32 -#endif -#define ELF_ARCH EM_MIPS -#define EXSTACK_DEFAULT true - -#ifdef TARGET_ABI_MIPSN32 -#define elf_check_abi(x) ((x) & EF_MIPS_ABI2) -#else -#define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) -#endif - -#endif /* TARGET_MIPS */ - #ifdef TARGET_MICROBLAZE #define elf_check_arch(x) ( (x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD) diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index a4b7fadbd698f..f400bc2fdb7ee 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -10,6 +10,10 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_MIPS +#define EXSTACK_DEFAULT true + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_BASE_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index 67bc9631346eb..c455985a76b74 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -10,6 +10,16 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS64 +#define ELF_ARCH EM_MIPS +#define EXSTACK_DEFAULT true + +#ifdef TARGET_ABI_MIPSN32 +#define elf_check_abi(x) ((x) & EF_MIPS_ABI2) +#else +#define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) +#endif + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_BASE_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 From db325955c715c4e4237b370cb65c03ca3414e60d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:38:04 -1000 Subject: [PATCH 0468/1794] linux-user: Move elf parameters to microblaze/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 9 --------- linux-user/microblaze/target_elf.h | 5 +++++ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index a67147d43bca8..6c8771d804055 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,15 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_MICROBLAZE - -#define elf_check_arch(x) ( (x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD) - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_MICROBLAZE - -#endif /* TARGET_MICROBLAZE */ - #ifdef TARGET_OPENRISC #define ELF_ARCH EM_OPENRISC diff --git a/linux-user/microblaze/target_elf.h b/linux-user/microblaze/target_elf.h index 56de77d4f374b..a622cd8e4380d 100644 --- a/linux-user/microblaze/target_elf.h +++ b/linux-user/microblaze/target_elf.h @@ -10,6 +10,11 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_MICROBLAZE + +#define elf_check_arch(x) ((x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD) + #define HAVE_ELF_CORE_DUMP 1 /* From b333696601c72d1bffc8a608c25791113875571b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:40:21 -1000 Subject: [PATCH 0469/1794] linux-user: Move elf parameters to openrisc/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 ------- linux-user/openrisc/target_elf.h | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 6c8771d804055..d0993621c1856 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,13 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_OPENRISC - -#define ELF_ARCH EM_OPENRISC -#define ELF_CLASS ELFCLASS32 - -#endif /* TARGET_OPENRISC */ - #ifdef TARGET_SH4 #define ELF_CLASS ELFCLASS32 diff --git a/linux-user/openrisc/target_elf.h b/linux-user/openrisc/target_elf.h index ad80e4b41aedc..ed9739380fd94 100644 --- a/linux-user/openrisc/target_elf.h +++ b/linux-user/openrisc/target_elf.h @@ -10,6 +10,9 @@ #include "target_ptrace.h" +#define ELF_ARCH EM_OPENRISC +#define ELF_CLASS ELFCLASS32 + #define HAVE_ELF_CORE_DUMP 1 /* From ac2a6820bf1785c7ff28bb00a1b3e66765d82d3b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:42:01 -1000 Subject: [PATCH 0470/1794] linux-user: Move elf parameters to sh4/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 ------- linux-user/sh4/target_elf.h | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index d0993621c1856..1a6e81394c057 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,13 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_SH4 - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_SH - -#endif - #ifdef TARGET_M68K #define ELF_CLASS ELFCLASS32 diff --git a/linux-user/sh4/target_elf.h b/linux-user/sh4/target_elf.h index fd3ae68a0140f..61aea237c44d9 100644 --- a/linux-user/sh4/target_elf.h +++ b/linux-user/sh4/target_elf.h @@ -10,6 +10,9 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_SH + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_CORE_DUMP 1 From 4cbf380111b7f013bcfe6bff9c4f73f00ffdefc6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:43:03 -1000 Subject: [PATCH 0471/1794] linux-user: Move elf parameters to m68k/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 ------- linux-user/m68k/target_elf.h | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 1a6e81394c057..a3757c595e0b5 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,13 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_M68K - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_68K - -#endif - #ifdef TARGET_ALPHA #define ELF_CLASS ELFCLASS64 diff --git a/linux-user/m68k/target_elf.h b/linux-user/m68k/target_elf.h index 0737412cee6cd..073c85becc919 100644 --- a/linux-user/m68k/target_elf.h +++ b/linux-user/m68k/target_elf.h @@ -8,6 +8,9 @@ #ifndef M68K_TARGET_ELF_H #define M68K_TARGET_ELF_H +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_68K + #define HAVE_ELF_CORE_DUMP 1 /* From 39955adeb2928fb41757421fb2b1b91b18bfc8de Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:53:00 -1000 Subject: [PATCH 0472/1794] linux-user: Move elf parameters to alpha/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/alpha/target_elf.h | 3 +++ linux-user/elfload.c | 7 ------- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/linux-user/alpha/target_elf.h b/linux-user/alpha/target_elf.h index 52b68680ad35b..f9d6372c9f4fa 100644 --- a/linux-user/alpha/target_elf.h +++ b/linux-user/alpha/target_elf.h @@ -8,4 +8,7 @@ #ifndef ALPHA_TARGET_ELF_H #define ALPHA_TARGET_ELF_H +#define ELF_CLASS ELFCLASS64 +#define ELF_ARCH EM_ALPHA + #endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index a3757c595e0b5..aff800baffde5 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,13 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_ALPHA - -#define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_ALPHA - -#endif /* TARGET_ALPHA */ - #ifdef TARGET_S390X #define ELF_CLASS ELFCLASS64 From 2fed144511457a2bc83f8731b8af4dc4b476bba4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:55:36 -1000 Subject: [PATCH 0473/1794] linux-user: Move elf parameters to s390x/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 9 --------- linux-user/s390x/target_elf.h | 4 ++++ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index aff800baffde5..705d726922b8a 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,15 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_S390X - -#define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_S390 - -#define VDSO_HEADER "vdso.c.inc" - -#endif /* TARGET_S390X */ - #ifdef TARGET_RISCV #define ELF_ARCH EM_RISCV diff --git a/linux-user/s390x/target_elf.h b/linux-user/s390x/target_elf.h index 670c7b3eed7ed..b23e46ab468bb 100644 --- a/linux-user/s390x/target_elf.h +++ b/linux-user/s390x/target_elf.h @@ -10,6 +10,10 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS64 +#define ELF_ARCH EM_S390 +#define VDSO_HEADER "vdso.c.inc" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_CORE_DUMP 1 From 4a56d73ae20aa893ce4f3f7d691310f9d503e7b9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:03:31 -1000 Subject: [PATCH 0474/1794] linux-user: Move elf parameters to riscv/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 14 -------------- linux-user/riscv/target_elf.h | 10 ++++++++++ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 705d726922b8a..6e476d5308e0c 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,20 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_RISCV - -#define ELF_ARCH EM_RISCV - -#ifdef TARGET_RISCV32 -#define ELF_CLASS ELFCLASS32 -#define VDSO_HEADER "vdso-32.c.inc" -#else -#define ELF_CLASS ELFCLASS64 -#define VDSO_HEADER "vdso-64.c.inc" -#endif - -#endif /* TARGET_RISCV */ - #ifdef TARGET_HPPA #define ELF_CLASS ELFCLASS32 diff --git a/linux-user/riscv/target_elf.h b/linux-user/riscv/target_elf.h index 48d9af557bfae..51b8def1d16a8 100644 --- a/linux-user/riscv/target_elf.h +++ b/linux-user/riscv/target_elf.h @@ -8,6 +8,16 @@ #ifndef RISCV_TARGET_ELF_H #define RISCV_TARGET_ELF_H +#define ELF_ARCH EM_RISCV + +#ifdef TARGET_RISCV32 +#define ELF_CLASS ELFCLASS32 +#define VDSO_HEADER "vdso-32.c.inc" +#else +#define ELF_CLASS ELFCLASS64 +#define VDSO_HEADER "vdso-64.c.inc" +#endif + #define HAVE_ELF_HWCAP 1 #endif From 964bc6c1b251e46b6d635ccbd69990227f8891df Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:11:36 -1000 Subject: [PATCH 0475/1794] linux-user: Move elf parameters to hppa/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 11 ----------- linux-user/hppa/target_elf.h | 6 ++++++ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 6e476d5308e0c..6732011332718 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,17 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_HPPA - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_PARISC -#define STACK_GROWS_DOWN 0 -#define STACK_ALIGNMENT 64 - -#define VDSO_HEADER "vdso.c.inc" - -#endif /* TARGET_HPPA */ - #ifdef TARGET_XTENSA #define ELF_CLASS ELFCLASS32 diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h index b654758afaa53..9b6363a0a7519 100644 --- a/linux-user/hppa/target_elf.h +++ b/linux-user/hppa/target_elf.h @@ -8,8 +8,14 @@ #ifndef HPPA_TARGET_ELF_H #define HPPA_TARGET_ELF_H +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_PARISC + #define HAVE_ELF_PLATFORM 1 #define LO_COMMPAGE 0 +#define STACK_GROWS_DOWN 0 +#define STACK_ALIGNMENT 64 +#define VDSO_HEADER "vdso.c.inc" #endif From 60502ab782496c160223a7892197abaad1670187 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:13:03 -1000 Subject: [PATCH 0476/1794] linux-user: Move elf parameters to xtensa/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 ------- linux-user/xtensa/target_elf.h | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 6732011332718..804a819471333 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,13 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_XTENSA - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_XTENSA - -#endif /* TARGET_XTENSA */ - #ifdef TARGET_HEXAGON #define ELF_CLASS ELFCLASS32 diff --git a/linux-user/xtensa/target_elf.h b/linux-user/xtensa/target_elf.h index 850a7206a5091..0689e79be520b 100644 --- a/linux-user/xtensa/target_elf.h +++ b/linux-user/xtensa/target_elf.h @@ -10,6 +10,9 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_XTENSA + #define HAVE_ELF_CORE_DUMP 1 /* From 14095cb5f84c0a18707e86ed7f064e2fed3bf87a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:14:14 -1000 Subject: [PATCH 0477/1794] linux-user: Move elf parameters to hexagon/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 ------- linux-user/hexagon/target_elf.h | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 804a819471333..33c4214c95a7c 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,13 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_HEXAGON - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_HEXAGON - -#endif /* TARGET_HEXAGON */ - #ifndef ELF_MACHINE #define ELF_MACHINE ELF_ARCH #endif diff --git a/linux-user/hexagon/target_elf.h b/linux-user/hexagon/target_elf.h index eccf207f6b5b2..a9f6d77fc6e7f 100644 --- a/linux-user/hexagon/target_elf.h +++ b/linux-user/hexagon/target_elf.h @@ -18,4 +18,7 @@ #ifndef HEXAGON_TARGET_ELF_H #define HEXAGON_TARGET_ELF_H +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_HEXAGON + #endif From e6b7635c9a33918c671e1f2ef4b17b5c2f279d66 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:23:31 -1000 Subject: [PATCH 0478/1794] linux-user: Standardize on ELF_MACHINE not ELF_ARCH PowerPC was the one outlier that defined both ELF_ARCH and ELF_MACHINE; ELF_ARCH was defined incorrectly, necessitating the definition of elf_check_arch. However, the elf file header field in question is called e_machine, so ELF_MACHINE is in fact the better name. Mechanically change most target/target_elf.h files, then adjust ppc/target_elf.h manually. Do not provide a default for ELF_MACHINE. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/target_elf.h | 2 +- linux-user/alpha/target_elf.h | 2 +- linux-user/arm/target_elf.h | 2 +- linux-user/elfload.c | 6 +----- linux-user/hexagon/target_elf.h | 2 +- linux-user/hppa/target_elf.h | 2 +- linux-user/i386/target_elf.h | 2 +- linux-user/loongarch64/target_elf.h | 2 +- linux-user/m68k/target_elf.h | 2 +- linux-user/microblaze/target_elf.h | 2 +- linux-user/mips/target_elf.h | 2 +- linux-user/mips64/target_elf.h | 2 +- linux-user/openrisc/target_elf.h | 2 +- linux-user/ppc/target_elf.h | 2 -- linux-user/riscv/target_elf.h | 2 +- linux-user/s390x/target_elf.h | 2 +- linux-user/sh4/target_elf.h | 2 +- linux-user/sparc/target_elf.h | 4 ++-- linux-user/x86_64/target_elf.h | 2 +- linux-user/xtensa/target_elf.h | 2 +- 20 files changed, 20 insertions(+), 26 deletions(-) diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index 3c9fef93785bb..9ec51f6237fd7 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -10,7 +10,7 @@ #include "target_ptrace.h" -#define ELF_ARCH EM_AARCH64 +#define ELF_MACHINE EM_AARCH64 #define ELF_CLASS ELFCLASS64 #define HAVE_ELF_HWCAP 1 diff --git a/linux-user/alpha/target_elf.h b/linux-user/alpha/target_elf.h index f9d6372c9f4fa..864dc6e2e6fe7 100644 --- a/linux-user/alpha/target_elf.h +++ b/linux-user/alpha/target_elf.h @@ -9,6 +9,6 @@ #define ALPHA_TARGET_ELF_H #define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_ALPHA +#define ELF_MACHINE EM_ALPHA #endif diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index d871d6d665f3c..12cdc8e5a71a0 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -10,7 +10,7 @@ #include "target_ptrace.h" -#define ELF_ARCH EM_ARM +#define ELF_MACHINE EM_ARM #define ELF_CLASS ELFCLASS32 #define EXSTACK_DEFAULT true diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 33c4214c95a7c..c4817597104d2 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,12 +130,8 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifndef ELF_MACHINE -#define ELF_MACHINE ELF_ARCH -#endif - #ifndef elf_check_arch -#define elf_check_arch(x) ((x) == ELF_ARCH) +#define elf_check_arch(x) ((x) == ELF_MACHINE) #endif #ifndef elf_check_abi diff --git a/linux-user/hexagon/target_elf.h b/linux-user/hexagon/target_elf.h index a9f6d77fc6e7f..f81ae3895a6b9 100644 --- a/linux-user/hexagon/target_elf.h +++ b/linux-user/hexagon/target_elf.h @@ -19,6 +19,6 @@ #define HEXAGON_TARGET_ELF_H #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_HEXAGON +#define ELF_MACHINE EM_HEXAGON #endif diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h index 9b6363a0a7519..76930c9369ea6 100644 --- a/linux-user/hppa/target_elf.h +++ b/linux-user/hppa/target_elf.h @@ -9,7 +9,7 @@ #define HPPA_TARGET_ELF_H #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_PARISC +#define ELF_MACHINE EM_PARISC #define HAVE_ELF_PLATFORM 1 diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index dc58c0017ab50..c3caad68b91fd 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -11,7 +11,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_386 +#define ELF_MACHINE EM_386 #define EXSTACK_DEFAULT true #define VDSO_HEADER "vdso.c.inc" diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index 47bf51a41c2a9..b988592993428 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -9,7 +9,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_LOONGARCH +#define ELF_MACHINE EM_LOONGARCH #define EXSTACK_DEFAULT true #define elf_check_arch(x) ((x) == EM_LOONGARCH) #define VDSO_HEADER "vdso.c.inc" diff --git a/linux-user/m68k/target_elf.h b/linux-user/m68k/target_elf.h index 073c85becc919..b997fa0b6d662 100644 --- a/linux-user/m68k/target_elf.h +++ b/linux-user/m68k/target_elf.h @@ -9,7 +9,7 @@ #define M68K_TARGET_ELF_H #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_68K +#define ELF_MACHINE EM_68K #define HAVE_ELF_CORE_DUMP 1 diff --git a/linux-user/microblaze/target_elf.h b/linux-user/microblaze/target_elf.h index a622cd8e4380d..1ec91ea5a9621 100644 --- a/linux-user/microblaze/target_elf.h +++ b/linux-user/microblaze/target_elf.h @@ -11,7 +11,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_MICROBLAZE +#define ELF_MACHINE EM_MICROBLAZE #define elf_check_arch(x) ((x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD) diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index f400bc2fdb7ee..157306f7a0ab4 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -11,7 +11,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_MIPS +#define ELF_MACHINE EM_MIPS #define EXSTACK_DEFAULT true #define HAVE_ELF_HWCAP 1 diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index c455985a76b74..061471a0f129f 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -11,7 +11,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_MIPS +#define ELF_MACHINE EM_MIPS #define EXSTACK_DEFAULT true #ifdef TARGET_ABI_MIPSN32 diff --git a/linux-user/openrisc/target_elf.h b/linux-user/openrisc/target_elf.h index ed9739380fd94..e8554f5339e86 100644 --- a/linux-user/openrisc/target_elf.h +++ b/linux-user/openrisc/target_elf.h @@ -10,7 +10,7 @@ #include "target_ptrace.h" -#define ELF_ARCH EM_OPENRISC +#define ELF_MACHINE EM_OPENRISC #define ELF_CLASS ELFCLASS32 #define HAVE_ELF_CORE_DUMP 1 diff --git a/linux-user/ppc/target_elf.h b/linux-user/ppc/target_elf.h index 9a47f18fb8820..22854cf52fc4e 100644 --- a/linux-user/ppc/target_elf.h +++ b/linux-user/ppc/target_elf.h @@ -13,13 +13,11 @@ #define ELF_MACHINE PPC_ELF_MACHINE #ifdef TARGET_PPC64 -# define elf_check_arch(x) ((x) == EM_PPC64) # define ELF_CLASS ELFCLASS64 #else # define ELF_CLASS ELFCLASS32 # define EXSTACK_DEFAULT true #endif -#define ELF_ARCH EM_PPC #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 diff --git a/linux-user/riscv/target_elf.h b/linux-user/riscv/target_elf.h index 51b8def1d16a8..dbbfdf54d39c7 100644 --- a/linux-user/riscv/target_elf.h +++ b/linux-user/riscv/target_elf.h @@ -8,7 +8,7 @@ #ifndef RISCV_TARGET_ELF_H #define RISCV_TARGET_ELF_H -#define ELF_ARCH EM_RISCV +#define ELF_MACHINE EM_RISCV #ifdef TARGET_RISCV32 #define ELF_CLASS ELFCLASS32 diff --git a/linux-user/s390x/target_elf.h b/linux-user/s390x/target_elf.h index b23e46ab468bb..ef5edbd86099f 100644 --- a/linux-user/s390x/target_elf.h +++ b/linux-user/s390x/target_elf.h @@ -11,7 +11,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_S390 +#define ELF_MACHINE EM_S390 #define VDSO_HEADER "vdso.c.inc" #define HAVE_ELF_HWCAP 1 diff --git a/linux-user/sh4/target_elf.h b/linux-user/sh4/target_elf.h index 61aea237c44d9..d9e253d4250e0 100644 --- a/linux-user/sh4/target_elf.h +++ b/linux-user/sh4/target_elf.h @@ -11,7 +11,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_SH +#define ELF_MACHINE EM_SH #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_CORE_DUMP 1 diff --git a/linux-user/sparc/target_elf.h b/linux-user/sparc/target_elf.h index f89c708c462a8..6b0cac3caf09d 100644 --- a/linux-user/sparc/target_elf.h +++ b/linux-user/sparc/target_elf.h @@ -10,13 +10,13 @@ #ifndef TARGET_SPARC64 # define ELF_CLASS ELFCLASS32 -# define ELF_ARCH EM_SPARC +# define ELF_MACHINE EM_SPARC #elif defined(TARGET_ABI32) # define ELF_CLASS ELFCLASS32 # define elf_check_arch(x) ((x) == EM_SPARC32PLUS || (x) == EM_SPARC) #else # define ELF_CLASS ELFCLASS64 -# define ELF_ARCH EM_SPARCV9 +# define ELF_MACHINE EM_SPARCV9 #endif #define HAVE_ELF_HWCAP 1 diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index f3c09bb8da515..840bddf5ec60f 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -11,7 +11,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_X86_64 +#define ELF_MACHINE EM_X86_64 #define VDSO_HEADER "vdso.c.inc" #define HAVE_ELF_HWCAP 1 diff --git a/linux-user/xtensa/target_elf.h b/linux-user/xtensa/target_elf.h index 0689e79be520b..1bf8f2a14a4da 100644 --- a/linux-user/xtensa/target_elf.h +++ b/linux-user/xtensa/target_elf.h @@ -11,7 +11,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_XTENSA +#define ELF_MACHINE EM_XTENSA #define HAVE_ELF_CORE_DUMP 1 From eb727cc45a352ff259c4c2d85456f0412e738d43 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:26:43 -1000 Subject: [PATCH 0479/1794] linux-user: Rename elf_check_arch Rename to elf_check_machine to match ELF_MACHINE. Remove the unnecessary definition for loongarch64. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 6 +++--- linux-user/i386/target_elf.h | 2 +- linux-user/loongarch64/target_elf.h | 1 - linux-user/microblaze/target_elf.h | 2 +- linux-user/sparc/target_elf.h | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index c4817597104d2..aa0eed6dea0e8 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,8 +130,8 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifndef elf_check_arch -#define elf_check_arch(x) ((x) == ELF_MACHINE) +#ifndef elf_check_machine +#define elf_check_machine(x) ((x) == ELF_MACHINE) #endif #ifndef elf_check_abi @@ -346,7 +346,7 @@ static bool elf_check_ident(struct elfhdr *ehdr) This has to wait until after bswapping the header. */ static bool elf_check_ehdr(struct elfhdr *ehdr) { - return (elf_check_arch(ehdr->e_machine) + return (elf_check_machine(ehdr->e_machine) && elf_check_abi(ehdr->e_flags) && ehdr->e_ehsize == sizeof(struct elfhdr) && ehdr->e_phentsize == sizeof(struct elf_phdr) diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index c3caad68b91fd..eafac8f382179 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -30,7 +30,7 @@ typedef struct target_elf_gregset_t { /* * This is used to ensure we don't load something for the wrong architecture. */ -#define elf_check_arch(x) ((x) == EM_386 || (x) == EM_486) +#define elf_check_machine(x) ((x) == EM_386 || (x) == EM_486) /* * i386 is the only target which supplies AT_SYSINFO for the vdso. diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index b988592993428..3aa8c83958df3 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -11,7 +11,6 @@ #define ELF_CLASS ELFCLASS64 #define ELF_MACHINE EM_LOONGARCH #define EXSTACK_DEFAULT true -#define elf_check_arch(x) ((x) == EM_LOONGARCH) #define VDSO_HEADER "vdso.c.inc" #define HAVE_ELF_HWCAP 1 diff --git a/linux-user/microblaze/target_elf.h b/linux-user/microblaze/target_elf.h index 1ec91ea5a9621..7b3ef70d23288 100644 --- a/linux-user/microblaze/target_elf.h +++ b/linux-user/microblaze/target_elf.h @@ -13,7 +13,7 @@ #define ELF_CLASS ELFCLASS32 #define ELF_MACHINE EM_MICROBLAZE -#define elf_check_arch(x) ((x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD) +#define elf_check_machine(x) ((x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD) #define HAVE_ELF_CORE_DUMP 1 diff --git a/linux-user/sparc/target_elf.h b/linux-user/sparc/target_elf.h index 6b0cac3caf09d..7827767bcb2b4 100644 --- a/linux-user/sparc/target_elf.h +++ b/linux-user/sparc/target_elf.h @@ -13,7 +13,7 @@ # define ELF_MACHINE EM_SPARC #elif defined(TARGET_ABI32) # define ELF_CLASS ELFCLASS32 -# define elf_check_arch(x) ((x) == EM_SPARC32PLUS || (x) == EM_SPARC) +# define elf_check_machine(x) ((x) == EM_SPARC32PLUS || (x) == EM_SPARC) #else # define ELF_CLASS ELFCLASS64 # define ELF_MACHINE EM_SPARCV9 From 5dcc0b62059e2330f2746fa691ac6791e9a14cc3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:30:26 -1000 Subject: [PATCH 0480/1794] linux-user: Remove ELIBBAD from elfload.c The last use of this fallback was removed in 8e62a71738bc. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index aa0eed6dea0e8..c0326928d45f8 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -110,11 +110,6 @@ int info_is_fdpic(struct image_info *info) #define MAP_DENYWRITE 0 #endif -/* should probably go in elf.h */ -#ifndef ELIBBAD -#define ELIBBAD 80 -#endif - #if TARGET_BIG_ENDIAN #define ELF_DATA ELFDATA2MSB #else From dd1d0239a79c4e7947c901fcb1f232ecc0428a96 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:33:58 -1000 Subject: [PATCH 0481/1794] linux-user: Remove MAP_DENYWRITE from elfload.c The last use of this fallback was removed in bf858897b769. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index c0326928d45f8..8b92fba0f0abd 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -105,11 +105,6 @@ int info_is_fdpic(struct image_info *info) return info->personality == PER_LINUX_FDPIC; } -/* this flag is uneffective under linux too, should be deleted */ -#ifndef MAP_DENYWRITE -#define MAP_DENYWRITE 0 -#endif - #if TARGET_BIG_ENDIAN #define ELF_DATA ELFDATA2MSB #else From 8218eb6f52abd08c9e89baafdddec6f0d9234768 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:43:27 -1000 Subject: [PATCH 0482/1794] linux-user: Move arch_parse_elf_property to aarch64/elfload.c Rename the controlling macro to HAVE_ELF_GNU_PROPERTY to match the other HAVE_* macros. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/elfload.c | 18 +++++++++++++++ linux-user/aarch64/target_elf.h | 1 + linux-user/elfload.c | 39 +++++++-------------------------- linux-user/loader.h | 5 +++++ 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 07a0c3f844145..8076968251f73 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -1,10 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu.h" #include "loader.h" #include "target/arm/cpu-features.h" #include "target_elf.h" +#include "elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -349,6 +351,22 @@ const char *get_elf_platform(CPUState *cs) return TARGET_BIG_ENDIAN ? "aarch64_be" : "aarch64"; } +bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, + const uint32_t *data, + struct image_info *info, + Error **errp) +{ + if (pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) { + if (pr_datasz != sizeof(uint32_t)) { + error_setg(errp, "Ill-formed GNU_PROPERTY_AARCH64_FEATURE_1_AND"); + return false; + } + /* We will extract GNU_PROPERTY_AARCH64_FEATURE_1_BTI later. */ + info->note_flags = *data; + } + return true; +} + void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) { for (int i = 0; i < 31; i++) { diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index 9ec51f6237fd7..4cdeb64b0d4e1 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -17,6 +17,7 @@ #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 +#define HAVE_ELF_GNU_PROPERTY 1 /* * See linux kernel: arch/arm64/include/asm/elf.h, where diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8b92fba0f0abd..12d4873212e7b 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -166,41 +166,18 @@ const char *get_elf_platform(CPUState *cs) { return NULL; } const char *get_elf_base_platform(CPUState *cs) { return NULL; } #endif -#include "elf.h" - -/* We must delay the following stanzas until after "elf.h". */ -#if defined(TARGET_AARCH64) - -static bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, - const uint32_t *data, - struct image_info *info, - Error **errp) -{ - if (pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) { - if (pr_datasz != sizeof(uint32_t)) { - error_setg(errp, "Ill-formed GNU_PROPERTY_AARCH64_FEATURE_1_AND"); - return false; - } - /* We will extract GNU_PROPERTY_AARCH64_FEATURE_1_BTI later. */ - info->note_flags = *data; - } - return true; -} -#define ARCH_USE_GNU_PROPERTY 1 - -#else - -static bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, - const uint32_t *data, - struct image_info *info, - Error **errp) +#ifndef HAVE_ELF_GNU_PROPERTY +bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, + const uint32_t *data, struct image_info *info, + Error **errp) { g_assert_not_reached(); } -#define ARCH_USE_GNU_PROPERTY 0 - +#define HAVE_ELF_GNU_PROPERTY 0 #endif +#include "elf.h" + struct exec { unsigned int a_info; /* Use macros N_MAGIC, etc for access */ @@ -1233,7 +1210,7 @@ static bool parse_elf_properties(const ImageSource *src, uint32_t prev_type; /* Unless the arch requires properties, ignore them. */ - if (!ARCH_USE_GNU_PROPERTY) { + if (!HAVE_ELF_GNU_PROPERTY) { return true; } diff --git a/linux-user/loader.h b/linux-user/loader.h index 2175dd4e0af93..e42b8fa1e30f3 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -122,4 +122,9 @@ typedef struct { /* Note that both Elf32_Word and Elf64_Word are uint32_t. */ const VdsoImageInfo *get_vdso_image_info(uint32_t elf_flags); +bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, + const uint32_t *data, + struct image_info *info, + Error **errp); + #endif /* LINUX_USER_LOADER_H */ From ab2d261ab3e73f622d75c5c6cc06cc0b7db2d79a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:45:13 -1000 Subject: [PATCH 0483/1794] linux-user: Remove a.out declarations from elfload.c These should have been removed with the rest of the stub a.out support in b9329d4b5321, though they were not in use even then. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 12d4873212e7b..26c090c95d3e9 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -178,25 +178,6 @@ bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, #include "elf.h" -struct exec -{ - unsigned int a_info; /* Use macros N_MAGIC, etc for access */ - unsigned int a_text; /* length of text, in bytes */ - unsigned int a_data; /* length of data, in bytes */ - unsigned int a_bss; /* length of uninitialized data area, in bytes */ - unsigned int a_syms; /* length of symbol table data in file, in bytes */ - unsigned int a_entry; /* start address */ - unsigned int a_trsize; /* length of relocation info for text, in bytes */ - unsigned int a_drsize; /* length of relocation info for data, in bytes */ -}; - - -#define N_MAGIC(exec) ((exec).a_info & 0xffff) -#define OMAGIC 0407 -#define NMAGIC 0410 -#define ZMAGIC 0413 -#define QMAGIC 0314 - #define DLINFO_ITEMS 16 static inline void memcpy_fromfs(void * to, const void * from, unsigned long n) From 8bb7873f28e82944ab6fb2c633c09877fd4eeda8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 21:51:46 +1000 Subject: [PATCH 0484/1794] linux-user/sparc: Create target_ptrace.h Move target_pt_regs to target_ptrace.h. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/sparc/signal.c | 2 ++ linux-user/sparc/target_ptrace.h | 24 ++++++++++++++++++++++++ linux-user/sparc/target_syscall.h | 19 ------------------- 3 files changed, 26 insertions(+), 19 deletions(-) create mode 100644 linux-user/sparc/target_ptrace.h diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index 8181b8b92c1a5..d339f89928879 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -21,6 +21,8 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "target_ptrace.h" + /* A Sparc register window */ struct target_reg_window { diff --git a/linux-user/sparc/target_ptrace.h b/linux-user/sparc/target_ptrace.h new file mode 100644 index 0000000000000..a4d5416c1f971 --- /dev/null +++ b/linux-user/sparc/target_ptrace.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef SPARC_TARGET_PTRACE_H +#define SPARC_TARGET_PTRACE_H + +/* See arch/sparc/include/uapi/asm/ptrace.h. */ +struct target_pt_regs { +#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) + abi_ulong u_regs[16]; + abi_ulong tstate; + abi_ulong pc; + abi_ulong npc; + uint32_t y; + uint32_t magic; +#else + abi_ulong psr; + abi_ulong pc; + abi_ulong npc; + abi_ulong y; + abi_ulong u_regs[16]; +#endif +}; + +#endif /* SPARC_TARGET_PTRACE_H */ diff --git a/linux-user/sparc/target_syscall.h b/linux-user/sparc/target_syscall.h index e4211653574ed..a90ed2983a5a8 100644 --- a/linux-user/sparc/target_syscall.h +++ b/linux-user/sparc/target_syscall.h @@ -1,25 +1,6 @@ #ifndef SPARC_TARGET_SYSCALL_H #define SPARC_TARGET_SYSCALL_H -#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) -struct target_pt_regs { - abi_ulong u_regs[16]; - abi_ulong tstate; - abi_ulong pc; - abi_ulong npc; - uint32_t y; - uint32_t magic; -}; -#else -struct target_pt_regs { - abi_ulong psr; - abi_ulong pc; - abi_ulong npc; - abi_ulong y; - abi_ulong u_regs[16]; -}; -#endif - #ifdef TARGET_SPARC64 # define UNAME_MACHINE "sparc64" #else From 3f1b9dbdf5452a2baab00d46bd149f6f8192fe44 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 21:55:01 +1000 Subject: [PATCH 0485/1794] linux-user: Remove target_pt_regs from target_syscall.h All target_pt_regs which have not been broken out to target_ptrace.h by this point are unused. Remove them. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/alpha/target_syscall.h | 40 ----------------------------- linux-user/hexagon/target_syscall.h | 5 ---- linux-user/hppa/target_syscall.h | 18 ------------- linux-user/m68k/target_syscall.h | 16 ------------ linux-user/riscv/target_syscall.h | 35 ------------------------- 5 files changed, 114 deletions(-) diff --git a/linux-user/alpha/target_syscall.h b/linux-user/alpha/target_syscall.h index fda3a49f29be9..53706b749fc0c 100644 --- a/linux-user/alpha/target_syscall.h +++ b/linux-user/alpha/target_syscall.h @@ -1,46 +1,6 @@ #ifndef ALPHA_TARGET_SYSCALL_H #define ALPHA_TARGET_SYSCALL_H -/* default linux values for the selectors */ -#define __USER_DS (1) - -struct target_pt_regs { - abi_ulong r0; - abi_ulong r1; - abi_ulong r2; - abi_ulong r3; - abi_ulong r4; - abi_ulong r5; - abi_ulong r6; - abi_ulong r7; - abi_ulong r8; - abi_ulong r19; - abi_ulong r20; - abi_ulong r21; - abi_ulong r22; - abi_ulong r23; - abi_ulong r24; - abi_ulong r25; - abi_ulong r26; - abi_ulong r27; - abi_ulong r28; - abi_ulong hae; -/* JRP - These are the values provided to a0-a2 by PALcode */ - abi_ulong trap_a0; - abi_ulong trap_a1; - abi_ulong trap_a2; -/* These are saved by PAL-code: */ - abi_ulong ps; - abi_ulong pc; - abi_ulong gp; - abi_ulong r16; - abi_ulong r17; - abi_ulong r18; -/* Those is needed by qemu to temporary store the user stack pointer */ - abi_ulong usp; - abi_ulong unique; -}; - #define UNAME_MACHINE "alpha" #define UNAME_MINIMUM_RELEASE "2.6.32" diff --git a/linux-user/hexagon/target_syscall.h b/linux-user/hexagon/target_syscall.h index 7f91a4abc77a5..d9c94737a5c86 100644 --- a/linux-user/hexagon/target_syscall.h +++ b/linux-user/hexagon/target_syscall.h @@ -18,11 +18,6 @@ #ifndef HEXAGON_TARGET_SYSCALL_H #define HEXAGON_TARGET_SYSCALL_H -struct target_pt_regs { - abi_long sepc; - abi_long sp; -}; - #define UNAME_MACHINE "hexagon" #define UNAME_MINIMUM_RELEASE "4.15.0" diff --git a/linux-user/hppa/target_syscall.h b/linux-user/hppa/target_syscall.h index 9a8f8ca628175..4b21e85371330 100644 --- a/linux-user/hppa/target_syscall.h +++ b/linux-user/hppa/target_syscall.h @@ -1,24 +1,6 @@ #ifndef HPPA_TARGET_SYSCALL_H #define HPPA_TARGET_SYSCALL_H -struct target_pt_regs { - target_ulong gr[32]; - uint64_t fr[32]; - target_ulong sr[8]; - target_ulong iasq[2]; - target_ulong iaoq[2]; - target_ulong cr27; - target_ulong __pad0; - target_ulong orig_r28; - target_ulong ksp; - target_ulong kpc; - target_ulong sar; - target_ulong iir; - target_ulong isr; - target_ulong ior; - target_ulong ipsw; -}; - #define UNAME_MACHINE "parisc" #define UNAME_MINIMUM_RELEASE "2.6.32" #define TARGET_CLONE_BACKWARDS diff --git a/linux-user/m68k/target_syscall.h b/linux-user/m68k/target_syscall.h index 8d4ddbd76c8f4..3ca0231c70951 100644 --- a/linux-user/m68k/target_syscall.h +++ b/linux-user/m68k/target_syscall.h @@ -1,22 +1,6 @@ #ifndef M68K_TARGET_SYSCALL_H #define M68K_TARGET_SYSCALL_H -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -struct target_pt_regs { - abi_long d1, d2, d3, d4, d5, d6, d7; - abi_long a0, a1, a2, a3, a4, a5, a6; - abi_ulong d0; - abi_ulong usp; - abi_ulong orig_d0; - int16_t stkadj; - uint16_t sr; - abi_ulong pc; - uint16_t fntvex; - uint16_t __fill; -}; - #define UNAME_MACHINE "m68k" #define UNAME_MINIMUM_RELEASE "2.6.32" diff --git a/linux-user/riscv/target_syscall.h b/linux-user/riscv/target_syscall.h index 7601f10c28e72..69a7b753eb467 100644 --- a/linux-user/riscv/target_syscall.h +++ b/linux-user/riscv/target_syscall.h @@ -8,41 +8,6 @@ #ifndef LINUX_USER_RISCV_TARGET_SYSCALL_H #define LINUX_USER_RISCV_TARGET_SYSCALL_H -struct target_pt_regs { - abi_long sepc; - abi_long ra; - abi_long sp; - abi_long gp; - abi_long tp; - abi_long t0; - abi_long t1; - abi_long t2; - abi_long s0; - abi_long s1; - abi_long a0; - abi_long a1; - abi_long a2; - abi_long a3; - abi_long a4; - abi_long a5; - abi_long a6; - abi_long a7; - abi_long s2; - abi_long s3; - abi_long s4; - abi_long s5; - abi_long s6; - abi_long s7; - abi_long s8; - abi_long s9; - abi_long s10; - abi_long s11; - abi_long t3; - abi_long t4; - abi_long t5; - abi_long t6; -}; - #ifdef TARGET_RISCV32 #define UNAME_MACHINE "riscv32" #define UNAME_MINIMUM_RELEASE "5.4.0" From a362960b29749750168601f3969ec9364082ee59 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Tue, 26 Aug 2025 11:21:27 +0100 Subject: [PATCH 0486/1794] target/arm: Clean up of register field definitions Clean up the definitions of NSW and NSA fields in the VTCR register. These two fields are already defined properly using FIELD() so they are actually duplications. Also, define the NSW and NSA fields in the VSTCR register using FIELD() and remove their definitions based on VTCR fields. Signed-off-by: Gustavo Romero Message-id: 20250725014755.2122579-1-gustavo.romero@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/internals.h | 8 +++----- target/arm/ptw.c | 8 ++++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index 1b3d0244fd6ee..3f86b070447ca 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -113,11 +113,6 @@ FIELD(DBGWCR, WT, 20, 1) FIELD(DBGWCR, MASK, 24, 5) FIELD(DBGWCR, SSCE, 29, 1) -#define VTCR_NSW (1u << 29) -#define VTCR_NSA (1u << 30) -#define VSTCR_SW VTCR_NSW -#define VSTCR_SA VTCR_NSA - /* Bit definitions for CPACR (AArch32 only) */ FIELD(CPACR, CP10, 20, 2) FIELD(CPACR, CP11, 22, 2) @@ -220,6 +215,9 @@ FIELD(VTCR, NSA, 30, 1) FIELD(VTCR, DS, 32, 1) FIELD(VTCR, SL2, 33, 1) +FIELD(VSTCR, SW, 29, 1) +FIELD(VSTCR, SA, 30, 1) + #define HCRX_ENAS0 (1ULL << 0) #define HCRX_ENALS (1ULL << 1) #define HCRX_ENASR (1ULL << 2) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 561bf2678e5ac..ed5c728eab6fa 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -193,9 +193,9 @@ static ARMMMUIdx ptw_idx_for_stage_2(CPUARMState *env, ARMMMUIdx stage2idx) return ARMMMUIdx_Phys_Realm; case ARMSS_Secure: if (stage2idx == ARMMMUIdx_Stage2_S) { - s2walk_secure = !(env->cp15.vstcr_el2 & VSTCR_SW); + s2walk_secure = !(env->cp15.vstcr_el2 & R_VSTCR_SW_MASK); } else { - s2walk_secure = !(env->cp15.vtcr_el2 & VTCR_NSW); + s2walk_secure = !(env->cp15.vtcr_el2 & R_VTCR_NSW_MASK); } return s2walk_secure ? ARMMMUIdx_Phys_S : ARMMMUIdx_Phys_NS; default: @@ -3372,9 +3372,9 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, */ if (in_space == ARMSS_Secure) { result->f.attrs.secure = - !(env->cp15.vstcr_el2 & (VSTCR_SA | VSTCR_SW)) + !(env->cp15.vstcr_el2 & (R_VSTCR_SA_MASK | R_VSTCR_SW_MASK)) && (ipa_secure - || !(env->cp15.vtcr_el2 & (VTCR_NSA | VTCR_NSW))); + || !(env->cp15.vtcr_el2 & (R_VTCR_NSA_MASK | R_VTCR_NSW_MASK))); result->f.attrs.space = arm_secure_to_space(result->f.attrs.secure); } From 2561626c25e05bc6f673b29f7dd46a20fa6719c3 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 26 Aug 2025 11:21:27 +0100 Subject: [PATCH 0487/1794] tests/functional/test_aarch64_device_passthrough: update image TF-A needs to be patched to enable support for FEAT_TCR2 and FEAT_SCTLR2. This new image contains updated firmware. Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250727074202.83141-2-richard.henderson@linaro.org Message-ID: <20250719035838.2284029-2-pierrick.bouvier@linaro.org> Signed-off-by: Peter Maydell --- .../aarch64/test_device_passthrough.py | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/functional/aarch64/test_device_passthrough.py b/tests/functional/aarch64/test_device_passthrough.py index 1f3f158a9fff4..17437784bbe14 100755 --- a/tests/functional/aarch64/test_device_passthrough.py +++ b/tests/functional/aarch64/test_device_passthrough.py @@ -9,7 +9,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os +from os.path import join from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command, wait_for_console_pattern @@ -77,15 +77,16 @@ class Aarch64DevicePassthrough(QemuSystemTest): - # https://github.com/pbo-linaro/qemu-linux-stack + # https://github.com/pbo-linaro/qemu-linux-stack/tree/device_passthrough + # $ ./build.sh && ./archive_artifacts.sh out.tar.xz # # Linux kernel is compiled with defconfig + # IOMMUFD + VFIO_DEVICE_CDEV + ARM_SMMU_V3_IOMMUFD # https://docs.kernel.org/driver-api/vfio.html#vfio-device-cde ASSET_DEVICE_PASSTHROUGH_STACK = Asset( - ('https://fileserver.linaro.org/s/fx5DXxBYme8dw2G/' - 'download/device_passthrough.tar.xz'), - '812750b664d61c2986f2b149939ae28cafbd60d53e9c7e4b16e97143845e196d') + ('https://github.com/pbo-linaro/qemu-linux-stack/' + 'releases/download/build/device_passthrough-c3fb84a.tar.xz'), + '15ac2b02bed0c0ea8e3e007de0bcfdaf6fd51c1ba98213f841dc7d01d6f72f04') # This tests the device passthrough implementation, by booting a VM # supporting it with two nvme disks attached, and launching a nested VM @@ -96,16 +97,16 @@ def test_aarch64_device_passthrough(self): self.vm.set_console() - stack_path_tar_gz = self.ASSET_DEVICE_PASSTHROUGH_STACK.fetch() - self.archive_extract(stack_path_tar_gz, format="tar") + stack_path_tar = self.ASSET_DEVICE_PASSTHROUGH_STACK.fetch() + self.archive_extract(stack_path_tar, format="tar") stack = self.scratch_file('out') - kernel = os.path.join(stack, 'Image.gz') - rootfs_host = os.path.join(stack, 'host.ext4') - disk_vfio = os.path.join(stack, 'disk_vfio') - disk_iommufd = os.path.join(stack, 'disk_iommufd') - guest_cmd = os.path.join(stack, 'guest.sh') - nested_guest_cmd = os.path.join(stack, 'nested_guest.sh') + kernel = join(stack, 'Image.gz') + rootfs_host = join(stack, 'host.ext4') + disk_vfio = join(stack, 'disk_vfio') + disk_iommufd = join(stack, 'disk_iommufd') + guest_cmd = join(stack, 'guest.sh') + nested_guest_cmd = join(stack, 'nested_guest.sh') # we generate two random disks with open(disk_vfio, "wb") as d: d.write(randbytes(512)) with open(disk_iommufd, "wb") as d: d.write(randbytes(1024)) From 84249d026bc3878c196ecd3e8558609ba9260eb6 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 26 Aug 2025 11:21:27 +0100 Subject: [PATCH 0488/1794] tests/functional/test_aarch64_rme: update image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TF-A needs to be patched to enable support for FEAT_TCR2 and FEAT_SCTLR2. This new image contains updated firmware. Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20250727074202.83141-3-richard.henderson@linaro.org Message-ID: <20250719035838.2284029-3-pierrick.bouvier@linaro.org> [PMM: switch to os.makedirs(..., exist_ok=True) to improve robustness when re-run after test was cancelled midway] Signed-off-by: Peter Maydell --- tests/functional/aarch64/test_rme_sbsaref.py | 64 ++++++++------- tests/functional/aarch64/test_rme_virt.py | 85 +++++++------------- 2 files changed, 66 insertions(+), 83 deletions(-) diff --git a/tests/functional/aarch64/test_rme_sbsaref.py b/tests/functional/aarch64/test_rme_sbsaref.py index 100f1c7738bcf..ca892e0a8c9d4 100755 --- a/tests/functional/aarch64/test_rme_sbsaref.py +++ b/tests/functional/aarch64/test_rme_sbsaref.py @@ -10,21 +10,23 @@ # SPDX-License-Identifier: GPL-2.0-or-later import os +from os.path import join +import shutil from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern from qemu_test import exec_command_and_wait_for_pattern -from test_rme_virt import test_realms_guest class Aarch64RMESbsaRefMachine(QemuSystemTest): - # Stack is built with OP-TEE build environment from those instructions: + # Stack is inspired from: # https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/ - # https://github.com/pbo-linaro/qemu-rme-stack + # https://github.com/pbo-linaro/qemu-linux-stack/tree/rme_sbsa_release + # ./build.sh && ./archive_artifacts.sh out.tar.xz ASSET_RME_STACK_SBSA = Asset( - ('https://fileserver.linaro.org/s/KJyeBxL82mz2r7F/' - 'download/rme-stack-op-tee-4.2.0-cca-v4-sbsa.tar.gz'), - 'dd9ab28ec869bdf3b5376116cb3689103b43433fd5c4bca0f4a8d8b3c104999e') + ('https://github.com/pbo-linaro/qemu-linux-stack/' + 'releases/download/build/rme_sbsa_release-a7f02cf.tar.xz'), + '27d8400b11befb828d6db0cab97e7ae102d0992c928d3dfbf38b24b6cf6c324c') # This tests the FEAT_RME cpu implementation, by booting a VM supporting it, # and launching a nested VM using it. @@ -35,35 +37,41 @@ def test_aarch64_rme_sbsaref(self): self.vm.set_console() - stack_path_tar_gz = self.ASSET_RME_STACK_SBSA.fetch() - self.archive_extract(stack_path_tar_gz, format="tar") + stack_path_tar = self.ASSET_RME_STACK_SBSA.fetch() + self.archive_extract(stack_path_tar, format="tar") - rme_stack = self.scratch_file('rme-stack-op-tee-4.2.0-cca-v4-sbsa') - pflash0 = os.path.join(rme_stack, 'images', 'SBSA_FLASH0.fd') - pflash1 = os.path.join(rme_stack, 'images', 'SBSA_FLASH1.fd') - virtual = os.path.join(rme_stack, 'images', 'disks', 'virtual') - drive = os.path.join(rme_stack, 'out-br', 'images', 'rootfs.ext4') + rme_stack = self.scratch_file('.') + pflash0 = join(rme_stack, 'out', 'SBSA_FLASH0.fd') + pflash1 = join(rme_stack, 'out', 'SBSA_FLASH1.fd') + rootfs = join(rme_stack, 'out', 'host.ext4') - self.vm.add_args('-cpu', 'max,x-rme=on,pauth-impdef=on') + efi = join(rme_stack, 'out', 'EFI') + os.makedirs(efi, exist_ok=True) + shutil.copyfile(join(rme_stack, 'out', 'Image'), join(efi, 'Image')) + with open(join(efi, 'startup.nsh'), 'w') as startup: + startup.write('fs0:Image nokaslr root=/dev/vda rw init=/init --' + ' /host/out/lkvm run --realm' + ' -m 256m' + ' --restricted_mem' + ' --kernel /host/out/Image' + ' --disk /host/out/guest.ext4' + ' --params "root=/dev/vda rw init=/init"') + + self.vm.add_args('-cpu', 'max,x-rme=on') + self.vm.add_args('-smp', '2') self.vm.add_args('-m', '2G') self.vm.add_args('-M', 'sbsa-ref') self.vm.add_args('-drive', f'file={pflash0},format=raw,if=pflash') self.vm.add_args('-drive', f'file={pflash1},format=raw,if=pflash') - self.vm.add_args('-drive', f'file=fat:rw:{virtual},format=raw') - self.vm.add_args('-drive', f'format=raw,if=none,file={drive},id=hd0') - self.vm.add_args('-device', 'virtio-blk-pci,drive=hd0') - self.vm.add_args('-device', 'virtio-9p-pci,fsdev=shr0,mount_tag=shr0') - self.vm.add_args('-fsdev', f'local,security_model=none,path={rme_stack},id=shr0') - self.vm.add_args('-device', 'virtio-net-pci,netdev=net0') - self.vm.add_args('-netdev', 'user,id=net0') - + self.vm.add_args('-drive', f'file=fat:rw:{efi},format=raw') + self.vm.add_args('-drive', f'format=raw,file={rootfs},if=virtio') + self.vm.add_args('-virtfs', + f'local,path={rme_stack}/,mount_tag=host,' + 'security_model=mapped,readonly=off') self.vm.launch() - # Wait for host VM boot to complete. - wait_for_console_pattern(self, 'Welcome to Buildroot', - failure_message='Synchronous Exception at') - exec_command_and_wait_for_pattern(self, 'root', '#') - - test_realms_guest(self) + # Wait for host and guest VM boot to complete. + wait_for_console_pattern(self, 'root@guest', + failure_message='Kernel panic') if __name__ == '__main__': QemuSystemTest.main() diff --git a/tests/functional/aarch64/test_rme_virt.py b/tests/functional/aarch64/test_rme_virt.py index 8452d27928fbd..bb603aaa26c6f 100755 --- a/tests/functional/aarch64/test_rme_virt.py +++ b/tests/functional/aarch64/test_rme_virt.py @@ -9,50 +9,22 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os +from os.path import join from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command, wait_for_console_pattern from qemu_test import exec_command_and_wait_for_pattern -def test_realms_guest(test_rme_instance): - - # Boot the (nested) guest VM - exec_command(test_rme_instance, - 'qemu-system-aarch64 -M virt,gic-version=3 ' - '-cpu host -enable-kvm -m 512M ' - '-M confidential-guest-support=rme0 ' - '-object rme-guest,id=rme0 ' - '-device virtio-net-pci,netdev=net0,romfile= ' - '-netdev user,id=net0 ' - '-kernel /mnt/out/bin/Image ' - '-initrd /mnt/out-br/images/rootfs.cpio ' - '-serial stdio') - # Detect Realm activation during (nested) guest boot. - wait_for_console_pattern(test_rme_instance, - 'SMC_RMI_REALM_ACTIVATE') - # Wait for (nested) guest boot to complete. - wait_for_console_pattern(test_rme_instance, - 'Welcome to Buildroot') - exec_command_and_wait_for_pattern(test_rme_instance, 'root', '#') - # query (nested) guest cca report - exec_command(test_rme_instance, 'cca-workload-attestation report') - wait_for_console_pattern(test_rme_instance, - '"cca-platform-hash-algo-id": "sha-256"') - wait_for_console_pattern(test_rme_instance, - '"cca-realm-hash-algo-id": "sha-512"') - wait_for_console_pattern(test_rme_instance, - '"cca-realm-public-key-hash-algo-id": "sha-256"') - class Aarch64RMEVirtMachine(QemuSystemTest): - # Stack is built with OP-TEE build environment from those instructions: + # Stack is inspired from: # https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/ - # https://github.com/pbo-linaro/qemu-rme-stack + # https://github.com/pbo-linaro/qemu-linux-stack/tree/rme_release + # ./build.sh && ./archive_artifacts.sh out.tar.xz ASSET_RME_STACK_VIRT = Asset( - ('https://fileserver.linaro.org/s/iaRsNDJp2CXHMSJ/' - 'download/rme-stack-op-tee-4.2.0-cca-v4-qemu_v8.tar.gz'), - '1851adc232b094384d8b879b9a2cfff07ef3d6205032b85e9b3a4a9ae6b0b7ad') + ('https://github.com/pbo-linaro/qemu-linux-stack/' + 'releases/download/build/rme_release-86101e5.tar.xz'), + 'e42fef8439badb52a071ac446fc33cff4cb7d61314c7a28fdbe61a11e1faad3a') # This tests the FEAT_RME cpu implementation, by booting a VM supporting it, # and launching a nested VM using it. @@ -63,15 +35,16 @@ def test_aarch64_rme_virt(self): self.vm.set_console() - stack_path_tar_gz = self.ASSET_RME_STACK_VIRT.fetch() - self.archive_extract(stack_path_tar_gz, format="tar") + stack_path_tar = self.ASSET_RME_STACK_VIRT.fetch() + self.archive_extract(stack_path_tar, format="tar") - rme_stack = self.scratch_file('rme-stack-op-tee-4.2.0-cca-v4-qemu_v8') - kernel = os.path.join(rme_stack, 'out', 'bin', 'Image') - bios = os.path.join(rme_stack, 'out', 'bin', 'flash.bin') - drive = os.path.join(rme_stack, 'out-br', 'images', 'rootfs.ext4') + rme_stack = self.scratch_file('.') + kernel = join(rme_stack, 'out', 'Image') + bios = join(rme_stack, 'out', 'flash.bin') + rootfs = join(rme_stack, 'out', 'host.ext4') - self.vm.add_args('-cpu', 'max,x-rme=on,pauth-impdef=on') + self.vm.add_args('-cpu', 'max,x-rme=on') + self.vm.add_args('-smp', '2') self.vm.add_args('-m', '2G') self.vm.add_args('-M', 'virt,acpi=off,' 'virtualization=on,' @@ -79,23 +52,25 @@ def test_aarch64_rme_virt(self): 'gic-version=3') self.vm.add_args('-bios', bios) self.vm.add_args('-kernel', kernel) - self.vm.add_args('-drive', f'format=raw,if=none,file={drive},id=hd0') - self.vm.add_args('-device', 'virtio-blk-pci,drive=hd0') - self.vm.add_args('-device', 'virtio-9p-device,fsdev=shr0,mount_tag=shr0') - self.vm.add_args('-fsdev', f'local,security_model=none,path={rme_stack},id=shr0') - self.vm.add_args('-device', 'virtio-net-pci,netdev=net0') - self.vm.add_args('-netdev', 'user,id=net0') + self.vm.add_args('-drive', f'format=raw,file={rootfs},if=virtio') + self.vm.add_args('-virtfs', + f'local,path={rme_stack}/,mount_tag=host,' + 'security_model=mapped,readonly=off') # We need to add nokaslr to avoid triggering this sporadic bug: # https://gitlab.com/qemu-project/qemu/-/issues/2823 - self.vm.add_args('-append', 'root=/dev/vda nokaslr') + self.vm.add_args('-append', + 'nokaslr root=/dev/vda rw init=/init --' + ' /host/out/lkvm run --realm' + ' -m 256m' + ' --restricted_mem' + ' --kernel /host/out/Image' + ' --disk /host/out/guest.ext4' + ' --params "root=/dev/vda rw init=/init"') self.vm.launch() - # Wait for host VM boot to complete. - wait_for_console_pattern(self, 'Welcome to Buildroot', - failure_message='Synchronous Exception at') - exec_command_and_wait_for_pattern(self, 'root', '#') - - test_realms_guest(self) + # Wait for host and guest VM boot to complete. + wait_for_console_pattern(self, 'root@guest', + failure_message='Kernel panic') if __name__ == '__main__': QemuSystemTest.main() From 8a60ffe9a8f46ed514656eb4a40d1386c439daf8 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Tue, 26 Aug 2025 11:21:28 +0100 Subject: [PATCH 0489/1794] target/arm: Implement FEAT_SCTLR2 and enable with -cpu max Add FEAT_SCTLR2, which introduces the SCTLR2_EL1, SCTLR2_EL2, and SCTLR2_EL3 registers. These registers are extensions of the SCTLR_ELx ones. Signed-off-by: Gustavo Romero Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-id: 20250727074202.83141-4-richard.henderson@linaro.org Message-ID: <20250711140828.1714666-4-gustavo.romero@linaro.org> [rth: Remove FEAT_MEC code; handle SCR and HCRX enable bits.] Signed-off-by: Richard Henderson Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + target/arm/cpu-features.h | 5 ++ target/arm/cpu.c | 3 ++ target/arm/cpu.h | 15 ++++++ target/arm/helper.c | 97 ++++++++++++++++++++++++++++++++--- target/arm/internals.h | 1 + target/arm/tcg/cpu64.c | 5 +- 7 files changed, 119 insertions(+), 8 deletions(-) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 890dc6fee214c..66043b0747aa1 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -121,6 +121,7 @@ the following architecture extensions: - FEAT_RPRES (Increased precision of FRECPE and FRSQRTE) - FEAT_S2FWB (Stage 2 forced Write-Back) - FEAT_SB (Speculation Barrier) +- FEAT_SCTLR2 (Extension to SCTLR_ELx) - FEAT_SEL2 (Secure EL2) - FEAT_SHA1 (SHA1 instructions) - FEAT_SHA256 (SHA256 instructions) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 5876162428af2..e372543bf3588 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -904,6 +904,11 @@ static inline bool isar_feature_aa64_nv2(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64MMFR2, NV) >= 2; } +static inline bool isar_feature_aa64_sctlr2(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64MMFR3, SCTLRX) != 0; +} + static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64DFR0, PMUVER) >= 4 && diff --git a/target/arm/cpu.c b/target/arm/cpu.c index e2b2337399cf5..2ab04cb5f7cf3 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -644,6 +644,9 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el) if (cpu_isar_feature(aa64_fgt, cpu)) { env->cp15.scr_el3 |= SCR_FGTEN; } + if (cpu_isar_feature(aa64_sctlr2, cpu)) { + env->cp15.scr_el3 |= SCR_SCTLR2EN; + } } if (target_el == 2) { diff --git a/target/arm/cpu.h b/target/arm/cpu.h index dc9b6dce4c922..08a29802e1339 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -337,6 +337,7 @@ typedef struct CPUArchState { }; uint64_t sctlr_el[4]; }; + uint64_t sctlr2_el[4]; /* Extension to System control register. */ uint64_t vsctlr; /* Virtualization System control register. */ uint64_t cpacr_el1; /* Architectural feature access control register */ uint64_t cptr_el[4]; /* ARMv8 feature trap registers */ @@ -1420,6 +1421,19 @@ void pmu_init(ARMCPU *cpu); #define SCTLR_SPINTMASK (1ULL << 62) /* FEAT_NMI */ #define SCTLR_TIDCP (1ULL << 63) /* FEAT_TIDCP1 */ +#define SCTLR2_EMEC (1ULL << 1) /* FEAT_MEC */ +#define SCTLR2_NMEA (1ULL << 2) /* FEAT_DoubleFault2 */ +#define SCTLR2_ENADERR (1ULL << 3) /* FEAT_ADERR */ +#define SCTLR2_ENANERR (1ULL << 4) /* FEAT_ANERR */ +#define SCTLR2_EASE (1ULL << 5) /* FEAT_DoubleFault2 */ +#define SCTLR2_ENIDCP128 (1ULL << 6) /* FEAT_SYSREG128 */ +#define SCTLR2_ENPACM (1ULL << 7) /* FEAT_PAuth_LR */ +#define SCTLR2_ENPACM0 (1ULL << 8) /* FEAT_PAuth_LR */ +#define SCTLR2_CPTA (1ULL << 9) /* FEAT_CPA2 */ +#define SCTLR2_CPTA0 (1ULL << 10) /* FEAT_CPA2 */ +#define SCTLR2_CPTM (1ULL << 11) /* FEAT_CPA2 */ +#define SCTLR2_CPTM0 (1ULL << 12) /* FEAT_CAP2 */ + #define CPSR_M (0x1fU) #define CPSR_T (1U << 5) #define CPSR_F (1U << 6) @@ -1712,6 +1726,7 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_HXEN (1ULL << 38) #define SCR_TRNDR (1ULL << 40) #define SCR_ENTP2 (1ULL << 41) +#define SCR_SCTLR2EN (1ULL << 44) #define SCR_GPF (1ULL << 48) #define SCR_NSE (1ULL << 62) diff --git a/target/arm/helper.c b/target/arm/helper.c index 0c1299ff841f3..11ddeabb13267 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -741,6 +741,9 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) if (cpu_isar_feature(aa64_ecv, cpu)) { valid_mask |= SCR_ECVEN; } + if (cpu_isar_feature(aa64_sctlr2, cpu)) { + valid_mask |= SCR_SCTLR2EN; + } } else { valid_mask &= ~(SCR_RW | SCR_ST); if (cpu_isar_feature(aa32_ras, cpu)) { @@ -3907,23 +3910,21 @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri, ARMCPU *cpu = env_archcpu(env); uint64_t valid_mask = 0; - /* FEAT_MOPS adds MSCEn and MCE2 */ if (cpu_isar_feature(aa64_mops, cpu)) { valid_mask |= HCRX_MSCEN | HCRX_MCE2; } - - /* FEAT_NMI adds TALLINT, VINMI and VFNMI */ if (cpu_isar_feature(aa64_nmi, cpu)) { valid_mask |= HCRX_TALLINT | HCRX_VINMI | HCRX_VFNMI; } - /* FEAT_CMOW adds CMOW */ if (cpu_isar_feature(aa64_cmow, cpu)) { valid_mask |= HCRX_CMOW; } - /* FEAT_XS adds FGTnXS, FnXS */ if (cpu_isar_feature(aa64_xs, cpu)) { valid_mask |= HCRX_FGTNXS | HCRX_FNXS; } + if (cpu_isar_feature(aa64_sctlr2, cpu)) { + valid_mask |= HCRX_SCTLR2EN; + } /* Clear RES0 bits. */ env->cp15.hcrx_el2 = value & valid_mask; @@ -3981,11 +3982,16 @@ uint64_t arm_hcrx_el2_eff(CPUARMState *env) * This may need to be revisited for future bits. */ if (!arm_is_el2_enabled(env)) { + ARMCPU *cpu = env_archcpu(env); uint64_t hcrx = 0; - if (cpu_isar_feature(aa64_mops, env_archcpu(env))) { - /* MSCEn behaves as 1 if EL2 is not enabled */ + + /* Bits which whose effective value is 1 if el2 not enabled. */ + if (cpu_isar_feature(aa64_mops, cpu)) { hcrx |= HCRX_MSCEN; } + if (cpu_isar_feature(aa64_sctlr2, cpu)) { + hcrx |= HCRX_SCTLR2EN; + } return hcrx; } if (arm_feature(env, ARM_FEATURE_EL3) && !(env->cp15.scr_el3 & SCR_HXEN)) { @@ -4513,6 +4519,8 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) static const struct E2HAlias aliases[] = { { K(3, 0, 1, 0, 0), K(3, 4, 1, 0, 0), K(3, 5, 1, 0, 0), "SCTLR", "SCTLR_EL2", "SCTLR_EL12" }, + { K(3, 0, 1, 0, 3), K(3, 4, 1, 0, 3), K(3, 5, 1, 0, 3), + "SCTLR2_EL1", "SCTLR2_EL2", "SCTLR2_EL12", isar_feature_aa64_sctlr2 }, { K(3, 0, 1, 0, 2), K(3, 4, 1, 1, 2), K(3, 5, 1, 0, 2), "CPACR", "CPTR_EL2", "CPACR_EL12" }, { K(3, 0, 2, 0, 0), K(3, 4, 2, 0, 0), K(3, 5, 2, 0, 0), @@ -5994,6 +6002,77 @@ static const ARMCPRegInfo actlr2_hactlr2_reginfo[] = { .resetvalue = 0 }, }; +static CPAccessResult sctlr2_el2_access(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) < 3 + && arm_feature(env, ARM_FEATURE_EL3) + && !(env->cp15.scr_el3 & SCR_SCTLR2EN)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +static CPAccessResult sctlr2_el1_access(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + CPAccessResult ret = access_tvm_trvm(env, ri, isread); + if (ret != CP_ACCESS_OK) { + return ret; + } + if (arm_current_el(env) < 2 && !(arm_hcrx_el2_eff(env) & HCRX_SCTLR2EN)) { + return CP_ACCESS_TRAP_EL2; + } + return sctlr2_el2_access(env, ri, isread); +} + +static void sctlr2_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t valid_mask = 0; + + value &= valid_mask; + raw_write(env, ri, value); +} + +static void sctlr2_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t valid_mask = 0; + + value &= valid_mask; + raw_write(env, ri, value); +} + +static void sctlr2_el3_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t valid_mask = 0; + + value &= valid_mask; + raw_write(env, ri, value); +} + +static const ARMCPRegInfo sctlr2_reginfo[] = { + { .name = "SCTLR2_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .opc2 = 3, .crn = 1, .crm = 0, + .access = PL1_RW, .accessfn = sctlr2_el1_access, + .writefn = sctlr2_el1_write, .fgt = FGT_SCTLR_EL1, + .nv2_redirect_offset = 0x278 | NV2_REDIR_NV1, + .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[1]) }, + { .name = "SCTLR2_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 3, .crn = 1, .crm = 0, + .access = PL2_RW, .accessfn = sctlr2_el2_access, + .writefn = sctlr2_el2_write, + .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[2]) }, + { .name = "SCTLR2_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .opc2 = 3, .crn = 1, .crm = 0, + .access = PL3_RW, .writefn = sctlr2_el3_write, + .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[3]) }, +}; + void register_cp_regs_for_features(ARMCPU *cpu) { /* Register all the coprocessor registers based on feature bits */ @@ -7223,6 +7302,10 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, nmi_reginfo); } + if (cpu_isar_feature(aa64_sctlr2, cpu)) { + define_arm_cp_regs(cpu, sctlr2_reginfo); + } + if (cpu_isar_feature(any_predinv, cpu)) { define_arm_cp_regs(cpu, predinv_reginfo); } diff --git a/target/arm/internals.h b/target/arm/internals.h index 3f86b070447ca..fb72236255139 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -230,6 +230,7 @@ FIELD(VSTCR, SA, 30, 1) #define HCRX_CMOW (1ULL << 9) #define HCRX_MCE2 (1ULL << 10) #define HCRX_MSCEN (1ULL << 11) +#define HCRX_SCTLR2EN (1ULL << 15) #define HPFAR_NS (1ULL << 63) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 35cddbafa4c75..f4efff03a5996 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1247,7 +1247,10 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR2, E0PD, 1); /* FEAT_E0PD */ SET_IDREG(isar, ID_AA64MMFR2, t); - FIELD_DP64_IDREG(isar, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ + t = GET_IDREG(isar, ID_AA64MMFR3); + t = FIELD_DP64(t, ID_AA64MMFR3, SCTLRX, 1); /* FEAT_SCTLR2 */ + t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ + SET_IDREG(isar, ID_AA64MMFR3, t); t = GET_IDREG(isar, ID_AA64ZFR0); t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 2); /* FEAT_SVE2p1 */ From 6e6d5fb4b928f09b46d0fa99830d75cddb22f73b Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Tue, 26 Aug 2025 11:21:28 +0100 Subject: [PATCH 0490/1794] target/arm: Implement FEAT_TCR2 and enable with -cpu max Add FEAT_TCR2, which introduces the TCR2_EL1 and TCR2_EL2 registers. These registers are extensions of the TCR_ELx registers and provide top-level control of the EL10 and EL20 translation regimes. Signed-off-by: Gustavo Romero Signed-off-by: Richard Henderson Reviewed-by: Richard Henderson Message-id: 20250727074202.83141-5-richard.henderson@linaro.org Message-ID: <20250711140828.1714666-5-gustavo.romero@linaro.org> Reviewed-by: Richard Henderson [rth: Remove FEAT_MEC code; handle SCR and HCRX enable bits.] Signed-off-by: Richard Henderson Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + target/arm/cpu-features.h | 5 +++ target/arm/cpu.c | 3 ++ target/arm/cpu.h | 2 + target/arm/helper.c | 71 +++++++++++++++++++++++++++++++++++ target/arm/internals.h | 19 ++++++++++ target/arm/tcg/cpu64.c | 1 + 7 files changed, 102 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 66043b0747aa1..1c597d867383c 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -149,6 +149,7 @@ the following architecture extensions: - FEAT_SPECRES (Speculation restriction instructions) - FEAT_SSBS (Speculative Store Bypass Safe) - FEAT_SSBS2 (MRS and MSR instructions for SSBS version 2) +- FEAT_TCR2 (Support for TCR2_ELx) - FEAT_TGran16K (Support for 16KB memory translation granule size at stage 1) - FEAT_TGran4K (Support for 4KB memory translation granule size at stage 1) - FEAT_TGran64K (Support for 64KB memory translation granule size at stage 1) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index e372543bf3588..8ec8c3feb3dea 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -904,6 +904,11 @@ static inline bool isar_feature_aa64_nv2(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64MMFR2, NV) >= 2; } +static inline bool isar_feature_aa64_tcr2(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64MMFR3, TCRX) != 0; +} + static inline bool isar_feature_aa64_sctlr2(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64MMFR3, SCTLRX) != 0; diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 2ab04cb5f7cf3..27a4610da5992 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -644,6 +644,9 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el) if (cpu_isar_feature(aa64_fgt, cpu)) { env->cp15.scr_el3 |= SCR_FGTEN; } + if (cpu_isar_feature(aa64_tcr2, cpu)) { + env->cp15.scr_el3 |= SCR_TCR2EN; + } if (cpu_isar_feature(aa64_sctlr2, cpu)) { env->cp15.scr_el3 |= SCR_SCTLR2EN; } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 08a29802e1339..c15d79a106b73 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -366,6 +366,7 @@ typedef struct CPUArchState { uint64_t vsttbr_el2; /* Secure Virtualization Translation Table. */ /* MMU translation table base control. */ uint64_t tcr_el[4]; + uint64_t tcr2_el[3]; uint64_t vtcr_el2; /* Virtualization Translation Control. */ uint64_t vstcr_el2; /* Secure Virtualization Translation Control. */ uint32_t c2_data; /* MPU data cacheable bits. */ @@ -1726,6 +1727,7 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_HXEN (1ULL << 38) #define SCR_TRNDR (1ULL << 40) #define SCR_ENTP2 (1ULL << 41) +#define SCR_TCR2EN (1ULL << 43) #define SCR_SCTLR2EN (1ULL << 44) #define SCR_GPF (1ULL << 48) #define SCR_NSE (1ULL << 62) diff --git a/target/arm/helper.c b/target/arm/helper.c index 11ddeabb13267..5a219703aed07 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -741,6 +741,9 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) if (cpu_isar_feature(aa64_ecv, cpu)) { valid_mask |= SCR_ECVEN; } + if (cpu_isar_feature(aa64_tcr2, cpu)) { + valid_mask |= SCR_TCR2EN; + } if (cpu_isar_feature(aa64_sctlr2, cpu)) { valid_mask |= SCR_SCTLR2EN; } @@ -3922,6 +3925,9 @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri, if (cpu_isar_feature(aa64_xs, cpu)) { valid_mask |= HCRX_FGTNXS | HCRX_FNXS; } + if (cpu_isar_feature(aa64_tcr2, cpu)) { + valid_mask |= HCRX_TCR2EN; + } if (cpu_isar_feature(aa64_sctlr2, cpu)) { valid_mask |= HCRX_SCTLR2EN; } @@ -3989,6 +3995,9 @@ uint64_t arm_hcrx_el2_eff(CPUARMState *env) if (cpu_isar_feature(aa64_mops, cpu)) { hcrx |= HCRX_MSCEN; } + if (cpu_isar_feature(aa64_tcr2, cpu)) { + hcrx |= HCRX_TCR2EN; + } if (cpu_isar_feature(aa64_sctlr2, cpu)) { hcrx |= HCRX_SCTLR2EN; } @@ -4529,6 +4538,8 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) "TTBR1_EL1", "TTBR1_EL2", "TTBR1_EL12" }, { K(3, 0, 2, 0, 2), K(3, 4, 2, 0, 2), K(3, 5, 2, 0, 2), "TCR_EL1", "TCR_EL2", "TCR_EL12" }, + { K(3, 0, 2, 0, 3), K(3, 4, 2, 0, 3), K(3, 5, 2, 0, 3), + "TCR2_EL1", "TCR2_EL2", "TCR2_EL12", isar_feature_aa64_tcr2 }, { K(3, 0, 4, 0, 0), K(3, 4, 4, 0, 0), K(3, 5, 4, 0, 0), "SPSR_EL1", "SPSR_EL2", "SPSR_EL12" }, { K(3, 0, 4, 0, 1), K(3, 4, 4, 0, 1), K(3, 5, 4, 0, 1), @@ -6073,6 +6084,62 @@ static const ARMCPRegInfo sctlr2_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[3]) }, }; +static CPAccessResult tcr2_el2_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) < 3 + && arm_feature(env, ARM_FEATURE_EL3) + && !(env->cp15.scr_el3 & SCR_TCR2EN)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +static CPAccessResult tcr2_el1_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + CPAccessResult ret = access_tvm_trvm(env, ri, isread); + if (ret != CP_ACCESS_OK) { + return ret; + } + if (arm_current_el(env) < 2 && !(arm_hcrx_el2_eff(env) & HCRX_TCR2EN)) { + return CP_ACCESS_TRAP_EL2; + } + return tcr2_el2_access(env, ri, isread); +} + +static void tcr2_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t valid_mask = 0; + + value &= valid_mask; + raw_write(env, ri, value); +} + +static void tcr2_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t valid_mask = 0; + + value &= valid_mask; + raw_write(env, ri, value); +} + +static const ARMCPRegInfo tcr2_reginfo[] = { + { .name = "TCR2_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .opc2 = 3, .crn = 2, .crm = 0, + .access = PL1_RW, .accessfn = tcr2_el1_access, + .writefn = tcr2_el1_write, .fgt = FGT_TCR_EL1, + .nv2_redirect_offset = 0x270 | NV2_REDIR_NV1, + .fieldoffset = offsetof(CPUARMState, cp15.tcr2_el[1]) }, + { .name = "TCR2_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 3, .crn = 2, .crm = 0, + .access = PL2_RW, .accessfn = tcr2_el2_access, + .writefn = tcr2_el2_write, + .fieldoffset = offsetof(CPUARMState, cp15.tcr2_el[2]) }, +}; + void register_cp_regs_for_features(ARMCPU *cpu) { /* Register all the coprocessor registers based on feature bits */ @@ -7306,6 +7373,10 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, sctlr2_reginfo); } + if (cpu_isar_feature(aa64_tcr2, cpu)) { + define_arm_cp_regs(cpu, tcr2_reginfo); + } + if (cpu_isar_feature(any_predinv, cpu)) { define_arm_cp_regs(cpu, predinv_reginfo); } diff --git a/target/arm/internals.h b/target/arm/internals.h index fb72236255139..f5a1e75db376d 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -196,6 +196,24 @@ FIELD(CPTR_EL3, TCPAC, 31, 1) #define TTBCR_SH1 (1U << 28) #define TTBCR_EAE (1U << 31) +#define TCR2_PNCH (1ULL << 0) +#define TCR2_PIE (1ULL << 1) +#define TCR2_E0POE (1ULL << 2) +#define TCR2_POE (1ULL << 3) +#define TCR2_AIE (1ULL << 4) +#define TCR2_D128 (1ULL << 5) +#define TCR2_PTTWI (1ULL << 10) +#define TCR2_HAFT (1ULL << 11) +#define TCR2_AMEC0 (1ULL << 12) +#define TCR2_AMEC1 (1ULL << 13) +#define TCR2_DISCH0 (1ULL << 14) +#define TCR2_DISCH1 (1ULL << 15) +#define TCR2_A2 (1ULL << 16) +#define TCR2_FNG0 (1ULL << 17) +#define TCR2_FNG1 (1ULL << 18) +#define TCR2_FNGNA0 (1ULL << 20) +#define TCR2_FNGNA1 (1ULL << 21) + FIELD(VTCR, T0SZ, 0, 6) FIELD(VTCR, SL0, 6, 2) FIELD(VTCR, IRGN0, 8, 2) @@ -230,6 +248,7 @@ FIELD(VSTCR, SA, 30, 1) #define HCRX_CMOW (1ULL << 9) #define HCRX_MCE2 (1ULL << 10) #define HCRX_MSCEN (1ULL << 11) +#define HCRX_TCR2EN (1ULL << 14) #define HCRX_SCTLR2EN (1ULL << 15) #define HPFAR_NS (1ULL << 63) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index f4efff03a5996..4eb51420ef642 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1248,6 +1248,7 @@ void aarch64_max_tcg_initfn(Object *obj) SET_IDREG(isar, ID_AA64MMFR2, t); t = GET_IDREG(isar, ID_AA64MMFR3); + t = FIELD_DP64(t, ID_AA64MMFR3, TCRX, 1); /* FEAT_TCR2 */ t = FIELD_DP64(t, ID_AA64MMFR3, SCTLRX, 1); /* FEAT_SCTLR2 */ t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ SET_IDREG(isar, ID_AA64MMFR3, t); From 376cdd7e9c94f1e03b2c58e068e8ebfe78b49514 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 26 Aug 2025 11:21:28 +0100 Subject: [PATCH 0491/1794] hw/intc/arm_gicv3_kvm: preserve pending interrupts during cpr Close a race condition that causes cpr-transfer to lose VFIO interrupts on ARM. CPR stops VCPUs but does not disable VFIO interrupts, which may continue to arrive throughout the transition to new QEMU. CPR calls kvm_irqchip_remove_irqfd_notifier_gsi in old QEMU to force future interrupts to the producer eventfd, where they are preserved. Old QEMU then destroys the old KVM instance. However, interrupts may already be pending in KVM state. To preserve them, call ioctl KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES to flush them to guest RAM, where they will be picked up when the new KVM+VCPU instance is created. Cc: qemu-stable@nongnu.org Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Message-id: 1754936384-278328-1-git-send-email-steven.sistare@oracle.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_kvm.c | 15 +++++++++++++++ include/hw/intc/arm_gicv3_common.h | 3 +++ 2 files changed, 18 insertions(+) diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 6166283cd1a3d..0cd14d78a7596 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -31,6 +31,7 @@ #include "gicv3_internal.h" #include "vgic_common.h" #include "migration/blocker.h" +#include "migration/misc.h" #include "qom/object.h" #include "target/arm/cpregs.h" @@ -776,6 +777,17 @@ static void vm_change_state_handler(void *opaque, bool running, } } +static int kvm_arm_gicv3_notifier(NotifierWithReturn *notifier, + MigrationEvent *e, Error **errp) +{ + if (e->type == MIG_EVENT_PRECOPY_DONE) { + GICv3State *s = container_of(notifier, GICv3State, cpr_notifier); + return kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES, + NULL, true, errp); + } + return 0; +} static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) { @@ -917,6 +929,9 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES)) { qemu_add_vm_change_state_handler(vm_change_state_handler, s); + migration_add_notifier_mode(&s->cpr_notifier, + kvm_arm_gicv3_notifier, + MIG_MODE_CPR_TRANSFER); } } diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index c18503869f9a8..572d971d22c05 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -27,6 +27,7 @@ #include "hw/sysbus.h" #include "hw/intc/arm_gic_common.h" #include "qom/object.h" +#include "qemu/notify.h" /* * Maximum number of possible interrupts, determined by the GIC architecture. @@ -271,6 +272,8 @@ struct GICv3State { GICv3CPUState *cpu; /* List of all ITSes connected to this GIC */ GPtrArray *itslist; + + NotifierWithReturn cpr_notifier; }; #define GICV3_BITMAP_ACCESSORS(BMP) \ From 186db6a73bc5c01026bb9f4f4a59e442c0156841 Mon Sep 17 00:00:00 2001 From: Smail AIDER Date: Tue, 26 Aug 2025 11:21:28 +0100 Subject: [PATCH 0492/1794] target/arm: Trap PMCR when MDCR_EL2.TPMCR is set Trap PMCR_EL0 or PMCR accesses to EL2 when MDCR_EL2.TPMCR is set. Similar to MDCR_EL2.TPM, MDCR_EL2.TPMCR allows trapping EL0 and EL1 accesses to the PMCR register to EL2. Cc: qemu-stable@nongnu.org Signed-off-by: Smail AIDER Reviewed-by: Richard Henderson Message-id: 20250811112143.1577055-2-smail.aider@huawei.com Message-Id: <20250722131925.2119169-1-smail.aider@huawei.com> Signed-off-by: Peter Maydell --- target/arm/cpregs-pmu.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/target/arm/cpregs-pmu.c b/target/arm/cpregs-pmu.c index 9c4431c18bae8..31c01eddc8768 100644 --- a/target/arm/cpregs-pmu.c +++ b/target/arm/cpregs-pmu.c @@ -228,22 +228,27 @@ static bool event_supported(uint16_t number) return supported_event_map[number] != UNSUPPORTED_EVENT; } -static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) +static CPAccessResult do_pmreg_access(CPUARMState *env, bool is_pmcr) { /* * Performance monitor registers user accessibility is controlled - * by PMUSERENR. MDCR_EL2.TPM and MDCR_EL3.TPM allow configurable + * by PMUSERENR. MDCR_EL2.TPM/TPMCR and MDCR_EL3.TPM allow configurable * trapping to EL2 or EL3 for other accesses. */ int el = arm_current_el(env); - uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); if (el == 0 && !(env->cp15.c9_pmuserenr & 1)) { return CP_ACCESS_TRAP_EL1; } - if (el < 2 && (mdcr_el2 & MDCR_TPM)) { - return CP_ACCESS_TRAP_EL2; + if (el < 2) { + uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); + + if (mdcr_el2 & MDCR_TPM) { + return CP_ACCESS_TRAP_EL2; + } + if (is_pmcr && (mdcr_el2 & MDCR_TPMCR)) { + return CP_ACCESS_TRAP_EL2; + } } if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TPM)) { return CP_ACCESS_TRAP_EL3; @@ -252,6 +257,19 @@ static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } +static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + return do_pmreg_access(env, false); +} + +static CPAccessResult pmreg_access_pmcr(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + return do_pmreg_access(env, true); +} + static CPAccessResult pmreg_access_xevcntr(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) @@ -1187,14 +1205,14 @@ void define_pm_cpregs(ARMCPU *cpu) .fgt = FGT_PMCR_EL0, .type = ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr), - .accessfn = pmreg_access, + .accessfn = pmreg_access_pmcr, .readfn = pmcr_read, .raw_readfn = raw_read, .writefn = pmcr_write, .raw_writefn = raw_write, }; const ARMCPRegInfo pmcr64 = { .name = "PMCR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 0, - .access = PL0_RW, .accessfn = pmreg_access, + .access = PL0_RW, .accessfn = pmreg_access_pmcr, .fgt = FGT_PMCR_EL0, .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr), From 3b53af353b0b2e00fced6f00de87c03346542665 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Aug 2025 11:21:29 +0100 Subject: [PATCH 0493/1794] target/arm: Add feature predicate for FEAT_CSSC Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250803014019.416797-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 8ec8c3feb3dea..41511d08350b0 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -604,6 +604,11 @@ static inline bool isar_feature_aa64_rpres(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64ISAR2, RPRES); } +static inline bool isar_feature_aa64_cssc(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64ISAR2, CSSC) != 0; +} + static inline bool isar_feature_aa64_lut(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64ISAR2, LUT); From d0e4b9d4d77ab3685fc22b71de0f4fd220afa17a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Aug 2025 11:21:29 +0100 Subject: [PATCH 0494/1794] target/arm: Implement MIN/MAX (immediate) Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250803014019.416797-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 10 ++++++++ target/arm/tcg/translate-a64.c | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 8c798cde2b43d..c1811b0274723 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -156,6 +156,16 @@ MOVZ . 10 100101 .. ................ ..... @movw_32 MOVK . 11 100101 .. ................ ..... @movw_64 MOVK . 11 100101 .. ................ ..... @movw_32 +# Min/Max (immediate) + +@minmaxi_s sf:1 .. ........... imm:s8 rn:5 rd:5 &rri_sf +@minmaxi_u sf:1 .. ........... imm:8 rn:5 rd:5 &rri_sf + +SMAX_i . 00 1000111 0000 ........ ..... ..... @minmaxi_s +SMIN_i . 00 1000111 0010 ........ ..... ..... @minmaxi_s +UMAX_i . 00 1000111 0001 ........ ..... ..... @minmaxi_u +UMIN_i . 00 1000111 0011 ........ ..... ..... @minmaxi_u + # Bitfield &bitfield rd rn sf immr imms diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index dbf47595dbe2e..b70ae5befd251 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -4552,6 +4552,50 @@ TRANS(SUB_i, gen_rri, a, 1, 1, tcg_gen_sub_i64) TRANS(ADDS_i, gen_rri, a, 0, 1, a->sf ? gen_add64_CC : gen_add32_CC) TRANS(SUBS_i, gen_rri, a, 0, 1, a->sf ? gen_sub64_CC : gen_sub32_CC) +/* + * Min/Max (immediate) + */ + +static void gen_wrap3_i32(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, NeonGenTwoOpFn fn) +{ + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + + tcg_gen_extrl_i64_i32(t1, n); + tcg_gen_extrl_i64_i32(t2, m); + fn(t1, t1, t2); + tcg_gen_extu_i32_i64(d, t1); +} + +static void gen_smax32_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + gen_wrap3_i32(d, n, m, tcg_gen_smax_i32); +} + +static void gen_smin32_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + gen_wrap3_i32(d, n, m, tcg_gen_smin_i32); +} + +static void gen_umax32_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + gen_wrap3_i32(d, n, m, tcg_gen_umax_i32); +} + +static void gen_umin32_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + gen_wrap3_i32(d, n, m, tcg_gen_umin_i32); +} + +TRANS_FEAT(SMAX_i, aa64_cssc, gen_rri, a, 0, 0, + a->sf ? tcg_gen_smax_i64 : gen_smax32_i64) +TRANS_FEAT(SMIN_i, aa64_cssc, gen_rri, a, 0, 0, + a->sf ? tcg_gen_smin_i64 : gen_smin32_i64) +TRANS_FEAT(UMAX_i, aa64_cssc, gen_rri, a, 0, 0, + a->sf ? tcg_gen_umax_i64 : gen_umax32_i64) +TRANS_FEAT(UMIN_i, aa64_cssc, gen_rri, a, 0, 0, + a->sf ? tcg_gen_umin_i64 : gen_umin32_i64) + /* * Add/subtract (immediate, with tags) */ From 506538208ddabc9b15b02a7e865aa1b64f9f18e5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Aug 2025 11:21:29 +0100 Subject: [PATCH 0495/1794] target/arm: Implement MIN/MAX (register) Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250803014019.416797-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 5 +++++ target/arm/tcg/translate-a64.c | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index c1811b0274723..a886b3ba4c706 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -708,6 +708,11 @@ GMI 1 00 11010110 ..... 000101 ..... ..... @rrr PACGA 1 00 11010110 ..... 001100 ..... ..... @rrr +SMAX . 00 11010110 ..... 011000 ..... ..... @rrr_sf +SMIN . 00 11010110 ..... 011010 ..... ..... @rrr_sf +UMAX . 00 11010110 ..... 011001 ..... ..... @rrr_sf +UMIN . 00 11010110 ..... 011011 ..... ..... @rrr_sf + # Data Processing (1-source) @rr . .......... ..... ...... rn:5 rd:5 &rr diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index b70ae5befd251..bb92bdc296a9e 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8201,6 +8201,28 @@ static bool trans_PACGA(DisasContext *s, arg_rrr *a) return false; } +static bool gen_rrr(DisasContext *s, arg_rrr_sf *a, ArithTwoOp fn) +{ + TCGv_i64 tcg_rm = cpu_reg(s, a->rm); + TCGv_i64 tcg_rn = cpu_reg(s, a->rn); + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + + fn(tcg_rd, tcg_rn, tcg_rm); + if (!a->sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + return true; +} + +TRANS_FEAT(SMAX, aa64_cssc, gen_rrr, a, + a->sf ? tcg_gen_smax_i64 : gen_smax32_i64) +TRANS_FEAT(SMIN, aa64_cssc, gen_rrr, a, + a->sf ? tcg_gen_smin_i64 : gen_smin32_i64) +TRANS_FEAT(UMAX, aa64_cssc, gen_rrr, a, + a->sf ? tcg_gen_umax_i64 : gen_umax32_i64) +TRANS_FEAT(UMIN, aa64_cssc, gen_rrr, a, + a->sf ? tcg_gen_umin_i64 : gen_umin32_i64) + typedef void ArithOneOp(TCGv_i64, TCGv_i64); static bool gen_rr(DisasContext *s, int rd, int rn, ArithOneOp fn) From 8a4bb8b975f80022cdea27757cd8d14f13bf65a9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Aug 2025 11:21:29 +0100 Subject: [PATCH 0496/1794] target/arm: Split out gen_wrap2_i32 helper Wrapper to extract the low 32 bits, perform an operation, and zero-extend back to 64 bits. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250803014019.416797-5-richard.henderson@linaro.org [PMM: fixed wrong output-reg argument in callsites; add comment] Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index bb92bdc296a9e..c0fa9a44e7afb 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8231,13 +8231,22 @@ static bool gen_rr(DisasContext *s, int rd, int rn, ArithOneOp fn) return true; } -static void gen_rbit32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +/* + * Perform 32-bit operation fn on the low half of n; + * the high half of the output is zeroed. + */ +static void gen_wrap2_i32(TCGv_i64 d, TCGv_i64 n, NeonGenOneOpFn fn) { - TCGv_i32 t32 = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(t32, tcg_rn); - gen_helper_rbit(t32, t32); - tcg_gen_extu_i32_i64(tcg_rd, t32); + tcg_gen_extrl_i64_i32(t, n); + fn(t, t); + tcg_gen_extu_i32_i64(d, t); +} + +static void gen_rbit32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + gen_wrap2_i32(tcg_rd, tcg_rn, gen_helper_rbit); } static void gen_rev16_xx(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i64 mask) @@ -8293,11 +8302,7 @@ static void gen_clz64(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) static void gen_cls32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) { - TCGv_i32 t32 = tcg_temp_new_i32(); - - tcg_gen_extrl_i64_i32(t32, tcg_rn); - tcg_gen_clrsb_i32(t32, t32); - tcg_gen_extu_i32_i64(tcg_rd, t32); + gen_wrap2_i32(tcg_rd, tcg_rn, tcg_gen_clrsb_i32); } TRANS(CLZ, gen_rr, a->rd, a->rn, a->sf ? gen_clz64 : gen_clz32) From 994a260feac452ca478af5bd4ba4bff45b889b6e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 26 Aug 2025 17:11:12 +0100 Subject: [PATCH 0497/1794] target/arm: Implement CTZ, CNT, ABS Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250803014019.416797-6-richard.henderson@linaro.org [PMM: fix tcg_rd/tcg_rn mixup] Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 4 ++++ target/arm/tcg/translate-a64.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index a886b3ba4c706..766c610c019fa 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -726,6 +726,10 @@ REV64 1 10 11010110 00000 000011 ..... ..... @rr CLZ . 10 11010110 00000 000100 ..... ..... @rr_sf CLS . 10 11010110 00000 000101 ..... ..... @rr_sf +CTZ . 10 11010110 00000 000110 ..... ..... @rr_sf +CNT . 10 11010110 00000 000111 ..... ..... @rr_sf +ABS . 10 11010110 00000 001000 ..... ..... @rr_sf + &pacaut rd rn z @pacaut . .. ........ ..... .. z:1 ... rn:5 rd:5 &pacaut diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index c0fa9a44e7afb..259aa70a36d60 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8308,6 +8308,37 @@ static void gen_cls32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) TRANS(CLZ, gen_rr, a->rd, a->rn, a->sf ? gen_clz64 : gen_clz32) TRANS(CLS, gen_rr, a->rd, a->rn, a->sf ? tcg_gen_clrsb_i64 : gen_cls32) +static void gen_ctz32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + TCGv_i32 t32 = tcg_temp_new_i32(); + + tcg_gen_extrl_i64_i32(t32, tcg_rn); + tcg_gen_ctzi_i32(t32, t32, 32); + tcg_gen_extu_i32_i64(tcg_rd, t32); +} + +static void gen_ctz64(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + tcg_gen_ctzi_i64(tcg_rd, tcg_rn, 64); +} + +static void gen_cnt32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + gen_wrap2_i32(tcg_rd, tcg_rn, tcg_gen_ctpop_i32); +} + +static void gen_abs32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + gen_wrap2_i32(tcg_rd, tcg_rn, tcg_gen_abs_i32); +} + +TRANS_FEAT(CTZ, aa64_cssc, gen_rr, a->rd, a->rn, + a->sf ? gen_ctz64 : gen_ctz32) +TRANS_FEAT(CNT, aa64_cssc, gen_rr, a->rd, a->rn, + a->sf ? tcg_gen_ctpop_i64 : gen_cnt32) +TRANS_FEAT(ABS, aa64_cssc, gen_rr, a->rd, a->rn, + a->sf ? tcg_gen_abs_i64 : gen_abs32) + static bool gen_pacaut(DisasContext *s, arg_pacaut *a, NeonGenTwo64OpEnvFn fn) { TCGv_i64 tcg_rd, tcg_rn; From 7494f8bbfbb030c5c40a42e4d71430115e4f7a63 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Aug 2025 11:21:30 +0100 Subject: [PATCH 0498/1794] target/arm: Enable FEAT_CSSC for -cpu max Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250803014019.416797-7-richard.henderson@linaro.org [PMM: rebased to handle linux-user elfload.c refactor] Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + linux-user/aarch64/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 1c597d867383c..b12f013b4fc1b 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -30,6 +30,7 @@ the following architecture extensions: - FEAT_CMOW (Control for cache maintenance permission) - FEAT_CRC32 (CRC32 instructions) - FEAT_Crypto (Cryptographic Extension) +- FEAT_CSSC (Common Short Sequence Compression instructions) - FEAT_CSV2 (Cache speculation variant 2) - FEAT_CSV2_1p1 (Cache speculation variant 2, version 1.1) - FEAT_CSV2_1p2 (Cache speculation variant 2, version 1.2) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 8076968251f73..dd5f34398a221 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -215,6 +215,7 @@ abi_ulong get_elf_hwcap2(CPUState *cs) GET_FEATURE_ID(aa64_sme_b16b16, ARM_HWCAP2_A64_SME_B16B16); GET_FEATURE_ID(aa64_sme_f16f16, ARM_HWCAP2_A64_SME_F16F16); GET_FEATURE_ID(aa64_sve_b16b16, ARM_HWCAP2_A64_SVE_B16B16); + GET_FEATURE_ID(aa64_cssc, ARM_HWCAP2_A64_CSSC); return hwcaps; } diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 4eb51420ef642..eaf8846a6a52a 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1178,6 +1178,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR2, MOPS, 1); /* FEAT_MOPS */ t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* FEAT_HBC */ t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* FEAT_WFxT */ + t = FIELD_DP64(t, ID_AA64ISAR2, CSSC, 1); /* FEAT_CSSC */ SET_IDREG(isar, ID_AA64ISAR2, t); t = GET_IDREG(isar, ID_AA64PFR0); From 36bc78aca83cfd3c8f73cbcb428bc7f0ce7a4a61 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 26 Aug 2025 11:21:30 +0100 Subject: [PATCH 0499/1794] hw/arm: add static NVDIMMs in device tree NVDIMM is used for fast rootfs with EROFS, for example by kata containers. To allow booting with static NVDIMM memory, add them to the device tree in arm virt machine. This allows users to boot directly with nvdimm memory devices without having to rely on ACPI and hotplug. Verified to work with command invocation: ./qemu-system-aarch64 \ -M virt,nvdimm=on \ -cpu cortex-a57 \ -m 4G,slots=2,maxmem=8G \ -object memory-backend-file,id=mem1,share=on,mem-path=/tmp/nvdimm,size=4G,readonly=off \ -device nvdimm,id=nvdimm1,memdev=mem1,unarmed=off \ -drive file=./debian-12-nocloud-arm64-commited.qcow2,format=qcow2 \ -kernel ./vmlinuz-6.1.0-13-arm64 \ -append "root=/dev/vda1 console=ttyAMA0,115200 acpi=off" -initrd ./initrd.img-6.1.0-13-arm64 \ -nographic \ -serial mon:stdio Signed-off-by: Manos Pitsidianakis Message-id: 20250807-nvdimm_arm64_virt-v2-1-b8054578bea8@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/boot.c | 42 ++++++++++++++++++++++++++++++++++++++++++ hw/arm/virt.c | 8 +++++--- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index d391cd01bb1b9..1e57c4ab9ee86 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -25,6 +25,7 @@ #include "hw/boards.h" #include "system/reset.h" #include "hw/loader.h" +#include "hw/mem/memory-device.h" #include "elf.h" #include "system/device_tree.h" #include "qemu/config-file.h" @@ -515,6 +516,29 @@ static void fdt_add_psci_node(void *fdt, ARMCPU *armcpu) qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn); } +static int fdt_add_pmem_node(void *fdt, uint32_t acells, uint32_t scells, + int64_t mem_base, int64_t size, int64_t node) +{ + int ret; + + g_autofree char *nodename = g_strdup_printf("/pmem@%" PRIx64, mem_base); + + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "pmem-region"); + ret = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", acells, + mem_base, scells, size); + if (ret) { + return ret; + } + + if (node >= 0) { + return qemu_fdt_setprop_cell(fdt, nodename, "numa-node-id", + node); + } + + return 0; +} + int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, hwaddr addr_limit, AddressSpace *as, MachineState *ms, ARMCPU *cpu) @@ -525,6 +549,7 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, unsigned int i; hwaddr mem_base, mem_len; char **node_path; + g_autofree MemoryDeviceInfoList *md_list = NULL; Error *err = NULL; if (binfo->dtb_filename) { @@ -628,6 +653,23 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, } } + md_list = qmp_memory_device_list(); + for (MemoryDeviceInfoList *m = md_list; m != NULL; m = m->next) { + MemoryDeviceInfo *mi = m->value; + + if (mi->type == MEMORY_DEVICE_INFO_KIND_NVDIMM) { + PCDIMMDeviceInfo *di = mi->u.nvdimm.data; + + rc = fdt_add_pmem_node(fdt, acells, scells, + di->addr, di->size, di->node); + if (rc < 0) { + fprintf(stderr, "couldn't add NVDIMM /pmem@%"PRIx64" node\n", + di->addr); + goto fail; + } + } + } + rc = fdt_path_offset(fdt, "/chosen"); if (rc < 0) { qemu_fdt_add_subnode(fdt, "/chosen"); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 9326cfc895f7b..1e63f40fbece3 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2917,7 +2917,7 @@ static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, const MachineState *ms = MACHINE(hotplug_dev); const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); - if (!vms->acpi_dev) { + if (!vms->acpi_dev && !(is_nvdimm && !dev->hotplugged)) { error_setg(errp, "memory hotplug is not enabled: missing acpi-ged device"); return; @@ -2949,8 +2949,10 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev, nvdimm_plug(ms->nvdimms_state); } - hotplug_handler_plug(HOTPLUG_HANDLER(vms->acpi_dev), - dev, &error_abort); + if (vms->acpi_dev) { + hotplug_handler_plug(HOTPLUG_HANDLER(vms->acpi_dev), + dev, &error_abort); + } } static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, From 5ffd387e9e0f787744fadaad35e1bf92224b0642 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 19 Aug 2025 12:56:48 +0100 Subject: [PATCH 0500/1794] scripts/kernel-doc: Avoid new Perl precedence warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Newer versions of Perl (5.41.x and up) emit a warning for code in kernel-doc: Possible precedence problem between ! and pattern match (m//) at /scripts/kernel-doc line 1597. This is because the code does: if (!$param =~ /\w\.\.\.$/) { In Perl, the ! operator has higher precedence than the =~ pattern-match binding, so the effect of this condition is to first logically-negate the string $param into a true-or-false value and then try to pattern match it against the regex, which in this case will always fail. This is almost certainly not what the author intended. In the new Python version of kernel-doc in the Linux kernel, the equivalent code is written: if KernRe(r'\w\.\.\.$').search(param): # For named variable parameters of the form `x...`, # remove the dots param = param[:-3] else: # Handles unnamed variable parameters param = "..." which is a more sensible way of writing the behaviour you would get if you put in brackets to make the regex match first and then negate the result. Take this as the intended behaviour, and update the Perl to match. For QEMU, this produces no change in output, presumably because we never used the "unnamed variable parameters" syntax. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Daniel P. Berrangé Reviewed-by: Mauro Carvalho Chehab Message-id: 20250819115648.2125709-1-peter.maydell@linaro.org --- scripts/kernel-doc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index fec83f53eda32..117ec8fcd1f82 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -1594,13 +1594,12 @@ sub push_parameter($$$$$) { if ($type eq "" && $param =~ /\.\.\.$/) { - if (!$param =~ /\w\.\.\.$/) { - # handles unnamed variable parameters - $param = "..."; - } - elsif ($param =~ /\w\.\.\.$/) { + if ($param =~ /\w\.\.\.$/) { # for named variable parameters of the form `x...`, remove the dots $param =~ s/\.\.\.$//; + } else { + # handles unnamed variable parameters + $param = "..."; } if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") { $parameterdescs{$param} = "variable arguments"; From 3f34478007a2423399dbdfae5e3deeca3898da9c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 14 Aug 2025 18:13:16 +0100 Subject: [PATCH 0501/1794] docs/sphinx/kerneldoc.py: Handle new LINENO syntax The new upstream kernel-doc that we plan to update to uses a different syntax for the LINENO directives that the Sphinx extension parses: instead of #define LINENO 86 it has .. LINENO 86 Update the kerneldoc.py extension to handle both syntaxes, so that it will work with both the old and the new kernel-doc. Signed-off-by: Peter Maydell Reviewed-by: Paolo Bonzini Reviewed-by: Mauro Carvalho Chehab Message-id: 20250814171324.1614516-2-peter.maydell@linaro.org --- docs/sphinx/kerneldoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/kerneldoc.py b/docs/sphinx/kerneldoc.py index 3aa972f2e89c1..30bb343198387 100644 --- a/docs/sphinx/kerneldoc.py +++ b/docs/sphinx/kerneldoc.py @@ -127,7 +127,7 @@ def run(self): result = ViewList() lineoffset = 0; - line_regex = re.compile("^#define LINENO ([0-9]+)$") + line_regex = re.compile(r"^(?:\.\.|#define) LINENO ([0-9]+)$") for line in lines: match = line_regex.search(line) if match: From 4a1bc66d3f61cb803b16bb1691e46b5ec59d25f5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 14 Aug 2025 18:13:17 +0100 Subject: [PATCH 0502/1794] tests/qtest/libqtest.h: Remove stray space from doc comment The doc comment for qtest_cb_for_every_machine has a stray space at the start of its description, which makes kernel-doc think that this line is part of the documentation of the skip_old_versioned argument. The result is that the HTML doesn't have a "Description" section and the text is instead put in the wrong place. Remove the stray space. Signed-off-by: Peter Maydell Reviewed-by: Paolo Bonzini Reviewed-by: Mauro Carvalho Chehab Message-id: 20250814171324.1614516-3-peter.maydell@linaro.org --- tests/qtest/libqtest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index b3f2e7fbefd8d..fd27521a9c761 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -977,7 +977,7 @@ void qtest_qmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds, * @cb: Pointer to the callback function * @skip_old_versioned: true if versioned old machine types should be skipped * - * Call a callback function for every name of all available machines. + * Call a callback function for every name of all available machines. */ void qtest_cb_for_every_machine(void (*cb)(const char *machine), bool skip_old_versioned); From 2b2765ac4045642563cc92ad98c2244a0aa0c7fc Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 14 Aug 2025 18:13:18 +0100 Subject: [PATCH 0503/1794] scripts: Import Python kerneldoc from Linux kernel We last synced our copy of kerneldoc with Linux back in 2020. In the interim, upstream has entirely rewritten the script in Python, and the new Python version is split into a main script plus some libraries in the kernel's scripts/lib/kdoc. Import all these files. These are the versions as of kernel commit 0cc53520e68be, with no local changes. We use the same lib/kdoc/ directory as the kernel does here, so we can avoid having to edit the top-level script just to adjust a pathname, even though it is probably not the naming we would have picked if this was a purely QEMU script. The Sphinx conf.py still points at the Perl version of the script, so this Python code will not be invoked to build the docs yet. NB: checkpatch complains about many things in this commit, including the use of "GPL-2.0" rather than "GPL-2.0-only" in the SPDX tags, but since this is a third party import we can ignore this. Signed-off-by: Peter Maydell Reviewed-by: Paolo Bonzini Reviewed-by: Mauro Carvalho Chehab Message-id: 20250814171324.1614516-4-peter.maydell@linaro.org --- scripts/kernel-doc.py | 325 ++++++ scripts/lib/kdoc/kdoc_files.py | 291 ++++++ scripts/lib/kdoc/kdoc_item.py | 42 + scripts/lib/kdoc/kdoc_output.py | 749 ++++++++++++++ scripts/lib/kdoc/kdoc_parser.py | 1669 +++++++++++++++++++++++++++++++ scripts/lib/kdoc/kdoc_re.py | 270 +++++ 6 files changed, 3346 insertions(+) create mode 100755 scripts/kernel-doc.py create mode 100644 scripts/lib/kdoc/kdoc_files.py create mode 100644 scripts/lib/kdoc/kdoc_item.py create mode 100644 scripts/lib/kdoc/kdoc_output.py create mode 100644 scripts/lib/kdoc/kdoc_parser.py create mode 100644 scripts/lib/kdoc/kdoc_re.py diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py new file mode 100755 index 0000000000000..fc3d46ef519f8 --- /dev/null +++ b/scripts/kernel-doc.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab . +# +# pylint: disable=C0103,R0915 +# +# Converted from the kernel-doc script originally written in Perl +# under GPLv2, copyrighted since 1998 by the following authors: +# +# Aditya Srivastava +# Akira Yokosawa +# Alexander A. Klimov +# Alexander Lobakin +# André Almeida +# Andy Shevchenko +# Anna-Maria Behnsen +# Armin Kuster +# Bart Van Assche +# Ben Hutchings +# Borislav Petkov +# Chen-Yu Tsai +# Coco Li +# Conchúr Navid +# Daniel Santos +# Danilo Cesar Lemes de Paula +# Dan Luedtke +# Donald Hunter +# Gabriel Krisman Bertazi +# Greg Kroah-Hartman +# Harvey Harrison +# Horia Geanta +# Ilya Dryomov +# Jakub Kicinski +# Jani Nikula +# Jason Baron +# Jason Gunthorpe +# Jérémy Bobbio +# Johannes Berg +# Johannes Weiner +# Jonathan Cameron +# Jonathan Corbet +# Jonathan Neuschäfer +# Kamil Rytarowski +# Kees Cook +# Laurent Pinchart +# Levin, Alexander (Sasha Levin) +# Linus Torvalds +# Lucas De Marchi +# Mark Rutland +# Markus Heiser +# Martin Waitz +# Masahiro Yamada +# Matthew Wilcox +# Mauro Carvalho Chehab +# Michal Wajdeczko +# Michael Zucchi +# Mike Rapoport +# Niklas Söderlund +# Nishanth Menon +# Paolo Bonzini +# Pavan Kumar Linga +# Pavel Pisa +# Peter Maydell +# Pierre-Louis Bossart +# Randy Dunlap +# Richard Kennedy +# Rich Walker +# Rolf Eike Beer +# Sakari Ailus +# Silvio Fricke +# Simon Huggins +# Tim Waugh +# Tomasz Warniełło +# Utkarsh Tripathi +# valdis.kletnieks@vt.edu +# Vegard Nossum +# Will Deacon +# Yacine Belkadi +# Yujie Liu + +""" +kernel_doc +========== + +Print formatted kernel documentation to stdout + +Read C language source or header FILEs, extract embedded +documentation comments, and print formatted documentation +to standard output. + +The documentation comments are identified by the "/**" +opening comment mark. + +See Documentation/doc-guide/kernel-doc.rst for the +documentation comment syntax. +""" + +import argparse +import logging +import os +import sys + +# Import Python modules + +LIB_DIR = "lib/kdoc" +SRC_DIR = os.path.dirname(os.path.realpath(__file__)) + +sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) + +from kdoc_files import KernelFiles # pylint: disable=C0413 +from kdoc_output import RestFormat, ManFormat # pylint: disable=C0413 + +DESC = """ +Read C language source or header FILEs, extract embedded documentation comments, +and print formatted documentation to standard output. + +The documentation comments are identified by the "/**" opening comment mark. + +See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. +""" + +EXPORT_FILE_DESC = """ +Specify an additional FILE in which to look for EXPORT_SYMBOL information. + +May be used multiple times. +""" + +EXPORT_DESC = """ +Only output documentation for the symbols that have been +exported using EXPORT_SYMBOL() and related macros in any input +FILE or -export-file FILE. +""" + +INTERNAL_DESC = """ +Only output documentation for the symbols that have NOT been +exported using EXPORT_SYMBOL() and related macros in any input +FILE or -export-file FILE. +""" + +FUNCTION_DESC = """ +Only output documentation for the given function or DOC: section +title. All other functions and DOC: sections are ignored. + +May be used multiple times. +""" + +NOSYMBOL_DESC = """ +Exclude the specified symbol from the output documentation. + +May be used multiple times. +""" + +FILES_DESC = """ +Header and C source files to be parsed. +""" + +WARN_CONTENTS_BEFORE_SECTIONS_DESC = """ +Warns if there are contents before sections (deprecated). + +This option is kept just for backward-compatibility, but it does nothing, +neither here nor at the original Perl script. +""" + + +class MsgFormatter(logging.Formatter): + """Helper class to format warnings on a similar way to kernel-doc.pl""" + + def format(self, record): + record.levelname = record.levelname.capitalize() + return logging.Formatter.format(self, record) + +def main(): + """Main program""" + + parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, + description=DESC) + + # Normal arguments + + parser.add_argument("-v", "-verbose", "--verbose", action="store_true", + help="Verbose output, more warnings and other information.") + + parser.add_argument("-d", "-debug", "--debug", action="store_true", + help="Enable debug messages") + + parser.add_argument("-M", "-modulename", "--modulename", + default="Kernel API", + help="Allow setting a module name at the output.") + + parser.add_argument("-l", "-enable-lineno", "--enable_lineno", + action="store_true", + help="Enable line number output (only in ReST mode)") + + # Arguments to control the warning behavior + + parser.add_argument("-Wreturn", "--wreturn", action="store_true", + help="Warns about the lack of a return markup on functions.") + + parser.add_argument("-Wshort-desc", "-Wshort-description", "--wshort-desc", + action="store_true", + help="Warns if initial short description is missing") + + parser.add_argument("-Wcontents-before-sections", + "--wcontents-before-sections", action="store_true", + help=WARN_CONTENTS_BEFORE_SECTIONS_DESC) + + parser.add_argument("-Wall", "--wall", action="store_true", + help="Enable all types of warnings") + + parser.add_argument("-Werror", "--werror", action="store_true", + help="Treat warnings as errors.") + + parser.add_argument("-export-file", "--export-file", action='append', + help=EXPORT_FILE_DESC) + + # Output format mutually-exclusive group + + out_group = parser.add_argument_group("Output format selection (mutually exclusive)") + + out_fmt = out_group.add_mutually_exclusive_group() + + out_fmt.add_argument("-m", "-man", "--man", action="store_true", + help="Output troff manual page format.") + out_fmt.add_argument("-r", "-rst", "--rst", action="store_true", + help="Output reStructuredText format (default).") + out_fmt.add_argument("-N", "-none", "--none", action="store_true", + help="Do not output documentation, only warnings.") + + # Output selection mutually-exclusive group + + sel_group = parser.add_argument_group("Output selection (mutually exclusive)") + sel_mut = sel_group.add_mutually_exclusive_group() + + sel_mut.add_argument("-e", "-export", "--export", action='store_true', + help=EXPORT_DESC) + + sel_mut.add_argument("-i", "-internal", "--internal", action='store_true', + help=INTERNAL_DESC) + + sel_mut.add_argument("-s", "-function", "--symbol", action='append', + help=FUNCTION_DESC) + + # Those are valid for all 3 types of filter + parser.add_argument("-n", "-nosymbol", "--nosymbol", action='append', + help=NOSYMBOL_DESC) + + parser.add_argument("-D", "-no-doc-sections", "--no-doc-sections", + action='store_true', help="Don't outputt DOC sections") + + parser.add_argument("files", metavar="FILE", + nargs="+", help=FILES_DESC) + + args = parser.parse_args() + + if args.wall: + args.wreturn = True + args.wshort_desc = True + args.wcontents_before_sections = True + + logger = logging.getLogger() + + if not args.debug: + logger.setLevel(logging.INFO) + else: + logger.setLevel(logging.DEBUG) + + formatter = MsgFormatter('%(levelname)s: %(message)s') + + handler = logging.StreamHandler() + handler.setFormatter(formatter) + + logger.addHandler(handler) + + python_ver = sys.version_info[:2] + if python_ver < (3,6): + logger.warning("Python 3.6 or later is required by kernel-doc") + + # Return 0 here to avoid breaking compilation + sys.exit(0) + + if python_ver < (3,7): + logger.warning("Python 3.7 or later is required for correct results") + + if args.man: + out_style = ManFormat(modulename=args.modulename) + elif args.none: + out_style = None + else: + out_style = RestFormat() + + kfiles = KernelFiles(verbose=args.verbose, + out_style=out_style, werror=args.werror, + wreturn=args.wreturn, wshort_desc=args.wshort_desc, + wcontents_before_sections=args.wcontents_before_sections) + + kfiles.parse(args.files, export_file=args.export_file) + + for t in kfiles.msg(enable_lineno=args.enable_lineno, export=args.export, + internal=args.internal, symbol=args.symbol, + nosymbol=args.nosymbol, export_file=args.export_file, + no_doc_sections=args.no_doc_sections): + msg = t[1] + if msg: + print(msg) + + error_count = kfiles.errors + if not error_count: + sys.exit(0) + + if args.werror: + print(f"{error_count} warnings as errors") + sys.exit(error_count) + + if args.verbose: + print(f"{error_count} errors") + + if args.none: + sys.exit(0) + + sys.exit(error_count) + + +# Call main method +if __name__ == "__main__": + main() diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py new file mode 100644 index 0000000000000..9e09b45b02faa --- /dev/null +++ b/scripts/lib/kdoc/kdoc_files.py @@ -0,0 +1,291 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab . +# +# pylint: disable=R0903,R0913,R0914,R0917 + +""" +Parse lernel-doc tags on multiple kernel source files. +""" + +import argparse +import logging +import os +import re + +from kdoc_parser import KernelDoc +from kdoc_output import OutputFormat + + +class GlobSourceFiles: + """ + Parse C source code file names and directories via an Interactor. + """ + + def __init__(self, srctree=None, valid_extensions=None): + """ + Initialize valid extensions with a tuple. + + If not defined, assume default C extensions (.c and .h) + + It would be possible to use python's glob function, but it is + very slow, and it is not interactive. So, it would wait to read all + directories before actually do something. + + So, let's use our own implementation. + """ + + if not valid_extensions: + self.extensions = (".c", ".h") + else: + self.extensions = valid_extensions + + self.srctree = srctree + + def _parse_dir(self, dirname): + """Internal function to parse files recursively""" + + with os.scandir(dirname) as obj: + for entry in obj: + name = os.path.join(dirname, entry.name) + + if entry.is_dir(): + yield from self._parse_dir(name) + + if not entry.is_file(): + continue + + basename = os.path.basename(name) + + if not basename.endswith(self.extensions): + continue + + yield name + + def parse_files(self, file_list, file_not_found_cb): + """ + Define an interator to parse all source files from file_list, + handling directories if any + """ + + if not file_list: + return + + for fname in file_list: + if self.srctree: + f = os.path.join(self.srctree, fname) + else: + f = fname + + if os.path.isdir(f): + yield from self._parse_dir(f) + elif os.path.isfile(f): + yield f + elif file_not_found_cb: + file_not_found_cb(fname) + + +class KernelFiles(): + """ + Parse kernel-doc tags on multiple kernel source files. + + There are two type of parsers defined here: + - self.parse_file(): parses both kernel-doc markups and + EXPORT_SYMBOL* macros; + - self.process_export_file(): parses only EXPORT_SYMBOL* macros. + """ + + def warning(self, msg): + """Ancillary routine to output a warning and increment error count""" + + self.config.log.warning(msg) + self.errors += 1 + + def error(self, msg): + """Ancillary routine to output an error and increment error count""" + + self.config.log.error(msg) + self.errors += 1 + + def parse_file(self, fname): + """ + Parse a single Kernel source. + """ + + # Prevent parsing the same file twice if results are cached + if fname in self.files: + return + + doc = KernelDoc(self.config, fname) + export_table, entries = doc.parse_kdoc() + + self.export_table[fname] = export_table + + self.files.add(fname) + self.export_files.add(fname) # parse_kdoc() already check exports + + self.results[fname] = entries + + def process_export_file(self, fname): + """ + Parses EXPORT_SYMBOL* macros from a single Kernel source file. + """ + + # Prevent parsing the same file twice if results are cached + if fname in self.export_files: + return + + doc = KernelDoc(self.config, fname) + export_table = doc.parse_export() + + if not export_table: + self.error(f"Error: Cannot check EXPORT_SYMBOL* on {fname}") + export_table = set() + + self.export_table[fname] = export_table + self.export_files.add(fname) + + def file_not_found_cb(self, fname): + """ + Callback to warn if a file was not found. + """ + + self.error(f"Cannot find file {fname}") + + def __init__(self, verbose=False, out_style=None, + werror=False, wreturn=False, wshort_desc=False, + wcontents_before_sections=False, + logger=None): + """ + Initialize startup variables and parse all files + """ + + if not verbose: + verbose = bool(os.environ.get("KBUILD_VERBOSE", 0)) + + if out_style is None: + out_style = OutputFormat() + + if not werror: + kcflags = os.environ.get("KCFLAGS", None) + if kcflags: + match = re.search(r"(\s|^)-Werror(\s|$)/", kcflags) + if match: + werror = True + + # reading this variable is for backwards compat just in case + # someone was calling it with the variable from outside the + # kernel's build system + kdoc_werror = os.environ.get("KDOC_WERROR", None) + if kdoc_werror: + werror = kdoc_werror + + # Some variables are global to the parser logic as a whole as they are + # used to send control configuration to KernelDoc class. As such, + # those variables are read-only inside the KernelDoc. + self.config = argparse.Namespace + + self.config.verbose = verbose + self.config.werror = werror + self.config.wreturn = wreturn + self.config.wshort_desc = wshort_desc + self.config.wcontents_before_sections = wcontents_before_sections + + if not logger: + self.config.log = logging.getLogger("kernel-doc") + else: + self.config.log = logger + + self.config.warning = self.warning + + self.config.src_tree = os.environ.get("SRCTREE", None) + + # Initialize variables that are internal to KernelFiles + + self.out_style = out_style + + self.errors = 0 + self.results = {} + + self.files = set() + self.export_files = set() + self.export_table = {} + + def parse(self, file_list, export_file=None): + """ + Parse all files + """ + + glob = GlobSourceFiles(srctree=self.config.src_tree) + + for fname in glob.parse_files(file_list, self.file_not_found_cb): + self.parse_file(fname) + + for fname in glob.parse_files(export_file, self.file_not_found_cb): + self.process_export_file(fname) + + def out_msg(self, fname, name, arg): + """ + Return output messages from a file name using the output style + filtering. + + If output type was not handled by the syler, return None. + """ + + # NOTE: we can add rules here to filter out unwanted parts, + # although OutputFormat.msg already does that. + + return self.out_style.msg(fname, name, arg) + + def msg(self, enable_lineno=False, export=False, internal=False, + symbol=None, nosymbol=None, no_doc_sections=False, + filenames=None, export_file=None): + """ + Interacts over the kernel-doc results and output messages, + returning kernel-doc markups on each interaction + """ + + self.out_style.set_config(self.config) + + if not filenames: + filenames = sorted(self.results.keys()) + + glob = GlobSourceFiles(srctree=self.config.src_tree) + + for fname in filenames: + function_table = set() + + if internal or export: + if not export_file: + export_file = [fname] + + for f in glob.parse_files(export_file, self.file_not_found_cb): + function_table |= self.export_table[f] + + if symbol: + for s in symbol: + function_table.add(s) + + self.out_style.set_filter(export, internal, symbol, nosymbol, + function_table, enable_lineno, + no_doc_sections) + + msg = "" + if fname not in self.results: + self.config.log.warning("No kernel-doc for file %s", fname) + continue + + for arg in self.results[fname]: + m = self.out_msg(fname, arg.name, arg) + + if m is None: + ln = arg.get("ln", 0) + dtype = arg.get('type', "") + + self.config.log.warning("%s:%d Can't handle %s", + fname, ln, dtype) + else: + msg += m + + if msg: + yield fname, msg diff --git a/scripts/lib/kdoc/kdoc_item.py b/scripts/lib/kdoc/kdoc_item.py new file mode 100644 index 0000000000000..b3b225764550d --- /dev/null +++ b/scripts/lib/kdoc/kdoc_item.py @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# A class that will, eventually, encapsulate all of the parsed data that we +# then pass into the output modules. +# + +class KdocItem: + def __init__(self, name, type, start_line, **other_stuff): + self.name = name + self.type = type + self.declaration_start_line = start_line + self.sections = {} + self.sections_start_lines = {} + self.parameterlist = [] + self.parameterdesc_start_lines = [] + self.parameterdescs = {} + self.parametertypes = {} + # + # Just save everything else into our own dict so that the output + # side can grab it directly as before. As we move things into more + # structured data, this will, hopefully, fade away. + # + self.other_stuff = other_stuff + + def get(self, key, default = None): + return self.other_stuff.get(key, default) + + def __getitem__(self, key): + return self.get(key) + + # + # Tracking of section and parameter information. + # + def set_sections(self, sections, start_lines): + self.sections = sections + self.section_start_lines = start_lines + + def set_params(self, names, descs, types, starts): + self.parameterlist = names + self.parameterdescs = descs + self.parametertypes = types + self.parameterdesc_start_lines = starts diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output.py new file mode 100644 index 0000000000000..ea8914537ba08 --- /dev/null +++ b/scripts/lib/kdoc/kdoc_output.py @@ -0,0 +1,749 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab . +# +# pylint: disable=C0301,R0902,R0911,R0912,R0913,R0914,R0915,R0917 + +""" +Implement output filters to print kernel-doc documentation. + +The implementation uses a virtual base class (OutputFormat) which +contains a dispatches to virtual methods, and some code to filter +out output messages. + +The actual implementation is done on one separate class per each type +of output. Currently, there are output classes for ReST and man/troff. +""" + +import os +import re +from datetime import datetime + +from kdoc_parser import KernelDoc, type_param +from kdoc_re import KernRe + + +function_pointer = KernRe(r"([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)", cache=False) + +# match expressions used to find embedded type information +type_constant = KernRe(r"\b``([^\`]+)``\b", cache=False) +type_constant2 = KernRe(r"\%([-_*\w]+)", cache=False) +type_func = KernRe(r"(\w+)\(\)", cache=False) +type_param_ref = KernRe(r"([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) + +# Special RST handling for func ptr params +type_fp_param = KernRe(r"\@(\w+)\(\)", cache=False) + +# Special RST handling for structs with func ptr params +type_fp_param2 = KernRe(r"\@(\w+->\S+)\(\)", cache=False) + +type_env = KernRe(r"(\$\w+)", cache=False) +type_enum = KernRe(r"\&(enum\s*([_\w]+))", cache=False) +type_struct = KernRe(r"\&(struct\s*([_\w]+))", cache=False) +type_typedef = KernRe(r"\&(typedef\s*([_\w]+))", cache=False) +type_union = KernRe(r"\&(union\s*([_\w]+))", cache=False) +type_member = KernRe(r"\&([_\w]+)(\.|->)([_\w]+)", cache=False) +type_fallback = KernRe(r"\&([_\w]+)", cache=False) +type_member_func = type_member + KernRe(r"\(\)", cache=False) + + +class OutputFormat: + """ + Base class for OutputFormat. If used as-is, it means that only + warnings will be displayed. + """ + + # output mode. + OUTPUT_ALL = 0 # output all symbols and doc sections + OUTPUT_INCLUDE = 1 # output only specified symbols + OUTPUT_EXPORTED = 2 # output exported symbols + OUTPUT_INTERNAL = 3 # output non-exported symbols + + # Virtual member to be overriden at the inherited classes + highlights = [] + + def __init__(self): + """Declare internal vars and set mode to OUTPUT_ALL""" + + self.out_mode = self.OUTPUT_ALL + self.enable_lineno = None + self.nosymbol = {} + self.symbol = None + self.function_table = None + self.config = None + self.no_doc_sections = False + + self.data = "" + + def set_config(self, config): + """ + Setup global config variables used by both parser and output. + """ + + self.config = config + + def set_filter(self, export, internal, symbol, nosymbol, function_table, + enable_lineno, no_doc_sections): + """ + Initialize filter variables according with the requested mode. + + Only one choice is valid between export, internal and symbol. + + The nosymbol filter can be used on all modes. + """ + + self.enable_lineno = enable_lineno + self.no_doc_sections = no_doc_sections + self.function_table = function_table + + if symbol: + self.out_mode = self.OUTPUT_INCLUDE + elif export: + self.out_mode = self.OUTPUT_EXPORTED + elif internal: + self.out_mode = self.OUTPUT_INTERNAL + else: + self.out_mode = self.OUTPUT_ALL + + if nosymbol: + self.nosymbol = set(nosymbol) + + + def highlight_block(self, block): + """ + Apply the RST highlights to a sub-block of text. + """ + + for r, sub in self.highlights: + block = r.sub(sub, block) + + return block + + def out_warnings(self, args): + """ + Output warnings for identifiers that will be displayed. + """ + + for log_msg in args.warnings: + self.config.warning(log_msg) + + def check_doc(self, name, args): + """Check if DOC should be output""" + + if self.no_doc_sections: + return False + + if name in self.nosymbol: + return False + + if self.out_mode == self.OUTPUT_ALL: + self.out_warnings(args) + return True + + if self.out_mode == self.OUTPUT_INCLUDE: + if name in self.function_table: + self.out_warnings(args) + return True + + return False + + def check_declaration(self, dtype, name, args): + """ + Checks if a declaration should be output or not based on the + filtering criteria. + """ + + if name in self.nosymbol: + return False + + if self.out_mode == self.OUTPUT_ALL: + self.out_warnings(args) + return True + + if self.out_mode in [self.OUTPUT_INCLUDE, self.OUTPUT_EXPORTED]: + if name in self.function_table: + return True + + if self.out_mode == self.OUTPUT_INTERNAL: + if dtype != "function": + self.out_warnings(args) + return True + + if name not in self.function_table: + self.out_warnings(args) + return True + + return False + + def msg(self, fname, name, args): + """ + Handles a single entry from kernel-doc parser + """ + + self.data = "" + + dtype = args.type + + if dtype == "doc": + self.out_doc(fname, name, args) + return self.data + + if not self.check_declaration(dtype, name, args): + return self.data + + if dtype == "function": + self.out_function(fname, name, args) + return self.data + + if dtype == "enum": + self.out_enum(fname, name, args) + return self.data + + if dtype == "typedef": + self.out_typedef(fname, name, args) + return self.data + + if dtype in ["struct", "union"]: + self.out_struct(fname, name, args) + return self.data + + # Warn if some type requires an output logic + self.config.log.warning("doesn't now how to output '%s' block", + dtype) + + return None + + # Virtual methods to be overridden by inherited classes + # At the base class, those do nothing. + def out_doc(self, fname, name, args): + """Outputs a DOC block""" + + def out_function(self, fname, name, args): + """Outputs a function""" + + def out_enum(self, fname, name, args): + """Outputs an enum""" + + def out_typedef(self, fname, name, args): + """Outputs a typedef""" + + def out_struct(self, fname, name, args): + """Outputs a struct""" + + +class RestFormat(OutputFormat): + """Consts and functions used by ReST output""" + + highlights = [ + (type_constant, r"``\1``"), + (type_constant2, r"``\1``"), + + # Note: need to escape () to avoid func matching later + (type_member_func, r":c:type:`\1\2\3\\(\\) <\1>`"), + (type_member, r":c:type:`\1\2\3 <\1>`"), + (type_fp_param, r"**\1\\(\\)**"), + (type_fp_param2, r"**\1\\(\\)**"), + (type_func, r"\1()"), + (type_enum, r":c:type:`\1 <\2>`"), + (type_struct, r":c:type:`\1 <\2>`"), + (type_typedef, r":c:type:`\1 <\2>`"), + (type_union, r":c:type:`\1 <\2>`"), + + # in rst this can refer to any type + (type_fallback, r":c:type:`\1`"), + (type_param_ref, r"**\1\2**") + ] + blankline = "\n" + + sphinx_literal = KernRe(r'^[^.].*::$', cache=False) + sphinx_cblock = KernRe(r'^\.\.\ +code-block::', cache=False) + + def __init__(self): + """ + Creates class variables. + + Not really mandatory, but it is a good coding style and makes + pylint happy. + """ + + super().__init__() + self.lineprefix = "" + + def print_lineno(self, ln): + """Outputs a line number""" + + if self.enable_lineno and ln is not None: + ln += 1 + self.data += f".. LINENO {ln}\n" + + def output_highlight(self, args): + """ + Outputs a C symbol that may require being converted to ReST using + the self.highlights variable + """ + + input_text = args + output = "" + in_literal = False + litprefix = "" + block = "" + + for line in input_text.strip("\n").split("\n"): + + # If we're in a literal block, see if we should drop out of it. + # Otherwise, pass the line straight through unmunged. + if in_literal: + if line.strip(): # If the line is not blank + # If this is the first non-blank line in a literal block, + # figure out the proper indent. + if not litprefix: + r = KernRe(r'^(\s*)') + if r.match(line): + litprefix = '^' + r.group(1) + else: + litprefix = "" + + output += line + "\n" + elif not KernRe(litprefix).match(line): + in_literal = False + else: + output += line + "\n" + else: + output += line + "\n" + + # Not in a literal block (or just dropped out) + if not in_literal: + block += line + "\n" + if self.sphinx_literal.match(line) or self.sphinx_cblock.match(line): + in_literal = True + litprefix = "" + output += self.highlight_block(block) + block = "" + + # Handle any remaining block + if block: + output += self.highlight_block(block) + + # Print the output with the line prefix + for line in output.strip("\n").split("\n"): + self.data += self.lineprefix + line + "\n" + + def out_section(self, args, out_docblock=False): + """ + Outputs a block section. + + This could use some work; it's used to output the DOC: sections, and + starts by putting out the name of the doc section itself, but that + tends to duplicate a header already in the template file. + """ + for section, text in args.sections.items(): + # Skip sections that are in the nosymbol_table + if section in self.nosymbol: + continue + + if out_docblock: + if not self.out_mode == self.OUTPUT_INCLUDE: + self.data += f".. _{section}:\n\n" + self.data += f'{self.lineprefix}**{section}**\n\n' + else: + self.data += f'{self.lineprefix}**{section}**\n\n' + + self.print_lineno(args.section_start_lines.get(section, 0)) + self.output_highlight(text) + self.data += "\n" + self.data += "\n" + + def out_doc(self, fname, name, args): + if not self.check_doc(name, args): + return + self.out_section(args, out_docblock=True) + + def out_function(self, fname, name, args): + + oldprefix = self.lineprefix + signature = "" + + func_macro = args.get('func_macro', False) + if func_macro: + signature = name + else: + if args.get('functiontype'): + signature = args['functiontype'] + " " + signature += name + " (" + + ln = args.declaration_start_line + count = 0 + for parameter in args.parameterlist: + if count != 0: + signature += ", " + count += 1 + dtype = args.parametertypes.get(parameter, "") + + if function_pointer.search(dtype): + signature += function_pointer.group(1) + parameter + function_pointer.group(3) + else: + signature += dtype + + if not func_macro: + signature += ")" + + self.print_lineno(ln) + if args.get('typedef') or not args.get('functiontype'): + self.data += f".. c:macro:: {name}\n\n" + + if args.get('typedef'): + self.data += " **Typedef**: " + self.lineprefix = "" + self.output_highlight(args.get('purpose', "")) + self.data += "\n\n**Syntax**\n\n" + self.data += f" ``{signature}``\n\n" + else: + self.data += f"``{signature}``\n\n" + else: + self.data += f".. c:function:: {signature}\n\n" + + if not args.get('typedef'): + self.print_lineno(ln) + self.lineprefix = " " + self.output_highlight(args.get('purpose', "")) + self.data += "\n" + + # Put descriptive text into a container (HTML
) to help set + # function prototypes apart + self.lineprefix = " " + + if args.parameterlist: + self.data += ".. container:: kernelindent\n\n" + self.data += f"{self.lineprefix}**Parameters**\n\n" + + for parameter in args.parameterlist: + parameter_name = KernRe(r'\[.*').sub('', parameter) + dtype = args.parametertypes.get(parameter, "") + + if dtype: + self.data += f"{self.lineprefix}``{dtype}``\n" + else: + self.data += f"{self.lineprefix}``{parameter}``\n" + + self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0)) + + self.lineprefix = " " + if parameter_name in args.parameterdescs and \ + args.parameterdescs[parameter_name] != KernelDoc.undescribed: + + self.output_highlight(args.parameterdescs[parameter_name]) + self.data += "\n" + else: + self.data += f"{self.lineprefix}*undescribed*\n\n" + self.lineprefix = " " + + self.out_section(args) + self.lineprefix = oldprefix + + def out_enum(self, fname, name, args): + + oldprefix = self.lineprefix + ln = args.declaration_start_line + + self.data += f"\n\n.. c:enum:: {name}\n\n" + + self.print_lineno(ln) + self.lineprefix = " " + self.output_highlight(args.get('purpose', '')) + self.data += "\n" + + self.data += ".. container:: kernelindent\n\n" + outer = self.lineprefix + " " + self.lineprefix = outer + " " + self.data += f"{outer}**Constants**\n\n" + + for parameter in args.parameterlist: + self.data += f"{outer}``{parameter}``\n" + + if args.parameterdescs.get(parameter, '') != KernelDoc.undescribed: + self.output_highlight(args.parameterdescs[parameter]) + else: + self.data += f"{self.lineprefix}*undescribed*\n\n" + self.data += "\n" + + self.lineprefix = oldprefix + self.out_section(args) + + def out_typedef(self, fname, name, args): + + oldprefix = self.lineprefix + ln = args.declaration_start_line + + self.data += f"\n\n.. c:type:: {name}\n\n" + + self.print_lineno(ln) + self.lineprefix = " " + + self.output_highlight(args.get('purpose', '')) + + self.data += "\n" + + self.lineprefix = oldprefix + self.out_section(args) + + def out_struct(self, fname, name, args): + + purpose = args.get('purpose', "") + declaration = args.get('definition', "") + dtype = args.type + ln = args.declaration_start_line + + self.data += f"\n\n.. c:{dtype}:: {name}\n\n" + + self.print_lineno(ln) + + oldprefix = self.lineprefix + self.lineprefix += " " + + self.output_highlight(purpose) + self.data += "\n" + + self.data += ".. container:: kernelindent\n\n" + self.data += f"{self.lineprefix}**Definition**::\n\n" + + self.lineprefix = self.lineprefix + " " + + declaration = declaration.replace("\t", self.lineprefix) + + self.data += f"{self.lineprefix}{dtype} {name}" + ' {' + "\n" + self.data += f"{declaration}{self.lineprefix}" + "};\n\n" + + self.lineprefix = " " + self.data += f"{self.lineprefix}**Members**\n\n" + for parameter in args.parameterlist: + if not parameter or parameter.startswith("#"): + continue + + parameter_name = parameter.split("[", maxsplit=1)[0] + + if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed: + continue + + self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0)) + + self.data += f"{self.lineprefix}``{parameter}``\n" + + self.lineprefix = " " + self.output_highlight(args.parameterdescs[parameter_name]) + self.lineprefix = " " + + self.data += "\n" + + self.data += "\n" + + self.lineprefix = oldprefix + self.out_section(args) + + +class ManFormat(OutputFormat): + """Consts and functions used by man pages output""" + + highlights = ( + (type_constant, r"\1"), + (type_constant2, r"\1"), + (type_func, r"\\fB\1\\fP"), + (type_enum, r"\\fI\1\\fP"), + (type_struct, r"\\fI\1\\fP"), + (type_typedef, r"\\fI\1\\fP"), + (type_union, r"\\fI\1\\fP"), + (type_param, r"\\fI\1\\fP"), + (type_param_ref, r"\\fI\1\2\\fP"), + (type_member, r"\\fI\1\2\3\\fP"), + (type_fallback, r"\\fI\1\\fP") + ) + blankline = "" + + date_formats = [ + "%a %b %d %H:%M:%S %Z %Y", + "%a %b %d %H:%M:%S %Y", + "%Y-%m-%d", + "%b %d %Y", + "%B %d %Y", + "%m %d %Y", + ] + + def __init__(self, modulename): + """ + Creates class variables. + + Not really mandatory, but it is a good coding style and makes + pylint happy. + """ + + super().__init__() + self.modulename = modulename + + dt = None + tstamp = os.environ.get("KBUILD_BUILD_TIMESTAMP") + if tstamp: + for fmt in self.date_formats: + try: + dt = datetime.strptime(tstamp, fmt) + break + except ValueError: + pass + + if not dt: + dt = datetime.now() + + self.man_date = dt.strftime("%B %Y") + + def output_highlight(self, block): + """ + Outputs a C symbol that may require being highlighted with + self.highlights variable using troff syntax + """ + + contents = self.highlight_block(block) + + if isinstance(contents, list): + contents = "\n".join(contents) + + for line in contents.strip("\n").split("\n"): + line = KernRe(r"^\s*").sub("", line) + if not line: + continue + + if line[0] == ".": + self.data += "\\&" + line + "\n" + else: + self.data += line + "\n" + + def out_doc(self, fname, name, args): + if not self.check_doc(name, args): + return + + self.data += f'.TH "{self.modulename}" 9 "{self.modulename}" "{self.man_date}" "API Manual" LINUX' + "\n" + + for section, text in args.sections.items(): + self.data += f'.SH "{section}"' + "\n" + self.output_highlight(text) + + def out_function(self, fname, name, args): + """output function in man""" + + self.data += f'.TH "{name}" 9 "{name}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n" + + self.data += ".SH NAME\n" + self.data += f"{name} \\- {args['purpose']}\n" + + self.data += ".SH SYNOPSIS\n" + if args.get('functiontype', ''): + self.data += f'.B "{args["functiontype"]}" {name}' + "\n" + else: + self.data += f'.B "{name}' + "\n" + + count = 0 + parenth = "(" + post = "," + + for parameter in args.parameterlist: + if count == len(args.parameterlist) - 1: + post = ");" + + dtype = args.parametertypes.get(parameter, "") + if function_pointer.match(dtype): + # Pointer-to-function + self.data += f'".BI "{parenth}{function_pointer.group(1)}" " ") ({function_pointer.group(2)}){post}"' + "\n" + else: + dtype = KernRe(r'([^\*])$').sub(r'\1 ', dtype) + + self.data += f'.BI "{parenth}{dtype}" "{post}"' + "\n" + count += 1 + parenth = "" + + if args.parameterlist: + self.data += ".SH ARGUMENTS\n" + + for parameter in args.parameterlist: + parameter_name = re.sub(r'\[.*', '', parameter) + + self.data += f'.IP "{parameter}" 12' + "\n" + self.output_highlight(args.parameterdescs.get(parameter_name, "")) + + for section, text in args.sections.items(): + self.data += f'.SH "{section.upper()}"' + "\n" + self.output_highlight(text) + + def out_enum(self, fname, name, args): + self.data += f'.TH "{self.modulename}" 9 "enum {name}" "{self.man_date}" "API Manual" LINUX' + "\n" + + self.data += ".SH NAME\n" + self.data += f"enum {name} \\- {args['purpose']}\n" + + self.data += ".SH SYNOPSIS\n" + self.data += f"enum {name}" + " {\n" + + count = 0 + for parameter in args.parameterlist: + self.data += f'.br\n.BI " {parameter}"' + "\n" + if count == len(args.parameterlist) - 1: + self.data += "\n};\n" + else: + self.data += ", \n.br\n" + + count += 1 + + self.data += ".SH Constants\n" + + for parameter in args.parameterlist: + parameter_name = KernRe(r'\[.*').sub('', parameter) + self.data += f'.IP "{parameter}" 12' + "\n" + self.output_highlight(args.parameterdescs.get(parameter_name, "")) + + for section, text in args.sections.items(): + self.data += f'.SH "{section}"' + "\n" + self.output_highlight(text) + + def out_typedef(self, fname, name, args): + module = self.modulename + purpose = args.get('purpose') + + self.data += f'.TH "{module}" 9 "{name}" "{self.man_date}" "API Manual" LINUX' + "\n" + + self.data += ".SH NAME\n" + self.data += f"typedef {name} \\- {purpose}\n" + + for section, text in args.sections.items(): + self.data += f'.SH "{section}"' + "\n" + self.output_highlight(text) + + def out_struct(self, fname, name, args): + module = self.modulename + purpose = args.get('purpose') + definition = args.get('definition') + + self.data += f'.TH "{module}" 9 "{args.type} {name}" "{self.man_date}" "API Manual" LINUX' + "\n" + + self.data += ".SH NAME\n" + self.data += f"{args.type} {name} \\- {purpose}\n" + + # Replace tabs with two spaces and handle newlines + declaration = definition.replace("\t", " ") + declaration = KernRe(r"\n").sub('"\n.br\n.BI "', declaration) + + self.data += ".SH SYNOPSIS\n" + self.data += f"{args.type} {name} " + "{" + "\n.br\n" + self.data += f'.BI "{declaration}\n' + "};\n.br\n\n" + + self.data += ".SH Members\n" + for parameter in args.parameterlist: + if parameter.startswith("#"): + continue + + parameter_name = re.sub(r"\[.*", "", parameter) + + if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed: + continue + + self.data += f'.IP "{parameter}" 12' + "\n" + self.output_highlight(args.parameterdescs.get(parameter_name)) + + for section, text in args.sections.items(): + self.data += f'.SH "{section}"' + "\n" + self.output_highlight(text) diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py new file mode 100644 index 0000000000000..fe730099eca86 --- /dev/null +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -0,0 +1,1669 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab . +# +# pylint: disable=C0301,C0302,R0904,R0912,R0913,R0914,R0915,R0917,R1702 + +""" +kdoc_parser +=========== + +Read a C language source or header FILE and extract embedded +documentation comments +""" + +import sys +import re +from pprint import pformat + +from kdoc_re import NestedMatch, KernRe +from kdoc_item import KdocItem + +# +# Regular expressions used to parse kernel-doc markups at KernelDoc class. +# +# Let's declare them in lowercase outside any class to make easier to +# convert from the python script. +# +# As those are evaluated at the beginning, no need to cache them +# + +# Allow whitespace at end of comment start. +doc_start = KernRe(r'^/\*\*\s*$', cache=False) + +doc_end = KernRe(r'\*/', cache=False) +doc_com = KernRe(r'\s*\*\s*', cache=False) +doc_com_body = KernRe(r'\s*\* ?', cache=False) +doc_decl = doc_com + KernRe(r'(\w+)', cache=False) + +# @params and a strictly limited set of supported section names +# Specifically: +# Match @word: +# @...: +# @{section-name}: +# while trying to not match literal block starts like "example::" +# +known_section_names = 'description|context|returns?|notes?|examples?' +known_sections = KernRe(known_section_names, flags = re.I) +doc_sect = doc_com + \ + KernRe(r'\s*(\@[.\w]+|\@\.\.\.|' + known_section_names + r')\s*:([^:].*)?$', + flags=re.I, cache=False) + +doc_content = doc_com_body + KernRe(r'(.*)', cache=False) +doc_inline_start = KernRe(r'^\s*/\*\*\s*$', cache=False) +doc_inline_sect = KernRe(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=False) +doc_inline_end = KernRe(r'^\s*\*/\s*$', cache=False) +doc_inline_oneline = KernRe(r'^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$', cache=False) +attribute = KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", + flags=re.I | re.S, cache=False) + +export_symbol = KernRe(r'^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*', cache=False) +export_symbol_ns = KernRe(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*', cache=False) + +type_param = KernRe(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) + +# +# Tests for the beginning of a kerneldoc block in its various forms. +# +doc_block = doc_com + KernRe(r'DOC:\s*(.*)?', cache=False) +doc_begin_data = KernRe(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)", cache = False) +doc_begin_func = KernRe(str(doc_com) + # initial " * ' + r"(?:\w+\s*\*\s*)?" + # type (not captured) + r'(?:define\s+)?' + # possible "define" (not captured) + r'(\w+)\s*(?:\(\w*\))?\s*' + # name and optional "(...)" + r'(?:[-:].*)?$', # description (not captured) + cache = False) + +# +# A little helper to get rid of excess white space +# +multi_space = KernRe(r'\s\s+') +def trim_whitespace(s): + return multi_space.sub(' ', s.strip()) + +class state: + """ + State machine enums + """ + + # Parser states + NORMAL = 0 # normal code + NAME = 1 # looking for function name + DECLARATION = 2 # We have seen a declaration which might not be done + BODY = 3 # the body of the comment + SPECIAL_SECTION = 4 # doc section ending with a blank line + PROTO = 5 # scanning prototype + DOCBLOCK = 6 # documentation block + INLINE_NAME = 7 # gathering doc outside main block + INLINE_TEXT = 8 # reading the body of inline docs + + name = [ + "NORMAL", + "NAME", + "DECLARATION", + "BODY", + "SPECIAL_SECTION", + "PROTO", + "DOCBLOCK", + "INLINE_NAME", + "INLINE_TEXT", + ] + + +SECTION_DEFAULT = "Description" # default section + +class KernelEntry: + + def __init__(self, config, ln): + self.config = config + + self._contents = [] + self.prototype = "" + + self.warnings = [] + + self.parameterlist = [] + self.parameterdescs = {} + self.parametertypes = {} + self.parameterdesc_start_lines = {} + + self.section_start_lines = {} + self.sections = {} + + self.anon_struct_union = False + + self.leading_space = None + + # State flags + self.brcount = 0 + self.declaration_start_line = ln + 1 + + # + # Management of section contents + # + def add_text(self, text): + self._contents.append(text) + + def contents(self): + return '\n'.join(self._contents) + '\n' + + # TODO: rename to emit_message after removal of kernel-doc.pl + def emit_msg(self, log_msg, warning=True): + """Emit a message""" + + if not warning: + self.config.log.info(log_msg) + return + + # Delegate warning output to output logic, as this way it + # will report warnings/info only for symbols that are output + + self.warnings.append(log_msg) + return + + # + # Begin a new section. + # + def begin_section(self, line_no, title = SECTION_DEFAULT, dump = False): + if dump: + self.dump_section(start_new = True) + self.section = title + self.new_start_line = line_no + + def dump_section(self, start_new=True): + """ + Dumps section contents to arrays/hashes intended for that purpose. + """ + # + # If we have accumulated no contents in the default ("description") + # section, don't bother. + # + if self.section == SECTION_DEFAULT and not self._contents: + return + name = self.section + contents = self.contents() + + if type_param.match(name): + name = type_param.group(1) + + self.parameterdescs[name] = contents + self.parameterdesc_start_lines[name] = self.new_start_line + + self.new_start_line = 0 + + else: + if name in self.sections and self.sections[name] != "": + # Only warn on user-specified duplicate section names + if name != SECTION_DEFAULT: + self.emit_msg(self.new_start_line, + f"duplicate section name '{name}'\n") + # Treat as a new paragraph - add a blank line + self.sections[name] += '\n' + contents + else: + self.sections[name] = contents + self.section_start_lines[name] = self.new_start_line + self.new_start_line = 0 + +# self.config.log.debug("Section: %s : %s", name, pformat(vars(self))) + + if start_new: + self.section = SECTION_DEFAULT + self._contents = [] + + +class KernelDoc: + """ + Read a C language source or header FILE and extract embedded + documentation comments. + """ + + # Section names + + section_context = "Context" + section_return = "Return" + + undescribed = "-- undescribed --" + + def __init__(self, config, fname): + """Initialize internal variables""" + + self.fname = fname + self.config = config + + # Initial state for the state machines + self.state = state.NORMAL + + # Store entry currently being processed + self.entry = None + + # Place all potential outputs into an array + self.entries = [] + + # + # We need Python 3.7 for its "dicts remember the insertion + # order" guarantee + # + if sys.version_info.major == 3 and sys.version_info.minor < 7: + self.emit_msg(0, + 'Python 3.7 or later is required for correct results') + + def emit_msg(self, ln, msg, warning=True): + """Emit a message""" + + log_msg = f"{self.fname}:{ln} {msg}" + + if self.entry: + self.entry.emit_msg(log_msg, warning) + return + + if warning: + self.config.log.warning(log_msg) + else: + self.config.log.info(log_msg) + + def dump_section(self, start_new=True): + """ + Dumps section contents to arrays/hashes intended for that purpose. + """ + + if self.entry: + self.entry.dump_section(start_new) + + # TODO: rename it to store_declaration after removal of kernel-doc.pl + def output_declaration(self, dtype, name, **args): + """ + Stores the entry into an entry array. + + The actual output and output filters will be handled elsewhere + """ + + item = KdocItem(name, dtype, self.entry.declaration_start_line, **args) + item.warnings = self.entry.warnings + + # Drop empty sections + # TODO: improve empty sections logic to emit warnings + sections = self.entry.sections + for section in ["Description", "Return"]: + if section in sections and not sections[section].rstrip(): + del sections[section] + item.set_sections(sections, self.entry.section_start_lines) + item.set_params(self.entry.parameterlist, self.entry.parameterdescs, + self.entry.parametertypes, + self.entry.parameterdesc_start_lines) + self.entries.append(item) + + self.config.log.debug("Output: %s:%s = %s", dtype, name, pformat(args)) + + def reset_state(self, ln): + """ + Ancillary routine to create a new entry. It initializes all + variables used by the state machine. + """ + + self.entry = KernelEntry(self.config, ln) + + # State flags + self.state = state.NORMAL + + def push_parameter(self, ln, decl_type, param, dtype, + org_arg, declaration_name): + """ + Store parameters and their descriptions at self.entry. + """ + + if self.entry.anon_struct_union and dtype == "" and param == "}": + return # Ignore the ending }; from anonymous struct/union + + self.entry.anon_struct_union = False + + param = KernRe(r'[\[\)].*').sub('', param, count=1) + + if dtype == "" and param.endswith("..."): + if KernRe(r'\w\.\.\.$').search(param): + # For named variable parameters of the form `x...`, + # remove the dots + param = param[:-3] + else: + # Handles unnamed variable parameters + param = "..." + + if param not in self.entry.parameterdescs or \ + not self.entry.parameterdescs[param]: + + self.entry.parameterdescs[param] = "variable arguments" + + elif dtype == "" and (not param or param == "void"): + param = "void" + self.entry.parameterdescs[param] = "no arguments" + + elif dtype == "" and param in ["struct", "union"]: + # Handle unnamed (anonymous) union or struct + dtype = param + param = "{unnamed_" + param + "}" + self.entry.parameterdescs[param] = "anonymous\n" + self.entry.anon_struct_union = True + + # Handle cache group enforcing variables: they do not need + # to be described in header files + elif "__cacheline_group" in param: + # Ignore __cacheline_group_begin and __cacheline_group_end + return + + # Warn if parameter has no description + # (but ignore ones starting with # as these are not parameters + # but inline preprocessor statements) + if param not in self.entry.parameterdescs and not param.startswith("#"): + self.entry.parameterdescs[param] = self.undescribed + + if "." not in param: + if decl_type == 'function': + dname = f"{decl_type} parameter" + else: + dname = f"{decl_type} member" + + self.emit_msg(ln, + f"{dname} '{param}' not described in '{declaration_name}'") + + # Strip spaces from param so that it is one continuous string on + # parameterlist. This fixes a problem where check_sections() + # cannot find a parameter like "addr[6 + 2]" because it actually + # appears as "addr[6", "+", "2]" on the parameter list. + # However, it's better to maintain the param string unchanged for + # output, so just weaken the string compare in check_sections() + # to ignore "[blah" in a parameter string. + + self.entry.parameterlist.append(param) + org_arg = KernRe(r'\s\s+').sub(' ', org_arg) + self.entry.parametertypes[param] = org_arg + + + def create_parameter_list(self, ln, decl_type, args, + splitter, declaration_name): + """ + Creates a list of parameters, storing them at self.entry. + """ + + # temporarily replace all commas inside function pointer definition + arg_expr = KernRe(r'(\([^\),]+),') + while arg_expr.search(args): + args = arg_expr.sub(r"\1#", args) + + for arg in args.split(splitter): + # Strip comments + arg = KernRe(r'\/\*.*\*\/').sub('', arg) + + # Ignore argument attributes + arg = KernRe(r'\sPOS0?\s').sub(' ', arg) + + # Strip leading/trailing spaces + arg = arg.strip() + arg = KernRe(r'\s+').sub(' ', arg, count=1) + + if arg.startswith('#'): + # Treat preprocessor directive as a typeless variable just to fill + # corresponding data structures "correctly". Catch it later in + # output_* subs. + + # Treat preprocessor directive as a typeless variable + self.push_parameter(ln, decl_type, arg, "", + "", declaration_name) + + elif KernRe(r'\(.+\)\s*\(').search(arg): + # Pointer-to-function + + arg = arg.replace('#', ',') + + r = KernRe(r'[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)') + if r.match(arg): + param = r.group(1) + else: + self.emit_msg(ln, f"Invalid param: {arg}") + param = arg + + dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) + self.push_parameter(ln, decl_type, param, dtype, + arg, declaration_name) + + elif KernRe(r'\(.+\)\s*\[').search(arg): + # Array-of-pointers + + arg = arg.replace('#', ',') + r = KernRe(r'[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+\s*\]\s*)*\)') + if r.match(arg): + param = r.group(1) + else: + self.emit_msg(ln, f"Invalid param: {arg}") + param = arg + + dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) + + self.push_parameter(ln, decl_type, param, dtype, + arg, declaration_name) + + elif arg: + arg = KernRe(r'\s*:\s*').sub(":", arg) + arg = KernRe(r'\s*\[').sub('[', arg) + + args = KernRe(r'\s*,\s*').split(arg) + if args[0] and '*' in args[0]: + args[0] = re.sub(r'(\*+)\s*', r' \1', args[0]) + + first_arg = [] + r = KernRe(r'^(.*\s+)(.*?\[.*\].*)$') + if args[0] and r.match(args[0]): + args.pop(0) + first_arg.extend(r.group(1)) + first_arg.append(r.group(2)) + else: + first_arg = KernRe(r'\s+').split(args.pop(0)) + + args.insert(0, first_arg.pop()) + dtype = ' '.join(first_arg) + + for param in args: + if KernRe(r'^(\*+)\s*(.*)').match(param): + r = KernRe(r'^(\*+)\s*(.*)') + if not r.match(param): + self.emit_msg(ln, f"Invalid param: {param}") + continue + + param = r.group(1) + + self.push_parameter(ln, decl_type, r.group(2), + f"{dtype} {r.group(1)}", + arg, declaration_name) + + elif KernRe(r'(.*?):(\w+)').search(param): + r = KernRe(r'(.*?):(\w+)') + if not r.match(param): + self.emit_msg(ln, f"Invalid param: {param}") + continue + + if dtype != "": # Skip unnamed bit-fields + self.push_parameter(ln, decl_type, r.group(1), + f"{dtype}:{r.group(2)}", + arg, declaration_name) + else: + self.push_parameter(ln, decl_type, param, dtype, + arg, declaration_name) + + def check_sections(self, ln, decl_name, decl_type): + """ + Check for errors inside sections, emitting warnings if not found + parameters are described. + """ + for section in self.entry.sections: + if section not in self.entry.parameterlist and \ + not known_sections.search(section): + if decl_type == 'function': + dname = f"{decl_type} parameter" + else: + dname = f"{decl_type} member" + self.emit_msg(ln, + f"Excess {dname} '{section}' description in '{decl_name}'") + + def check_return_section(self, ln, declaration_name, return_type): + """ + If the function doesn't return void, warns about the lack of a + return description. + """ + + if not self.config.wreturn: + return + + # Ignore an empty return type (It's a macro) + # Ignore functions with a "void" return type (but not "void *") + if not return_type or KernRe(r'void\s*\w*\s*$').search(return_type): + return + + if not self.entry.sections.get("Return", None): + self.emit_msg(ln, + f"No description found for return value of '{declaration_name}'") + + def dump_struct(self, ln, proto): + """ + Store an entry for an struct or union + """ + + type_pattern = r'(struct|union)' + + qualifiers = [ + "__attribute__", + "__packed", + "__aligned", + "____cacheline_aligned_in_smp", + "____cacheline_aligned", + ] + + definition_body = r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) + ")?" + struct_members = KernRe(type_pattern + r'([^\{\};]+)(\{)([^\{\}]*)(\})([^\{\}\;]*)(\;)') + + # Extract struct/union definition + members = None + declaration_name = None + decl_type = None + + r = KernRe(type_pattern + r'\s+(\w+)\s*' + definition_body) + if r.search(proto): + decl_type = r.group(1) + declaration_name = r.group(2) + members = r.group(3) + else: + r = KernRe(r'typedef\s+' + type_pattern + r'\s*' + definition_body + r'\s*(\w+)\s*;') + + if r.search(proto): + decl_type = r.group(1) + declaration_name = r.group(3) + members = r.group(2) + + if not members: + self.emit_msg(ln, f"{proto} error: Cannot parse struct or union!") + return + + if self.entry.identifier != declaration_name: + self.emit_msg(ln, + f"expecting prototype for {decl_type} {self.entry.identifier}. Prototype was for {decl_type} {declaration_name} instead\n") + return + + args_pattern = r'([^,)]+)' + + sub_prefixes = [ + (KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', re.S | re.I), ''), + (KernRe(r'\/\*\s*private:.*', re.S | re.I), ''), + + # Strip comments + (KernRe(r'\/\*.*?\*\/', re.S), ''), + + # Strip attributes + (attribute, ' '), + (KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '), + (KernRe(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '), + (KernRe(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '), + (KernRe(r'\s*__packed\s*', re.S), ' '), + (KernRe(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '), + (KernRe(r'\s*____cacheline_aligned_in_smp', re.S), ' '), + (KernRe(r'\s*____cacheline_aligned', re.S), ' '), + + # Unwrap struct_group macros based on this definition: + # __struct_group(TAG, NAME, ATTRS, MEMBERS...) + # which has variants like: struct_group(NAME, MEMBERS...) + # Only MEMBERS arguments require documentation. + # + # Parsing them happens on two steps: + # + # 1. drop struct group arguments that aren't at MEMBERS, + # storing them as STRUCT_GROUP(MEMBERS) + # + # 2. remove STRUCT_GROUP() ancillary macro. + # + # The original logic used to remove STRUCT_GROUP() using an + # advanced regex: + # + # \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*; + # + # with two patterns that are incompatible with + # Python re module, as it has: + # + # - a recursive pattern: (?1) + # - an atomic grouping: (?>...) + # + # I tried a simpler version: but it didn't work either: + # \bSTRUCT_GROUP\(([^\)]+)\)[^;]*; + # + # As it doesn't properly match the end parenthesis on some cases. + # + # So, a better solution was crafted: there's now a NestedMatch + # class that ensures that delimiters after a search are properly + # matched. So, the implementation to drop STRUCT_GROUP() will be + # handled in separate. + + (KernRe(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('), + (KernRe(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_GROUP('), + (KernRe(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r'struct \1 \2; STRUCT_GROUP('), + (KernRe(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROUP('), + + # Replace macros + # + # TODO: use NestedMatch for FOO($1, $2, ...) matches + # + # it is better to also move those to the NestedMatch logic, + # to ensure that parenthesis will be properly matched. + + (KernRe(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), + (KernRe(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'), + (KernRe(r'DECLARE_BITMAP\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'), + (KernRe(r'DECLARE_HASHTABLE\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[1 << ((\2) - 1)]'), + (KernRe(r'DECLARE_KFIFO\s*\(' + args_pattern + r',\s*' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), + (KernRe(r'DECLARE_KFIFO_PTR\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), + (KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\1 \2[]'), + (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S), r'dma_addr_t \1'), + (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S), r'__u32 \1'), + (KernRe(r'VIRTIO_DECLARE_FEATURES\s*\(' + args_pattern + r'\)', re.S), r'u64 \1; u64 \1_array[VIRTIO_FEATURES_DWORDS]'), + ] + + # Regexes here are guaranteed to have the end limiter matching + # the start delimiter. Yet, right now, only one replace group + # is allowed. + + sub_nested_prefixes = [ + (re.compile(r'\bSTRUCT_GROUP\('), r'\1'), + ] + + for search, sub in sub_prefixes: + members = search.sub(sub, members) + + nested = NestedMatch() + + for search, sub in sub_nested_prefixes: + members = nested.sub(search, sub, members) + + # Keeps the original declaration as-is + declaration = members + + # Split nested struct/union elements + # + # This loop was simpler at the original kernel-doc perl version, as + # while ($members =~ m/$struct_members/) { ... } + # reads 'members' string on each interaction. + # + # Python behavior is different: it parses 'members' only once, + # creating a list of tuples from the first interaction. + # + # On other words, this won't get nested structs. + # + # So, we need to have an extra loop on Python to override such + # re limitation. + + while True: + tuples = struct_members.findall(members) + if not tuples: + break + + for t in tuples: + newmember = "" + maintype = t[0] + s_ids = t[5] + content = t[3] + + oldmember = "".join(t) + + for s_id in s_ids.split(','): + s_id = s_id.strip() + + newmember += f"{maintype} {s_id}; " + s_id = KernRe(r'[:\[].*').sub('', s_id) + s_id = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', s_id) + + for arg in content.split(';'): + arg = arg.strip() + + if not arg: + continue + + r = KernRe(r'^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)') + if r.match(arg): + # Pointer-to-function + dtype = r.group(1) + name = r.group(2) + extra = r.group(3) + + if not name: + continue + + if not s_id: + # Anonymous struct/union + newmember += f"{dtype}{name}{extra}; " + else: + newmember += f"{dtype}{s_id}.{name}{extra}; " + + else: + arg = arg.strip() + # Handle bitmaps + arg = KernRe(r':\s*\d+\s*').sub('', arg) + + # Handle arrays + arg = KernRe(r'\[.*\]').sub('', arg) + + # Handle multiple IDs + arg = KernRe(r'\s*,\s*').sub(',', arg) + + r = KernRe(r'(.*)\s+([\S+,]+)') + + if r.search(arg): + dtype = r.group(1) + names = r.group(2) + else: + newmember += f"{arg}; " + continue + + for name in names.split(','): + name = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', name).strip() + + if not name: + continue + + if not s_id: + # Anonymous struct/union + newmember += f"{dtype} {name}; " + else: + newmember += f"{dtype} {s_id}.{name}; " + + members = members.replace(oldmember, newmember) + + # Ignore other nested elements, like enums + members = re.sub(r'(\{[^\{\}]*\})', '', members) + + self.create_parameter_list(ln, decl_type, members, ';', + declaration_name) + self.check_sections(ln, declaration_name, decl_type) + + # Adjust declaration for better display + declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration) + declaration = KernRe(r'\}\s+;').sub('};', declaration) + + # Better handle inlined enums + while True: + r = KernRe(r'(enum\s+\{[^\}]+),([^\n])') + if not r.search(declaration): + break + + declaration = r.sub(r'\1,\n\2', declaration) + + def_args = declaration.split('\n') + level = 1 + declaration = "" + for clause in def_args: + + clause = clause.strip() + clause = KernRe(r'\s+').sub(' ', clause, count=1) + + if not clause: + continue + + if '}' in clause and level > 1: + level -= 1 + + if not KernRe(r'^\s*#').match(clause): + declaration += "\t" * level + + declaration += "\t" + clause + "\n" + if "{" in clause and "}" not in clause: + level += 1 + + self.output_declaration(decl_type, declaration_name, + definition=declaration, + purpose=self.entry.declaration_purpose) + + def dump_enum(self, ln, proto): + """ + Stores an enum inside self.entries array. + """ + + # Ignore members marked private + proto = KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', flags=re.S).sub('', proto) + proto = KernRe(r'\/\*\s*private:.*}', flags=re.S).sub('}', proto) + + # Strip comments + proto = KernRe(r'\/\*.*?\*\/', flags=re.S).sub('', proto) + + # Strip #define macros inside enums + proto = KernRe(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto) + + # + # Parse out the name and members of the enum. Typedef form first. + # + r = KernRe(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;') + if r.search(proto): + declaration_name = r.group(2) + members = r.group(1).rstrip() + # + # Failing that, look for a straight enum + # + else: + r = KernRe(r'enum\s+(\w*)\s*\{(.*)\}') + if r.match(proto): + declaration_name = r.group(1) + members = r.group(2).rstrip() + # + # OK, this isn't going to work. + # + else: + self.emit_msg(ln, f"{proto}: error: Cannot parse enum!") + return + # + # Make sure we found what we were expecting. + # + if self.entry.identifier != declaration_name: + if self.entry.identifier == "": + self.emit_msg(ln, + f"{proto}: wrong kernel-doc identifier on prototype") + else: + self.emit_msg(ln, + f"expecting prototype for enum {self.entry.identifier}. " + f"Prototype was for enum {declaration_name} instead") + return + + if not declaration_name: + declaration_name = "(anonymous)" + # + # Parse out the name of each enum member, and verify that we + # have a description for it. + # + member_set = set() + members = KernRe(r'\([^;)]*\)').sub('', members) + for arg in members.split(','): + if not arg: + continue + arg = KernRe(r'^\s*(\w+).*').sub(r'\1', arg) + self.entry.parameterlist.append(arg) + if arg not in self.entry.parameterdescs: + self.entry.parameterdescs[arg] = self.undescribed + self.emit_msg(ln, + f"Enum value '{arg}' not described in enum '{declaration_name}'") + member_set.add(arg) + # + # Ensure that every described member actually exists in the enum. + # + for k in self.entry.parameterdescs: + if k not in member_set: + self.emit_msg(ln, + f"Excess enum value '%{k}' description in '{declaration_name}'") + + self.output_declaration('enum', declaration_name, + purpose=self.entry.declaration_purpose) + + def dump_declaration(self, ln, prototype): + """ + Stores a data declaration inside self.entries array. + """ + + if self.entry.decl_type == "enum": + self.dump_enum(ln, prototype) + elif self.entry.decl_type == "typedef": + self.dump_typedef(ln, prototype) + elif self.entry.decl_type in ["union", "struct"]: + self.dump_struct(ln, prototype) + else: + # This would be a bug + self.emit_message(ln, f'Unknown declaration type: {self.entry.decl_type}') + + def dump_function(self, ln, prototype): + """ + Stores a function of function macro inside self.entries array. + """ + + func_macro = False + return_type = '' + decl_type = 'function' + + # Prefixes that would be removed + sub_prefixes = [ + (r"^static +", "", 0), + (r"^extern +", "", 0), + (r"^asmlinkage +", "", 0), + (r"^inline +", "", 0), + (r"^__inline__ +", "", 0), + (r"^__inline +", "", 0), + (r"^__always_inline +", "", 0), + (r"^noinline +", "", 0), + (r"^__FORTIFY_INLINE +", "", 0), + (r"__init +", "", 0), + (r"__init_or_module +", "", 0), + (r"__deprecated +", "", 0), + (r"__flatten +", "", 0), + (r"__meminit +", "", 0), + (r"__must_check +", "", 0), + (r"__weak +", "", 0), + (r"__sched +", "", 0), + (r"_noprof", "", 0), + (r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +", "", 0), + (r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +", "", 0), + (r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +", "", 0), + (r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)", r"\1, \2", 0), + (r"__attribute_const__ +", "", 0), + + # It seems that Python support for re.X is broken: + # At least for me (Python 3.13), this didn't work +# (r""" +# __attribute__\s*\(\( +# (?: +# [\w\s]+ # attribute name +# (?:\([^)]*\))? # attribute arguments +# \s*,? # optional comma at the end +# )+ +# \)\)\s+ +# """, "", re.X), + + # So, remove whitespaces and comments from it + (r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+", "", 0), + ] + + for search, sub, flags in sub_prefixes: + prototype = KernRe(search, flags).sub(sub, prototype) + + # Macros are a special case, as they change the prototype format + new_proto = KernRe(r"^#\s*define\s+").sub("", prototype) + if new_proto != prototype: + is_define_proto = True + prototype = new_proto + else: + is_define_proto = False + + # Yes, this truly is vile. We are looking for: + # 1. Return type (may be nothing if we're looking at a macro) + # 2. Function name + # 3. Function parameters. + # + # All the while we have to watch out for function pointer parameters + # (which IIRC is what the two sections are for), C types (these + # regexps don't even start to express all the possibilities), and + # so on. + # + # If you mess with these regexps, it's a good idea to check that + # the following functions' documentation still comes out right: + # - parport_register_device (function pointer parameters) + # - atomic_set (macro) + # - pci_match_device, __copy_to_user (long return type) + + name = r'[a-zA-Z0-9_~:]+' + prototype_end1 = r'[^\(]*' + prototype_end2 = r'[^\{]*' + prototype_end = fr'\(({prototype_end1}|{prototype_end2})\)' + + # Besides compiling, Perl qr{[\w\s]+} works as a non-capturing group. + # So, this needs to be mapped in Python with (?:...)? or (?:...)+ + + type1 = r'(?:[\w\s]+)?' + type2 = r'(?:[\w\s]+\*+)+' + + found = False + + if is_define_proto: + r = KernRe(r'^()(' + name + r')\s+') + + if r.search(prototype): + return_type = '' + declaration_name = r.group(2) + func_macro = True + + found = True + + if not found: + patterns = [ + rf'^()({name})\s*{prototype_end}', + rf'^({type1})\s+({name})\s*{prototype_end}', + rf'^({type2})\s*({name})\s*{prototype_end}', + ] + + for p in patterns: + r = KernRe(p) + + if r.match(prototype): + + return_type = r.group(1) + declaration_name = r.group(2) + args = r.group(3) + + self.create_parameter_list(ln, decl_type, args, ',', + declaration_name) + + found = True + break + if not found: + self.emit_msg(ln, + f"cannot understand function prototype: '{prototype}'") + return + + if self.entry.identifier != declaration_name: + self.emit_msg(ln, + f"expecting prototype for {self.entry.identifier}(). Prototype was for {declaration_name}() instead") + return + + self.check_sections(ln, declaration_name, "function") + + self.check_return_section(ln, declaration_name, return_type) + + if 'typedef' in return_type: + self.output_declaration(decl_type, declaration_name, + typedef=True, + functiontype=return_type, + purpose=self.entry.declaration_purpose, + func_macro=func_macro) + else: + self.output_declaration(decl_type, declaration_name, + typedef=False, + functiontype=return_type, + purpose=self.entry.declaration_purpose, + func_macro=func_macro) + + def dump_typedef(self, ln, proto): + """ + Stores a typedef inside self.entries array. + """ + + typedef_type = r'((?:\s+[\w\*]+\b){0,7}\s+(?:\w+\b|\*+))\s*' + typedef_ident = r'\*?\s*(\w\S+)\s*' + typedef_args = r'\s*\((.*)\);' + + typedef1 = KernRe(r'typedef' + typedef_type + r'\(' + typedef_ident + r'\)' + typedef_args) + typedef2 = KernRe(r'typedef' + typedef_type + typedef_ident + typedef_args) + + # Strip comments + proto = KernRe(r'/\*.*?\*/', flags=re.S).sub('', proto) + + # Parse function typedef prototypes + for r in [typedef1, typedef2]: + if not r.match(proto): + continue + + return_type = r.group(1).strip() + declaration_name = r.group(2) + args = r.group(3) + + if self.entry.identifier != declaration_name: + self.emit_msg(ln, + f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n") + return + + decl_type = 'function' + self.create_parameter_list(ln, decl_type, args, ',', declaration_name) + + self.output_declaration(decl_type, declaration_name, + typedef=True, + functiontype=return_type, + purpose=self.entry.declaration_purpose) + return + + # Handle nested parentheses or brackets + r = KernRe(r'(\(*.\)\s*|\[*.\]\s*);$') + while r.search(proto): + proto = r.sub('', proto) + + # Parse simple typedefs + r = KernRe(r'typedef.*\s+(\w+)\s*;') + if r.match(proto): + declaration_name = r.group(1) + + if self.entry.identifier != declaration_name: + self.emit_msg(ln, + f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n") + return + + self.output_declaration('typedef', declaration_name, + purpose=self.entry.declaration_purpose) + return + + self.emit_msg(ln, "error: Cannot parse typedef!") + + @staticmethod + def process_export(function_set, line): + """ + process EXPORT_SYMBOL* tags + + This method doesn't use any variable from the class, so declare it + with a staticmethod decorator. + """ + + # We support documenting some exported symbols with different + # names. A horrible hack. + suffixes = [ '_noprof' ] + + # Note: it accepts only one EXPORT_SYMBOL* per line, as having + # multiple export lines would violate Kernel coding style. + + if export_symbol.search(line): + symbol = export_symbol.group(2) + elif export_symbol_ns.search(line): + symbol = export_symbol_ns.group(2) + else: + return False + # + # Found an export, trim out any special suffixes + # + for suffix in suffixes: + # Be backward compatible with Python < 3.9 + if symbol.endswith(suffix): + symbol = symbol[:-len(suffix)] + function_set.add(symbol) + return True + + def process_normal(self, ln, line): + """ + STATE_NORMAL: looking for the /** to begin everything. + """ + + if not doc_start.match(line): + return + + # start a new entry + self.reset_state(ln) + + # next line is always the function name + self.state = state.NAME + + def process_name(self, ln, line): + """ + STATE_NAME: Looking for the "name - description" line + """ + # + # Check for a DOC: block and handle them specially. + # + if doc_block.search(line): + + if not doc_block.group(1): + self.entry.begin_section(ln, "Introduction") + else: + self.entry.begin_section(ln, doc_block.group(1)) + + self.entry.identifier = self.entry.section + self.state = state.DOCBLOCK + # + # Otherwise we're looking for a normal kerneldoc declaration line. + # + elif doc_decl.search(line): + self.entry.identifier = doc_decl.group(1) + + # Test for data declaration + if doc_begin_data.search(line): + self.entry.decl_type = doc_begin_data.group(1) + self.entry.identifier = doc_begin_data.group(2) + # + # Look for a function description + # + elif doc_begin_func.search(line): + self.entry.identifier = doc_begin_func.group(1) + self.entry.decl_type = "function" + # + # We struck out. + # + else: + self.emit_msg(ln, + f"This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{line}") + self.state = state.NORMAL + return + # + # OK, set up for a new kerneldoc entry. + # + self.state = state.BODY + self.entry.identifier = self.entry.identifier.strip(" ") + # if there's no @param blocks need to set up default section here + self.entry.begin_section(ln + 1) + # + # Find the description portion, which *should* be there but + # isn't always. + # (We should be able to capture this from the previous parsing - someday) + # + r = KernRe("[-:](.*)") + if r.search(line): + self.entry.declaration_purpose = trim_whitespace(r.group(1)) + self.state = state.DECLARATION + else: + self.entry.declaration_purpose = "" + + if not self.entry.declaration_purpose and self.config.wshort_desc: + self.emit_msg(ln, + f"missing initial short description on line:\n{line}") + + if not self.entry.identifier and self.entry.decl_type != "enum": + self.emit_msg(ln, + f"wrong kernel-doc identifier on line:\n{line}") + self.state = state.NORMAL + + if self.config.verbose: + self.emit_msg(ln, + f"Scanning doc for {self.entry.decl_type} {self.entry.identifier}", + warning=False) + # + # Failed to find an identifier. Emit a warning + # + else: + self.emit_msg(ln, f"Cannot find identifier on line:\n{line}") + + # + # Helper function to determine if a new section is being started. + # + def is_new_section(self, ln, line): + if doc_sect.search(line): + self.state = state.BODY + # + # Pick out the name of our new section, tweaking it if need be. + # + newsection = doc_sect.group(1) + if newsection.lower() == 'description': + newsection = 'Description' + elif newsection.lower() == 'context': + newsection = 'Context' + self.state = state.SPECIAL_SECTION + elif newsection.lower() in ["@return", "@returns", + "return", "returns"]: + newsection = "Return" + self.state = state.SPECIAL_SECTION + elif newsection[0] == '@': + self.state = state.SPECIAL_SECTION + # + # Initialize the contents, and get the new section going. + # + newcontents = doc_sect.group(2) + if not newcontents: + newcontents = "" + self.dump_section() + self.entry.begin_section(ln, newsection) + self.entry.leading_space = None + + self.entry.add_text(newcontents.lstrip()) + return True + return False + + # + # Helper function to detect (and effect) the end of a kerneldoc comment. + # + def is_comment_end(self, ln, line): + if doc_end.search(line): + self.dump_section() + + # Look for doc_com + + doc_end: + r = KernRe(r'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') + if r.match(line): + self.emit_msg(ln, f"suspicious ending line: {line}") + + self.entry.prototype = "" + self.entry.new_start_line = ln + 1 + + self.state = state.PROTO + return True + return False + + + def process_decl(self, ln, line): + """ + STATE_DECLARATION: We've seen the beginning of a declaration + """ + if self.is_new_section(ln, line) or self.is_comment_end(ln, line): + return + # + # Look for anything with the " * " line beginning. + # + if doc_content.search(line): + cont = doc_content.group(1) + # + # A blank line means that we have moved out of the declaration + # part of the comment (without any "special section" parameter + # descriptions). + # + if cont == "": + self.state = state.BODY + # + # Otherwise we have more of the declaration section to soak up. + # + else: + self.entry.declaration_purpose = \ + trim_whitespace(self.entry.declaration_purpose + ' ' + cont) + else: + # Unknown line, ignore + self.emit_msg(ln, f"bad line: {line}") + + + def process_special(self, ln, line): + """ + STATE_SPECIAL_SECTION: a section ending with a blank line + """ + # + # If we have hit a blank line (only the " * " marker), then this + # section is done. + # + if KernRe(r"\s*\*\s*$").match(line): + self.entry.begin_section(ln, dump = True) + self.state = state.BODY + return + # + # Not a blank line, look for the other ways to end the section. + # + if self.is_new_section(ln, line) or self.is_comment_end(ln, line): + return + # + # OK, we should have a continuation of the text for this section. + # + if doc_content.search(line): + cont = doc_content.group(1) + # + # If the lines of text after the first in a special section have + # leading white space, we need to trim it out or Sphinx will get + # confused. For the second line (the None case), see what we + # find there and remember it. + # + if self.entry.leading_space is None: + r = KernRe(r'^(\s+)') + if r.match(cont): + self.entry.leading_space = len(r.group(1)) + else: + self.entry.leading_space = 0 + # + # Otherwise, before trimming any leading chars, be *sure* + # that they are white space. We should maybe warn if this + # isn't the case. + # + for i in range(0, self.entry.leading_space): + if cont[i] != " ": + self.entry.leading_space = i + break + # + # Add the trimmed result to the section and we're done. + # + self.entry.add_text(cont[self.entry.leading_space:]) + else: + # Unknown line, ignore + self.emit_msg(ln, f"bad line: {line}") + + def process_body(self, ln, line): + """ + STATE_BODY: the bulk of a kerneldoc comment. + """ + if self.is_new_section(ln, line) or self.is_comment_end(ln, line): + return + + if doc_content.search(line): + cont = doc_content.group(1) + self.entry.add_text(cont) + else: + # Unknown line, ignore + self.emit_msg(ln, f"bad line: {line}") + + def process_inline_name(self, ln, line): + """STATE_INLINE_NAME: beginning of docbook comments within a prototype.""" + + if doc_inline_sect.search(line): + self.entry.begin_section(ln, doc_inline_sect.group(1)) + self.entry.add_text(doc_inline_sect.group(2).lstrip()) + self.state = state.INLINE_TEXT + elif doc_inline_end.search(line): + self.dump_section() + self.state = state.PROTO + elif doc_content.search(line): + self.emit_msg(ln, f"Incorrect use of kernel-doc format: {line}") + self.state = state.PROTO + # else ... ?? + + def process_inline_text(self, ln, line): + """STATE_INLINE_TEXT: docbook comments within a prototype.""" + + if doc_inline_end.search(line): + self.dump_section() + self.state = state.PROTO + elif doc_content.search(line): + self.entry.add_text(doc_content.group(1)) + # else ... ?? + + def syscall_munge(self, ln, proto): # pylint: disable=W0613 + """ + Handle syscall definitions + """ + + is_void = False + + # Strip newlines/CR's + proto = re.sub(r'[\r\n]+', ' ', proto) + + # Check if it's a SYSCALL_DEFINE0 + if 'SYSCALL_DEFINE0' in proto: + is_void = True + + # Replace SYSCALL_DEFINE with correct return type & function name + proto = KernRe(r'SYSCALL_DEFINE.*\(').sub('long sys_', proto) + + r = KernRe(r'long\s+(sys_.*?),') + if r.search(proto): + proto = KernRe(',').sub('(', proto, count=1) + elif is_void: + proto = KernRe(r'\)').sub('(void)', proto, count=1) + + # Now delete all of the odd-numbered commas in the proto + # so that argument types & names don't have a comma between them + count = 0 + length = len(proto) + + if is_void: + length = 0 # skip the loop if is_void + + for ix in range(length): + if proto[ix] == ',': + count += 1 + if count % 2 == 1: + proto = proto[:ix] + ' ' + proto[ix + 1:] + + return proto + + def tracepoint_munge(self, ln, proto): + """ + Handle tracepoint definitions + """ + + tracepointname = None + tracepointargs = None + + # Match tracepoint name based on different patterns + r = KernRe(r'TRACE_EVENT\((.*?),') + if r.search(proto): + tracepointname = r.group(1) + + r = KernRe(r'DEFINE_SINGLE_EVENT\((.*?),') + if r.search(proto): + tracepointname = r.group(1) + + r = KernRe(r'DEFINE_EVENT\((.*?),(.*?),') + if r.search(proto): + tracepointname = r.group(2) + + if tracepointname: + tracepointname = tracepointname.lstrip() + + r = KernRe(r'TP_PROTO\((.*?)\)') + if r.search(proto): + tracepointargs = r.group(1) + + if not tracepointname or not tracepointargs: + self.emit_msg(ln, + f"Unrecognized tracepoint format:\n{proto}\n") + else: + proto = f"static inline void trace_{tracepointname}({tracepointargs})" + self.entry.identifier = f"trace_{self.entry.identifier}" + + return proto + + def process_proto_function(self, ln, line): + """Ancillary routine to process a function prototype""" + + # strip C99-style comments to end of line + line = KernRe(r"\/\/.*$", re.S).sub('', line) + # + # Soak up the line's worth of prototype text, stopping at { or ; if present. + # + if KernRe(r'\s*#\s*define').match(line): + self.entry.prototype = line + elif not line.startswith('#'): # skip other preprocessor stuff + r = KernRe(r'([^\{]*)') + if r.match(line): + self.entry.prototype += r.group(1) + " " + # + # If we now have the whole prototype, clean it up and declare victory. + # + if '{' in line or ';' in line or KernRe(r'\s*#\s*define').match(line): + # strip comments and surrounding spaces + self.entry.prototype = KernRe(r'/\*.*\*/').sub('', self.entry.prototype).strip() + # + # Handle self.entry.prototypes for function pointers like: + # int (*pcs_config)(struct foo) + # by turning it into + # int pcs_config(struct foo) + # + r = KernRe(r'^(\S+\s+)\(\s*\*(\S+)\)') + self.entry.prototype = r.sub(r'\1\2', self.entry.prototype) + # + # Handle special declaration syntaxes + # + if 'SYSCALL_DEFINE' in self.entry.prototype: + self.entry.prototype = self.syscall_munge(ln, + self.entry.prototype) + else: + r = KernRe(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT') + if r.search(self.entry.prototype): + self.entry.prototype = self.tracepoint_munge(ln, + self.entry.prototype) + # + # ... and we're done + # + self.dump_function(ln, self.entry.prototype) + self.reset_state(ln) + + def process_proto_type(self, ln, line): + """Ancillary routine to process a type""" + + # Strip C99-style comments and surrounding whitespace + line = KernRe(r"//.*$", re.S).sub('', line).strip() + if not line: + return # nothing to see here + + # To distinguish preprocessor directive from regular declaration later. + if line.startswith('#'): + line += ";" + # + # Split the declaration on any of { } or ;, and accumulate pieces + # until we hit a semicolon while not inside {brackets} + # + r = KernRe(r'(.*?)([{};])') + for chunk in r.split(line): + if chunk: # Ignore empty matches + self.entry.prototype += chunk + # + # This cries out for a match statement ... someday after we can + # drop Python 3.9 ... + # + if chunk == '{': + self.entry.brcount += 1 + elif chunk == '}': + self.entry.brcount -= 1 + elif chunk == ';' and self.entry.brcount <= 0: + self.dump_declaration(ln, self.entry.prototype) + self.reset_state(ln) + return + # + # We hit the end of the line while still in the declaration; put + # in a space to represent the newline. + # + self.entry.prototype += ' ' + + def process_proto(self, ln, line): + """STATE_PROTO: reading a function/whatever prototype.""" + + if doc_inline_oneline.search(line): + self.entry.begin_section(ln, doc_inline_oneline.group(1)) + self.entry.add_text(doc_inline_oneline.group(2)) + self.dump_section() + + elif doc_inline_start.search(line): + self.state = state.INLINE_NAME + + elif self.entry.decl_type == 'function': + self.process_proto_function(ln, line) + + else: + self.process_proto_type(ln, line) + + def process_docblock(self, ln, line): + """STATE_DOCBLOCK: within a DOC: block.""" + + if doc_end.search(line): + self.dump_section() + self.output_declaration("doc", self.entry.identifier) + self.reset_state(ln) + + elif doc_content.search(line): + self.entry.add_text(doc_content.group(1)) + + def parse_export(self): + """ + Parses EXPORT_SYMBOL* macros from a single Kernel source file. + """ + + export_table = set() + + try: + with open(self.fname, "r", encoding="utf8", + errors="backslashreplace") as fp: + + for line in fp: + self.process_export(export_table, line) + + except IOError: + return None + + return export_table + + # + # The state/action table telling us which function to invoke in + # each state. + # + state_actions = { + state.NORMAL: process_normal, + state.NAME: process_name, + state.BODY: process_body, + state.DECLARATION: process_decl, + state.SPECIAL_SECTION: process_special, + state.INLINE_NAME: process_inline_name, + state.INLINE_TEXT: process_inline_text, + state.PROTO: process_proto, + state.DOCBLOCK: process_docblock, + } + + def parse_kdoc(self): + """ + Open and process each line of a C source file. + The parsing is controlled via a state machine, and the line is passed + to a different process function depending on the state. The process + function may update the state as needed. + + Besides parsing kernel-doc tags, it also parses export symbols. + """ + + prev = "" + prev_ln = None + export_table = set() + + try: + with open(self.fname, "r", encoding="utf8", + errors="backslashreplace") as fp: + for ln, line in enumerate(fp): + + line = line.expandtabs().strip("\n") + + # Group continuation lines on prototypes + if self.state == state.PROTO: + if line.endswith("\\"): + prev += line.rstrip("\\") + if not prev_ln: + prev_ln = ln + continue + + if prev: + ln = prev_ln + line = prev + line + prev = "" + prev_ln = None + + self.config.log.debug("%d %s: %s", + ln, state.name[self.state], + line) + + # This is an optimization over the original script. + # There, when export_file was used for the same file, + # it was read twice. Here, we use the already-existing + # loop to parse exported symbols as well. + # + if (self.state != state.NORMAL) or \ + not self.process_export(export_table, line): + # Hand this line to the appropriate state handler + self.state_actions[self.state](self, ln, line) + + except OSError: + self.config.log.error(f"Error: Cannot open file {self.fname}") + + return export_table, self.entries diff --git a/scripts/lib/kdoc/kdoc_re.py b/scripts/lib/kdoc/kdoc_re.py new file mode 100644 index 0000000000000..612223e1e7238 --- /dev/null +++ b/scripts/lib/kdoc/kdoc_re.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab . + +""" +Regular expression ancillary classes. + +Those help caching regular expressions and do matching for kernel-doc. +""" + +import re + +# Local cache for regular expressions +re_cache = {} + + +class KernRe: + """ + Helper class to simplify regex declaration and usage, + + It calls re.compile for a given pattern. It also allows adding + regular expressions and define sub at class init time. + + Regular expressions can be cached via an argument, helping to speedup + searches. + """ + + def _add_regex(self, string, flags): + """ + Adds a new regex or re-use it from the cache. + """ + self.regex = re_cache.get(string, None) + if not self.regex: + self.regex = re.compile(string, flags=flags) + if self.cache: + re_cache[string] = self.regex + + def __init__(self, string, cache=True, flags=0): + """ + Compile a regular expression and initialize internal vars. + """ + + self.cache = cache + self.last_match = None + + self._add_regex(string, flags) + + def __str__(self): + """ + Return the regular expression pattern. + """ + return self.regex.pattern + + def __add__(self, other): + """ + Allows adding two regular expressions into one. + """ + + return KernRe(str(self) + str(other), cache=self.cache or other.cache, + flags=self.regex.flags | other.regex.flags) + + def match(self, string): + """ + Handles a re.match storing its results + """ + + self.last_match = self.regex.match(string) + return self.last_match + + def search(self, string): + """ + Handles a re.search storing its results + """ + + self.last_match = self.regex.search(string) + return self.last_match + + def findall(self, string): + """ + Alias to re.findall + """ + + return self.regex.findall(string) + + def split(self, string): + """ + Alias to re.split + """ + + return self.regex.split(string) + + def sub(self, sub, string, count=0): + """ + Alias to re.sub + """ + + return self.regex.sub(sub, string, count=count) + + def group(self, num): + """ + Returns the group results of the last match + """ + + return self.last_match.group(num) + + +class NestedMatch: + """ + Finding nested delimiters is hard with regular expressions. It is + even harder on Python with its normal re module, as there are several + advanced regular expressions that are missing. + + This is the case of this pattern: + + '\\bSTRUCT_GROUP(\\(((?:(?>[^)(]+)|(?1))*)\\))[^;]*;' + + which is used to properly match open/close parenthesis of the + string search STRUCT_GROUP(), + + Add a class that counts pairs of delimiters, using it to match and + replace nested expressions. + + The original approach was suggested by: + https://stackoverflow.com/questions/5454322/python-how-to-match-nested-parentheses-with-regex + + Although I re-implemented it to make it more generic and match 3 types + of delimiters. The logic checks if delimiters are paired. If not, it + will ignore the search string. + """ + + # TODO: make NestedMatch handle multiple match groups + # + # Right now, regular expressions to match it are defined only up to + # the start delimiter, e.g.: + # + # \bSTRUCT_GROUP\( + # + # is similar to: STRUCT_GROUP\((.*)\) + # except that the content inside the match group is delimiter's aligned. + # + # The content inside parenthesis are converted into a single replace + # group (e.g. r`\1'). + # + # It would be nice to change such definition to support multiple + # match groups, allowing a regex equivalent to. + # + # FOO\((.*), (.*), (.*)\) + # + # it is probably easier to define it not as a regular expression, but + # with some lexical definition like: + # + # FOO(arg1, arg2, arg3) + + DELIMITER_PAIRS = { + '{': '}', + '(': ')', + '[': ']', + } + + RE_DELIM = re.compile(r'[\{\}\[\]\(\)]') + + def _search(self, regex, line): + """ + Finds paired blocks for a regex that ends with a delimiter. + + The suggestion of using finditer to match pairs came from: + https://stackoverflow.com/questions/5454322/python-how-to-match-nested-parentheses-with-regex + but I ended using a different implementation to align all three types + of delimiters and seek for an initial regular expression. + + The algorithm seeks for open/close paired delimiters and place them + into a stack, yielding a start/stop position of each match when the + stack is zeroed. + + The algorithm shoud work fine for properly paired lines, but will + silently ignore end delimiters that preceeds an start delimiter. + This should be OK for kernel-doc parser, as unaligned delimiters + would cause compilation errors. So, we don't need to rise exceptions + to cover such issues. + """ + + stack = [] + + for match_re in regex.finditer(line): + start = match_re.start() + offset = match_re.end() + + d = line[offset - 1] + if d not in self.DELIMITER_PAIRS: + continue + + end = self.DELIMITER_PAIRS[d] + stack.append(end) + + for match in self.RE_DELIM.finditer(line[offset:]): + pos = match.start() + offset + + d = line[pos] + + if d in self.DELIMITER_PAIRS: + end = self.DELIMITER_PAIRS[d] + + stack.append(end) + continue + + # Does the end delimiter match what it is expected? + if stack and d == stack[-1]: + stack.pop() + + if not stack: + yield start, offset, pos + 1 + break + + def search(self, regex, line): + """ + This is similar to re.search: + + It matches a regex that it is followed by a delimiter, + returning occurrences only if all delimiters are paired. + """ + + for t in self._search(regex, line): + + yield line[t[0]:t[2]] + + def sub(self, regex, sub, line, count=0): + """ + This is similar to re.sub: + + It matches a regex that it is followed by a delimiter, + replacing occurrences only if all delimiters are paired. + + if r'\1' is used, it works just like re: it places there the + matched paired data with the delimiter stripped. + + If count is different than zero, it will replace at most count + items. + """ + out = "" + + cur_pos = 0 + n = 0 + + for start, end, pos in self._search(regex, line): + out += line[cur_pos:start] + + # Value, ignoring start/end delimiters + value = line[end:pos - 1] + + # replaces \1 at the sub string, if \1 is used there + new_sub = sub + new_sub = new_sub.replace(r'\1', value) + + out += new_sub + + # Drop end ';' if any + if line[pos] == ';': + pos += 1 + + cur_pos = pos + n += 1 + + if count and count >= n: + break + + # Append the remaining string + l = len(line) + out += line[cur_pos:l] + + return out From 7f58572cff7e79c7733ba504cd200af2287706f5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 14 Aug 2025 18:13:19 +0100 Subject: [PATCH 0504/1794] scripts/kernel-doc: strip QEMU_ from function definitions This commit is the Python version of our older commit b30df2751e5 ("scripts/kernel-doc: strip QEMU_ from function definitions"). Some versions of Sphinx get confused if function attributes are left on the C code from kernel-doc; strip out any QEMU_* prefixes from function prototypes. Signed-off-by: Peter Maydell Reviewed-by: Paolo Bonzini Reviewed-by: Mauro Carvalho Chehab Message-id: 20250814171324.1614516-5-peter.maydell@linaro.org --- scripts/lib/kdoc/kdoc_parser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index fe730099eca86..32b43562929b7 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -907,6 +907,7 @@ def dump_function(self, ln, prototype): (r"^__always_inline +", "", 0), (r"^noinline +", "", 0), (r"^__FORTIFY_INLINE +", "", 0), + (r"QEMU_[A-Z_]+ +", "", 0), (r"__init +", "", 0), (r"__init_or_module +", "", 0), (r"__deprecated +", "", 0), From 9cbe72b868b7cb04f7553220738f1286b6f0dc32 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 14 Aug 2025 18:13:20 +0100 Subject: [PATCH 0505/1794] scripts/kernel-doc: tweak for QEMU coding standards This commit makes the equivalent changes to the Python script that we had for the old Perl script in commit 4cf41794411f ("docs: tweak kernel-doc for QEMU coding standards"). To repeat the rationale from that commit: Surprisingly, QEMU does have a pretty consistent doc comment style and it is not very different from the Linux kernel's. Of the documentation "sigils", only "#" separates the QEMU doc comment style from Linux's, and it has 200+ instances vs. 6 for the kernel's '&struct foo' (all in accel/tcg/translate-all.c), so it's clear that the two standards are different in this respect. In addition, our structs are typedefed and recognized by CamelCase names. Note that in 4cf41794411f we used '(?!)' as our type_fallback regex; this is strictly not quite a replacement for the upstream '\&([_\w]+)', because the latter includes a group that can later be matched with \1, and the former does not. The old perl script did not care about this, but the python version does, so we must include the extra set of brackets to ensure we have a group. This commit does not include all the same changes that 4cf41794411f did. Of the missing pieces, some had already gone in an earlier kernel-doc update; the parts we still had but do not include here are: @@ -2057,7 +2060,7 @@ } elsif (/$doc_decl/o) { $identifier = $1; - if (/\s*([\w\s]+?)(\(\))?\s*-/) { + if (/\s*([\w\s]+?)(\s*-|:)/) { $identifier = $1; } @@ -2067,7 +2070,7 @@ $contents = ""; $section = $section_default; $new_start_line = $. + 1; - if (/-(.*)/) { + if (/[-:](.*)/) { # strip leading/trailing/multiple spaces $descr= $1; $descr =~ s/^\s*//; The second of these is already in the upstream version: the line r = KernRe("[-:](.*)") in process_name() matches the regex we have. The first change has been refactored into the doc_begin_data and doc_begin_func changes. Since the output HTML for QEMU's documentation has no relevant changes with the new kerneldoc, we assume that this too has been handled upstream. Signed-off-by: Peter Maydell Reviewed-by: Paolo Bonzini Reviewed-by: Mauro Carvalho Chehab Message-id: 20250814171324.1614516-6-peter.maydell@linaro.org --- scripts/lib/kdoc/kdoc_output.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output.py index ea8914537ba08..39fa872dfca79 100644 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -38,12 +38,12 @@ type_fp_param2 = KernRe(r"\@(\w+->\S+)\(\)", cache=False) type_env = KernRe(r"(\$\w+)", cache=False) -type_enum = KernRe(r"\&(enum\s*([_\w]+))", cache=False) -type_struct = KernRe(r"\&(struct\s*([_\w]+))", cache=False) -type_typedef = KernRe(r"\&(typedef\s*([_\w]+))", cache=False) -type_union = KernRe(r"\&(union\s*([_\w]+))", cache=False) -type_member = KernRe(r"\&([_\w]+)(\.|->)([_\w]+)", cache=False) -type_fallback = KernRe(r"\&([_\w]+)", cache=False) +type_enum = KernRe(r"#(enum\s*([_\w]+))", cache=False) +type_struct = KernRe(r"#(struct\s*([_\w]+))", cache=False) +type_typedef = KernRe(r"#(([A-Z][_\w]*))", cache=False) +type_union = KernRe(r"#(union\s*([_\w]+))", cache=False) +type_member = KernRe(r"#([_\w]+)(\.|->)([_\w]+)", cache=False) +type_fallback = KernRe(r"((?!))", cache=False) # this never matches type_member_func = type_member + KernRe(r"\(\)", cache=False) From 33be8171e2fe02173d24ffd61bd97bf9c2b37834 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 14 Aug 2025 18:13:21 +0100 Subject: [PATCH 0506/1794] scripts/kerneldoc: Switch to the Python kernel-doc script Change the Sphinx config to run the new Python kernel-doc script instead of the Perl one. The only difference between the two is that the new script does not handle the -sphinx-version option, instead assuming that Sphinx is always at least version 3: so we must delete the code that passes that option to avoid the Python script complaining about an unknown option. QEMU's minimum Sphinx version is already 3.4.3, so this doesn't change the set of versions we can handle. Signed-off-by: Peter Maydell Reviewed-by: Paolo Bonzini Reviewed-by: Mauro Carvalho Chehab Message-id: 20250814171324.1614516-7-peter.maydell@linaro.org --- docs/conf.py | 4 +++- docs/sphinx/kerneldoc.py | 5 ----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index f892a6e1da376..e09769e5f83d4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -341,7 +341,9 @@ # We use paths starting from qemu_docdir here so that you can run # sphinx-build from anywhere and the kerneldoc extension can still # find everything. -kerneldoc_bin = ['perl', os.path.join(qemu_docdir, '../scripts/kernel-doc')] +# Since kernel-doc is now a Python script, we should run it with whatever +# Python this sphinx is using (rather than letting it find one via env) +kerneldoc_bin = [sys.executable, os.path.join(qemu_docdir, '../scripts/kernel-doc.py')] kerneldoc_srctree = os.path.join(qemu_docdir, '..') hxtool_srctree = os.path.join(qemu_docdir, '..') qapidoc_srctree = os.path.join(qemu_docdir, '..') diff --git a/docs/sphinx/kerneldoc.py b/docs/sphinx/kerneldoc.py index 30bb343198387..9721072e47685 100644 --- a/docs/sphinx/kerneldoc.py +++ b/docs/sphinx/kerneldoc.py @@ -63,11 +63,6 @@ def run(self): env = self.state.document.settings.env cmd = env.config.kerneldoc_bin + ['-rst', '-enable-lineno'] - # Pass the version string to kernel-doc, as it needs to use a different - # dialect, depending what the C domain supports for each specific - # Sphinx versions - cmd += ['-sphinx-version', sphinx.__version__] - # Pass through the warnings-as-errors flag if env.config.kerneldoc_werror: cmd += ['-Werror'] From 619d5f0211ac69d71291505a8528671ab83764e3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 14 Aug 2025 18:13:22 +0100 Subject: [PATCH 0507/1794] scripts/kernel-doc: Delete the old Perl kernel-doc script We can now delete the old Perl kernel-doc script. For posterity, this is a complete diff of the local changes that we were carrying between the kernel's Perl script as of kernel commit 72b97d0b911872ba (the last time we synced it) and our local copy: --- /tmp/kdoc 2025-08-14 10:42:47.620331939 +0100 +++ scripts/kernel-doc 2025-02-17 10:44:34.528421457 +0000 @@ -1,5 +1,5 @@ #!/usr/bin/env perl -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only use warnings; use strict; @@ -224,12 +224,12 @@ my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params my $type_fp_param2 = '\@(\w+->\S+)\(\)'; # Special RST handling for structs with func ptr params my $type_env = '(\$\w+)'; -my $type_enum = '\&(enum\s*([_\w]+))'; -my $type_struct = '\&(struct\s*([_\w]+))'; -my $type_typedef = '\&(typedef\s*([_\w]+))'; -my $type_union = '\&(union\s*([_\w]+))'; -my $type_member = '\&([_\w]+)(\.|->)([_\w]+)'; -my $type_fallback = '\&([_\w]+)'; +my $type_enum = '#(enum\s*([_\w]+))'; +my $type_struct = '#(struct\s*([_\w]+))'; +my $type_typedef = '#(([A-Z][_\w]*))'; +my $type_union = '#(union\s*([_\w]+))'; +my $type_member = '#([_\w]+)(\.|->)([_\w]+)'; +my $type_fallback = '(?!)'; # this never matches my $type_member_func = $type_member . '\(\)'; # Output conversion substitutions. @@ -1745,6 +1745,9 @@ )+ \)\)\s+//x; + # Strip QEMU specific compiler annotations + $prototype =~ s/QEMU_[A-Z_]+ +//; + # Yes, this truly is vile. We are looking for: # 1. Return type (may be nothing if we're looking at a macro) # 2. Function name @@ -2057,7 +2060,7 @@ } elsif (/$doc_decl/o) { $identifier = $1; - if (/\s*([\w\s]+?)(\(\))?\s*-/) { + if (/\s*([\w\s]+?)(\s*-|:)/) { $identifier = $1; } @@ -2067,7 +2070,7 @@ $contents = ""; $section = $section_default; $new_start_line = $. + 1; - if (/-(.*)/) { + if (/[-:](.*)/) { # strip leading/trailing/multiple spaces $descr= $1; $descr =~ s/^\s*//; These changes correspond to: 06e2329636f license: Update deprecated SPDX tag GPL-2.0 to GPL-2.0-only (a bulk change which we won't bother to re-apply to this third-party script) b30df2751e5 scripts/kernel-doc: strip QEMU_ from function definitions 4cf41794411 docs: tweak kernel-doc for QEMU coding standards We have already applied the equivalent of these changes to the Python code in libs/kdoc/ in the preceding commits. Signed-off-by: Peter Maydell Reviewed-by: Mauro Carvalho Chehab Reviewed-by: Paolo Bonzini --- .editorconfig | 2 +- scripts/kernel-doc | 2441 -------------------------------------------- 2 files changed, 1 insertion(+), 2442 deletions(-) delete mode 100755 scripts/kernel-doc diff --git a/.editorconfig b/.editorconfig index a04cb9054cb0d..258d41ab485ab 100644 --- a/.editorconfig +++ b/.editorconfig @@ -55,7 +55,7 @@ indent_size = 4 emacs_mode = perl # but user kernel "style" for imported scripts -[scripts/{kernel-doc,get_maintainer.pl,checkpatch.pl}] +[scripts/{get_maintainer.pl,checkpatch.pl}] indent_style = tab indent_size = 8 emacs_mode = perl diff --git a/scripts/kernel-doc b/scripts/kernel-doc deleted file mode 100755 index 117ec8fcd1f82..0000000000000 --- a/scripts/kernel-doc +++ /dev/null @@ -1,2441 +0,0 @@ -#!/usr/bin/env perl -# SPDX-License-Identifier: GPL-2.0-only - -use warnings; -use strict; - -## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ## -## Copyright (C) 2000, 1 Tim Waugh ## -## Copyright (C) 2001 Simon Huggins ## -## Copyright (C) 2005-2012 Randy Dunlap ## -## Copyright (C) 2012 Dan Luedtke ## -## ## -## #define enhancements by Armin Kuster ## -## Copyright (c) 2000 MontaVista Software, Inc. ## -## ## -## This software falls under the GNU General Public License. ## -## Please read the COPYING file for more information ## - -# 18/01/2001 - Cleanups -# Functions prototyped as foo(void) same as foo() -# Stop eval'ing where we don't need to. -# -- huggie@earth.li - -# 27/06/2001 - Allowed whitespace after initial "/**" and -# allowed comments before function declarations. -# -- Christian Kreibich - -# Still to do: -# - add perldoc documentation -# - Look more closely at some of the scarier bits :) - -# 26/05/2001 - Support for separate source and object trees. -# Return error code. -# Keith Owens - -# 23/09/2001 - Added support for typedefs, structs, enums and unions -# Support for Context section; can be terminated using empty line -# Small fixes (like spaces vs. \s in regex) -# -- Tim Jansen - -# 25/07/2012 - Added support for HTML5 -# -- Dan Luedtke - -sub usage { - my $message = <<"EOF"; -Usage: $0 [OPTION ...] FILE ... - -Read C language source or header FILEs, extract embedded documentation comments, -and print formatted documentation to standard output. - -The documentation comments are identified by "/**" opening comment mark. See -Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. - -Output format selection (mutually exclusive): - -man Output troff manual page format. This is the default. - -rst Output reStructuredText format. - -none Do not output documentation, only warnings. - -Output format selection modifier (affects only ReST output): - - -sphinx-version Use the ReST C domain dialect compatible with an - specific Sphinx Version. - If not specified, kernel-doc will auto-detect using - the sphinx-build version found on PATH. - -Output selection (mutually exclusive): - -export Only output documentation for symbols that have been - exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() - in any input FILE or -export-file FILE. - -internal Only output documentation for symbols that have NOT been - exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() - in any input FILE or -export-file FILE. - -function NAME Only output documentation for the given function(s) - or DOC: section title(s). All other functions and DOC: - sections are ignored. May be specified multiple times. - -nosymbol NAME Exclude the specified symbols from the output - documentation. May be specified multiple times. - -Output selection modifiers: - -no-doc-sections Do not output DOC: sections. - -enable-lineno Enable output of #define LINENO lines. Only works with - reStructuredText format. - -export-file FILE Specify an additional FILE in which to look for - EXPORT_SYMBOL() and EXPORT_SYMBOL_GPL(). To be used with - -export or -internal. May be specified multiple times. - -Other parameters: - -v Verbose output, more warnings and other information. - -h Print this help. - -Werror Treat warnings as errors. - -EOF - print $message; - exit 1; -} - -# -# format of comments. -# In the following table, (...)? signifies optional structure. -# (...)* signifies 0 or more structure elements -# /** -# * function_name(:)? (- short description)? -# (* @parameterx: (description of parameter x)?)* -# (* a blank line)? -# * (Description:)? (Description of function)? -# * (section header: (section description)? )* -# (*)?*/ -# -# So .. the trivial example would be: -# -# /** -# * my_function -# */ -# -# If the Description: header tag is omitted, then there must be a blank line -# after the last parameter specification. -# e.g. -# /** -# * my_function - does my stuff -# * @my_arg: its mine damnit -# * -# * Does my stuff explained. -# */ -# -# or, could also use: -# /** -# * my_function - does my stuff -# * @my_arg: its mine damnit -# * Description: Does my stuff explained. -# */ -# etc. -# -# Besides functions you can also write documentation for structs, unions, -# enums and typedefs. Instead of the function name you must write the name -# of the declaration; the struct/union/enum/typedef must always precede -# the name. Nesting of declarations is not supported. -# Use the argument mechanism to document members or constants. -# e.g. -# /** -# * struct my_struct - short description -# * @a: first member -# * @b: second member -# * -# * Longer description -# */ -# struct my_struct { -# int a; -# int b; -# /* private: */ -# int c; -# }; -# -# All descriptions can be multiline, except the short function description. -# -# For really longs structs, you can also describe arguments inside the -# body of the struct. -# eg. -# /** -# * struct my_struct - short description -# * @a: first member -# * @b: second member -# * -# * Longer description -# */ -# struct my_struct { -# int a; -# int b; -# /** -# * @c: This is longer description of C -# * -# * You can use paragraphs to describe arguments -# * using this method. -# */ -# int c; -# }; -# -# This should be use only for struct/enum members. -# -# You can also add additional sections. When documenting kernel functions you -# should document the "Context:" of the function, e.g. whether the functions -# can be called form interrupts. Unlike other sections you can end it with an -# empty line. -# A non-void function should have a "Return:" section describing the return -# value(s). -# Example-sections should contain the string EXAMPLE so that they are marked -# appropriately in DocBook. -# -# Example: -# /** -# * user_function - function that can only be called in user context -# * @a: some argument -# * Context: !in_interrupt() -# * -# * Some description -# * Example: -# * user_function(22); -# */ -# ... -# -# -# All descriptive text is further processed, scanning for the following special -# patterns, which are highlighted appropriately. -# -# 'funcname()' - function -# '$ENVVAR' - environmental variable -# '&struct_name' - name of a structure (up to two words including 'struct') -# '&struct_name.member' - name of a structure member -# '@parameter' - name of a parameter -# '%CONST' - name of a constant. -# '``LITERAL``' - literal string without any spaces on it. - -## init lots of data - -my $errors = 0; -my $warnings = 0; -my $anon_struct_union = 0; - -# match expressions used to find embedded type information -my $type_constant = '\b``([^\`]+)``\b'; -my $type_constant2 = '\%([-_\w]+)'; -my $type_func = '(\w+)\(\)'; -my $type_param = '\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)'; -my $type_param_ref = '([\!]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)'; -my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params -my $type_fp_param2 = '\@(\w+->\S+)\(\)'; # Special RST handling for structs with func ptr params -my $type_env = '(\$\w+)'; -my $type_enum = '#(enum\s*([_\w]+))'; -my $type_struct = '#(struct\s*([_\w]+))'; -my $type_typedef = '#(([A-Z][_\w]*))'; -my $type_union = '#(union\s*([_\w]+))'; -my $type_member = '#([_\w]+)(\.|->)([_\w]+)'; -my $type_fallback = '(?!)'; # this never matches -my $type_member_func = $type_member . '\(\)'; - -# Output conversion substitutions. -# One for each output format - -# these are pretty rough -my @highlights_man = ( - [$type_constant, "\$1"], - [$type_constant2, "\$1"], - [$type_func, "\\\\fB\$1\\\\fP"], - [$type_enum, "\\\\fI\$1\\\\fP"], - [$type_struct, "\\\\fI\$1\\\\fP"], - [$type_typedef, "\\\\fI\$1\\\\fP"], - [$type_union, "\\\\fI\$1\\\\fP"], - [$type_param, "\\\\fI\$1\\\\fP"], - [$type_param_ref, "\\\\fI\$1\$2\\\\fP"], - [$type_member, "\\\\fI\$1\$2\$3\\\\fP"], - [$type_fallback, "\\\\fI\$1\\\\fP"] - ); -my $blankline_man = ""; - -# rst-mode -my @highlights_rst = ( - [$type_constant, "``\$1``"], - [$type_constant2, "``\$1``"], - # Note: need to escape () to avoid func matching later - [$type_member_func, "\\:c\\:type\\:`\$1\$2\$3\\\\(\\\\) <\$1>`"], - [$type_member, "\\:c\\:type\\:`\$1\$2\$3 <\$1>`"], - [$type_fp_param, "**\$1\\\\(\\\\)**"], - [$type_fp_param2, "**\$1\\\\(\\\\)**"], - [$type_func, "\$1()"], - [$type_enum, "\\:c\\:type\\:`\$1 <\$2>`"], - [$type_struct, "\\:c\\:type\\:`\$1 <\$2>`"], - [$type_typedef, "\\:c\\:type\\:`\$1 <\$2>`"], - [$type_union, "\\:c\\:type\\:`\$1 <\$2>`"], - # in rst this can refer to any type - [$type_fallback, "\\:c\\:type\\:`\$1`"], - [$type_param_ref, "**\$1\$2**"] - ); -my $blankline_rst = "\n"; - -# read arguments -if ($#ARGV == -1) { - usage(); -} - -my $kernelversion; -my ($sphinx_major, $sphinx_minor, $sphinx_patch); - -my $dohighlight = ""; - -my $verbose = 0; -my $Werror = 0; -my $output_mode = "rst"; -my $output_preformatted = 0; -my $no_doc_sections = 0; -my $enable_lineno = 0; -my @highlights = @highlights_rst; -my $blankline = $blankline_rst; -my $modulename = "Kernel API"; - -use constant { - OUTPUT_ALL => 0, # output all symbols and doc sections - OUTPUT_INCLUDE => 1, # output only specified symbols - OUTPUT_EXPORTED => 2, # output exported symbols - OUTPUT_INTERNAL => 3, # output non-exported symbols -}; -my $output_selection = OUTPUT_ALL; -my $show_not_found = 0; # No longer used - -my @export_file_list; - -my @build_time; -if (defined($ENV{'KBUILD_BUILD_TIMESTAMP'}) && - (my $seconds = `date -d"${ENV{'KBUILD_BUILD_TIMESTAMP'}}" +%s`) ne '') { - @build_time = gmtime($seconds); -} else { - @build_time = localtime; -} - -my $man_date = ('January', 'February', 'March', 'April', 'May', 'June', - 'July', 'August', 'September', 'October', - 'November', 'December')[$build_time[4]] . - " " . ($build_time[5]+1900); - -# Essentially these are globals. -# They probably want to be tidied up, made more localised or something. -# CAVEAT EMPTOR! Some of the others I localised may not want to be, which -# could cause "use of undefined value" or other bugs. -my ($function, %function_table, %parametertypes, $declaration_purpose); -my %nosymbol_table = (); -my $declaration_start_line; -my ($type, $declaration_name, $return_type); -my ($newsection, $newcontents, $prototype, $brcount, %source_map); - -if (defined($ENV{'KBUILD_VERBOSE'})) { - $verbose = "$ENV{'KBUILD_VERBOSE'}"; -} - -if (defined($ENV{'KDOC_WERROR'})) { - $Werror = "$ENV{'KDOC_WERROR'}"; -} - -if (defined($ENV{'KCFLAGS'})) { - my $kcflags = "$ENV{'KCFLAGS'}"; - - if ($kcflags =~ /Werror/) { - $Werror = 1; - } -} - -# Generated docbook code is inserted in a template at a point where -# docbook v3.1 requires a non-zero sequence of RefEntry's; see: -# https://www.oasis-open.org/docbook/documentation/reference/html/refentry.html -# We keep track of number of generated entries and generate a dummy -# if needs be to ensure the expanded template can be postprocessed -# into html. -my $section_counter = 0; - -my $lineprefix=""; - -# Parser states -use constant { - STATE_NORMAL => 0, # normal code - STATE_NAME => 1, # looking for function name - STATE_BODY_MAYBE => 2, # body - or maybe more description - STATE_BODY => 3, # the body of the comment - STATE_BODY_WITH_BLANK_LINE => 4, # the body, which has a blank line - STATE_PROTO => 5, # scanning prototype - STATE_DOCBLOCK => 6, # documentation block - STATE_INLINE => 7, # gathering doc outside main block -}; -my $state; -my $in_doc_sect; -my $leading_space; - -# Inline documentation state -use constant { - STATE_INLINE_NA => 0, # not applicable ($state != STATE_INLINE) - STATE_INLINE_NAME => 1, # looking for member name (@foo:) - STATE_INLINE_TEXT => 2, # looking for member documentation - STATE_INLINE_END => 3, # done - STATE_INLINE_ERROR => 4, # error - Comment without header was found. - # Spit a warning as it's not - # proper kernel-doc and ignore the rest. -}; -my $inline_doc_state; - -#declaration types: can be -# 'function', 'struct', 'union', 'enum', 'typedef' -my $decl_type; - -my $doc_start = '^/\*\*\s*$'; # Allow whitespace at end of comment start. -my $doc_end = '\*/'; -my $doc_com = '\s*\*\s*'; -my $doc_com_body = '\s*\* ?'; -my $doc_decl = $doc_com . '(\w+)'; -# @params and a strictly limited set of supported section names -my $doc_sect = $doc_com . - '\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:(.*)'; -my $doc_content = $doc_com_body . '(.*)'; -my $doc_block = $doc_com . 'DOC:\s*(.*)?'; -my $doc_inline_start = '^\s*/\*\*\s*$'; -my $doc_inline_sect = '\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)'; -my $doc_inline_end = '^\s*\*/\s*$'; -my $doc_inline_oneline = '^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$'; -my $export_symbol = '^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*;'; - -my %parameterdescs; -my %parameterdesc_start_lines; -my @parameterlist; -my %sections; -my @sectionlist; -my %section_start_lines; -my $sectcheck; -my $struct_actual; - -my $contents = ""; -my $new_start_line = 0; - -# the canonical section names. see also $doc_sect above. -my $section_default = "Description"; # default section -my $section_intro = "Introduction"; -my $section = $section_default; -my $section_context = "Context"; -my $section_return = "Return"; - -my $undescribed = "-- undescribed --"; - -reset_state(); - -while ($ARGV[0] =~ m/^--?(.*)/) { - my $cmd = $1; - shift @ARGV; - if ($cmd eq "man") { - $output_mode = "man"; - @highlights = @highlights_man; - $blankline = $blankline_man; - } elsif ($cmd eq "rst") { - $output_mode = "rst"; - @highlights = @highlights_rst; - $blankline = $blankline_rst; - } elsif ($cmd eq "none") { - $output_mode = "none"; - } elsif ($cmd eq "module") { # not needed for XML, inherits from calling document - $modulename = shift @ARGV; - } elsif ($cmd eq "function") { # to only output specific functions - $output_selection = OUTPUT_INCLUDE; - $function = shift @ARGV; - $function_table{$function} = 1; - } elsif ($cmd eq "nosymbol") { # Exclude specific symbols - my $symbol = shift @ARGV; - $nosymbol_table{$symbol} = 1; - } elsif ($cmd eq "export") { # only exported symbols - $output_selection = OUTPUT_EXPORTED; - %function_table = (); - } elsif ($cmd eq "internal") { # only non-exported symbols - $output_selection = OUTPUT_INTERNAL; - %function_table = (); - } elsif ($cmd eq "export-file") { - my $file = shift @ARGV; - push(@export_file_list, $file); - } elsif ($cmd eq "v") { - $verbose = 1; - } elsif ($cmd eq "Werror") { - $Werror = 1; - } elsif (($cmd eq "h") || ($cmd eq "help")) { - usage(); - } elsif ($cmd eq 'no-doc-sections') { - $no_doc_sections = 1; - } elsif ($cmd eq 'enable-lineno') { - $enable_lineno = 1; - } elsif ($cmd eq 'show-not-found') { - $show_not_found = 1; # A no-op but don't fail - } elsif ($cmd eq "sphinx-version") { - my $ver_string = shift @ARGV; - if ($ver_string =~ m/^(\d+)(\.\d+)?(\.\d+)?/) { - $sphinx_major = $1; - if (defined($2)) { - $sphinx_minor = substr($2,1); - } else { - $sphinx_minor = 0; - } - if (defined($3)) { - $sphinx_patch = substr($3,1) - } else { - $sphinx_patch = 0; - } - } else { - die "Sphinx version should either major.minor or major.minor.patch format\n"; - } - } else { - # Unknown argument - usage(); - } -} - -# continue execution near EOF; - -# The C domain dialect changed on Sphinx 3. So, we need to check the -# version in order to produce the right tags. -sub findprog($) -{ - foreach(split(/:/, $ENV{PATH})) { - return "$_/$_[0]" if(-x "$_/$_[0]"); - } -} - -sub get_sphinx_version() -{ - my $ver; - - my $cmd = "sphinx-build"; - if (!findprog($cmd)) { - my $cmd = "sphinx-build3"; - if (!findprog($cmd)) { - $sphinx_major = 1; - $sphinx_minor = 2; - $sphinx_patch = 0; - printf STDERR "Warning: Sphinx version not found. Using default (Sphinx version %d.%d.%d)\n", - $sphinx_major, $sphinx_minor, $sphinx_patch; - return; - } - } - - open IN, "$cmd --version 2>&1 |"; - while () { - if (m/^\s*sphinx-build\s+([\d]+)\.([\d\.]+)(\+\/[\da-f]+)?$/) { - $sphinx_major = $1; - $sphinx_minor = $2; - $sphinx_patch = $3; - last; - } - # Sphinx 1.2.x uses a different format - if (m/^\s*Sphinx.*\s+([\d]+)\.([\d\.]+)$/) { - $sphinx_major = $1; - $sphinx_minor = $2; - $sphinx_patch = $3; - last; - } - } - close IN; -} - -# get kernel version from env -sub get_kernel_version() { - my $version = 'unknown kernel version'; - - if (defined($ENV{'KERNELVERSION'})) { - $version = $ENV{'KERNELVERSION'}; - } - return $version; -} - -# -sub print_lineno { - my $lineno = shift; - if ($enable_lineno && defined($lineno)) { - print "#define LINENO " . $lineno . "\n"; - } -} -## -# dumps section contents to arrays/hashes intended for that purpose. -# -sub dump_section { - my $file = shift; - my $name = shift; - my $contents = join "\n", @_; - - if ($name =~ m/$type_param/) { - $name = $1; - $parameterdescs{$name} = $contents; - $sectcheck = $sectcheck . $name . " "; - $parameterdesc_start_lines{$name} = $new_start_line; - $new_start_line = 0; - } elsif ($name eq "@\.\.\.") { - $name = "..."; - $parameterdescs{$name} = $contents; - $sectcheck = $sectcheck . $name . " "; - $parameterdesc_start_lines{$name} = $new_start_line; - $new_start_line = 0; - } else { - if (defined($sections{$name}) && ($sections{$name} ne "")) { - # Only warn on user specified duplicate section names. - if ($name ne $section_default) { - print STDERR "${file}:$.: warning: duplicate section name '$name'\n"; - ++$warnings; - } - $sections{$name} .= $contents; - } else { - $sections{$name} = $contents; - push @sectionlist, $name; - $section_start_lines{$name} = $new_start_line; - $new_start_line = 0; - } - } -} - -## -# dump DOC: section after checking that it should go out -# -sub dump_doc_section { - my $file = shift; - my $name = shift; - my $contents = join "\n", @_; - - if ($no_doc_sections) { - return; - } - - return if (defined($nosymbol_table{$name})); - - if (($output_selection == OUTPUT_ALL) || - (($output_selection == OUTPUT_INCLUDE) && - defined($function_table{$name}))) - { - dump_section($file, $name, $contents); - output_blockhead({'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'module' => $modulename, - 'content-only' => ($output_selection != OUTPUT_ALL), }); - } -} - -## -# output function -# -# parameterdescs, a hash. -# function => "function name" -# parameterlist => @list of parameters -# parameterdescs => %parameter descriptions -# sectionlist => @list of sections -# sections => %section descriptions -# - -sub output_highlight { - my $contents = join "\n",@_; - my $line; - -# DEBUG -# if (!defined $contents) { -# use Carp; -# confess "output_highlight got called with no args?\n"; -# } - -# print STDERR "contents b4:$contents\n"; - eval $dohighlight; - die $@ if $@; -# print STDERR "contents af:$contents\n"; - - foreach $line (split "\n", $contents) { - if (! $output_preformatted) { - $line =~ s/^\s*//; - } - if ($line eq ""){ - if (! $output_preformatted) { - print $lineprefix, $blankline; - } - } else { - if ($output_mode eq "man" && substr($line, 0, 1) eq ".") { - print "\\&$line"; - } else { - print $lineprefix, $line; - } - } - print "\n"; - } -} - -## -# output function in man -sub output_function_man(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - my $count; - - print ".TH \"$args{'function'}\" 9 \"$args{'function'}\" \"$man_date\" \"Kernel Hacker's Manual\" LINUX\n"; - - print ".SH NAME\n"; - print $args{'function'} . " \\- " . $args{'purpose'} . "\n"; - - print ".SH SYNOPSIS\n"; - if ($args{'functiontype'} ne "") { - print ".B \"" . $args{'functiontype'} . "\" " . $args{'function'} . "\n"; - } else { - print ".B \"" . $args{'function'} . "\n"; - } - $count = 0; - my $parenth = "("; - my $post = ","; - foreach my $parameter (@{$args{'parameterlist'}}) { - if ($count == $#{$args{'parameterlist'}}) { - $post = ");"; - } - $type = $args{'parametertypes'}{$parameter}; - if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { - # pointer-to-function - print ".BI \"" . $parenth . $1 . "\" " . " \") (" . $2 . ")" . $post . "\"\n"; - } else { - $type =~ s/([^\*])$/$1 /; - print ".BI \"" . $parenth . $type . "\" " . " \"" . $post . "\"\n"; - } - $count++; - $parenth = ""; - } - - print ".SH ARGUMENTS\n"; - foreach $parameter (@{$args{'parameterlist'}}) { - my $parameter_name = $parameter; - $parameter_name =~ s/\[.*//; - - print ".IP \"" . $parameter . "\" 12\n"; - output_highlight($args{'parameterdescs'}{$parameter_name}); - } - foreach $section (@{$args{'sectionlist'}}) { - print ".SH \"", uc $section, "\"\n"; - output_highlight($args{'sections'}{$section}); - } -} - -## -# output enum in man -sub output_enum_man(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - my $count; - - print ".TH \"$args{'module'}\" 9 \"enum $args{'enum'}\" \"$man_date\" \"API Manual\" LINUX\n"; - - print ".SH NAME\n"; - print "enum " . $args{'enum'} . " \\- " . $args{'purpose'} . "\n"; - - print ".SH SYNOPSIS\n"; - print "enum " . $args{'enum'} . " {\n"; - $count = 0; - foreach my $parameter (@{$args{'parameterlist'}}) { - print ".br\n.BI \" $parameter\"\n"; - if ($count == $#{$args{'parameterlist'}}) { - print "\n};\n"; - last; - } - else { - print ", \n.br\n"; - } - $count++; - } - - print ".SH Constants\n"; - foreach $parameter (@{$args{'parameterlist'}}) { - my $parameter_name = $parameter; - $parameter_name =~ s/\[.*//; - - print ".IP \"" . $parameter . "\" 12\n"; - output_highlight($args{'parameterdescs'}{$parameter_name}); - } - foreach $section (@{$args{'sectionlist'}}) { - print ".SH \"$section\"\n"; - output_highlight($args{'sections'}{$section}); - } -} - -## -# output struct in man -sub output_struct_man(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - - print ".TH \"$args{'module'}\" 9 \"" . $args{'type'} . " " . $args{'struct'} . "\" \"$man_date\" \"API Manual\" LINUX\n"; - - print ".SH NAME\n"; - print $args{'type'} . " " . $args{'struct'} . " \\- " . $args{'purpose'} . "\n"; - - my $declaration = $args{'definition'}; - $declaration =~ s/\t/ /g; - $declaration =~ s/\n/"\n.br\n.BI \"/g; - print ".SH SYNOPSIS\n"; - print $args{'type'} . " " . $args{'struct'} . " {\n.br\n"; - print ".BI \"$declaration\n};\n.br\n\n"; - - print ".SH Members\n"; - foreach $parameter (@{$args{'parameterlist'}}) { - ($parameter =~ /^#/) && next; - - my $parameter_name = $parameter; - $parameter_name =~ s/\[.*//; - - ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; - print ".IP \"" . $parameter . "\" 12\n"; - output_highlight($args{'parameterdescs'}{$parameter_name}); - } - foreach $section (@{$args{'sectionlist'}}) { - print ".SH \"$section\"\n"; - output_highlight($args{'sections'}{$section}); - } -} - -## -# output typedef in man -sub output_typedef_man(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - - print ".TH \"$args{'module'}\" 9 \"$args{'typedef'}\" \"$man_date\" \"API Manual\" LINUX\n"; - - print ".SH NAME\n"; - print "typedef " . $args{'typedef'} . " \\- " . $args{'purpose'} . "\n"; - - foreach $section (@{$args{'sectionlist'}}) { - print ".SH \"$section\"\n"; - output_highlight($args{'sections'}{$section}); - } -} - -sub output_blockhead_man(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - my $count; - - print ".TH \"$args{'module'}\" 9 \"$args{'module'}\" \"$man_date\" \"API Manual\" LINUX\n"; - - foreach $section (@{$args{'sectionlist'}}) { - print ".SH \"$section\"\n"; - output_highlight($args{'sections'}{$section}); - } -} - -## -# output in restructured text -# - -# -# This could use some work; it's used to output the DOC: sections, and -# starts by putting out the name of the doc section itself, but that tends -# to duplicate a header already in the template file. -# -sub output_blockhead_rst(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - - foreach $section (@{$args{'sectionlist'}}) { - next if (defined($nosymbol_table{$section})); - - if ($output_selection != OUTPUT_INCLUDE) { - print "**$section**\n\n"; - } - print_lineno($section_start_lines{$section}); - output_highlight_rst($args{'sections'}{$section}); - print "\n"; - } -} - -# -# Apply the RST highlights to a sub-block of text. -# -sub highlight_block($) { - # The dohighlight kludge requires the text be called $contents - my $contents = shift; - eval $dohighlight; - die $@ if $@; - return $contents; -} - -# -# Regexes used only here. -# -my $sphinx_literal = '^[^.].*::$'; -my $sphinx_cblock = '^\.\.\ +code-block::'; - -sub output_highlight_rst { - my $input = join "\n",@_; - my $output = ""; - my $line; - my $in_literal = 0; - my $litprefix; - my $block = ""; - - foreach $line (split "\n",$input) { - # - # If we're in a literal block, see if we should drop out - # of it. Otherwise pass the line straight through unmunged. - # - if ($in_literal) { - if (! ($line =~ /^\s*$/)) { - # - # If this is the first non-blank line in a literal - # block we need to figure out what the proper indent is. - # - if ($litprefix eq "") { - $line =~ /^(\s*)/; - $litprefix = '^' . $1; - $output .= $line . "\n"; - } elsif (! ($line =~ /$litprefix/)) { - $in_literal = 0; - } else { - $output .= $line . "\n"; - } - } else { - $output .= $line . "\n"; - } - } - # - # Not in a literal block (or just dropped out) - # - if (! $in_literal) { - $block .= $line . "\n"; - if (($line =~ /$sphinx_literal/) || ($line =~ /$sphinx_cblock/)) { - $in_literal = 1; - $litprefix = ""; - $output .= highlight_block($block); - $block = "" - } - } - } - - if ($block) { - $output .= highlight_block($block); - } - foreach $line (split "\n", $output) { - print $lineprefix . $line . "\n"; - } -} - -sub output_function_rst(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - my $oldprefix = $lineprefix; - my $start = ""; - my $is_macro = 0; - - if ($sphinx_major < 3) { - if ($args{'typedef'}) { - print ".. c:type:: ". $args{'function'} . "\n\n"; - print_lineno($declaration_start_line); - print " **Typedef**: "; - $lineprefix = ""; - output_highlight_rst($args{'purpose'}); - $start = "\n\n**Syntax**\n\n ``"; - $is_macro = 1; - } else { - print ".. c:function:: "; - } - } else { - if ($args{'typedef'} || $args{'functiontype'} eq "") { - $is_macro = 1; - print ".. c:macro:: ". $args{'function'} . "\n\n"; - } else { - print ".. c:function:: "; - } - - if ($args{'typedef'}) { - print_lineno($declaration_start_line); - print " **Typedef**: "; - $lineprefix = ""; - output_highlight_rst($args{'purpose'}); - $start = "\n\n**Syntax**\n\n ``"; - } else { - print "``" if ($is_macro); - } - } - if ($args{'functiontype'} ne "") { - $start .= $args{'functiontype'} . " " . $args{'function'} . " ("; - } else { - $start .= $args{'function'} . " ("; - } - print $start; - - my $count = 0; - foreach my $parameter (@{$args{'parameterlist'}}) { - if ($count ne 0) { - print ", "; - } - $count++; - $type = $args{'parametertypes'}{$parameter}; - - if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { - # pointer-to-function - print $1 . $parameter . ") (" . $2 . ")"; - } else { - print $type; - } - } - if ($is_macro) { - print ")``\n\n"; - } else { - print ")\n\n"; - } - if (!$args{'typedef'}) { - print_lineno($declaration_start_line); - $lineprefix = " "; - output_highlight_rst($args{'purpose'}); - print "\n"; - } - - print "**Parameters**\n\n"; - $lineprefix = " "; - foreach $parameter (@{$args{'parameterlist'}}) { - my $parameter_name = $parameter; - $parameter_name =~ s/\[.*//; - $type = $args{'parametertypes'}{$parameter}; - - if ($type ne "") { - print "``$type``\n"; - } else { - print "``$parameter``\n"; - } - - print_lineno($parameterdesc_start_lines{$parameter_name}); - - if (defined($args{'parameterdescs'}{$parameter_name}) && - $args{'parameterdescs'}{$parameter_name} ne $undescribed) { - output_highlight_rst($args{'parameterdescs'}{$parameter_name}); - } else { - print " *undescribed*\n"; - } - print "\n"; - } - - $lineprefix = $oldprefix; - output_section_rst(@_); -} - -sub output_section_rst(%) { - my %args = %{$_[0]}; - my $section; - my $oldprefix = $lineprefix; - $lineprefix = ""; - - foreach $section (@{$args{'sectionlist'}}) { - print "**$section**\n\n"; - print_lineno($section_start_lines{$section}); - output_highlight_rst($args{'sections'}{$section}); - print "\n"; - } - print "\n"; - $lineprefix = $oldprefix; -} - -sub output_enum_rst(%) { - my %args = %{$_[0]}; - my ($parameter); - my $oldprefix = $lineprefix; - my $count; - - if ($sphinx_major < 3) { - my $name = "enum " . $args{'enum'}; - print "\n\n.. c:type:: " . $name . "\n\n"; - } else { - my $name = $args{'enum'}; - print "\n\n.. c:enum:: " . $name . "\n\n"; - } - print_lineno($declaration_start_line); - $lineprefix = " "; - output_highlight_rst($args{'purpose'}); - print "\n"; - - print "**Constants**\n\n"; - $lineprefix = " "; - foreach $parameter (@{$args{'parameterlist'}}) { - print "``$parameter``\n"; - if ($args{'parameterdescs'}{$parameter} ne $undescribed) { - output_highlight_rst($args{'parameterdescs'}{$parameter}); - } else { - print " *undescribed*\n"; - } - print "\n"; - } - - $lineprefix = $oldprefix; - output_section_rst(@_); -} - -sub output_typedef_rst(%) { - my %args = %{$_[0]}; - my ($parameter); - my $oldprefix = $lineprefix; - my $name; - - if ($sphinx_major < 3) { - $name = "typedef " . $args{'typedef'}; - } else { - $name = $args{'typedef'}; - } - print "\n\n.. c:type:: " . $name . "\n\n"; - print_lineno($declaration_start_line); - $lineprefix = " "; - output_highlight_rst($args{'purpose'}); - print "\n"; - - $lineprefix = $oldprefix; - output_section_rst(@_); -} - -sub output_struct_rst(%) { - my %args = %{$_[0]}; - my ($parameter); - my $oldprefix = $lineprefix; - - if ($sphinx_major < 3) { - my $name = $args{'type'} . " " . $args{'struct'}; - print "\n\n.. c:type:: " . $name . "\n\n"; - } else { - my $name = $args{'struct'}; - if ($args{'type'} eq 'union') { - print "\n\n.. c:union:: " . $name . "\n\n"; - } else { - print "\n\n.. c:struct:: " . $name . "\n\n"; - } - } - print_lineno($declaration_start_line); - $lineprefix = " "; - output_highlight_rst($args{'purpose'}); - print "\n"; - - print "**Definition**\n\n"; - print "::\n\n"; - my $declaration = $args{'definition'}; - $declaration =~ s/\t/ /g; - print " " . $args{'type'} . " " . $args{'struct'} . " {\n$declaration };\n\n"; - - print "**Members**\n\n"; - $lineprefix = " "; - foreach $parameter (@{$args{'parameterlist'}}) { - ($parameter =~ /^#/) && next; - - my $parameter_name = $parameter; - $parameter_name =~ s/\[.*//; - - ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; - $type = $args{'parametertypes'}{$parameter}; - print_lineno($parameterdesc_start_lines{$parameter_name}); - print "``" . $parameter . "``\n"; - output_highlight_rst($args{'parameterdescs'}{$parameter_name}); - print "\n"; - } - print "\n"; - - $lineprefix = $oldprefix; - output_section_rst(@_); -} - -## none mode output functions - -sub output_function_none(%) { -} - -sub output_enum_none(%) { -} - -sub output_typedef_none(%) { -} - -sub output_struct_none(%) { -} - -sub output_blockhead_none(%) { -} - -## -# generic output function for all types (function, struct/union, typedef, enum); -# calls the generated, variable output_ function name based on -# functype and output_mode -sub output_declaration { - no strict 'refs'; - my $name = shift; - my $functype = shift; - my $func = "output_${functype}_$output_mode"; - - return if (defined($nosymbol_table{$name})); - - if (($output_selection == OUTPUT_ALL) || - (($output_selection == OUTPUT_INCLUDE || - $output_selection == OUTPUT_EXPORTED) && - defined($function_table{$name})) || - ($output_selection == OUTPUT_INTERNAL && - !($functype eq "function" && defined($function_table{$name})))) - { - &$func(@_); - $section_counter++; - } -} - -## -# generic output function - calls the right one based on current output mode. -sub output_blockhead { - no strict 'refs'; - my $func = "output_blockhead_" . $output_mode; - &$func(@_); - $section_counter++; -} - -## -# takes a declaration (struct, union, enum, typedef) and -# invokes the right handler. NOT called for functions. -sub dump_declaration($$) { - no strict 'refs'; - my ($prototype, $file) = @_; - my $func = "dump_" . $decl_type; - &$func(@_); -} - -sub dump_union($$) { - dump_struct(@_); -} - -sub dump_struct($$) { - my $x = shift; - my $file = shift; - - if ($x =~ /(struct|union)\s+(\w+)\s*\{(.*)\}(\s*(__packed|__aligned|____cacheline_aligned_in_smp|____cacheline_aligned|__attribute__\s*\(\([a-z0-9,_\s\(\)]*\)\)))*/) { - my $decl_type = $1; - $declaration_name = $2; - my $members = $3; - - # ignore members marked private: - $members =~ s/\/\*\s*private:.*?\/\*\s*public:.*?\*\///gosi; - $members =~ s/\/\*\s*private:.*//gosi; - # strip comments: - $members =~ s/\/\*.*?\*\///gos; - # strip attributes - $members =~ s/\s*__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)/ /gi; - $members =~ s/\s*__aligned\s*\([^;]*\)/ /gos; - $members =~ s/\s*__packed\s*/ /gos; - $members =~ s/\s*CRYPTO_MINALIGN_ATTR/ /gos; - $members =~ s/\s*____cacheline_aligned_in_smp/ /gos; - $members =~ s/\s*____cacheline_aligned/ /gos; - - # replace DECLARE_BITMAP - $members =~ s/__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)/DECLARE_BITMAP($1, __ETHTOOL_LINK_MODE_MASK_NBITS)/gos; - $members =~ s/DECLARE_BITMAP\s*\(([^,)]+),\s*([^,)]+)\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos; - # replace DECLARE_HASHTABLE - $members =~ s/DECLARE_HASHTABLE\s*\(([^,)]+),\s*([^,)]+)\)/unsigned long $1\[1 << (($2) - 1)\]/gos; - # replace DECLARE_KFIFO - $members =~ s/DECLARE_KFIFO\s*\(([^,)]+),\s*([^,)]+),\s*([^,)]+)\)/$2 \*$1/gos; - # replace DECLARE_KFIFO_PTR - $members =~ s/DECLARE_KFIFO_PTR\s*\(([^,)]+),\s*([^,)]+)\)/$2 \*$1/gos; - - my $declaration = $members; - - # Split nested struct/union elements as newer ones - while ($members =~ m/(struct|union)([^\{\};]+)\{([^\{\}]*)\}([^\{\}\;]*)\;/) { - my $newmember; - my $maintype = $1; - my $ids = $4; - my $content = $3; - foreach my $id(split /,/, $ids) { - $newmember .= "$maintype $id; "; - - $id =~ s/[:\[].*//; - $id =~ s/^\s*\**(\S+)\s*/$1/; - foreach my $arg (split /;/, $content) { - next if ($arg =~ m/^\s*$/); - if ($arg =~ m/^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)/) { - # pointer-to-function - my $type = $1; - my $name = $2; - my $extra = $3; - next if (!$name); - if ($id =~ m/^\s*$/) { - # anonymous struct/union - $newmember .= "$type$name$extra; "; - } else { - $newmember .= "$type$id.$name$extra; "; - } - } else { - my $type; - my $names; - $arg =~ s/^\s+//; - $arg =~ s/\s+$//; - # Handle bitmaps - $arg =~ s/:\s*\d+\s*//g; - # Handle arrays - $arg =~ s/\[.*\]//g; - # The type may have multiple words, - # and multiple IDs can be defined, like: - # const struct foo, *bar, foobar - # So, we remove spaces when parsing the - # names, in order to match just names - # and commas for the names - $arg =~ s/\s*,\s*/,/g; - if ($arg =~ m/(.*)\s+([\S+,]+)/) { - $type = $1; - $names = $2; - } else { - $newmember .= "$arg; "; - next; - } - foreach my $name (split /,/, $names) { - $name =~ s/^\s*\**(\S+)\s*/$1/; - next if (($name =~ m/^\s*$/)); - if ($id =~ m/^\s*$/) { - # anonymous struct/union - $newmember .= "$type $name; "; - } else { - $newmember .= "$type $id.$name; "; - } - } - } - } - } - $members =~ s/(struct|union)([^\{\};]+)\{([^\{\}]*)\}([^\{\}\;]*)\;/$newmember/; - } - - # Ignore other nested elements, like enums - $members =~ s/(\{[^\{\}]*\})//g; - - create_parameterlist($members, ';', $file, $declaration_name); - check_sections($file, $declaration_name, $decl_type, $sectcheck, $struct_actual); - - # Adjust declaration for better display - $declaration =~ s/([\{;])/$1\n/g; - $declaration =~ s/\}\s+;/};/g; - # Better handle inlined enums - do {} while ($declaration =~ s/(enum\s+\{[^\}]+),([^\n])/$1,\n$2/); - - my @def_args = split /\n/, $declaration; - my $level = 1; - $declaration = ""; - foreach my $clause (@def_args) { - $clause =~ s/^\s+//; - $clause =~ s/\s+$//; - $clause =~ s/\s+/ /; - next if (!$clause); - $level-- if ($clause =~ m/(\})/ && $level > 1); - if (!($clause =~ m/^\s*#/)) { - $declaration .= "\t" x $level; - } - $declaration .= "\t" . $clause . "\n"; - $level++ if ($clause =~ m/(\{)/ && !($clause =~m/\}/)); - } - output_declaration($declaration_name, - 'struct', - {'struct' => $declaration_name, - 'module' => $modulename, - 'definition' => $declaration, - 'parameterlist' => \@parameterlist, - 'parameterdescs' => \%parameterdescs, - 'parametertypes' => \%parametertypes, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose, - 'type' => $decl_type - }); - } - else { - print STDERR "${file}:$.: error: Cannot parse struct or union!\n"; - ++$errors; - } -} - - -sub show_warnings($$) { - my $functype = shift; - my $name = shift; - - return 0 if (defined($nosymbol_table{$name})); - - return 1 if ($output_selection == OUTPUT_ALL); - - if ($output_selection == OUTPUT_EXPORTED) { - if (defined($function_table{$name})) { - return 1; - } else { - return 0; - } - } - if ($output_selection == OUTPUT_INTERNAL) { - if (!($functype eq "function" && defined($function_table{$name}))) { - return 1; - } else { - return 0; - } - } - if ($output_selection == OUTPUT_INCLUDE) { - if (defined($function_table{$name})) { - return 1; - } else { - return 0; - } - } - die("Please add the new output type at show_warnings()"); -} - -sub dump_enum($$) { - my $x = shift; - my $file = shift; - my $members; - - - $x =~ s@/\*.*?\*/@@gos; # strip comments. - # strip #define macros inside enums - $x =~ s@#\s*((define|ifdef)\s+|endif)[^;]*;@@gos; - - if ($x =~ /typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;/) { - $declaration_name = $2; - $members = $1; - } elsif ($x =~ /enum\s+(\w*)\s*\{(.*)\}/) { - $declaration_name = $1; - $members = $2; - } - - if ($declaration_name) { - my %_members; - - $members =~ s/\s+$//; - - foreach my $arg (split ',', $members) { - $arg =~ s/^\s*(\w+).*/$1/; - push @parameterlist, $arg; - if (!$parameterdescs{$arg}) { - $parameterdescs{$arg} = $undescribed; - if (show_warnings("enum", $declaration_name)) { - print STDERR "${file}:$.: warning: Enum value '$arg' not described in enum '$declaration_name'\n"; - } - } - $_members{$arg} = 1; - } - - while (my ($k, $v) = each %parameterdescs) { - if (!exists($_members{$k})) { - if (show_warnings("enum", $declaration_name)) { - print STDERR "${file}:$.: warning: Excess enum value '$k' description in '$declaration_name'\n"; - } - } - } - - output_declaration($declaration_name, - 'enum', - {'enum' => $declaration_name, - 'module' => $modulename, - 'parameterlist' => \@parameterlist, - 'parameterdescs' => \%parameterdescs, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose - }); - } else { - print STDERR "${file}:$.: error: Cannot parse enum!\n"; - ++$errors; - } -} - -my $typedef_type = qr { ((?:\s+[\w\*]+){1,8})\s* }x; -my $typedef_ident = qr { \*?\s*(\w\S+)\s* }x; -my $typedef_args = qr { \s*\((.*)\); }x; - -my $typedef1 = qr { typedef$typedef_type\($typedef_ident\)$typedef_args }x; -my $typedef2 = qr { typedef$typedef_type$typedef_ident$typedef_args }x; - -sub dump_typedef($$) { - my $x = shift; - my $file = shift; - - $x =~ s@/\*.*?\*/@@gos; # strip comments. - - # Parse function typedef prototypes - if ($x =~ $typedef1 || $x =~ $typedef2) { - $return_type = $1; - $declaration_name = $2; - my $args = $3; - $return_type =~ s/^\s+//; - - create_parameterlist($args, ',', $file, $declaration_name); - - output_declaration($declaration_name, - 'function', - {'function' => $declaration_name, - 'typedef' => 1, - 'module' => $modulename, - 'functiontype' => $return_type, - 'parameterlist' => \@parameterlist, - 'parameterdescs' => \%parameterdescs, - 'parametertypes' => \%parametertypes, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose - }); - return; - } - - while (($x =~ /\(*.\)\s*;$/) || ($x =~ /\[*.\]\s*;$/)) { - $x =~ s/\(*.\)\s*;$/;/; - $x =~ s/\[*.\]\s*;$/;/; - } - - if ($x =~ /typedef.*\s+(\w+)\s*;/) { - $declaration_name = $1; - - output_declaration($declaration_name, - 'typedef', - {'typedef' => $declaration_name, - 'module' => $modulename, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose - }); - } - else { - print STDERR "${file}:$.: error: Cannot parse typedef!\n"; - ++$errors; - } -} - -sub save_struct_actual($) { - my $actual = shift; - - # strip all spaces from the actual param so that it looks like one string item - $actual =~ s/\s*//g; - $struct_actual = $struct_actual . $actual . " "; -} - -sub create_parameterlist($$$$) { - my $args = shift; - my $splitter = shift; - my $file = shift; - my $declaration_name = shift; - my $type; - my $param; - - # temporarily replace commas inside function pointer definition - while ($args =~ /(\([^\),]+),/) { - $args =~ s/(\([^\),]+),/$1#/g; - } - - foreach my $arg (split($splitter, $args)) { - # strip comments - $arg =~ s/\/\*.*\*\///; - # strip leading/trailing spaces - $arg =~ s/^\s*//; - $arg =~ s/\s*$//; - $arg =~ s/\s+/ /; - - if ($arg =~ /^#/) { - # Treat preprocessor directive as a typeless variable just to fill - # corresponding data structures "correctly". Catch it later in - # output_* subs. - push_parameter($arg, "", "", $file); - } elsif ($arg =~ m/\(.+\)\s*\(/) { - # pointer-to-function - $arg =~ tr/#/,/; - $arg =~ m/[^\(]+\(\*?\s*([\w\.]*)\s*\)/; - $param = $1; - $type = $arg; - $type =~ s/([^\(]+\(\*?)\s*$param/$1/; - save_struct_actual($param); - push_parameter($param, $type, $arg, $file, $declaration_name); - } elsif ($arg) { - $arg =~ s/\s*:\s*/:/g; - $arg =~ s/\s*\[/\[/g; - - my @args = split('\s*,\s*', $arg); - if ($args[0] =~ m/\*/) { - $args[0] =~ s/(\*+)\s*/ $1/; - } - - my @first_arg; - if ($args[0] =~ /^(.*\s+)(.*?\[.*\].*)$/) { - shift @args; - push(@first_arg, split('\s+', $1)); - push(@first_arg, $2); - } else { - @first_arg = split('\s+', shift @args); - } - - unshift(@args, pop @first_arg); - $type = join " ", @first_arg; - - foreach $param (@args) { - if ($param =~ m/^(\*+)\s*(.*)/) { - save_struct_actual($2); - - push_parameter($2, "$type $1", $arg, $file, $declaration_name); - } - elsif ($param =~ m/(.*?):(\d+)/) { - if ($type ne "") { # skip unnamed bit-fields - save_struct_actual($1); - push_parameter($1, "$type:$2", $arg, $file, $declaration_name) - } - } - else { - save_struct_actual($param); - push_parameter($param, $type, $arg, $file, $declaration_name); - } - } - } - } -} - -sub push_parameter($$$$$) { - my $param = shift; - my $type = shift; - my $org_arg = shift; - my $file = shift; - my $declaration_name = shift; - - if (($anon_struct_union == 1) && ($type eq "") && - ($param eq "}")) { - return; # ignore the ending }; from anon. struct/union - } - - $anon_struct_union = 0; - $param =~ s/[\[\)].*//; - - if ($type eq "" && $param =~ /\.\.\.$/) - { - if ($param =~ /\w\.\.\.$/) { - # for named variable parameters of the form `x...`, remove the dots - $param =~ s/\.\.\.$//; - } else { - # handles unnamed variable parameters - $param = "..."; - } - if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") { - $parameterdescs{$param} = "variable arguments"; - } - } - elsif ($type eq "" && ($param eq "" or $param eq "void")) - { - $param="void"; - $parameterdescs{void} = "no arguments"; - } - elsif ($type eq "" && ($param eq "struct" or $param eq "union")) - # handle unnamed (anonymous) union or struct: - { - $type = $param; - $param = "{unnamed_" . $param . "}"; - $parameterdescs{$param} = "anonymous\n"; - $anon_struct_union = 1; - } - - # warn if parameter has no description - # (but ignore ones starting with # as these are not parameters - # but inline preprocessor statements); - # Note: It will also ignore void params and unnamed structs/unions - if (!defined $parameterdescs{$param} && $param !~ /^#/) { - $parameterdescs{$param} = $undescribed; - - if (show_warnings($type, $declaration_name) && $param !~ /\./) { - print STDERR - "${file}:$.: warning: Function parameter or member '$param' not described in '$declaration_name'\n"; - ++$warnings; - } - } - - # strip spaces from $param so that it is one continuous string - # on @parameterlist; - # this fixes a problem where check_sections() cannot find - # a parameter like "addr[6 + 2]" because it actually appears - # as "addr[6", "+", "2]" on the parameter list; - # but it's better to maintain the param string unchanged for output, - # so just weaken the string compare in check_sections() to ignore - # "[blah" in a parameter string; - ###$param =~ s/\s*//g; - push @parameterlist, $param; - $org_arg =~ s/\s\s+/ /g; - $parametertypes{$param} = $org_arg; -} - -sub check_sections($$$$$) { - my ($file, $decl_name, $decl_type, $sectcheck, $prmscheck) = @_; - my @sects = split ' ', $sectcheck; - my @prms = split ' ', $prmscheck; - my $err; - my ($px, $sx); - my $prm_clean; # strip trailing "[array size]" and/or beginning "*" - - foreach $sx (0 .. $#sects) { - $err = 1; - foreach $px (0 .. $#prms) { - $prm_clean = $prms[$px]; - $prm_clean =~ s/\[.*\]//; - $prm_clean =~ s/__attribute__\s*\(\([a-z,_\*\s\(\)]*\)\)//i; - # ignore array size in a parameter string; - # however, the original param string may contain - # spaces, e.g.: addr[6 + 2] - # and this appears in @prms as "addr[6" since the - # parameter list is split at spaces; - # hence just ignore "[..." for the sections check; - $prm_clean =~ s/\[.*//; - - ##$prm_clean =~ s/^\**//; - if ($prm_clean eq $sects[$sx]) { - $err = 0; - last; - } - } - if ($err) { - if ($decl_type eq "function") { - print STDERR "${file}:$.: warning: " . - "Excess function parameter " . - "'$sects[$sx]' " . - "description in '$decl_name'\n"; - ++$warnings; - } - } - } -} - -## -# Checks the section describing the return value of a function. -sub check_return_section { - my $file = shift; - my $declaration_name = shift; - my $return_type = shift; - - # Ignore an empty return type (It's a macro) - # Ignore functions with a "void" return type. (But don't ignore "void *") - if (($return_type eq "") || ($return_type =~ /void\s*\w*\s*$/)) { - return; - } - - if (!defined($sections{$section_return}) || - $sections{$section_return} eq "") { - print STDERR "${file}:$.: warning: " . - "No description found for return value of " . - "'$declaration_name'\n"; - ++$warnings; - } -} - -## -# takes a function prototype and the name of the current file being -# processed and spits out all the details stored in the global -# arrays/hashes. -sub dump_function($$) { - my $prototype = shift; - my $file = shift; - my $noret = 0; - - print_lineno($new_start_line); - - $prototype =~ s/^static +//; - $prototype =~ s/^extern +//; - $prototype =~ s/^asmlinkage +//; - $prototype =~ s/^inline +//; - $prototype =~ s/^__inline__ +//; - $prototype =~ s/^__inline +//; - $prototype =~ s/^__always_inline +//; - $prototype =~ s/^noinline +//; - $prototype =~ s/__init +//; - $prototype =~ s/__init_or_module +//; - $prototype =~ s/__meminit +//; - $prototype =~ s/__must_check +//; - $prototype =~ s/__weak +//; - $prototype =~ s/__sched +//; - $prototype =~ s/__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +//; - my $define = $prototype =~ s/^#\s*define\s+//; #ak added - $prototype =~ s/__attribute__\s*\(\( - (?: - [\w\s]++ # attribute name - (?:\([^)]*+\))? # attribute arguments - \s*+,? # optional comma at the end - )+ - \)\)\s+//x; - - # Strip QEMU specific compiler annotations - $prototype =~ s/QEMU_[A-Z_]+ +//; - - # Yes, this truly is vile. We are looking for: - # 1. Return type (may be nothing if we're looking at a macro) - # 2. Function name - # 3. Function parameters. - # - # All the while we have to watch out for function pointer parameters - # (which IIRC is what the two sections are for), C types (these - # regexps don't even start to express all the possibilities), and - # so on. - # - # If you mess with these regexps, it's a good idea to check that - # the following functions' documentation still comes out right: - # - parport_register_device (function pointer parameters) - # - atomic_set (macro) - # - pci_match_device, __copy_to_user (long return type) - - if ($define && $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s+/) { - # This is an object-like macro, it has no return type and no parameter - # list. - # Function-like macros are not allowed to have spaces between - # declaration_name and opening parenthesis (notice the \s+). - $return_type = $1; - $declaration_name = $2; - $noret = 1; - } elsif ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s*\*+\s*\w+\s*\*+\s*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/) { - $return_type = $1; - $declaration_name = $2; - my $args = $3; - - create_parameterlist($args, ',', $file, $declaration_name); - } else { - print STDERR "${file}:$.: warning: cannot understand function prototype: '$prototype'\n"; - return; - } - - my $prms = join " ", @parameterlist; - check_sections($file, $declaration_name, "function", $sectcheck, $prms); - - # This check emits a lot of warnings at the moment, because many - # functions don't have a 'Return' doc section. So until the number - # of warnings goes sufficiently down, the check is only performed in - # verbose mode. - # TODO: always perform the check. - if ($verbose && !$noret) { - check_return_section($file, $declaration_name, $return_type); - } - - # The function parser can be called with a typedef parameter. - # Handle it. - if ($return_type =~ /typedef/) { - output_declaration($declaration_name, - 'function', - {'function' => $declaration_name, - 'typedef' => 1, - 'module' => $modulename, - 'functiontype' => $return_type, - 'parameterlist' => \@parameterlist, - 'parameterdescs' => \%parameterdescs, - 'parametertypes' => \%parametertypes, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose - }); - } else { - output_declaration($declaration_name, - 'function', - {'function' => $declaration_name, - 'module' => $modulename, - 'functiontype' => $return_type, - 'parameterlist' => \@parameterlist, - 'parameterdescs' => \%parameterdescs, - 'parametertypes' => \%parametertypes, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose - }); - } -} - -sub reset_state { - $function = ""; - %parameterdescs = (); - %parametertypes = (); - @parameterlist = (); - %sections = (); - @sectionlist = (); - $sectcheck = ""; - $struct_actual = ""; - $prototype = ""; - - $state = STATE_NORMAL; - $inline_doc_state = STATE_INLINE_NA; -} - -sub tracepoint_munge($) { - my $file = shift; - my $tracepointname = 0; - my $tracepointargs = 0; - - if ($prototype =~ m/TRACE_EVENT\((.*?),/) { - $tracepointname = $1; - } - if ($prototype =~ m/DEFINE_SINGLE_EVENT\((.*?),/) { - $tracepointname = $1; - } - if ($prototype =~ m/DEFINE_EVENT\((.*?),(.*?),/) { - $tracepointname = $2; - } - $tracepointname =~ s/^\s+//; #strip leading whitespace - if ($prototype =~ m/TP_PROTO\((.*?)\)/) { - $tracepointargs = $1; - } - if (($tracepointname eq 0) || ($tracepointargs eq 0)) { - print STDERR "${file}:$.: warning: Unrecognized tracepoint format: \n". - "$prototype\n"; - } else { - $prototype = "static inline void trace_$tracepointname($tracepointargs)"; - } -} - -sub syscall_munge() { - my $void = 0; - - $prototype =~ s@[\r\n]+@ @gos; # strip newlines/CR's -## if ($prototype =~ m/SYSCALL_DEFINE0\s*\(\s*(a-zA-Z0-9_)*\s*\)/) { - if ($prototype =~ m/SYSCALL_DEFINE0/) { - $void = 1; -## $prototype = "long sys_$1(void)"; - } - - $prototype =~ s/SYSCALL_DEFINE.*\(/long sys_/; # fix return type & func name - if ($prototype =~ m/long (sys_.*?),/) { - $prototype =~ s/,/\(/; - } elsif ($void) { - $prototype =~ s/\)/\(void\)/; - } - - # now delete all of the odd-number commas in $prototype - # so that arg types & arg names don't have a comma between them - my $count = 0; - my $len = length($prototype); - if ($void) { - $len = 0; # skip the for-loop - } - for (my $ix = 0; $ix < $len; $ix++) { - if (substr($prototype, $ix, 1) eq ',') { - $count++; - if ($count % 2 == 1) { - substr($prototype, $ix, 1) = ' '; - } - } - } -} - -sub process_proto_function($$) { - my $x = shift; - my $file = shift; - - $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line - - if ($x =~ m#\s*/\*\s+MACDOC\s*#io || ($x =~ /^#/ && $x !~ /^#\s*define/)) { - # do nothing - } - elsif ($x =~ /([^\{]*)/) { - $prototype .= $1; - } - - if (($x =~ /\{/) || ($x =~ /\#\s*define/) || ($x =~ /;/)) { - $prototype =~ s@/\*.*?\*/@@gos; # strip comments. - $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's. - $prototype =~ s@^\s+@@gos; # strip leading spaces - - # Handle prototypes for function pointers like: - # int (*pcs_config)(struct foo) - $prototype =~ s@^(\S+\s+)\(\s*\*(\S+)\)@$1$2@gos; - - if ($prototype =~ /SYSCALL_DEFINE/) { - syscall_munge(); - } - if ($prototype =~ /TRACE_EVENT/ || $prototype =~ /DEFINE_EVENT/ || - $prototype =~ /DEFINE_SINGLE_EVENT/) - { - tracepoint_munge($file); - } - dump_function($prototype, $file); - reset_state(); - } -} - -sub process_proto_type($$) { - my $x = shift; - my $file = shift; - - $x =~ s@[\r\n]+@ @gos; # strip newlines/cr's. - $x =~ s@^\s+@@gos; # strip leading spaces - $x =~ s@\s+$@@gos; # strip trailing spaces - $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line - - if ($x =~ /^#/) { - # To distinguish preprocessor directive from regular declaration later. - $x .= ";"; - } - - while (1) { - if ( $x =~ /([^\{\};]*)([\{\};])(.*)/ ) { - if( length $prototype ) { - $prototype .= " " - } - $prototype .= $1 . $2; - ($2 eq '{') && $brcount++; - ($2 eq '}') && $brcount--; - if (($2 eq ';') && ($brcount == 0)) { - dump_declaration($prototype, $file); - reset_state(); - last; - } - $x = $3; - } else { - $prototype .= $x; - last; - } - } -} - - -sub map_filename($) { - my $file; - my ($orig_file) = @_; - - if (defined($ENV{'SRCTREE'})) { - $file = "$ENV{'SRCTREE'}" . "/" . $orig_file; - } else { - $file = $orig_file; - } - - if (defined($source_map{$file})) { - $file = $source_map{$file}; - } - - return $file; -} - -sub process_export_file($) { - my ($orig_file) = @_; - my $file = map_filename($orig_file); - - if (!open(IN,"<$file")) { - print STDERR "Error: Cannot open file $file\n"; - ++$errors; - return; - } - - while () { - if (/$export_symbol/) { - next if (defined($nosymbol_table{$2})); - $function_table{$2} = 1; - } - } - - close(IN); -} - -# -# Parsers for the various processing states. -# -# STATE_NORMAL: looking for the /** to begin everything. -# -sub process_normal() { - if (/$doc_start/o) { - $state = STATE_NAME; # next line is always the function name - $in_doc_sect = 0; - $declaration_start_line = $. + 1; - } -} - -# -# STATE_NAME: Looking for the "name - description" line -# -sub process_name($$) { - my $file = shift; - my $identifier; - my $descr; - - if (/$doc_block/o) { - $state = STATE_DOCBLOCK; - $contents = ""; - $new_start_line = $.; - - if ( $1 eq "" ) { - $section = $section_intro; - } else { - $section = $1; - } - } - elsif (/$doc_decl/o) { - $identifier = $1; - if (/\s*([\w\s]+?)(\s*-|:)/) { - $identifier = $1; - } - - $state = STATE_BODY; - # if there's no @param blocks need to set up default section - # here - $contents = ""; - $section = $section_default; - $new_start_line = $. + 1; - if (/[-:](.*)/) { - # strip leading/trailing/multiple spaces - $descr= $1; - $descr =~ s/^\s*//; - $descr =~ s/\s*$//; - $descr =~ s/\s+/ /g; - $declaration_purpose = $descr; - $state = STATE_BODY_MAYBE; - } else { - $declaration_purpose = ""; - } - - if (($declaration_purpose eq "") && $verbose) { - print STDERR "${file}:$.: warning: missing initial short description on line:\n"; - print STDERR $_; - ++$warnings; - } - - if ($identifier =~ m/^struct\b/) { - $decl_type = 'struct'; - } elsif ($identifier =~ m/^union\b/) { - $decl_type = 'union'; - } elsif ($identifier =~ m/^enum\b/) { - $decl_type = 'enum'; - } elsif ($identifier =~ m/^typedef\b/) { - $decl_type = 'typedef'; - } else { - $decl_type = 'function'; - } - - if ($verbose) { - print STDERR "${file}:$.: info: Scanning doc for $identifier\n"; - } - } else { - print STDERR "${file}:$.: warning: Cannot understand $_ on line $.", - " - I thought it was a doc line\n"; - ++$warnings; - $state = STATE_NORMAL; - } -} - - -# -# STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment. -# -sub process_body($$) { - my $file = shift; - - # Until all named variable macro parameters are - # documented using the bare name (`x`) rather than with - # dots (`x...`), strip the dots: - if ($section =~ /\w\.\.\.$/) { - $section =~ s/\.\.\.$//; - - if ($verbose) { - print STDERR "${file}:$.: warning: Variable macro arguments should be documented without dots\n"; - ++$warnings; - } - } - - if ($state == STATE_BODY_WITH_BLANK_LINE && /^\s*\*\s?\S/) { - dump_section($file, $section, $contents); - $section = $section_default; - $new_start_line = $.; - $contents = ""; - } - - if (/$doc_sect/i) { # case insensitive for supported section names - $newsection = $1; - $newcontents = $2; - - # map the supported section names to the canonical names - if ($newsection =~ m/^description$/i) { - $newsection = $section_default; - } elsif ($newsection =~ m/^context$/i) { - $newsection = $section_context; - } elsif ($newsection =~ m/^returns?$/i) { - $newsection = $section_return; - } elsif ($newsection =~ m/^\@return$/) { - # special: @return is a section, not a param description - $newsection = $section_return; - } - - if (($contents ne "") && ($contents ne "\n")) { - if (!$in_doc_sect && $verbose) { - print STDERR "${file}:$.: warning: contents before sections\n"; - ++$warnings; - } - dump_section($file, $section, $contents); - $section = $section_default; - } - - $in_doc_sect = 1; - $state = STATE_BODY; - $contents = $newcontents; - $new_start_line = $.; - while (substr($contents, 0, 1) eq " ") { - $contents = substr($contents, 1); - } - if ($contents ne "") { - $contents .= "\n"; - } - $section = $newsection; - $leading_space = undef; - } elsif (/$doc_end/) { - if (($contents ne "") && ($contents ne "\n")) { - dump_section($file, $section, $contents); - $section = $section_default; - $contents = ""; - } - # look for doc_com + + doc_end: - if ($_ =~ m'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') { - print STDERR "${file}:$.: warning: suspicious ending line: $_"; - ++$warnings; - } - - $prototype = ""; - $state = STATE_PROTO; - $brcount = 0; - $new_start_line = $. + 1; - } elsif (/$doc_content/) { - if ($1 eq "") { - if ($section eq $section_context) { - dump_section($file, $section, $contents); - $section = $section_default; - $contents = ""; - $new_start_line = $.; - $state = STATE_BODY; - } else { - if ($section ne $section_default) { - $state = STATE_BODY_WITH_BLANK_LINE; - } else { - $state = STATE_BODY; - } - $contents .= "\n"; - } - } elsif ($state == STATE_BODY_MAYBE) { - # Continued declaration purpose - chomp($declaration_purpose); - $declaration_purpose .= " " . $1; - $declaration_purpose =~ s/\s+/ /g; - } else { - my $cont = $1; - if ($section =~ m/^@/ || $section eq $section_context) { - if (!defined $leading_space) { - if ($cont =~ m/^(\s+)/) { - $leading_space = $1; - } else { - $leading_space = ""; - } - } - $cont =~ s/^$leading_space//; - } - $contents .= $cont . "\n"; - } - } else { - # i dont know - bad line? ignore. - print STDERR "${file}:$.: warning: bad line: $_"; - ++$warnings; - } -} - - -# -# STATE_PROTO: reading a function/whatever prototype. -# -sub process_proto($$) { - my $file = shift; - - if (/$doc_inline_oneline/) { - $section = $1; - $contents = $2; - if ($contents ne "") { - $contents .= "\n"; - dump_section($file, $section, $contents); - $section = $section_default; - $contents = ""; - } - } elsif (/$doc_inline_start/) { - $state = STATE_INLINE; - $inline_doc_state = STATE_INLINE_NAME; - } elsif ($decl_type eq 'function') { - process_proto_function($_, $file); - } else { - process_proto_type($_, $file); - } -} - -# -# STATE_DOCBLOCK: within a DOC: block. -# -sub process_docblock($$) { - my $file = shift; - - if (/$doc_end/) { - dump_doc_section($file, $section, $contents); - $section = $section_default; - $contents = ""; - $function = ""; - %parameterdescs = (); - %parametertypes = (); - @parameterlist = (); - %sections = (); - @sectionlist = (); - $prototype = ""; - $state = STATE_NORMAL; - } elsif (/$doc_content/) { - if ( $1 eq "" ) { - $contents .= $blankline; - } else { - $contents .= $1 . "\n"; - } - } -} - -# -# STATE_INLINE: docbook comments within a prototype. -# -sub process_inline($$) { - my $file = shift; - - # First line (state 1) needs to be a @parameter - if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) { - $section = $1; - $contents = $2; - $new_start_line = $.; - if ($contents ne "") { - while (substr($contents, 0, 1) eq " ") { - $contents = substr($contents, 1); - } - $contents .= "\n"; - } - $inline_doc_state = STATE_INLINE_TEXT; - # Documentation block end */ - } elsif (/$doc_inline_end/) { - if (($contents ne "") && ($contents ne "\n")) { - dump_section($file, $section, $contents); - $section = $section_default; - $contents = ""; - } - $state = STATE_PROTO; - $inline_doc_state = STATE_INLINE_NA; - # Regular text - } elsif (/$doc_content/) { - if ($inline_doc_state == STATE_INLINE_TEXT) { - $contents .= $1 . "\n"; - # nuke leading blank lines - if ($contents =~ /^\s*$/) { - $contents = ""; - } - } elsif ($inline_doc_state == STATE_INLINE_NAME) { - $inline_doc_state = STATE_INLINE_ERROR; - print STDERR "${file}:$.: warning: "; - print STDERR "Incorrect use of kernel-doc format: $_"; - ++$warnings; - } - } -} - - -sub process_file($) { - my $file; - my $initial_section_counter = $section_counter; - my ($orig_file) = @_; - - $file = map_filename($orig_file); - - if (!open(IN_FILE,"<$file")) { - print STDERR "Error: Cannot open file $file\n"; - ++$errors; - return; - } - - $. = 1; - - $section_counter = 0; - while () { - while (s/\\\s*$//) { - $_ .= ; - } - # Replace tabs by spaces - while ($_ =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {}; - # Hand this line to the appropriate state handler - if ($state == STATE_NORMAL) { - process_normal(); - } elsif ($state == STATE_NAME) { - process_name($file, $_); - } elsif ($state == STATE_BODY || $state == STATE_BODY_MAYBE || - $state == STATE_BODY_WITH_BLANK_LINE) { - process_body($file, $_); - } elsif ($state == STATE_INLINE) { # scanning for inline parameters - process_inline($file, $_); - } elsif ($state == STATE_PROTO) { - process_proto($file, $_); - } elsif ($state == STATE_DOCBLOCK) { - process_docblock($file, $_); - } - } - - # Make sure we got something interesting. - if ($initial_section_counter == $section_counter && $ - output_mode ne "none") { - if ($output_selection == OUTPUT_INCLUDE) { - print STDERR "${file}:1: warning: '$_' not found\n" - for keys %function_table; - } - else { - print STDERR "${file}:1: warning: no structured comments found\n"; - } - } - close IN_FILE; -} - - -if ($output_mode eq "rst") { - get_sphinx_version() if (!$sphinx_major); -} - -$kernelversion = get_kernel_version(); - -# generate a sequence of code that will splice in highlighting information -# using the s// operator. -for (my $k = 0; $k < @highlights; $k++) { - my $pattern = $highlights[$k][0]; - my $result = $highlights[$k][1]; -# print STDERR "scanning pattern:$pattern, highlight:($result)\n"; - $dohighlight .= "\$contents =~ s:$pattern:$result:gs;\n"; -} - -# Read the file that maps relative names to absolute names for -# separate source and object directories and for shadow trees. -if (open(SOURCE_MAP, "<.tmp_filelist.txt")) { - my ($relname, $absname); - while() { - chop(); - ($relname, $absname) = (split())[0..1]; - $relname =~ s:^/+::; - $source_map{$relname} = $absname; - } - close(SOURCE_MAP); -} - -if ($output_selection == OUTPUT_EXPORTED || - $output_selection == OUTPUT_INTERNAL) { - - push(@export_file_list, @ARGV); - - foreach (@export_file_list) { - chomp; - process_export_file($_); - } -} - -foreach (@ARGV) { - chomp; - process_file($_); -} -if ($verbose && $errors) { - print STDERR "$errors errors\n"; -} -if ($verbose && $warnings) { - print STDERR "$warnings warnings\n"; -} - -if ($Werror && $warnings) { - print STDERR "$warnings warnings as Errors\n"; - exit($warnings); -} else { - exit($output_mode eq "none" ? 0 : $errors) -} From e0ca100425853aa362acefeb027800d952fb222d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 14 Aug 2025 18:13:23 +0100 Subject: [PATCH 0508/1794] MAINTAINERS: Put kernel-doc under the "docs build machinery" section We never had a MAINTAINERS entry for the old kernel-doc script; add the files for the new Python kernel-doc under "Sphinx documentation configuration and build machinery", as the most appropriate subsection. Mauro has kindly volunteered to help with maintenance/review of this area of the codebase, so add him as a maintainer. Signed-off-by: Peter Maydell Reviewed-by: Paolo Bonzini Reviewed-by: Mauro Carvalho Chehab Message-id: 20250814171324.1614516-9-peter.maydell@linaro.org --- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8f074e43712b4..8147fff3523ea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4441,6 +4441,7 @@ F: po/*.po Sphinx documentation configuration and build machinery M: John Snow M: Peter Maydell +M: Mauro Carvalho Chehab S: Maintained F: docs/conf.py F: docs/*/conf.py @@ -4449,6 +4450,8 @@ F: docs/sphinx/ F: docs/_templates/ F: docs/devel/docs.rst F: docs/devel/qapi-domain.rst +F: scripts/kernel-doc +F: scripts/lib/kdoc/ Rust build system integration M: Manos Pitsidianakis From c2fae597099ef6ff81ca63d69ee28eddb982d894 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 19 Aug 2025 15:56:58 +0100 Subject: [PATCH 0509/1794] target/arm: Correct condition of aa64_atomics feature function The ARMv8.1-Atomics feature (renamed FEAT_LSE in more modern versions of the Arm ARM) has always ben indicated by ID_AA64ISAR0.ATOMIC being 0b0010 or greater; 0b0001 is a reserved unused value. We were incorrectly checking for != 0; this had no harmful effects because all the CPUs set their value for this field to either 0 (for not having the feature) or 2 (if they do have it), but it's better to match what the architecture specifies here. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250819145659.2165160-1-peter.maydell@linaro.org --- target/arm/cpu-features.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 41511d08350b0..d48754bcf27bf 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -408,7 +408,7 @@ static inline bool isar_feature_aa64_crc32(const ARMISARegisters *id) static inline bool isar_feature_aa64_atomics(const ARMISARegisters *id) { - return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) >= 2; } static inline bool isar_feature_aa64_rdm(const ARMISARegisters *id) From 1748c0d59228c7790940d8be381df1c3108022b1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Aug 2025 22:26:47 +1000 Subject: [PATCH 0510/1794] qemu/atomic: Finish renaming atomic128-cas.h headers The aarch64 header was not renamed with the others, meaning it was skipped in favor of the generic version. Cc: qemu-stable@nongnu.org Fixes: 15606965400b ("qemu/atomic: Rename atomic128-cas.h headers using .h.inc suffix") Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250815122653.701782-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- .../include/aarch64/host/{atomic128-cas.h => atomic128-cas.h.inc} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename host/include/aarch64/host/{atomic128-cas.h => atomic128-cas.h.inc} (100%) diff --git a/host/include/aarch64/host/atomic128-cas.h b/host/include/aarch64/host/atomic128-cas.h.inc similarity index 100% rename from host/include/aarch64/host/atomic128-cas.h rename to host/include/aarch64/host/atomic128-cas.h.inc From 1d5e88e7b5b83130deb312d05de11267baac4c05 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Aug 2025 22:26:48 +1000 Subject: [PATCH 0511/1794] qemu/atomic: Add atomic16 primitives for xchg, fetch_and, fetch_or Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250815122653.701782-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- host/include/aarch64/host/atomic128-cas.h.inc | 57 +++++++++++ host/include/generic/host/atomic128-cas.h.inc | 96 +++++++++++++++++++ 2 files changed, 153 insertions(+) diff --git a/host/include/aarch64/host/atomic128-cas.h.inc b/host/include/aarch64/host/atomic128-cas.h.inc index 991da4ef54333..aec27df182089 100644 --- a/host/include/aarch64/host/atomic128-cas.h.inc +++ b/host/include/aarch64/host/atomic128-cas.h.inc @@ -38,6 +38,63 @@ static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) return int128_make128(oldl, oldh); } +static inline Int128 atomic16_xchg(Int128 *ptr, Int128 new) +{ + uint64_t newl = int128_getlo(new), newh = int128_gethi(new); + uint64_t oldl, oldh; + uint32_t tmp; + + asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t" + "stlxp %w[tmp], %[newl], %[newh], %[mem]\n\t" + "cbnz %w[tmp], 0b" + : [mem] "+m"(*ptr), [tmp] "=&r"(tmp), + [oldl] "=&r"(oldl), [oldh] "=&r"(oldh) + : [newl] "r"(newl), [newh] "r"(newh) + : "memory"); + + return int128_make128(oldl, oldh); +} + +static inline Int128 atomic16_fetch_and(Int128 *ptr, Int128 new) +{ + uint64_t newl = int128_getlo(new), newh = int128_gethi(new); + uint64_t oldl, oldh, tmpl, tmph; + uint32_t tmp; + + asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t" + "and %[tmpl], %[oldl], %[newl]\n\t" + "and %[tmph], %[oldh], %[newh]\n\t" + "stlxp %w[tmp], %[tmpl], %[tmph], %[mem]\n\t" + "cbnz %w[tmp], 0b" + : [mem] "+m"(*ptr), [tmp] "=&r"(tmp), + [oldl] "=&r"(oldl), [oldh] "=&r"(oldh) + : [newl] "r"(newl), [newh] "r"(newh), + [tmpl] "r"(tmpl), [tmph] "r"(tmph) + : "memory"); + + return int128_make128(oldl, oldh); +} + +static inline Int128 atomic16_fetch_or(Int128 *ptr, Int128 new) +{ + uint64_t newl = int128_getlo(new), newh = int128_gethi(new); + uint64_t oldl, oldh, tmpl, tmph; + uint32_t tmp; + + asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t" + "orr %[tmpl], %[oldl], %[newl]\n\t" + "orr %[tmph], %[oldh], %[newh]\n\t" + "stlxp %w[tmp], %[tmpl], %[tmph], %[mem]\n\t" + "cbnz %w[tmp], 0b" + : [mem] "+m"(*ptr), [tmp] "=&r"(tmp), + [oldl] "=&r"(oldl), [oldh] "=&r"(oldh) + : [newl] "r"(newl), [newh] "r"(newh), + [tmpl] "r"(tmpl), [tmph] "r"(tmph) + : "memory"); + + return int128_make128(oldl, oldh); +} + # define CONFIG_CMPXCHG128 1 # define HAVE_CMPXCHG128 1 #endif diff --git a/host/include/generic/host/atomic128-cas.h.inc b/host/include/generic/host/atomic128-cas.h.inc index 6b40cc2271046..990162c56fe2b 100644 --- a/host/include/generic/host/atomic128-cas.h.inc +++ b/host/include/generic/host/atomic128-cas.h.inc @@ -23,6 +23,51 @@ atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) r.i = qatomic_cmpxchg__nocheck(ptr_align, c.i, n.i); return r.s; } + +/* + * Since we're looping anyway, use weak compare and swap. + * If the host supports weak, this will eliminate a second loop hidden + * within the atomic operation itself; otherwise the weak parameter is + * ignored. + */ +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_xchg(Int128 *ptr, Int128 new) +{ + __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128 old = *ptr_align; + + while (!__atomic_compare_exchange_n(ptr_align, &old, new, true, + __ATOMIC_SEQ_CST, 0)) { + continue; + } + return old; +} + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_fetch_and(Int128 *ptr, Int128 val) +{ + __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128 old = *ptr_align; + + while (!__atomic_compare_exchange_n(ptr_align, &old, old & val, true, + __ATOMIC_SEQ_CST, 0)) { + continue; + } + return old; +} + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_fetch_or(Int128 *ptr, Int128 val) +{ + __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128 old = *ptr_align; + + while (!__atomic_compare_exchange_n(ptr_align, &old, old | val, true, + __ATOMIC_SEQ_CST, 0)) { + continue; + } + return old; +} # define HAVE_CMPXCHG128 1 #elif defined(CONFIG_CMPXCHG128) static inline Int128 ATTRIBUTE_ATOMIC128_OPT @@ -36,6 +81,57 @@ atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) r.i = __sync_val_compare_and_swap_16(ptr_align, c.i, n.i); return r.s; } + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_xchg(Int128 *ptr, Int128 new) +{ + Int128Aligned *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128Alias o, n; + + n.s = new; + o.s = *ptr_align; + while (1) { + __int128 c = __sync_val_compare_and_swap_16(ptr_align, o.i, n.i); + if (c == o.i) { + return o.s; + } + o.i = c; + } +} + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_fetch_and(Int128 *ptr, Int128 val) +{ + Int128Aligned *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128Alias o, v; + + v.s = val; + o.s = *ptr_align; + while (1) { + __int128 c = __sync_val_compare_and_swap_16(ptr_align, o.i, o.i & v.i); + if (c == o.i) { + return o.s; + } + o.i = c; + } +} + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_fetch_or(Int128 *ptr, Int128 val) +{ + Int128Aligned *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128Alias o, v; + + v.s = val; + o.s = *ptr_align; + while (1) { + __int128 c = __sync_val_compare_and_swap_16(ptr_align, o.i, o.i | v.i); + if (c == o.i) { + return o.s; + } + o.i = c; + } +} # define HAVE_CMPXCHG128 1 #else /* Fallback definition that must be optimized away, or error. */ From 33aefd187eaff71dbc686c43f7acfdd0a81c7de4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Aug 2025 22:26:49 +1000 Subject: [PATCH 0512/1794] accel/tcg: Add cpu_atomic_*_mmu for 16-byte xchg, fetch_and, fetch_or Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250815122653.701782-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- accel/tcg/atomic_template.h | 80 +++++++++++++++++++++++++++-- include/accel/tcg/cpu-ldst-common.h | 13 +++-- 2 files changed, 86 insertions(+), 7 deletions(-) diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index 08a475c10ca04..ae5203b43904a 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -100,7 +100,6 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr, return ret; } -#if DATA_SIZE < 16 ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) { @@ -108,7 +107,28 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, DATA_SIZE, retaddr); DATA_TYPE ret; +#if DATA_SIZE == 16 + ret = atomic16_xchg(haddr, val); +#else ret = qatomic_xchg__nocheck(haddr, val); +#endif + ATOMIC_MMU_CLEANUP; + atomic_trace_rmw_post(env, addr, + VALUE_LOW(ret), + VALUE_HIGH(ret), + VALUE_LOW(val), + VALUE_HIGH(val), + oi); + return ret; +} + +#if DATA_SIZE == 16 +ABI_TYPE ATOMIC_NAME(fetch_and)(CPUArchState *env, vaddr addr, ABI_TYPE val, + MemOpIdx oi, uintptr_t retaddr) +{ + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); + DATA_TYPE ret = atomic16_fetch_and(haddr, val); ATOMIC_MMU_CLEANUP; atomic_trace_rmw_post(env, addr, VALUE_LOW(ret), @@ -119,6 +139,22 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, return ret; } +ABI_TYPE ATOMIC_NAME(fetch_or)(CPUArchState *env, vaddr addr, ABI_TYPE val, + MemOpIdx oi, uintptr_t retaddr) +{ + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); + DATA_TYPE ret = atomic16_fetch_or(haddr, val); + ATOMIC_MMU_CLEANUP; + atomic_trace_rmw_post(env, addr, + VALUE_LOW(ret), + VALUE_HIGH(ret), + VALUE_LOW(val), + VALUE_HIGH(val), + oi); + return ret; +} +#else #define GEN_ATOMIC_HELPER(X) \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \ ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ @@ -188,7 +224,7 @@ GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) #undef GEN_ATOMIC_HELPER_FN -#endif /* DATA SIZE < 16 */ +#endif /* DATA SIZE == 16 */ #undef END @@ -225,7 +261,6 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr, return BSWAP(ret); } -#if DATA_SIZE < 16 ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) { @@ -233,7 +268,28 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, DATA_SIZE, retaddr); ABI_TYPE ret; +#if DATA_SIZE == 16 + ret = atomic16_xchg(haddr, BSWAP(val)); +#else ret = qatomic_xchg__nocheck(haddr, BSWAP(val)); +#endif + ATOMIC_MMU_CLEANUP; + atomic_trace_rmw_post(env, addr, + VALUE_LOW(ret), + VALUE_HIGH(ret), + VALUE_LOW(val), + VALUE_HIGH(val), + oi); + return BSWAP(ret); +} + +#if DATA_SIZE == 16 +ABI_TYPE ATOMIC_NAME(fetch_and)(CPUArchState *env, vaddr addr, ABI_TYPE val, + MemOpIdx oi, uintptr_t retaddr) +{ + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); + DATA_TYPE ret = atomic16_fetch_and(haddr, BSWAP(val)); ATOMIC_MMU_CLEANUP; atomic_trace_rmw_post(env, addr, VALUE_LOW(ret), @@ -244,6 +300,22 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, return BSWAP(ret); } +ABI_TYPE ATOMIC_NAME(fetch_or)(CPUArchState *env, vaddr addr, ABI_TYPE val, + MemOpIdx oi, uintptr_t retaddr) +{ + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); + DATA_TYPE ret = atomic16_fetch_or(haddr, BSWAP(val)); + ATOMIC_MMU_CLEANUP; + atomic_trace_rmw_post(env, addr, + VALUE_LOW(ret), + VALUE_HIGH(ret), + VALUE_LOW(val), + VALUE_HIGH(val), + oi); + return BSWAP(ret); +} +#else #define GEN_ATOMIC_HELPER(X) \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \ ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ @@ -317,7 +389,7 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new) #undef ADD #undef GEN_ATOMIC_HELPER_FN -#endif /* DATA_SIZE < 16 */ +#endif /* DATA_SIZE == 16 */ #undef END #endif /* DATA_SIZE > 1 */ diff --git a/include/accel/tcg/cpu-ldst-common.h b/include/accel/tcg/cpu-ldst-common.h index 8bf17c2fab0f7..17a3250dedaa4 100644 --- a/include/accel/tcg/cpu-ldst-common.h +++ b/include/accel/tcg/cpu-ldst-common.h @@ -100,9 +100,6 @@ GEN_ATOMIC_HELPER_ALL(umax_fetch) GEN_ATOMIC_HELPER_ALL(xchg) -#undef GEN_ATOMIC_HELPER_ALL -#undef GEN_ATOMIC_HELPER - Int128 cpu_atomic_cmpxchgo_le_mmu(CPUArchState *env, vaddr addr, Int128 cmpv, Int128 newv, MemOpIdx oi, uintptr_t retaddr); @@ -110,6 +107,16 @@ Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, vaddr addr, Int128 cmpv, Int128 newv, MemOpIdx oi, uintptr_t retaddr); +GEN_ATOMIC_HELPER(xchg, Int128, o_le) +GEN_ATOMIC_HELPER(xchg, Int128, o_be) +GEN_ATOMIC_HELPER(fetch_and, Int128, o_le) +GEN_ATOMIC_HELPER(fetch_and, Int128, o_be) +GEN_ATOMIC_HELPER(fetch_or, Int128, o_le) +GEN_ATOMIC_HELPER(fetch_or, Int128, o_be) + +#undef GEN_ATOMIC_HELPER_ALL +#undef GEN_ATOMIC_HELPER + uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra); uint16_t cpu_ldw_code_mmu(CPUArchState *env, vaddr addr, From 092ac2481a4301d0282227bb4ee8641b3f39e437 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Aug 2025 22:26:50 +1000 Subject: [PATCH 0513/1794] tcg: Add tcg_gen_atomic_{xchg,fetch_and,fetch_or}_i128 Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250815122653.701782-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- accel/tcg/atomic_common.c.inc | 9 ++++ accel/tcg/tcg-runtime.h | 12 +++++ include/tcg/tcg-op-common.h | 7 +++ include/tcg/tcg-op.h | 3 ++ tcg/tcg-op-ldst.c | 97 +++++++++++++++++++++++++++++++++-- 5 files changed, 125 insertions(+), 3 deletions(-) diff --git a/accel/tcg/atomic_common.c.inc b/accel/tcg/atomic_common.c.inc index 6056598c23d42..bca93a0ac49bd 100644 --- a/accel/tcg/atomic_common.c.inc +++ b/accel/tcg/atomic_common.c.inc @@ -122,5 +122,14 @@ GEN_ATOMIC_HELPERS(umax_fetch) GEN_ATOMIC_HELPERS(xchg) +#if HAVE_CMPXCHG128 +ATOMIC_HELPER(xchgo_be, Int128) +ATOMIC_HELPER(xchgo_le, Int128) +ATOMIC_HELPER(fetch_ando_be, Int128) +ATOMIC_HELPER(fetch_ando_le, Int128) +ATOMIC_HELPER(fetch_oro_be, Int128) +ATOMIC_HELPER(fetch_oro_le, Int128) +#endif + #undef ATOMIC_HELPER #undef GEN_ATOMIC_HELPERS diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h index c23b5e66c4631..8436599b9f1d0 100644 --- a/accel/tcg/tcg-runtime.h +++ b/accel/tcg/tcg-runtime.h @@ -63,6 +63,18 @@ DEF_HELPER_FLAGS_5(atomic_cmpxchgo_be, TCG_CALL_NO_WG, i128, env, i64, i128, i128, i32) DEF_HELPER_FLAGS_5(atomic_cmpxchgo_le, TCG_CALL_NO_WG, i128, env, i64, i128, i128, i32) +DEF_HELPER_FLAGS_4(atomic_xchgo_be, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) +DEF_HELPER_FLAGS_4(atomic_xchgo_le, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) +DEF_HELPER_FLAGS_4(atomic_fetch_ando_be, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) +DEF_HELPER_FLAGS_4(atomic_fetch_ando_le, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) +DEF_HELPER_FLAGS_4(atomic_fetch_oro_be, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) +DEF_HELPER_FLAGS_4(atomic_fetch_oro_le, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) #endif DEF_HELPER_FLAGS_5(nonatomic_cmpxchgo, TCG_CALL_NO_WG, diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index e1071adebf26a..f752ef440b209 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -344,6 +344,8 @@ void tcg_gen_atomic_xchg_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, TCGArg, MemOp, TCGType); void tcg_gen_atomic_xchg_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, TCGArg, MemOp, TCGType); +void tcg_gen_atomic_xchg_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128, + TCGArg, MemOp, TCGType); void tcg_gen_atomic_fetch_add_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, TCGArg, MemOp, TCGType); @@ -411,6 +413,11 @@ void tcg_gen_atomic_umax_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, void tcg_gen_atomic_umax_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_and_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_or_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128, + TCGArg, MemOp, TCGType); + /* Vector ops */ void tcg_gen_mov_vec(TCGv_vec, TCGv_vec); diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index c912578fdd2ee..232733cb718ae 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -134,13 +134,16 @@ DEF_ATOMIC3(tcg_gen_nonatomic_cmpxchg, i128) DEF_ATOMIC2(tcg_gen_atomic_xchg, i32) DEF_ATOMIC2(tcg_gen_atomic_xchg, i64) +DEF_ATOMIC2(tcg_gen_atomic_xchg, i128) DEF_ATOMIC2(tcg_gen_atomic_fetch_add, i32) DEF_ATOMIC2(tcg_gen_atomic_fetch_add, i64) DEF_ATOMIC2(tcg_gen_atomic_fetch_and, i32) DEF_ATOMIC2(tcg_gen_atomic_fetch_and, i64) +DEF_ATOMIC2(tcg_gen_atomic_fetch_and, i128) DEF_ATOMIC2(tcg_gen_atomic_fetch_or, i32) DEF_ATOMIC2(tcg_gen_atomic_fetch_or, i64) +DEF_ATOMIC2(tcg_gen_atomic_fetch_or, i128) DEF_ATOMIC2(tcg_gen_atomic_fetch_xor, i32) DEF_ATOMIC2(tcg_gen_atomic_fetch_xor, i64) DEF_ATOMIC2(tcg_gen_atomic_fetch_smin, i32) diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index 548496002d765..67c15fd4d0dc0 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -801,6 +801,8 @@ typedef void (*gen_atomic_op_i32)(TCGv_i32, TCGv_env, TCGv_i64, TCGv_i32, TCGv_i32); typedef void (*gen_atomic_op_i64)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64, TCGv_i32); +typedef void (*gen_atomic_op_i128)(TCGv_i128, TCGv_env, TCGv_i64, + TCGv_i128, TCGv_i32); #ifdef CONFIG_ATOMIC64 # define WITH_ATOMIC64(X) X, @@ -1201,6 +1203,94 @@ static void do_atomic_op_i64(TCGv_i64 ret, TCGTemp *addr, TCGv_i64 val, } } +static void do_nonatomic_op_i128(TCGv_i128 ret, TCGTemp *addr, TCGv_i128 val, + TCGArg idx, MemOp memop, bool new_val, + void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i128 t = tcg_temp_ebb_new_i128(); + TCGv_i128 r = tcg_temp_ebb_new_i128(); + + tcg_gen_qemu_ld_i128_int(r, addr, idx, memop); + gen(TCGV128_LOW(t), TCGV128_LOW(r), TCGV128_LOW(val)); + gen(TCGV128_HIGH(t), TCGV128_HIGH(r), TCGV128_HIGH(val)); + tcg_gen_qemu_st_i128_int(t, addr, idx, memop); + + tcg_gen_mov_i128(ret, r); + tcg_temp_free_i128(t); + tcg_temp_free_i128(r); +} + +static void do_atomic_op_i128(TCGv_i128 ret, TCGTemp *addr, TCGv_i128 val, + TCGArg idx, MemOp memop, void * const table[]) +{ + gen_atomic_op_i128 gen = table[memop & (MO_SIZE | MO_BSWAP)]; + + if (gen) { + MemOpIdx oi = make_memop_idx(memop & ~MO_SIGN, idx); + TCGv_i64 a64 = maybe_extend_addr64(addr); + gen(ret, tcg_env, a64, val, tcg_constant_i32(oi)); + maybe_free_addr64(a64); + return; + } + + gen_helper_exit_atomic(tcg_env); + /* Produce a result */ + tcg_gen_movi_i64(TCGV128_LOW(ret), 0); + tcg_gen_movi_i64(TCGV128_HIGH(ret), 0); +} + +#define GEN_ATOMIC_HELPER128(NAME, OP, NEW) \ +static void * const table_##NAME[(MO_SIZE | MO_BSWAP) + 1] = { \ + [MO_8] = gen_helper_atomic_##NAME##b, \ + [MO_16 | MO_LE] = gen_helper_atomic_##NAME##w_le, \ + [MO_16 | MO_BE] = gen_helper_atomic_##NAME##w_be, \ + [MO_32 | MO_LE] = gen_helper_atomic_##NAME##l_le, \ + [MO_32 | MO_BE] = gen_helper_atomic_##NAME##l_be, \ + WITH_ATOMIC64([MO_64 | MO_LE] = gen_helper_atomic_##NAME##q_le) \ + WITH_ATOMIC64([MO_64 | MO_BE] = gen_helper_atomic_##NAME##q_be) \ + WITH_ATOMIC128([MO_128 | MO_LE] = gen_helper_atomic_##NAME##o_le) \ + WITH_ATOMIC128([MO_128 | MO_BE] = gen_helper_atomic_##NAME##o_be) \ +}; \ +void tcg_gen_atomic_##NAME##_i32_chk(TCGv_i32 ret, TCGTemp *addr, \ + TCGv_i32 val, TCGArg idx, \ + MemOp memop, TCGType addr_type) \ +{ \ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); \ + tcg_debug_assert((memop & MO_SIZE) <= MO_32); \ + if (tcg_ctx->gen_tb->cflags & CF_PARALLEL) { \ + do_atomic_op_i32(ret, addr, val, idx, memop, table_##NAME); \ + } else { \ + do_nonatomic_op_i32(ret, addr, val, idx, memop, NEW, \ + tcg_gen_##OP##_i32); \ + } \ +} \ +void tcg_gen_atomic_##NAME##_i64_chk(TCGv_i64 ret, TCGTemp *addr, \ + TCGv_i64 val, TCGArg idx, \ + MemOp memop, TCGType addr_type) \ +{ \ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); \ + tcg_debug_assert((memop & MO_SIZE) <= MO_64); \ + if (tcg_ctx->gen_tb->cflags & CF_PARALLEL) { \ + do_atomic_op_i64(ret, addr, val, idx, memop, table_##NAME); \ + } else { \ + do_nonatomic_op_i64(ret, addr, val, idx, memop, NEW, \ + tcg_gen_##OP##_i64); \ + } \ +} \ +void tcg_gen_atomic_##NAME##_i128_chk(TCGv_i128 ret, TCGTemp *addr, \ + TCGv_i128 val, TCGArg idx, \ + MemOp memop, TCGType addr_type) \ +{ \ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); \ + tcg_debug_assert((memop & MO_SIZE) == MO_128); \ + if (tcg_ctx->gen_tb->cflags & CF_PARALLEL) { \ + do_atomic_op_i128(ret, addr, val, idx, memop, table_##NAME); \ + } else { \ + do_nonatomic_op_i128(ret, addr, val, idx, memop, NEW, \ + tcg_gen_##OP##_i64); \ + } \ +} + #define GEN_ATOMIC_HELPER(NAME, OP, NEW) \ static void * const table_##NAME[(MO_SIZE | MO_BSWAP) + 1] = { \ [MO_8] = gen_helper_atomic_##NAME##b, \ @@ -1239,8 +1329,8 @@ void tcg_gen_atomic_##NAME##_i64_chk(TCGv_i64 ret, TCGTemp *addr, \ } GEN_ATOMIC_HELPER(fetch_add, add, 0) -GEN_ATOMIC_HELPER(fetch_and, and, 0) -GEN_ATOMIC_HELPER(fetch_or, or, 0) +GEN_ATOMIC_HELPER128(fetch_and, and, 0) +GEN_ATOMIC_HELPER128(fetch_or, or, 0) GEN_ATOMIC_HELPER(fetch_xor, xor, 0) GEN_ATOMIC_HELPER(fetch_smin, smin, 0) GEN_ATOMIC_HELPER(fetch_umin, umin, 0) @@ -1266,6 +1356,7 @@ static void tcg_gen_mov2_i64(TCGv_i64 r, TCGv_i64 a, TCGv_i64 b) tcg_gen_mov_i64(r, b); } -GEN_ATOMIC_HELPER(xchg, mov2, 0) +GEN_ATOMIC_HELPER128(xchg, mov2, 0) #undef GEN_ATOMIC_HELPER +#undef GEN_ATOMIC_HELPER128 From 905c2c34fe3432879f31ef0aa64ce478e573276b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 14:50:06 +1000 Subject: [PATCH 0514/1794] target/arm: Rename isar_feature_aa64_atomics This is FEAT_LSE -- rename the predicate to match. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Message-id: 20250830045006.380393-1-richard.henderson@linaro.org Message-id: 20250815122653.701782-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- bsd-user/aarch64/target_arch_elf.h | 2 +- linux-user/aarch64/elfload.c | 2 +- target/arm/cpu-features.h | 2 +- target/arm/tcg/translate-a64.c | 24 ++++++++++++------------ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/bsd-user/aarch64/target_arch_elf.h b/bsd-user/aarch64/target_arch_elf.h index cc87f475b3f9f..cec254f88b9ce 100644 --- a/bsd-user/aarch64/target_arch_elf.h +++ b/bsd-user/aarch64/target_arch_elf.h @@ -114,7 +114,7 @@ static uint32_t get_elf_hwcap(void) GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3); GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4); GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); - GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS); + GET_FEATURE_ID(aa64_lse, ARM_HWCAP_A64_ATOMICS); GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM); GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP); GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA); diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index dd5f34398a221..8bf39c47304ba 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -154,7 +154,7 @@ abi_ulong get_elf_hwcap(CPUState *cs) GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3); GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4); GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); - GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS); + GET_FEATURE_ID(aa64_lse, ARM_HWCAP_A64_ATOMICS); GET_FEATURE_ID(aa64_lse2, ARM_HWCAP_A64_USCAT); GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM); GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP); diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index d48754bcf27bf..451b37b5b3923 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -406,7 +406,7 @@ static inline bool isar_feature_aa64_crc32(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64ISAR0, CRC32) != 0; } -static inline bool isar_feature_aa64_atomics(const ARMISARegisters *id) +static inline bool isar_feature_aa64_lse(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) >= 2; } diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 259aa70a36d60..0ba537268cda9 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3237,7 +3237,7 @@ static bool trans_LDXP(DisasContext *s, arg_stxr *a) static bool trans_CASP(DisasContext *s, arg_CASP *a) { - if (!dc_isar_feature(aa64_atomics, s)) { + if (!dc_isar_feature(aa64_lse, s)) { return false; } if (((a->rt | a->rs) & 1) != 0) { @@ -3250,7 +3250,7 @@ static bool trans_CASP(DisasContext *s, arg_CASP *a) static bool trans_CAS(DisasContext *s, arg_CAS *a) { - if (!dc_isar_feature(aa64_atomics, s)) { + if (!dc_isar_feature(aa64_lse, s)) { return false; } gen_compare_and_swap(s, a->rs, a->rt, a->rn, a->sz); @@ -3743,15 +3743,15 @@ static bool do_atomic_ld(DisasContext *s, arg_atomic *a, AtomicThreeOpFn *fn, return true; } -TRANS_FEAT(LDADD, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_add_i64, 0, false) -TRANS_FEAT(LDCLR, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_and_i64, 0, true) -TRANS_FEAT(LDEOR, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_xor_i64, 0, false) -TRANS_FEAT(LDSET, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_or_i64, 0, false) -TRANS_FEAT(LDSMAX, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_smax_i64, MO_SIGN, false) -TRANS_FEAT(LDSMIN, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_smin_i64, MO_SIGN, false) -TRANS_FEAT(LDUMAX, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_umax_i64, 0, false) -TRANS_FEAT(LDUMIN, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_umin_i64, 0, false) -TRANS_FEAT(SWP, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_xchg_i64, 0, false) +TRANS_FEAT(LDADD, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_add_i64, 0, false) +TRANS_FEAT(LDCLR, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_and_i64, 0, true) +TRANS_FEAT(LDEOR, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_xor_i64, 0, false) +TRANS_FEAT(LDSET, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_or_i64, 0, false) +TRANS_FEAT(LDSMAX, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_smax_i64, MO_SIGN, false) +TRANS_FEAT(LDSMIN, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_smin_i64, MO_SIGN, false) +TRANS_FEAT(LDUMAX, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_umax_i64, 0, false) +TRANS_FEAT(LDUMIN, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_umin_i64, 0, false) +TRANS_FEAT(SWP, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_xchg_i64, 0, false) static bool trans_LDAPR(DisasContext *s, arg_LDAPR *a) { @@ -3759,7 +3759,7 @@ static bool trans_LDAPR(DisasContext *s, arg_LDAPR *a) TCGv_i64 clean_addr; MemOp mop; - if (!dc_isar_feature(aa64_atomics, s) || + if (!dc_isar_feature(aa64_lse, s) || !dc_isar_feature(aa64_rcpc_8_3, s)) { return false; } From 99e441107efb34a8af990cedbfbda0567d0bb387 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Aug 2025 22:26:52 +1000 Subject: [PATCH 0515/1794] target/arm: Implement FEAT_LSE128 This feature contains the LDCLRP, LDSETP, and SWPP instructions. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250815122653.701782-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 5 ++++ target/arm/tcg/a64.decode | 7 +++++ target/arm/tcg/translate-a64.c | 49 ++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 451b37b5b3923..e49e0ae3af009 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -411,6 +411,11 @@ static inline bool isar_feature_aa64_lse(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) >= 2; } +static inline bool isar_feature_aa64_lse128(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) >= 3; +} + static inline bool isar_feature_aa64_rdm(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64ISAR0, RDM) != 0; diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 766c610c019fa..55ff6c504f112 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -546,6 +546,13 @@ SWP .. 111 0 00 . . 1 ..... 1000 00 ..... ..... @atomic LDAPR sz:2 111 0 00 1 0 1 11111 1100 00 rn:5 rt:5 +# Atomic 128-bit memory operations +&atomic128 rn rt rt2 a r +@atomic128 ........ a:1 r:1 . rt2:5 ...... rn:5 rt:5 &atomic128 +LDCLRP 00011001 . . 1 ..... 000100 ..... ..... @atomic128 +LDSETP 00011001 . . 1 ..... 001100 ..... ..... @atomic128 +SWPP 00011001 . . 1 ..... 100000 ..... ..... @atomic128 + # Load/store register (pointer authentication) # LDRA immediate is 10 bits signed and scaled, but the bits aren't all contiguous diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 0ba537268cda9..37bedc3780b78 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3753,6 +3753,55 @@ TRANS_FEAT(LDUMAX, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_umax_i64, 0, TRANS_FEAT(LDUMIN, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_umin_i64, 0, false) TRANS_FEAT(SWP, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_xchg_i64, 0, false) +typedef void Atomic128ThreeOpFn(TCGv_i128, TCGv_i64, TCGv_i128, TCGArg, MemOp); + +static bool do_atomic128_ld(DisasContext *s, arg_atomic128 *a, + Atomic128ThreeOpFn *fn, bool invert) +{ + MemOp mop; + int rlo, rhi; + TCGv_i64 clean_addr, tlo, thi; + TCGv_i128 t16; + + if (a->rt == 31 || a->rt2 == 31 || a->rt == a->rt2) { + return false; + } + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + mop = check_atomic_align(s, a->rn, MO_128); + clean_addr = gen_mte_check1(s, cpu_reg_sp(s, a->rn), false, + a->rn != 31, mop); + + rlo = (s->be_data == MO_LE ? a->rt : a->rt2); + rhi = (s->be_data == MO_LE ? a->rt2 : a->rt); + + tlo = read_cpu_reg(s, rlo, true); + thi = read_cpu_reg(s, rhi, true); + if (invert) { + tcg_gen_not_i64(tlo, tlo); + tcg_gen_not_i64(thi, thi); + } + /* + * The tcg atomic primitives are all full barriers. Therefore we + * can ignore the Acquire and Release bits of this instruction. + */ + t16 = tcg_temp_new_i128(); + tcg_gen_concat_i64_i128(t16, tlo, thi); + + fn(t16, clean_addr, t16, get_mem_index(s), mop); + + tcg_gen_extr_i128_i64(cpu_reg(s, rlo), cpu_reg(s, rhi), t16); + return true; +} + +TRANS_FEAT(LDCLRP, aa64_lse128, do_atomic128_ld, + a, tcg_gen_atomic_fetch_and_i128, true) +TRANS_FEAT(LDSETP, aa64_lse128, do_atomic128_ld, + a, tcg_gen_atomic_fetch_or_i128, false) +TRANS_FEAT(SWPP, aa64_lse128, do_atomic128_ld, + a, tcg_gen_atomic_xchg_i128, false) + static bool trans_LDAPR(DisasContext *s, arg_LDAPR *a) { bool iss_sf = ldst_iss_sf(a->sz, false, false); From 23f5b02447503d9e28f6fad690ccce850c7bb656 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Aug 2025 22:26:53 +1000 Subject: [PATCH 0516/1794] target/arm: Enable FEAT_LSE128 for -cpu max Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250815122653.701782-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + linux-user/aarch64/elfload.c | 1 + target/arm/tcg/cpu64.c | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index b12f013b4fc1b..4e8aca8b5d51c 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -89,6 +89,7 @@ the following architecture extensions: - FEAT_LRCPC2 (Load-acquire RCpc instructions v2) - FEAT_LSE (Large System Extensions) - FEAT_LSE2 (Large System Extensions v2) +- FEAT_LSE128 (128-bit Atomics) - FEAT_LVA (Large Virtual Address space) - FEAT_MixedEnd (Mixed-endian support) - FEAT_MixedEndEL0 (Mixed-endian support at EL0) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 8bf39c47304ba..77d03b50e1b93 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -216,6 +216,7 @@ abi_ulong get_elf_hwcap2(CPUState *cs) GET_FEATURE_ID(aa64_sme_f16f16, ARM_HWCAP2_A64_SME_F16F16); GET_FEATURE_ID(aa64_sve_b16b16, ARM_HWCAP2_A64_SVE_B16B16); GET_FEATURE_ID(aa64_cssc, ARM_HWCAP2_A64_CSSC); + GET_FEATURE_ID(aa64_lse128, ARM_HWCAP2_A64_LSE128); return hwcaps; } diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index eaf8846a6a52a..b8b1981e702ce 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1145,7 +1145,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); /* FEAT_SHA1 */ t = FIELD_DP64(t, ID_AA64ISAR0, SHA2, 2); /* FEAT_SHA512 */ t = FIELD_DP64(t, ID_AA64ISAR0, CRC32, 1); /* FEAT_CRC32 */ - t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 2); /* FEAT_LSE */ + t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 3); /* FEAT_LSE, FEAT_LSE128 */ t = FIELD_DP64(t, ID_AA64ISAR0, RDM, 1); /* FEAT_RDM */ t = FIELD_DP64(t, ID_AA64ISAR0, SHA3, 1); /* FEAT_SHA3 */ t = FIELD_DP64(t, ID_AA64ISAR0, SM3, 1); /* FEAT_SM3 */ From 2e27650bddd35477d994a795a3b1cb57c8ed5c76 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 21 Aug 2025 16:42:29 +0100 Subject: [PATCH 0517/1794] hw/arm/stm32f205_soc: Don't leak TYPE_OR_IRQ objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In stm32f250_soc_initfn() we mostly use the standard pattern for child objects of calling object_initialize_child(). However for s->adc_irqs we call object_new() and then later qdev_realize(), and we never unref the object on deinit. This causes a leak, detected by ASAN on the device-introspect-test: Indirect leak of 10 byte(s) in 1 object(s) allocated from: #0 0x5b9fc4789de3 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-arm+0x21f1de3) (BuildId: 267a2619a026ed91c78a07b1eb2ef15381538efe) #1 0x740de3f28b09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x740de3f3e4d8 in g_strdup (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x784d8) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #3 0x5b9fc70159e1 in g_strdup_inline /usr/include/glib-2.0/glib/gstrfuncs.h:321:10 #4 0x5b9fc70159e1 in object_property_try_add /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:1276:18 #5 0x5b9fc7015f94 in object_property_add /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:1294:12 #6 0x5b9fc701b900 in object_add_link_prop /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:2021:10 #7 0x5b9fc701b3fc in object_property_add_link /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:2037:12 #8 0x5b9fc4c299fb in qdev_init_gpio_out_named /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/core/gpio.c:90:9 #9 0x5b9fc4c29b26 in qdev_init_gpio_out /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/core/gpio.c:101:5 #10 0x5b9fc4c0f77a in or_irq_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/core/or-irq.c:70:5 #11 0x5b9fc70257e1 in object_init_with_type /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:428:9 #12 0x5b9fc700cd4b in object_initialize_with_type /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:570:5 #13 0x5b9fc700e66d in object_new_with_type /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:774:5 #14 0x5b9fc700e750 in object_new /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:789:12 #15 0x5b9fc68b2162 in stm32f205_soc_initfn /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/arm/stm32f205_soc.c:69:26 Switch to using object_initialize_child() like all our other child objects for this SoC object. Cc: qemu-stable@nongnu.org Fixes: b63041c8f6b ("STM32F205: Connect the ADC devices") Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250821154229.2417453-1-peter.maydell@linaro.org --- hw/arm/stm32f205_soc.c | 10 +++++----- include/hw/arm/stm32f205_soc.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index 229af7fb108b2..e3c7203c6e7fc 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -66,7 +66,7 @@ static void stm32f205_soc_initfn(Object *obj) TYPE_STM32F2XX_TIMER); } - s->adc_irqs = OR_IRQ(object_new(TYPE_OR_IRQ)); + object_initialize_child(obj, "adc-irq-orgate", &s->adc_irqs, TYPE_OR_IRQ); for (i = 0; i < STM_NUM_ADCS; i++) { object_initialize_child(obj, "adc[*]", &s->adc[i], TYPE_STM32F2XX_ADC); @@ -171,12 +171,12 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) } /* ADC 1 to 3 */ - object_property_set_int(OBJECT(s->adc_irqs), "num-lines", STM_NUM_ADCS, + object_property_set_int(OBJECT(&s->adc_irqs), "num-lines", STM_NUM_ADCS, &error_abort); - if (!qdev_realize(DEVICE(s->adc_irqs), NULL, errp)) { + if (!qdev_realize(DEVICE(&s->adc_irqs), NULL, errp)) { return; } - qdev_connect_gpio_out(DEVICE(s->adc_irqs), 0, + qdev_connect_gpio_out(DEVICE(&s->adc_irqs), 0, qdev_get_gpio_in(armv7m, ADC_IRQ)); for (i = 0; i < STM_NUM_ADCS; i++) { @@ -187,7 +187,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, adc_addr[i]); sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(DEVICE(s->adc_irqs), i)); + qdev_get_gpio_in(DEVICE(&s->adc_irqs), i)); } /* SPI 1 and 2 */ diff --git a/include/hw/arm/stm32f205_soc.h b/include/hw/arm/stm32f205_soc.h index 4f4c8bbebc1e4..46eda3403a939 100644 --- a/include/hw/arm/stm32f205_soc.h +++ b/include/hw/arm/stm32f205_soc.h @@ -59,7 +59,7 @@ struct STM32F205State { STM32F2XXADCState adc[STM_NUM_ADCS]; STM32F2XXSPIState spi[STM_NUM_SPIS]; - OrIRQState *adc_irqs; + OrIRQState adc_irqs; MemoryRegion sram; MemoryRegion flash; From a12ff7f807e3ae2e254ac1de45c3892831479a0f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 23 Jul 2025 15:15:04 +0200 Subject: [PATCH 0518/1794] ui/keymaps: Avoid trace crash and improve error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parse_keyboard_layout() passes a possibly null @filename to trace_keymap_parse(). Trace backend log then formats it with %s, which crashes on some systems. Fix by moving the null check before the trace_keymap_parse(). While there, improve the error messages a bit. Fixes: d3b787fa7dde (keymaps: add tracing) Signed-off-by: Markus Armbruster Message-ID: <20250723131504.1482657-1-armbru@redhat.com> Reviewed-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé --- ui/keymaps.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ui/keymaps.c b/ui/keymaps.c index 6ceaa97085abf..2359dbfe7e6d6 100644 --- a/ui/keymaps.c +++ b/ui/keymaps.c @@ -86,19 +86,25 @@ static int parse_keyboard_layout(kbd_layout_t *k, const name2keysym_t *table, const char *language, Error **errp) { + g_autofree char *filename = NULL; int ret; FILE *f; - char * filename; char line[1024]; char keyname[64]; int len; filename = qemu_find_file(QEMU_FILE_TYPE_KEYMAP, language); + if (!filename) { + error_setg(errp, "could not find keymap file for language '%s'", + language); + return -1; + } + trace_keymap_parse(filename); - f = filename ? fopen(filename, "r") : NULL; - g_free(filename); + + f = fopen(filename, "r"); if (!f) { - error_setg(errp, "could not read keymap file: '%s'", language); + error_setg_file_open(errp, errno, filename); return -1; } From 83f6dceb8f5c0a1efe806a9a4905cbc743a8a378 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 30 Jul 2025 09:27:09 +0200 Subject: [PATCH 0519/1794] qga: Fix ubsan warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When compiling QEMU with --enable-ubsan there is a undefined behavior warning when running "make check": .../qga/commands-linux.c:452:15: runtime error: applying non-zero offset 5 to null pointer #0 0x55ea7b89450c in build_guest_fsinfo_for_pci_dev ..../qga/commands-linux.c:452:15 Fix it by avoiding the additional pointer variable here and use an "offset" integer variable instead. Signed-off-by: Thomas Huth Reviewed-by: Daniel P. Berrangé Reviewed-by: Kostiantyn Kostiuk Link: https://lore.kernel.org/qemu-devel/20250730072709.27077-1-thuth@redhat.com Signed-off-by: Kostiantyn Kostiuk --- qga/commands-linux.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qga/commands-linux.c b/qga/commands-linux.c index 9dc0c82503563..4a09ddc760cc7 100644 --- a/qga/commands-linux.c +++ b/qga/commands-linux.c @@ -400,10 +400,10 @@ static bool build_guest_fsinfo_for_pci_dev(char const *syspath, Error **errp) { unsigned int pci[4], host, hosts[8], tgt[3]; - int i, nhosts = 0, pcilen; + int i, offset, nhosts = 0, pcilen; GuestPCIAddress *pciaddr = disk->pci_controller; bool has_ata = false, has_host = false, has_tgt = false; - char *p, *q, *driver = NULL; + char *p, *driver = NULL; bool ret = false; p = strstr(syspath, "/devices/pci"); @@ -445,13 +445,13 @@ static bool build_guest_fsinfo_for_pci_dev(char const *syspath, p = strstr(syspath, "/ata"); if (p) { - q = p + 4; + offset = 4; has_ata = true; } else { p = strstr(syspath, "/host"); - q = p + 5; + offset = 5; } - if (p && sscanf(q, "%u", &host) == 1) { + if (p && sscanf(p + offset, "%u", &host) == 1) { has_host = true; nhosts = build_hosts(syspath, p, has_ata, hosts, ARRAY_SIZE(hosts), errp); From 42bdb911c22f9449f7a310efc73b70548ca42b24 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Thu, 7 Aug 2025 15:32:21 +0200 Subject: [PATCH 0520/1794] qga: fix potentially not initialized nr_volumes in qga_vss_fsfreeze() In this function we could have this variable not initialized. If this could be acceptable on error, the variable could be left not initialized f.e. as follows: void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset) { ... if (mountpoints) { ... if (num_mount_points == 0) { /* If there is no valid mount points, just exit. */ goto out; } } ... if (!mountpoints) { ... if (num_fixed_drives == 0) { goto out; /* If there is no fixed drive, just exit. */ } } ... } Stay on safe side, initialize the variable at the beginning. Signed-off-by: Denis V. Lunev CC: Kostiantyn Kostiuk CC: Michael Roth Reviewed-by: Kostiantyn Kostiuk Link: https://lore.kernel.org/qemu-devel/20250807133221.1135453-1-den@openvz.org Signed-off-by: Kostiantyn Kostiuk --- qga/vss-win32.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qga/vss-win32.c b/qga/vss-win32.c index f444a25a70e40..b272bfc7829c4 100644 --- a/qga/vss-win32.c +++ b/qga/vss-win32.c @@ -157,6 +157,8 @@ void qga_vss_fsfreeze(int *nr_volume, bool freeze, .errp = errp, }; + *nr_volume = 0; + g_assert(errp); /* requester.cpp requires it */ func = (QGAVSSRequesterFunc)GetProcAddress(provider_lib, func_name); if (!func) { From 9646155bb01c076f982601b1ebdead73efcb58a1 Mon Sep 17 00:00:00 2001 From: Kostiantyn Kostiuk Date: Mon, 25 Aug 2025 17:52:40 +0300 Subject: [PATCH 0521/1794] qga-vss: Replace asserts with condition and report error Reviewed-by: Yan Vugenfirer Link: https://lore.kernel.org/qemu-devel/20250825145241.170717-2-kkostiuk@redhat.com Signed-off-by: Kostiantyn Kostiuk --- qga/vss-win32/requester.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp index 4401d55e3a41b..bc260abb96c40 100644 --- a/qga/vss-win32/requester.cpp +++ b/qga/vss-win32/requester.cpp @@ -347,7 +347,12 @@ void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset) goto out; } - assert(pCreateVssBackupComponents != NULL); + if (!pCreateVssBackupComponents) { + err_set(errset, (HRESULT)ERROR_PROC_NOT_FOUND, + "CreateVssBackupComponents proc address absent. Did you call requester_init()?"); + goto out; + } + hr = pCreateVssBackupComponents(&vss_ctx.pVssbc); if (FAILED(hr)) { err_set(errset, hr, "failed to create VSS backup components"); @@ -579,8 +584,16 @@ void requester_thaw(int *num_vols, void *mountpints, ErrorSet *errset) /* Tell the provider that the snapshot is finished. */ SetEvent(vss_ctx.hEventThaw); - assert(vss_ctx.pVssbc); - assert(vss_ctx.pAsyncSnapshot); + if (!vss_ctx.pVssbc) { + err_set(errset, (HRESULT)VSS_E_BAD_STATE, + "CreateVssBackupComponents is missing. Did you freeze the volumes?"); + return; + } + if (!vss_ctx.pAsyncSnapshot) { + err_set(errset, (HRESULT)VSS_E_BAD_STATE, + "AsyncSnapshot set is missing. Did you freeze the volumes?"); + return; + } HRESULT hr = WaitForAsync(vss_ctx.pAsyncSnapshot); switch (hr) { From ed42682a66ef9f5e892abb7bcc3f211e1180f075 Mon Sep 17 00:00:00 2001 From: Kostiantyn Kostiuk Date: Mon, 25 Aug 2025 17:52:41 +0300 Subject: [PATCH 0522/1794] qga-vss: Remove unused dependencies Reviewed-by: Yan Vugenfirer Link: https://lore.kernel.org/qemu-devel/20250825145241.170717-3-kkostiuk@redhat.com Signed-off-by: Kostiantyn Kostiuk --- qga/vss-win32/meson.build | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/qga/vss-win32/meson.build b/qga/vss-win32/meson.build index 0ac918910b427..a6b810f12a570 100644 --- a/qga/vss-win32/meson.build +++ b/qga/vss-win32/meson.build @@ -13,13 +13,11 @@ qga_vss = shared_module( link_args: link_args, vs_module_defs: 'qga-vss.def', dependencies: [ - glib, socket, cc.find_library('ole32'), cc.find_library('oleaut32'), cc.find_library('shlwapi'), - cc.find_library('uuid'), - cc.find_library('intl') + cc.find_library('uuid') ] ) From 3b0ba59762380fff9c91a031301f6f661abaac96 Mon Sep 17 00:00:00 2001 From: Kostiantyn Kostiuk Date: Mon, 25 Aug 2025 17:05:48 +0300 Subject: [PATCH 0523/1794] qga: Fix channel initialization check in run_agent_once Reviewed-by: Yan Vugenfirer Reviewed-by: Michal Privoznik Link: https://lore.kernel.org/qemu-devel/20250825140549.146617-2-kkostiuk@redhat.com Signed-off-by: Kostiantyn Kostiuk --- qga/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qga/main.c b/qga/main.c index 6c02f3ec386b3..a1bf8f53acba3 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1563,7 +1563,7 @@ static void cleanup_agent(GAState *s) static int run_agent_once(GAState *s) { if (!s->channel && - channel_init(s, s->config->method, s->config->channel_path, + !channel_init(s, s->config->method, s->config->channel_path, s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) { g_critical("failed to initialize guest agent channel"); return EXIT_FAILURE; From b44c8a6d837ed4e082dd03d79095a4e9141eff5b Mon Sep 17 00:00:00 2001 From: Kostiantyn Kostiuk Date: Mon, 25 Aug 2025 17:05:49 +0300 Subject: [PATCH 0524/1794] qga: ignore channel_init() fail if 'retry_path' is set On Windows, we run QGA with `-d --retry-path` options by default, and expect that QGA will start even without the vioserial driver and will wait for communication forever. Reviewed-by: Yan Vugenfirer Reviewed-by: Michal Privoznik Link: https://lore.kernel.org/qemu-devel/20250825140549.146617-3-kkostiuk@redhat.com Signed-off-by: Kostiantyn Kostiuk --- qga/main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/qga/main.c b/qga/main.c index a1bf8f53acba3..dd1c216f9a13a 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1512,8 +1512,12 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) if (!channel_init(s, s->config->method, s->config->channel_path, s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) { - g_critical("failed to initialize guest agent channel"); - return NULL; + if (s->config->retry_path) { + g_info("failed to initialize guest agent channel, will retry"); + } else { + g_critical("failed to initialize guest agent channel"); + return NULL; + } } if (config->daemonize) { From edf3780a7dad4658ab7b72ea37e310a2be9b16d3 Mon Sep 17 00:00:00 2001 From: Kostiantyn Kostiuk Date: Mon, 25 Aug 2025 16:53:11 +0300 Subject: [PATCH 0525/1794] qga-vss: Write hex value of error in log QGA-VSS writes error using error_setg_win32_internal, which call g_win32_error_message. g_win32_error_message - translate a Win32 error code (as returned by GetLastError()) into the corresponding message. In the same time, we call error_setg_win32_internal with error codes from different Windows componets like VSS or Performance monitor that provides different codes and can't be converted with g_win32_error_message. In this case, the empty suffix will be returned so error will be masked. This commit directly add hex value of error code. Reproduce: - Run QGA command: {"execute": "guest-fsfreeze-freeze-list", "arguments": {"mountpoints": ["D:"]}} QGA error example: - before changes: {"error": {"class": "GenericError", "desc": "failed to add D: to snapshot set: "}} - after changes: {"error": {"class": "GenericError", "desc": "failed to add D: to snapshot set: Windows error 0x8004230e: "}} Reviewed-by: Yan Vugenfirer Link: https://lore.kernel.org/qemu-devel/20250825135311.138330-1-kkostiuk@redhat.com Signed-off-by: Kostiantyn Kostiuk --- qga/vss-win32/requester.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp index bc260abb96c40..5615955b6f3e8 100644 --- a/qga/vss-win32/requester.cpp +++ b/qga/vss-win32/requester.cpp @@ -28,8 +28,9 @@ #define err_set(e, err, fmt, ...) { \ (e)->error_setg_win32_wrapper((e)->errp, __FILE__, __LINE__, __func__, \ - err, fmt, ## __VA_ARGS__); \ - qga_debug(fmt, ## __VA_ARGS__); \ + err, fmt ": Windows error 0x%lx", \ + ## __VA_ARGS__, err); \ + qga_debug(fmt ": Windows error 0x%lx", ## __VA_ARGS__, err); \ } /* Bad idea, works only when (e)->errp != NULL: */ #define err_is_set(e) ((e)->errp && *(e)->errp) From 85ff0e956bf26a93c92e4dca8f6257613269a0cf Mon Sep 17 00:00:00 2001 From: Kostiantyn Kostiuk Date: Mon, 25 Aug 2025 17:31:55 +0300 Subject: [PATCH 0526/1794] qga/installer: Remove QGA VSS if QGA installation failed When QGA Installer failed to install QGA service but install QGA VSS provider, provider should be removed before installer exits. Otherwise QGA VSS will has broken infomation and prevent QGA installation in next run. Reviewed-by: Yan Vugenfirer Link: https://lore.kernel.org/qemu-devel/20250825143155.160913-1-kkostiuk@redhat.com Signed-off-by: Kostiantyn Kostiuk --- qga/installer/qemu-ga.wxs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/qga/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs index df572adb4ad53..32b8308728dcb 100644 --- a/qga/installer/qemu-ga.wxs +++ b/qga/installer/qemu-ga.wxs @@ -151,6 +151,14 @@ Return="check" > + + @@ -174,8 +182,19 @@ - Installed - NOT REMOVE + + + + + + + NOT REMOVE + + + NOT REMOVE + + + Installed From 28c5d27dd4dc4100a96ff4c9e5871dd23c6b02ec Mon Sep 17 00:00:00 2001 From: "minglei.liu" Date: Fri, 11 Jul 2025 10:17:14 +0800 Subject: [PATCH 0527/1794] qga: Fix truncated output handling in guest-exec status reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: minglei.liu Fixes: a1853dca743 Reviewed-by: Daniel P. Berrangé Reviewed-by: Kostiantyn Kostiuk Link: https://lore.kernel.org/qemu-devel/20250711021714.91258-1-minglei.liu@smartx.com Signed-off-by: Kostiantyn Kostiuk --- qga/commands.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qga/commands.c b/qga/commands.c index 5a5fad31f89ee..5f20af25d3476 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -205,13 +205,15 @@ GuestExecStatus *qmp_guest_exec_status(int64_t pid, Error **errp) #endif if (gei->out.length > 0) { ges->out_data = g_base64_encode(gei->out.data, gei->out.length); - ges->has_out_truncated = gei->out.truncated; + ges->has_out_truncated = true; + ges->out_truncated = gei->out.truncated; } g_free(gei->out.data); if (gei->err.length > 0) { ges->err_data = g_base64_encode(gei->err.data, gei->err.length); - ges->has_err_truncated = gei->err.truncated; + ges->has_err_truncated = true; + ges->err_truncated = gei->err.truncated; } g_free(gei->err.data); From b2e4534a2c9ce3d20ba44d855f1e2b71cc53c3a3 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 23 Jul 2025 15:32:56 +0200 Subject: [PATCH 0528/1794] i386/kvm/vmsr_energy: Plug memory leak on failure to connect socket vmsr_open_socket() leaks the Error set by qio_channel_socket_connect_sync(). Plug the leak by not creating the Error. Fixes: 0418f90809ae (Add support for RAPL MSRs in KVM/Qemu) Signed-off-by: Markus Armbruster Message-ID: <20250723133257.1497640-2-armbru@redhat.com> Reviewed-by: Zhao Liu --- target/i386/kvm/vmsr_energy.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/target/i386/kvm/vmsr_energy.c b/target/i386/kvm/vmsr_energy.c index 58ce3df53a3e3..890322ae37226 100644 --- a/target/i386/kvm/vmsr_energy.c +++ b/target/i386/kvm/vmsr_energy.c @@ -57,13 +57,9 @@ QIOChannelSocket *vmsr_open_socket(const char *path) }; QIOChannelSocket *sioc = qio_channel_socket_new(); - Error *local_err = NULL; qio_channel_set_name(QIO_CHANNEL(sioc), "vmsr-helper"); - qio_channel_socket_connect_sync(sioc, - &saddr, - &local_err); - if (local_err) { + if (qio_channel_socket_connect_sync(sioc, &saddr, NULL) < 0) { /* Close socket. */ qio_channel_close(QIO_CHANNEL(sioc), NULL); object_unref(OBJECT(sioc)); From ec14a3de622ae30a8afa78b6f564bc743b753ee1 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 23 Jul 2025 15:32:57 +0200 Subject: [PATCH 0529/1794] vfio scsi ui: Error-check qio_channel_socket_connect_sync() the same way qio_channel_socket_connect_sync() returns 0 on success, and -1 on failure, with errp set. Some callers check the return value, and some check whether errp was set. For consistency, always check the return value, and always check it's negative. Signed-off-by: Markus Armbruster Message-ID: <20250723133257.1497640-3-armbru@redhat.com> Reviewed-by: Zhao Liu --- hw/vfio-user/proxy.c | 2 +- scsi/pr-manager-helper.c | 9 ++------- ui/input-barrier.c | 5 +---- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c index 2275d3fe395f2..2c03d49f97641 100644 --- a/hw/vfio-user/proxy.c +++ b/hw/vfio-user/proxy.c @@ -885,7 +885,7 @@ VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp) sioc = qio_channel_socket_new(); ioc = QIO_CHANNEL(sioc); - if (qio_channel_socket_connect_sync(sioc, addr, errp)) { + if (qio_channel_socket_connect_sync(sioc, addr, errp) < 0) { object_unref(OBJECT(ioc)); return NULL; } diff --git a/scsi/pr-manager-helper.c b/scsi/pr-manager-helper.c index 6b86f01b01fe5..aea751fb047d7 100644 --- a/scsi/pr-manager-helper.c +++ b/scsi/pr-manager-helper.c @@ -105,20 +105,15 @@ static int pr_manager_helper_initialize(PRManagerHelper *pr_mgr, .u.q_unix.path = path }; QIOChannelSocket *sioc = qio_channel_socket_new(); - Error *local_err = NULL; - uint32_t flags; int r; assert(!pr_mgr->ioc); qio_channel_set_name(QIO_CHANNEL(sioc), "pr-manager-helper"); - qio_channel_socket_connect_sync(sioc, - &saddr, - &local_err); + r = qio_channel_socket_connect_sync(sioc, &saddr, errp); g_free(path); - if (local_err) { + if (r < 0) { object_unref(OBJECT(sioc)); - error_propagate(errp, local_err); return -ENOTCONN; } diff --git a/ui/input-barrier.c b/ui/input-barrier.c index 9793258aac1d7..0a2198ca50031 100644 --- a/ui/input-barrier.c +++ b/ui/input-barrier.c @@ -490,7 +490,6 @@ static gboolean input_barrier_event(QIOChannel *ioc G_GNUC_UNUSED, static void input_barrier_complete(UserCreatable *uc, Error **errp) { InputBarrier *ib = INPUT_BARRIER(uc); - Error *local_err = NULL; if (!ib->name) { error_setg(errp, QERR_MISSING_PARAMETER, "name"); @@ -506,9 +505,7 @@ static void input_barrier_complete(UserCreatable *uc, Error **errp) ib->sioc = qio_channel_socket_new(); qio_channel_set_name(QIO_CHANNEL(ib->sioc), "barrier-client"); - qio_channel_socket_connect_sync(ib->sioc, &ib->saddr, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (qio_channel_socket_connect_sync(ib->sioc, &ib->saddr, errp) < 0) { return; } From a60e1544b515e5205489141fc76cdf26de3a5f1b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 25 Jul 2025 15:50:31 +0200 Subject: [PATCH 0530/1794] qtest/qom-test: Shallow testing of qom-list / qom-get This test traverses the QOM sub-tree rooted at /machine with a combination of qom-list and qom-get. In my x86_64 testing, it runs almost 12000 QMP commands in 34 seconds. With -m slow, we test more machines, and it takes almost 84000 commands in almost four minutes. Since commit 3dd93992ffb (tests/qtest/qom-test: unit test for qom-list-get), the test traverses this tree a second time, with qom-list-get. In my x86_64 testing, this takes some 200 QMP commands and around two seconds, and some 1100 in just under 12s with -m slow. Traversing the entire tree is useful, because it exercise the QOM property getters. Traversing it twice not so much. Make the qom-list / qom-get test shallow unless -m slow is given: don't recurse. Cuts the number of commands to around 600, and run time to under 5s for me. Signed-off-by: Markus Armbruster Message-ID: <20250725135034.2280477-3-armbru@redhat.com> Reviewed-by: Steve Sistare --- tests/qtest/qom-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index 4ade1c728c014..7dea0d802ddb5 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -180,7 +180,7 @@ static void test_properties(QTestState *qts, const char *path, bool recurse) links = g_slist_delete_link(links, links); } while (children) { - test_properties(qts, children->data, true); + test_properties(qts, children->data, g_test_slow()); g_free(children->data); children = g_slist_delete_link(children, children); } From 67a392f7cf2b1662c21fb609b25bd745d25a2d05 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 25 Jul 2025 15:50:32 +0200 Subject: [PATCH 0531/1794] qtest/qom-test: Traverse entire QOM tree This test traverses the QOM sub-tree rooted at /machine. Traverse the entire tree instead. The x86_64 test runs some 40 additional QMP commands, and stays under 5s for me. Signed-off-by: Markus Armbruster Message-ID: <20250725135034.2280477-4-armbru@redhat.com> Reviewed-by: Steve Sistare --- tests/qtest/qom-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index 7dea0d802ddb5..a2db56bf2201d 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -211,7 +211,7 @@ static void test_machine(gconstpointer data) test_properties(qts, "/machine", true); - qlist_append_str(paths, "/machine"); + qlist_append_str(paths, "/"); test_list_get(qts, paths); test_list_get_value(qts); From c4c3ee8c1de5f04b4a34b53c2f3d6e85f5a2fe6f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 25 Jul 2025 15:50:33 +0200 Subject: [PATCH 0532/1794] qtest/qom-test: Don't bother to execute QMP command quit Signed-off-by: Markus Armbruster Message-ID: <20250725135034.2280477-5-armbru@redhat.com> Reviewed-by: Steve Sistare --- tests/qtest/qom-test.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index a2db56bf2201d..2da9918e164eb 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -215,10 +215,6 @@ static void test_machine(gconstpointer data) test_list_get(qts, paths); test_list_get_value(qts); - response = qtest_qmp(qts, "{ 'execute': 'quit' }"); - g_assert(qdict_haskey(response, "return")); - qobject_unref(response); - qtest_quit(qts); g_free((void *)machine); } From f0f682675c9b95ef762cec8dd1b868171a3dda67 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 25 Jul 2025 15:50:34 +0200 Subject: [PATCH 0533/1794] MAINTAINERS: Cover tests/qtest/qom-test.c Signed-off-by: Markus Armbruster Message-ID: <20250725135034.2280477-6-armbru@redhat.com> --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8147fff3523ea..0207946537503 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3454,6 +3454,7 @@ F: qom/ F: tests/unit/check-qom-interface.c F: tests/unit/check-qom-proplist.c F: tests/unit/test-qdev-global-props.c +F: tests/qtest/qom-test.c QOM boilerplate conversion script M: Eduardo Habkost From c9a1ea9c52e6462ad5c7814f3abd65baa69dc4ce Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 28 Jul 2025 16:57:47 +0200 Subject: [PATCH 0534/1794] Revert "tests/qtest: use qos_printf instead of g_test_message" This reverts commit 30ea13e9d97dcbd4ea541ddf9e8857fa1d5cb30f. Also rewrites qos_printf() calls added later. "make check" prints many lines like stdout: 138: UNKNOWN: # # qos_test running single test in subprocess stdout: 139: UNKNOWN: # # set_protocol_features: 0x42 stdout: 140: UNKNOWN: # # set_owner: start of session stdout: 141: UNKNOWN: # # vhost-user: un-handled message: 14 stdout: 142: UNKNOWN: # # vhost-user: un-handled message: 14 stdout: 143: UNKNOWN: # # set_vring(0)=enabled stdout: 144: UNKNOWN: # # set_vring(1)=enabled stdout: 145: UNKNOWN: # # set_vring(0)=enabled stdout: 146: UNKNOWN: # # set_vring(1)=enabled stdout: 147: UNKNOWN: # # set_vring(0)=enabled stdout: 148: UNKNOWN: # # set_vring(1)=enabled stdout: 149: UNKNOWN: # # set_vring(0)=enabled stdout: 150: UNKNOWN: # # set_vring(1)=enabled stdout: 151: UNKNOWN: # # set_vring(0)=enabled stdout: 152: UNKNOWN: # # set_vring(1)=enabled stdout: 153: UNKNOWN: # # set_vring_num: 0/256 stdout: 154: UNKNOWN: # # set_vring_addr: 0x7f9060000000/0x7f905ffff000/0x7f9060001000 Turns out this is qos-test, and the culprit is a commit meant to ease debugging. Revert it until a better solution is found. Signed-off-by: Markus Armbruster Message-ID: <20250728145747.3165315-1-armbru@redhat.com> [Commit message clarified] --- tests/qtest/qos-test.c | 5 ----- tests/qtest/vhost-user-test.c | 27 +++++++++++++-------------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c index abfd4b9512d5a..00f39f33f65d7 100644 --- a/tests/qtest/qos-test.c +++ b/tests/qtest/qos-test.c @@ -328,11 +328,6 @@ static void walk_path(QOSGraphNode *orig_path, int len) int main(int argc, char **argv, char** envp) { g_test_init(&argc, &argv, NULL); - - if (g_test_subprocess()) { - qos_printf("qos_test running single test in subprocess\n"); - } - if (g_test_verbose()) { qos_printf("ENVIRONMENT VARIABLES: {\n"); for (char **env = envp; *env != 0; env++) { diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index 75cb3e44b217c..56472ca709f1b 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -26,7 +26,6 @@ #include "libqos/virtio-pci.h" #include "libqos/malloc-pc.h" -#include "libqos/qgraph_internal.h" #include "hw/virtio/virtio-net.h" #include "standard-headers/linux/vhost_types.h" @@ -345,7 +344,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) } if (size != VHOST_USER_HDR_SIZE) { - qos_printf("%s: Wrong message size received %d\n", __func__, size); + g_test_message("Wrong message size received %d", size); return; } @@ -356,8 +355,8 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) p += VHOST_USER_HDR_SIZE; size = qemu_chr_fe_read_all(chr, p, msg.size); if (size != msg.size) { - qos_printf("%s: Wrong message size received %d != %d\n", - __func__, size, msg.size); + g_test_message("Wrong message size received %d != %d", + size, msg.size); goto out; } } @@ -393,7 +392,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * We don't need to do anything here, the remote is just * letting us know it is in charge. Just log it. */ - qos_printf("set_owner: start of session\n"); + g_test_message("set_owner: start of session\n"); break; case VHOST_USER_GET_PROTOCOL_FEATURES: @@ -419,7 +418,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * the remote end to send this. There is no handshake reply so * just log the details for debugging. */ - qos_printf("set_protocol_features: 0x%"PRIx64 "\n", msg.payload.u64); + g_test_message("set_protocol_features: 0x%"PRIx64 "\n", msg.payload.u64); break; /* @@ -427,11 +426,11 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * address of the vrings but we can simply report them. */ case VHOST_USER_SET_VRING_NUM: - qos_printf("set_vring_num: %d/%d\n", + g_test_message("set_vring_num: %d/%d\n", msg.payload.state.index, msg.payload.state.num); break; case VHOST_USER_SET_VRING_ADDR: - qos_printf("set_vring_addr: 0x%"PRIx64"/0x%"PRIx64"/0x%"PRIx64"\n", + g_test_message("set_vring_addr: 0x%"PRIx64"/0x%"PRIx64"/0x%"PRIx64"\n", msg.payload.addr.avail_user_addr, msg.payload.addr.desc_user_addr, msg.payload.addr.used_user_addr); @@ -464,7 +463,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) case VHOST_USER_SET_VRING_CALL: /* consume the fd */ if (!qemu_chr_fe_get_msgfds(chr, &fd, 1) && fd < 0) { - qos_printf("call fd: %d, do not set non-blocking\n", fd); + g_test_message("call fd: %d, do not set non-blocking\n", fd); break; } /* @@ -510,12 +509,12 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * fully functioning vhost-user we would enable/disable the * vring monitoring. */ - qos_printf("set_vring(%d)=%s\n", msg.payload.state.index, + g_test_message("set_vring(%d)=%s\n", msg.payload.state.index, msg.payload.state.num ? "enabled" : "disabled"); break; default: - qos_printf("vhost-user: un-handled message: %d\n", msg.request); + g_test_message("vhost-user: un-handled message: %d\n", msg.request); break; } @@ -539,7 +538,7 @@ static const char *init_hugepagefs(void) } if (access(path, R_OK | W_OK | X_OK)) { - qos_printf("access on path (%s): %s", path, strerror(errno)); + g_test_message("access on path (%s): %s", path, strerror(errno)); g_test_fail(); return NULL; } @@ -549,13 +548,13 @@ static const char *init_hugepagefs(void) } while (ret != 0 && errno == EINTR); if (ret != 0) { - qos_printf("statfs on path (%s): %s", path, strerror(errno)); + g_test_message("statfs on path (%s): %s", path, strerror(errno)); g_test_fail(); return NULL; } if (fs.f_type != HUGETLBFS_MAGIC) { - qos_printf("Warning: path not on HugeTLBFS: %s", path); + g_test_message("Warning: path not on HugeTLBFS: %s", path); g_test_fail(); return NULL; } From a80151c9da1a848e5d3ad7153080beaf0745e4cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 27 Jun 2024 09:10:39 +0200 Subject: [PATCH 0535/1794] hw/sd/sdcard: Remove support for spec v1.10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support for spec v1.10 was deprecated in QEMU v9.1. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-ID: <20240627071040.36190-4-philmd@linaro.org> --- docs/about/deprecated.rst | 6 ------ docs/about/removed-features.rst | 5 +++++ hw/sd/sd.c | 12 ++---------- include/hw/sd/sd.h | 1 - 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 5d1579dcf8296..6ae69206817a4 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -434,12 +434,6 @@ recommending to switch to their stable counterparts: - "Zve64f" should be replaced with "zve64f" - "Zve64d" should be replaced with "zve64d" -``-device sd-card,spec_version=1`` (since 9.1) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -SD physical layer specification v2.00 supersedes the v1.10 one. -v2.00 is the default since QEMU 3.0.0. - Block device options '''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 25a904032c558..332d07e2b18d8 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1186,6 +1186,11 @@ by using ``-machine graphics=off``. The 'pvrdma' device and the whole RDMA subsystem have been removed. +``-device sd-card,spec_version=1`` (since 10.2) +''''''''''''''''''''''''''''''''''''''''''''''' + +SD physical layer specification v2.00 supersedes the v1.10 one. + Related binaries ---------------- diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 8c290595f011a..8b142e4796fbf 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -195,7 +195,6 @@ static bool sd_is_emmc(SDState *sd) static const char *sd_version_str(enum SDPhySpecificationVersion version) { static const char *sdphy_version[] = { - [SD_PHY_SPECv1_10_VERS] = "v1.10", [SD_PHY_SPECv2_00_VERS] = "v2.00", [SD_PHY_SPECv3_01_VERS] = "v3.01", }; @@ -407,11 +406,7 @@ static void sd_set_ocr(SDState *sd) static void sd_set_scr(SDState *sd) { sd->scr[0] = 0 << 4; /* SCR structure version 1.0 */ - if (sd->spec_version == SD_PHY_SPECv1_10_VERS) { - sd->scr[0] |= 1; /* Spec Version 1.10 */ - } else { - sd->scr[0] |= 2; /* Spec Version 2.00 or Version 3.0X */ - } + sd->scr[0] |= 2; /* Spec Version 2.00 or Version 3.0X */ sd->scr[1] = (2 << 4) /* SDSC Card (Security Version 1.01) */ | 0b0101; /* 1-bit or 4-bit width bus modes */ sd->scr[2] = 0x00; /* Extended Security is not supported. */ @@ -1555,9 +1550,6 @@ static sd_rsp_type_t sd_cmd_DE_SELECT_CARD(SDState *sd, SDRequest req) /* CMD8 */ static sd_rsp_type_t sd_cmd_SEND_IF_COND(SDState *sd, SDRequest req) { - if (sd->spec_version < SD_PHY_SPECv2_00_VERS) { - return sd_cmd_illegal(sd, req); - } if (sd->state != sd_idle_state) { return sd_invalid_state_for_cmd(sd, req); } @@ -2773,7 +2765,7 @@ static void sd_realize(DeviceState *dev, Error **errp) int ret; switch (sd->spec_version) { - case SD_PHY_SPECv1_10_VERS + case SD_PHY_SPECv2_00_VERS ... SD_PHY_SPECv3_01_VERS: break; default: diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h index 55d363f58fb17..91b5c40a5f893 100644 --- a/include/hw/sd/sd.h +++ b/include/hw/sd/sd.h @@ -56,7 +56,6 @@ #define AKE_SEQ_ERROR (1 << 3) enum SDPhySpecificationVersion { - SD_PHY_SPECv1_10_VERS = 1, SD_PHY_SPECv2_00_VERS = 2, SD_PHY_SPECv3_01_VERS = 3, }; From b8d6e05f16b77231d11b96659072b302290b3396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 10 Jun 2025 11:19:34 +0200 Subject: [PATCH 0536/1794] target/ppc/kvm: Avoid using alloca() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kvmppc_load_htab_chunk() is used for migration, thus is not a hot path. Use the heap instead of the stack, removing the alloca() call. Reported-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Reviewed-by: Manos Pitsidianakis Reviewed-by: Harsh Prateek Bora Reviewed-by: Stefan Hajnoczi Message-Id: <20250901132626.28639-2-philmd@linaro.org> --- target/ppc/kvm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index d145774b09ae6..2521ff65c6c35 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -2760,11 +2760,11 @@ int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, int64_t max_ns) int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index, uint16_t n_valid, uint16_t n_invalid, Error **errp) { - struct kvm_get_htab_header *buf; - size_t chunksize = sizeof(*buf) + n_valid * HASH_PTE_SIZE_64; + size_t chunksize = sizeof(struct kvm_get_htab_header) + + n_valid * HASH_PTE_SIZE_64; + g_autofree struct kvm_get_htab_header *buf = g_malloc(chunksize); ssize_t rc; - buf = alloca(chunksize); buf->index = index; buf->n_valid = n_valid; buf->n_invalid = n_invalid; From 32ee080ccdde8a90d5ee3b56e28f95ada35dee4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 10 Jun 2025 11:25:39 +0200 Subject: [PATCH 0537/1794] docs/devel/style: Mention alloca() family API is forbidden MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested-by: Alex Bennée Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Stefan Hajnoczi Message-Id: <20250901132626.28639-4-philmd@linaro.org> --- docs/devel/style.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/devel/style.rst b/docs/devel/style.rst index d025933808e11..941fe14bfd49b 100644 --- a/docs/devel/style.rst +++ b/docs/devel/style.rst @@ -446,8 +446,8 @@ Low level memory management =========================== Use of the ``malloc/free/realloc/calloc/valloc/memalign/posix_memalign`` -APIs is not allowed in the QEMU codebase. Instead of these routines, -use the GLib memory allocation routines +or ``alloca/g_alloca/g_newa/g_newa0`` APIs is not allowed in the QEMU codebase. +Instead of these routines, use the GLib memory allocation routines ``g_malloc/g_malloc0/g_new/g_new0/g_realloc/g_free`` or QEMU's ``qemu_memalign/qemu_blockalign/qemu_vfree`` APIs. From e74416713fe166a6f21cc5ee2000cfd0c248e1a7 Mon Sep 17 00:00:00 2001 From: Djordje Todorovic Date: Wed, 18 Jun 2025 12:27:49 +0000 Subject: [PATCH 0538/1794] hw/pci: Allow explicit function numbers in pci MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since there is no pch_gbe emulation, we could be using func other than 0 when adding new devices to specific boards. Signed-off-by: Chao-ying Fu Signed-off-by: Djordje Todorovic Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250901102850.1172983-13-djordje.todorovic@htecgroup.com> [PMD: Compare with null character ('\0'), not '0'] Signed-off-by: Philippe Mathieu-Daudé --- hw/pci/pci.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index c70b5ceebaf1f..297196b2421a7 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -984,14 +984,15 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp, slot = val; - if (funcp != NULL) { - if (*e != '.') + if (funcp != NULL && *e != '\0') { + if (*e != '.') { return -1; - + } p = e + 1; val = strtoul(p, &e, 16); - if (e == p) + if (e == p) { return -1; + } func = val; } @@ -2054,13 +2055,15 @@ bool pci_init_nic_in_slot(PCIBus *rootbus, const char *model, int dom, busnr, devfn; PCIDevice *pci_dev; unsigned slot; + unsigned func; + PCIBus *bus; if (!nd) { return false; } - if (!devaddr || pci_parse_devaddr(devaddr, &dom, &busnr, &slot, NULL) < 0) { + if (!devaddr || pci_parse_devaddr(devaddr, &dom, &busnr, &slot, &func) < 0) { error_report("Invalid PCI device address %s for device %s", devaddr, model); exit(1); @@ -2071,7 +2074,7 @@ bool pci_init_nic_in_slot(PCIBus *rootbus, const char *model, exit(1); } - devfn = PCI_DEVFN(slot, 0); + devfn = PCI_DEVFN(slot, func); bus = pci_find_bus_nr(rootbus, busnr); if (!bus) { From 7ef54b7bf38104c59523a0d7559ae964676178b2 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 30 Jul 2025 15:04:34 -0700 Subject: [PATCH 0539/1794] migration: compile migration/ram.c once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-ID: <20250730220435.1139101-2-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- migration/meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migration/meson.build b/migration/meson.build index 276da3be5a32e..45e9445f97d8b 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -31,6 +31,7 @@ system_ss.add(files( 'multifd-zero-page.c', 'options.c', 'postcopy-ram.c', + 'ram.c', 'savevm.c', 'socket.c', 'tls.c', @@ -50,5 +51,4 @@ system_ss.add(when: uadk, if_true: files('multifd-uadk.c')) system_ss.add(when: qatzip, if_true: files('multifd-qatzip.c')) specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', - if_true: files('ram.c', - 'vfio.c')) + if_true: files('vfio.c')) From 01b6fb37056bf5c6a734e77b386f4e9c830b2ce0 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 30 Jul 2025 15:04:35 -0700 Subject: [PATCH 0540/1794] migration/vfio: compile only once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-ID: <20250730220435.1139101-3-pierrick.bouvier@linaro.org> [PMD: Cover vfio-stub.c in MAINTAINERS] Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + migration/meson.build | 6 +++--- migration/vfio-stub.c | 16 ++++++++++++++++ migration/vfio.c | 14 -------------- 4 files changed, 20 insertions(+), 17 deletions(-) create mode 100644 migration/vfio-stub.c diff --git a/MAINTAINERS b/MAINTAINERS index 0207946537503..040ef06494cf3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2268,6 +2268,7 @@ F: util/vfio-helpers.c F: include/hw/vfio/ F: docs/devel/migration/vfio.rst F: qapi/vfio.json +F: migration/vfio-stub.c F: tests/functional/aarch64/test_device_passthrough.py vfio-igd diff --git a/migration/meson.build b/migration/meson.build index 45e9445f97d8b..0f71544a8254c 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -49,6 +49,6 @@ system_ss.add(when: zstd, if_true: files('multifd-zstd.c')) system_ss.add(when: qpl, if_true: files('multifd-qpl.c')) system_ss.add(when: uadk, if_true: files('multifd-uadk.c')) system_ss.add(when: qatzip, if_true: files('multifd-qatzip.c')) - -specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', - if_true: files('vfio.c')) +system_ss.add(when: 'CONFIG_VFIO', + if_true: files('vfio.c'), + if_false: files('vfio-stub.c')) diff --git a/migration/vfio-stub.c b/migration/vfio-stub.c new file mode 100644 index 0000000000000..f59ebe075dc73 --- /dev/null +++ b/migration/vfio-stub.c @@ -0,0 +1,16 @@ +/* + * QEMU live migration - stubs for VFIO + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "migration.h" + +void migration_populate_vfio_info(MigrationInfo *info) +{ +} + +void migration_reset_vfio_bytes_transferred(void) +{ +} diff --git a/migration/vfio.c b/migration/vfio.c index 0b64e49ef060b..af6ae2c1e19e6 100644 --- a/migration/vfio.c +++ b/migration/vfio.c @@ -8,13 +8,8 @@ #include "qemu/osdep.h" #include "qapi/qapi-types-migration.h" #include "migration.h" -#include CONFIG_DEVICES - -#ifdef CONFIG_VFIO #include "hw/vfio/vfio-migration.h" -#endif -#ifdef CONFIG_VFIO void migration_populate_vfio_info(MigrationInfo *info) { if (vfio_migration_active()) { @@ -27,12 +22,3 @@ void migration_reset_vfio_bytes_transferred(void) { vfio_migration_reset_bytes_transferred(); } -#else -void migration_populate_vfio_info(MigrationInfo *info) -{ -} - -void migration_reset_vfio_bytes_transferred(void) -{ -} -#endif From 38838f0837a3fcfadf000d885329216195a5f9ae Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 30 Jul 2025 15:05:17 -0700 Subject: [PATCH 0541/1794] cpu-target: build compilation unit once for user/system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250730220519.1140447-2-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- cpu-target.c | 5 ----- meson.build | 3 ++- target-info-stub.c | 4 ++++ 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index 772e35495b857..f030e2c642ed0 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "cpu.h" #include "accel/accel-cpu-ops.h" #include "system/cpus.h" #include "exec/cpu-common.h" @@ -27,10 +26,6 @@ #include "hw/core/cpu.h" #include "trace/trace-root.h" -/* Validate correct placement of CPUArchState. */ -QEMU_BUILD_BUG_ON(offsetof(ArchCPU, parent_obj) != 0); -QEMU_BUILD_BUG_ON(offsetof(ArchCPU, env) != sizeof(CPUState)); - /* enable or disable single step mode. EXCP_DEBUG is returned by the CPU loop after each instruction */ void cpu_single_step(CPUState *cpu, int enabled) diff --git a/meson.build b/meson.build index 0d42de61ae6c6..7ff84787cf197 100644 --- a/meson.build +++ b/meson.build @@ -3876,7 +3876,8 @@ if have_block endif common_ss.add(files('cpu-common.c')) -specific_ss.add(files('cpu-target.c')) +user_ss.add(files('cpu-target.c')) +system_ss.add(files('cpu-target.c')) subdir('system') diff --git a/target-info-stub.c b/target-info-stub.c index ca0caa3686c3a..d96d8249c1dd8 100644 --- a/target-info-stub.c +++ b/target-info-stub.c @@ -12,6 +12,10 @@ #include "hw/boards.h" #include "cpu.h" +/* Validate correct placement of CPUArchState. */ +QEMU_BUILD_BUG_ON(offsetof(ArchCPU, parent_obj) != 0); +QEMU_BUILD_BUG_ON(offsetof(ArchCPU, env) != sizeof(CPUState)); + static const TargetInfo target_info_stub = { .target_name = TARGET_NAME, .target_arch = SYS_EMU_TARGET__MAX, From 9dbb61bb2cfea836bfdb10260a5ebe4d0678463a Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 30 Jul 2025 15:05:18 -0700 Subject: [PATCH 0542/1794] include/exec/target_page.h: move page-target.c to header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250730220519.1140447-3-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- include/exec/target_page.h | 11 ++++++++++- meson.build | 2 +- page-target.c | 21 --------------------- 3 files changed, 11 insertions(+), 23 deletions(-) delete mode 100644 page-target.c diff --git a/include/exec/target_page.h b/include/exec/target_page.h index ca0ebbc8bbd2c..813591c9b51cf 100644 --- a/include/exec/target_page.h +++ b/include/exec/target_page.h @@ -62,6 +62,15 @@ static inline int qemu_target_page_bits(void) return TARGET_PAGE_BITS; } -size_t qemu_target_pages_to_MiB(size_t pages); +/* Convert target pages to MiB (2**20). */ +static inline size_t qemu_target_pages_to_MiB(size_t pages) +{ + int page_bits = TARGET_PAGE_BITS; + + /* So far, the largest (non-huge) page size is 64k, i.e. 16 bits. */ + g_assert(page_bits < 20); + + return pages >> (20 - page_bits); +} #endif diff --git a/meson.build b/meson.build index 7ff84787cf197..fa6186db33435 100644 --- a/meson.build +++ b/meson.build @@ -3899,7 +3899,7 @@ if get_option('b_lto') pagevary = declare_dependency(link_with: pagevary) endif common_ss.add(pagevary) -specific_ss.add(files('page-target.c', 'page-vary-target.c')) +specific_ss.add(files('page-vary-target.c')) common_ss.add(files('target-info.c')) specific_ss.add(files('target-info-stub.c')) diff --git a/page-target.c b/page-target.c deleted file mode 100644 index 8fcd5443b526b..0000000000000 --- a/page-target.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * QEMU page values getters (target independent) - * - * Copyright (c) 2003 Fabrice Bellard - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "qemu/osdep.h" -#include "exec/target_page.h" - -/* Convert target pages to MiB (2**20). */ -size_t qemu_target_pages_to_MiB(size_t pages) -{ - int page_bits = TARGET_PAGE_BITS; - - /* So far, the largest (non-huge) page size is 64k, i.e. 16 bits. */ - g_assert(page_bits < 20); - - return pages >> (20 - page_bits); -} From 0df57e00d22412320873c2a7548f49c72b247e42 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Fri, 1 Aug 2025 10:40:05 -0700 Subject: [PATCH 0543/1794] hw/meson: enter target hw first MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can reuse target source sets for "generic" devices that are related to a single architecture (like interrupt controllers). Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20250801174006.2466508-2-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/meson.build | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/hw/meson.build b/hw/meson.build index 791ce21ab42af..1022bdb8069a1 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -1,3 +1,26 @@ +# Enter target code first to reuse variables associated +subdir('alpha') +subdir('arm') +subdir('avr') +subdir('hppa') +subdir('xenpv') # i386 uses it +subdir('i386') +subdir('loongarch') +subdir('m68k') +subdir('microblaze') +subdir('mips') +subdir('openrisc') +subdir('ppc') +subdir('remote') +subdir('riscv') +subdir('rx') +subdir('s390x') +subdir('sh4') +subdir('sparc') +subdir('sparc64') +subdir('tricore') +subdir('xtensa') + subdir('9pfs') subdir('acpi') subdir('adc') @@ -44,26 +67,4 @@ subdir('virtio') subdir('vmapple') subdir('watchdog') subdir('xen') -subdir('xenpv') subdir('fsi') - -subdir('alpha') -subdir('arm') -subdir('avr') -subdir('hppa') -subdir('i386') -subdir('loongarch') -subdir('m68k') -subdir('microblaze') -subdir('mips') -subdir('openrisc') -subdir('ppc') -subdir('remote') -subdir('riscv') -subdir('rx') -subdir('s390x') -subdir('sh4') -subdir('sparc') -subdir('sparc64') -subdir('tricore') -subdir('xtensa') From c0a3bdf62c260aa647491b4906c55177a2c08d23 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Fri, 1 Aug 2025 10:40:06 -0700 Subject: [PATCH 0544/1794] hw/intc: compile some arm related source once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let kvm related gic file out for now, as they are compiled only on aarch64 hosts. Signed-off-by: Pierrick Bouvier Tested-by: Philippe Mathieu-Daudé Message-ID: <20250801174006.2466508-3-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/meson.build | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 3137521a4ad19..3efb276b6e628 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -38,11 +38,11 @@ if config_all_devices.has_key('CONFIG_APIC') or \ endif specific_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c', 'apic_common.c')) -specific_ss.add(when: 'CONFIG_ARM_GIC', if_true: files('arm_gicv3_cpuif_common.c')) -specific_ss.add(when: 'CONFIG_ARM_GICV3', if_true: files('arm_gicv3_cpuif.c')) +arm_common_ss.add(when: 'CONFIG_ARM_GIC', if_true: files('arm_gicv3_cpuif_common.c')) +arm_common_ss.add(when: 'CONFIG_ARM_GICV3', if_true: files('arm_gicv3_cpuif.c')) specific_ss.add(when: 'CONFIG_ARM_GIC_KVM', if_true: files('arm_gic_kvm.c')) specific_ss.add(when: ['CONFIG_ARM_GIC_KVM', 'TARGET_AARCH64'], if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.c')) -specific_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c')) +arm_common_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c')) specific_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_irqmp.c')) specific_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic.c')) specific_ss.add(when: 'CONFIG_LOONGSON_LIOINTC', if_true: files('loongson_liointc.c')) From 8e4649cac9bcddc050d2df07908075e9e69bccc7 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 7 Aug 2025 13:08:06 +0200 Subject: [PATCH 0545/1794] e1000e: Prevent crash from legacy interrupt firing after MSI-X enable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A race condition between guest driver actions and QEMU timers can lead to an assertion failure when the guest switches the e1000e from legacy interrupt mode to MSI-X. If a legacy interrupt delay timer (TIDV or RDTR) is active, but the guest enables MSI-X before the timer fires, the pending interrupt cause can trigger an assert in e1000e_intmgr_collect_delayed_causes(). This patch removes the assertion and executes the code that clears the pending legacy causes. This change is safe and introduces no unintended behavioral side effects, as it only alters a state that previously led to termination. - when core->delayed_causes == 0 the function was already a no-op and remains so. - when core->delayed_causes != 0 the function would previously crash due to the assertion failure. The patch now defines a safe outcome by clearing the cause and returning. Since behavior after the assertion never existed, this simply corrects the crash. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1863 Suggested-by: Akihiko Odaki Signed-off-by: Laurent Vivier Acked-by: Jason Wang Reviewed-by: Akihiko Odaki Message-ID: <20250807110806.409065-1-lvivier@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/e1000e_core.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index 24138587905b4..06657bb3ac5ca 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -341,11 +341,6 @@ e1000e_intmgr_collect_delayed_causes(E1000ECore *core) { uint32_t res; - if (msix_enabled(core->owner)) { - assert(core->delayed_causes == 0); - return 0; - } - res = core->delayed_causes; core->delayed_causes = 0; From c5ade4f9d289fed61df7a04950f8f607e26353e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 11 Aug 2025 11:36:00 +0200 Subject: [PATCH 0546/1794] scripts/coverity-scan/COMPONENTS.md: Add a 'plugins' category MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cover the TCG plugins files under their own Coverity category. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Acked-by: Alex Bennée Message-Id: <20250811094341.91597-1-philmd@linaro.org> --- scripts/coverity-scan/COMPONENTS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md index 72995903ff9ea..95805b536bcf4 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -147,6 +147,9 @@ tcg system ~ .*/qemu(/system/.*|/accel/.*) +plugins + ~ .*/qemu(/contrib|/tests/tcg)?/plugins/.* + (headers) ~ .*/qemu(/include/.*) From 7baa9c39fc3d525216f0cedcfda5374c26d50e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 11 Aug 2025 11:31:08 +0200 Subject: [PATCH 0547/1794] hw/scsi/mptsas: Avoid silent integer truncation in MPI_FUNC_IOC_INIT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the MaxDevices 8-bit field of the request / response structures of the MPI_FUNCTION_IOC_INIT command, the 0x00 value means "max 256 devices". This is not a problem because when max_devices=256, its value (0x100), being casted to a uint8_t, is truncated to 0x00. However Coverity complains for an "Overflowed constant". Fix by re-using the request fields in the response, since they are not modified and use the same types. Fix: Coverity 1547736 (Overflowed constant) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-Id: <20250811095550.93655-1-philmd@linaro.org> --- 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 1ebe0b82a79d1..4ada35b7ec8fb 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -579,11 +579,11 @@ static void mptsas_process_ioc_init(MPTSASState *s, MPIMsgIOCInit *req) } memset(&reply, 0, sizeof(reply)); - reply.WhoInit = s->who_init; + reply.WhoInit = req->WhoInit; reply.MsgLength = sizeof(reply) / 4; reply.Function = req->Function; - reply.MaxDevices = s->max_devices; - reply.MaxBuses = s->max_buses; + reply.MaxDevices = req->MaxDevices; + reply.MaxBuses = req->MaxBuses; reply.MsgContext = req->MsgContext; mptsas_fix_ioc_init_reply_endianness(&reply); From 831d75fd735dbd116703d3a1ca5e271dc930ebae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Aug 2025 15:36:05 +0200 Subject: [PATCH 0548/1794] hw/ssi: Document ssi_transfer() method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A SPI transaction consists of shifting bit in sync with the CLK line, writing on the MOSI (output) line / and reading MISO (input) line. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Gustavo Romero Reviewed-by: Alex Bennée --- include/hw/ssi/ssi.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/hw/ssi/ssi.h b/include/hw/ssi/ssi.h index 3cdcbd5390428..2ad8033d8f5b7 100644 --- a/include/hw/ssi/ssi.h +++ b/include/hw/ssi/ssi.h @@ -38,6 +38,7 @@ struct SSIPeripheralClass { /* if you have standard or no CS behaviour, just override transfer. * This is called when the device cs is active (true by default). + * See ssi_transfer(). */ uint32_t (*transfer)(SSIPeripheral *dev, uint32_t val); /* called when the CS line changes. Optional, devices only need to implement @@ -52,6 +53,7 @@ struct SSIPeripheralClass { * of the CS behaviour at the device level. transfer, set_cs, and * cs_polarity are unused if this is overwritten. Transfer_raw will * always be called for the device for every txrx access to the parent bus + * See ssi_transfer(). */ uint32_t (*transfer_raw)(SSIPeripheral *dev, uint32_t val); }; @@ -110,6 +112,18 @@ bool ssi_realize_and_unref(DeviceState *dev, SSIBus *bus, Error **errp); /* Master interface. */ SSIBus *ssi_create_bus(DeviceState *parent, const char *name); +/** + * Transfer a word on a SSI bus + * @bus: SSI bus + * @val: word to transmit + * + * At the same time, read a word and write the @val one on the SSI bus. + * + * SSI words might vary between 8 and 32 bits. The same number of bits + * written is received. + * + * Return: word value received + */ uint32_t ssi_transfer(SSIBus *bus, uint32_t val); DeviceState *ssi_get_cs(SSIBus *bus, uint8_t cs_index); From 14ab44b96d5bf761af81cc723314ef5ecf73ed17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Aug 2025 08:40:38 +0200 Subject: [PATCH 0549/1794] elf: Add EF_MIPS_ARCH_ASE definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Include MIPS ASE ELF definitions from binutils: https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=include/elf/mips.h;h=4fc190f404d828ded84e621bfcece5fa9f9c23c8;hb=HEAD#l210 Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250814070650.78657-2-philmd@linaro.org> --- include/elf.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/elf.h b/include/elf.h index e7259ec366fca..bbfac055de44e 100644 --- a/include/elf.h +++ b/include/elf.h @@ -56,6 +56,13 @@ typedef int64_t Elf64_Sxword; #define EF_MIPS_ARCH_32R6 0x90000000 /* MIPS32r6 code. */ #define EF_MIPS_ARCH_64R6 0xa0000000 /* MIPS64r6 code. */ +/* MIPS Architectural Extensions. */ +#define EF_MIPS_ARCH_ASE 0x0f000000 + +#define EF_MIPS_ARCH_ASE_MICROMIPS 0x02000000 +#define EF_MIPS_ARCH_ASE_M16 0x04000000 +#define EF_MIPS_ARCH_ASE_MDMX 0x08000000 + /* The ABI of a file. */ #define EF_MIPS_ABI_O32 0x00001000 /* O32 ABI. */ #define EF_MIPS_ABI_O64 0x00002000 /* O32 extended for 64 bit. */ From 7a09b3cc70ab6d717b18dec5c5995f7a06af4593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Aug 2025 08:40:49 +0200 Subject: [PATCH 0550/1794] linux-user/mips: Select 74Kf CPU to run MIPS16e binaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 74Kf is our latest CPU supporting MIPS16e ASE. Note, currently QEMU doesn't have 64-bit CPU supporting MIPS16e ASE. Cc: qemu-stable@nongnu.org Fixes: 6ea219d0196..d19954f46df ("target-mips: MIPS16 support") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3054 Reported-by: Justin Applegate Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250814070650.78657-3-philmd@linaro.org> --- linux-user/mips/elfload.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c index e0c50f50ed290..6f1880befcfc7 100644 --- a/linux-user/mips/elfload.c +++ b/linux-user/mips/elfload.c @@ -37,6 +37,9 @@ const char *get_elf_cpu_model(uint32_t eflags) if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { return "mips32r6-generic"; } + if ((eflags & EF_MIPS_ARCH_ASE) == EF_MIPS_ARCH_ASE_M16) { + return "74Kf"; + } if (eflags & EF_MIPS_NAN2008) { return "P5600"; } From 51c3aebfda6489b49cebef593a1ceb597cb97a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Aug 2025 08:41:26 +0200 Subject: [PATCH 0551/1794] linux-user/mips: Select M14Kc CPU to run microMIPS binaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The M14Kc is our latest CPU supporting the microMIPS ASE. Note, currently QEMU doesn't have 64-bit CPU supporting microMIPS ASE. Cc: qemu-stable@nongnu.org Fixes: 3c824109da0 ("target-mips: microMIPS ASE support") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3054 Reported-by: Justin Applegate Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250814070650.78657-4-philmd@linaro.org> --- linux-user/mips/elfload.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c index 6f1880befcfc7..cc5bbf05ab2be 100644 --- a/linux-user/mips/elfload.c +++ b/linux-user/mips/elfload.c @@ -37,6 +37,9 @@ const char *get_elf_cpu_model(uint32_t eflags) if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { return "mips32r6-generic"; } + if ((eflags & EF_MIPS_ARCH_ASE) == EF_MIPS_ARCH_ASE_MICROMIPS) { + return "M14Kc"; + } if ((eflags & EF_MIPS_ARCH_ASE) == EF_MIPS_ARCH_ASE_M16) { return "74Kf"; } From 1f82ca723478f44823a18e7151e487d58da03659 Mon Sep 17 00:00:00 2001 From: Denis Rastyogin Date: Thu, 14 Aug 2025 13:48:32 +0300 Subject: [PATCH 0552/1794] target/mips: fix TLB huge page check to use 64-bit shift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use extract64(entry, psn, 1) instead of (entry & (1 << psn)) to avoid undefined behavior for shifts by 32–63 and to make bit extraction intent explicit. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Denis Rastyogin Message-ID: <20250814104914.13101-1-gerben@altlinux.org> Signed-off-by: Philippe Mathieu-Daudé --- target/mips/tcg/system/tlb_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/mips/tcg/system/tlb_helper.c b/target/mips/tcg/system/tlb_helper.c index eccaf3624cb3d..1e8901556d660 100644 --- a/target/mips/tcg/system/tlb_helper.c +++ b/target/mips/tcg/system/tlb_helper.c @@ -652,7 +652,7 @@ static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, return 0; } - if ((entry & (1 << psn)) && hugepg) { + if (extract64(entry, psn, 1) && hugepg) { *huge_page = true; *hgpg_directory_hit = true; entry = get_tlb_entry_layout(env, entry, leaf_mop, pf_ptew); From 46d03bb23dde86513465724760d85f42eb17539e Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Wed, 20 Aug 2025 17:55:17 +0530 Subject: [PATCH 0553/1794] hw/ppc: Fix build error with CONFIG_POWERNV disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently when CONFIG_POWERNV is not enabled, the build fails, such as with --without-default-devices: $ ./configure --without-default-devices $ make [281/283] Linking target qemu-system-ppc64 FAILED: qemu-system-ppc64 cc -m64 @qemu-system-ppc64.rsp /usr/bin/ld: libqemu-ppc64-softmmu.a.p/target_ppc_misc_helper.c.o: in function `helper_load_sprd': .../target/ppc/misc_helper.c:335:(.text+0xcdc): undefined reference to `pnv_chip_find_core' /usr/bin/ld: libqemu-ppc64-softmmu.a.p/target_ppc_misc_helper.c.o: in function `helper_store_sprd': .../target/ppc/misc_helper.c:375:(.text+0xdf4): undefined reference to `pnv_chip_find_core' collect2: error: ld returned 1 exit status ... This is since target/ppc/misc_helper.c references PowerNV specific 'pnv_chip_find_core' call. Split the PowerNV specific SPRD code out of the generic PowerPC code, by moving the SPRD code to pnv.c Fixes: 9808ce6d5cb ("target/ppc: Big-core scratch register fix") Cc: Philippe Mathieu-Daudé Reported-by: Thomas Huth Suggested-by: Cédric Le Goater Signed-off-by: Aditya Gupta Acked-by: Cédric Le Goater Message-ID: <20250820122516.949766-2-adityag@linux.ibm.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/ppc/pnv.c | 86 ++++++++++++++++++++++++++++++++++++++++ target/ppc/cpu.h | 4 ++ target/ppc/misc_helper.c | 59 +++------------------------ 3 files changed, 96 insertions(+), 53 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index d84c9067edb35..9c74f46091a7f 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" +#include "qemu/log.h" #include "qemu/units.h" #include "qemu/cutils.h" #include "qapi/error.h" @@ -1794,12 +1795,83 @@ static void pnv_chip_power9_pec_realize(PnvChip *chip, Error **errp) } } +static uint64_t pnv_handle_sprd_load(CPUPPCState *env) +{ + PowerPCCPU *cpu = env_archcpu(env); + PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; + uint64_t sprc = env->spr[SPR_POWER_SPRC]; + + if (pc->big_core) { + pc = pnv_chip_find_core(pc->chip, CPU_CORE(pc)->core_id & ~0x1); + } + + switch (sprc & 0x3e0) { + case 0: /* SCRATCH0-3 */ + case 1: /* SCRATCH4-7 */ + return pc->scratch[(sprc >> 3) & 0x7]; + + case 0x1e0: /* core thread state */ + if (env->excp_model == POWERPC_EXCP_POWER9) { + /* + * Only implement for POWER9 because skiboot uses it to check + * big-core mode. Other bits are unimplemented so we would + * prefer to get unimplemented message on POWER10 if it were + * used anywhere. + */ + if (pc->big_core) { + return PPC_BIT(63); + } else { + return 0; + } + } + /* fallthru */ + + default: + qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" + TARGET_FMT_lx"\n", sprc); + break; + } + return 0; +} + +static void pnv_handle_sprd_store(CPUPPCState *env, uint64_t val) +{ + PowerPCCPU *cpu = env_archcpu(env); + uint64_t sprc = env->spr[SPR_POWER_SPRC]; + PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; + int nr; + + if (pc->big_core) { + pc = pnv_chip_find_core(pc->chip, CPU_CORE(pc)->core_id & ~0x1); + } + + switch (sprc & 0x3e0) { + case 0: /* SCRATCH0-3 */ + case 1: /* SCRATCH4-7 */ + /* + * Log stores to SCRATCH, because some firmware uses these for + * debugging and logging, but they would normally be read by the BMC, + * which is not implemented in QEMU yet. This gives a way to get at the + * information. Could also dump these upon checkstop. + */ + nr = (sprc >> 3) & 0x7; + pc->scratch[nr] = val; + break; + default: + qemu_log_mask(LOG_UNIMP, "mtSPRD: Unimplemented SPRC:0x" + TARGET_FMT_lx"\n", sprc); + break; + } +} + static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) { PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); Pnv9Chip *chip9 = PNV9_CHIP(dev); PnvChip *chip = PNV_CHIP(dev); Pnv9Psi *psi9 = &chip9->psi; + PowerPCCPU *cpu; + PowerPCCPUClass *cpu_class; Error *local_err = NULL; int i; @@ -1827,6 +1899,12 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) return; } + /* Set handlers for Special registers, such as SPRD */ + cpu = chip->cores[0]->threads[0]; + cpu_class = POWERPC_CPU_GET_CLASS(cpu); + cpu_class->load_sprd = pnv_handle_sprd_load; + cpu_class->store_sprd = pnv_handle_sprd_store; + /* XIVE interrupt controller (POWER9) */ object_property_set_int(OBJECT(&chip9->xive), "ic-bar", PNV9_XIVE_IC_BASE(chip), &error_fatal); @@ -2078,6 +2156,8 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); PnvChip *chip = PNV_CHIP(dev); Pnv10Chip *chip10 = PNV10_CHIP(dev); + PowerPCCPU *cpu; + PowerPCCPUClass *cpu_class; Error *local_err = NULL; int i; @@ -2105,6 +2185,12 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) return; } + /* Set handlers for Special registers, such as SPRD */ + cpu = chip->cores[0]->threads[0]; + cpu_class = POWERPC_CPU_GET_CLASS(cpu); + cpu_class->load_sprd = pnv_handle_sprd_load; + cpu_class->store_sprd = pnv_handle_sprd_store; + /* XIVE2 interrupt controller (POWER10) */ object_property_set_int(OBJECT(&chip10->xive), "ic-bar", PNV10_XIVE2_IC_BASE(chip), &error_fatal); diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 6b90543811f05..0e26e4343de71 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1522,6 +1522,10 @@ struct PowerPCCPUClass { void (*init_proc)(CPUPPCState *env); int (*check_pow)(CPUPPCState *env); int (*check_attn)(CPUPPCState *env); + + /* Handlers to be set by the machine initialising the chips */ + uint64_t (*load_sprd)(CPUPPCState *env); + void (*store_sprd)(CPUPPCState *env, uint64_t val); }; static inline bool ppc_cpu_core_single_threaded(CPUState *cs) diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index e7d94625185c3..0e625cbb704d9 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -328,69 +328,22 @@ target_ulong helper_load_sprd(CPUPPCState *env) * accessed by powernv machines. */ PowerPCCPU *cpu = env_archcpu(env); - PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; - target_ulong sprc = env->spr[SPR_POWER_SPRC]; + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - if (pc->big_core) { - pc = pnv_chip_find_core(pc->chip, CPU_CORE(pc)->core_id & ~0x1); + if (pcc->load_sprd) { + return pcc->load_sprd(env); } - switch (sprc & 0x3e0) { - case 0: /* SCRATCH0-3 */ - case 1: /* SCRATCH4-7 */ - return pc->scratch[(sprc >> 3) & 0x7]; - - case 0x1e0: /* core thread state */ - if (env->excp_model == POWERPC_EXCP_POWER9) { - /* - * Only implement for POWER9 because skiboot uses it to check - * big-core mode. Other bits are unimplemented so we would - * prefer to get unimplemented message on POWER10 if it were - * used anywhere. - */ - if (pc->big_core) { - return PPC_BIT(63); - } else { - return 0; - } - } - /* fallthru */ - - default: - qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" - TARGET_FMT_lx"\n", sprc); - break; - } return 0; } void helper_store_sprd(CPUPPCState *env, target_ulong val) { - target_ulong sprc = env->spr[SPR_POWER_SPRC]; PowerPCCPU *cpu = env_archcpu(env); - PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; - int nr; - - if (pc->big_core) { - pc = pnv_chip_find_core(pc->chip, CPU_CORE(pc)->core_id & ~0x1); - } + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - switch (sprc & 0x3e0) { - case 0: /* SCRATCH0-3 */ - case 1: /* SCRATCH4-7 */ - /* - * Log stores to SCRATCH, because some firmware uses these for - * debugging and logging, but they would normally be read by the BMC, - * which is not implemented in QEMU yet. This gives a way to get at the - * information. Could also dump these upon checkstop. - */ - nr = (sprc >> 3) & 0x7; - pc->scratch[nr] = val; - break; - default: - qemu_log_mask(LOG_UNIMP, "mtSPRD: Unimplemented SPRC:0x" - TARGET_FMT_lx"\n", sprc); - break; + if (pcc->store_sprd) { + return pcc->store_sprd(env, val); } } From 01941107ebda4756e63a841ff5c457bc6a77c6ce Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 21 Aug 2025 16:40:51 +0100 Subject: [PATCH 0554/1794] hw/irq: New qemu_init_irq_child() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The qemu_init_irq() function initializes a TYPE_IRQ QOM object. The caller is therefore responsible for eventually calling qemu_free_irq() to unref (and thus free) it. In many places where we want to initialize an IRQ we are in the init/realize of some other QOM object; if we have a variant of this function that calls object_initialize_child() then the IRQ will be automatically cleaned up when its parent object is destroyed, and we don't need to remember to manually free it. Implement qemu_init_irq_child(), which is to qemu_init_irq() what object_initialize_child() is to object_initialize(). Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250821154053.2417090-2-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/irq.c | 8 ++++++++ include/hw/irq.h | 23 ++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/hw/core/irq.c b/hw/core/irq.c index 6dd8d47bd6ea5..0c768f7704e91 100644 --- a/hw/core/irq.c +++ b/hw/core/irq.c @@ -49,6 +49,14 @@ void qemu_init_irq(IRQState *irq, qemu_irq_handler handler, void *opaque, init_irq_fields(irq, handler, opaque, n); } +void qemu_init_irq_child(Object *parent, const char *propname, + IRQState *irq, qemu_irq_handler handler, + void *opaque, int n) +{ + object_initialize_child(parent, propname, irq, TYPE_IRQ); + init_irq_fields(irq, handler, opaque, n); +} + void qemu_init_irqs(IRQState irq[], size_t count, qemu_irq_handler handler, void *opaque) { diff --git a/include/hw/irq.h b/include/hw/irq.h index b3012237acde9..291fdd67df465 100644 --- a/include/hw/irq.h +++ b/include/hw/irq.h @@ -36,11 +36,32 @@ static inline void qemu_irq_pulse(qemu_irq irq) /* * Init a single IRQ. The irq is assigned with a handler, an opaque data - * and the interrupt number. + * and the interrupt number. The caller must free this with qemu_free_irq(). + * If you are using this inside a device's init or realize method, then + * qemu_init_irq_child() is probably a better choice to avoid the need + * to manually clean up the IRQ. */ void qemu_init_irq(IRQState *irq, qemu_irq_handler handler, void *opaque, int n); +/** + * qemu_init_irq_child: Initialize IRQ and make it a QOM child + * @parent: QOM object which owns this IRQ + * @propname: child property name + * @irq: pointer to IRQState to initialize + * @handler: handler function for incoming interrupts + * @opaque: opaque data to pass to @handler + * @n: interrupt number to pass to @handler + * + * Init a single IRQ and make the IRQ object a child of @parent with + * the child-property name @propname. The IRQ object will thus be + * automatically freed when @parent is destroyed. + */ +void qemu_init_irq_child(Object *parent, const char *propname, + IRQState *irq, qemu_irq_handler handler, + void *opaque, int n); + + /** * qemu_init_irqs: Initialize an array of IRQs. * From d1c9061b97d57d194e44023eb7e52fedde155e61 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 21 Aug 2025 16:40:52 +0100 Subject: [PATCH 0555/1794] hw/char/serial-pci-multi: Use qemu_init_irq_child() to avoid leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The serial-pci-multi device initializes an IRQ with qemu_init_irq() in its instance_init function; however it never calls qemu_free_irq(), so the init/deinit cycle has a memory leak, which ASAN catches in the device-introspect-test: Direct leak of 576 byte(s) in 6 object(s) allocated from: #0 0x626306ddade3 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qem u-system-arm+0x21f1de3) (BuildId: 52ece17287eba2d68e5be980e1856cd1f6be932f) #1 0x7756ade79b09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1 eb6131419edb83b2178b682829a6913cf682d75) #2 0x7756ade5b45a in g_hash_table_new_full (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x4445a ) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #3 0x62630965da37 in object_initialize_with_type /mnt/nvmedisk/linaro/qemu-from-laptop/qem u/build/arm-asan/../../qom/object.c:568:23 #4 0x62630965d440 in object_initialize /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/ar m-asan/../../qom/object.c:578:5 #5 0x626309653eeb in qemu_init_irq /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-as an/../../hw/core/irq.c:48:5 #6 0x6263072370bb in multi_serial_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/char/serial-pci-multi.c:183:9 Use the new qemu_init_irq_child() function instead, so that the IRQ object is automatically unreffed when the serial-pci device is deinited. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250821154053.2417090-3-peter.maydell@linaro.org> [PMD: Use "irq[*]" as child property name] Signed-off-by: Philippe Mathieu-Daudé --- hw/char/serial-pci-multi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/char/serial-pci-multi.c b/hw/char/serial-pci-multi.c index 13df272691a64..34f30fb70b80c 100644 --- a/hw/char/serial-pci-multi.c +++ b/hw/char/serial-pci-multi.c @@ -180,7 +180,8 @@ static void multi_serial_init(Object *o) size_t i, nports = multi_serial_get_port_count(PCI_DEVICE_GET_CLASS(dev)); for (i = 0; i < nports; i++) { - qemu_init_irq(&pms->irqs[i], multi_serial_irq_mux, pms, i); + qemu_init_irq_child(o, "irq[*]", &pms->irqs[i], + multi_serial_irq_mux, pms, i); object_initialize_child(o, "serial[*]", &pms->state[i], TYPE_SERIAL); } } From f905be62379aab0c5874756e1a73b33581d7011d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 21 Aug 2025 16:40:53 +0100 Subject: [PATCH 0556/1794] hw/ide/ich.c: Use qemu_init_irq_child() to avoid memory leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ICH9 PCI device uses qemu_init_irq() in its instance_init method, but fails to clean it up in its uninit. This results in a leak, detected by ASAN when running the device-introspect-test: Direct leak of 96 byte(s) in 1 object(s) allocated from: #0 0x58f3b53ecde3 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qem u-system-arm+0x21f1de3) (BuildId: 8dcd38b1d76bd7bd44f905c38200f4cceafd7ca4) #1 0x72e446dd5b09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1 eb6131419edb83b2178b682829a6913cf682d75) #2 0x72e446db745a in g_hash_table_new_full (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x4445a ) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #3 0x58f3b7c6fc67 in object_initialize_with_type /mnt/nvmedisk/linaro/qemu-from-laptop/qem u/build/arm-asan/../../qom/object.c:568:23 #4 0x58f3b7c6f670 in object_initialize /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/ar m-asan/../../qom/object.c:578:5 #5 0x58f3b7c6611b in qemu_init_irq /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/core/irq.c:48:5 #6 0x58f3b5c6e931 in pci_ich9_ahci_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/ide/ich.c:117:5 We could call qemu_free_irq() in pci_ich9_uninit(), but since we have a method of initializing the IRQ that doesn't need manual freeing, use that instead: qemu_init_irq_child(). Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250821154053.2417090-4-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/ide/ich.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/ide/ich.c b/hw/ide/ich.c index 4cade0d12199c..b00987f08d4cd 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -114,7 +114,8 @@ static void pci_ich9_ahci_init(Object *obj) { AHCIPCIState *d = ICH9_AHCI(obj); - qemu_init_irq(&d->irq, pci_ich9_ahci_update_irq, d, 0); + qemu_init_irq_child(obj, "update-irq", &d->irq, + pci_ich9_ahci_update_irq, d, 0); ahci_init(&d->ahci, DEVICE(obj)); d->ahci.irq = &d->irq; } From 3284d1c07cfd8d42aa27d1cf83d3e65fcd62e35e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 21 Aug 2025 16:44:59 +0100 Subject: [PATCH 0557/1794] hw/gpio/pca9554: Avoid leak in pca9554_set_pin() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In pca9554_set_pin() we have a string property which we parse in order to set some non-string fields in the device state. So we call visit_type_str(), passing it the address of the local variable state_str. visit_type_str() will allocate a new copy of the string; we never free this string, so the result is a memory leak, detected by ASAN during a "make check" run: Direct leak of 5 byte(s) in 1 object(s) allocated from: #0 0x5d605212ede3 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-arm+0x21f1de3) ( BuildId: 3d5373c89317f58bfcd191a33988c7347714be14) #1 0x7f7edea57b09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1eb6131419edb83b2178b68282 9a6913cf682d75) #2 0x7f7edea6d4d8 in g_strdup (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x784d8) (BuildId: 1eb6131419edb83b2178b68282 9a6913cf682d75) #3 0x5d6055289a91 in g_strdup_inline /usr/include/glib-2.0/glib/gstrfuncs.h:321:10 #4 0x5d6055289a91 in qobject_input_type_str /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qapi/qo bject-input-visitor.c:542:12 #5 0x5d605528479c in visit_type_str /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qapi/qapi-visit -core.c:349:10 #6 0x5d60528bdd87 in pca9554_set_pin /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/gpio/pca9554.c:179:10 #7 0x5d60549bcbbb in object_property_set /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:1450:5 #8 0x5d60549d2055 in object_property_set_qobject /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/qom-qobject.c:28:10 #9 0x5d60549bcdf1 in object_property_set_str /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:1458:15 #10 0x5d605439d077 in gb200nvl_bmc_i2c_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/arm/aspeed.c:1267:5 #11 0x5d60543a3bbc in aspeed_machine_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/arm/aspeed.c:493:9 Make the state_str g_autofree, so that we will always free it, on both error-exit and success codepaths. Cc: qemu-stable@nongnu.org Fixes: de0c7d543bca ("misc: Add a pca9554 GPIO device model") Signed-off-by: Peter Maydell Reviewed-by: Glenn Miles Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250821154459.2417976-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/gpio/pca9554.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/gpio/pca9554.c b/hw/gpio/pca9554.c index de3f883aee933..eac0d23be34a5 100644 --- a/hw/gpio/pca9554.c +++ b/hw/gpio/pca9554.c @@ -174,7 +174,7 @@ static void pca9554_set_pin(Object *obj, Visitor *v, const char *name, PCA9554State *s = PCA9554(obj); int pin, rc, val; uint8_t state, mask; - char *state_str; + g_autofree char *state_str = NULL; if (!visit_type_str(v, name, &state_str, errp)) { return; From ac6b124180f7698084ef2a59282e8fa65a45f23b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 21 Aug 2025 16:43:58 +0100 Subject: [PATCH 0558/1794] hw/char/max78000_uart: Destroy FIFO on deinit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the max78000_uart we create a FIFO in the instance_init function, but we don't destroy it on deinit, so ASAN reports a leak in the device-introspect-test: #0 0x561cc92d5de3 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-arm+0x21f1de3) (BuildId: 98fdf9fc85c3beaeca8eda0be8412f1e11b9c6ad) #1 0x70cbf2afab09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x561ccc4c884d in fifo8_create /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../util/fifo8.c:27:18 #3 0x561cc9744ec9 in max78000_uart_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/char/max78000_uart.c:241:5 Add an instance_finalize method to destroy the FIFO. Cc: qemu-stable@nongnu.org Fixes: d447e4b70295 ("MAX78000: UART Implementation") Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250821154358.2417744-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/char/max78000_uart.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/char/max78000_uart.c b/hw/char/max78000_uart.c index 19506d52ef995..c76c0e759b6af 100644 --- a/hw/char/max78000_uart.c +++ b/hw/char/max78000_uart.c @@ -247,6 +247,12 @@ static void max78000_uart_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } +static void max78000_uart_finalize(Object *obj) +{ + Max78000UartState *s = MAX78000_UART(obj); + fifo8_destroy(&s->rx_fifo); +} + static void max78000_uart_realize(DeviceState *dev, Error **errp) { Max78000UartState *s = MAX78000_UART(dev); @@ -274,6 +280,7 @@ static const TypeInfo max78000_uart_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Max78000UartState), .instance_init = max78000_uart_init, + .instance_finalize = max78000_uart_finalize, .class_init = max78000_uart_class_init, }; From 4dec497264c2e03b32fc82d6f24a694661b14d64 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 26 Aug 2025 18:49:55 +0100 Subject: [PATCH 0559/1794] hw/misc/xlnx-versal-cframe-reg: Free FIFO, g_tree on deinit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the xlnx-versal-cframe-reg device we create a FIFO in instance_init but don't destroy it on deinit, causing ASAN to report a leak in the device-introspect-test: Direct leak of 400 byte(s) in 1 object(s) allocated from: #0 0x5aded4d54e23 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-aarch64+0x24ffe23) (BuildId: 9f1e6c53fecd904ba5fc1f521d7da080a0e4103b) #1 0x71fbfac9bb09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x5aded850059d in fifo8_create /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../util/fifo8.c:27:18 #3 0x5aded582b9e4 in fifo32_create /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/include/qemu/fifo32.h:35:5 #4 0x5aded582b326 in cframe_reg_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/misc/xlnx-versal-cframe-reg.c:693:5 Similarly, we don't clean up the g_tree we create: Direct leak of 48 byte(s) in 1 object(s) allocated from: #0 0x5aded4d54e23 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-aarch64+0x24ffe23) (BuildId: 9f1e6c5 3fecd904ba5fc1f521d7da080a0e4103b) #1 0x71fbfac9bb09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x71fbfaccc799 in g_tree_new_full (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x93799) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d7 5) #3 0x5aded582b21a in cframe_reg_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/misc/xlnx-versal-cframe-reg.c:691:18 Add an instance_finalize method to clean up what we allocated in instance_init. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Manos Pitsidianakis Reviewed-by: Francisco Iglesias Message-ID: <20250826174956.3010274-2-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/misc/xlnx-versal-cframe-reg.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/misc/xlnx-versal-cframe-reg.c b/hw/misc/xlnx-versal-cframe-reg.c index 1ce083e2409d2..95e167b9213bc 100644 --- a/hw/misc/xlnx-versal-cframe-reg.c +++ b/hw/misc/xlnx-versal-cframe-reg.c @@ -693,6 +693,14 @@ static void cframe_reg_init(Object *obj) fifo32_create(&s->new_f_data, FRAME_NUM_WORDS); } +static void cframe_reg_finalize(Object *obj) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(obj); + + fifo32_destroy(&s->new_f_data); + g_tree_destroy(s->cframes); +} + static const VMStateDescription vmstate_cframe = { .name = "cframe", .version_id = 1, @@ -833,6 +841,7 @@ static const TypeInfo cframe_reg_info = { .instance_size = sizeof(XlnxVersalCFrameReg), .class_init = cframe_reg_class_init, .instance_init = cframe_reg_init, + .instance_finalize = cframe_reg_finalize, .interfaces = (const InterfaceInfo[]) { { TYPE_XLNX_CFI_IF }, { } From 6592f710e4e1890a8a71e157266060bceacef6dd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 26 Aug 2025 18:49:56 +0100 Subject: [PATCH 0560/1794] hw/display/xlnx_dp: Don't leak dpcd and edid objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the xnlx_dp_init() function we create the s->dpcd and s->edid objects with qdev_new(); then in xlnx_dp_realize() we realize the dpcd with qdev_realize() and the edid with qdev_realize_and_unref(). This is inconsistent, and both ways result in a memory leak for the instance_init -> deinit lifecycle tested by device-introspect-test: Indirect leak of 1968 byte(s) in 1 object(s) allocated from: #0 0x5aded4d54e23 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-aarch64+0x24ffe23) (BuildId: 9f1e6c5 3fecd904ba5fc1f521d7da080a0e4103b) #1 0x71fbfac9bb09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x5aded7b9211c in object_new_with_type /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:767:15 #3 0x5aded7b92240 in object_new /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:789:12 #4 0x5aded7b773e4 in qdev_new /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/core/qdev.c:149:19 #5 0x5aded54458be in xlnx_dp_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/display/xlnx_dp.c:1272:20 Direct leak of 344 byte(s) in 1 object(s) allocated from: #0 0x5aded4d54e23 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-aarch64+0x24ffe23) (BuildId: 9f1e6c53fecd904ba5fc1f521d7da080a0e4103b) #1 0x71fbfac9bb09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x5aded7b9211c in object_new_with_type /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:767:15 #3 0x5aded7b92240 in object_new /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:789:12 #4 0x5aded7b773e4 in qdev_new /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/core/qdev.c:149:19 #5 0x5aded5445a56 in xlnx_dp_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/display/xlnx_dp.c:1275:22 Instead, explicitly object_unref() after we have added the objects as child properties of the device. This means they will automatically be freed when this device is deinited. When we do this, qdev_realize() is the correct way to realize them in xlnx_dp_realize(). Signed-off-by: Peter Maydell Reviewed-by: Francisco Iglesias Reviewed-by: Manos Pitsidianakis Reviewed-by: Edgar E. Iglesias Message-ID: <20250826174956.3010274-3-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/xlnx_dp.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index 7c980ee6423d0..ef73e1815fc18 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -1267,14 +1267,18 @@ static void xlnx_dp_init(Object *obj) s->aux_bus = aux_bus_init(DEVICE(obj), "aux"); /* - * Initialize DPCD and EDID.. + * Initialize DPCD and EDID. Once we have added the objects as + * child properties of this device, we can drop the reference we + * hold to them, leaving the child-property as the only reference. */ s->dpcd = DPCD(qdev_new("dpcd")); object_property_add_child(OBJECT(s), "dpcd", OBJECT(s->dpcd)); + object_unref(s->dpcd); s->edid = I2CDDC(qdev_new("i2c-ddc")); i2c_slave_set_address(I2C_SLAVE(s->edid), 0x50); object_property_add_child(OBJECT(s), "edid", OBJECT(s->edid)); + object_unref(s->edid); fifo8_create(&s->rx_fifo, 16); fifo8_create(&s->tx_fifo, 16); @@ -1311,8 +1315,8 @@ static void xlnx_dp_realize(DeviceState *dev, Error **errp) qdev_realize(DEVICE(s->dpcd), BUS(s->aux_bus), &error_fatal); aux_map_slave(AUX_SLAVE(s->dpcd), 0x0000); - qdev_realize_and_unref(DEVICE(s->edid), BUS(aux_get_i2c_bus(s->aux_bus)), - &error_fatal); + qdev_realize(DEVICE(s->edid), BUS(aux_get_i2c_bus(s->aux_bus)), + &error_fatal); s->console = graphic_console_init(dev, 0, &xlnx_dp_gfx_ops, s); surface = qemu_console_surface(s->console); From acba1ebcad9a0dd8c08495edaf5b8ce6a748bb01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Aug 2025 16:24:16 +0200 Subject: [PATCH 0561/1794] hw/mips: Remove mipssim machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "mipssim" machine is deprecated since commit facfc943cb9 ("hw/mips: Mark the "mipssim" machine as deprecated"), released in v10.0; time to remove. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Ján Tomko Reviewed-by: Richard Henderson Reviewed-by: Jiaxun Yang Message-Id: <20250828143800.49842-2-philmd@linaro.org> --- MAINTAINERS | 1 - configs/devices/mips-softmmu/common.mak | 1 - docs/about/deprecated.rst | 12 -- docs/about/removed-features.rst | 5 + docs/system/target-mips.rst | 11 -- hw/mips/Kconfig | 7 - hw/mips/meson.build | 1 - hw/mips/mipssim.c | 249 ------------------------ 8 files changed, 5 insertions(+), 282 deletions(-) delete mode 100644 hw/mips/mipssim.c diff --git a/MAINTAINERS b/MAINTAINERS index 040ef06494cf3..3d3bbc0026076 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1414,7 +1414,6 @@ F: tests/functional/mips*/test_tuxrun.py Mipssim R: Aleksandar Rikalo S: Orphan -F: hw/mips/mipssim.c F: hw/net/mipsnet.c Fuloong 2E diff --git a/configs/devices/mips-softmmu/common.mak b/configs/devices/mips-softmmu/common.mak index b50107feafe3e..cdeae7ce450bc 100644 --- a/configs/devices/mips-softmmu/common.mak +++ b/configs/devices/mips-softmmu/common.mak @@ -6,4 +6,3 @@ # Boards are selected by default, uncomment to keep out of the build. # CONFIG_MALTA=n -# CONFIG_MIPSSIM=n diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 6ae69206817a4..b2420732e1d7a 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -313,18 +313,6 @@ and serves as the initial engineering sample rather than a production version. A newer revision, A1, is now supported, and the ``ast2700a1-evb`` should replace the older A0 version. -Mips ``mipssim`` machine (since 10.0) -''''''''''''''''''''''''''''''''''''' - -Linux dropped support for this virtual machine type in kernel v3.7, and -there does not seem to be anybody around who is still using this board -in QEMU: Most former MIPS-related people are working on other architectures -in their everyday job nowadays, and we are also not aware of anybody still -using old binaries with this board (i.e. there is also no binary available -online to check that this board did not completely bitrot yet). It is -recommended to use another MIPS machine for future MIPS code development -instead. - RISC-V default machine option (since 10.0) '''''''''''''''''''''''''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 332d07e2b18d8..dc3d4eaa2d115 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1107,6 +1107,11 @@ were added for little endian CPUs. Big endian support was never tested and likely never worked. Starting with QEMU v10.1, the machines are now only available as little-endian machines. +Mips ``mipssim`` machine (removed in 10.2) +'''''''''''''''''''''''''''''''''''''''''' + +Linux dropped support for this virtual machine type in kernel v3.7, and +there was also no binary available online to use with that board. linux-user mode CPUs -------------------- diff --git a/docs/system/target-mips.rst b/docs/system/target-mips.rst index 9028c3b304d49..2a152e1338011 100644 --- a/docs/system/target-mips.rst +++ b/docs/system/target-mips.rst @@ -12,8 +12,6 @@ machine types are emulated: - An ACER Pica \"pica61\". This machine needs the 64-bit emulator. -- MIPS emulator pseudo board \"mipssim\" - - A MIPS Magnum R4000 machine \"magnum\". This machine needs the 64-bit emulator. @@ -80,15 +78,6 @@ The Loongson-3 virtual platform emulation supports: - Both KVM and TCG supported -The mipssim pseudo board emulation provides an environment similar to -what the proprietary MIPS emulator uses for running Linux. It supports: - -- A range of MIPS CPUs, default is the 24Kf - -- PC style serial port - -- MIPSnet network emulation - .. include:: cpu-models-mips.rst.inc .. _nanoMIPS-System-emulator: diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig index f84fffcd3238d..b59cb2f1114e0 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -13,13 +13,6 @@ config MALTA select SERIAL_MM select SMBUS_EEPROM -config MIPSSIM - bool - default y - depends on MIPS - select SERIAL_MM - select MIPSNET - config JAZZ bool default y diff --git a/hw/mips/meson.build b/hw/mips/meson.build index 31dbd2bf4d91f..390f0fd7f9d16 100644 --- a/hw/mips/meson.build +++ b/hw/mips/meson.build @@ -8,7 +8,6 @@ mips_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('cps.c')) if 'CONFIG_TCG' in config_all_accel mips_ss.add(when: 'CONFIG_JAZZ', if_true: files('jazz.c')) -mips_ss.add(when: 'CONFIG_MIPSSIM', if_true: files('mipssim.c')) mips_ss.add(when: 'CONFIG_FULOONG', if_true: files('fuloong2e.c')) mips_ss.add(when: 'CONFIG_MIPS_BOSTON', if_true: files('boston.c')) endif diff --git a/hw/mips/mipssim.c b/hw/mips/mipssim.c deleted file mode 100644 index e843307b9b6ac..0000000000000 --- a/hw/mips/mipssim.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * QEMU/mipssim emulation - * - * Emulates a very simple machine model similar to the one used by the - * proprietary MIPS emulator. - * - * Copyright (c) 2007 Thiemo Seufer - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/datadir.h" -#include "system/address-spaces.h" -#include "hw/clock.h" -#include "hw/mips/mips.h" -#include "hw/char/serial-mm.h" -#include "net/net.h" -#include "system/system.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "elf.h" -#include "hw/sysbus.h" -#include "hw/qdev-properties.h" -#include "qemu/error-report.h" -#include "system/qtest.h" -#include "system/reset.h" -#include "cpu.h" - -#define BIOS_SIZE (4 * MiB) - -static struct _loaderparams { - int ram_size; - const char *kernel_filename; - const char *kernel_cmdline; - const char *initrd_filename; -} loaderparams; - -typedef struct ResetData { - MIPSCPU *cpu; - uint64_t vector; -} ResetData; - -static uint64_t load_kernel(void) -{ - uint64_t entry, kernel_high, initrd_size; - long kernel_size; - ram_addr_t initrd_offset; - - kernel_size = load_elf(loaderparams.kernel_filename, NULL, - cpu_mips_kseg0_to_phys, NULL, - &entry, NULL, - &kernel_high, NULL, - TARGET_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB, - EM_MIPS, 1, 0); - if (kernel_size < 0) { - error_report("could not load kernel '%s': %s", - loaderparams.kernel_filename, - load_elf_strerror(kernel_size)); - exit(1); - } - - /* load initrd */ - initrd_size = 0; - initrd_offset = 0; - if (loaderparams.initrd_filename) { - initrd_size = get_image_size(loaderparams.initrd_filename); - if (initrd_size > 0) { - initrd_offset = ROUND_UP(kernel_high, INITRD_PAGE_SIZE); - if (initrd_offset + initrd_size > loaderparams.ram_size) { - error_report("memory too small for initial ram disk '%s'", - loaderparams.initrd_filename); - exit(1); - } - initrd_size = load_image_targphys(loaderparams.initrd_filename, - initrd_offset, loaderparams.ram_size - initrd_offset); - } - if (initrd_size == (target_ulong) -1) { - error_report("could not load initial ram disk '%s'", - loaderparams.initrd_filename); - exit(1); - } - } - return entry; -} - -static void main_cpu_reset(void *opaque) -{ - ResetData *s = (ResetData *)opaque; - CPUMIPSState *env = &s->cpu->env; - - cpu_reset(CPU(s->cpu)); - env->active_tc.PC = s->vector & ~(target_ulong)1; - if (s->vector & 1) { - env->hflags |= MIPS_HFLAG_M16; - } -} - -static void mipsnet_init(int base, qemu_irq irq) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qemu_create_nic_device("mipsnet", true, NULL); - if (!dev) { - return; - } - - s = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(s, &error_fatal); - sysbus_connect_irq(s, 0, irq); - memory_region_add_subregion(get_system_io(), - base, - sysbus_mmio_get_region(s, 0)); -} - -static void -mips_mipssim_init(MachineState *machine) -{ - const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - const char *initrd_filename = machine->initrd_filename; - const char *bios_name = TARGET_BIG_ENDIAN ? "mips_bios.bin" - : "mipsel_bios.bin"; - char *filename; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *isa = g_new(MemoryRegion, 1); - MemoryRegion *bios = g_new(MemoryRegion, 1); - Clock *cpuclk; - MIPSCPU *cpu; - CPUMIPSState *env; - ResetData *reset_info; - int bios_size; - - cpuclk = clock_new(OBJECT(machine), "cpu-refclk"); -#ifdef TARGET_MIPS64 - clock_set_hz(cpuclk, 6000000); /* 6 MHz */ -#else - clock_set_hz(cpuclk, 12000000); /* 12 MHz */ -#endif - - /* Init CPUs. */ - cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk, - TARGET_BIG_ENDIAN); - env = &cpu->env; - - reset_info = g_new0(ResetData, 1); - reset_info->cpu = cpu; - reset_info->vector = env->active_tc.PC; - qemu_register_reset(main_cpu_reset, reset_info); - - /* Allocate RAM. */ - memory_region_init_rom(bios, NULL, "mips_mipssim.bios", BIOS_SIZE, - &error_fatal); - - memory_region_add_subregion(address_space_mem, 0, machine->ram); - - /* Map the BIOS / boot exception handler. */ - memory_region_add_subregion(address_space_mem, 0x1fc00000LL, bios); - /* Load a BIOS / boot exception handler image. */ - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, - machine->firmware ?: bios_name); - if (filename) { - bios_size = load_image_targphys(filename, 0x1fc00000LL, BIOS_SIZE); - g_free(filename); - } else { - bios_size = -1; - } - if ((bios_size < 0 || bios_size > BIOS_SIZE) && - machine->firmware && !qtest_enabled()) { - /* Bail out if we have neither a kernel image nor boot vector code. */ - error_report("Could not load MIPS bios '%s'", machine->firmware); - exit(1); - } else { - /* We have a boot vector start address. */ - env->active_tc.PC = (target_long)(int32_t)0xbfc00000; - } - - if (kernel_filename) { - loaderparams.ram_size = machine->ram_size; - loaderparams.kernel_filename = kernel_filename; - loaderparams.kernel_cmdline = kernel_cmdline; - loaderparams.initrd_filename = initrd_filename; - reset_info->vector = load_kernel(); - } - - /* Init CPU internal devices. */ - cpu_mips_irq_init_cpu(cpu); - cpu_mips_clock_init(cpu); - - /* - * Register 64 KB of ISA IO space at 0x1fd00000. But without interrupts - * (except for the hardcoded serial port interrupt) -device cannot work, - * so do not expose the ISA bus to the user. - */ - memory_region_init_alias(isa, NULL, "isa_mmio", - get_system_io(), 0, 0x00010000); - memory_region_add_subregion(get_system_memory(), 0x1fd00000, isa); - - /* - * A single 16450 sits at offset 0x3f8. It is attached to - * MIPS CPU INT2, which is interrupt 4. - */ - if (serial_hd(0)) { - DeviceState *dev = qdev_new(TYPE_SERIAL_MM); - - qdev_prop_set_chr(dev, "chardev", serial_hd(0)); - qdev_prop_set_uint8(dev, "regshift", 0); - qdev_prop_set_uint8(dev, "endianness", DEVICE_LITTLE_ENDIAN); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, env->irq[4]); - memory_region_add_subregion(get_system_io(), 0x3f8, - sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); - } - - /* MIPSnet uses the MIPS CPU INT0, which is interrupt 2. */ - mipsnet_init(0x4200, env->irq[2]); -} - -static void mips_mipssim_machine_init(MachineClass *mc) -{ - mc->desc = "MIPS MIPSsim platform"; - mc->init = mips_mipssim_init; -#ifdef TARGET_MIPS64 - mc->default_cpu_type = MIPS_CPU_TYPE_NAME("5Kf"); -#else - mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf"); -#endif - mc->default_ram_id = "mips_mipssim.ram"; -} - -DEFINE_MACHINE("mipssim", mips_mipssim_machine_init) From 60c8ee1a6d6ad89dd55f3066062dc788f4a419dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Aug 2025 16:25:56 +0200 Subject: [PATCH 0562/1794] hw/net: Remove mipsnet device model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mipsnet device model was only used by the mipssim machine, which just got removed. Remove as now dead code. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Ján Tomko Reviewed-by: Richard Henderson Reviewed-by: Jiaxun Yang Message-Id: <20250828143800.49842-3-philmd@linaro.org> --- MAINTAINERS | 5 - hw/net/Kconfig | 3 - hw/net/meson.build | 1 - hw/net/mipsnet.c | 297 -------------------------------------------- hw/net/trace-events | 7 -- 5 files changed, 313 deletions(-) delete mode 100644 hw/net/mipsnet.c diff --git a/MAINTAINERS b/MAINTAINERS index 3d3bbc0026076..1ae28e880424f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1411,11 +1411,6 @@ F: include/hw/southbridge/piix.h F: tests/functional/mips*/test_malta.py F: tests/functional/mips*/test_tuxrun.py -Mipssim -R: Aleksandar Rikalo -S: Orphan -F: hw/net/mipsnet.c - Fuloong 2E M: Huacai Chen M: Philippe Mathieu-Daudé diff --git a/hw/net/Kconfig b/hw/net/Kconfig index 7f80218d10ff5..2b513d689584b 100644 --- a/hw/net/Kconfig +++ b/hw/net/Kconfig @@ -82,9 +82,6 @@ config OPENCORES_ETH config XGMAC bool -config MIPSNET - bool - config ALLWINNER_EMAC bool diff --git a/hw/net/meson.build b/hw/net/meson.build index e6759e26ca6cc..913eaedbc5293 100644 --- a/hw/net/meson.build +++ b/hw/net/meson.build @@ -23,7 +23,6 @@ system_ss.add(when: 'CONFIG_LAN9118_PHY', if_true: files('lan9118_phy.c')) system_ss.add(when: 'CONFIG_NE2000_ISA', if_true: files('ne2000-isa.c')) system_ss.add(when: 'CONFIG_OPENCORES_ETH', if_true: files('opencores_eth.c')) system_ss.add(when: 'CONFIG_XGMAC', if_true: files('xgmac.c')) -system_ss.add(when: 'CONFIG_MIPSNET', if_true: files('mipsnet.c')) system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('xilinx_axienet.c')) system_ss.add(when: 'CONFIG_ALLWINNER_EMAC', if_true: files('allwinner_emac.c')) system_ss.add(when: 'CONFIG_ALLWINNER_SUN8I_EMAC', if_true: files('allwinner-sun8i-emac.c')) diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c deleted file mode 100644 index 583aa1c7de6df..0000000000000 --- a/hw/net/mipsnet.c +++ /dev/null @@ -1,297 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "net/net.h" -#include "qemu/module.h" -#include "trace.h" -#include "hw/sysbus.h" -#include "migration/vmstate.h" -#include "qom/object.h" - -/* MIPSnet register offsets */ - -#define MIPSNET_DEV_ID 0x00 -#define MIPSNET_BUSY 0x08 -#define MIPSNET_RX_DATA_COUNT 0x0c -#define MIPSNET_TX_DATA_COUNT 0x10 -#define MIPSNET_INT_CTL 0x14 -# define MIPSNET_INTCTL_TXDONE 0x00000001 -# define MIPSNET_INTCTL_RXDONE 0x00000002 -# define MIPSNET_INTCTL_TESTBIT 0x80000000 -#define MIPSNET_INTERRUPT_INFO 0x18 -#define MIPSNET_RX_DATA_BUFFER 0x1c -#define MIPSNET_TX_DATA_BUFFER 0x20 - -#define MAX_ETH_FRAME_SIZE 1514 - -#define TYPE_MIPS_NET "mipsnet" -OBJECT_DECLARE_SIMPLE_TYPE(MIPSnetState, MIPS_NET) - -struct MIPSnetState { - SysBusDevice parent_obj; - - uint32_t busy; - uint32_t rx_count; - uint32_t rx_read; - uint32_t tx_count; - uint32_t tx_written; - uint32_t intctl; - uint8_t rx_buffer[MAX_ETH_FRAME_SIZE]; - uint8_t tx_buffer[MAX_ETH_FRAME_SIZE]; - MemoryRegion io; - qemu_irq irq; - NICState *nic; - NICConf conf; -}; - -static void mipsnet_reset(MIPSnetState *s) -{ - s->busy = 1; - s->rx_count = 0; - s->rx_read = 0; - s->tx_count = 0; - s->tx_written = 0; - s->intctl = 0; - memset(s->rx_buffer, 0, MAX_ETH_FRAME_SIZE); - memset(s->tx_buffer, 0, MAX_ETH_FRAME_SIZE); -} - -static void mipsnet_update_irq(MIPSnetState *s) -{ - int isr = !!s->intctl; - trace_mipsnet_irq(isr, s->intctl); - qemu_set_irq(s->irq, isr); -} - -static int mipsnet_buffer_full(MIPSnetState *s) -{ - if (s->rx_count >= MAX_ETH_FRAME_SIZE) { - return 1; - } - return 0; -} - -static int mipsnet_can_receive(NetClientState *nc) -{ - MIPSnetState *s = qemu_get_nic_opaque(nc); - - if (s->busy) { - return 0; - } - return !mipsnet_buffer_full(s); -} - -static ssize_t mipsnet_receive(NetClientState *nc, - const uint8_t *buf, size_t size) -{ - MIPSnetState *s = qemu_get_nic_opaque(nc); - - trace_mipsnet_receive(size); - if (!mipsnet_can_receive(nc)) { - return 0; - } - - if (size >= sizeof(s->rx_buffer)) { - return 0; - } - s->busy = 1; - - /* Just accept everything. */ - - /* Write packet data. */ - memcpy(s->rx_buffer, buf, size); - - s->rx_count = size; - s->rx_read = 0; - - /* Now we can signal we have received something. */ - s->intctl |= MIPSNET_INTCTL_RXDONE; - mipsnet_update_irq(s); - - return size; -} - -static uint64_t mipsnet_ioport_read(void *opaque, hwaddr addr, - unsigned int size) -{ - MIPSnetState *s = opaque; - int ret = 0; - - addr &= 0x3f; - switch (addr) { - case MIPSNET_DEV_ID: - ret = be32_to_cpu(0x4d495053); /* MIPS */ - break; - case MIPSNET_DEV_ID + 4: - ret = be32_to_cpu(0x4e455430); /* NET0 */ - break; - case MIPSNET_BUSY: - ret = s->busy; - break; - case MIPSNET_RX_DATA_COUNT: - ret = s->rx_count; - break; - case MIPSNET_TX_DATA_COUNT: - ret = s->tx_count; - break; - case MIPSNET_INT_CTL: - ret = s->intctl; - s->intctl &= ~MIPSNET_INTCTL_TESTBIT; - break; - case MIPSNET_INTERRUPT_INFO: - /* XXX: This seems to be a per-VPE interrupt number. */ - ret = 0; - break; - case MIPSNET_RX_DATA_BUFFER: - if (s->rx_count) { - s->rx_count--; - ret = s->rx_buffer[s->rx_read++]; - if (mipsnet_can_receive(s->nic->ncs)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } - } - break; - /* Reads as zero. */ - case MIPSNET_TX_DATA_BUFFER: - default: - break; - } - trace_mipsnet_read(addr, ret); - return ret; -} - -static void mipsnet_ioport_write(void *opaque, hwaddr addr, - uint64_t val, unsigned int size) -{ - MIPSnetState *s = opaque; - - addr &= 0x3f; - trace_mipsnet_write(addr, val); - switch (addr) { - case MIPSNET_TX_DATA_COUNT: - s->tx_count = (val <= MAX_ETH_FRAME_SIZE) ? val : 0; - s->tx_written = 0; - break; - case MIPSNET_INT_CTL: - if (val & MIPSNET_INTCTL_TXDONE) { - s->intctl &= ~MIPSNET_INTCTL_TXDONE; - } else if (val & MIPSNET_INTCTL_RXDONE) { - s->intctl &= ~MIPSNET_INTCTL_RXDONE; - } else if (val & MIPSNET_INTCTL_TESTBIT) { - mipsnet_reset(s); - s->intctl |= MIPSNET_INTCTL_TESTBIT; - } else if (!val) { - /* ACK testbit interrupt, flag was cleared on read. */ - } - s->busy = !!s->intctl; - mipsnet_update_irq(s); - if (mipsnet_can_receive(s->nic->ncs)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } - break; - case MIPSNET_TX_DATA_BUFFER: - s->tx_buffer[s->tx_written++] = val; - if ((s->tx_written >= MAX_ETH_FRAME_SIZE) - || (s->tx_written == s->tx_count)) { - /* Send buffer. */ - trace_mipsnet_send(s->tx_written); - qemu_send_packet(qemu_get_queue(s->nic), - s->tx_buffer, s->tx_written); - s->tx_count = s->tx_written = 0; - s->intctl |= MIPSNET_INTCTL_TXDONE; - s->busy = 1; - mipsnet_update_irq(s); - } - break; - /* Read-only registers */ - case MIPSNET_DEV_ID: - case MIPSNET_BUSY: - case MIPSNET_RX_DATA_COUNT: - case MIPSNET_INTERRUPT_INFO: - case MIPSNET_RX_DATA_BUFFER: - default: - break; - } -} - -static const VMStateDescription vmstate_mipsnet = { - .name = "mipsnet", - .version_id = 0, - .minimum_version_id = 0, - .fields = (const VMStateField[]) { - VMSTATE_UINT32(busy, MIPSnetState), - VMSTATE_UINT32(rx_count, MIPSnetState), - VMSTATE_UINT32(rx_read, MIPSnetState), - VMSTATE_UINT32(tx_count, MIPSnetState), - VMSTATE_UINT32(tx_written, MIPSnetState), - VMSTATE_UINT32(intctl, MIPSnetState), - VMSTATE_BUFFER(rx_buffer, MIPSnetState), - VMSTATE_BUFFER(tx_buffer, MIPSnetState), - VMSTATE_END_OF_LIST() - } -}; - -static NetClientInfo net_mipsnet_info = { - .type = NET_CLIENT_DRIVER_NIC, - .size = sizeof(NICState), - .receive = mipsnet_receive, -}; - -static const MemoryRegionOps mipsnet_ioport_ops = { - .read = mipsnet_ioport_read, - .write = mipsnet_ioport_write, - .impl.min_access_size = 1, - .impl.max_access_size = 4, -}; - -static void mipsnet_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - MIPSnetState *s = MIPS_NET(dev); - - memory_region_init_io(&s->io, OBJECT(dev), &mipsnet_ioport_ops, s, - "mipsnet-io", 36); - sysbus_init_mmio(sbd, &s->io); - sysbus_init_irq(sbd, &s->irq); - - s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, - &dev->mem_reentrancy_guard, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); -} - -static void mipsnet_sysbus_reset(DeviceState *dev) -{ - MIPSnetState *s = MIPS_NET(dev); - mipsnet_reset(s); -} - -static const Property mipsnet_properties[] = { - DEFINE_NIC_PROPERTIES(MIPSnetState, conf), -}; - -static void mipsnet_class_init(ObjectClass *klass, const void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = mipsnet_realize; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->desc = "MIPS Simulator network device"; - device_class_set_legacy_reset(dc, mipsnet_sysbus_reset); - dc->vmsd = &vmstate_mipsnet; - device_class_set_props(dc, mipsnet_properties); -} - -static const TypeInfo mipsnet_info = { - .name = TYPE_MIPS_NET, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MIPSnetState), - .class_init = mipsnet_class_init, -}; - -static void mipsnet_register_types(void) -{ - type_register_static(&mipsnet_info); -} - -type_init(mipsnet_register_types) diff --git a/hw/net/trace-events b/hw/net/trace-events index 72b69c4a8bb89..e82d7490c33b6 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -20,13 +20,6 @@ lan9118_phy_reset(void) "" lance_mem_readw(uint64_t addr, uint32_t ret) "addr=0x%"PRIx64"val=0x%04x" lance_mem_writew(uint64_t addr, uint32_t val) "addr=0x%"PRIx64"val=0x%04x" -# mipsnet.c -mipsnet_send(uint32_t size) "sending len=%u" -mipsnet_receive(uint32_t size) "receiving len=%u" -mipsnet_read(uint64_t addr, uint32_t val) "read addr=0x%" PRIx64 " val=0x%x" -mipsnet_write(uint64_t addr, uint64_t val) "write addr=0x%" PRIx64 " val=0x%" PRIx64 -mipsnet_irq(uint32_t isr, uint32_t intctl) "set irq to %d (0x%02x)" - # ne2000.c ne2000_read(uint64_t addr, uint64_t val) "read addr=0x%" PRIx64 " val=0x%" PRIx64 ne2000_write(uint64_t addr, uint64_t val) "write addr=0x%" PRIx64 " val=0x%" PRIx64 From 79d472a51015f9c9ab341a5f56b8c450870c006b Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 1 Sep 2025 07:56:22 +0200 Subject: [PATCH 0563/1794] hw/sd/sdcard: Add validation for boot-partition-size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure we are not silently rounding down or even wrapping around, causing inconsistencies with the provided image. Signed-off-by: Jan Kiszka Reviewed-by: Alex Bennée [PMD: Use g_autofree, suggested by Alex] Message-ID: <1fff448da042bdf8cff7733ce67cadff4c540f1d.1756706188.git.jan.kiszka@siemens.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/sd/sd.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 8b142e4796fbf..5603b391bf727 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2810,6 +2810,15 @@ static void sd_realize(DeviceState *dev, Error **errp) } blk_set_dev_ops(sd->blk, &sd_block_ops, sd); } + if (sd->boot_part_size % (128 * KiB) || + sd->boot_part_size > 255 * 128 * KiB) { + g_autofree char *size_str = size_to_str(sd->boot_part_size); + + error_setg(errp, "Invalid boot partition size: %s", size_str); + error_append_hint(errp, + "The boot partition size must be multiples of 128K" + "and not larger than 32640K.\n"); + } } static void emmc_realize(DeviceState *dev, Error **errp) From e2d7c1a3cdc46d6b2e8afa8db8a7ef4c2740a2fe Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 1 Sep 2025 07:56:24 +0200 Subject: [PATCH 0564/1794] hw/sd/sdcard: Refactor sd_bootpart_offset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function provides the offset for any partition in the block image, not only the boot partitions, therefore rename it. Align the constant names with the numbering scheme in the standard and use constants for both boot partitions for consistency reasons. There is also no reason to return early if boot_part_size is zero because the existing code will provide the right value in that case as well. Signed-off-by: Jan Kiszka Reviewed-by: Philippe Mathieu-Daudé Message-ID: <66e9b07476aad61820c4f42f4f984cc90752ba5e.1756706188.git.jan.kiszka@siemens.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/sd/sd.c | 16 ++++++++-------- hw/sd/sdmmc-internal.h | 3 ++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 5603b391bf727..d7a496d77c920 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -833,14 +833,14 @@ static uint32_t sd_blk_len(SDState *sd) /* * This requires a disk image that has two boot partitions inserted at the - * beginning of it. The size of the boot partitions is the "boot-size" - * property. + * beginning of it, followed by an RPMB partition. The size of the boot + * partitions is the "boot-partition-size" property. */ -static uint32_t sd_bootpart_offset(SDState *sd) +static uint32_t sd_part_offset(SDState *sd) { unsigned partition_access; - if (!sd->boot_part_size || !sd_is_emmc(sd)) { + if (!sd_is_emmc(sd)) { return 0; } @@ -849,9 +849,9 @@ static uint32_t sd_bootpart_offset(SDState *sd) switch (partition_access) { case EXT_CSD_PART_CONFIG_ACC_DEFAULT: return sd->boot_part_size * 2; - case EXT_CSD_PART_CONFIG_ACC_BOOT0: + case EXT_CSD_PART_CONFIG_ACC_BOOT1: return 0; - case EXT_CSD_PART_CONFIG_ACC_BOOT0 + 1: + case EXT_CSD_PART_CONFIG_ACC_BOOT2: return sd->boot_part_size * 1; default: g_assert_not_reached(); @@ -1052,7 +1052,7 @@ static const VMStateDescription sd_vmstate = { static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) { trace_sdcard_read_block(addr, len); - addr += sd_bootpart_offset(sd); + addr += sd_part_offset(sd); if (!sd->blk || blk_pread(sd->blk, addr, len, sd->data, 0) < 0) { fprintf(stderr, "sd_blk_read: read error on host side\n"); } @@ -1061,7 +1061,7 @@ static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len) { trace_sdcard_write_block(addr, len); - addr += sd_bootpart_offset(sd); + addr += sd_part_offset(sd); if (!sd->blk || blk_pwrite(sd->blk, addr, len, sd->data, 0) < 0) { fprintf(stderr, "sd_blk_write: write error on host side\n"); } diff --git a/hw/sd/sdmmc-internal.h b/hw/sd/sdmmc-internal.h index 91eb5b6b2fc06..ce6bc4e6ec477 100644 --- a/hw/sd/sdmmc-internal.h +++ b/hw/sd/sdmmc-internal.h @@ -116,7 +116,8 @@ DECLARE_OBJ_CHECKERS(SDState, SDCardClass, SDMMC_COMMON, TYPE_SDMMC_COMMON) #define EXT_CSD_PART_CONFIG_ACC_MASK (0x7) #define EXT_CSD_PART_CONFIG_ACC_DEFAULT (0x0) -#define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1) +#define EXT_CSD_PART_CONFIG_ACC_BOOT1 (0x1) +#define EXT_CSD_PART_CONFIG_ACC_BOOT2 (0x2) #define EXT_CSD_PART_CONFIG_EN_MASK (0x7 << 3) #define EXT_CSD_PART_CONFIG_EN_BOOT0 (0x1 << 3) From 22ece1a6ebf5d9e0b0a3e376dcfbfe9d96d209b9 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 1 Sep 2025 07:56:26 +0200 Subject: [PATCH 0565/1794] crypto/hmac: Allow to build hmac over multiple qcrypto_gnutls_hmac_bytes[v] calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the buffers that should be considered for building the hmac are not available at the same time, the current API is unsuitable. Extend it so that passing a NULL pointer as result_len is used as indicator that further buffers will be passed in succeeding calls to qcrypto_gnutls_hmac_bytes[v]. Signed-off-by: Jan Kiszka Reviewed-by: Philippe Mathieu-Daudé Message-ID: <2d3539c247a6c323491a3821f0e5b6fc382a4686.1756706188.git.jan.kiszka@siemens.com> Signed-off-by: Philippe Mathieu-Daudé --- crypto/hmac-gcrypt.c | 4 +++- crypto/hmac-glib.c | 4 +++- crypto/hmac-gnutls.c | 4 +++- crypto/hmac-nettle.c | 4 +++- include/crypto/hmac.h | 12 ++++++++++++ 5 files changed, 24 insertions(+), 4 deletions(-) diff --git a/crypto/hmac-gcrypt.c b/crypto/hmac-gcrypt.c index 5273086eb9ac9..e428d17479881 100644 --- a/crypto/hmac-gcrypt.c +++ b/crypto/hmac-gcrypt.c @@ -121,7 +121,9 @@ qcrypto_gcrypt_hmac_bytesv(QCryptoHmac *hmac, return -1; } - if (*resultlen == 0) { + if (resultlen == NULL) { + return 0; + } else if (*resultlen == 0) { *resultlen = ret; *result = g_new0(uint8_t, *resultlen); } else if (*resultlen != ret) { diff --git a/crypto/hmac-glib.c b/crypto/hmac-glib.c index ea80c8d1b23a2..b845133a058e8 100644 --- a/crypto/hmac-glib.c +++ b/crypto/hmac-glib.c @@ -104,7 +104,9 @@ qcrypto_glib_hmac_bytesv(QCryptoHmac *hmac, return -1; } - if (*resultlen == 0) { + if (resultlen == NULL) { + return 0; + } else if (*resultlen == 0) { *resultlen = ret; *result = g_new0(uint8_t, *resultlen); } else if (*resultlen != ret) { diff --git a/crypto/hmac-gnutls.c b/crypto/hmac-gnutls.c index 822995505cd95..3c5bcbe80beae 100644 --- a/crypto/hmac-gnutls.c +++ b/crypto/hmac-gnutls.c @@ -119,7 +119,9 @@ qcrypto_gnutls_hmac_bytesv(QCryptoHmac *hmac, return -1; } - if (*resultlen == 0) { + if (resultlen == NULL) { + return 0; + } else if (*resultlen == 0) { *resultlen = ret; *result = g_new0(uint8_t, *resultlen); } else if (*resultlen != ret) { diff --git a/crypto/hmac-nettle.c b/crypto/hmac-nettle.c index dd5b2ab7a1947..2cff7931e120e 100644 --- a/crypto/hmac-nettle.c +++ b/crypto/hmac-nettle.c @@ -164,7 +164,9 @@ qcrypto_nettle_hmac_bytesv(QCryptoHmac *hmac, } } - if (*resultlen == 0) { + if (resultlen == NULL) { + return 0; + } else if (*resultlen == 0) { *resultlen = qcrypto_hmac_alg_map[hmac->alg].len; *result = g_new0(uint8_t, *resultlen); } else if (*resultlen != qcrypto_hmac_alg_map[hmac->alg].len) { diff --git a/include/crypto/hmac.h b/include/crypto/hmac.h index da8a1e3ceb9dd..af3d5f8feb212 100644 --- a/include/crypto/hmac.h +++ b/include/crypto/hmac.h @@ -90,6 +90,12 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoHmac, qcrypto_hmac_free) * The memory referenced in @result must be released with a call * to g_free() when no longer required by the caller. * + * If @result_len is set to a NULL pointer, no result will be returned, and + * the hmac object can be used for further invocations of qcrypto_hmac_bytes() + * or qcrypto_hmac_bytesv() until a non-NULL pointer is provided. This allows + * to build the hmac across memory regions that are not available at the same + * time. + * * Returns: * 0 on success, -1 on error */ @@ -123,6 +129,12 @@ int qcrypto_hmac_bytesv(QCryptoHmac *hmac, * The memory referenced in @result must be released with a call * to g_free() when no longer required by the caller. * + * If @result_len is set to a NULL pointer, no result will be returned, and + * the hmac object can be used for further invocations of qcrypto_hmac_bytes() + * or qcrypto_hmac_bytesv() until a non-NULL pointer is provided. This allows + * to build the hmac across memory regions that are not available at the same + * time. + * * Returns: * 0 on success, -1 on error */ From 319ca84949fc3134774342d50790592680c3b9b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Sep 2025 08:46:24 +0200 Subject: [PATCH 0566/1794] hw/arm/virt: Include 'system/system.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hw/arm/virt.c should include 'system/system.h' for : serial_hd() qemu_add_machine_init_done_notifier() Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Link: https://lore.kernel.org/qemu-devel/20250731144019.1403591-1-clg@redhat.com Signed-off-by: Cédric Le Goater Message-ID: <20250901064631.530723-2-clg@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/virt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 1e63f40fbece3..e5c4142e822d7 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -50,6 +50,7 @@ #include "system/kvm.h" #include "system/hvf.h" #include "system/qtest.h" +#include "system/system.h" #include "hw/loader.h" #include "qapi/error.h" #include "qemu/bitops.h" From 02423bc9d329b7ff274aa2cf7da544dc339d9724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Sep 2025 08:46:25 +0200 Subject: [PATCH 0567/1794] hw/isa/superio: Include 'system/system.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Files using serial_hd() should include 'system/system.h'. Fix that. Cc: Michael S. Tsirkin Cc: Paolo Bonzini Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250901064631.530723-3-clg@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/isa/isa-superio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c index 2853485977cf4..941b0f91d7522 100644 --- a/hw/isa/isa-superio.c +++ b/hw/isa/isa-superio.c @@ -15,6 +15,7 @@ #include "qemu/module.h" #include "qapi/error.h" #include "system/blockdev.h" +#include "system/system.h" #include "chardev/char.h" #include "hw/char/parallel.h" #include "hw/block/fdc.h" From f4e39e06d1c8cfc0cfd4d2f839d85f568072435d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Sep 2025 08:46:26 +0200 Subject: [PATCH 0568/1794] hw/mips/loongson3_virt: Include 'system/system.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Files using serial_hd() should include 'system/system.h'. Fix that. Cc: Philippe Mathieu-Daudé Cc: Huacai Chen Cc: Jiaxun Yang Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250901064631.530723-4-clg@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/mips/loongson3_virt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index de6fbcc0cb4f5..672083dec9864 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -49,6 +49,7 @@ #include "system/qtest.h" #include "system/reset.h" #include "system/runstate.h" +#include "system/system.h" #include "qemu/error-report.h" #define PM_CNTL_MODE 0x10 From 42ab9014a9de94fc8c0aa97b1822230a5ee96bfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Sep 2025 08:46:27 +0200 Subject: [PATCH 0569/1794] hw/mips/malta: Include 'system/system.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Files using serial_hd() should include 'system/system.h'. Fix that. Cc: Philippe Mathieu-Daudé Cc: Aurelien Jarno Cc: Jiaxun Yang Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250901064631.530723-5-clg@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/mips/malta.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/mips/malta.c b/hw/mips/malta.c index cbdbb21056807..344dc8ca76675 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -52,6 +52,7 @@ #include "system/qtest.h" #include "system/reset.h" #include "system/runstate.h" +#include "system/system.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "system/kvm.h" From 21dca6e6c79e28dc05f7a0722895618b489223a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 1 Sep 2025 09:27:35 +0200 Subject: [PATCH 0570/1794] docs/about/removed-features: Clarify 'device_add' is removed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All other titles in removed-features.rst mention when the feature was removed using "removed in". Use that instead of "since" which we use for when a feature is deprecated. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Markus Armbruster Message-Id: <20250901113957.17113-1-philmd@linaro.org> --- docs/about/removed-features.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index dc3d4eaa2d115..fff781d6b7c3b 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -730,8 +730,8 @@ Use ``multifd-channels`` instead. Use ``multifd-compression`` instead. -Incorrectly typed ``device_add`` arguments (since 9.2) -'''''''''''''''''''''''''''''''''''''''''''''''''''''' +Incorrectly typed ``device_add`` arguments (removed in 9.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Due to shortcomings in the internal implementation of ``device_add``, QEMU used to incorrectly accept certain invalid arguments. Any object From 7e52554c293184083f571265daacfc9aa57c3d55 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 1 Sep 2025 11:22:14 +0100 Subject: [PATCH 0571/1794] hw/arm/boot: Correctly free the MemoryDeviceInfoList MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running the bios-tables-test under ASAN we see leaks like this: Direct leak of 16 byte(s) in 1 object(s) allocated from: #0 0x5bc58579b00d in calloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-aarch64+0x250400d) (BuildId: 2e27b63dc9ac45f522ced40a17c2a60cc32f1d38) #1 0x7b4ad90337b1 in g_malloc0 (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x637b1) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x5bc5861826db in qmp_memory_device_list /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/mem/memory-device.c:307:34 #3 0x5bc587a9edb6 in arm_load_dtb /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/arm/boot.c:656:15 Indirect leak of 28 byte(s) in 2 object(s) allocated from: #0 0x5bc58579ae23 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-aarch64+0x2503e23) (BuildId: 2e27b63dc9ac45f522ced40a17c2a60cc32f1d38) #1 0x7b4ad6c8f947 in __vasprintf_internal libio/vasprintf.c:116:16 #2 0x7b4ad9080a52 in g_vasprintf (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0xb0a52) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #3 0x7b4ad90515e4 in g_strdup_vprintf (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x815e4) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #4 0x7b4ad9051940 in g_strdup_printf (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x81940) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #5 0x5bc5885eb739 in object_get_canonical_path /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:2123:19 #6 0x5bc58618dca8 in pc_dimm_md_fill_device_info /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/mem/pc-dimm.c:268:18 #7 0x5bc586182792 in qmp_memory_device_list /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/mem/memory-device.c:310:9 This happens because we declared the MemoryDeviceInfoList *md_list with g_autofree, which will free the direct memory with g_free() but doesn't free all the other data structures referenced by it. Instead what we want is to declare the pointer with g_autoptr(), which will automatically call the qapi_free_MemoryDeviceInfoList() cleanup function when the variable goes out of scope. Fixes: 36bc78aca83cfd ("hw/arm: add static NVDIMMs in device tree") Signed-off-by: Peter Maydell Reviewed-by: Manos Pitsidianakis Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250901102214.3748011-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/boot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 1e57c4ab9ee86..d0840308f5aa1 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -549,7 +549,7 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, unsigned int i; hwaddr mem_base, mem_len; char **node_path; - g_autofree MemoryDeviceInfoList *md_list = NULL; + g_autoptr(MemoryDeviceInfoList) md_list = NULL; Error *err = NULL; if (binfo->dtb_filename) { From e502e614f4c3e5ee7b12cf1c926d9581262fd626 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Mon, 1 Sep 2025 21:31:58 +0100 Subject: [PATCH 0572/1794] hw/i386/pc_piix.c: remove unnecessary if() from pc_init1() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the isapc logic has been split out of pc_piix.c, the PCI Host Bridge (phb) object is now always set in pc_init1(). Since phb is now guaranteed not to be NULL, Coverity reports that the if() statement surrounding ioapic_init_gsi() is now unnecessary and can be removed along with the phb NULL initialiser. Coverity: CID 1620557 Signed-off-by: Mark Cave-Ayland Fixes: 99d0630a45 ("hw/i386/pc_piix.c: assume pcmc->pci_enabled is always true in pc_init1()") Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250901203409.1196620-1-mark.caveayland@nutanix.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/pc_piix.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 7e78b6daa6dd5..caf8bab68e24a 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -106,7 +106,7 @@ static void pc_init1(MachineState *machine, const char *pci_type) X86MachineState *x86ms = X86_MACHINE(machine); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *system_io = get_system_io(); - Object *phb = NULL; + Object *phb; ISABus *isa_bus; Object *piix4_pm = NULL; qemu_irq smi_irq; @@ -284,9 +284,7 @@ static void pc_init1(MachineState *machine, const char *pci_type) pc_i8259_create(isa_bus, gsi_state->i8259_irq); } - if (phb) { - ioapic_init_gsi(gsi_state, phb); - } + ioapic_init_gsi(gsi_state, phb); if (tcg_enabled()) { x86_register_ferr_irq(x86ms->gsi[13]); From 1566b8c8df9e8603f5d03cc1a7708c4ecfda0897 Mon Sep 17 00:00:00 2001 From: Stefan Weil via Date: Sat, 9 Aug 2025 08:13:02 +0200 Subject: [PATCH 0573/1794] chardev/baum: Fix compiler warning for Windows builds Compiler warning: ../chardev/baum.c:657:25: warning: comparison between pointer and integer Use brlapi_fileDescriptor instead of int for brlapi_fd and BRLAPI_INVALID_FILE_DESCRIPTOR instead of -1. Signed-off-by: Stefan Weil Reviewed-by: Samuel Thibault Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- chardev/baum.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/chardev/baum.c b/chardev/baum.c index f3e8cd27f0673..ad6832150416f 100644 --- a/chardev/baum.c +++ b/chardev/baum.c @@ -94,7 +94,7 @@ struct BaumChardev { Chardev parent; brlapi_handle_t *brlapi; - int brlapi_fd; + brlapi_fileDescriptor brlapi_fd; unsigned int x, y; bool deferred_init; @@ -654,7 +654,7 @@ static void baum_chr_open(Chardev *chr, baum->brlapi = handle; baum->brlapi_fd = brlapi__openConnection(handle, NULL, NULL); - if (baum->brlapi_fd == -1) { + if (baum->brlapi_fd == BRLAPI_INVALID_FILE_DESCRIPTOR) { error_setg(errp, "brlapi__openConnection: %s", brlapi_strerror(brlapi_error_location())); g_free(handle); @@ -665,6 +665,10 @@ static void baum_chr_open(Chardev *chr, baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum); + /* + * On Windows, brlapi_fd is a pointer, which is being used here + * as an integer, but in practice it seems to work + */ qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum); } From 606978500c3d18fb89a49844f253097b17f757de Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sun, 24 Aug 2025 03:05:32 +0300 Subject: [PATCH 0574/1794] block/curl: fix curl internal handles handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit block/curl.c uses CURLMOPT_SOCKETFUNCTION to register a socket callback. According to the documentation, this callback is called not just with application-created sockets but also with internal curl sockets, - and for such sockets, user data pointer is not set by the application, so the result qemu crashing. Pass BDRVCURLState directly to the callback function as user pointer, instead of relying on CURLINFO_PRIVATE. This problem started happening with update of libcurl from 8.9 to 8.10 -- apparently with this change curl started using private handles more. (CURLINFO_PRIVATE is used in one more place, in curl_multi_check_completion() - it might need a similar fix too) Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3081 Cc: qemu-stable@qemu.org Reviewed-by: Daniel P. Berrangé Signed-off-by: Michael Tokarev --- block/curl.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/block/curl.c b/block/curl.c index 5467678024f71..00b949ea45f6a 100644 --- a/block/curl.c +++ b/block/curl.c @@ -162,13 +162,9 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, void *userp, void *sp) { - BDRVCURLState *s; - CURLState *state = NULL; + BDRVCURLState *s = userp; CURLSocket *socket; - curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&state); - s = state->s; - socket = g_hash_table_lookup(s->sockets, GINT_TO_POINTER(fd)); if (!socket) { socket = g_new0(CURLSocket, 1); @@ -605,6 +601,7 @@ static void curl_attach_aio_context(BlockDriverState *bs, assert(!s->multi); s->multi = curl_multi_init(); s->aio_context = new_context; + curl_multi_setopt(s->multi, CURLMOPT_SOCKETDATA, s); curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb); curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s); curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb); From 29e68f41c064299d4b45f3517c2e4400b1c17231 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Mon, 25 Aug 2025 12:52:46 +0300 Subject: [PATCH 0575/1794] block/curl: drop old/unuspported curl version checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We currently require libcurl >=7.29.0 (since f9cd86fe72be3cd8). Drop older LIBCURL_VERSION_NUM checks from the driver. Reviewed-by: Daniel P. Berrangé Signed-off-by: Michael Tokarev --- block/curl.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/block/curl.c b/block/curl.c index 00b949ea45f6a..e0f98e035a45e 100644 --- a/block/curl.c +++ b/block/curl.c @@ -516,7 +516,7 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state) CURLOPT_REDIR_PROTOCOLS_STR, PROTOCOLS)) { goto err; } -#elif LIBCURL_VERSION_NUM >= 0x071304 +#else if (curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS) || curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS)) { goto err; @@ -821,22 +821,11 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, goto out; } #endif - /* Prior CURL 7.19.4 return value of 0 could mean that the file size is not - * know or the size is zero. From 7.19.4 CURL returns -1 if size is not - * known and zero if it is really zero-length file. */ -#if LIBCURL_VERSION_NUM >= 0x071304 if (cl < 0) { pstrcpy(state->errmsg, CURL_ERROR_SIZE, "Server didn't report file size."); goto out; } -#else - if (cl <= 0) { - pstrcpy(state->errmsg, CURL_ERROR_SIZE, - "Unknown file size or zero-length file."); - goto out; - } -#endif s->len = cl; From 0ac122d933323610b3dc7ce846cb47ba48d78266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 11 Aug 2025 11:43:41 +0200 Subject: [PATCH 0576/1794] scripts/coverity-scan/COMPONENTS.md: Add a 'plugins' category MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cover the TCG plugins files under their own Coverity category. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Acked-by: Alex Bennée Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- scripts/coverity-scan/COMPONENTS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md index 72995903ff9ea..95805b536bcf4 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -147,6 +147,9 @@ tcg system ~ .*/qemu(/system/.*|/accel/.*) +plugins + ~ .*/qemu(/contrib|/tests/tcg)?/plugins/.* + (headers) ~ .*/qemu(/include/.*) From 25fef09ce17ac1ae22638a0b57d97c2bd5cd7d83 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Wed, 27 Aug 2025 11:02:28 +0530 Subject: [PATCH 0577/1794] docs: fix typo in xive doc "Interrupt Pending Buffer" IPB, which got written as IBP due to typo. The "IPB" register is also mentioned in same doc multiple times. Signed-off-by: Aditya Gupta Reviewed-by: Thomas Huth Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- docs/specs/ppc-xive.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/specs/ppc-xive.rst b/docs/specs/ppc-xive.rst index 83d43f658b902..968cc760d4669 100644 --- a/docs/specs/ppc-xive.rst +++ b/docs/specs/ppc-xive.rst @@ -157,7 +157,7 @@ Interrupt flow from an O/S perspective After an event data has been enqueued in the O/S Event Queue, the IVPE raises the bit corresponding to the priority of the pending interrupt -in the register IBP (Interrupt Pending Buffer) to indicate that an +in the register IPB (Interrupt Pending Buffer) to indicate that an event is pending in one of the 8 priority queues. The Pending Interrupt Priority Register (PIPR) is also updated using the IPB. This register represent the priority of the most favored pending From 27ea28a0b369b4b14a485a5d6f045e0dc1db4e38 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Aug 2025 13:49:06 +0000 Subject: [PATCH 0578/1794] tcg/arm: Fix tgen_deposit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When converting from tcg_out_deposit, the arguments were not shuffled properly. Cc: qemu-stable@nongnu.org Fixes: cf4905c03135f1181e8 ("tcg: Convert deposit to TCGOutOpDeposit") Reported-by: Michael Tokarev Tested-by: Michael Tokarev Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/arm/tcg-target.c.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 836894b16ade7..338c57b061631 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -975,7 +975,8 @@ static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2, unsigned ofs, unsigned len) { /* bfi/bfc */ - tcg_out32(s, 0x07c00010 | (COND_AL << 28) | (a0 << 12) | a1 + tcg_debug_assert(a0 == a1); + tcg_out32(s, 0x07c00010 | (COND_AL << 28) | (a0 << 12) | a2 | (ofs << 7) | ((ofs + len - 1) << 16)); } From b8eb3dd49583729edceb18628e626eac15a15de4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 5 Aug 2025 11:40:31 +1000 Subject: [PATCH 0579/1794] cpuinfo/i386: Detect GFNI as an AVX extension We won't use the SSE GFNI instructions, so delay detection until we know AVX is present. Signed-off-by: Richard Henderson --- host/include/i386/host/cpuinfo.h | 1 + include/qemu/cpuid.h | 3 +++ util/cpuinfo-i386.c | 1 + 3 files changed, 5 insertions(+) diff --git a/host/include/i386/host/cpuinfo.h b/host/include/i386/host/cpuinfo.h index 9541a64da6121..93d029d499bbc 100644 --- a/host/include/i386/host/cpuinfo.h +++ b/host/include/i386/host/cpuinfo.h @@ -27,6 +27,7 @@ #define CPUINFO_ATOMIC_VMOVDQU (1u << 17) #define CPUINFO_AES (1u << 18) #define CPUINFO_PCLMUL (1u << 19) +#define CPUINFO_GFNI (1u << 20) /* Initialized with a constructor. */ extern unsigned cpuinfo; diff --git a/include/qemu/cpuid.h b/include/qemu/cpuid.h index b11161555b691..de7a9005096a8 100644 --- a/include/qemu/cpuid.h +++ b/include/qemu/cpuid.h @@ -68,6 +68,9 @@ #ifndef bit_AVX512VBMI2 #define bit_AVX512VBMI2 (1 << 6) #endif +#ifndef bit_GFNI +#define bit_GFNI (1 << 8) +#endif /* Leaf 0x80000001, %ecx */ #ifndef bit_LZCNT diff --git a/util/cpuinfo-i386.c b/util/cpuinfo-i386.c index c8c8a1b3705eb..f4c5b6ff407c9 100644 --- a/util/cpuinfo-i386.c +++ b/util/cpuinfo-i386.c @@ -50,6 +50,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) if ((bv & 6) == 6) { info |= CPUINFO_AVX1; info |= (b7 & bit_AVX2 ? CPUINFO_AVX2 : 0); + info |= (c7 & bit_GFNI ? CPUINFO_GFNI : 0); if ((bv & 0xe0) == 0xe0) { info |= (b7 & bit_AVX512F ? CPUINFO_AVX512F : 0); From 26c41cc4a3d998caa700407a27e18755a6e1895c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Aug 2025 20:33:27 +1000 Subject: [PATCH 0580/1794] tcg/i386: Expand sari of bits-1 as pcmpgt Expand arithmetic right shift of bits-1 as a comparison vs 0. Suggested-by: Paolo Bonzini Signed-off-by: Richard Henderson --- tcg/i386/tcg-target.c.inc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 088c6c9264b01..4cd5d4276c992 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -4357,6 +4357,12 @@ static void expand_vec_sari(TCGType type, unsigned vece, { TCGv_vec t1, t2; + if (imm >= (8 << vece) - 1) { + tcg_gen_cmp_vec(TCG_COND_LT, vece, v0, v1, + tcg_constant_vec(type, MO_64, 0)); + return; + } + switch (vece) { case MO_8: /* Unpack to 16-bit, shift, and repack. */ From ba8a86d67aca43a854c78380c917845059c83d4c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Aug 2025 20:38:40 +1000 Subject: [PATCH 0581/1794] tcg/i386: Use canonical operand ordering in expand_vec_sari MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The optimizer prefers to have constants as the second operand, so expand LT x,0 instead of GT 0,x. This will not affect the generated code at all. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/i386/tcg-target.c.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 4cd5d4276c992..8260c35edd090 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -4399,8 +4399,8 @@ static void expand_vec_sari(TCGType type, unsigned vece, /* Otherwise we will need to use a compare vs 0 to produce * the sign-extend, shift and merge. */ - tcg_gen_cmp_vec(TCG_COND_GT, MO_64, t1, - tcg_constant_vec(type, MO_64, 0), v1); + tcg_gen_cmp_vec(TCG_COND_LT, MO_64, t1, v1, + tcg_constant_vec(type, MO_64, 0)); tcg_gen_shri_vec(MO_64, v0, v1, imm); tcg_gen_shli_vec(MO_64, t1, t1, 64 - imm); tcg_gen_or_vec(MO_64, v0, v0, t1); From 6c76a1f687cd509d26dae44ea39bf396b251fe0e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 5 Aug 2025 11:56:33 +1000 Subject: [PATCH 0582/1794] tcg/i386: Add INDEX_op_x86_vgf2p8affineqb_vec Add a backend-specific opcode for expanding the GFNI vgf2p8affineqb instruction, which we can use for expanding 8-bit immediate shifts and rotates. Signed-off-by: Richard Henderson --- tcg/i386/tcg-target-opc.h.inc | 1 + tcg/i386/tcg-target.c.inc | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/tcg/i386/tcg-target-opc.h.inc b/tcg/i386/tcg-target-opc.h.inc index 8cc0dbaeafee2..8a5cb34dbe79d 100644 --- a/tcg/i386/tcg-target-opc.h.inc +++ b/tcg/i386/tcg-target-opc.h.inc @@ -35,3 +35,4 @@ DEF(x86_punpckh_vec, 1, 2, 0, TCG_OPF_VECTOR) DEF(x86_vpshldi_vec, 1, 2, 1, TCG_OPF_VECTOR) DEF(x86_vpshldv_vec, 1, 3, 0, TCG_OPF_VECTOR) DEF(x86_vpshrdv_vec, 1, 3, 0, TCG_OPF_VECTOR) +DEF(x86_vgf2p8affineqb_vec, 1, 2, 1, TCG_OPF_VECTOR) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 8260c35edd090..efaca0ca674f1 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -451,6 +451,7 @@ static bool tcg_target_const_match(int64_t val, int ct, #define OPC_VPBROADCASTW (0x79 | P_EXT38 | P_DATA16) #define OPC_VPBROADCASTD (0x58 | P_EXT38 | P_DATA16) #define OPC_VPBROADCASTQ (0x59 | P_EXT38 | P_DATA16) +#define OPC_VGF2P8AFFINEQB (0xce | P_EXT3A | P_DATA16 | P_VEXW) #define OPC_VPMOVM2B (0x28 | P_EXT38 | P_SIMDF3 | P_EVEX) #define OPC_VPMOVM2W (0x28 | P_EXT38 | P_SIMDF3 | P_VEXW | P_EVEX) #define OPC_VPMOVM2D (0x38 | P_EXT38 | P_SIMDF3 | P_EVEX) @@ -4084,6 +4085,10 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, insn = vpshldi_insn[vece]; sub = args[3]; goto gen_simd_imm8; + case INDEX_op_x86_vgf2p8affineqb_vec: + insn = OPC_VGF2P8AFFINEQB; + sub = args[3]; + goto gen_simd_imm8; case INDEX_op_not_vec: insn = OPC_VPTERNLOGQ; @@ -4188,6 +4193,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_x86_punpckl_vec: case INDEX_op_x86_punpckh_vec: case INDEX_op_x86_vpshldi_vec: + case INDEX_op_x86_vgf2p8affineqb_vec: #if TCG_TARGET_REG_BITS == 32 case INDEX_op_dup2_vec: #endif From cb2540979264c8d3984e26c5dd90a840e47ec5dd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 5 Aug 2025 15:21:44 +1000 Subject: [PATCH 0583/1794] tcg/i386: Use vgf2p8affineqb for MO_8 vector shifts A constant matrix can describe the movement of the 8 bits, so these shifts can be performed with one instruction. Logic courtesy of Andi Kleen : https://gcc.gnu.org/pipermail/gcc-patches/2025-August/691624.html Signed-off-by: Richard Henderson --- tcg/i386/tcg-target.c.inc | 75 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index efaca0ca674f1..ee272668619a7 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -4342,12 +4342,46 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) } } +static void gen_vgf2p8affineqb0(TCGType type, TCGv_vec v0, + TCGv_vec v1, uint64_t matrix) +{ + vec_gen_4(INDEX_op_x86_vgf2p8affineqb_vec, type, MO_8, + tcgv_vec_arg(v0), tcgv_vec_arg(v1), + tcgv_vec_arg(tcg_constant_vec(type, MO_64, matrix)), 0); +} + static void expand_vec_shi(TCGType type, unsigned vece, bool right, TCGv_vec v0, TCGv_vec v1, TCGArg imm) { + static const uint64_t gf2_shi[2][8] = { + /* left shift */ + { 0, + 0x0001020408102040ull, + 0x0000010204081020ull, + 0x0000000102040810ull, + 0x0000000001020408ull, + 0x0000000000010204ull, + 0x0000000000000102ull, + 0x0000000000000001ull }, + /* right shift */ + { 0, + 0x0204081020408000ull, + 0x0408102040800000ull, + 0x0810204080000000ull, + 0x1020408000000000ull, + 0x2040800000000000ull, + 0x4080000000000000ull, + 0x8000000000000000ull } + }; uint8_t mask; tcg_debug_assert(vece == MO_8); + + if (cpuinfo & CPUINFO_GFNI) { + gen_vgf2p8affineqb0(type, v0, v1, gf2_shi[right][imm]); + return; + } + if (right) { mask = 0xff >> imm; tcg_gen_shri_vec(MO_16, v0, v1, imm); @@ -4361,6 +4395,16 @@ static void expand_vec_shi(TCGType type, unsigned vece, bool right, static void expand_vec_sari(TCGType type, unsigned vece, TCGv_vec v0, TCGv_vec v1, TCGArg imm) { + static const uint64_t gf2_sar[8] = { + 0, + 0x0204081020408080ull, + 0x0408102040808080ull, + 0x0810204080808080ull, + 0x1020408080808080ull, + 0x2040808080808080ull, + 0x4080808080808080ull, + 0x8080808080808080ull, + }; TCGv_vec t1, t2; if (imm >= (8 << vece) - 1) { @@ -4371,6 +4415,11 @@ static void expand_vec_sari(TCGType type, unsigned vece, switch (vece) { case MO_8: + if (cpuinfo & CPUINFO_GFNI) { + gen_vgf2p8affineqb0(type, v0, v1, gf2_sar[imm]); + break; + } + /* Unpack to 16-bit, shift, and repack. */ t1 = tcg_temp_new_vec(type); t2 = tcg_temp_new_vec(type); @@ -4422,12 +4471,30 @@ static void expand_vec_sari(TCGType type, unsigned vece, static void expand_vec_rotli(TCGType type, unsigned vece, TCGv_vec v0, TCGv_vec v1, TCGArg imm) { + static const uint64_t gf2_rol[8] = { + 0, + 0x8001020408102040ull, + 0x4080010204081020ull, + 0x2040800102040810ull, + 0x1020408001020408ull, + 0x0810204080010204ull, + 0x0408102040800102ull, + 0x0204081020408001ull, + }; TCGv_vec t; - if (vece != MO_8 && have_avx512vbmi2) { - vec_gen_4(INDEX_op_x86_vpshldi_vec, type, vece, - tcgv_vec_arg(v0), tcgv_vec_arg(v1), tcgv_vec_arg(v1), imm); - return; + if (vece == MO_8) { + if (cpuinfo & CPUINFO_GFNI) { + gen_vgf2p8affineqb0(type, v0, v1, gf2_rol[imm]); + return; + } + } else { + if (have_avx512vbmi2) { + vec_gen_4(INDEX_op_x86_vpshldi_vec, type, vece, + tcgv_vec_arg(v0), tcgv_vec_arg(v1), + tcgv_vec_arg(v1), imm); + return; + } } t = tcg_temp_new_vec(type); From 889bf9a067804211c2fc4d09a8dc1a66f1472f89 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Tue, 5 Aug 2025 16:36:31 -0500 Subject: [PATCH 0584/1794] MAINTAINERS: Add myself as reviewer for PowerNV and XIVE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding myself as reviewer for PowerNV and XIVE areas. Signed-off-by: Glenn Miles Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250805213646.3285026-1-milesg@linux.ibm.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1ae28e880424f..fb045388b92c2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1575,6 +1575,7 @@ F: tests/functional/ppc64/test_tuxrun.py PowerNV (Non-Virtualized) M: Nicholas Piggin R: Aditya Gupta +R: Glenn Miles L: qemu-ppc@nongnu.org S: Odd Fixes F: docs/system/ppc/powernv.rst @@ -2781,6 +2782,7 @@ T: git https://github.com/philmd/qemu.git fw_cfg-next XIVE R: Gautam Menghani +R: Glenn Miles L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/*/*xive* From 432ca3dfa3d57a7bf1e427576fcfca4ab0079a50 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Thu, 14 Aug 2025 00:05:10 +0800 Subject: [PATCH 0585/1794] vfio/igd: Enable quirks when IGD is not the primary display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since linux 6.15, commit 41112160ca87 ("vfio/pci: match IGD devices in display controller class"), IGD related regions are also exposed when IGD is not primary display (device class is Display controller). Allow IGD quirks to be enabled in this configuration so that guests can have display output on IGD when it is not the primary display. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250813160510.23553-1-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 7 ++++--- hw/vfio/pci.h | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index ee0767b0b89c0..f116c40ccd933 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -460,7 +460,7 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) int gen; if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || - !vfio_is_vga(vdev) || nr != 0) { + !vfio_is_base_display(vdev) || nr != 0) { return; } @@ -518,7 +518,7 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) Error *err = NULL; if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || - !vfio_is_vga(vdev)) { + !vfio_is_base_display(vdev)) { return true; } @@ -534,12 +534,13 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) /* * For backward compatibility, enable legacy mode when * - Device geneation is 6 to 9 (including both) - * - IGD claims VGA cycles on host + * - IGD exposes itself as VGA controller and claims VGA cycles on host * - Machine type is i440fx (pc_piix) * - IGD device is at guest BDF 00:02.0 * - Not manually disabled by x-igd-legacy-mode=off */ if ((vdev->igd_legacy_mode != ON_OFF_AUTO_OFF) && + vfio_is_vga(vdev) && (gen >= 6 && gen <= 9) && !(gmch & IGD_GMCH_VGA_DISABLE) && !strcmp(MACHINE_GET_CLASS(qdev_get_machine())->family, "pc_piix") && diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 810a842f4a153..923cf9c2f79d0 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -203,6 +203,11 @@ static inline bool vfio_is_vga(VFIOPCIDevice *vdev) return (vdev->class_code >> 8) == PCI_CLASS_DISPLAY_VGA; } +static inline bool vfio_is_base_display(VFIOPCIDevice *vdev) +{ + return (vdev->class_code >> 16) == PCI_BASE_CLASS_DISPLAY; +} + /* MSI/MSI-X/INTx */ void vfio_pci_vector_init(VFIOPCIDevice *vdev, int nr); void vfio_pci_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, From aeb1a50d4a7f464a8ff0a66e0beec2a5e1ef6342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Sep 2025 08:46:28 +0200 Subject: [PATCH 0586/1794] vfio: Remove 'vfio-amd-xgbe' device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VFIO_AMD_XGBE device type has been deprecated in the QEMU 10.0 timeframe. The AMD "Seattle" device is not supported anymore. Remove it. Reviewed-by: Eric Auger Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250901064631.530723-6-clg@redhat.com Signed-off-by: Cédric Le Goater --- docs/about/deprecated.rst | 6 - docs/about/removed-features.rst | 9 + docs/devel/kconfig.rst | 1 - hw/arm/Kconfig | 1 - hw/arm/virt.c | 2 - hw/core/sysbus-fdt.c | 316 -------------------------------- hw/vfio/Kconfig | 5 - hw/vfio/amd-xgbe.c | 61 ------ hw/vfio/meson.build | 1 - include/hw/vfio/vfio-amd-xgbe.h | 46 ----- 10 files changed, 9 insertions(+), 439 deletions(-) delete mode 100644 hw/vfio/amd-xgbe.c delete mode 100644 include/hw/vfio/vfio-amd-xgbe.h diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index b2420732e1d7a..eb424f96d26be 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -498,12 +498,6 @@ The vfio-calxeda-xgmac device allows to assign a host Calxeda Highbank string) to a guest. Calxeda HW has been ewasted now and there is no point keeping that device. -``-device vfio-amd-xgbe`` (since 10.0) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The vfio-amd-xgbe device allows to assign a host AMD 10GbE controller -to a guest ("amd,xgbe-seattle-v1a" compatibility string). AMD "Seattle" -is not supported anymore and there is no point keeping that device. - ``-device vfio-platform`` (since 10.0) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The vfio-platform device allows to assign a host platform device diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index fff781d6b7c3b..eb0e5128ba148 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1280,6 +1280,15 @@ The corresponding upstream server project is no longer maintained. Users are recommended to switch to an alternative distributed block device driver such as RBD. +VFIO devices +------------ + +``-device vfio-amd-xgbe`` (since 10.2) +'''''''''''''''''''''''''''''''''''''' +The vfio-amd-xgbe device allows to assign a host AMD 10GbE controller +to a guest ("amd,xgbe-seattle-v1a" compatibility string). AMD "Seattle" +is not supported anymore and there is no point keeping that device. + Tools ----- diff --git a/docs/devel/kconfig.rst b/docs/devel/kconfig.rst index 493b76c4fbf70..9fdf5015298ea 100644 --- a/docs/devel/kconfig.rst +++ b/docs/devel/kconfig.rst @@ -59,7 +59,6 @@ stanza like the following:: config ARM_VIRT bool imply PCI_DEVICES - imply VFIO_AMD_XGBE imply VFIO_XGMAC select A15MPCORE select ACPI diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 2aa4b5d77864f..64b2ec87b59b1 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -5,7 +5,6 @@ config ARM_VIRT depends on TCG || KVM || HVF imply PCI_DEVICES imply TEST_DEVICES - imply VFIO_AMD_XGBE imply VFIO_PLATFORM imply VFIO_XGMAC imply TPM_TIS_SYSBUS diff --git a/hw/arm/virt.c b/hw/arm/virt.c index e5c4142e822d7..75fb157f6cc67 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -39,7 +39,6 @@ #include "hw/arm/virt.h" #include "hw/block/flash.h" #include "hw/vfio/vfio-calxeda-xgmac.h" -#include "hw/vfio/vfio-amd-xgbe.h" #include "hw/display/ramfb.h" #include "net/net.h" #include "system/device_tree.h" @@ -3219,7 +3218,6 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data) */ mc->max_cpus = 512; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_CALXEDA_XGMAC); - machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_AMD_XGBE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_PLATFORM); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c index c339a27875cbe..d3649d5367e72 100644 --- a/hw/core/sysbus-fdt.c +++ b/hw/core/sysbus-fdt.c @@ -34,7 +34,6 @@ #include "hw/platform-bus.h" #include "hw/vfio/vfio-platform.h" #include "hw/vfio/vfio-calxeda-xgmac.h" -#include "hw/vfio/vfio-amd-xgbe.h" #include "hw/vfio/vfio-region.h" #include "hw/display/ramfb.h" #include "hw/uefi/var-service-api.h" @@ -68,142 +67,6 @@ typedef struct HostProperty { #ifdef CONFIG_LINUX -/** - * copy_properties_from_host - * - * copies properties listed in an array from host device tree to - * guest device tree. If a non optional property is not found, the - * function asserts. An optional property is ignored if not found - * in the host device tree. - * @props: array of HostProperty to copy - * @nb_props: number of properties in the array - * @host_dt: host device tree blob - * @guest_dt: guest device tree blob - * @node_path: host dt node path where the property is supposed to be - found - * @nodename: guest node name the properties should be added to - */ -static void copy_properties_from_host(HostProperty *props, int nb_props, - void *host_fdt, void *guest_fdt, - char *node_path, char *nodename) -{ - int i, prop_len; - const void *r; - Error *err = NULL; - - for (i = 0; i < nb_props; i++) { - r = qemu_fdt_getprop(host_fdt, node_path, - props[i].name, - &prop_len, - &err); - if (r) { - qemu_fdt_setprop(guest_fdt, nodename, - props[i].name, r, prop_len); - } else { - if (props[i].optional && prop_len == -FDT_ERR_NOTFOUND) { - /* optional property does not exist */ - error_free(err); - } else { - error_report_err(err); - } - if (!props[i].optional) { - /* mandatory property not found: bail out */ - exit(1); - } - err = NULL; - } - } -} - -/* clock properties whose values are copied/pasted from host */ -static HostProperty clock_copied_properties[] = { - {"compatible", false}, - {"#clock-cells", false}, - {"clock-frequency", true}, - {"clock-output-names", true}, -}; - -/** - * fdt_build_clock_node - * - * Build a guest clock node, used as a dependency from a passthrough'ed - * device. Most information are retrieved from the host clock node. - * Also check the host clock is a fixed one. - * - * @host_fdt: host device tree blob from which info are retrieved - * @guest_fdt: guest device tree blob where the clock node is added - * @host_phandle: phandle of the clock in host device tree - * @guest_phandle: phandle to assign to the guest node - */ -static void fdt_build_clock_node(void *host_fdt, void *guest_fdt, - uint32_t host_phandle, - uint32_t guest_phandle) -{ - char *node_path = NULL; - char *nodename; - const void *r; - int ret, node_offset, prop_len, path_len = 16; - - node_offset = fdt_node_offset_by_phandle(host_fdt, host_phandle); - if (node_offset <= 0) { - error_report("not able to locate clock handle %d in host device tree", - host_phandle); - exit(1); - } - node_path = g_malloc(path_len); - while ((ret = fdt_get_path(host_fdt, node_offset, node_path, path_len)) - == -FDT_ERR_NOSPACE) { - path_len += 16; - node_path = g_realloc(node_path, path_len); - } - if (ret < 0) { - error_report("not able to retrieve node path for clock handle %d", - host_phandle); - exit(1); - } - - r = qemu_fdt_getprop(host_fdt, node_path, "compatible", &prop_len, - &error_fatal); - if (strcmp(r, "fixed-clock")) { - error_report("clock handle %d is not a fixed clock", host_phandle); - exit(1); - } - - nodename = strrchr(node_path, '/'); - qemu_fdt_add_subnode(guest_fdt, nodename); - - copy_properties_from_host(clock_copied_properties, - ARRAY_SIZE(clock_copied_properties), - host_fdt, guest_fdt, - node_path, nodename); - - qemu_fdt_setprop_cell(guest_fdt, nodename, "phandle", guest_phandle); - - g_free(node_path); -} - -/** - * sysfs_to_dt_name: convert the name found in sysfs into the node name - * for instance e0900000.xgmac is converted into xgmac@e0900000 - * @sysfs_name: directory name in sysfs - * - * returns the device tree name upon success or NULL in case the sysfs name - * does not match the expected format - */ -static char *sysfs_to_dt_name(const char *sysfs_name) -{ - gchar **substrings = g_strsplit(sysfs_name, ".", 2); - char *dt_name = NULL; - - if (!substrings || !substrings[0] || !substrings[1]) { - goto out; - } - dt_name = g_strdup_printf("%s@%s", substrings[1], substrings[0]); -out: - g_strfreev(substrings); - return dt_name; -} - /* Device Specific Code */ /** @@ -261,183 +124,6 @@ static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque) g_free(nodename); return 0; } - -/* AMD xgbe properties whose values are copied/pasted from host */ -static HostProperty amd_xgbe_copied_properties[] = { - {"compatible", false}, - {"dma-coherent", true}, - {"amd,per-channel-interrupt", true}, - {"phy-mode", false}, - {"mac-address", true}, - {"amd,speed-set", false}, - {"amd,serdes-blwc", true}, - {"amd,serdes-cdr-rate", true}, - {"amd,serdes-pq-skew", true}, - {"amd,serdes-tx-amp", true}, - {"amd,serdes-dfe-tap-config", true}, - {"amd,serdes-dfe-tap-enable", true}, - {"clock-names", false}, -}; - -/** - * add_amd_xgbe_fdt_node - * - * Generates the combined xgbe/phy node following kernel >=4.2 - * binding documentation: - * Documentation/devicetree/bindings/net/amd-xgbe.txt: - * Also 2 clock nodes are created (dma and ptp) - * - * Asserts in case of error - */ -static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque) -{ - PlatformBusFDTData *data = opaque; - PlatformBusDevice *pbus = data->pbus; - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); - VFIODevice *vbasedev = &vdev->vbasedev; - VFIOINTp *intp; - const char *parent_node = data->pbus_node_name; - char **node_path, *nodename, *dt_name; - void *guest_fdt = data->fdt, *host_fdt; - const void *r; - int i, prop_len; - uint32_t *irq_attr, *reg_attr; - const uint32_t *host_clock_phandles; - uint64_t mmio_base, irq_number; - uint32_t guest_clock_phandles[2]; - - host_fdt = load_device_tree_from_sysfs(); - - dt_name = sysfs_to_dt_name(vbasedev->name); - if (!dt_name) { - error_report("%s incorrect sysfs device name %s", - __func__, vbasedev->name); - exit(1); - } - node_path = qemu_fdt_node_path(host_fdt, dt_name, vdev->compat, - &error_fatal); - if (!node_path || !node_path[0]) { - error_report("%s unable to retrieve node path for %s/%s", - __func__, dt_name, vdev->compat); - exit(1); - } - - if (node_path[1]) { - error_report("%s more than one node matching %s/%s!", - __func__, dt_name, vdev->compat); - exit(1); - } - - g_free(dt_name); - - if (vbasedev->num_regions != 5) { - error_report("%s Does the host dt node combine XGBE/PHY?", __func__); - exit(1); - } - - /* generate nodes for DMA_CLK and PTP_CLK */ - r = qemu_fdt_getprop(host_fdt, node_path[0], "clocks", - &prop_len, &error_fatal); - if (prop_len != 8) { - error_report("%s clocks property should contain 2 handles", __func__); - exit(1); - } - host_clock_phandles = r; - guest_clock_phandles[0] = qemu_fdt_alloc_phandle(guest_fdt); - guest_clock_phandles[1] = qemu_fdt_alloc_phandle(guest_fdt); - - /** - * clock handles fetched from host dt are in be32 layout whereas - * rest of the code uses cpu layout. Also guest clock handles are - * in cpu layout. - */ - fdt_build_clock_node(host_fdt, guest_fdt, - be32_to_cpu(host_clock_phandles[0]), - guest_clock_phandles[0]); - - fdt_build_clock_node(host_fdt, guest_fdt, - be32_to_cpu(host_clock_phandles[1]), - guest_clock_phandles[1]); - - /* combined XGBE/PHY node */ - mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); - nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, - vbasedev->name, mmio_base); - qemu_fdt_add_subnode(guest_fdt, nodename); - - copy_properties_from_host(amd_xgbe_copied_properties, - ARRAY_SIZE(amd_xgbe_copied_properties), - host_fdt, guest_fdt, - node_path[0], nodename); - - qemu_fdt_setprop_cells(guest_fdt, nodename, "clocks", - guest_clock_phandles[0], - guest_clock_phandles[1]); - - reg_attr = g_new(uint32_t, vbasedev->num_regions * 2); - for (i = 0; i < vbasedev->num_regions; i++) { - mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i); - reg_attr[2 * i] = cpu_to_be32(mmio_base); - reg_attr[2 * i + 1] = cpu_to_be32( - memory_region_size(vdev->regions[i]->mem)); - } - qemu_fdt_setprop(guest_fdt, nodename, "reg", reg_attr, - vbasedev->num_regions * 2 * sizeof(uint32_t)); - - irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3); - for (i = 0; i < vbasedev->num_irqs; i++) { - irq_number = platform_bus_get_irqn(pbus, sbdev , i) - + data->irq_start; - irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI); - irq_attr[3 * i + 1] = cpu_to_be32(irq_number); - /* - * General device interrupt and PCS auto-negotiation interrupts are - * level-sensitive while the 4 per-channel interrupts are edge - * sensitive - */ - QLIST_FOREACH(intp, &vdev->intp_list, next) { - if (intp->pin == i) { - break; - } - } - if (intp->flags & VFIO_IRQ_INFO_AUTOMASKED) { - irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI); - } else { - irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); - } - } - qemu_fdt_setprop(guest_fdt, nodename, "interrupts", - irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t)); - - g_free(host_fdt); - g_strfreev(node_path); - g_free(irq_attr); - g_free(reg_attr); - g_free(nodename); - return 0; -} - -/* DT compatible matching */ -static bool vfio_platform_match(SysBusDevice *sbdev, - const BindingEntry *entry) -{ - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); - const char *compat; - unsigned int n; - - for (n = vdev->num_compat, compat = vdev->compat; n > 0; - n--, compat += strlen(compat) + 1) { - if (!strcmp(entry->compat, compat)) { - return true; - } - } - - return false; -} - -#define VFIO_PLATFORM_BINDING(compat, add_fn) \ - {TYPE_VFIO_PLATFORM, (compat), (add_fn), vfio_platform_match} - #endif /* CONFIG_LINUX */ #ifdef CONFIG_TPM @@ -512,8 +198,6 @@ static bool type_match(SysBusDevice *sbdev, const BindingEntry *entry) static const BindingEntry bindings[] = { #ifdef CONFIG_LINUX TYPE_BINDING(TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node), - TYPE_BINDING(TYPE_VFIO_AMD_XGBE, add_amd_xgbe_fdt_node), - VFIO_PLATFORM_BINDING("amd,xgbe-seattle-v1a", add_amd_xgbe_fdt_node), #endif #ifdef CONFIG_TPM TYPE_BINDING(TYPE_TPM_TIS_SYSBUS, add_tpm_tis_fdt_node), diff --git a/hw/vfio/Kconfig b/hw/vfio/Kconfig index 91d9023b79b59..bc984f1986396 100644 --- a/hw/vfio/Kconfig +++ b/hw/vfio/Kconfig @@ -28,11 +28,6 @@ config VFIO_XGMAC default y depends on VFIO_PLATFORM -config VFIO_AMD_XGBE - bool - default y - depends on VFIO_PLATFORM - config VFIO_AP bool default y diff --git a/hw/vfio/amd-xgbe.c b/hw/vfio/amd-xgbe.c deleted file mode 100644 index 58f590e385b90..0000000000000 --- a/hw/vfio/amd-xgbe.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * AMD XGBE VFIO device - * - * Copyright Linaro Limited, 2015 - * - * Authors: - * Eric Auger - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "hw/vfio/vfio-amd-xgbe.h" -#include "migration/vmstate.h" -#include "qemu/module.h" -#include "qemu/error-report.h" - -static void amd_xgbe_realize(DeviceState *dev, Error **errp) -{ - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev); - VFIOAmdXgbeDeviceClass *k = VFIO_AMD_XGBE_DEVICE_GET_CLASS(dev); - - warn_report("-device vfio-amd-xgbe is deprecated"); - vdev->compat = g_strdup("amd,xgbe-seattle-v1a"); - vdev->num_compat = 1; - - k->parent_realize(dev, errp); -} - -static const VMStateDescription vfio_platform_amd_xgbe_vmstate = { - .name = "vfio-amd-xgbe", - .unmigratable = 1, -}; - -static void vfio_amd_xgbe_class_init(ObjectClass *klass, const void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VFIOAmdXgbeDeviceClass *vcxc = - VFIO_AMD_XGBE_DEVICE_CLASS(klass); - device_class_set_parent_realize(dc, amd_xgbe_realize, - &vcxc->parent_realize); - dc->desc = "VFIO AMD XGBE"; - dc->vmsd = &vfio_platform_amd_xgbe_vmstate; -} - -static const TypeInfo vfio_amd_xgbe_dev_info = { - .name = TYPE_VFIO_AMD_XGBE, - .parent = TYPE_VFIO_PLATFORM, - .instance_size = sizeof(VFIOAmdXgbeDevice), - .class_init = vfio_amd_xgbe_class_init, - .class_size = sizeof(VFIOAmdXgbeDeviceClass), -}; - -static void register_amd_xgbe_dev_type(void) -{ - type_register_static(&vfio_amd_xgbe_dev_info); -} - -type_init(register_amd_xgbe_dev_type) diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index bfaf6be805483..0edcaf5155c88 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -20,7 +20,6 @@ vfio_ss.add(when: 'CONFIG_VFIO_IGD', if_true: files('igd.c')) specific_ss.add_all(when: 'CONFIG_VFIO', if_true: vfio_ss) system_ss.add(when: 'CONFIG_VFIO_XGMAC', if_true: files('calxeda-xgmac.c')) -system_ss.add(when: 'CONFIG_VFIO_AMD_XGBE', if_true: files('amd-xgbe.c')) system_ss.add(when: 'CONFIG_VFIO', if_true: files( 'cpr.c', 'cpr-legacy.c', diff --git a/include/hw/vfio/vfio-amd-xgbe.h b/include/hw/vfio/vfio-amd-xgbe.h deleted file mode 100644 index a894546c02d14..0000000000000 --- a/include/hw/vfio/vfio-amd-xgbe.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * VFIO AMD XGBE device - * - * Copyright Linaro Limited, 2015 - * - * Authors: - * Eric Auger - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#ifndef HW_VFIO_VFIO_AMD_XGBE_H -#define HW_VFIO_VFIO_AMD_XGBE_H - -#include "hw/vfio/vfio-platform.h" -#include "qom/object.h" - -#define TYPE_VFIO_AMD_XGBE "vfio-amd-xgbe" - -/** - * This device exposes: - * - 5 MMIO regions: MAC, PCS, SerDes Rx/Tx regs, - SerDes Integration Registers 1/2 & 2/2 - * - 2 level sensitive IRQs and optional DMA channel IRQs - */ -struct VFIOAmdXgbeDevice { - VFIOPlatformDevice vdev; -}; - -typedef struct VFIOAmdXgbeDevice VFIOAmdXgbeDevice; - -struct VFIOAmdXgbeDeviceClass { - /*< private >*/ - VFIOPlatformDeviceClass parent_class; - /*< public >*/ - DeviceRealize parent_realize; -}; - -typedef struct VFIOAmdXgbeDeviceClass VFIOAmdXgbeDeviceClass; - -DECLARE_OBJ_CHECKERS(VFIOAmdXgbeDevice, VFIOAmdXgbeDeviceClass, - VFIO_AMD_XGBE_DEVICE, TYPE_VFIO_AMD_XGBE) - -#endif From 8ebc416ac17a71aec267df1ca5cb5301cc6c4906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Sep 2025 08:46:29 +0200 Subject: [PATCH 0587/1794] vfio: Remove 'vfio-calxeda-xgmac' device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VFIO_XGMAC device type has been deprecated in the QEMU 10.0 timeframe. Remove it. Reviewed-by: Eric Auger Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250901064631.530723-7-clg@redhat.com Signed-off-by: Cédric Le Goater --- docs/about/deprecated.rst | 7 --- docs/about/removed-features.rst | 7 +++ docs/devel/kconfig.rst | 1 - hw/arm/Kconfig | 1 - hw/arm/virt.c | 3 +- hw/core/sysbus-fdt.c | 67 ---------------------------- hw/vfio/Kconfig | 5 --- hw/vfio/calxeda-xgmac.c | 61 ------------------------- hw/vfio/meson.build | 1 - include/hw/vfio/vfio-calxeda-xgmac.h | 43 ------------------ 10 files changed, 8 insertions(+), 188 deletions(-) delete mode 100644 hw/vfio/calxeda-xgmac.c delete mode 100644 include/hw/vfio/vfio-calxeda-xgmac.h diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index eb424f96d26be..d0fa8e5536b58 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -491,13 +491,6 @@ which is not enough for all types of use cases, use ``reconnect-ms`` instead. VFIO device options ''''''''''''''''''' -``-device vfio-calxeda-xgmac`` (since 10.0) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The vfio-calxeda-xgmac device allows to assign a host Calxeda Highbank -10Gb XGMAC Ethernet controller device ("calxeda,hb-xgmac" compatibility -string) to a guest. Calxeda HW has been ewasted now and there is no point -keeping that device. - ``-device vfio-platform`` (since 10.0) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The vfio-platform device allows to assign a host platform device diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index eb0e5128ba148..db3f22941c25e 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1283,6 +1283,13 @@ device driver such as RBD. VFIO devices ------------ +``-device vfio-calxeda-xgmac`` (since 10.2) +''''''''''''''''''''''''''''''''''''''''''' +The vfio-calxeda-xgmac device allows to assign a host Calxeda Highbank +10Gb XGMAC Ethernet controller device ("calxeda,hb-xgmac" compatibility +string) to a guest. Calxeda HW has been ewasted now and there is no point +keeping that device. + ``-device vfio-amd-xgbe`` (since 10.2) '''''''''''''''''''''''''''''''''''''' The vfio-amd-xgbe device allows to assign a host AMD 10GbE controller diff --git a/docs/devel/kconfig.rst b/docs/devel/kconfig.rst index 9fdf5015298ea..1d4a114a022af 100644 --- a/docs/devel/kconfig.rst +++ b/docs/devel/kconfig.rst @@ -59,7 +59,6 @@ stanza like the following:: config ARM_VIRT bool imply PCI_DEVICES - imply VFIO_XGMAC select A15MPCORE select ACPI select ARM_SMMUV3 diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 64b2ec87b59b1..3fca48349ade9 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -6,7 +6,6 @@ config ARM_VIRT imply PCI_DEVICES imply TEST_DEVICES imply VFIO_PLATFORM - imply VFIO_XGMAC imply TPM_TIS_SYSBUS imply TPM_TIS_I2C imply NVDIMM diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 75fb157f6cc67..6a887228bbb8b 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -38,7 +38,7 @@ #include "hw/arm/primecell.h" #include "hw/arm/virt.h" #include "hw/block/flash.h" -#include "hw/vfio/vfio-calxeda-xgmac.h" +#include "hw/vfio/vfio-platform.h" #include "hw/display/ramfb.h" #include "net/net.h" #include "system/device_tree.h" @@ -3217,7 +3217,6 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data) * configuration of the particular instance. */ mc->max_cpus = 512; - machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_CALXEDA_XGMAC); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_PLATFORM); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c index d3649d5367e72..07117363a6f27 100644 --- a/hw/core/sysbus-fdt.c +++ b/hw/core/sysbus-fdt.c @@ -32,9 +32,6 @@ #include "system/device_tree.h" #include "system/tpm.h" #include "hw/platform-bus.h" -#include "hw/vfio/vfio-platform.h" -#include "hw/vfio/vfio-calxeda-xgmac.h" -#include "hw/vfio/vfio-region.h" #include "hw/display/ramfb.h" #include "hw/uefi/var-service-api.h" #include "hw/arm/fdt.h" @@ -65,67 +62,6 @@ typedef struct HostProperty { bool optional; } HostProperty; -#ifdef CONFIG_LINUX - -/* Device Specific Code */ - -/** - * add_calxeda_midway_xgmac_fdt_node - * - * Generates a simple node with following properties: - * compatible string, regs, interrupts, dma-coherent - */ -static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque) -{ - PlatformBusFDTData *data = opaque; - PlatformBusDevice *pbus = data->pbus; - void *fdt = data->fdt; - const char *parent_node = data->pbus_node_name; - int compat_str_len, i; - char *nodename; - uint32_t *irq_attr, *reg_attr; - uint64_t mmio_base, irq_number; - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); - VFIODevice *vbasedev = &vdev->vbasedev; - - mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); - nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, - vbasedev->name, mmio_base); - qemu_fdt_add_subnode(fdt, nodename); - - compat_str_len = strlen(vdev->compat) + 1; - qemu_fdt_setprop(fdt, nodename, "compatible", - vdev->compat, compat_str_len); - - qemu_fdt_setprop(fdt, nodename, "dma-coherent", "", 0); - - reg_attr = g_new(uint32_t, vbasedev->num_regions * 2); - for (i = 0; i < vbasedev->num_regions; i++) { - mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i); - reg_attr[2 * i] = cpu_to_be32(mmio_base); - reg_attr[2 * i + 1] = cpu_to_be32( - memory_region_size(vdev->regions[i]->mem)); - } - qemu_fdt_setprop(fdt, nodename, "reg", reg_attr, - vbasedev->num_regions * 2 * sizeof(uint32_t)); - - irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3); - for (i = 0; i < vbasedev->num_irqs; i++) { - irq_number = platform_bus_get_irqn(pbus, sbdev , i) - + data->irq_start; - irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI); - irq_attr[3 * i + 1] = cpu_to_be32(irq_number); - irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI); - } - qemu_fdt_setprop(fdt, nodename, "interrupts", - irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t)); - g_free(irq_attr); - g_free(reg_attr); - g_free(nodename); - return 0; -} -#endif /* CONFIG_LINUX */ - #ifdef CONFIG_TPM /* * add_tpm_tis_fdt_node: Create a DT node for TPM TIS @@ -196,9 +132,6 @@ static bool type_match(SysBusDevice *sbdev, const BindingEntry *entry) /* list of supported dynamic sysbus bindings */ static const BindingEntry bindings[] = { -#ifdef CONFIG_LINUX - TYPE_BINDING(TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node), -#endif #ifdef CONFIG_TPM TYPE_BINDING(TYPE_TPM_TIS_SYSBUS, add_tpm_tis_fdt_node), #endif diff --git a/hw/vfio/Kconfig b/hw/vfio/Kconfig index bc984f1986396..9a1dbe29267ea 100644 --- a/hw/vfio/Kconfig +++ b/hw/vfio/Kconfig @@ -23,11 +23,6 @@ config VFIO_PLATFORM select VFIO depends on LINUX && PLATFORM_BUS -config VFIO_XGMAC - bool - default y - depends on VFIO_PLATFORM - config VFIO_AP bool default y diff --git a/hw/vfio/calxeda-xgmac.c b/hw/vfio/calxeda-xgmac.c deleted file mode 100644 index 03f2ff57630b5..0000000000000 --- a/hw/vfio/calxeda-xgmac.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * calxeda xgmac VFIO device - * - * Copyright Linaro Limited, 2014 - * - * Authors: - * Eric Auger - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "hw/vfio/vfio-calxeda-xgmac.h" -#include "migration/vmstate.h" -#include "qemu/module.h" -#include "qemu/error-report.h" - -static void calxeda_xgmac_realize(DeviceState *dev, Error **errp) -{ - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev); - VFIOCalxedaXgmacDeviceClass *k = VFIO_CALXEDA_XGMAC_DEVICE_GET_CLASS(dev); - - warn_report("-device vfio-calxeda-xgmac is deprecated"); - vdev->compat = g_strdup("calxeda,hb-xgmac"); - vdev->num_compat = 1; - - k->parent_realize(dev, errp); -} - -static const VMStateDescription vfio_platform_calxeda_xgmac_vmstate = { - .name = "vfio-calxeda-xgmac", - .unmigratable = 1, -}; - -static void vfio_calxeda_xgmac_class_init(ObjectClass *klass, const void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VFIOCalxedaXgmacDeviceClass *vcxc = - VFIO_CALXEDA_XGMAC_DEVICE_CLASS(klass); - device_class_set_parent_realize(dc, calxeda_xgmac_realize, - &vcxc->parent_realize); - dc->desc = "VFIO Calxeda XGMAC"; - dc->vmsd = &vfio_platform_calxeda_xgmac_vmstate; -} - -static const TypeInfo vfio_calxeda_xgmac_dev_info = { - .name = TYPE_VFIO_CALXEDA_XGMAC, - .parent = TYPE_VFIO_PLATFORM, - .instance_size = sizeof(VFIOCalxedaXgmacDevice), - .class_init = vfio_calxeda_xgmac_class_init, - .class_size = sizeof(VFIOCalxedaXgmacDeviceClass), -}; - -static void register_calxeda_xgmac_dev_type(void) -{ - type_register_static(&vfio_calxeda_xgmac_dev_info); -} - -type_init(register_calxeda_xgmac_dev_type) diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 0edcaf5155c88..06473a078990e 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -19,7 +19,6 @@ vfio_ss.add(when: 'CONFIG_VFIO_IGD', if_true: files('igd.c')) specific_ss.add_all(when: 'CONFIG_VFIO', if_true: vfio_ss) -system_ss.add(when: 'CONFIG_VFIO_XGMAC', if_true: files('calxeda-xgmac.c')) system_ss.add(when: 'CONFIG_VFIO', if_true: files( 'cpr.c', 'cpr-legacy.c', diff --git a/include/hw/vfio/vfio-calxeda-xgmac.h b/include/hw/vfio/vfio-calxeda-xgmac.h deleted file mode 100644 index 8482f151dd5ab..0000000000000 --- a/include/hw/vfio/vfio-calxeda-xgmac.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * VFIO calxeda xgmac device - * - * Copyright Linaro Limited, 2014 - * - * Authors: - * Eric Auger - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#ifndef HW_VFIO_VFIO_CALXEDA_XGMAC_H -#define HW_VFIO_VFIO_CALXEDA_XGMAC_H - -#include "hw/vfio/vfio-platform.h" -#include "qom/object.h" - -#define TYPE_VFIO_CALXEDA_XGMAC "vfio-calxeda-xgmac" - -/** - * This device exposes: - * - a single MMIO region corresponding to its register space - * - 3 IRQS (main and 2 power related IRQs) - */ -struct VFIOCalxedaXgmacDevice { - VFIOPlatformDevice vdev; -}; -typedef struct VFIOCalxedaXgmacDevice VFIOCalxedaXgmacDevice; - -struct VFIOCalxedaXgmacDeviceClass { - /*< private >*/ - VFIOPlatformDeviceClass parent_class; - /*< public >*/ - DeviceRealize parent_realize; -}; -typedef struct VFIOCalxedaXgmacDeviceClass VFIOCalxedaXgmacDeviceClass; - -DECLARE_OBJ_CHECKERS(VFIOCalxedaXgmacDevice, VFIOCalxedaXgmacDeviceClass, - VFIO_CALXEDA_XGMAC_DEVICE, TYPE_VFIO_CALXEDA_XGMAC) - -#endif From 762c85543948bf1f7838d663995648635d3f4b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Sep 2025 08:46:30 +0200 Subject: [PATCH 0588/1794] vfio: Remove 'vfio-platform' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VFIO_PLATFORM device type has been deprecated in the QEMU 10.0 timeframe. All dependent devices have been removed. Now remove the core vfio platform framework. Rename VFIO_DEVICE_TYPE_PLATFORM enum to VFIO_DEVICE_TYPE_UNUSED to maintain the same index for the CCW and AP VFIO device types. Reviewed-by: Eric Auger Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250901064631.530723-8-clg@redhat.com Signed-off-by: Cédric Le Goater --- docs/about/deprecated.rst | 12 - docs/about/removed-features.rst | 9 + hw/arm/Kconfig | 1 - hw/arm/virt.c | 2 - hw/vfio/Kconfig | 6 - hw/vfio/meson.build | 1 - hw/vfio/platform.c | 716 -------------------------------- hw/vfio/trace-events | 11 - include/hw/vfio/vfio-device.h | 2 +- include/hw/vfio/vfio-platform.h | 78 ---- 10 files changed, 10 insertions(+), 828 deletions(-) delete mode 100644 hw/vfio/platform.c delete mode 100644 include/hw/vfio/vfio-platform.h diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index d0fa8e5536b58..2fa2c47b6807c 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -488,18 +488,6 @@ Stream ``reconnect`` (since 9.2) The ``reconnect`` option only allows specifying second granularity timeouts, which is not enough for all types of use cases, use ``reconnect-ms`` instead. -VFIO device options -''''''''''''''''''' - -``-device vfio-platform`` (since 10.0) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The vfio-platform device allows to assign a host platform device -to a guest in a generic manner. Integrating a new device into -the vfio-platform infrastructure requires some adaptation at -both kernel and qemu level. No such attempt has been done for years -and the conclusion is that vfio-platform has not got any traction. -PCIe passthrough shall be the mainline solution. - CPU device properties ''''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index db3f22941c25e..2d3a684e53a4e 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1296,6 +1296,15 @@ The vfio-amd-xgbe device allows to assign a host AMD 10GbE controller to a guest ("amd,xgbe-seattle-v1a" compatibility string). AMD "Seattle" is not supported anymore and there is no point keeping that device. +``-device vfio-platform`` (since 10.2) +'''''''''''''''''''''''''''''''''''''' +The vfio-platform device allows to assign a host platform device +to a guest in a generic manner. Integrating a new device into +the vfio-platform infrastructure requires some adaptation at +both kernel and qemu level. No such attempt has been done for years +and the conclusion is that vfio-platform has not got any traction. +PCIe passthrough shall be the mainline solution. + Tools ----- diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 3fca48349ade9..3baa6c6c74778 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -5,7 +5,6 @@ config ARM_VIRT depends on TCG || KVM || HVF imply PCI_DEVICES imply TEST_DEVICES - imply VFIO_PLATFORM imply TPM_TIS_SYSBUS imply TPM_TIS_I2C imply NVDIMM diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 6a887228bbb8b..6f01746e74606 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -38,7 +38,6 @@ #include "hw/arm/primecell.h" #include "hw/arm/virt.h" #include "hw/block/flash.h" -#include "hw/vfio/vfio-platform.h" #include "hw/display/ramfb.h" #include "net/net.h" #include "system/device_tree.h" @@ -3218,7 +3217,6 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data) */ mc->max_cpus = 512; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); - machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_PLATFORM); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); diff --git a/hw/vfio/Kconfig b/hw/vfio/Kconfig index 9a1dbe29267ea..27de24e4db1f0 100644 --- a/hw/vfio/Kconfig +++ b/hw/vfio/Kconfig @@ -17,12 +17,6 @@ config VFIO_CCW select VFIO depends on LINUX && S390_CCW_VIRTIO -config VFIO_PLATFORM - bool - default y - select VFIO - depends on LINUX && PLATFORM_BUS - config VFIO_AP bool default y diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 06473a078990e..d3ed3cb7ac199 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -13,7 +13,6 @@ vfio_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files( 'pci.c', )) vfio_ss.add(when: 'CONFIG_VFIO_CCW', if_true: files('ccw.c')) -vfio_ss.add(when: 'CONFIG_VFIO_PLATFORM', if_true: files('platform.c')) vfio_ss.add(when: 'CONFIG_VFIO_AP', if_true: files('ap.c')) vfio_ss.add(when: 'CONFIG_VFIO_IGD', if_true: files('igd.c')) diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c deleted file mode 100644 index 5c1795a26fe79..0000000000000 --- a/hw/vfio/platform.c +++ /dev/null @@ -1,716 +0,0 @@ -/* - * vfio based device assignment support - platform devices - * - * Copyright Linaro Limited, 2014 - * - * Authors: - * Kim Phillips - * Eric Auger - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Based on vfio based PCI device assignment support: - * Copyright Red Hat, Inc. 2012 - */ - -#include "qemu/osdep.h" -#include CONFIG_DEVICES /* CONFIG_IOMMUFD */ -#include "qapi/error.h" -#include -#include - -#include "hw/vfio/vfio-platform.h" -#include "system/iommufd.h" -#include "migration/vmstate.h" -#include "qemu/error-report.h" -#include "qemu/lockable.h" -#include "qemu/main-loop.h" -#include "qemu/module.h" -#include "qemu/range.h" -#include "system/memory.h" -#include "system/address-spaces.h" -#include "qemu/queue.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "hw/irq.h" -#include "hw/platform-bus.h" -#include "hw/qdev-properties.h" -#include "system/kvm.h" -#include "hw/vfio/vfio-region.h" - -/* - * Functions used whatever the injection method - */ - -static inline bool vfio_irq_is_automasked(VFIOINTp *intp) -{ - return intp->flags & VFIO_IRQ_INFO_AUTOMASKED; -} - -/** - * vfio_init_intp - allocate, initialize the IRQ struct pointer - * and add it into the list of IRQs - * @vbasedev: the VFIO device handle - * @info: irq info struct retrieved from VFIO driver - * @errp: error object - */ -static VFIOINTp *vfio_init_intp(VFIODevice *vbasedev, - struct vfio_irq_info info, Error **errp) -{ - int ret; - VFIOPlatformDevice *vdev = - container_of(vbasedev, VFIOPlatformDevice, vbasedev); - SysBusDevice *sbdev = SYS_BUS_DEVICE(vdev); - VFIOINTp *intp; - - intp = g_malloc0(sizeof(*intp)); - intp->vdev = vdev; - intp->pin = info.index; - intp->flags = info.flags; - intp->state = VFIO_IRQ_INACTIVE; - intp->kvm_accel = false; - - sysbus_init_irq(sbdev, &intp->qemuirq); - - /* Get an eventfd for trigger */ - intp->interrupt = g_new0(EventNotifier, 1); - ret = event_notifier_init(intp->interrupt, 0); - if (ret) { - g_free(intp->interrupt); - g_free(intp); - error_setg_errno(errp, -ret, - "failed to initialize trigger eventfd notifier"); - return NULL; - } - if (vfio_irq_is_automasked(intp)) { - /* Get an eventfd for resample/unmask */ - intp->unmask = g_new0(EventNotifier, 1); - ret = event_notifier_init(intp->unmask, 0); - if (ret) { - g_free(intp->interrupt); - g_free(intp->unmask); - g_free(intp); - error_setg_errno(errp, -ret, - "failed to initialize resample eventfd notifier"); - return NULL; - } - } - - QLIST_INSERT_HEAD(&vdev->intp_list, intp, next); - return intp; -} - -/** - * vfio_set_trigger_eventfd - set VFIO eventfd handling - * - * @intp: IRQ struct handle - * @handler: handler to be called on eventfd signaling - * - * Setup VFIO signaling and attach an optional user-side handler - * to the eventfd - */ -static int vfio_set_trigger_eventfd(VFIOINTp *intp, - eventfd_user_side_handler_t handler) -{ - VFIODevice *vbasedev = &intp->vdev->vbasedev; - int32_t fd = event_notifier_get_fd(intp->interrupt); - Error *err = NULL; - - qemu_set_fd_handler(fd, (IOHandler *)handler, NULL, intp); - - if (!vfio_device_irq_set_signaling(vbasedev, intp->pin, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { - error_reportf_err(err, VFIO_MSG_PREFIX, vbasedev->name); - qemu_set_fd_handler(fd, NULL, NULL, NULL); - return -EINVAL; - } - - return 0; -} - -/* - * Functions only used when eventfds are handled on user-side - * ie. without irqfd - */ - -/** - * vfio_mmap_set_enabled - enable/disable the fast path mode - * @vdev: the VFIO platform device - * @enabled: the target mmap state - * - * enabled = true ~ fast path = MMIO region is mmaped (no KVM TRAP); - * enabled = false ~ slow path = MMIO region is trapped and region callbacks - * are called; slow path enables to trap the device IRQ status register reset -*/ - -static void vfio_mmap_set_enabled(VFIOPlatformDevice *vdev, bool enabled) -{ - int i; - - for (i = 0; i < vdev->vbasedev.num_regions; i++) { - vfio_region_mmaps_set_enabled(vdev->regions[i], enabled); - } -} - -/** - * vfio_intp_mmap_enable - timer function, restores the fast path - * if there is no more active IRQ - * @opaque: actually points to the VFIO platform device - * - * Called on mmap timer timeout, this function checks whether the - * IRQ is still active and if not, restores the fast path. - * by construction a single eventfd is handled at a time. - * if the IRQ is still active, the timer is re-programmed. - */ -static void vfio_intp_mmap_enable(void *opaque) -{ - VFIOINTp *tmp; - VFIOPlatformDevice *vdev = (VFIOPlatformDevice *)opaque; - - QEMU_LOCK_GUARD(&vdev->intp_mutex); - QLIST_FOREACH(tmp, &vdev->intp_list, next) { - if (tmp->state == VFIO_IRQ_ACTIVE) { - trace_vfio_platform_intp_mmap_enable(tmp->pin); - /* re-program the timer to check active status later */ - timer_mod(vdev->mmap_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + - vdev->mmap_timeout); - return; - } - } - vfio_mmap_set_enabled(vdev, true); -} - -/** - * vfio_intp_inject_pending_lockheld - Injects a pending IRQ - * @opaque: opaque pointer, in practice the VFIOINTp handle - * - * The function is called on a previous IRQ completion, from - * vfio_platform_eoi, while the intp_mutex is locked. - * Also in such situation, the slow path already is set and - * the mmap timer was already programmed. - */ -static void vfio_intp_inject_pending_lockheld(VFIOINTp *intp) -{ - trace_vfio_platform_intp_inject_pending_lockheld(intp->pin, - event_notifier_get_fd(intp->interrupt)); - - intp->state = VFIO_IRQ_ACTIVE; - - /* trigger the virtual IRQ */ - qemu_set_irq(intp->qemuirq, 1); -} - -/** - * vfio_intp_interrupt - The user-side eventfd handler - * @opaque: opaque pointer which in practice is the VFIOINTp handle - * - * the function is entered in event handler context: - * the vIRQ is injected into the guest if there is no other active - * or pending IRQ. - */ -static void vfio_intp_interrupt(VFIOINTp *intp) -{ - int ret; - VFIOINTp *tmp; - VFIOPlatformDevice *vdev = intp->vdev; - bool delay_handling = false; - - QEMU_LOCK_GUARD(&vdev->intp_mutex); - if (intp->state == VFIO_IRQ_INACTIVE) { - QLIST_FOREACH(tmp, &vdev->intp_list, next) { - if (tmp->state == VFIO_IRQ_ACTIVE || - tmp->state == VFIO_IRQ_PENDING) { - delay_handling = true; - break; - } - } - } - if (delay_handling) { - /* - * the new IRQ gets a pending status and is pushed in - * the pending queue - */ - intp->state = VFIO_IRQ_PENDING; - trace_vfio_intp_interrupt_set_pending(intp->pin); - QSIMPLEQ_INSERT_TAIL(&vdev->pending_intp_queue, - intp, pqnext); - event_notifier_test_and_clear(intp->interrupt); - return; - } - - trace_vfio_platform_intp_interrupt(intp->pin, - event_notifier_get_fd(intp->interrupt)); - - ret = event_notifier_test_and_clear(intp->interrupt); - if (!ret) { - error_report("Error when clearing fd=%d (ret = %d)", - event_notifier_get_fd(intp->interrupt), ret); - } - - intp->state = VFIO_IRQ_ACTIVE; - - /* sets slow path */ - vfio_mmap_set_enabled(vdev, false); - - /* trigger the virtual IRQ */ - qemu_set_irq(intp->qemuirq, 1); - - /* - * Schedule the mmap timer which will restore fastpath when no IRQ - * is active anymore - */ - if (vdev->mmap_timeout) { - timer_mod(vdev->mmap_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + - vdev->mmap_timeout); - } -} - -/** - * vfio_platform_eoi - IRQ completion routine - * @vbasedev: the VFIO device handle - * - * De-asserts the active virtual IRQ and unmasks the physical IRQ - * (effective for level sensitive IRQ auto-masked by the VFIO driver). - * Then it handles next pending IRQ if any. - * eoi function is called on the first access to any MMIO region - * after an IRQ was triggered, trapped since slow path was set. - * It is assumed this access corresponds to the IRQ status - * register reset. With such a mechanism, a single IRQ can be - * handled at a time since there is no way to know which IRQ - * was completed by the guest (we would need additional details - * about the IRQ status register mask). - */ -static void vfio_platform_eoi(VFIODevice *vbasedev) -{ - VFIOINTp *intp; - VFIOPlatformDevice *vdev = - container_of(vbasedev, VFIOPlatformDevice, vbasedev); - - QEMU_LOCK_GUARD(&vdev->intp_mutex); - QLIST_FOREACH(intp, &vdev->intp_list, next) { - if (intp->state == VFIO_IRQ_ACTIVE) { - trace_vfio_platform_eoi(intp->pin, - event_notifier_get_fd(intp->interrupt)); - intp->state = VFIO_IRQ_INACTIVE; - - /* deassert the virtual IRQ */ - qemu_set_irq(intp->qemuirq, 0); - - if (vfio_irq_is_automasked(intp)) { - /* unmasks the physical level-sensitive IRQ */ - vfio_device_irq_unmask(vbasedev, intp->pin); - } - - /* a single IRQ can be active at a time */ - break; - } - } - /* in case there are pending IRQs, handle the first one */ - if (!QSIMPLEQ_EMPTY(&vdev->pending_intp_queue)) { - intp = QSIMPLEQ_FIRST(&vdev->pending_intp_queue); - vfio_intp_inject_pending_lockheld(intp); - QSIMPLEQ_REMOVE_HEAD(&vdev->pending_intp_queue, pqnext); - } -} - -/** - * vfio_start_eventfd_injection - starts the virtual IRQ injection using - * user-side handled eventfds - * @sbdev: the sysbus device handle - * @irq: the qemu irq handle - */ - -static void vfio_start_eventfd_injection(SysBusDevice *sbdev, qemu_irq irq) -{ - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); - VFIOINTp *intp; - - QLIST_FOREACH(intp, &vdev->intp_list, next) { - if (intp->qemuirq == irq) { - break; - } - } - assert(intp); - - if (vfio_set_trigger_eventfd(intp, vfio_intp_interrupt)) { - abort(); - } -} - -/* - * Functions used for irqfd - */ - -/** - * vfio_set_resample_eventfd - sets the resamplefd for an IRQ - * @intp: the IRQ struct handle - * programs the VFIO driver to unmask this IRQ when the - * intp->unmask eventfd is triggered - */ -static int vfio_set_resample_eventfd(VFIOINTp *intp) -{ - int32_t fd = event_notifier_get_fd(intp->unmask); - VFIODevice *vbasedev = &intp->vdev->vbasedev; - Error *err = NULL; - - qemu_set_fd_handler(fd, NULL, NULL, NULL); - if (!vfio_device_irq_set_signaling(vbasedev, intp->pin, 0, - VFIO_IRQ_SET_ACTION_UNMASK, fd, &err)) { - error_reportf_err(err, VFIO_MSG_PREFIX, vbasedev->name); - return -EINVAL; - } - return 0; -} - -/** - * vfio_start_irqfd_injection - starts the virtual IRQ injection using - * irqfd - * - * @sbdev: the sysbus device handle - * @irq: the qemu irq handle - * - * In case the irqfd setup fails, we fallback to userspace handled eventfd - */ -static void vfio_start_irqfd_injection(SysBusDevice *sbdev, qemu_irq irq) -{ - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); - VFIOINTp *intp; - - if (!kvm_irqfds_enabled() || !kvm_resamplefds_enabled() || - !vdev->irqfd_allowed) { - goto fail_irqfd; - } - - QLIST_FOREACH(intp, &vdev->intp_list, next) { - if (intp->qemuirq == irq) { - break; - } - } - assert(intp); - - if (kvm_irqchip_add_irqfd_notifier(kvm_state, intp->interrupt, - intp->unmask, irq) < 0) { - goto fail_irqfd; - } - - if (vfio_set_trigger_eventfd(intp, NULL) < 0) { - goto fail_vfio; - } - if (vfio_irq_is_automasked(intp)) { - if (vfio_set_resample_eventfd(intp) < 0) { - goto fail_vfio; - } - trace_vfio_platform_start_level_irqfd_injection(intp->pin, - event_notifier_get_fd(intp->interrupt), - event_notifier_get_fd(intp->unmask)); - } else { - trace_vfio_platform_start_edge_irqfd_injection(intp->pin, - event_notifier_get_fd(intp->interrupt)); - } - - intp->kvm_accel = true; - - return; -fail_vfio: - kvm_irqchip_remove_irqfd_notifier(kvm_state, intp->interrupt, irq); - abort(); -fail_irqfd: - vfio_start_eventfd_injection(sbdev, irq); -} - -/* VFIO skeleton */ - -static void vfio_platform_compute_needs_reset(VFIODevice *vbasedev) -{ - vbasedev->needs_reset = true; -} - -/* not implemented yet */ -static int vfio_platform_hot_reset_multi(VFIODevice *vbasedev) -{ - return -1; -} - -/** - * vfio_populate_device - Allocate and populate MMIO region - * and IRQ structs according to driver returned information - * @vbasedev: the VFIO device handle - * @errp: error object - * - */ -static bool vfio_populate_device(VFIODevice *vbasedev, Error **errp) -{ - VFIOINTp *intp, *tmp; - int i, ret = -1; - VFIOPlatformDevice *vdev = - container_of(vbasedev, VFIOPlatformDevice, vbasedev); - - if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PLATFORM)) { - error_setg(errp, "this isn't a platform device"); - return false; - } - - vdev->regions = g_new0(VFIORegion *, vbasedev->num_regions); - - for (i = 0; i < vbasedev->num_regions; i++) { - char *name = g_strdup_printf("VFIO %s region %d\n", vbasedev->name, i); - - vdev->regions[i] = g_new0(VFIORegion, 1); - ret = vfio_region_setup(OBJECT(vdev), vbasedev, - vdev->regions[i], i, name); - g_free(name); - if (ret) { - error_setg_errno(errp, -ret, "failed to get region %d info", i); - goto reg_error; - } - } - - vdev->mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, - vfio_intp_mmap_enable, vdev); - - QSIMPLEQ_INIT(&vdev->pending_intp_queue); - - for (i = 0; i < vbasedev->num_irqs; i++) { - struct vfio_irq_info irq; - - ret = vfio_device_get_irq_info(vbasedev, i, &irq); - - if (ret) { - error_setg_errno(errp, -ret, "failed to get device irq info"); - goto irq_err; - } else { - trace_vfio_platform_populate_interrupts(irq.index, - irq.count, - irq.flags); - intp = vfio_init_intp(vbasedev, irq, errp); - if (!intp) { - goto irq_err; - } - } - } - return true; -irq_err: - timer_del(vdev->mmap_timer); - QLIST_FOREACH_SAFE(intp, &vdev->intp_list, next, tmp) { - QLIST_REMOVE(intp, next); - g_free(intp); - } -reg_error: - for (i = 0; i < vbasedev->num_regions; i++) { - if (vdev->regions[i]) { - vfio_region_finalize(vdev->regions[i]); - } - g_free(vdev->regions[i]); - } - g_free(vdev->regions); - return false; -} - -/* specialized functions for VFIO Platform devices */ -static VFIODeviceOps vfio_platform_ops = { - .vfio_compute_needs_reset = vfio_platform_compute_needs_reset, - .vfio_hot_reset_multi = vfio_platform_hot_reset_multi, - .vfio_eoi = vfio_platform_eoi, -}; - -/** - * vfio_base_device_init - perform preliminary VFIO setup - * @vbasedev: the VFIO device handle - * @errp: error object - * - * Implement the VFIO command sequence that allows to discover - * assigned device resources: group extraction, device - * fd retrieval, resource query. - * Precondition: the device name must be initialized - */ -static bool vfio_base_device_init(VFIODevice *vbasedev, Error **errp) -{ - /* @fd takes precedence over @sysfsdev which takes precedence over @host */ - if (vbasedev->fd < 0 && vbasedev->sysfsdev) { - vfio_device_free_name(vbasedev); - vbasedev->name = g_path_get_basename(vbasedev->sysfsdev); - } else if (vbasedev->fd < 0) { - if (!vbasedev->name || strchr(vbasedev->name, '/')) { - error_setg(errp, "wrong host device name"); - return false; - } - - vbasedev->sysfsdev = g_strdup_printf("/sys/bus/platform/devices/%s", - vbasedev->name); - } - - if (!vfio_device_get_name(vbasedev, errp)) { - return false; - } - - if (!vfio_device_attach(vbasedev->name, vbasedev, - &address_space_memory, errp)) { - return false; - } - - if (vfio_populate_device(vbasedev, errp)) { - return true; - } - - vfio_device_detach(vbasedev); - return false; -} - -/** - * vfio_platform_realize - the device realize function - * @dev: device state pointer - * @errp: error - * - * initialize the device, its memory regions and IRQ structures - * IRQ are started separately - */ -static void vfio_platform_realize(DeviceState *dev, Error **errp) -{ - ERRP_GUARD(); - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev); - SysBusDevice *sbdev = SYS_BUS_DEVICE(dev); - VFIODevice *vbasedev = &vdev->vbasedev; - int i; - - warn_report("-device vfio-platform is deprecated"); - qemu_mutex_init(&vdev->intp_mutex); - - trace_vfio_platform_realize(vbasedev->sysfsdev ? - vbasedev->sysfsdev : vbasedev->name, - vdev->compat); - - if (!vfio_base_device_init(vbasedev, errp)) { - goto init_err; - } - - if (!vdev->compat) { - GError *gerr = NULL; - gchar *contents; - gsize length; - char *path; - - path = g_strdup_printf("%s/of_node/compatible", vbasedev->sysfsdev); - if (!g_file_get_contents(path, &contents, &length, &gerr)) { - error_setg(errp, "%s", gerr->message); - g_error_free(gerr); - g_free(path); - return; - } - g_free(path); - vdev->compat = contents; - for (vdev->num_compat = 0; length; vdev->num_compat++) { - size_t skip = strlen(contents) + 1; - contents += skip; - length -= skip; - } - } - - for (i = 0; i < vbasedev->num_regions; i++) { - if (vfio_region_mmap(vdev->regions[i])) { - warn_report("%s mmap unsupported, performance may be slow", - memory_region_name(vdev->regions[i]->mem)); - } - sysbus_init_mmio(sbdev, vdev->regions[i]->mem); - } - return; - -init_err: - if (vdev->vbasedev.name) { - error_prepend(errp, VFIO_MSG_PREFIX, vdev->vbasedev.name); - } else { - error_prepend(errp, "vfio error: "); - } -} - -static const VMStateDescription vfio_platform_vmstate = { - .name = "vfio-platform", - .unmigratable = 1, -}; - -static const Property vfio_platform_dev_properties[] = { - DEFINE_PROP_STRING("host", VFIOPlatformDevice, vbasedev.name), - DEFINE_PROP_STRING("sysfsdev", VFIOPlatformDevice, vbasedev.sysfsdev), - DEFINE_PROP_BOOL("x-no-mmap", VFIOPlatformDevice, vbasedev.no_mmap, false), - DEFINE_PROP_UINT32("mmap-timeout-ms", VFIOPlatformDevice, - mmap_timeout, 1100), - DEFINE_PROP_BOOL("x-irqfd", VFIOPlatformDevice, irqfd_allowed, true), -#ifdef CONFIG_IOMMUFD - DEFINE_PROP_LINK("iommufd", VFIOPlatformDevice, vbasedev.iommufd, - TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *), -#endif -}; - -static void vfio_platform_instance_init(Object *obj) -{ - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(obj); - VFIODevice *vbasedev = &vdev->vbasedev; - - vfio_device_init(vbasedev, VFIO_DEVICE_TYPE_PLATFORM, &vfio_platform_ops, - DEVICE(vdev), false); -} - -#ifdef CONFIG_IOMMUFD -static void vfio_platform_set_fd(Object *obj, const char *str, Error **errp) -{ - vfio_device_set_fd(&VFIO_PLATFORM_DEVICE(obj)->vbasedev, str, errp); -} -#endif - -static void vfio_platform_class_init(ObjectClass *klass, const void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); - - dc->realize = vfio_platform_realize; - device_class_set_props(dc, vfio_platform_dev_properties); -#ifdef CONFIG_IOMMUFD - object_class_property_add_str(klass, "fd", NULL, vfio_platform_set_fd); -#endif - dc->vmsd = &vfio_platform_vmstate; - dc->desc = "VFIO-based platform device assignment"; - sbc->connect_irq_notifier = vfio_start_irqfd_injection; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - - object_class_property_set_description(klass, /* 2.4 */ - "host", - "Host device name of assigned device"); - object_class_property_set_description(klass, /* 2.4 and 2.5 */ - "x-no-mmap", - "Disable MMAP for device. Allows to trace MMIO " - "accesses (DEBUG)"); - object_class_property_set_description(klass, /* 2.4 */ - "mmap-timeout-ms", - "When EOI is not provided by KVM/QEMU, wait time " - "(milliseconds) to re-enable device direct access " - "after level interrupt (DEBUG)"); - object_class_property_set_description(klass, /* 2.4 */ - "x-irqfd", - "Allow disabling irqfd support (DEBUG)"); - object_class_property_set_description(klass, /* 2.6 */ - "sysfsdev", - "Host sysfs path of assigned device"); -#ifdef CONFIG_IOMMUFD - object_class_property_set_description(klass, /* 9.0 */ - "iommufd", - "Set host IOMMUFD backend device"); -#endif -} - -static const TypeInfo vfio_platform_dev_info = { - .name = TYPE_VFIO_PLATFORM, - .parent = TYPE_DYNAMIC_SYS_BUS_DEVICE, - .instance_size = sizeof(VFIOPlatformDevice), - .instance_init = vfio_platform_instance_init, - .class_init = vfio_platform_class_init, - .class_size = sizeof(VFIOPlatformDeviceClass), -}; - -static void register_vfio_platform_dev_type(void) -{ - type_register_static(&vfio_platform_dev_info); -} - -type_init(register_vfio_platform_dev_type) diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index fc6ed230d0c89..e3d571f8c845d 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -127,17 +127,6 @@ vfio_region_unmap(const char *name, unsigned long offset, unsigned long end) "Re vfio_region_sparse_mmap_header(const char *name, int index, int nr_areas) "Device %s region %d: %d sparse mmap entries" vfio_region_sparse_mmap_entry(int i, unsigned long start, unsigned long end) "sparse entry %d [0x%lx - 0x%lx]" -# platform.c -vfio_platform_realize(char *name, char *compat) "vfio device %s, compat = %s" -vfio_platform_eoi(int pin, int fd) "EOI IRQ pin %d (fd=%d)" -vfio_platform_intp_mmap_enable(int pin) "IRQ #%d still active, stay in slow path" -vfio_platform_intp_interrupt(int pin, int fd) "Inject IRQ #%d (fd = %d)" -vfio_platform_intp_inject_pending_lockheld(int pin, int fd) "Inject pending IRQ #%d (fd = %d)" -vfio_platform_populate_interrupts(int pin, int count, int flags) "- IRQ index %d: count %d, flags=0x%x" -vfio_intp_interrupt_set_pending(int index) "irq %d is set PENDING" -vfio_platform_start_level_irqfd_injection(int index, int fd, int resamplefd) "IRQ index=%d, fd = %d, resamplefd = %d" -vfio_platform_start_edge_irqfd_injection(int index, int fd) "IRQ index=%d, fd = %d" - # spapr.c vfio_prereg_listener_region_add_skip(uint64_t start, uint64_t end) "0x%"PRIx64" - 0x%"PRIx64 vfio_prereg_listener_region_del_skip(uint64_t start, uint64_t end) "0x%"PRIx64" - 0x%"PRIx64 diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index 6e4d5ccdac6ea..e7e6243e2dd7c 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -36,7 +36,7 @@ enum { VFIO_DEVICE_TYPE_PCI = 0, - VFIO_DEVICE_TYPE_PLATFORM = 1, + VFIO_DEVICE_TYPE_UNUSED = 1, VFIO_DEVICE_TYPE_CCW = 2, VFIO_DEVICE_TYPE_AP = 3, }; diff --git a/include/hw/vfio/vfio-platform.h b/include/hw/vfio/vfio-platform.h deleted file mode 100644 index 256d8500b70a2..0000000000000 --- a/include/hw/vfio/vfio-platform.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * vfio based device assignment support - platform devices - * - * Copyright Linaro Limited, 2014 - * - * Authors: - * Kim Phillips - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Based on vfio based PCI device assignment support: - * Copyright Red Hat, Inc. 2012 - */ - -#ifndef HW_VFIO_VFIO_PLATFORM_H -#define HW_VFIO_VFIO_PLATFORM_H - -#include "hw/sysbus.h" -#include "hw/vfio/vfio-device.h" -#include "qemu/event_notifier.h" -#include "qemu/queue.h" -#include "qom/object.h" - -#define TYPE_VFIO_PLATFORM "vfio-platform" - -enum { - VFIO_IRQ_INACTIVE = 0, - VFIO_IRQ_PENDING = 1, - VFIO_IRQ_ACTIVE = 2, - /* VFIO_IRQ_ACTIVE_AND_PENDING cannot happen with VFIO */ -}; - -typedef struct VFIOINTp { - QLIST_ENTRY(VFIOINTp) next; /* entry for IRQ list */ - QSIMPLEQ_ENTRY(VFIOINTp) pqnext; /* entry for pending IRQ queue */ - EventNotifier *interrupt; /* eventfd triggered on interrupt */ - EventNotifier *unmask; /* eventfd for unmask on QEMU bypass */ - qemu_irq qemuirq; - struct VFIOPlatformDevice *vdev; /* back pointer to device */ - int state; /* inactive, pending, active */ - uint8_t pin; /* index */ - uint32_t flags; /* IRQ info flags */ - bool kvm_accel; /* set when QEMU bypass through KVM enabled */ -} VFIOINTp; - -/* function type for user side eventfd handler */ -typedef void (*eventfd_user_side_handler_t)(VFIOINTp *intp); - -typedef struct VFIORegion VFIORegion; - -struct VFIOPlatformDevice { - SysBusDevice sbdev; - VFIODevice vbasedev; /* not a QOM object */ - VFIORegion **regions; - QLIST_HEAD(, VFIOINTp) intp_list; /* list of IRQs */ - /* queue of pending IRQs */ - QSIMPLEQ_HEAD(, VFIOINTp) pending_intp_queue; - char *compat; /* DT compatible values, separated by NUL */ - unsigned int num_compat; /* number of compatible values */ - uint32_t mmap_timeout; /* delay to re-enable mmaps after interrupt */ - QEMUTimer *mmap_timer; /* allows fast-path resume after IRQ hit */ - QemuMutex intp_mutex; /* protect the intp_list IRQ state */ - bool irqfd_allowed; /* debug option to force irqfd on/off */ -}; -typedef struct VFIOPlatformDevice VFIOPlatformDevice; - -struct VFIOPlatformDeviceClass { - /*< private >*/ - SysBusDeviceClass parent_class; - /*< public >*/ -}; -typedef struct VFIOPlatformDeviceClass VFIOPlatformDeviceClass; - -DECLARE_OBJ_CHECKERS(VFIOPlatformDevice, VFIOPlatformDeviceClass, - VFIO_PLATFORM_DEVICE, TYPE_VFIO_PLATFORM) - -#endif /* HW_VFIO_VFIO_PLATFORM_H */ From e7a47f717718441b546090fe3fa91e2705ca125b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Sep 2025 08:46:31 +0200 Subject: [PATCH 0589/1794] vfio: Move vfio-region.h under hw/vfio/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the removal of vfio-platform, header file vfio-region.h no longer needs to be a public VFIO interface. Move it under hw/vfio. Reviewed-by: Eric Auger Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250901064631.530723-9-clg@redhat.com Signed-off-by: Cédric Le Goater --- {include/hw => hw}/vfio/vfio-region.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {include/hw => hw}/vfio/vfio-region.h (100%) diff --git a/include/hw/vfio/vfio-region.h b/hw/vfio/vfio-region.h similarity index 100% rename from include/hw/vfio/vfio-region.h rename to hw/vfio/vfio-region.h From 36cd81dc139f899127d868ba9baaf3079c336efc Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 13 Aug 2025 07:17:47 -0700 Subject: [PATCH 0590/1794] vfio/container: set error on cpr failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set an error message if vfio_cpr_ram_discard_register_listener fails so the fail label gets a valid error object. Reported-by: Cédric Le Goater Fixes: eba1f657cbb1 ("vfio/container: recover from unmap-all-vaddr failure") Signed-off-by: Steve Sistare Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1755094667-281419-1-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/listener.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index f498e23a93747..5ebafaa07ea15 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -574,6 +574,9 @@ void vfio_container_region_add(VFIOContainerBase *bcontainer, vfio_ram_discard_register_listener(bcontainer, section); } else if (!vfio_cpr_ram_discard_register_listener(bcontainer, section)) { + error_setg(&err, + "vfio_cpr_ram_discard_register_listener for %s failed", + memory_region_name(section->mr)); goto fail; } return; From ceb59c1cc61dee57c8806571e7c723e555914547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 14 Aug 2025 17:34:19 +0200 Subject: [PATCH 0591/1794] vfio: Report an error when the 'dma_max_mappings' limit is reached MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VFIO IOMMU Type1 kernel driver enforces a default IOMMU mapping limit of 65535, which is configurable via the 'dma_max_mappings' module parameter. When this limit is reached, QEMU issues a warning and fails the mapping operation, but allows the VM to continue running, potentially causing issues later. This scenario occurs with SEV-SNP guests, which must update all IOMMU mappings during initialization. To address this, update vfio_ram_discard_register_listener() to accept an 'Error **' parameter and propagate the error to the caller. This change will halt the VM immediately, at init time, with the same error message. Additionally, the same behavior will be enforced at runtime. While this might be considered too brutal, the rarity of this case and the planned removal of the dma_max_mappings module parameter make it a reasonable approach. Cc: Alex Williamson Reviewed-by: Yi Liu Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250814153419.1643897-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/listener.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index 5ebafaa07ea15..c244be5e218ae 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -250,8 +250,9 @@ static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl, return 0; } -static void vfio_ram_discard_register_listener(VFIOContainerBase *bcontainer, - MemoryRegionSection *section) +static bool vfio_ram_discard_register_listener(VFIOContainerBase *bcontainer, + MemoryRegionSection *section, + Error **errp) { RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); int target_page_size = qemu_target_page_size(); @@ -316,13 +317,15 @@ static void vfio_ram_discard_register_listener(VFIOContainerBase *bcontainer, if (vrdl_mappings + max_memslots - vrdl_count > bcontainer->dma_max_mappings) { - warn_report("%s: possibly running out of DMA mappings. E.g., try" + error_setg(errp, "%s: possibly running out of DMA mappings. E.g., try" " increasing the 'block-size' of virtio-mem devies." " Maximum possible DMA mappings: %d, Maximum possible" " memslots: %d", __func__, bcontainer->dma_max_mappings, max_memslots); + return false; } } + return true; } static void vfio_ram_discard_unregister_listener(VFIOContainerBase *bcontainer, @@ -571,7 +574,9 @@ void vfio_container_region_add(VFIOContainerBase *bcontainer, */ if (memory_region_has_ram_discard_manager(section->mr)) { if (!cpr_remap) { - vfio_ram_discard_register_listener(bcontainer, section); + if (!vfio_ram_discard_register_listener(bcontainer, section, &err)) { + goto fail; + } } else if (!vfio_cpr_ram_discard_register_listener(bcontainer, section)) { error_setg(&err, From 1b50621881241ac5bc75ae7f8aa4c278ada8a668 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 27 Aug 2025 20:08:10 +0100 Subject: [PATCH 0592/1794] hw/vfio-user: add x-pci-class-code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This new option was not added to vfio_user_pci_dev_properties, which caused an incorrect class code for vfio-user devices. Fixes: a59d06305fff ("vfio/pci: Introduce x-pci-class-code option") Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250827190810.1645340-1-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index be71c777291f0..dfaa89498dfd0 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -406,6 +406,8 @@ static const Property vfio_user_pci_dev_properties[] = { sub_vendor_id, PCI_ANY_ID), DEFINE_PROP_UINT32("x-pci-sub-device-id", VFIOPCIDevice, sub_device_id, PCI_ANY_ID), + DEFINE_PROP_UINT32("x-pci-class-code", VFIOPCIDevice, + class_code, PCI_ANY_ID), DEFINE_PROP_BOOL("x-send-queued", VFIOUserPCIDevice, send_queued, false), DEFINE_PROP_UINT32("x-msg-timeout", VFIOUserPCIDevice, wait_time, 5000), DEFINE_PROP_BOOL("x-no-posted-writes", VFIOUserPCIDevice, no_post, false), From bb1a6f1f43374a1850c314c4d0e945667d013d07 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Fri, 22 Aug 2025 02:40:42 -0400 Subject: [PATCH 0593/1794] vfio: Introduce helper vfio_pci_from_vfio_device() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce helper vfio_pci_from_vfio_device() to transform from VFIODevice to VFIOPCIDevice, also to hide low level VFIO_DEVICE_TYPE_PCI type check. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250822064101.123526-5-zhenzhong.duan@intel.com [ clg: Added documentation ] Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 4 ++-- hw/vfio/device.c | 2 +- hw/vfio/iommufd.c | 4 ++-- hw/vfio/listener.c | 4 ++-- hw/vfio/pci.c | 9 +++++++++ hw/vfio/pci.h | 12 ++++++++++++ 6 files changed, 28 insertions(+), 7 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 3e13feaa74c30..134ddccc52449 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -1087,7 +1087,7 @@ static int vfio_legacy_pci_hot_reset(VFIODevice *vbasedev, bool single) /* Prep dependent devices for reset and clear our marker. */ QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { if (!vbasedev_iter->dev->realized || - vbasedev_iter->type != VFIO_DEVICE_TYPE_PCI) { + !vfio_pci_from_vfio_device(vbasedev_iter)) { continue; } tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev); @@ -1172,7 +1172,7 @@ static int vfio_legacy_pci_hot_reset(VFIODevice *vbasedev, bool single) QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { if (!vbasedev_iter->dev->realized || - vbasedev_iter->type != VFIO_DEVICE_TYPE_PCI) { + !vfio_pci_from_vfio_device(vbasedev_iter)) { continue; } tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev); diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 52a1996dc4e12..08f12ac31f44f 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -129,7 +129,7 @@ static inline const char *action_to_str(int action) static const char *index_to_str(VFIODevice *vbasedev, int index) { - if (vbasedev->type != VFIO_DEVICE_TYPE_PCI) { + if (!vfio_pci_from_vfio_device(vbasedev)) { return NULL; } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 48c590b6a9467..8c27222f754a2 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -737,8 +737,8 @@ iommufd_cdev_dep_get_realized_vpdev(struct vfio_pci_dependent_device *dep_dev, } vbasedev_tmp = iommufd_cdev_pci_find_by_devid(dep_dev->devid); - if (!vbasedev_tmp || !vbasedev_tmp->dev->realized || - vbasedev_tmp->type != VFIO_DEVICE_TYPE_PCI) { + if (!vfio_pci_from_vfio_device(vbasedev_tmp) || + !vbasedev_tmp->dev->realized) { return NULL; } diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index c244be5e218ae..e093833165983 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -453,7 +453,7 @@ static void vfio_device_error_append(VFIODevice *vbasedev, Error **errp) * MMIO region mapping failures are not fatal but in this case PCI * peer-to-peer transactions are broken. */ - if (vbasedev && vbasedev->type == VFIO_DEVICE_TYPE_PCI) { + if (vfio_pci_from_vfio_device(vbasedev)) { error_append_hint(errp, "%s: PCI peer-to-peer transactions " "on BARs are not supported.\n", vbasedev->name); } @@ -759,7 +759,7 @@ static bool vfio_section_is_vfio_pci(MemoryRegionSection *section, owner = memory_region_owner(section->mr); QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { - if (vbasedev->type != VFIO_DEVICE_TYPE_PCI) { + if (!vfio_pci_from_vfio_device(vbasedev)) { continue; } pcidev = container_of(vbasedev, VFIOPCIDevice, vbasedev); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 07257d0fa049b..3fe5b03eb1160 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2833,6 +2833,15 @@ static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f) return ret; } +/* Transform from VFIODevice to VFIOPCIDevice. Return NULL if fails. */ +VFIOPCIDevice *vfio_pci_from_vfio_device(VFIODevice *vbasedev) +{ + if (vbasedev && vbasedev->type == VFIO_DEVICE_TYPE_PCI) { + return container_of(vbasedev, VFIOPCIDevice, vbasedev); + } + return NULL; +} + void vfio_sub_page_bar_update_mappings(VFIOPCIDevice *vdev) { PCIDevice *pdev = &vdev->pdev; diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 923cf9c2f79d0..96144b6fdeb94 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -226,6 +226,18 @@ void vfio_pci_write_config(PCIDevice *pdev, uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size); void vfio_vga_write(void *opaque, hwaddr addr, uint64_t data, unsigned size); +/** + * vfio_pci_from_vfio_device: Transform from VFIODevice to + * VFIOPCIDevice + * + * This function checks if the given @vbasedev is a VFIO PCI device. + * If it is, it returns the containing VFIOPCIDevice. + * + * @vbasedev: The VFIODevice to transform + * + * Return: The VFIOPCIDevice on success, NULL on failure. + */ +VFIOPCIDevice *vfio_pci_from_vfio_device(VFIODevice *vbasedev); void vfio_sub_page_bar_update_mappings(VFIOPCIDevice *vdev); bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev); bool vfio_config_quirk_setup(VFIOPCIDevice *vdev, Error **errp); From 42875d256d204e69b608f2bd265f85fae32dd4bd Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:41 +0100 Subject: [PATCH 0594/1794] vfio/vfio-container-base.h: update VFIOContainerBase declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the VFIOContainerBase declaration to match our current coding guidelines: remove the explicit typedef (this is already handled by the OBJECT_DECLARE_TYPE() macro), add a blank line after the parent object, rename parent to parent_obj, and move the macro declaration next to the VFIOContainerBase struct declaration. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-2-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- include/hw/vfio/vfio-container-base.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index bded6e993ffd3..acbd48a18a3a3 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -33,8 +33,9 @@ typedef struct VFIOAddressSpace { /* * This is the base object for vfio container backends */ -typedef struct VFIOContainerBase { - Object parent; +struct VFIOContainerBase { + Object parent_obj; + VFIOAddressSpace *space; MemoryListener listener; Error *error; @@ -51,7 +52,10 @@ typedef struct VFIOContainerBase { QLIST_HEAD(, VFIODevice) device_list; GList *iova_ranges; NotifierWithReturn cpr_reboot_notifier; -} VFIOContainerBase; +}; + +#define TYPE_VFIO_IOMMU "vfio-iommu" +OBJECT_DECLARE_TYPE(VFIOContainerBase, VFIOIOMMUClass, VFIO_IOMMU) typedef struct VFIOGuestIOMMU { VFIOContainerBase *bcontainer; @@ -105,14 +109,11 @@ vfio_container_get_page_size_mask(const VFIOContainerBase *bcontainer) return bcontainer->pgsizes; } -#define TYPE_VFIO_IOMMU "vfio-iommu" #define TYPE_VFIO_IOMMU_LEGACY TYPE_VFIO_IOMMU "-legacy" #define TYPE_VFIO_IOMMU_SPAPR TYPE_VFIO_IOMMU "-spapr" #define TYPE_VFIO_IOMMU_IOMMUFD TYPE_VFIO_IOMMU "-iommufd" #define TYPE_VFIO_IOMMU_USER TYPE_VFIO_IOMMU "-user" -OBJECT_DECLARE_TYPE(VFIOContainerBase, VFIOIOMMUClass, VFIO_IOMMU) - struct VFIOIOMMUClass { ObjectClass parent_class; From 98c12de5aeb1cb464a9cde13cd7a53dd6520d3aa Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:42 +0100 Subject: [PATCH 0595/1794] vfio/vfio-container.h: update VFIOContainer declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the VFIOContainer declaration so that it is closer to our coding guidelines: emove the explicit typedef (this is already handled by the OBJECT_DECLARE_TYPE() macro) and add a blank line after the parent object. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-3-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- include/hw/vfio/vfio-container.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/hw/vfio/vfio-container.h b/include/hw/vfio/vfio-container.h index 21e5807e48e1b..50c91788d5b95 100644 --- a/include/hw/vfio/vfio-container.h +++ b/include/hw/vfio/vfio-container.h @@ -25,13 +25,14 @@ typedef struct VFIOGroup { bool ram_block_discard_allowed; } VFIOGroup; -typedef struct VFIOContainer { +struct VFIOContainer { VFIOContainerBase bcontainer; + int fd; /* /dev/vfio/vfio, empowered by the attached groups */ unsigned iommu_type; QLIST_HEAD(, VFIOGroup) group_list; VFIOContainerCPR cpr; -} VFIOContainer; +}; OBJECT_DECLARE_SIMPLE_TYPE(VFIOContainer, VFIO_IOMMU_LEGACY); From 5255ba39b16539bd4b28a961d196812af1684c02 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:43 +0100 Subject: [PATCH 0596/1794] hw/vfio/cpr-legacy.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOContainer and VFIOContainerBase instead of accessing bcontainer directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: Steve Sistare Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-4-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr-legacy.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index 553b203e9b60b..8f437194fae03 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -41,8 +41,8 @@ static int vfio_legacy_cpr_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mr) { - const VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + const VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); + struct vfio_iommu_type1_dma_map map = { .argsz = sizeof(map), .flags = VFIO_DMA_MAP_FLAG_VADDR, @@ -65,7 +65,7 @@ static void vfio_region_remap(MemoryListener *listener, { VFIOContainer *container = container_of(listener, VFIOContainer, cpr.remap_listener); - vfio_container_region_add(&container->bcontainer, section, true); + vfio_container_region_add(VFIO_IOMMU(container), section, true); } static bool vfio_cpr_supported(VFIOContainer *container, Error **errp) @@ -98,7 +98,7 @@ static int vfio_container_pre_save(void *opaque) static int vfio_container_post_load(void *opaque, int version_id) { VFIOContainer *container = opaque; - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); dma_map_fn saved_dma_map = vioc->dma_map; Error *local_err = NULL; @@ -135,7 +135,7 @@ static int vfio_cpr_fail_notifier(NotifierWithReturn *notifier, { VFIOContainer *container = container_of(notifier, VFIOContainer, cpr.transfer_notifier); - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); if (e->type != MIG_EVENT_PRECOPY_FAILED) { return 0; @@ -167,7 +167,7 @@ static int vfio_cpr_fail_notifier(NotifierWithReturn *notifier, bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp) { - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); Error **cpr_blocker = &container->cpr.blocker; migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier, @@ -191,7 +191,7 @@ bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp) void vfio_legacy_cpr_unregister_container(VFIOContainer *container) { - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); migration_remove_notifier(&bcontainer->cpr_reboot_notifier); migrate_del_blocker(&container->cpr.blocker); From 5947f69b63639b54f02db1727c559f8aae32d849 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:44 +0100 Subject: [PATCH 0597/1794] hw/vfio/container.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOContainer and VFIOContainerBase instead of accessing bcontainer directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-5-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 134ddccc52449..030c6d3f89cf5 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -71,7 +71,7 @@ static int vfio_dma_unmap_bitmap(const VFIOContainer *container, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb) { - const VFIOContainerBase *bcontainer = &container->bcontainer; + const VFIOContainerBase *bcontainer = VFIO_IOMMU(container); struct vfio_iommu_type1_dma_unmap *unmap; struct vfio_bitmap *bitmap; VFIOBitmap vbmap; @@ -124,8 +124,7 @@ static int vfio_legacy_dma_unmap_one(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb) { - const VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + const VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); struct vfio_iommu_type1_dma_unmap unmap = { .argsz = sizeof(unmap), .flags = 0, @@ -213,8 +212,7 @@ static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mr) { - const VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + const VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); struct vfio_iommu_type1_dma_map map = { .argsz = sizeof(map), .flags = VFIO_DMA_MAP_FLAG_READ, @@ -246,8 +244,7 @@ static int vfio_legacy_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, bool start, Error **errp) { - const VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + const VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); int ret; struct vfio_iommu_type1_dirty_bitmap dirty = { .argsz = sizeof(dirty), @@ -272,8 +269,7 @@ vfio_legacy_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, static int vfio_legacy_query_dirty_bitmap(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) { - const VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + const VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); struct vfio_iommu_type1_dirty_bitmap *dbitmap; struct vfio_iommu_type1_dirty_bitmap_get *range; int ret; @@ -495,7 +491,7 @@ static void vfio_get_iommu_info_migration(VFIOContainer *container, { struct vfio_info_cap_header *hdr; struct vfio_iommu_type1_info_cap_migration *cap_mig; - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); hdr = vfio_get_iommu_info_cap(info, VFIO_IOMMU_TYPE1_INFO_CAP_MIGRATION); if (!hdr) { @@ -518,8 +514,7 @@ static void vfio_get_iommu_info_migration(VFIOContainer *container, static bool vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp) { - VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); g_autofree struct vfio_iommu_type1_info *info = NULL; int ret; @@ -634,7 +629,7 @@ static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, if (!cpr_is_incoming()) { QLIST_FOREACH(bcontainer, &space->containers, next) { - container = container_of(bcontainer, VFIOContainer, bcontainer); + container = VFIO_IOMMU_LEGACY(bcontainer); if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) { return vfio_container_group_add(container, group, errp); } @@ -652,7 +647,7 @@ static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, * create the container struct and group list. */ QLIST_FOREACH(bcontainer, &space->containers, next) { - container = container_of(bcontainer, VFIOContainer, bcontainer); + container = VFIO_IOMMU_LEGACY(bcontainer); if (vfio_cpr_container_match(container, group, fd)) { return vfio_container_group_add(container, group, errp); @@ -672,7 +667,7 @@ static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, goto fail; } new_container = true; - bcontainer = &container->bcontainer; + bcontainer = VFIO_IOMMU(container); if (!vfio_legacy_cpr_register_container(container, errp)) { goto fail; @@ -735,7 +730,7 @@ static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, static void vfio_container_disconnect(VFIOGroup *group) { VFIOContainer *container = group->container; - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); QLIST_REMOVE(group, container_next); @@ -781,7 +776,7 @@ static VFIOGroup *vfio_group_get(int groupid, AddressSpace *as, Error **errp) QLIST_FOREACH(group, &vfio_group_list, next) { if (group->groupid == groupid) { /* Found it. Now is it already in the right context? */ - if (group->container->bcontainer.space->as == as) { + if (VFIO_IOMMU(group->container)->space->as == as) { return group; } else { error_setg(errp, "group %d used in multiple address spaces", @@ -895,7 +890,7 @@ static bool vfio_device_get(VFIOGroup *group, const char *name, } } - vfio_device_prepare(vbasedev, &group->container->bcontainer, info); + vfio_device_prepare(vbasedev, VFIO_IOMMU(group->container), info); vbasedev->fd = fd; vbasedev->group = group; From 1f778031ec64cab0e79ab22f1313de59b81de3e5 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:45 +0100 Subject: [PATCH 0598/1794] ppc/spapr_pci_vfio.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a QOM cast to convert to VFIOContainer instead of accessing bcontainer directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-6-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/ppc/spapr_pci_vfio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c index e318d0d912f3e..7e1c71ef59d5c 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -106,7 +106,7 @@ static VFIOContainer *vfio_eeh_as_container(AddressSpace *as) out: vfio_address_space_put(space); - return container_of(bcontainer, VFIOContainer, bcontainer); + return VFIO_IOMMU_LEGACY(bcontainer); } static bool vfio_eeh_as_ok(AddressSpace *as) From 1ea79b4b9a1477dca3ff53358fc9ebdd73d55938 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:46 +0100 Subject: [PATCH 0599/1794] vfio/spapr.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOContainer and VFIOContainerBase instead of accessing bcontainer directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-7-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/spapr.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 564b70ef97fb8..c41e4588d6f2f 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -62,7 +62,7 @@ static void vfio_prereg_listener_region_add(MemoryListener *listener, VFIOSpaprContainer *scontainer = container_of(listener, VFIOSpaprContainer, prereg_listener); VFIOContainer *container = &scontainer->container; - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); const hwaddr gpa = section->offset_within_address_space; hwaddr end; int ret; @@ -244,7 +244,7 @@ static bool vfio_spapr_create_window(VFIOContainer *container, hwaddr *pgsize, Error **errp) { int ret = 0; - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, container); IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); @@ -352,8 +352,7 @@ vfio_spapr_container_add_section_window(VFIOContainerBase *bcontainer, MemoryRegionSection *section, Error **errp) { - VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, container); VFIOHostDMAWindow *hostwin; @@ -443,8 +442,7 @@ static void vfio_spapr_container_del_section_window(VFIOContainerBase *bcontainer, MemoryRegionSection *section) { - VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, container); @@ -465,8 +463,7 @@ vfio_spapr_container_del_section_window(VFIOContainerBase *bcontainer, static void vfio_spapr_container_release(VFIOContainerBase *bcontainer) { - VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, container); VFIOHostDMAWindow *hostwin, *next; @@ -484,8 +481,7 @@ static void vfio_spapr_container_release(VFIOContainerBase *bcontainer) static bool vfio_spapr_container_setup(VFIOContainerBase *bcontainer, Error **errp) { - VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, container); struct vfio_iommu_spapr_tce_info info; From 507a118e9f8cc328c55adc531336e075fa8ee2d7 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:47 +0100 Subject: [PATCH 0600/1794] vfio/vfio-container.h: rename VFIOContainer bcontainer field to parent_obj MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that nothing accesses the bcontainer field directly, rename bcontainer to parent_obj as per our current coding guidelines. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-8-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- include/hw/vfio/vfio-container.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/vfio/vfio-container.h b/include/hw/vfio/vfio-container.h index 50c91788d5b95..240f56699336f 100644 --- a/include/hw/vfio/vfio-container.h +++ b/include/hw/vfio/vfio-container.h @@ -26,7 +26,7 @@ typedef struct VFIOGroup { } VFIOGroup; struct VFIOContainer { - VFIOContainerBase bcontainer; + VFIOContainerBase parent_obj; int fd; /* /dev/vfio/vfio, empowered by the attached groups */ unsigned iommu_type; From 52a1cc3dc00f09779aeb9aa9c6fcdc0284a40d4c Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:48 +0100 Subject: [PATCH 0601/1794] vfio-user/container.h: update VFIOUserContainer declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the VFIOUserContainer declaration so that it is closer to our coding guidelines: remove the explicit typedef (this is already handled by the OBJECT_DECLARE_TYPE() macro) and add a blank line after the parent object. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: John Levon Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-9-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/container.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/vfio-user/container.h b/hw/vfio-user/container.h index 2bb1fa13431c2..d5d2275af7329 100644 --- a/hw/vfio-user/container.h +++ b/hw/vfio-user/container.h @@ -13,10 +13,11 @@ #include "hw/vfio-user/proxy.h" /* MMU container sub-class for vfio-user. */ -typedef struct VFIOUserContainer { +struct VFIOUserContainer { VFIOContainerBase bcontainer; + VFIOUserProxy *proxy; -} VFIOUserContainer; +}; OBJECT_DECLARE_SIMPLE_TYPE(VFIOUserContainer, VFIO_IOMMU_USER); From 06229592fa7e6173b979158e9759f0d40a183861 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:49 +0100 Subject: [PATCH 0602/1794] vfio/container.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOUserContainer and VFIOContainerBase instead of accessing bcontainer directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-10-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/container.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/hw/vfio-user/container.c b/hw/vfio-user/container.c index d589dd90f501b..3cdbd44c1aac2 100644 --- a/hw/vfio-user/container.c +++ b/hw/vfio-user/container.c @@ -24,16 +24,14 @@ */ static void vfio_user_listener_begin(VFIOContainerBase *bcontainer) { - VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, - bcontainer); + VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); container->proxy->async_ops = true; } static void vfio_user_listener_commit(VFIOContainerBase *bcontainer) { - VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, - bcontainer); + VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); /* wait here for any async requests sent during the transaction */ container->proxy->async_ops = false; @@ -44,8 +42,8 @@ static int vfio_user_dma_unmap(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb, bool unmap_all) { - VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, - bcontainer); + VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); + Error *local_err = NULL; int ret = 0; @@ -86,8 +84,8 @@ static int vfio_user_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mrp) { - VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, - bcontainer); + VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); + int fd = memory_region_get_fd(mrp); Error *local_err = NULL; int ret = 0; @@ -173,8 +171,7 @@ static int vfio_user_query_dirty_bitmap(const VFIOContainerBase *bcontainer, static bool vfio_user_setup(VFIOContainerBase *bcontainer, Error **errp) { - VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, - bcontainer); + VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); assert(container->proxy->dma_pgsizes != 0); bcontainer->pgsizes = container->proxy->dma_pgsizes; @@ -218,7 +215,7 @@ vfio_user_container_connect(AddressSpace *as, VFIODevice *vbasedev, goto put_space_exit; } - bcontainer = &container->bcontainer; + bcontainer = VFIO_IOMMU(container); ret = ram_block_uncoordinated_discard_disable(true); if (ret) { @@ -263,7 +260,7 @@ vfio_user_container_connect(AddressSpace *as, VFIODevice *vbasedev, static void vfio_user_container_disconnect(VFIOUserContainer *container) { - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); VFIOAddressSpace *space = bcontainer->space; @@ -291,7 +288,7 @@ static bool vfio_user_device_get(VFIOUserContainer *container, vbasedev->fd = -1; - vfio_device_prepare(vbasedev, &container->bcontainer, &info); + vfio_device_prepare(vbasedev, VFIO_IOMMU(container), &info); return true; } @@ -315,8 +312,7 @@ static bool vfio_user_device_attach(const char *name, VFIODevice *vbasedev, static void vfio_user_device_detach(VFIODevice *vbasedev) { - VFIOUserContainer *container = container_of(vbasedev->bcontainer, - VFIOUserContainer, bcontainer); + VFIOUserContainer *container = VFIO_IOMMU_USER(vbasedev->bcontainer); vfio_device_unprepare(vbasedev); From 81b53891ca52288540e6e4f34bb924284feebc58 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:50 +0100 Subject: [PATCH 0603/1794] vfio-user/container.h: rename VFIOUserContainer bcontainer field to parent_obj MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that nothing accesses the bcontainer field directly, rename bcontainer to parent_obj as per our current coding guidelines. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: John Levon Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-11-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/container.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/vfio-user/container.h b/hw/vfio-user/container.h index d5d2275af7329..96aa6785d9b39 100644 --- a/hw/vfio-user/container.h +++ b/hw/vfio-user/container.h @@ -14,7 +14,7 @@ /* MMU container sub-class for vfio-user. */ struct VFIOUserContainer { - VFIOContainerBase bcontainer; + VFIOContainerBase parent_obj; VFIOUserProxy *proxy; }; From b458e9e9e4c171622f19df18f5363c7ef4e8697f Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:51 +0100 Subject: [PATCH 0604/1794] vfio-user/pci.c: update VFIOUserPCIDevice declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the VFIOUserPCIDevice declaration so that it is closer to our coding guidelines: add a blank line after the parent object. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: John Levon Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-12-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index dfaa89498dfd0..29cb592e9cf4d 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -21,6 +21,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(VFIOUserPCIDevice, VFIO_USER_PCI) struct VFIOUserPCIDevice { VFIOPCIDevice device; + SocketAddress *socket; bool send_queued; /* all sends are queued */ uint32_t wait_time; /* timeout for message replies */ From 5d1219e358a559680fdc34b112e2b04806f5ff62 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:52 +0100 Subject: [PATCH 0605/1794] vfio-user/pci.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOUserPCIDevice and VFIOPCIDevice instead of accessing device directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-13-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index 29cb592e9cf4d..7b6a6514f6c22 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -214,8 +214,9 @@ static void vfio_user_compute_needs_reset(VFIODevice *vbasedev) static Object *vfio_user_pci_get_object(VFIODevice *vbasedev) { - VFIOUserPCIDevice *vdev = container_of(vbasedev, VFIOUserPCIDevice, - device.vbasedev); + VFIOUserPCIDevice *vdev = VFIO_USER_PCI(container_of(vbasedev, + VFIOPCIDevice, + vbasedev)); return OBJECT(vdev); } @@ -420,7 +421,7 @@ static void vfio_user_pci_set_socket(Object *obj, Visitor *v, const char *name, VFIOUserPCIDevice *udev = VFIO_USER_PCI(obj); bool success; - if (udev->device.vbasedev.proxy) { + if (VFIO_PCI_BASE(udev)->vbasedev.proxy) { error_setg(errp, "Proxy is connected"); return; } From 5fc421b8cd4813fa8ca9131905f12c4eedf55051 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:53 +0100 Subject: [PATCH 0606/1794] vfio-user/pci.c: rename VFIOUserPCIDevice device field to parent_obj MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that nothing accesses the device field directly, rename device to parent_obj as per our current coding guidelines. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: John Levon Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-14-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index 7b6a6514f6c22..c3947a8f2ec9c 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -20,7 +20,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(VFIOUserPCIDevice, VFIO_USER_PCI) struct VFIOUserPCIDevice { - VFIOPCIDevice device; + VFIOPCIDevice parent_obj; SocketAddress *socket; bool send_queued; /* all sends are queued */ From 750e424fd04311aaeeb85536744c4cd3e460404d Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:54 +0100 Subject: [PATCH 0607/1794] vfio/pci.h: update VFIOPCIDevice declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the VFIOPCIDevice declaration so that it is closer to our coding guidelines: add a blank line after the parent object. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-15-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.h | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 96144b6fdeb94..2db76b6f4fb58 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -124,6 +124,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(VFIOPCIDevice, VFIO_PCI_BASE) struct VFIOPCIDevice { PCIDevice pdev; + VFIODevice vbasedev; VFIOINTx intx; unsigned int config_size; From 77f143cc418121fb30ad9e26dd90334dcf5851fc Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:56 +0100 Subject: [PATCH 0608/1794] vfio/pci.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOPCIDevice and PCIDevice instead of accessing pdev directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-17-mark.caveayland@nutanix.com [ clg: Updated vfio_sub_page_bar_update_mappings() ] Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 204 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 121 insertions(+), 83 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 3fe5b03eb1160..052591d2c970a 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -117,6 +117,7 @@ static void vfio_intx_mmap_enable(void *opaque) static void vfio_intx_interrupt(void *opaque) { VFIOPCIDevice *vdev = opaque; + PCIDevice *pdev = PCI_DEVICE(vdev); if (!event_notifier_test_and_clear(&vdev->intx.interrupt)) { return; @@ -125,7 +126,7 @@ static void vfio_intx_interrupt(void *opaque) trace_vfio_intx_interrupt(vdev->vbasedev.name, 'A' + vdev->intx.pin); vdev->intx.pending = true; - pci_irq_assert(&vdev->pdev); + pci_irq_assert(pdev); vfio_mmap_set_enabled(vdev, false); if (vdev->intx.mmap_timeout) { timer_mod(vdev->intx.mmap_timer, @@ -136,6 +137,7 @@ static void vfio_intx_interrupt(void *opaque) void vfio_pci_intx_eoi(VFIODevice *vbasedev) { VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); + PCIDevice *pdev = PCI_DEVICE(vdev); if (!vdev->intx.pending) { return; @@ -144,13 +146,14 @@ void vfio_pci_intx_eoi(VFIODevice *vbasedev) trace_vfio_pci_intx_eoi(vbasedev->name); vdev->intx.pending = false; - pci_irq_deassert(&vdev->pdev); + pci_irq_deassert(pdev); vfio_device_irq_unmask(vbasedev, VFIO_PCI_INTX_IRQ_INDEX); } static bool vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) { #ifdef CONFIG_KVM + PCIDevice *pdev = PCI_DEVICE(vdev); int irq_fd = event_notifier_get_fd(&vdev->intx.interrupt); if (vdev->no_kvm_intx || !kvm_irqfds_enabled() || @@ -163,7 +166,7 @@ static bool vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) qemu_set_fd_handler(irq_fd, NULL, NULL, vdev); vfio_device_irq_mask(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.pending = false; - pci_irq_deassert(&vdev->pdev); + pci_irq_deassert(pdev); /* Get an eventfd for resample/unmask */ if (!vfio_notifier_init(vdev, &vdev->intx.unmask, "intx-unmask", 0, errp)) { @@ -241,6 +244,8 @@ static bool vfio_cpr_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) static void vfio_intx_disable_kvm(VFIOPCIDevice *vdev) { #ifdef CONFIG_KVM + PCIDevice *pdev = PCI_DEVICE(vdev); + if (!vdev->intx.kvm_accel) { return; } @@ -251,7 +256,7 @@ static void vfio_intx_disable_kvm(VFIOPCIDevice *vdev) */ vfio_device_irq_mask(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.pending = false; - pci_irq_deassert(&vdev->pdev); + pci_irq_deassert(pdev); /* Tell KVM to stop listening for an INTx irqfd */ if (kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, &vdev->intx.interrupt, @@ -307,7 +312,7 @@ static void vfio_intx_routing_notifier(PCIDevice *pdev) return; } - route = pci_device_route_intx_to_irq(&vdev->pdev, vdev->intx.pin); + route = pci_device_route_intx_to_irq(pdev, vdev->intx.pin); if (pci_intx_route_changed(&vdev->intx.route, &route)) { vfio_intx_update(vdev, &route); @@ -324,7 +329,8 @@ static void vfio_irqchip_change(Notifier *notify, void *data) static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) { - uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1); + PCIDevice *pdev = PCI_DEVICE(vdev); + uint8_t pin = vfio_pci_read_config(pdev, PCI_INTERRUPT_PIN, 1); Error *err = NULL; int32_t fd; @@ -342,7 +348,7 @@ static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) } vdev->intx.pin = pin - 1; /* Pin A (1) -> irq[0] */ - pci_config_set_interrupt_pin(vdev->pdev.config, pin); + pci_config_set_interrupt_pin(pdev->config, pin); #ifdef CONFIG_KVM /* @@ -350,7 +356,7 @@ static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) * where we won't actually use the result anyway. */ if (kvm_irqfds_enabled() && kvm_resamplefds_enabled()) { - vdev->intx.route = pci_device_route_intx_to_irq(&vdev->pdev, + vdev->intx.route = pci_device_route_intx_to_irq(pdev, vdev->intx.pin); } #endif @@ -390,13 +396,14 @@ static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) static void vfio_intx_disable(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); int fd; timer_del(vdev->intx.mmap_timer); vfio_intx_disable_kvm(vdev); vfio_device_irq_disable(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.pending = false; - pci_irq_deassert(&vdev->pdev); + pci_irq_deassert(pdev); vfio_mmap_set_enabled(vdev, true); fd = event_notifier_get_fd(&vdev->intx.interrupt); @@ -428,6 +435,7 @@ static void vfio_msi_interrupt(void *opaque) { VFIOMSIVector *vector = opaque; VFIOPCIDevice *vdev = vector->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); MSIMessage (*get_msg)(PCIDevice *dev, unsigned vector); void (*notify)(PCIDevice *dev, unsigned vector); MSIMessage msg; @@ -442,9 +450,9 @@ static void vfio_msi_interrupt(void *opaque) notify = msix_notify; /* A masked vector firing needs to use the PBA, enable it */ - if (msix_is_masked(&vdev->pdev, nr)) { + if (msix_is_masked(pdev, nr)) { set_bit(nr, vdev->msix->pending); - memory_region_set_enabled(&vdev->pdev.msix_pba_mmio, true); + memory_region_set_enabled(&pdev->msix_pba_mmio, true); trace_vfio_msix_pba_enable(vdev->vbasedev.name); } } else if (vdev->interrupt == VFIO_INT_MSI) { @@ -454,9 +462,9 @@ static void vfio_msi_interrupt(void *opaque) abort(); } - msg = get_msg(&vdev->pdev, nr); + msg = get_msg(pdev, nr); trace_vfio_msi_interrupt(vdev->vbasedev.name, nr, msg.address, msg.data); - notify(&vdev->pdev, nr); + notify(pdev, nr); } void vfio_pci_msi_set_handler(VFIOPCIDevice *vdev, int nr, bool enable) @@ -495,6 +503,7 @@ static int vfio_enable_msix_no_vec(VFIOPCIDevice *vdev) static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) { + PCIDevice *pdev = PCI_DEVICE(vdev); struct vfio_irq_set *irq_set; int ret = 0, i, argsz; int32_t *fds; @@ -537,7 +546,7 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) */ if (vdev->msi_vectors[i].use) { if (vdev->msi_vectors[i].virq < 0 || - (msix && msix_is_masked(&vdev->pdev, i))) { + (msix && msix_is_masked(pdev, i))) { fd = event_notifier_get_fd(&vdev->msi_vectors[i].interrupt); } else { fd = event_notifier_get_fd(&vdev->msi_vectors[i].kvm_interrupt); @@ -557,12 +566,14 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) void vfio_pci_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, int vector_n, bool msix) { + PCIDevice *pdev = PCI_DEVICE(vdev); + if ((msix && vdev->no_kvm_msix) || (!msix && vdev->no_kvm_msi)) { return; } vector->virq = kvm_irqchip_add_msi_route(&vfio_route_change, - vector_n, &vdev->pdev); + vector_n, pdev); } static void vfio_connect_kvm_msi_virq(VFIOMSIVector *vector, int nr) @@ -631,7 +642,7 @@ static void set_irq_signalling(VFIODevice *vbasedev, VFIOMSIVector *vector, void vfio_pci_vector_init(VFIOPCIDevice *vdev, int nr) { VFIOMSIVector *vector = &vdev->msi_vectors[nr]; - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); Error *local_err = NULL; vector->vdev = vdev; @@ -720,7 +731,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, clear_bit(nr, vdev->msix->pending); if (find_first_bit(vdev->msix->pending, vdev->nr_vectors) == vdev->nr_vectors) { - memory_region_set_enabled(&vdev->pdev.msix_pba_mmio, false); + memory_region_set_enabled(&pdev->msix_pba_mmio, false); trace_vfio_msix_pba_disable(vdev->vbasedev.name); } @@ -771,7 +782,9 @@ static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) void vfio_pci_msix_set_notifiers(VFIOPCIDevice *vdev) { - msix_set_vector_notifiers(&vdev->pdev, vfio_msix_vector_use, + PCIDevice *pdev = PCI_DEVICE(vdev); + + msix_set_vector_notifiers(pdev, vfio_msix_vector_use, vfio_msix_vector_release, NULL); } @@ -798,6 +811,7 @@ void vfio_pci_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev) static void vfio_msix_enable(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); int ret; vfio_disable_interrupts(vdev); @@ -814,7 +828,7 @@ static void vfio_msix_enable(VFIOPCIDevice *vdev) */ vfio_pci_prepare_kvm_msi_virq_batch(vdev); - if (msix_set_vector_notifiers(&vdev->pdev, vfio_msix_vector_use, + if (msix_set_vector_notifiers(pdev, vfio_msix_vector_use, vfio_msix_vector_release, NULL)) { error_report("vfio: msix_set_vector_notifiers failed"); } @@ -852,11 +866,12 @@ static void vfio_msix_enable(VFIOPCIDevice *vdev) static void vfio_msi_enable(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); int ret, i; vfio_disable_interrupts(vdev); - vdev->nr_vectors = msi_nr_vectors_allocated(&vdev->pdev); + vdev->nr_vectors = msi_nr_vectors_allocated(pdev); retry: /* * Setting vector notifiers needs to enable route for each vector. @@ -949,10 +964,11 @@ static void vfio_msi_disable_common(VFIOPCIDevice *vdev) static void vfio_msix_disable(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); Error *err = NULL; int i; - msix_unset_vector_notifiers(&vdev->pdev); + msix_unset_vector_notifiers(pdev); /* * MSI-X will only release vectors if MSI-X is still enabled on the @@ -960,8 +976,8 @@ static void vfio_msix_disable(VFIOPCIDevice *vdev) */ for (i = 0; i < vdev->nr_vectors; i++) { if (vdev->msi_vectors[i].use) { - vfio_msix_vector_release(&vdev->pdev, i); - msix_vector_unuse(&vdev->pdev, i); + vfio_msix_vector_release(pdev, i); + msix_vector_unuse(pdev, i); } } @@ -998,6 +1014,7 @@ static void vfio_msi_disable(VFIOPCIDevice *vdev) static void vfio_update_msi(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); int i; for (i = 0; i < vdev->nr_vectors; i++) { @@ -1008,8 +1025,8 @@ static void vfio_update_msi(VFIOPCIDevice *vdev) continue; } - msg = msi_get_message(&vdev->pdev, i); - vfio_update_kvm_msi_virq(vector, msg, &vdev->pdev); + msg = msi_get_message(pdev, i); + vfio_update_kvm_msi_virq(vector, msg, pdev); } } @@ -1171,13 +1188,14 @@ static const MemoryRegionOps vfio_rom_ops = { static void vfio_pci_size_rom(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); VFIODevice *vbasedev = &vdev->vbasedev; uint32_t orig, size = cpu_to_le32((uint32_t)PCI_ROM_ADDRESS_MASK); char *name; - if (vdev->pdev.romfile || !vdev->pdev.rom_bar) { + if (pdev->romfile || !pdev->rom_bar) { /* Since pci handles romfile, just print a message and return */ - if (vfio_opt_rom_in_denylist(vdev) && vdev->pdev.romfile) { + if (vfio_opt_rom_in_denylist(vdev) && pdev->romfile) { warn_report("Device at %s is known to cause system instability" " issues during option rom execution", vdev->vbasedev.name); @@ -1206,7 +1224,7 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) } if (vfio_opt_rom_in_denylist(vdev)) { - if (vdev->pdev.rom_bar > 0) { + if (pdev->rom_bar > 0) { warn_report("Device at %s is known to cause system instability" " issues during option rom execution", vdev->vbasedev.name); @@ -1225,12 +1243,12 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) name = g_strdup_printf("vfio[%s].rom", vdev->vbasedev.name); - memory_region_init_io(&vdev->pdev.rom, OBJECT(vdev), + memory_region_init_io(&pdev->rom, OBJECT(vdev), &vfio_rom_ops, vdev, name, size); g_free(name); - pci_register_bar(&vdev->pdev, PCI_ROM_SLOT, - PCI_BASE_ADDRESS_SPACE_MEMORY, &vdev->pdev.rom); + pci_register_bar(pdev, PCI_ROM_SLOT, + PCI_BASE_ADDRESS_SPACE_MEMORY, &pdev->rom); vdev->rom_read_failed = false; } @@ -1503,6 +1521,7 @@ static void vfio_disable_interrupts(VFIOPCIDevice *vdev) static bool vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp) { + PCIDevice *pdev = PCI_DEVICE(vdev); uint16_t ctrl; bool msi_64bit, msi_maskbit; int ret, entries; @@ -1523,7 +1542,7 @@ static bool vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp) trace_vfio_msi_setup(vdev->vbasedev.name, pos); - ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit, &err); + ret = msi_init(pdev, pos, entries, msi_64bit, msi_maskbit, &err); if (ret < 0) { if (ret == -ENOTSUP) { return true; @@ -1716,6 +1735,7 @@ static bool vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) */ static bool vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) { + PCIDevice *pdev = PCI_DEVICE(vdev); uint8_t pos; uint16_t ctrl; uint32_t table, pba; @@ -1723,7 +1743,7 @@ static bool vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) VFIOMSIXInfo *msix; int ret; - pos = pci_find_capability(&vdev->pdev, PCI_CAP_ID_MSIX); + pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); if (!pos) { return true; } @@ -1815,12 +1835,13 @@ static bool vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) static bool vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) { + PCIDevice *pdev = PCI_DEVICE(vdev); int ret; Error *err = NULL; vdev->msix->pending = g_new0(unsigned long, BITS_TO_LONGS(vdev->msix->entries)); - ret = msix_init(&vdev->pdev, vdev->msix->entries, + ret = msix_init(pdev, vdev->msix->entries, vdev->bars[vdev->msix->table_bar].mr, vdev->msix->table_bar, vdev->msix->table_offset, vdev->bars[vdev->msix->pba_bar].mr, @@ -1852,7 +1873,7 @@ static bool vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) * vector-use notifier is called, which occurs on unmask, we test whether * PBA emulation is needed and again disable if not. */ - memory_region_set_enabled(&vdev->pdev.msix_pba_mmio, false); + memory_region_set_enabled(&pdev->msix_pba_mmio, false); /* * The emulated machine may provide a paravirt interface for MSIX setup @@ -1864,7 +1885,7 @@ static bool vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) */ if (object_property_get_bool(OBJECT(qdev_get_machine()), "vfio-no-msix-emulation", NULL)) { - memory_region_set_enabled(&vdev->pdev.msix_table_mmio, false); + memory_region_set_enabled(&pdev->msix_table_mmio, false); } return true; @@ -1872,10 +1893,12 @@ static bool vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) void vfio_pci_teardown_msi(VFIOPCIDevice *vdev) { - msi_uninit(&vdev->pdev); + PCIDevice *pdev = PCI_DEVICE(vdev); + + msi_uninit(pdev); if (vdev->msix) { - msix_uninit(&vdev->pdev, + msix_uninit(pdev, vdev->bars[vdev->msix->table_bar].mr, vdev->bars[vdev->msix->pba_bar].mr); g_free(vdev->msix->pending); @@ -1936,6 +1959,7 @@ static void vfio_bars_prepare(VFIOPCIDevice *vdev) static void vfio_bar_register(VFIOPCIDevice *vdev, int nr) { + PCIDevice *pdev = PCI_DEVICE(vdev); VFIOBAR *bar = &vdev->bars[nr]; char *name; @@ -1957,7 +1981,7 @@ static void vfio_bar_register(VFIOPCIDevice *vdev, int nr) } } - pci_register_bar(&vdev->pdev, nr, bar->type, bar->mr); + pci_register_bar(pdev, nr, bar->type, bar->mr); } static void vfio_bars_register(VFIOPCIDevice *vdev) @@ -1971,6 +1995,7 @@ static void vfio_bars_register(VFIOPCIDevice *vdev) void vfio_pci_bars_exit(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); int i; for (i = 0; i < PCI_ROM_SLOT; i++) { @@ -1984,7 +2009,7 @@ void vfio_pci_bars_exit(VFIOPCIDevice *vdev) } if (vdev->vga) { - pci_unregister_vga(&vdev->pdev); + pci_unregister_vga(pdev); vfio_vga_quirk_exit(vdev); } } @@ -2056,8 +2081,10 @@ static void vfio_set_word_bits(uint8_t *buf, uint16_t val, uint16_t mask) static void vfio_add_emulated_word(VFIOPCIDevice *vdev, int pos, uint16_t val, uint16_t mask) { - vfio_set_word_bits(vdev->pdev.config + pos, val, mask); - vfio_set_word_bits(vdev->pdev.wmask + pos, ~mask, mask); + PCIDevice *pdev = PCI_DEVICE(vdev); + + vfio_set_word_bits(pdev->config + pos, val, mask); + vfio_set_word_bits(pdev->wmask + pos, ~mask, mask); vfio_set_word_bits(vdev->emulated_config_bits + pos, mask, mask); } @@ -2069,8 +2096,10 @@ static void vfio_set_long_bits(uint8_t *buf, uint32_t val, uint32_t mask) static void vfio_add_emulated_long(VFIOPCIDevice *vdev, int pos, uint32_t val, uint32_t mask) { - vfio_set_long_bits(vdev->pdev.config + pos, val, mask); - vfio_set_long_bits(vdev->pdev.wmask + pos, ~mask, mask); + PCIDevice *pdev = PCI_DEVICE(vdev); + + vfio_set_long_bits(pdev->config + pos, val, mask); + vfio_set_long_bits(pdev->wmask + pos, ~mask, mask); vfio_set_long_bits(vdev->emulated_config_bits + pos, mask, mask); } @@ -2078,7 +2107,8 @@ static void vfio_pci_enable_rp_atomics(VFIOPCIDevice *vdev) { struct vfio_device_info_cap_pci_atomic_comp *cap; g_autofree struct vfio_device_info *info = NULL; - PCIBus *bus = pci_get_bus(&vdev->pdev); + PCIDevice *pdev = PCI_DEVICE(vdev); + PCIBus *bus = pci_get_bus(pdev); PCIDevice *parent = bus->parent_dev; struct vfio_info_cap_header *hdr; uint32_t mask = 0; @@ -2094,8 +2124,8 @@ static void vfio_pci_enable_rp_atomics(VFIOPCIDevice *vdev) if (pci_bus_is_root(bus) || !parent || !parent->exp.exp_cap || pcie_cap_get_type(parent) != PCI_EXP_TYPE_ROOT_PORT || pcie_cap_get_version(parent) != PCI_EXP_FLAGS_VER2 || - vdev->pdev.devfn || - vdev->pdev.cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + pdev->devfn || + pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { return; } @@ -2139,8 +2169,10 @@ static void vfio_pci_enable_rp_atomics(VFIOPCIDevice *vdev) static void vfio_pci_disable_rp_atomics(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); + if (vdev->clear_parent_atomics_on_exit) { - PCIDevice *parent = pci_get_bus(&vdev->pdev)->parent_dev; + PCIDevice *parent = pci_get_bus(pdev)->parent_dev; uint8_t *pos = parent->config + parent->exp.exp_cap + PCI_EXP_DEVCAP2; pci_long_test_and_clear_mask(pos, PCI_EXP_DEVCAP2_ATOMIC_COMP32 | @@ -2152,10 +2184,11 @@ static void vfio_pci_disable_rp_atomics(VFIOPCIDevice *vdev) static bool vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, Error **errp) { + PCIDevice *pdev = PCI_DEVICE(vdev); uint16_t flags; uint8_t type; - flags = pci_get_word(vdev->pdev.config + pos + PCI_CAP_FLAGS); + flags = pci_get_word(pdev->config + pos + PCI_CAP_FLAGS); type = (flags & PCI_EXP_FLAGS_TYPE) >> 4; if (type != PCI_EXP_TYPE_ENDPOINT && @@ -2167,8 +2200,8 @@ static bool vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, return false; } - if (!pci_bus_is_express(pci_get_bus(&vdev->pdev))) { - PCIBus *bus = pci_get_bus(&vdev->pdev); + if (!pci_bus_is_express(pci_get_bus(pdev))) { + PCIBus *bus = pci_get_bus(pdev); PCIDevice *bridge; /* @@ -2200,7 +2233,7 @@ static bool vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, return true; } - } else if (pci_bus_is_root(pci_get_bus(&vdev->pdev))) { + } else if (pci_bus_is_root(pci_get_bus(pdev))) { /* * On a Root Complex bus Endpoints become Root Complex Integrated * Endpoints, which changes the type and clears the LNK & LNK2 fields. @@ -2268,20 +2301,20 @@ static bool vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, 1, PCI_EXP_FLAGS_VERS); } - pos = pci_add_capability(&vdev->pdev, PCI_CAP_ID_EXP, pos, size, - errp); + pos = pci_add_capability(pdev, PCI_CAP_ID_EXP, pos, size, errp); if (pos < 0) { return false; } - vdev->pdev.exp.exp_cap = pos; + pdev->exp.exp_cap = pos; return true; } static void vfio_check_pcie_flr(VFIOPCIDevice *vdev, uint8_t pos) { - uint32_t cap = pci_get_long(vdev->pdev.config + pos + PCI_EXP_DEVCAP); + PCIDevice *pdev = PCI_DEVICE(vdev); + uint32_t cap = pci_get_long(pdev->config + pos + PCI_EXP_DEVCAP); if (cap & PCI_EXP_DEVCAP_FLR) { trace_vfio_check_pcie_flr(vdev->vbasedev.name); @@ -2291,7 +2324,8 @@ static void vfio_check_pcie_flr(VFIOPCIDevice *vdev, uint8_t pos) static void vfio_check_pm_reset(VFIOPCIDevice *vdev, uint8_t pos) { - uint16_t csr = pci_get_word(vdev->pdev.config + pos + PCI_PM_CTRL); + PCIDevice *pdev = PCI_DEVICE(vdev); + uint16_t csr = pci_get_word(pdev->config + pos + PCI_PM_CTRL); if (!(csr & PCI_PM_CTRL_NO_SOFT_RESET)) { trace_vfio_check_pm_reset(vdev->vbasedev.name); @@ -2301,7 +2335,8 @@ static void vfio_check_pm_reset(VFIOPCIDevice *vdev, uint8_t pos) static void vfio_check_af_flr(VFIOPCIDevice *vdev, uint8_t pos) { - uint8_t cap = pci_get_byte(vdev->pdev.config + pos + PCI_AF_CAP); + PCIDevice *pdev = PCI_DEVICE(vdev); + uint8_t cap = pci_get_byte(pdev->config + pos + PCI_AF_CAP); if ((cap & PCI_AF_CAP_TP) && (cap & PCI_AF_CAP_FLR)) { trace_vfio_check_af_flr(vdev->vbasedev.name); @@ -2312,7 +2347,7 @@ static void vfio_check_af_flr(VFIOPCIDevice *vdev, uint8_t pos) static bool vfio_add_vendor_specific_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, Error **errp) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); pos = pci_add_capability(pdev, PCI_CAP_ID_VNDR, pos, size, errp); if (pos < 0) { @@ -2334,7 +2369,7 @@ static bool vfio_add_vendor_specific_cap(VFIOPCIDevice *vdev, int pos, static bool vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) { ERRP_GUARD(); - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); uint8_t cap_id, next, size; bool ret; @@ -2420,17 +2455,18 @@ static bool vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) static int vfio_setup_rebar_ecap(VFIOPCIDevice *vdev, uint16_t pos) { + PCIDevice *pdev = PCI_DEVICE(vdev); uint32_t ctrl; int i, nbar; - ctrl = pci_get_long(vdev->pdev.config + pos + PCI_REBAR_CTRL); + ctrl = pci_get_long(pdev->config + pos + PCI_REBAR_CTRL); nbar = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >> PCI_REBAR_CTRL_NBAR_SHIFT; for (i = 0; i < nbar; i++) { uint32_t cap; int size; - ctrl = pci_get_long(vdev->pdev.config + pos + PCI_REBAR_CTRL + (i * 8)); + ctrl = pci_get_long(pdev->config + pos + PCI_REBAR_CTRL + (i * 8)); size = (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> PCI_REBAR_CTRL_BAR_SHIFT; /* The cap register reports sizes 1MB to 128TB, with 4 reserved bits */ @@ -2468,7 +2504,7 @@ static int vfio_setup_rebar_ecap(VFIOPCIDevice *vdev, uint16_t pos) static void vfio_add_ext_cap(VFIOPCIDevice *vdev) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); uint32_t header; uint16_t cap_id, next, size; uint8_t cap_ver; @@ -2562,7 +2598,7 @@ static void vfio_add_ext_cap(VFIOPCIDevice *vdev) bool vfio_pci_add_capabilities(VFIOPCIDevice *vdev, Error **errp) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST) || !pdev->config[PCI_CAPABILITY_LIST]) { @@ -2579,7 +2615,7 @@ bool vfio_pci_add_capabilities(VFIOPCIDevice *vdev, Error **errp) void vfio_pci_pre_reset(VFIOPCIDevice *vdev) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); uint16_t cmd; vfio_disable_interrupts(vdev); @@ -2796,7 +2832,7 @@ static int vfio_pci_save_config(VFIODevice *vbasedev, QEMUFile *f, Error **errp) static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f) { VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); pcibus_t old_addr[PCI_NUM_REGIONS - 1]; int bar, ret; @@ -2844,7 +2880,7 @@ VFIOPCIDevice *vfio_pci_from_vfio_device(VFIODevice *vbasedev) void vfio_sub_page_bar_update_mappings(VFIOPCIDevice *vdev) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); int page_size = qemu_real_host_page_size(); int bar; @@ -2928,6 +2964,7 @@ bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) bool vfio_pci_populate_device(VFIOPCIDevice *vdev, Error **errp) { + PCIDevice *pdev = PCI_DEVICE(vdev); VFIODevice *vbasedev = &vdev->vbasedev; struct vfio_region_info *reg_info = NULL; struct vfio_irq_info irq_info; @@ -2979,7 +3016,7 @@ bool vfio_pci_populate_device(VFIOPCIDevice *vdev, Error **errp) vdev->config_size = reg_info->size; if (vdev->config_size == PCI_CONFIG_SPACE_SIZE) { - vdev->pdev.cap_present &= ~QEMU_PCI_CAP_EXPRESS; + pdev->cap_present &= ~QEMU_PCI_CAP_EXPRESS; } vdev->config_offset = reg_info->offset; @@ -3183,25 +3220,26 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) void vfio_pci_config_register_vga(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); assert(vdev->vga != NULL); - pci_register_vga(&vdev->pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem, + pci_register_vga(pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem, &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem, &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem); } bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); VFIODevice *vbasedev = &vdev->vbasedev; uint32_t config_space_size; int ret; - config_space_size = MIN(pci_config_size(&vdev->pdev), vdev->config_size); + config_space_size = MIN(pci_config_size(pdev), vdev->config_size); /* Get a copy of config space */ ret = vfio_pci_config_space_read(vdev, 0, config_space_size, - vdev->pdev.config); + pdev->config); if (ret < (int)config_space_size) { ret = ret < 0 ? -ret : EFAULT; error_setg_errno(errp, ret, "failed to read device config space"); @@ -3286,10 +3324,10 @@ bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) PCI_HEADER_TYPE_MULTI_FUNCTION; /* Restore or clear multifunction, this is always controlled by QEMU */ - if (vdev->pdev.cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { - vdev->pdev.config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; + if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + pdev->config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; } else { - vdev->pdev.config[PCI_HEADER_TYPE] &= ~PCI_HEADER_TYPE_MULTI_FUNCTION; + pdev->config[PCI_HEADER_TYPE] &= ~PCI_HEADER_TYPE_MULTI_FUNCTION; } /* @@ -3297,8 +3335,8 @@ bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) * BAR, such as might be the case with the option ROM, we can get * confusing, unwritable, residual addresses from the host here. */ - memset(&vdev->pdev.config[PCI_BASE_ADDRESS_0], 0, 24); - memset(&vdev->pdev.config[PCI_ROM_ADDRESS], 0, 4); + memset(&pdev->config[PCI_BASE_ADDRESS_0], 0, 24); + memset(&pdev->config[PCI_ROM_ADDRESS], 0, 4); vfio_pci_size_rom(vdev); @@ -3319,7 +3357,7 @@ bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) bool vfio_pci_interrupt_setup(VFIOPCIDevice *vdev, Error **errp) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); /* QEMU emulates all of MSI & MSIX */ if (pdev->cap_present & QEMU_PCI_CAP_MSIX) { @@ -3332,10 +3370,10 @@ bool vfio_pci_interrupt_setup(VFIOPCIDevice *vdev, Error **errp) vdev->msi_cap_size); } - if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) { + if (vfio_pci_read_config(pdev, PCI_INTERRUPT_PIN, 1)) { vdev->intx.mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, vfio_intx_mmap_enable, vdev); - pci_device_set_intx_routing_notifier(&vdev->pdev, + pci_device_set_intx_routing_notifier(pdev, vfio_intx_routing_notifier); vdev->irqchip_change_notifier.notify = vfio_irqchip_change; kvm_irqchip_add_change_notifier(&vdev->irqchip_change_notifier); @@ -3347,7 +3385,7 @@ bool vfio_pci_interrupt_setup(VFIOPCIDevice *vdev, Error **errp) */ if (!cpr_is_incoming() && !vfio_intx_enable(vdev, errp)) { timer_free(vdev->intx.mmap_timer); - pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); + pci_device_set_intx_routing_notifier(pdev, NULL); kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); return false; } @@ -3498,7 +3536,7 @@ static void vfio_pci_realize(PCIDevice *pdev, Error **errp) if (vdev->interrupt == VFIO_INT_INTx) { vfio_intx_disable(vdev); } - pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); + pci_device_set_intx_routing_notifier(pdev, NULL); if (vdev->irqchip_change_notifier.notify) { kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); } @@ -3530,7 +3568,7 @@ static void vfio_exitfn(PCIDevice *pdev) vfio_unregister_req_notifier(vdev); vfio_unregister_err_notifier(vdev); - pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); + pci_device_set_intx_routing_notifier(pdev, NULL); if (vdev->irqchip_change_notifier.notify) { kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); } From 31bfd70ef02045692d04bf53461399d3b81c0ea3 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:57 +0100 Subject: [PATCH 0609/1794] vfio/pci-quirks.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOPCIDevice and PCIDevice instead of accessing pdev directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-18-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci-quirks.c | 48 ++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index 3f002252acfb7..c97606dbf194d 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -113,6 +113,7 @@ static uint64_t vfio_generic_window_quirk_data_read(void *opaque, { VFIOConfigWindowQuirk *window = opaque; VFIOPCIDevice *vdev = window->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); uint64_t data; /* Always read data reg, discard if window enabled */ @@ -120,7 +121,7 @@ static uint64_t vfio_generic_window_quirk_data_read(void *opaque, addr + window->data_offset, size); if (window->window_enabled) { - data = vfio_pci_read_config(&vdev->pdev, window->address_val, size); + data = vfio_pci_read_config(pdev, window->address_val, size); trace_vfio_quirk_generic_window_data_read(vdev->vbasedev.name, memory_region_name(window->data_mem), data); } @@ -133,9 +134,10 @@ static void vfio_generic_window_quirk_data_write(void *opaque, hwaddr addr, { VFIOConfigWindowQuirk *window = opaque; VFIOPCIDevice *vdev = window->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); if (window->window_enabled) { - vfio_pci_write_config(&vdev->pdev, window->address_val, data, size); + vfio_pci_write_config(pdev, window->address_val, data, size); trace_vfio_quirk_generic_window_data_write(vdev->vbasedev.name, memory_region_name(window->data_mem), data); return; @@ -156,6 +158,7 @@ static uint64_t vfio_generic_quirk_mirror_read(void *opaque, { VFIOConfigMirrorQuirk *mirror = opaque; VFIOPCIDevice *vdev = mirror->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); uint64_t data; /* Read and discard in case the hardware cares */ @@ -163,7 +166,7 @@ static uint64_t vfio_generic_quirk_mirror_read(void *opaque, addr + mirror->offset, size); addr += mirror->config_offset; - data = vfio_pci_read_config(&vdev->pdev, addr, size); + data = vfio_pci_read_config(pdev, addr, size); trace_vfio_quirk_generic_mirror_read(vdev->vbasedev.name, memory_region_name(mirror->mem), addr, data); @@ -175,9 +178,10 @@ static void vfio_generic_quirk_mirror_write(void *opaque, hwaddr addr, { VFIOConfigMirrorQuirk *mirror = opaque; VFIOPCIDevice *vdev = mirror->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); addr += mirror->config_offset; - vfio_pci_write_config(&vdev->pdev, addr, data, size); + vfio_pci_write_config(pdev, addr, data, size); trace_vfio_quirk_generic_mirror_write(vdev->vbasedev.name, memory_region_name(mirror->mem), addr, data); @@ -211,7 +215,8 @@ static uint64_t vfio_ati_3c3_quirk_read(void *opaque, hwaddr addr, unsigned size) { VFIOPCIDevice *vdev = opaque; - uint64_t data = vfio_pci_read_config(&vdev->pdev, + PCIDevice *pdev = PCI_DEVICE(vdev); + uint64_t data = vfio_pci_read_config(pdev, PCI_BASE_ADDRESS_4 + 1, size); trace_vfio_quirk_ati_3c3_read(vdev->vbasedev.name, data); @@ -563,6 +568,7 @@ static uint64_t vfio_nvidia_3d0_quirk_read(void *opaque, { VFIONvidia3d0Quirk *quirk = opaque; VFIOPCIDevice *vdev = quirk->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); VFIONvidia3d0State old_state = quirk->state; uint64_t data = vfio_vga_read(&vdev->vga->region[QEMU_PCI_VGA_IO_HI], addr + 0x10, size); @@ -573,7 +579,7 @@ static uint64_t vfio_nvidia_3d0_quirk_read(void *opaque, (quirk->offset & ~(PCI_CONFIG_SPACE_SIZE - 1)) == 0x1800) { uint8_t offset = quirk->offset & (PCI_CONFIG_SPACE_SIZE - 1); - data = vfio_pci_read_config(&vdev->pdev, offset, size); + data = vfio_pci_read_config(pdev, offset, size); trace_vfio_quirk_nvidia_3d0_read(vdev->vbasedev.name, offset, size, data); } @@ -586,6 +592,7 @@ static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr, { VFIONvidia3d0Quirk *quirk = opaque; VFIOPCIDevice *vdev = quirk->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); VFIONvidia3d0State old_state = quirk->state; quirk->state = NONE; @@ -599,7 +606,7 @@ static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr, if ((quirk->offset & ~(PCI_CONFIG_SPACE_SIZE - 1)) == 0x1800) { uint8_t offset = quirk->offset & (PCI_CONFIG_SPACE_SIZE - 1); - vfio_pci_write_config(&vdev->pdev, offset, data, size); + vfio_pci_write_config(pdev, offset, data, size); trace_vfio_quirk_nvidia_3d0_write(vdev->vbasedev.name, offset, data, size); return; @@ -815,7 +822,7 @@ static void vfio_nvidia_quirk_mirror_write(void *opaque, hwaddr addr, { VFIOConfigMirrorQuirk *mirror = opaque; VFIOPCIDevice *vdev = mirror->vdev; - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); LastDataSet *last = (LastDataSet *)&mirror->data; vfio_generic_quirk_mirror_write(opaque, addr, data, size); @@ -1005,6 +1012,7 @@ static void vfio_rtl8168_quirk_address_write(void *opaque, hwaddr addr, { VFIOrtl8168Quirk *rtl = opaque; VFIOPCIDevice *vdev = rtl->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); rtl->enabled = false; @@ -1013,7 +1021,7 @@ static void vfio_rtl8168_quirk_address_write(void *opaque, hwaddr addr, rtl->addr = (uint32_t)data; if (data & 0x80000000U) { /* Do write */ - if (vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX) { + if (pdev->cap_present & QEMU_PCI_CAP_MSIX) { hwaddr offset = data & 0xfff; uint64_t val = rtl->data; @@ -1021,7 +1029,7 @@ static void vfio_rtl8168_quirk_address_write(void *opaque, hwaddr addr, (uint16_t)offset, val); /* Write to the proper guest MSI-X table instead */ - memory_region_dispatch_write(&vdev->pdev.msix_table_mmio, + memory_region_dispatch_write(&pdev->msix_table_mmio, offset, val, size_memop(size) | MO_LE, MEMTXATTRS_UNSPECIFIED); @@ -1049,11 +1057,12 @@ static uint64_t vfio_rtl8168_quirk_data_read(void *opaque, { VFIOrtl8168Quirk *rtl = opaque; VFIOPCIDevice *vdev = rtl->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); uint64_t data = vfio_region_read(&vdev->bars[2].region, addr + 0x70, size); - if (rtl->enabled && (vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX)) { + if (rtl->enabled && (pdev->cap_present & QEMU_PCI_CAP_MSIX)) { hwaddr offset = rtl->addr & 0xfff; - memory_region_dispatch_read(&vdev->pdev.msix_table_mmio, offset, + memory_region_dispatch_read(&pdev->msix_table_mmio, offset, &data, size_memop(size) | MO_LE, MEMTXATTRS_UNSPECIFIED); trace_vfio_quirk_rtl8168_msix_read(vdev->vbasedev.name, offset, data); @@ -1297,7 +1306,7 @@ static void vfio_radeon_set_gfx_only_reset(VFIOPCIDevice *vdev) static int vfio_radeon_reset(VFIOPCIDevice *vdev) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); int i, ret = 0; uint32_t data; @@ -1454,7 +1463,7 @@ static bool is_valid_std_cap_offset(uint8_t pos) static bool vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) { ERRP_GUARD(); - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); int ret, pos; bool c8_conflict = false, d4_conflict = false; uint8_t tmp; @@ -1547,6 +1556,7 @@ static bool vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) static bool vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) { ERRP_GUARD(); + PCIDevice *pdev = PCI_DEVICE(vdev); uint8_t membar_phys[16]; int ret, pos = 0xE8; @@ -1565,7 +1575,7 @@ static bool vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) return false; } - ret = pci_add_capability(&vdev->pdev, PCI_CAP_ID_VNDR, pos, + ret = pci_add_capability(pdev, PCI_CAP_ID_VNDR, pos, VMD_SHADOW_CAP_LEN, errp); if (ret < 0) { error_prepend(errp, "Failed to add VMD MEMBAR Shadow cap: "); @@ -1574,10 +1584,10 @@ static bool vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) memset(vdev->emulated_config_bits + pos, 0xFF, VMD_SHADOW_CAP_LEN); pos += PCI_CAP_FLAGS; - pci_set_byte(vdev->pdev.config + pos++, VMD_SHADOW_CAP_LEN); - pci_set_byte(vdev->pdev.config + pos++, VMD_SHADOW_CAP_VER); - pci_set_long(vdev->pdev.config + pos, 0x53484457); /* SHDW */ - memcpy(vdev->pdev.config + pos + 4, membar_phys, 16); + pci_set_byte(pdev->config + pos++, VMD_SHADOW_CAP_LEN); + pci_set_byte(pdev->config + pos++, VMD_SHADOW_CAP_VER); + pci_set_long(pdev->config + pos, 0x53484457); /* SHDW */ + memcpy(pdev->config + pos + 4, membar_phys, 16); return true; } From 54a3eb315023f59db0426d85604f4527bf7b1aaa Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:58 +0100 Subject: [PATCH 0610/1794] vfio/cpr.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOPCIDevice and PCIDevice instead of accessing pdev directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Steve Sistare Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-19-mark.caveayland@nutanix.com [ clg: Updated vfio_cpr_set_msi_virq() ] Signed-off-by: Cédric Le Goater --- hw/vfio/cpr.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index a831243e02071..f911988adddfd 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -56,7 +56,7 @@ static void vfio_cpr_claim_vectors(VFIOPCIDevice *vdev, int nr_vectors, { int i, fd; bool pending = false; - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); vdev->nr_vectors = nr_vectors; vdev->msi_vectors = g_new0(VFIOMSIVector, nr_vectors); @@ -99,7 +99,7 @@ static void vfio_cpr_claim_vectors(VFIOPCIDevice *vdev, int nr_vectors, static int vfio_cpr_pci_pre_load(void *opaque) { VFIOPCIDevice *vdev = opaque; - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); int size = MIN(pci_config_size(pdev), vdev->config_size); int i; @@ -113,7 +113,7 @@ static int vfio_cpr_pci_pre_load(void *opaque) static int vfio_cpr_pci_post_load(void *opaque, int version_id) { VFIOPCIDevice *vdev = opaque; - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); int nr_vectors; vfio_sub_page_bar_update_mappings(vdev); @@ -214,7 +214,7 @@ static int set_irqfd_notifier_gsi(KVMState *s, EventNotifier *n, static int vfio_cpr_set_msi_virq(VFIOPCIDevice *vdev, Error **errp, bool enable) { const char *op = (enable ? "enable" : "disable"); - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); int i, nr_vectors, ret = 0; if (msix_enabled(pdev)) { From 8d3776dd6d8740fb7c91c7c1c25b60fc7edfd19c Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:59 +0100 Subject: [PATCH 0611/1794] vfio/igd.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOPCIDevice and PCIDevice instead of accessing pdev directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Tomita Moeko Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-20-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index f116c40ccd933..4bfa2e0fcd22a 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -200,7 +200,7 @@ static bool vfio_pci_igd_opregion_detect(VFIOPCIDevice *vdev, } /* Hotplugging is not supported for opregion access */ - if (vdev->pdev.qdev.hotplugged) { + if (DEVICE(vdev)->hotplugged) { warn_report("IGD device detected, but OpRegion is not supported " "on hotplugged device."); return false; @@ -260,11 +260,12 @@ static int vfio_pci_igd_copy(VFIOPCIDevice *vdev, PCIDevice *pdev, static int vfio_pci_igd_host_init(VFIOPCIDevice *vdev, struct vfio_region_info *info) { + PCIDevice *pdev = PCI_DEVICE(vdev); PCIBus *bus; PCIDevice *host_bridge; int ret; - bus = pci_device_root_bus(&vdev->pdev); + bus = pci_device_root_bus(pdev); host_bridge = pci_find_device(bus, 0, PCI_DEVFN(0, 0)); if (!host_bridge) { @@ -327,13 +328,14 @@ type_init(vfio_pci_igd_register_types) static int vfio_pci_igd_lpc_init(VFIOPCIDevice *vdev, struct vfio_region_info *info) { + PCIDevice *pdev = PCI_DEVICE(vdev); PCIDevice *lpc_bridge; int ret; - lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev), + lpc_bridge = pci_find_device(pci_device_root_bus(pdev), 0, PCI_DEVFN(0x1f, 0)); if (!lpc_bridge) { - lpc_bridge = pci_create_simple(pci_device_root_bus(&vdev->pdev), + lpc_bridge = pci_create_simple(pci_device_root_bus(pdev), PCI_DEVFN(0x1f, 0), "vfio-pci-igd-lpc-bridge"); } @@ -350,13 +352,14 @@ static bool vfio_pci_igd_setup_lpc_bridge(VFIOPCIDevice *vdev, Error **errp) { struct vfio_region_info *host = NULL; struct vfio_region_info *lpc = NULL; + PCIDevice *pdev = PCI_DEVICE(vdev); PCIDevice *lpc_bridge; int ret; /* * Copying IDs or creating new devices are not supported on hotplug */ - if (vdev->pdev.qdev.hotplugged) { + if (DEVICE(vdev)->hotplugged) { error_setg(errp, "IGD LPC is not supported on hotplugged device"); return false; } @@ -366,7 +369,7 @@ static bool vfio_pci_igd_setup_lpc_bridge(VFIOPCIDevice *vdev, Error **errp) * can stuff host values into, so if there's already one there and it's not * one we can hack on, this quirk is no-go. Sorry Q35. */ - lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev), + lpc_bridge = pci_find_device(pci_device_root_bus(pdev), 0, PCI_DEVFN(0x1f, 0)); if (lpc_bridge && !object_dynamic_cast(OBJECT(lpc_bridge), "vfio-pci-igd-lpc-bridge")) { @@ -510,6 +513,7 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) { struct vfio_region_info *opregion = NULL; + PCIDevice *pdev = PCI_DEVICE(vdev); int ret, gen; uint64_t gms_size = 0; uint64_t *bdsm_size; @@ -529,7 +533,7 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) info_report("OpRegion detected on Intel display %x.", vdev->device_id); gen = igd_gen(vdev); - gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4); + gmch = vfio_pci_read_config(pdev, IGD_GMCH, 4); /* * For backward compatibility, enable legacy mode when @@ -544,7 +548,7 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) (gen >= 6 && gen <= 9) && !(gmch & IGD_GMCH_VGA_DISABLE) && !strcmp(MACHINE_GET_CLASS(qdev_get_machine())->family, "pc_piix") && - (&vdev->pdev == pci_find_device(pci_device_root_bus(&vdev->pdev), + (pdev == pci_find_device(pci_device_root_bus(pdev), 0, PCI_DEVFN(0x2, 0)))) { /* * IGD legacy mode requires: @@ -566,7 +570,7 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) */ ret = vfio_device_get_region_info(&vdev->vbasedev, VFIO_PCI_ROM_REGION_INDEX, &rom); - if ((ret || !rom->size) && !vdev->pdev.romfile) { + if ((ret || !rom->size) && !pdev->romfile) { error_setg(&err, "Device has no ROM"); goto error; } @@ -611,8 +615,8 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) * ASLS (OpRegion address) is read-only, emulated * It contains HPA, guest firmware need to reprogram it with GPA. */ - pci_set_long(vdev->pdev.config + IGD_ASLS, 0); - pci_set_long(vdev->pdev.wmask + IGD_ASLS, ~0); + pci_set_long(pdev->config + IGD_ASLS, 0); + pci_set_long(pdev->wmask + IGD_ASLS, ~0); pci_set_long(vdev->emulated_config_bits + IGD_ASLS, ~0); /* @@ -626,8 +630,8 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) } /* GMCH is read-only, emulated */ - pci_set_long(vdev->pdev.config + IGD_GMCH, gmch); - pci_set_long(vdev->pdev.wmask + IGD_GMCH, 0); + pci_set_long(pdev->config + IGD_GMCH, gmch); + pci_set_long(pdev->wmask + IGD_GMCH, 0); pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0); } @@ -636,12 +640,12 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) /* BDSM is read-write, emulated. BIOS needs to be able to write it */ if (gen < 11) { - pci_set_long(vdev->pdev.config + IGD_BDSM, 0); - pci_set_long(vdev->pdev.wmask + IGD_BDSM, ~0); + pci_set_long(pdev->config + IGD_BDSM, 0); + pci_set_long(pdev->wmask + IGD_BDSM, ~0); pci_set_long(vdev->emulated_config_bits + IGD_BDSM, ~0); } else { - pci_set_quad(vdev->pdev.config + IGD_BDSM_GEN11, 0); - pci_set_quad(vdev->pdev.wmask + IGD_BDSM_GEN11, ~0); + pci_set_quad(pdev->config + IGD_BDSM_GEN11, 0); + pci_set_quad(pdev->wmask + IGD_BDSM_GEN11, ~0); pci_set_quad(vdev->emulated_config_bits + IGD_BDSM_GEN11, ~0); } } From a49ef7a467c3ced0be048b02189092031e325d01 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:26:00 +0100 Subject: [PATCH 0612/1794] vfio-user/pci.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOPCIDevice and PCIDevice instead of accessing pdev directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-21-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index c3947a8f2ec9c..e2c309784fec5 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -65,7 +65,7 @@ static void vfio_user_msix_setup(VFIOPCIDevice *vdev) vdev->msix->pba_region = pba_reg; vfio_reg = vdev->bars[vdev->msix->pba_bar].mr; - msix_reg = &vdev->pdev.msix_pba_mmio; + msix_reg = &PCI_DEVICE(vdev)->msix_pba_mmio; memory_region_init_io(pba_reg, OBJECT(vdev), &vfio_user_pba_ops, vdev, "VFIO MSIX PBA", int128_get64(msix_reg->size)); memory_region_add_subregion_overlap(vfio_reg, vdev->msix->pba_offset, @@ -86,7 +86,7 @@ static void vfio_user_msix_teardown(VFIOPCIDevice *vdev) static void vfio_user_dma_read(VFIOPCIDevice *vdev, VFIOUserDMARW *msg) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); VFIOUserProxy *proxy = vdev->vbasedev.proxy; VFIOUserDMARW *res; MemTxResult r; @@ -134,7 +134,7 @@ static void vfio_user_dma_read(VFIOPCIDevice *vdev, VFIOUserDMARW *msg) static void vfio_user_dma_write(VFIOPCIDevice *vdev, VFIOUserDMARW *msg) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); VFIOUserProxy *proxy = vdev->vbasedev.proxy; MemTxResult r; From e2827210d6a9c56c1b14b00b414dfa9eb7843711 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:26:01 +0100 Subject: [PATCH 0613/1794] s390x/s390-pci-vfio.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to cast to VFIOPCIDevice instead of using container_of(). Signed-off-by: Mark Cave-Ayland Reviewed-by: Matthew Rosato Reviewed-by: Eric Farman Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-22-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/s390x/s390-pci-vfio.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c index aaf91319b4e3c..938a5511713da 100644 --- a/hw/s390x/s390-pci-vfio.c +++ b/hw/s390x/s390-pci-vfio.c @@ -62,7 +62,7 @@ S390PCIDMACount *s390_pci_start_dma_count(S390pciState *s, { S390PCIDMACount *cnt; uint32_t avail; - VFIOPCIDevice *vpdev = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + VFIOPCIDevice *vpdev = VFIO_PCI_BASE(pbdev->pdev); int id; assert(vpdev); @@ -108,7 +108,7 @@ static void s390_pci_read_base(S390PCIBusDevice *pbdev, { struct vfio_info_cap_header *hdr; struct vfio_device_info_cap_zpci_base *cap; - VFIOPCIDevice *vpci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + VFIOPCIDevice *vpci = VFIO_PCI_BASE(pbdev->pdev); uint64_t vfio_size; hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_BASE); @@ -162,7 +162,7 @@ static bool get_host_fh(S390PCIBusDevice *pbdev, struct vfio_device_info *info, { struct vfio_info_cap_header *hdr; struct vfio_device_info_cap_zpci_base *cap; - VFIOPCIDevice *vpci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + VFIOPCIDevice *vpci = VFIO_PCI_BASE(pbdev->pdev); hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_BASE); @@ -185,7 +185,7 @@ static void s390_pci_read_group(S390PCIBusDevice *pbdev, struct vfio_device_info_cap_zpci_group *cap; S390pciState *s = s390_get_phb(); ClpRspQueryPciGrp *resgrp; - VFIOPCIDevice *vpci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + VFIOPCIDevice *vpci = VFIO_PCI_BASE(pbdev->pdev); uint8_t start_gid = pbdev->zpci_fn.pfgid; hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_GROUP); @@ -264,7 +264,7 @@ static void s390_pci_read_util(S390PCIBusDevice *pbdev, { struct vfio_info_cap_header *hdr; struct vfio_device_info_cap_zpci_util *cap; - VFIOPCIDevice *vpci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + VFIOPCIDevice *vpci = VFIO_PCI_BASE(pbdev->pdev); hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_UTIL); @@ -291,7 +291,7 @@ static void s390_pci_read_pfip(S390PCIBusDevice *pbdev, { struct vfio_info_cap_header *hdr; struct vfio_device_info_cap_zpci_pfip *cap; - VFIOPCIDevice *vpci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + VFIOPCIDevice *vpci = VFIO_PCI_BASE(pbdev->pdev); hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_PFIP); @@ -314,7 +314,7 @@ static void s390_pci_read_pfip(S390PCIBusDevice *pbdev, static struct vfio_device_info *get_device_info(S390PCIBusDevice *pbdev) { - VFIOPCIDevice *vfio_pci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + VFIOPCIDevice *vfio_pci = VFIO_PCI_BASE(pbdev->pdev); return vfio_get_device_info(vfio_pci->vbasedev.fd); } From bb986792a968ee51cda72cd4cc05822198495375 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:26:02 +0100 Subject: [PATCH 0614/1794] vfio/pci.h: rename VFIOPCIDevice pdev field to parent_obj MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that nothing accesses the pdev field directly, rename pdev to parent_obj as per our current coding guidelines. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: Steve Sistare Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-23-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr.c | 4 ++-- hw/vfio/pci.c | 4 ++-- hw/vfio/pci.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index f911988adddfd..2c71fc1e8ef7b 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -173,8 +173,8 @@ const VMStateDescription vfio_cpr_pci_vmstate = { .post_load = vfio_cpr_pci_post_load, .needed = cpr_incoming_needed, .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(pdev, VFIOPCIDevice), - VMSTATE_MSIX_TEST(pdev, VFIOPCIDevice, pci_msix_present), + VMSTATE_PCI_DEVICE(parent_obj, VFIOPCIDevice), + VMSTATE_MSIX_TEST(parent_obj, VFIOPCIDevice, pci_msix_present), VMSTATE_VFIO_INTX(intx, VFIOPCIDevice), VMSTATE_END_OF_LIST() } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 052591d2c970a..d14e96b2f82d7 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2811,8 +2811,8 @@ static const VMStateDescription vmstate_vfio_pci_config = { .version_id = 1, .minimum_version_id = 1, .fields = (const VMStateField[]) { - VMSTATE_PCI_DEVICE(pdev, VFIOPCIDevice), - VMSTATE_MSIX_TEST(pdev, VFIOPCIDevice, vfio_msix_present), + VMSTATE_PCI_DEVICE(parent_obj, VFIOPCIDevice), + VMSTATE_MSIX_TEST(parent_obj, VFIOPCIDevice, vfio_msix_present), VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription * const []) { diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 2db76b6f4fb58..e0aef82a8931c 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -123,7 +123,7 @@ typedef struct VFIOMSIXInfo { OBJECT_DECLARE_SIMPLE_TYPE(VFIOPCIDevice, VFIO_PCI_BASE) struct VFIOPCIDevice { - PCIDevice pdev; + PCIDevice parent_obj; VFIODevice vbasedev; VFIOINTx intx; From 180c65c3467f49565ffe26f663a0482717f7d4ea Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 8 May 2025 16:41:17 +0200 Subject: [PATCH 0615/1794] hw/display/bcm2835_fb: Move inclusion of console.h to the .c file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The definitions from console.h are not needed in the bcm2835_fb.h header file yet, so let's move it to the place that really needs its definitions, i.e. into the bcm2835_fb.c file. This way the header can also be used by code that is not compiled with the CFLAGS that are required for pixman or OpenGL (in case their headers do not reside under /usr/include). Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth Message-ID: <20250508144120.163009-3-thuth@redhat.com> --- hw/display/bcm2835_fb.c | 1 + include/hw/display/bcm2835_fb.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c index 820e67ac8bb49..1bb2ee45a0157 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -27,6 +27,7 @@ #include "hw/display/bcm2835_fb.h" #include "hw/hw.h" #include "hw/irq.h" +#include "ui/console.h" #include "framebuffer.h" #include "ui/pixel_ops.h" #include "hw/misc/bcm2835_mbox_defs.h" diff --git a/include/hw/display/bcm2835_fb.h b/include/hw/display/bcm2835_fb.h index 49541bf08f450..acc9230b6a857 100644 --- a/include/hw/display/bcm2835_fb.h +++ b/include/hw/display/bcm2835_fb.h @@ -13,7 +13,6 @@ #define BCM2835_FB_H #include "hw/sysbus.h" -#include "ui/console.h" #include "qom/object.h" #define UPPER_RAM_BASE 0x40000000 From b3a51bb30bc72f88231e3b6a63c04eef5c36433c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 8 May 2025 16:41:20 +0200 Subject: [PATCH 0616/1794] Revert "meson.build: Disable -fzero-call-used-regs on OpenBSD" This reverts commit 2d6d995709482cc8b6a76dbb5334a28001a14a9a. OpenBSD 7.7 fixed the problem with the -fzero-call-used-regs on OpenBSD, see https://github.com/openbsd/src/commit/03eca72d1e030b7a542cd6aec1 for the fix there. Suggested-by: Brad Smith Signed-off-by: Thomas Huth Message-ID: <20250508144120.163009-6-thuth@redhat.com> --- meson.build | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/meson.build b/meson.build index fa6186db33435..3d7387335665c 100644 --- a/meson.build +++ b/meson.build @@ -709,11 +709,7 @@ hardening_flags = [ # # NB: Clang 17 is broken and SEGVs # https://github.com/llvm/llvm-project/issues/75168 -# -# NB2: This clashes with the "retguard" extension of OpenBSD's Clang -# https://gitlab.com/qemu-project/qemu/-/issues/2278 -if host_os != 'openbsd' and \ - cc.compiles('extern struct { void (*cb)(void); } s; void f(void) { s.cb(); }', +if cc.compiles('extern struct { void (*cb)(void); } s; void f(void) { s.cb(); }', name: '-fzero-call-used-regs=used-gpr', args: ['-O2', '-fzero-call-used-regs=used-gpr']) hardening_flags += '-fzero-call-used-regs=used-gpr' From 7af325c23ef5e94b77864d2d2ca64da4a5a35f30 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 28 Jul 2025 13:51:51 +0200 Subject: [PATCH 0617/1794] hw/mips/malta: Silence warning from ubsan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When compiling QEMU with --enable-ubsan there is a undefined behavior warning when using the malta machine: hw/mips/malta.c:1200:32: runtime error: addition of unsigned offset to 0x7fb620600000 overflowed to 0x7fb6205fffff SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior hw/mips/malta.c:1200:32 To fix the issue, check the bios_size whether we really loaded the firmware before trying to byte-swap the instructions here. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250728115152.187728-1-thuth@redhat.com> --- hw/mips/malta.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/mips/malta.c b/hw/mips/malta.c index 344dc8ca76675..02da629b5afca 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -1191,7 +1191,7 @@ void mips_malta_init(MachineState *machine) * In little endian mode the 32bit words in the bios are swapped, * a neat trick which allows bi-endian firmware. */ - if (!TARGET_BIG_ENDIAN) { + if (!TARGET_BIG_ENDIAN && bios_size > 0) { uint32_t *end, *addr; const size_t swapsize = MIN(bios_size, 0x3e0000); addr = rom_ptr(FLASH_ADDRESS, swapsize); From 2dbaf58bbe78f415ec867dc238f90321ed8a3f62 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 28 Jul 2025 19:25:45 +0200 Subject: [PATCH 0618/1794] system/physmem: Silence warning from ubsan When compiling QEMU with --enable-ubsan there is a undefined behavior warning when running the bios-tables-test for example: .../system/physmem.c:3243:13: runtime error: applying non-zero offset 262144 to null pointer #0 0x55ac1df5fbc4 in address_space_write_rom_internal .../system/physmem.c:3243:13 The problem is that buf is indeed NULL if the function is e.g. called with type == FLUSH_CACHE. Add a check to fix the issue. Reviewed-by: David Hildenbrand Signed-off-by: Thomas Huth Message-ID: <20250728172545.314178-1-thuth@redhat.com> --- system/physmem.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/system/physmem.c b/system/physmem.c index f498572fc821f..311011156c7db 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3231,8 +3231,10 @@ static inline MemTxResult address_space_write_rom_internal(AddressSpace *as, } } len -= l; - buf += l; addr += l; + if (buf) { + buf += l; + } } return MEMTX_OK; } From 38dd513263d814dc3cf554b899c118a46ca77577 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 21 Aug 2025 16:51:30 +0200 Subject: [PATCH 0619/1794] ui/vnc: Fix crash when specifying [vnc] without id in the config file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU currently crashes when there is a [vnc] section in the config file that does not have an "id = ..." line: $ echo "[vnc]" > /tmp/qemu.conf $ ./qemu-system-x86_64 -readconfig /tmp/qemu.conf qemu-system-x86_64: ../../devel/qemu/ui/vnc.c:4347: vnc_init_func: Assertion `id' failed. Aborted (core dumped) The required "id" is only set up automatically while parsing the command line, but not when reading the options from the config file. Thus let's move code that automatically adds the id (if it does not exist yet) to the init function that needs the id for the first time, replacing the assert() statement there. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2836 Reviewed-by: Marc-André Lureau Signed-off-by: Thomas Huth Message-ID: <20250821145130.845104-1-thuth@redhat.com> --- ui/vnc.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index 68ca4a68e7a23..9054fc812535b 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -4309,8 +4309,9 @@ void vnc_display_add_client(const char *id, int csock, bool skipauth) } } -static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts) +static char *vnc_auto_assign_id(QemuOpts *opts) { + QemuOptsList *olist = qemu_find_opts("vnc"); int i = 2; char *id; @@ -4320,23 +4321,18 @@ static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts) id = g_strdup_printf("vnc%d", i++); } qemu_opts_set_id(opts, id); + + return id; } void vnc_parse(const char *str) { QemuOptsList *olist = qemu_find_opts("vnc"); QemuOpts *opts = qemu_opts_parse_noisily(olist, str, !is_help_option(str)); - const char *id; if (!opts) { exit(1); } - - id = qemu_opts_id(opts); - if (!id) { - /* auto-assign id if not present */ - vnc_auto_assign_id(olist, opts); - } } int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp) @@ -4344,7 +4340,11 @@ int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp) Error *local_err = NULL; char *id = (char *)qemu_opts_id(opts); - assert(id); + if (!id) { + /* auto-assign id if not present */ + id = vnc_auto_assign_id(opts); + } + vnc_display_init(id, &local_err); if (local_err) { error_propagate(errp, local_err); From 5f81033e5b3e55eda2a1d06f959c05b61e719d68 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 29 Aug 2025 16:20:00 +0200 Subject: [PATCH 0620/1794] tests/functional/m68k: Avoid ResourceWarning in the nextcube test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit c3fd296cf7b1 ("functional: always enable all python warnings") we enabled more warnings for the functional tests. This triggers now a warning in the nextcube test: tests/functional/m68k/test_nextcube.py:47: ResourceWarning: unclosed file <_io.BufferedReader name='tests/functional/m68k/test_nextcube.NextCubeMachine.test_bootrom_framebuffer_size/scratch/dump.ppm'> width, height = Image.open(screenshot_path).size Use a proper "with" context to avoid it. Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth Message-ID: <20250829142000.62320-1-thuth@redhat.com> --- tests/functional/m68k/test_nextcube.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/m68k/test_nextcube.py b/tests/functional/m68k/test_nextcube.py index 13c72bd136a82..c1610e58456fa 100755 --- a/tests/functional/m68k/test_nextcube.py +++ b/tests/functional/m68k/test_nextcube.py @@ -44,7 +44,8 @@ def test_bootrom_framebuffer_size(self): self.check_bootrom_framebuffer(screenshot_path) from PIL import Image - width, height = Image.open(screenshot_path).size + with Image.open(screenshot_path) as image: + width, height = image.size self.assertEqual(width, 1120) self.assertEqual(height, 832) From 9f80f3695d305015f5271d946304e5cffee607c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 29 Aug 2025 15:26:14 +0100 Subject: [PATCH 0621/1794] tests/functional: enable force refresh of cached assets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the 'QEMU_TEST_REFRESH_CACHE' environment variable is set, then ignore any existing cached asset and download a fresh copy. This can be used to selectively refresh assets if set before running a single test script. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Reviewed-by: Richard Henderson Message-ID: <20250829142616.2633254-2-berrange@redhat.com> Signed-off-by: Thomas Huth --- docs/devel/testing/functional.rst | 3 +++ tests/functional/qemu_test/asset.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/docs/devel/testing/functional.rst b/docs/devel/testing/functional.rst index 3728bab6c0c4b..fdeaebaadcb99 100644 --- a/docs/devel/testing/functional.rst +++ b/docs/devel/testing/functional.rst @@ -312,6 +312,9 @@ The cache is populated in the ``~/.cache/qemu/download`` directory by default, but the location can be changed by setting the ``QEMU_TEST_CACHE_DIR`` environment variable. +To force the test suite to re-download the cache, even if still valid, +set the ``QEMU_TEST_REFRESH_CACHE`` environment variable. + Skipping tests -------------- diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index 704b84d0ea6ef..b5a6136d36552 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -72,6 +72,10 @@ def _check(self, cache_file): return self.hash == hl.hexdigest() def valid(self): + if os.getenv("QEMU_TEST_REFRESH_CACHE", None) is not None: + self.log.info("Force refresh of asset %s", self.url) + return False + return self.cache_file.exists() and self._check(self.cache_file) def fetchable(self): From 124ab930ba38c41a86533dbfabb7a3b3b270ef98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 29 Aug 2025 15:26:15 +0100 Subject: [PATCH 0622/1794] tests/functional: fix formatting of exception args MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The catch-all exception handler forgot the placeholder for the exception details. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-ID: <20250829142616.2633254-3-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index b5a6136d36552..5c74adf224102 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -173,7 +173,7 @@ def fetch(self): continue except Exception as e: tmp_cache_file.unlink() - raise AssetError(self, "Unable to download: " % e) + raise AssetError(self, "Unable to download: %s" % e) if not os.path.exists(tmp_cache_file): raise AssetError(self, "Download retries exceeded", transient=True) From 335da23abec85cd2f6d10f1fe36b28a02088e723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 29 Aug 2025 15:26:16 +0100 Subject: [PATCH 0623/1794] tests/functional: handle URLError when fetching assets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We treat most HTTP errors as non-fatal when fetching assets, but forgot to handle network level errors. This adds catching of URLError so that we retry on failure, and will ultimately trigger graceful skipping in the pre-cache task. Signed-off-by: Daniel P. Berrangé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Message-ID: <20250829142616.2633254-4-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/asset.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index 5c74adf224102..2dd32bf28d957 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -15,7 +15,7 @@ from time import sleep from pathlib import Path from shutil import copyfileobj -from urllib.error import HTTPError +from urllib.error import HTTPError, URLError class AssetError(Exception): def __init__(self, asset, msg, transient=False): @@ -171,6 +171,14 @@ def fetch(self): raise AssetError(self, "Unable to download: " "HTTP error %d" % e.code) continue + except URLError as e: + # This is typically a network/service level error + # eg urlopen error [Errno 110] Connection timed out> + tmp_cache_file.unlink() + 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) except Exception as e: tmp_cache_file.unlink() raise AssetError(self, "Unable to download: %s" % e) From 4effdc3c39f93e65396b46cc8432bbb37a8219e8 Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Thu, 4 Sep 2025 18:05:52 +0800 Subject: [PATCH 0624/1794] tests/functional/arm: Update test ASPEED SDK v03.02 for AST1030 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Message-ID: <20250904100556.1729604-2-kane_chen@aspeedtech.com> Signed-off-by: Thomas Huth --- tests/functional/arm/test_aspeed_ast1030.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/functional/arm/test_aspeed_ast1030.py b/tests/functional/arm/test_aspeed_ast1030.py index 77037f01793ce..42126b514ff9f 100755 --- a/tests/functional/arm/test_aspeed_ast1030.py +++ b/tests/functional/arm/test_aspeed_ast1030.py @@ -12,17 +12,17 @@ class AST1030Machine(LinuxKernelTest): - ASSET_ZEPHYR_3_00 = Asset( + ASSET_ZEPHYR_3_02 = Asset( ('https://github.com/AspeedTech-BMC' - '/zephyr/releases/download/v00.03.00/ast1030-evb-demo.zip'), - '37fe3ecd4a1b9d620971a15b96492a81093435396eeac69b6f3e384262ff555f') + '/zephyr/releases/download/v00.03.02/ast1030-evb-demo.zip'), + '1ec83caab3ddd5d09481772801be7210e222cb015ce22ec6fffb8a76956dcd4f') - def test_ast1030_zephyros_3_00(self): + def test_ast1030_zephyros_3_02(self): self.set_machine('ast1030-evb') - kernel_name = "ast1030-evb-demo/zephyr.elf" + kernel_name = "ast1030-evb-demo-3/zephyr.elf" kernel_file = self.archive_extract( - self.ASSET_ZEPHYR_3_00, member=kernel_name) + self.ASSET_ZEPHYR_3_02, member=kernel_name) self.vm.set_console() self.vm.add_args('-kernel', kernel_file, '-nographic') From bab31635d76fcbd9da8fff2c6c99906e86b449ec Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Thu, 4 Sep 2025 18:05:53 +0800 Subject: [PATCH 0625/1794] tests/functional/arm: Update test ASPEED SDK v09.07 for AST2500 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Message-ID: <20250904100556.1729604-3-kane_chen@aspeedtech.com> Signed-off-by: Thomas Huth --- tests/functional/arm/test_aspeed_ast2500.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/arm/test_aspeed_ast2500.py b/tests/functional/arm/test_aspeed_ast2500.py index 6923fe870170c..4fdd81e2f95b7 100755 --- a/tests/functional/arm/test_aspeed_ast2500.py +++ b/tests/functional/arm/test_aspeed_ast2500.py @@ -37,14 +37,14 @@ def test_arm_ast2500_evb_buildroot(self): self.do_test_arm_aspeed_buildroot_poweroff() - ASSET_SDK_V906_AST2500 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2500-default-obmc.tar.gz', - '542db84645b4efd8aed50385d7f4dd1caff379a987032311cfa7b563a3addb2a') + ASSET_SDK_V907_AST2500 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.07/ast2500-default-obmc.tar.gz', + 'd52bcc279a37c8d7679b3e4ef22cc77c36f0f6624c687b37334f798828afb077') def test_arm_ast2500_evb_sdk(self): self.set_machine('ast2500-evb') - self.archive_extract(self.ASSET_SDK_V906_AST2500) + self.archive_extract(self.ASSET_SDK_V907_AST2500) self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2500-default", "image-bmc")) From f57d0872694bc0de03bc6654db72f2ec7281f1ae Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Thu, 4 Sep 2025 18:05:54 +0800 Subject: [PATCH 0626/1794] tests/functional/arm: Update test ASPEED SDK v09.07 for AST2600 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Message-ID: <20250904100556.1729604-4-kane_chen@aspeedtech.com> Signed-off-by: Thomas Huth --- tests/functional/arm/test_aspeed_ast2600.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/arm/test_aspeed_ast2600.py b/tests/functional/arm/test_aspeed_ast2600.py index fdae4c939d8cc..129695ca4ec7f 100755 --- a/tests/functional/arm/test_aspeed_ast2600.py +++ b/tests/functional/arm/test_aspeed_ast2600.py @@ -97,14 +97,14 @@ def test_arm_ast2600_evb_buildroot_tpm(self): self.do_test_arm_aspeed_buildroot_poweroff() - ASSET_SDK_V906_AST2600 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2600-default-obmc.tar.gz', - '768d76e247896ad78c154b9cff4f766da2ce65f217d620b286a4a03a8a4f68f5') + ASSET_SDK_V907_AST2600 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.07/ast2600-default-obmc.tar.gz', + 'cb6c08595bcbba1672ce716b068ba4e48eda1ed9abe78a07b30392ba2278feba') def test_arm_ast2600_evb_sdk(self): self.set_machine('ast2600-evb') - self.archive_extract(self.ASSET_SDK_V906_AST2600) + self.archive_extract(self.ASSET_SDK_V907_AST2600) self.vm.add_args('-device', 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test') From 8cdcabe129cdd3270fd5c21708833464396fbf9b Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Thu, 4 Sep 2025 18:05:55 +0800 Subject: [PATCH 0627/1794] tests/functional/arm: Update test ASPEED SDK v09.07 for AST2700 vbootrom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Message-ID: <20250904100556.1729604-5-kane_chen@aspeedtech.com> Signed-off-by: Thomas Huth --- tests/functional/aarch64/test_aspeed_ast2700.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/functional/aarch64/test_aspeed_ast2700.py b/tests/functional/aarch64/test_aspeed_ast2700.py index d02dc7991c1a2..8a08bc46888eb 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700.py +++ b/tests/functional/aarch64/test_aspeed_ast2700.py @@ -54,6 +54,10 @@ def verify_openbmc_boot_and_login(self, name): 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2700-default-obmc.tar.gz', 'f1d53e0be8a404ecce3e105f72bc50fa4e090ad13160ffa91b10a6e0233a9dc6') + ASSET_SDK_V907_AST2700A1_VBOOROM = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.07/ast2700-default-obmc.tar.gz', + '6e9e0c4b13e0f26040eca3f4a7f17cf09fc0f5c37c820500ff79370cc3c44add') + def do_ast2700_i2c_test(self): exec_command_and_wait_for_pattern(self, 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-1/device/new_device ', @@ -127,10 +131,10 @@ def test_aarch64_ast2700a1_evb_sdk_v09_06(self): self.verify_openbmc_boot_and_login('ast2700-default') self.do_ast2700_i2c_test() - def test_aarch64_ast2700a1_evb_sdk_vbootrom_v09_06(self): + def test_aarch64_ast2700a1_evb_sdk_vbootrom_v09_07(self): self.set_machine('ast2700a1-evb') - self.archive_extract(self.ASSET_SDK_V906_AST2700A1) + self.archive_extract(self.ASSET_SDK_V907_AST2700A1_VBOOROM) self.start_ast2700_test_vbootrom('ast2700-default') self.verify_vbootrom_firmware_flow() self.verify_openbmc_boot_and_login('ast2700-default') From 8e22db01a1f45b108f55860514f8896952af550e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 8 Sep 2025 20:08:58 +0100 Subject: [PATCH 0628/1794] gitlab: replace avocado results files with meson results files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'results.xml' file and 'test-results' directory were both outputs of the avovcado test runner. Since we're now using meson with the new functional test framework, we must reference meson results files as the CI artifacts. Signed-off-by: Daniel P. Berrangé Reviewed-by: Alex Bennée Message-ID: <20250908190901.3571859-2-berrange@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/buildtest-template.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 038c3c9540ae5..4672298214960 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -104,11 +104,10 @@ when: always expire_in: 7 days paths: - - build/tests/results/latest/results.xml - - build/tests/results/latest/test-results + - build/meson-logs/testlog.txt - build/tests/functional/*/*/*.log reports: - junit: build/tests/results/latest/results.xml + junit: build/meson-logs/testlog.junit.xml before_script: - export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1 - export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache From ce1a7cd4cf090e21e0929566400fb759752e28c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 8 Sep 2025 20:08:59 +0100 Subject: [PATCH 0629/1794] gitlab: always include entire of meson-logs directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are files besides testlog.txt that may be useful as published CI artifacts. Signed-off-by: Daniel P. Berrangé Reviewed-by: Alex Bennée Message-ID: <20250908190901.3571859-3-berrange@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/buildtest-template.yml | 4 ++-- .gitlab-ci.d/buildtest.yml | 2 +- .gitlab-ci.d/crossbuild-template.yml | 2 +- .gitlab-ci.d/windows.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 4672298214960..4cc5b20790fd1 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -88,7 +88,7 @@ when: always expire_in: 7 days paths: - - build/meson-logs/testlog.txt + - build/meson-logs reports: junit: build/meson-logs/testlog.junit.xml @@ -104,7 +104,7 @@ when: always expire_in: 7 days paths: - - build/meson-logs/testlog.txt + - build/meson-logs - build/tests/functional/*/*/*.log reports: junit: build/meson-logs/testlog.junit.xml diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index d888a60063715..778289267f1ff 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -613,7 +613,7 @@ gcov: when: always expire_in: 2 days paths: - - build/meson-logs/testlog.txt + - build/meson-logs reports: junit: build/meson-logs/testlog.junit.xml coverage_report: diff --git a/.gitlab-ci.d/crossbuild-template.yml b/.gitlab-ci.d/crossbuild-template.yml index 303943f818f75..7e70376cfcc27 100644 --- a/.gitlab-ci.d/crossbuild-template.yml +++ b/.gitlab-ci.d/crossbuild-template.yml @@ -128,6 +128,6 @@ when: always expire_in: 7 days paths: - - build/meson-logs/testlog.txt + - build/meson-logs reports: junit: build/meson-logs/testlog.junit.xml diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index beac39e5bde81..f14e9ca1341ff 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -24,7 +24,7 @@ msys2-64bit: name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" expire_in: 7 days paths: - - build/meson-logs/testlog.txt + - build/meson-logs reports: junit: "build/meson-logs/testlog.junit.xml" before_script: From c65b3d49b96f2d2da30dba5fb96e07f79e73bd88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 8 Sep 2025 20:09:00 +0100 Subject: [PATCH 0630/1794] gitlab: include all junit XML files from meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The junit XML file produced by meson does not always have the name 'testlog.junit.xml' - in the case of 'make check-functional' there is a 'testlog-thorough.junit.xml' file too. Improve CI debugging robustness by capturing all junit files that meson produces. Signed-off-by: Daniel P. Berrangé Reviewed-by: Alex Bennée Message-ID: <20250908190901.3571859-4-berrange@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/buildtest-template.yml | 4 ++-- .gitlab-ci.d/buildtest.yml | 2 +- .gitlab-ci.d/crossbuild-template.yml | 2 +- .gitlab-ci.d/custom-runners.yml | 2 +- .gitlab-ci.d/windows.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 4cc5b20790fd1..308490a35acdb 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -90,7 +90,7 @@ paths: - build/meson-logs reports: - junit: build/meson-logs/testlog.junit.xml + junit: build/meson-logs/*.junit.xml .functional_test_job_template: extends: .common_test_job_template @@ -107,7 +107,7 @@ - build/meson-logs - build/tests/functional/*/*/*.log reports: - junit: build/meson-logs/testlog.junit.xml + junit: build/meson-logs/*.junit.xml before_script: - export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1 - export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 778289267f1ff..83c2867295afd 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -615,7 +615,7 @@ gcov: paths: - build/meson-logs reports: - junit: build/meson-logs/testlog.junit.xml + junit: build/meson-logs/*.junit.xml coverage_report: coverage_format: cobertura path: build/coverage.xml diff --git a/.gitlab-ci.d/crossbuild-template.yml b/.gitlab-ci.d/crossbuild-template.yml index 7e70376cfcc27..58136d06e4adf 100644 --- a/.gitlab-ci.d/crossbuild-template.yml +++ b/.gitlab-ci.d/crossbuild-template.yml @@ -130,4 +130,4 @@ paths: - build/meson-logs reports: - junit: build/meson-logs/testlog.junit.xml + junit: build/meson-logs/*.junit.xml diff --git a/.gitlab-ci.d/custom-runners.yml b/.gitlab-ci.d/custom-runners.yml index 1aa3c60efe991..2d493f70f7aab 100644 --- a/.gitlab-ci.d/custom-runners.yml +++ b/.gitlab-ci.d/custom-runners.yml @@ -26,7 +26,7 @@ - build/build.ninja - build/meson-logs reports: - junit: build/meson-logs/testlog.junit.xml + junit: build/meson-logs/*.junit.xml include: - local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml' diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index f14e9ca1341ff..1e6a01bd9ac87 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -26,7 +26,7 @@ msys2-64bit: paths: - build/meson-logs reports: - junit: "build/meson-logs/testlog.junit.xml" + junit: build/meson-logs/*.junit.xml before_script: - Write-Output "Acquiring msys2.exe installer at $(Get-Date -Format u)" - If ( !(Test-Path -Path msys64\var\cache ) ) { From f547894f68f9b18e6f3e4404b0a2e8ad013191f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 8 Sep 2025 20:09:01 +0100 Subject: [PATCH 0631/1794] gitlab: prevent duplicated meson log artifacts in test jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The build jobs will populate build/meson-logs/ with various files that are added as artifacts. The test jobs preserve the state of the build jobs, so we must delete any pre-existing logs to prevent confusion from duplicate artifacts. Signed-off-by: Daniel P. Berrangé Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-ID: <20250908190901.3571859-5-berrange@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/buildtest-template.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 308490a35acdb..d866cb12bb1ee 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -83,6 +83,10 @@ .native_test_job_template: extends: .common_test_job_template + before_script: + # Prevent logs from the build job that run earlier + # from being duplicated in the test job artifacts + - rm -f build/meson-logs/* artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" when: always @@ -111,6 +115,9 @@ before_script: - export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1 - export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache + # Prevent logs from the build job that run earlier + # from being duplicated in the test job artifacts + - rm -f build/meson-logs/* after_script: - cd build - du -chs ${CI_PROJECT_DIR}/*-cache From 595ba81145e76dbbcae700fce39253509172f9ff Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 3 Sep 2025 22:19:29 +0200 Subject: [PATCH 0632/1794] tests/functional: return output from cmd.py helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests might want to look at the whole output from a command execution, as well as just logging it. Add support for this. Signed-off-by: John Levon Reviewed-by: Daniel P. Berrangé Message-ID: <20250903201931.168317-2-john.levon@nutanix.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/cmd.py | 53 +++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/tests/functional/qemu_test/cmd.py b/tests/functional/qemu_test/cmd.py index dc5f422b77db5..c19dfc577ff29 100644 --- a/tests/functional/qemu_test/cmd.py +++ b/tests/functional/qemu_test/cmd.py @@ -45,6 +45,9 @@ def is_readable_executable_file(path): # If end of line is seen, with neither @success or @failure # return False # +# In both cases, also return the contents of the line (in bytes) +# up to that point. +# # If @failure is seen, then mark @test as failed def _console_read_line_until_match(test, vm, success, failure): msg = bytes([]) @@ -76,10 +79,23 @@ def _console_read_line_until_match(test, vm, success, failure): except: console_logger.debug(msg) - return done + return done, msg def _console_interaction(test, success_message, failure_message, send_string, keep_sending=False, vm=None): + """ + Interact with the console until either message is seen. + + :param success_message: if this message appears, finish interaction + :param failure_message: if this message appears, test fails + :param send_string: a string to send to the console before trying + to read a new line + :param keep_sending: keep sending the send string each time + :param vm: the VM to interact with + + :return: The collected output (in bytes form). + """ + assert not keep_sending or send_string assert success_message or send_string @@ -101,6 +117,8 @@ def _console_interaction(test, success_message, failure_message, if failure_message is not None: failure_message_b = failure_message.encode() + out = bytes([]) + while True: if send_string: vm.console_socket.sendall(send_string.encode()) @@ -113,11 +131,17 @@ def _console_interaction(test, success_message, failure_message, break continue - if _console_read_line_until_match(test, vm, - success_message_b, - failure_message_b): + done, line = _console_read_line_until_match(test, vm, + success_message_b, + failure_message_b) + + out += line + + if done: break + return out + def interrupt_interactive_console_until_pattern(test, success_message, failure_message=None, interrupt_string='\r'): @@ -140,10 +164,12 @@ def interrupt_interactive_console_until_pattern(test, success_message, :param failure_message: if this message appears, test fails :param interrupt_string: a string to send to the console before trying to read a new line + + :return: The collected output (in bytes form). """ assert success_message - _console_interaction(test, success_message, failure_message, - interrupt_string, True) + return _console_interaction(test, success_message, failure_message, + interrupt_string, True) def wait_for_console_pattern(test, success_message, failure_message=None, vm=None): @@ -155,9 +181,12 @@ def wait_for_console_pattern(test, success_message, failure_message=None, :type test: :class:`qemu_test.QemuSystemTest` :param success_message: if this message appears, test succeeds :param failure_message: if this message appears, test fails + + :return: The collected output (in bytes form). """ assert success_message - _console_interaction(test, success_message, failure_message, None, vm=vm) + return _console_interaction(test, success_message, failure_message, + None, vm=vm) def exec_command(test, command): """ @@ -168,8 +197,10 @@ def exec_command(test, command): :type test: :class:`qemu_test.QemuSystemTest` :param command: the command to send :type command: str + + :return: The collected output (in bytes form). """ - _console_interaction(test, None, None, command + '\r') + return _console_interaction(test, None, None, command + '\r') def exec_command_and_wait_for_pattern(test, command, success_message, failure_message=None): @@ -184,9 +215,13 @@ def exec_command_and_wait_for_pattern(test, command, :param command: the command to send :param success_message: if this message appears, test succeeds :param failure_message: if this message appears, test fails + + :return: The collected output (in bytes form). """ assert success_message - _console_interaction(test, success_message, failure_message, command + '\r') + + return _console_interaction(test, success_message, failure_message, + command + '\r') def get_qemu_img(test): test.log.debug('Looking for and selecting a qemu-img binary') From ba87a01e1af04599e1952cacfb7eb25f06e15da5 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 3 Sep 2025 22:19:30 +0200 Subject: [PATCH 0633/1794] tests/functional: add vm param to cmd.py helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the "vm" parameter of wait_for_console_pattern() to all the other utility functions; this allows them to be used on a VM other than test.vm. Signed-off-by: John Levon Reviewed-by: Daniel P. Berrangé Message-ID: <20250903201931.168317-3-john.levon@nutanix.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/cmd.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/functional/qemu_test/cmd.py b/tests/functional/qemu_test/cmd.py index c19dfc577ff29..8069c89730b6b 100644 --- a/tests/functional/qemu_test/cmd.py +++ b/tests/functional/qemu_test/cmd.py @@ -144,7 +144,8 @@ def _console_interaction(test, success_message, failure_message, def interrupt_interactive_console_until_pattern(test, success_message, failure_message=None, - interrupt_string='\r'): + interrupt_string='\r', + vm=None): """ Keep sending a string to interrupt a console prompt, while logging the console output. Typical use case is to break a boot loader prompt, such: @@ -164,12 +165,13 @@ def interrupt_interactive_console_until_pattern(test, success_message, :param failure_message: if this message appears, test fails :param interrupt_string: a string to send to the console before trying to read a new line + :param vm: VM to use :return: The collected output (in bytes form). """ assert success_message return _console_interaction(test, success_message, failure_message, - interrupt_string, True) + interrupt_string, True, vm=vm) def wait_for_console_pattern(test, success_message, failure_message=None, vm=None): @@ -181,6 +183,7 @@ def wait_for_console_pattern(test, success_message, failure_message=None, :type test: :class:`qemu_test.QemuSystemTest` :param success_message: if this message appears, test succeeds :param failure_message: if this message appears, test fails + :param vm: VM to use :return: The collected output (in bytes form). """ @@ -188,7 +191,7 @@ def wait_for_console_pattern(test, success_message, failure_message=None, return _console_interaction(test, success_message, failure_message, None, vm=vm) -def exec_command(test, command): +def exec_command(test, command, vm=None): """ Send a command to a console (appending CRLF characters), while logging the content. @@ -196,14 +199,16 @@ def exec_command(test, command): :param test: a test containing a VM. :type test: :class:`qemu_test.QemuSystemTest` :param command: the command to send + :param vm: VM to use :type command: str :return: The collected output (in bytes form). """ - return _console_interaction(test, None, None, command + '\r') + return _console_interaction(test, None, None, command + '\r', vm=vm) def exec_command_and_wait_for_pattern(test, command, - success_message, failure_message=None): + success_message, failure_message=None, + vm=None): """ Send a command to a console (appending CRLF characters), then wait for success_message to appear on the console, while logging the. @@ -215,13 +220,14 @@ def exec_command_and_wait_for_pattern(test, command, :param command: the command to send :param success_message: if this message appears, test succeeds :param failure_message: if this message appears, test fails + :param vm: VM to use :return: The collected output (in bytes form). """ assert success_message return _console_interaction(test, success_message, failure_message, - command + '\r') + command + '\r', vm=vm) def get_qemu_img(test): test.log.debug('Looking for and selecting a qemu-img binary') From fb352b3c85a990ba81e41e4e8c7eb53ccc3059a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 8 Sep 2025 14:57:19 +0100 Subject: [PATCH 0634/1794] tests/functional: fix infinite loop on console EOF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'recv' method will return an empty byte array, not None, when the socket has EOF. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Alex Bennée Message-ID: <20250908135722.3375580-2-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/cmd.py b/tests/functional/qemu_test/cmd.py index 8069c89730b6b..f544566245ba3 100644 --- a/tests/functional/qemu_test/cmd.py +++ b/tests/functional/qemu_test/cmd.py @@ -54,7 +54,7 @@ def _console_read_line_until_match(test, vm, success, failure): done = False while True: c = vm.console_socket.recv(1) - if c is None: + if not c: done = True test.fail( f"EOF in console, expected '{success}'") From e5381b041037f3918163f2b7f82415aee80cd64e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 8 Sep 2025 14:57:20 +0100 Subject: [PATCH 0635/1794] tests/functional: avoid duplicate messages on failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some scenarios the same tests is mentioned in both the 'res.results.errors' and 'res.results.failures' array returned by unittest.main(). This was seen when the 'tearDown' method raised an exception. In such a case, we printed out the same information about where to find a log file twice for each test. Track which tests we have already reported on, to avoid the duplication. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20250908135722.3375580-3-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index fbeb171058721..82a7724404b50 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -251,13 +251,14 @@ def main(): test_output_log = pycotap.LogMode.LogToError) res = unittest.main(module = None, testRunner = tr, exit = False, argv=[sys.argv[0], path] + sys.argv[1:]) + failed = {} for (test, message) in res.result.errors + res.result.failures: - - if hasattr(test, "log_filename"): + if hasattr(test, "log_filename") and not test.id() in failed: print('More information on ' + test.id() + ' could be found here:' '\n %s' % test.log_filename, file=sys.stderr) if hasattr(test, 'console_log_name'): print(' %s' % test.console_log_name, file=sys.stderr) + failed[test.id()] = True sys.exit(not res.result.wasSuccessful()) From 2ee187dcb55aee7f70b514db772090649235de70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 8 Sep 2025 14:57:21 +0100 Subject: [PATCH 0636/1794] tests/functional: avoid tearDown failure when QEMU dies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a QEMU process under test dies unexpectedly, the 'shutdown' method may well raise an exception. This causes the tearDown method to fail, which means any later cleanup code fails to get run. Most notably the log handlers don't get removed so the base.log file from an earlier test will get polluted with messages from any subsequent tests. The tearDown failure also results in pages of exceptions printed on the console, which obscures the real failure message / trace printed by the test. Ignore any shutdown failures in the tearDown method, since any test which cares about clean shutdown should have already cleaned up any running VMs. The tearDown method is just there as a safety net to cleanup resources. The base.log file will still containing log messages from the failed 'vm.shutdown' call too. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20250908135722.3375580-4-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 82a7724404b50..faa0a4f0db23e 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -404,7 +404,10 @@ def set_vm_arg(self, arg, value): def tearDown(self): for vm in self._vms.values(): - vm.shutdown() + try: + vm.shutdown() + except Exception as ex: + self.log.error("Failed to teardown VM: %s" % ex) logging.getLogger('console').removeHandler(self._console_log_fh) self._console_log_fh.close() super().tearDown() From 2fc170bcdc4d2f05534c68572b4f72a7d18c2119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 8 Sep 2025 14:57:22 +0100 Subject: [PATCH 0637/1794] tests/functional: purge scratch dir on test startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test suite purges the scratch dir in the tearDown method, but if python crashes (or is non-gracefully killed) this won't get run. Also the user can set QEMU_TEST_KEEP_SCRATCH to disable cleanup. Purging the scratch dir on startup ensures that tests always run from a clean state. Reported-by: Peter Maydell Signed-off-by: Daniel P. Berrangé Reviewed-by: Alex Bennée Message-ID: <20250908135722.3375580-5-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index faa0a4f0db23e..2c0abde395785 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -205,6 +205,10 @@ def setUp(self): self.outputdir = self.build_file('tests', 'functional', self.arch, self.id()) self.workdir = os.path.join(self.outputdir, 'scratch') + if os.path.exists(self.workdir): + # Purge as safety net in case of unclean termination of + # previous test, or use of QEMU_TEST_KEEP_SCRATCH + shutil.rmtree(self.workdir) os.makedirs(self.workdir, exist_ok=True) self.log_filename = self.log_file('base.log') From 769acb2a1e47b97ada8e0db6ff73e303b23764d8 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 9 Sep 2025 14:37:47 +0200 Subject: [PATCH 0638/1794] tests/functional/aarch64: Fix assets of test_hotplug_pci The old bookworm URLs don't work anymore, resulting in a 404 error now. Let's update the test to Debian Trixie to get it going again. Signed-off-by: Thomas Huth Signed-off-by: Peter Maydell --- tests/functional/aarch64/test_hotplug_pci.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/functional/aarch64/test_hotplug_pci.py b/tests/functional/aarch64/test_hotplug_pci.py index 0c67991b89e3a..bf677204319e5 100755 --- a/tests/functional/aarch64/test_hotplug_pci.py +++ b/tests/functional/aarch64/test_hotplug_pci.py @@ -15,14 +15,14 @@ class HotplugPCI(LinuxKernelTest): ASSET_KERNEL = Asset( - ('https://ftp.debian.org/debian/dists/bookworm/main/installer-arm64/' - '20230607+deb12u11/images/netboot/debian-installer/arm64/linux'), - 'd92a60392ce1e379ca198a1a820899f8f0d39a62d047c41ab79492f81541a9d9') + ('https://ftp.debian.org/debian/dists/trixie/main/installer-arm64/' + '20250803/images/netboot/debian-installer/arm64/linux'), + '93a6e4f9627d759375d28f863437a86a0659e125792a435f8e526dda006b7d5e') ASSET_INITRD = Asset( - ('https://ftp.debian.org/debian/dists/bookworm/main/installer-arm64/' - '20230607+deb12u11/images/netboot/debian-installer/arm64/initrd.gz'), - '9f817f76951f3237bca8216bee35267bfb826815687f4b2fcdd5e6c2a917790c') + ('https://ftp.debian.org/debian/dists/trixie/main/installer-arm64/' + '20250803/images/netboot/debian-installer/arm64/initrd.gz'), + 'f6c78af7078ca67638ef3a50c926cd3c1485673243f8b37952e6bd854d6ba007') def test_hotplug_pci(self): From c75847f9eb8fb09dde0c0f4fa4710c6bde11f5a9 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 3 Sep 2025 10:29:32 -0400 Subject: [PATCH 0639/1794] memory: Fix addr/len for flatview_access_allowed() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit flatview_access_allowed() should pass in the address offset of the memory region, rather than the global address space. Shouldn't be a major issue yet, since the addr is only used in an error log. Cc: Philippe Mathieu-Daudé Fixes: 3ab6fdc91b ("softmmu/physmem: Introduce MemTxAttrs::memory field and MEMTX_ACCESS_ERROR") Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: David Hildenbrand Link: https://lore.kernel.org/r/20250903142932.1038765-1-peterx@redhat.com Signed-off-by: Peter Xu --- system/physmem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index 311011156c7db..ddd58e9eb8243 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3027,7 +3027,7 @@ static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, l = len; mr = flatview_translate(fv, addr, &mr_addr, &l, true, attrs); - if (!flatview_access_allowed(mr, attrs, addr, len)) { + if (!flatview_access_allowed(mr, attrs, mr_addr, l)) { return MEMTX_ACCESS_ERROR; } return flatview_write_continue(fv, addr, attrs, buf, len, @@ -3118,7 +3118,7 @@ static MemTxResult flatview_read(FlatView *fv, hwaddr addr, l = len; mr = flatview_translate(fv, addr, &mr_addr, &l, false, attrs); - if (!flatview_access_allowed(mr, attrs, addr, len)) { + if (!flatview_access_allowed(mr, attrs, mr_addr, l)) { return MEMTX_ACCESS_ERROR; } return flatview_read_continue(fv, addr, attrs, buf, len, From ac7a892fd37ce4427d390ca8556203c9a2eb9d38 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 21 Aug 2025 12:59:02 -0400 Subject: [PATCH 0640/1794] memory: Fix leaks due to owner-shared MRs circular references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, QEMU refcounts the MR by always taking it from the owner. It's common that one object will have multiple MR objects embeded in the object itself. All the MRs in this case share the same lifespan of the owner object. It's also common that in the instance_init() of an object, MR A can be a container of MR B, C, D, by using memory_region_add_subregion*() set of memory region APIs. Now we have a circular reference issue, as when adding subregions for MR A, we essentially incremented the owner's refcount within the instance_init(), meaning the object will be self-boosted and its refcount can never go down to zero if the MRs won't get detached properly before object's finalize(). Delete subregions within object's finalize() won't work either, because finalize() will be invoked only if the refcount goes to zero first. What is worse, object_finalize() will do object_property_del_all() first before object_deinit(). Since embeded MRs will be properties of the owner object, it means they'll be freed _before_ the owner's finalize(). To fix that, teach memory API to stop refcount on MRs that share the same owner. Because if they share the lifecycle of the owner, then they share the same lifecycle between themselves, hence the refcount doesn't help but only introduce troubles. Meanwhile, allow auto-detachments of MRs during finalize() of MRs even against its container, as long as they belong to the same owner. The latter is needed because now it's possible to have MRs' finalize() happen in any order when they share the same lifespan with a same owner. In this case, we should allow finalize() to happen in any order of either the parent or child MR. Loose the mr->container check in MR's finalize() to allow auto-detach. Double check it shares the same owner. Proper document this behavior in code. This patch is heavily based on the work done by Akihiko Odaki: https://lore.kernel.org/r/CAFEAcA8DV40fGsci76r4yeP1P-SP_QjNRDD2OzPxjx5wRs0GEg@mail.gmail.com Cc: Akihiko Odaki Cc: Paolo Bonzini Reviewed-by: Clément Mathieu--Drif Reviewed-by: Peter Maydell Tested-by: Peter Maydell Link: https://lore.kernel.org/r/20250826221750.285242-1-peterx@redhat.com Signed-off-by: Peter Xu --- docs/devel/memory.rst | 7 +++++-- system/memory.c | 46 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/docs/devel/memory.rst b/docs/devel/memory.rst index 57fb2aec76e06..42d3ca29c431e 100644 --- a/docs/devel/memory.rst +++ b/docs/devel/memory.rst @@ -158,8 +158,11 @@ ioeventfd) can be changed during the region lifecycle. They take effect as soon as the region is made visible. This can be immediately, later, or never. -Destruction of a memory region happens automatically when the owner -object dies. +Destruction of a memory region happens automatically when the owner object +dies. When there are multiple memory regions under the same owner object, +the memory API will guarantee all memory regions will be properly detached +and finalized one by one. The order in which memory regions will be +finalized is not guaranteed. If however the memory region is part of a dynamically allocated data structure, you should call object_unparent() to destroy the memory region diff --git a/system/memory.c b/system/memory.c index 44701c465c1e9..cf8cad6961156 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1796,16 +1796,37 @@ static void memory_region_finalize(Object *obj) { MemoryRegion *mr = MEMORY_REGION(obj); - assert(!mr->container); - - /* We know the region is not visible in any address space (it - * does not have a container and cannot be a root either because - * it has no references, so we can blindly clear mr->enabled. - * memory_region_set_enabled instead could trigger a transaction - * and cause an infinite loop. + /* + * Each memory region (that can be freed) must have an owner, and it + * always has the same lifecycle of its owner. It means when reaching + * here, the memory region's owner's refcount is zero. + * + * Here it is possible that the MR has: + * + * (1) mr->container set, which means this MR is a subregion of a + * container MR. In this case they must share the same owner as the + * container (otherwise the container should have kept a refcount + * of this MR's owner). + * + * (2) mr->subregions non-empty, which means this MR is a container of + * one or more other MRs (which might have the the owner as this + * MR, or a different owner). + * + * We know the MR, or any MR that is attached to this one as either + * container or children, is not visible in any address space, because + * otherwise the address space should have taken at least one refcount + * of this MR's owner. So we can blindly clear mr->enabled. + * + * memory_region_set_enabled instead could trigger a transaction and + * cause an infinite loop. */ mr->enabled = false; memory_region_transaction_begin(); + if (mr->container) { + /* Must share the owner; see above comments */ + assert(mr->container->owner == mr->owner); + memory_region_del_subregion(mr->container, mr); + } while (!QTAILQ_EMPTY(&mr->subregions)) { MemoryRegion *subregion = QTAILQ_FIRST(&mr->subregions); memory_region_del_subregion(mr, subregion); @@ -2640,7 +2661,10 @@ static void memory_region_update_container_subregions(MemoryRegion *subregion) memory_region_transaction_begin(); - memory_region_ref(subregion); + if (mr->owner != subregion->owner) { + memory_region_ref(subregion); + } + QTAILQ_FOREACH(other, &mr->subregions, subregions_link) { if (subregion->priority >= other->priority) { QTAILQ_INSERT_BEFORE(other, subregion, subregions_link); @@ -2698,7 +2722,11 @@ void memory_region_del_subregion(MemoryRegion *mr, assert(alias->mapped_via_alias >= 0); } QTAILQ_REMOVE(&mr->subregions, subregion, subregions_link); - memory_region_unref(subregion); + + if (mr->owner != subregion->owner) { + memory_region_unref(subregion); + } + memory_region_update_pending |= mr->enabled && subregion->enabled; memory_region_transaction_commit(); } From 2d26741fc5170e51621eb365a34460fadb5f969e Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 22 Jul 2022 14:23:37 -0400 Subject: [PATCH 0641/1794] python: backport 'Change error classes to have better repr methods' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By passing all of the arguments to the base class and overriding the __str__ method when we want a different "human readable" message that isn't just printing the list of arguments, we can ensure that all custom error classes have a reasonable __repr__ implementation. In the case of ExecuteError, the pseudo-field that isn't actually correlated to an input argument can be re-imagined as a read-only property; this forces consistency in the class and makes the repr output more obviously correct. Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@afdb7893f3b34212da4259b7202973f9a8cb85b3 Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/error.py | 7 +++++-- python/qemu/qmp/message.py | 12 ++++++------ python/qemu/qmp/protocol.py | 7 +++++-- python/qemu/qmp/qmp_client.py | 20 +++++++++++++------- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/python/qemu/qmp/error.py b/python/qemu/qmp/error.py index 24ba4d505410b..c87b078f620ad 100644 --- a/python/qemu/qmp/error.py +++ b/python/qemu/qmp/error.py @@ -44,7 +44,10 @@ class ProtocolError(QMPError): :param error_message: Human-readable string describing the error. """ - def __init__(self, error_message: str): - super().__init__(error_message) + def __init__(self, error_message: str, *args: object): + super().__init__(error_message, *args) #: Human-readable error message, without any prefix. self.error_message: str = error_message + + def __str__(self) -> str: + return self.error_message diff --git a/python/qemu/qmp/message.py b/python/qemu/qmp/message.py index f76ccc9074670..c2e9dd0dd5429 100644 --- a/python/qemu/qmp/message.py +++ b/python/qemu/qmp/message.py @@ -178,15 +178,15 @@ class DeserializationError(ProtocolError): :param raw: The raw `bytes` that prompted the failure. """ def __init__(self, error_message: str, raw: bytes): - super().__init__(error_message) + super().__init__(error_message, raw) #: The raw `bytes` that were not understood as JSON. self.raw: bytes = raw def __str__(self) -> str: - return "\n".join([ + return "\n".join(( super().__str__(), f" raw bytes were: {str(self.raw)}", - ]) + )) class UnexpectedTypeError(ProtocolError): @@ -197,13 +197,13 @@ class UnexpectedTypeError(ProtocolError): :param value: The deserialized JSON value that wasn't an object. """ def __init__(self, error_message: str, value: object): - super().__init__(error_message) + super().__init__(error_message, value) #: The JSON value that was expected to be an object. self.value: object = value def __str__(self) -> str: strval = json.dumps(self.value, indent=2) - return "\n".join([ + return "\n".join(( super().__str__(), f" json value was: {strval}", - ]) + )) diff --git a/python/qemu/qmp/protocol.py b/python/qemu/qmp/protocol.py index a4ffdfad51bc9..86e588881b7c5 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -80,7 +80,7 @@ class ConnectError(QMPError): :param exc: The root-cause exception. """ def __init__(self, error_message: str, exc: Exception): - super().__init__(error_message) + super().__init__(error_message, exc) #: Human-readable error string self.error_message: str = error_message #: Wrapped root cause exception @@ -108,11 +108,14 @@ class StateError(QMPError): """ def __init__(self, error_message: str, state: Runstate, required: Runstate): - super().__init__(error_message) + super().__init__(error_message, state, required) self.error_message = error_message self.state = state self.required = required + def __str__(self) -> str: + return self.error_message + F = TypeVar('F', bound=Callable[..., Any]) # pylint: disable=invalid-name diff --git a/python/qemu/qmp/qmp_client.py b/python/qemu/qmp/qmp_client.py index 2a817f9db33b4..a87fb565ab508 100644 --- a/python/qemu/qmp/qmp_client.py +++ b/python/qemu/qmp/qmp_client.py @@ -41,7 +41,7 @@ class _WrappedProtocolError(ProtocolError): :param exc: The root-cause exception. """ def __init__(self, error_message: str, exc: Exception): - super().__init__(error_message) + super().__init__(error_message, exc) self.exc = exc def __str__(self) -> str: @@ -76,15 +76,21 @@ class ExecuteError(QMPError): """ def __init__(self, error_response: ErrorResponse, sent: Message, received: Message): - super().__init__(error_response.error.desc) + super().__init__(error_response, sent, received) #: The sent `Message` that caused the failure self.sent: Message = sent #: The received `Message` that indicated failure self.received: Message = received #: The parsed error response self.error: ErrorResponse = error_response - #: The QMP error class - self.error_class: str = error_response.error.class_ + + @property + def error_class(self) -> str: + """The QMP error class""" + return self.error.error.class_ + + def __str__(self) -> str: + return self.error.error.desc class ExecInterruptedError(QMPError): @@ -110,8 +116,8 @@ class _MsgProtocolError(ProtocolError): :param error_message: Human-readable string describing the error. :param msg: The QMP `Message` that caused the error. """ - def __init__(self, error_message: str, msg: Message): - super().__init__(error_message) + def __init__(self, error_message: str, msg: Message, *args: object): + super().__init__(error_message, msg, *args) #: The received `Message` that caused the error. self.msg: Message = msg @@ -150,7 +156,7 @@ class BadReplyError(_MsgProtocolError): :param sent: The message that was sent that prompted the error. """ def __init__(self, error_message: str, msg: Message, sent: Message): - super().__init__(error_message, msg) + super().__init__(error_message, msg, sent) #: The sent `Message` that caused the failure self.sent = sent From cb0e43804038e47bbaf0179ce67df678ec13a392 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 22 Jul 2022 15:42:23 -0400 Subject: [PATCH 0642/1794] python: backport 'EventListener: add __repr__ method' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the object is not stateful, this repr method prints what you'd expect. In cases where there are pending events, the output is augmented to illustrate that. The object itself has no idea if it's "active" or not, so it cannot convey that information. Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@8a6f2e136dae395fec8aa5fd77487cfe12d9e05e Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/events.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/python/qemu/qmp/events.py b/python/qemu/qmp/events.py index 6199776cc6647..6658349619279 100644 --- a/python/qemu/qmp/events.py +++ b/python/qemu/qmp/events.py @@ -497,6 +497,21 @@ def __init__( #: Optional, secondary event filter. self.event_filter: Optional[EventFilter] = event_filter + def __repr__(self) -> str: + args: List[str] = [] + if self.names: + args.append(f"names={self.names!r}") + if self.event_filter: + args.append(f"event_filter={self.event_filter!r}") + + if self._queue.qsize(): + state = f"" + else: + state = '' + + argstr = ", ".join(args) + return f"{type(self).__name__}{state}({argstr})" + @property def history(self) -> Tuple[Message, ...]: """ From 1e343714bfc06cc982e68a290f3809117d6dfcd0 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 3 May 2022 14:07:10 -0400 Subject: [PATCH 0643/1794] python: backport 'kick event queue on legacy event_pull()' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This corrects an oversight in qmp-shell operation where new events will not accumulate in the event queue when pressing "enter" with an empty command buffer, so no new events show up. Reported-by: Jag Raman Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@0443582d16cf9efd52b2c41a7b5be7af42c856cd Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/legacy.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py index 22a2b5616efa5..c8d0a29b56fe0 100644 --- a/python/qemu/qmp/legacy.py +++ b/python/qemu/qmp/legacy.py @@ -231,6 +231,9 @@ def pull_event(self, :return: The first available QMP event, or None. """ + # Kick the event loop to allow events to accumulate + self._sync(asyncio.sleep(0)) + if not wait: # wait is False/0: "do not wait, do not except." if self._qmp.events.empty(): From 094ded5227dc4e8dde2c78a9788bf6c90771ad85 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 22 Jul 2022 16:30:05 -0400 Subject: [PATCH 0644/1794] python: backport 'protocol: adjust logging name when changing client name' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The client name is mutable, so the logging name should also change to reflect it when it changes. Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@e10b73c633ce138ba30bc8beccd2ab31989eaf3d Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/protocol.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/python/qemu/qmp/protocol.py b/python/qemu/qmp/protocol.py index 86e588881b7c5..ec4762c567b32 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -217,10 +217,8 @@ class AsyncProtocol(Generic[T]): # ------------------------- def __init__(self, name: Optional[str] = None) -> None: - #: The nickname for this connection, if any. - self.name: Optional[str] = name - if self.name is not None: - self.logger = self.logger.getChild(self.name) + self._name: Optional[str] + self.name = name # stream I/O self._reader: Optional[StreamReader] = None @@ -257,6 +255,24 @@ def __repr__(self) -> str: tokens.append(f"runstate={self.runstate.name}") return f"<{cls_name} {' '.join(tokens)}>" + @property + def name(self) -> Optional[str]: + """ + The nickname for this connection, if any. + + This name is used for differentiating instances in debug output. + """ + return self._name + + @name.setter + def name(self, name: Optional[str]) -> None: + logger = logging.getLogger(__name__) + if name: + self.logger = logger.getChild(name) + else: + self.logger = logger + self._name = name + @property # @upper_half def runstate(self) -> Runstate: """The current `Runstate` of the connection.""" From f9d2e0a3bd7ba2a693a892881f91cf53fa90cc71 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 6 Jun 2023 13:19:11 -0400 Subject: [PATCH 0645/1794] python: backport 'drop Python3.6 workarounds' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the minimum version is 3.7, drop some of the 3.6-specific hacks we've been carrying. A single remaining compatibility hack concerning 3.6's lack of @asynccontextmanager is addressed in the following commit. Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@3e8e34e594cfc6b707e6f67959166acde4b421b8 Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/protocol.py | 13 ++--- python/qemu/qmp/qmp_tui.py | 8 +-- python/qemu/qmp/util.py | 107 ++---------------------------------- python/tests/protocol.py | 8 +-- 4 files changed, 17 insertions(+), 119 deletions(-) diff --git a/python/qemu/qmp/protocol.py b/python/qemu/qmp/protocol.py index ec4762c567b32..208bdec5c8953 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -36,13 +36,10 @@ from .error import QMPError from .util import ( bottom_half, - create_task, exception_summary, flush, - is_closing, pretty_traceback, upper_half, - wait_closed, ) @@ -682,8 +679,8 @@ async def _establish_session(self) -> None: reader_coro = self._bh_loop_forever(self._bh_recv_message, 'Reader') writer_coro = self._bh_loop_forever(self._bh_send_message, 'Writer') - self._reader_task = create_task(reader_coro) - self._writer_task = create_task(writer_coro) + self._reader_task = asyncio.create_task(reader_coro) + self._writer_task = asyncio.create_task(writer_coro) self._bh_tasks = asyncio.gather( self._reader_task, @@ -708,7 +705,7 @@ def _schedule_disconnect(self) -> None: if not self._dc_task: self._set_state(Runstate.DISCONNECTING) self.logger.debug("Scheduling disconnect.") - self._dc_task = create_task(self._bh_disconnect()) + self._dc_task = asyncio.create_task(self._bh_disconnect()) @upper_half async def _wait_disconnect(self) -> None: @@ -844,13 +841,13 @@ async def _bh_close_stream(self, error_pathway: bool = False) -> None: if not self._writer: return - if not is_closing(self._writer): + if not self._writer.is_closing(): self.logger.debug("Closing StreamWriter.") self._writer.close() self.logger.debug("Waiting for StreamWriter to close ...") try: - await wait_closed(self._writer) + await self._writer.wait_closed() except Exception: # pylint: disable=broad-except # It's hard to tell if the Stream is already closed or # not. Even if one of the tasks has failed, it may have diff --git a/python/qemu/qmp/qmp_tui.py b/python/qemu/qmp/qmp_tui.py index 2d9ebbd20bc7f..562be008d5e9d 100644 --- a/python/qemu/qmp/qmp_tui.py +++ b/python/qemu/qmp/qmp_tui.py @@ -40,7 +40,7 @@ from .message import DeserializationError, Message, UnexpectedTypeError from .protocol import ConnectError, Runstate from .qmp_client import ExecInterruptedError, QMPClient -from .util import create_task, pretty_traceback +from .util import pretty_traceback # The name of the signal that is used to update the history list @@ -225,7 +225,7 @@ def cb_send_to_server(self, raw_msg: str) -> None: """ try: msg = Message(bytes(raw_msg, encoding='utf-8')) - create_task(self._send_to_server(msg)) + asyncio.create_task(self._send_to_server(msg)) except (DeserializationError, UnexpectedTypeError) as err: raw_msg = format_json(raw_msg) logging.info('Invalid message: %s', err.error_message) @@ -246,7 +246,7 @@ def kill_app(self) -> None: Initiates killing of app. A bridge between asynchronous and synchronous code. """ - create_task(self._kill_app()) + asyncio.create_task(self._kill_app()) async def _kill_app(self) -> None: """ @@ -393,7 +393,7 @@ def run(self, debug: bool = False) -> None: handle_mouse=True, event_loop=event_loop) - create_task(self.manage_connection(), self.aloop) + self.aloop.create_task(self.manage_connection()) try: main_loop.run() except Exception as err: diff --git a/python/qemu/qmp/util.py b/python/qemu/qmp/util.py index ca6225e9cda04..0b3e781373d84 100644 --- a/python/qemu/qmp/util.py +++ b/python/qemu/qmp/util.py @@ -1,25 +1,15 @@ """ Miscellaneous Utilities -This module provides asyncio utilities and compatibility wrappers for -Python 3.6 to provide some features that otherwise become available in -Python 3.7+. - -Various logging and debugging utilities are also provided, such as -`exception_summary()` and `pretty_traceback()`, used primarily for -adding information into the logging stream. +This module provides asyncio and various logging and debugging +utilities, such as `exception_summary()` and `pretty_traceback()`, used +primarily for adding information into the logging stream. """ import asyncio import sys import traceback -from typing import ( - Any, - Coroutine, - Optional, - TypeVar, - cast, -) +from typing import TypeVar, cast T = TypeVar('T') @@ -79,95 +69,6 @@ def bottom_half(func: T) -> T: return func -# ------------------------------- -# Section: Compatibility Wrappers -# ------------------------------- - - -def create_task(coro: Coroutine[Any, Any, T], - loop: Optional[asyncio.AbstractEventLoop] = None - ) -> 'asyncio.Future[T]': - """ - Python 3.6-compatible `asyncio.create_task` wrapper. - - :param coro: The coroutine to execute in a task. - :param loop: Optionally, the loop to create the task in. - - :return: An `asyncio.Future` object. - """ - if sys.version_info >= (3, 7): - if loop is not None: - return loop.create_task(coro) - return asyncio.create_task(coro) # pylint: disable=no-member - - # Python 3.6: - return asyncio.ensure_future(coro, loop=loop) - - -def is_closing(writer: asyncio.StreamWriter) -> bool: - """ - Python 3.6-compatible `asyncio.StreamWriter.is_closing` wrapper. - - :param writer: The `asyncio.StreamWriter` object. - :return: `True` if the writer is closing, or closed. - """ - if sys.version_info >= (3, 7): - return writer.is_closing() - - # Python 3.6: - transport = writer.transport - assert isinstance(transport, asyncio.WriteTransport) - return transport.is_closing() - - -async def wait_closed(writer: asyncio.StreamWriter) -> None: - """ - Python 3.6-compatible `asyncio.StreamWriter.wait_closed` wrapper. - - :param writer: The `asyncio.StreamWriter` to wait on. - """ - if sys.version_info >= (3, 7): - await writer.wait_closed() - return - - # Python 3.6 - transport = writer.transport - assert isinstance(transport, asyncio.WriteTransport) - - while not transport.is_closing(): - await asyncio.sleep(0) - - # This is an ugly workaround, but it's the best I can come up with. - sock = transport.get_extra_info('socket') - - if sock is None: - # Our transport doesn't have a socket? ... - # Nothing we can reasonably do. - return - - while sock.fileno() != -1: - await asyncio.sleep(0) - - -def asyncio_run(coro: Coroutine[Any, Any, T], *, debug: bool = False) -> T: - """ - Python 3.6-compatible `asyncio.run` wrapper. - - :param coro: A coroutine to execute now. - :return: The return value from the coroutine. - """ - if sys.version_info >= (3, 7): - return asyncio.run(coro, debug=debug) - - # Python 3.6 - loop = asyncio.get_event_loop() - loop.set_debug(debug) - ret = loop.run_until_complete(coro) - loop.close() - - return ret - - # ---------------------------- # Section: Logging & Debugging # ---------------------------- diff --git a/python/tests/protocol.py b/python/tests/protocol.py index 56c4d441f9c4a..c254c77b176be 100644 --- a/python/tests/protocol.py +++ b/python/tests/protocol.py @@ -8,7 +8,6 @@ from qemu.qmp import ConnectError, Runstate from qemu.qmp.protocol import AsyncProtocol, StateError -from qemu.qmp.util import asyncio_run, create_task class NullProtocol(AsyncProtocol[None]): @@ -124,7 +123,7 @@ async def _runner(): if allow_cancellation: return raise - return create_task(_runner()) + return asyncio.create_task(_runner()) @contextmanager @@ -271,7 +270,7 @@ async def _watcher(): msg=f"Expected state '{state.name}'", ) - self.runstate_watcher = create_task(_watcher()) + self.runstate_watcher = asyncio.create_task(_watcher()) # Kick the loop and force the task to block on the event. await asyncio.sleep(0) @@ -589,7 +588,8 @@ async def _asyncTearDown(self): async def testSmoke(self): with TemporaryDirectory(suffix='.qmp') as tmpdir: sock = os.path.join(tmpdir, type(self.proto).__name__ + ".sock") - server_task = create_task(self.server.start_server_and_accept(sock)) + server_task = asyncio.create_task( + self.server.start_server_and_accept(sock)) # give the server a chance to start listening [...] await asyncio.sleep(0) From 0408b8d7a086486f5c1887798be744b2d73bcda9 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 6 Jun 2023 13:45:44 -0400 Subject: [PATCH 0646/1794] python: backport 'Use @asynciocontextmanager' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes a non-idiomatic use of a "coroutine callback" in favor of something a bit more standardized. Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@commit 97f7ffa3be17a50544b52767d14b6fd478c07b9e Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/protocol.py | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/python/qemu/qmp/protocol.py b/python/qemu/qmp/protocol.py index 208bdec5c8953..958aeca08ac29 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -15,6 +15,7 @@ import asyncio from asyncio import StreamReader, StreamWriter +from contextlib import asynccontextmanager from enum import Enum from functools import wraps import logging @@ -22,6 +23,7 @@ from ssl import SSLContext from typing import ( Any, + AsyncGenerator, Awaitable, Callable, Generic, @@ -337,9 +339,8 @@ async def start_server(self, address: SocketAddrT, This exception will wrap a more concrete one. In most cases, the wrapped exception will be `OSError`. """ - await self._session_guard( - self._do_start_server(address, ssl), - 'Failed to establish connection') + async with self._session_guard('Failed to establish connection'): + await self._do_start_server(address, ssl) assert self.runstate == Runstate.CONNECTING @upper_half @@ -362,12 +363,10 @@ async def accept(self) -> None: """ if self._accepted is None: raise QMPError("Cannot call accept() before start_server().") - await self._session_guard( - self._do_accept(), - 'Failed to establish connection') - await self._session_guard( - self._establish_session(), - 'Failed to establish session') + async with self._session_guard('Failed to establish connection'): + await self._do_accept() + async with self._session_guard('Failed to establish session'): + await self._establish_session() assert self.runstate == Runstate.RUNNING @upper_half @@ -392,12 +391,10 @@ async def connect(self, address: Union[SocketAddrT, socket.socket], protocol-level failure occurs while establishing a new session, the wrapped error may also be an `QMPError`. """ - await self._session_guard( - self._do_connect(address, ssl), - 'Failed to establish connection') - await self._session_guard( - self._establish_session(), - 'Failed to establish session') + async with self._session_guard('Failed to establish connection'): + await self._do_connect(address, ssl) + async with self._session_guard('Failed to establish session'): + await self._establish_session() assert self.runstate == Runstate.RUNNING @upper_half @@ -418,7 +415,8 @@ async def disconnect(self) -> None: # Section: Session machinery # -------------------------- - async def _session_guard(self, coro: Awaitable[None], emsg: str) -> None: + @asynccontextmanager + async def _session_guard(self, emsg: str) -> AsyncGenerator[None, None]: """ Async guard function used to roll back to `IDLE` on any error. @@ -435,10 +433,9 @@ async def _session_guard(self, coro: Awaitable[None], emsg: str) -> None: :raise ConnectError: When any other error is encountered in the guarded block. """ - # Note: After Python 3.6 support is removed, this should be an - # @asynccontextmanager instead of accepting a callback. try: - await coro + # Caller's code runs here. + yield except BaseException as err: self.logger.error("%s: %s", emsg, exception_summary(err)) self.logger.debug("%s:\n%s\n", emsg, pretty_traceback()) From 8fd9ccebd905dfe3afdee03732ba09e46a3a1d49 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 22 Jul 2022 13:59:19 -0400 Subject: [PATCH 0647/1794] python: backport 'qmp-shell: add common_parser()' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@20a88c2471f37d10520b2409046d59e1d0f1e905 Signed-off-by: John Snow Signed-off-by: Daniel P. Berrangé --- python/qemu/qmp/qmp_shell.py | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/python/qemu/qmp/qmp_shell.py b/python/qemu/qmp/qmp_shell.py index 98e684e9e8a64..02028e94b5a26 100644 --- a/python/qemu/qmp/qmp_shell.py +++ b/python/qemu/qmp/qmp_shell.py @@ -514,21 +514,27 @@ def die(msg: str) -> NoReturn: sys.exit(1) -def main() -> None: - """ - qmp-shell entry point: parse command line arguments and start the REPL. - """ +def common_parser() -> argparse.ArgumentParser: + """Build common parsing options used by qmp-shell and qmp-shell-wrap.""" parser = argparse.ArgumentParser() parser.add_argument('-H', '--hmp', action='store_true', help='Use HMP interface') - parser.add_argument('-N', '--skip-negotiation', action='store_true', - help='Skip negotiate (for qemu-ga)') parser.add_argument('-v', '--verbose', action='store_true', help='Verbose (echo commands sent and received)') parser.add_argument('-p', '--pretty', action='store_true', help='Pretty-print JSON') parser.add_argument('-l', '--logfile', help='Save log of all QMP messages to PATH') + return parser + + +def main() -> None: + """ + qmp-shell entry point: parse command line arguments and start the REPL. + """ + parser = common_parser() + parser.add_argument('-N', '--skip-negotiation', action='store_true', + help='Skip negotiate (for qemu-ga)') default_server = os.environ.get('QMP_SOCKET') parser.add_argument('qmp_server', action='store', @@ -564,16 +570,7 @@ def main_wrap() -> None: qmp-shell-wrap entry point: parse command line arguments and start the REPL. """ - parser = argparse.ArgumentParser() - parser.add_argument('-H', '--hmp', action='store_true', - help='Use HMP interface') - parser.add_argument('-v', '--verbose', action='store_true', - help='Verbose (echo commands sent and received)') - parser.add_argument('-p', '--pretty', action='store_true', - help='Pretty-print JSON') - parser.add_argument('-l', '--logfile', - help='Save log of all QMP messages to PATH') - + parser = common_parser() parser.add_argument('command', nargs=argparse.REMAINDER, help='QEMU command line to invoke') From 653f501434889b23a5062b3fe00488d140eb79fd Mon Sep 17 00:00:00 2001 From: Adam Dorsey Date: Mon, 14 Apr 2025 14:30:14 -0400 Subject: [PATCH 0648/1794] python: backport 'feat: allow setting read buffer limit' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose the limit parameter of the underlying StreamReader and StreamWriter instances. This is helpful for the use case of transferring files in and out of a VM via the QEMU guest agent's guest-file-open, guest-file-read, guest-file-write, and guest-file-close methods, as it allows pushing the buffer size up to the guest agent's limit of 48MB per transfer. Signed-off-by: Adam Dorsey cherry picked from commit python-qemu-qmp@9ba6a698344eb3b570fa4864e906c54042824cd6 cherry picked from commit python-qemu-qmp@e4d0d3f835d82283ee0e48438d1b154e18303491 [Squashed in linter fixups. --js] Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/protocol.py | 25 ++++++++++++++++--------- python/qemu/qmp/qmp_client.py | 18 ++++++++++++++---- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/python/qemu/qmp/protocol.py b/python/qemu/qmp/protocol.py index 958aeca08ac29..3d5eb553aadb3 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -53,6 +53,9 @@ UnixAddrT = str SocketAddrT = Union[UnixAddrT, InternetAddrT] +# Maximum allowable size of read buffer, default +_DEFAULT_READBUFLEN = 64 * 1024 + class Runstate(Enum): """Protocol session runstate.""" @@ -202,22 +205,26 @@ class AsyncProtocol(Generic[T]): will log to 'qemu.qmp.protocol', but each individual connection can be given its own logger by giving it a name; messages will then log to 'qemu.qmp.protocol.${name}'. + :param readbuflen: + The maximum read buffer length of the underlying StreamReader + instance. """ # pylint: disable=too-many-instance-attributes #: Logger object for debugging messages from this connection. logger = logging.getLogger(__name__) - # Maximum allowable size of read buffer - _limit = 64 * 1024 - # ------------------------- # Section: Public interface # ------------------------- - def __init__(self, name: Optional[str] = None) -> None: + def __init__( + self, name: Optional[str] = None, + readbuflen: int = _DEFAULT_READBUFLEN + ) -> None: self._name: Optional[str] self.name = name + self.readbuflen = readbuflen # stream I/O self._reader: Optional[StreamReader] = None @@ -574,7 +581,7 @@ async def _do_start_server(self, address: SocketAddrT, port=address[1], ssl=ssl, backlog=1, - limit=self._limit, + limit=self.readbuflen, ) else: coro = asyncio.start_unix_server( @@ -582,7 +589,7 @@ async def _do_start_server(self, address: SocketAddrT, path=address, ssl=ssl, backlog=1, - limit=self._limit, + limit=self.readbuflen, ) # Allow runstate watchers to witness 'CONNECTING' state; some @@ -637,7 +644,7 @@ async def _do_connect(self, address: Union[SocketAddrT, socket.socket], "fd=%d, family=%r, type=%r", address.fileno(), address.family, address.type) connect = asyncio.open_connection( - limit=self._limit, + limit=self.readbuflen, ssl=ssl, sock=address, ) @@ -647,14 +654,14 @@ async def _do_connect(self, address: Union[SocketAddrT, socket.socket], address[0], address[1], ssl=ssl, - limit=self._limit, + limit=self.readbuflen, ) else: self.logger.debug("Connecting to file://%s ...", address) connect = asyncio.open_unix_connection( path=address, ssl=ssl, - limit=self._limit, + limit=self.readbuflen, ) self._reader, self._writer = await connect diff --git a/python/qemu/qmp/qmp_client.py b/python/qemu/qmp/qmp_client.py index a87fb565ab508..d826331b6d5f2 100644 --- a/python/qemu/qmp/qmp_client.py +++ b/python/qemu/qmp/qmp_client.py @@ -170,6 +170,12 @@ class QMPClient(AsyncProtocol[Message], Events): :param name: Optional nickname for the connection, used for logging. + :param readbuflen: + The maximum buffer length for reads and writes to and from the QMP + server, in bytes. Default is 10MB. If `QMPClient` is used to + connect to a guest agent to transfer files via ``guest-file-read``/ + ``guest-file-write``, increasing this value may be required. + Basic script-style usage looks like this:: qmp = QMPClient('my_virtual_machine_name') @@ -203,14 +209,18 @@ async def run(self, address='/tmp/qemu.socket'): #: Logger object used for debugging messages. logger = logging.getLogger(__name__) - # Read buffer limit; 10MB like libvirt default - _limit = 10 * 1024 * 1024 + # Read buffer default limit; 10MB like libvirt default + _readbuflen = 10 * 1024 * 1024 # Type alias for pending execute() result items _PendingT = Union[Message, ExecInterruptedError] - def __init__(self, name: Optional[str] = None) -> None: - super().__init__(name) + def __init__( + self, + name: Optional[str] = None, + readbuflen: int = _readbuflen + ) -> None: + super().__init__(name, readbuflen) Events.__init__(self) #: Whether or not to await a greeting after establishing a connection. From a50b8572f0e3873db64dd6660cea047f067ca5f7 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 2 May 2022 18:54:20 -0400 Subject: [PATCH 0649/1794] python: backport 'make require() preserve async-ness' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is not strictly needed functionality-wise, but doing this allows sphinx to see which decorated methods are async. Without this, sphinx misses the "async" classifier on generated docs, which ... for an async library, isn't great. It does make an already gnarly function even gnarlier, though. So, what's going on here? A synchronous function (like require() before this patch) can return a coroutine that can be awaited on, for example: def some_func(): return asyncio.task(asyncio.sleep(5)) async def some_async_func(): await some_func() However, this function is not considered to be an "async" function in the eyes of the abstract syntax tree. Specifically, some_func.__code__.co_flags will not be set with CO_COROUTINE. The interpreter uses this flag to know if it's legal to use "await" from within the body of the function. Since this function is just wrapping another function, it doesn't matter much for the decorator, but sphinx uses the stdlib inspect.iscoroutinefunction() to determine when to add the "async" prefix in generated output. This function uses the presence of CO_COROUTINE. So, in order to preserve the "async" flag for docs, the require() decorator needs to differentiate based on whether it is decorating a sync or async function and use a different wrapping mechanism accordingly. Phew. Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@40aa9699d619849f528032aa456dd061a4afa957 Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/protocol.py | 53 ++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/python/qemu/qmp/protocol.py b/python/qemu/qmp/protocol.py index 3d5eb553aadb3..4d8a39f014b42 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -18,6 +18,7 @@ from contextlib import asynccontextmanager from enum import Enum from functools import wraps +from inspect import iscoroutinefunction import logging import socket from ssl import SSLContext @@ -130,6 +131,25 @@ def require(required_state: Runstate) -> Callable[[F], F]: :param required_state: The `Runstate` required to invoke this method. :raise StateError: When the required `Runstate` is not met. """ + def _check(proto: 'AsyncProtocol[Any]') -> None: + name = type(proto).__name__ + if proto.runstate == required_state: + return + + if proto.runstate == Runstate.CONNECTING: + emsg = f"{name} is currently connecting." + elif proto.runstate == Runstate.DISCONNECTING: + emsg = (f"{name} is disconnecting." + " Call disconnect() to return to IDLE state.") + elif proto.runstate == Runstate.RUNNING: + emsg = f"{name} is already connected and running." + elif proto.runstate == Runstate.IDLE: + emsg = f"{name} is disconnected and idle." + else: + assert False + + raise StateError(emsg, proto.runstate, required_state) + def _decorator(func: F) -> F: # _decorator is the decorator that is built by calling the # require() decorator factory; e.g.: @@ -140,29 +160,20 @@ def _decorator(func: F) -> F: @wraps(func) def _wrapper(proto: 'AsyncProtocol[Any]', *args: Any, **kwargs: Any) -> Any: - # _wrapper is the function that gets executed prior to the - # decorated method. - - name = type(proto).__name__ - - if proto.runstate != required_state: - if proto.runstate == Runstate.CONNECTING: - emsg = f"{name} is currently connecting." - elif proto.runstate == Runstate.DISCONNECTING: - emsg = (f"{name} is disconnecting." - " Call disconnect() to return to IDLE state.") - elif proto.runstate == Runstate.RUNNING: - emsg = f"{name} is already connected and running." - elif proto.runstate == Runstate.IDLE: - emsg = f"{name} is disconnected and idle." - else: - assert False - raise StateError(emsg, proto.runstate, required_state) - # No StateError, so call the wrapped method. + _check(proto) return func(proto, *args, **kwargs) - # Return the decorated method; - # Transforming Func to Decorated[Func]. + @wraps(func) + async def _async_wrapper(proto: 'AsyncProtocol[Any]', + *args: Any, **kwargs: Any) -> Any: + _check(proto) + return await func(proto, *args, **kwargs) + + # Return the decorated method; F => Decorated[F] + # Use an async version when applicable, which + # preserves async signature generation in sphinx. + if iscoroutinefunction(func): + return cast(F, _async_wrapper) return cast(F, _wrapper) # Return the decorator instance from the decorator factory. Phew! From fcaeeb7653d2c6f38183170e1cae5729adb7875c Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 22 Jul 2022 14:13:45 -0400 Subject: [PATCH 0650/1794] python: backport 'qmp-shell-wrap: handle missing binary gracefully' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@9c889dcbd58817b0c917a9d2dd16161f48ac8203 Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/qmp_shell.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/qemu/qmp/qmp_shell.py b/python/qemu/qmp/qmp_shell.py index 02028e94b5a26..c923ff09e1f4d 100644 --- a/python/qemu/qmp/qmp_shell.py +++ b/python/qemu/qmp/qmp_shell.py @@ -607,6 +607,8 @@ def main_wrap() -> None: for _ in qemu.repl(): pass + except FileNotFoundError: + sys.stderr.write(f"ERROR: QEMU executable '{cmd[0]}' not found.\n") finally: os.unlink(sockpath) From fd0ed46d4effbf2700804657bad9c6db086527c4 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 22 Jul 2022 15:55:45 -0400 Subject: [PATCH 0651/1794] python: backport 'qmp-tui: Do not crash if optional dependencies are not met' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on the discussion at https://github.com/pypa/pip/issues/9726 - even though the setuptools documentation implies that it is possible to guard script execution with optional dependency groups, this is not true in practice with the scripts generated by pip. Just do the simple thing and guard the import statements. Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@df520dcacf9a75dd4c82ab1129768de4128b554c Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/qmp_tui.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/python/qemu/qmp/qmp_tui.py b/python/qemu/qmp/qmp_tui.py index 562be008d5e9d..53ea6c59a714f 100644 --- a/python/qemu/qmp/qmp_tui.py +++ b/python/qemu/qmp/qmp_tui.py @@ -21,6 +21,7 @@ import logging from logging import Handler, LogRecord import signal +import sys from typing import ( List, Optional, @@ -30,10 +31,20 @@ cast, ) -from pygments import lexers -from pygments import token as Token -import urwid -import urwid_readline + +try: + from pygments import lexers + from pygments import token as Token + import urwid + import urwid_readline +except ModuleNotFoundError as exc: + print( + f"Module '{exc.name}' not found.", + "You need the optional 'tui' group: pip install qemu.qmp[tui]", + sep='\n', + file=sys.stderr, + ) + sys.exit(1) from .error import ProtocolError from .legacy import QEMUMonitorProtocol, QMPBadPortError From 5d99044d09db0fa8c2b3294e301927118f9effc9 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 13 Aug 2024 09:35:30 -0400 Subject: [PATCH 0652/1794] python: backport 'Remove deprecated get_event_loop calls' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This method was deprecated in 3.12 because it ordinarily should not be used from coroutines; if there is not a currently running event loop, this automatically creates a new event loop - which is usually not what you want from code that would ever run in the bottom half. In our case, we do want this behavior in two places: (1) The synchronous shim, for convenience: this allows fully sync programs to use QEMUMonitorProtocol() without needing to set up an event loop beforehand. This is intentional to fully box in the async complexities into the legacy sync shim. (2) The qmp_tui shell; instead of relying on asyncio.run to create and run an asyncio program, we need to be able to pass the current asyncio loop to urwid setup functions. For convenience, again, we create one if one is not present to simplify the creation of the TUI appliance. The remaining user of get_event_loop() was in fact one of the erroneous users that should not have been using this function: if there's no running event loop inside of a coroutine, you're in big trouble :) Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@aa1ff9907603a3033296027e1bd021133df86ef1 Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/legacy.py | 9 ++++++++- python/qemu/qmp/qmp_tui.py | 7 ++++++- python/tests/protocol.py | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py index c8d0a29b56fe0..735d42971e907 100644 --- a/python/qemu/qmp/legacy.py +++ b/python/qemu/qmp/legacy.py @@ -86,7 +86,14 @@ def __init__(self, "server argument should be False when passing a socket") self._qmp = QMPClient(nickname) - self._aloop = asyncio.get_event_loop() + + try: + self._aloop = asyncio.get_running_loop() + except RuntimeError: + # No running loop; since this is a sync shim likely to be + # used in fully sync programs, create one if neccessary. + self._aloop = asyncio.get_event_loop_policy().get_event_loop() + self._address = address self._timeout: Optional[float] = None diff --git a/python/qemu/qmp/qmp_tui.py b/python/qemu/qmp/qmp_tui.py index 53ea6c59a714f..12bdc17c99ed7 100644 --- a/python/qemu/qmp/qmp_tui.py +++ b/python/qemu/qmp/qmp_tui.py @@ -388,7 +388,12 @@ def run(self, debug: bool = False) -> None: screen = urwid.raw_display.Screen() screen.set_terminal_properties(256) - self.aloop = asyncio.get_event_loop() + try: + self.aloop = asyncio.get_running_loop() + except RuntimeError: + # No running asyncio event loop. Create one if necessary. + self.aloop = asyncio.get_event_loop_policy().get_event_loop() + self.aloop.set_debug(debug) # Gracefully handle SIGTERM and SIGINT signals diff --git a/python/tests/protocol.py b/python/tests/protocol.py index c254c77b176be..e565802516d22 100644 --- a/python/tests/protocol.py +++ b/python/tests/protocol.py @@ -227,7 +227,7 @@ def async_test(async_test_method): Decorator; adds SetUp and TearDown to async tests. """ async def _wrapper(self, *args, **kwargs): - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() loop.set_debug(True) await self._asyncSetUp() From 85f223e5b031eb8ab63fbca314a4fb296a3a2632 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 3 Sep 2025 01:06:30 -0400 Subject: [PATCH 0653/1794] python: backport 'avoid creating additional event loops per thread' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit is two backports squashed into one to avoid regressions. python: *really* remove get_event_loop A prior commit, aa1ff990, switched away from using get_event_loop *by default*, but this is not good enough to avoid deprecation warnings as `asyncio.get_event_loop_policy().get_event_loop()` is *also* deprecated. Replace this mechanism with explicit calls to asyncio.get_new_loop() and revise the cleanup mechanisms in __del__ to match. python: avoid creating additional event loops per thread "Too hasty by far!", commit 21ce2ee4 attempted to avoid deprecated behavior altogether by calling new_event_loop() directly if there was no loop currently running, but this has the unfortunate side effect of potentially creating multiple event loops per thread if tests instantiate multiple QMP connections in a single thread. This behavior is apparently not well-defined and causes problems in some, but not all, combinations of Python interpreter version and platform environment. Partially revert to Daniel Berrange's original patch, which calls get_event_loop and simply suppresses the deprecation warning in Python<=3.13. This time, however, additionally register new loops created with new_event_loop() so that future calls to get_event_loop() will return the loop already created. Reported-by: Richard W.M. Jones Reported-by: Daniel P. Berrangé Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@21ce2ee4f2df87efe84a27b9c5112487f4670622 cherry picked from commit python-qemu-qmp@c08fb82b38212956ccffc03fc6d015c3979f42fe Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/legacy.py | 46 +++++++++++++++++++++++--------------- python/qemu/qmp/qmp_tui.py | 10 ++------- python/qemu/qmp/util.py | 27 ++++++++++++++++++++++ 3 files changed, 57 insertions(+), 26 deletions(-) diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py index 735d42971e907..e46695ae2c872 100644 --- a/python/qemu/qmp/legacy.py +++ b/python/qemu/qmp/legacy.py @@ -38,6 +38,7 @@ from .error import QMPError from .protocol import Runstate, SocketAddrT from .qmp_client import QMPClient +from .util import get_or_create_event_loop #: QMPMessage is an entire QMP message of any kind. @@ -86,17 +87,13 @@ def __init__(self, "server argument should be False when passing a socket") self._qmp = QMPClient(nickname) - - try: - self._aloop = asyncio.get_running_loop() - except RuntimeError: - # No running loop; since this is a sync shim likely to be - # used in fully sync programs, create one if neccessary. - self._aloop = asyncio.get_event_loop_policy().get_event_loop() - self._address = address self._timeout: Optional[float] = None + # This is a sync shim intended for use in fully synchronous + # programs. Create and set an event loop if necessary. + self._aloop = get_or_create_event_loop() + if server: assert not isinstance(self._address, socket.socket) self._sync(self._qmp.start_server(self._address)) @@ -313,17 +310,30 @@ def send_fd_scm(self, fd: int) -> None: self._qmp.send_fd_scm(fd) def __del__(self) -> None: - if self._qmp.runstate == Runstate.IDLE: - return + if self._qmp.runstate != Runstate.IDLE: + self._qmp.logger.warning( + "QEMUMonitorProtocol object garbage collected without a prior " + "call to close()" + ) if not self._aloop.is_running(): - self.close() - else: - # Garbage collection ran while the event loop was running. - # Nothing we can do about it now, but if we don't raise our - # own error, the user will be treated to a lot of traceback - # they might not understand. + if self._qmp.runstate != Runstate.IDLE: + # If the user neglected to close the QMP session and we + # are not currently running in an asyncio context, we + # have the opportunity to close the QMP session. If we + # do not do this, the error messages presented over + # dangling async resources may not make any sense to the + # user. + self.close() + + if self._qmp.runstate != Runstate.IDLE: + # If QMP is still not quiesced, it means that the garbage + # collector ran from a context within the event loop and we + # are simply too late to take any corrective action. Raise + # our own error to give meaningful feedback to the user in + # order to prevent pages of asyncio stacktrace jargon. raise QMPError( - "QEMUMonitorProtocol.close()" - " was not called before object was garbage collected" + "QEMUMonitorProtocol.close() was not called before object was " + "garbage collected, and could not be closed due to GC running " + "in the event loop" ) diff --git a/python/qemu/qmp/qmp_tui.py b/python/qemu/qmp/qmp_tui.py index 12bdc17c99ed7..d946c205131aa 100644 --- a/python/qemu/qmp/qmp_tui.py +++ b/python/qemu/qmp/qmp_tui.py @@ -51,7 +51,7 @@ from .message import DeserializationError, Message, UnexpectedTypeError from .protocol import ConnectError, Runstate from .qmp_client import ExecInterruptedError, QMPClient -from .util import pretty_traceback +from .util import get_or_create_event_loop, pretty_traceback # The name of the signal that is used to update the history list @@ -387,13 +387,7 @@ def run(self, debug: bool = False) -> None: """ screen = urwid.raw_display.Screen() screen.set_terminal_properties(256) - - try: - self.aloop = asyncio.get_running_loop() - except RuntimeError: - # No running asyncio event loop. Create one if necessary. - self.aloop = asyncio.get_event_loop_policy().get_event_loop() - + self.aloop = get_or_create_event_loop() self.aloop.set_debug(debug) # Gracefully handle SIGTERM and SIGINT signals diff --git a/python/qemu/qmp/util.py b/python/qemu/qmp/util.py index 0b3e781373d84..47ec39a8b5e84 100644 --- a/python/qemu/qmp/util.py +++ b/python/qemu/qmp/util.py @@ -10,6 +10,7 @@ import sys import traceback from typing import TypeVar, cast +import warnings T = TypeVar('T') @@ -20,6 +21,32 @@ # -------------------------- +def get_or_create_event_loop() -> asyncio.AbstractEventLoop: + """ + Return this thread's current event loop, or create a new one. + + This function behaves similarly to asyncio.get_event_loop() in + Python<=3.13, where if there is no event loop currently associated + with the current context, it will create and register one. It should + generally not be used in any asyncio-native applications. + """ + try: + with warnings.catch_warnings(): + # Python <= 3.13 will trigger deprecation warnings if no + # event loop is set, but will create and set a new loop. + warnings.simplefilter("ignore") + loop = asyncio.get_event_loop() + except RuntimeError: + # Python 3.14+: No event loop set for this thread, + # create and set one. + loop = asyncio.new_event_loop() + # Set this loop as the current thread's loop, to be returned + # by calls to get_event_loop() in the future. + asyncio.set_event_loop(loop) + + return loop + + async def flush(writer: asyncio.StreamWriter) -> None: """ Utility function to ensure a StreamWriter is *fully* drained. From f414048e32262830e06d50240b2f15b6e5857efe Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 26 Aug 2025 13:04:50 -0400 Subject: [PATCH 0654/1794] python: synchronize qemu.qmp documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch collects comments and documentation changes from many commits in the python-qemu-qmp repository; bringing the qemu.git copy in bit-identical alignment with the standalone library *except* for several copyright messages that reference the "LICENSE" file which is, for QEMU, named "COPYING" instead and are therefore left unchanged. Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/__init__.py | 3 +- python/qemu/qmp/events.py | 35 +++++++--- python/qemu/qmp/legacy.py | 4 +- python/qemu/qmp/message.py | 10 ++- python/qemu/qmp/models.py | 8 +-- python/qemu/qmp/protocol.py | 37 ++++++---- python/qemu/qmp/qmp_client.py | 117 +++++++++++++++++++++++-------- python/qemu/qmp/qmp_shell.py | 128 ++++++++++++++++++++++++++-------- python/qemu/qmp/util.py | 9 ++- 9 files changed, 264 insertions(+), 87 deletions(-) diff --git a/python/qemu/qmp/__init__.py b/python/qemu/qmp/__init__.py index 69190d057a5b2..058139dc3cac5 100644 --- a/python/qemu/qmp/__init__.py +++ b/python/qemu/qmp/__init__.py @@ -39,7 +39,8 @@ logging.getLogger('qemu.qmp').addHandler(logging.NullHandler()) -# The order of these fields impact the Sphinx documentation order. +# IMPORTANT: When modifying this list, update the Sphinx overview docs. +# Anything visible in the qemu.qmp namespace should be on the overview page. __all__ = ( # Classes, most to least important 'QMPClient', diff --git a/python/qemu/qmp/events.py b/python/qemu/qmp/events.py index 6658349619279..cfb5f0ac621f2 100644 --- a/python/qemu/qmp/events.py +++ b/python/qemu/qmp/events.py @@ -12,7 +12,14 @@ ---------------------- In all of the following examples, we assume that we have a `QMPClient` -instantiated named ``qmp`` that is already connected. +instantiated named ``qmp`` that is already connected. For example: + +.. code:: python + + from qemu.qmp import QMPClient + + qmp = QMPClient('example-vm') + await qmp.connect('127.0.0.1', 1234) `listener()` context blocks with one name @@ -87,7 +94,9 @@ event = listener.get() print(f"Event arrived: {event['event']}") -This event stream will never end, so these blocks will never terminate. +This event stream will never end, so these blocks will never +terminate. Even if the QMP connection errors out prematurely, this +listener will go silent without raising an error. Using asyncio.Task to concurrently retrieve events @@ -227,16 +236,20 @@ async def print_events(listener): .. code:: python await qmp.execute('stop') - qmp.events.clear() + discarded = qmp.events.clear() await qmp.execute('cont') event = await qmp.events.get() assert event['event'] == 'RESUME' + assert discarded[0]['event'] == 'STOP' `EventListener` objects are FIFO queues. If events are not consumed, they will remain in the queue until they are witnessed or discarded via `clear()`. FIFO queues will be drained automatically upon leaving a context block, or when calling `remove_listener()`. +Any events removed from the queue in this fashion will be returned by +the clear call. + Accessing listener history ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -350,6 +363,12 @@ def filter(event: Message) -> bool: break +Note that in the above example, we explicitly wait on jobA to conclude +first, and then wait for jobB to do the same. All we have guaranteed is +that the code that waits for jobA will not accidentally consume the +event intended for the jobB waiter. + + Extending the `EventListener` class ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -407,13 +426,13 @@ def accept(self, event) -> bool: These interfaces are not ones I am sure I will keep or otherwise modify heavily. -qmp.listener()’s type signature +qmp.listen()’s type signature ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -`listener()` does not return anything, because it was assumed the caller +`listen()` does not return anything, because it was assumed the caller already had a handle to the listener. However, for -``qmp.listener(EventListener())`` forms, the caller will not have saved -a handle to the listener. +``qmp.listen(EventListener())`` forms, the caller will not have saved a +handle to the listener. Because this function can accept *many* listeners, I found it hard to accurately type in a way where it could be used in both “one” or “many” @@ -633,7 +652,7 @@ class Events: def __init__(self) -> None: self._listeners: List[EventListener] = [] - #: Default, all-events `EventListener`. + #: Default, all-events `EventListener`. See `qmp.events` for more info. self.events: EventListener = EventListener() self.register_listener(self.events) diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py index e46695ae2c872..060ed0eb9d453 100644 --- a/python/qemu/qmp/legacy.py +++ b/python/qemu/qmp/legacy.py @@ -293,8 +293,8 @@ def settimeout(self, timeout: Optional[float]) -> None: """ Set the timeout for QMP RPC execution. - This timeout affects the `cmd`, `cmd_obj`, and `command` methods. - The `accept`, `pull_event` and `get_event` methods have their + This timeout affects the `cmd`, `cmd_obj`, and `cmd_raw` methods. + The `accept`, `pull_event` and `get_events` methods have their own configurable timeouts. :param timeout: diff --git a/python/qemu/qmp/message.py b/python/qemu/qmp/message.py index c2e9dd0dd5429..dabb8ec360eef 100644 --- a/python/qemu/qmp/message.py +++ b/python/qemu/qmp/message.py @@ -28,7 +28,8 @@ class Message(MutableMapping[str, object]): be instantiated from either another mapping (like a `dict`), or from raw `bytes` that still need to be deserialized. - Once instantiated, it may be treated like any other MutableMapping:: + Once instantiated, it may be treated like any other + :py:obj:`~collections.abc.MutableMapping`:: >>> msg = Message(b'{"hello": "world"}') >>> assert msg['hello'] == 'world' @@ -50,12 +51,19 @@ class Message(MutableMapping[str, object]): >>> dict(msg) {'hello': 'world'} + Or pretty-printed:: + + >>> print(str(msg)) + { + "hello": "world" + } :param value: Initial value, if any. :param eager: When `True`, attempt to serialize or deserialize the initial value immediately, so that conversion exceptions are raised during the call to ``__init__()``. + """ # pylint: disable=too-many-ancestors diff --git a/python/qemu/qmp/models.py b/python/qemu/qmp/models.py index da52848d5a737..7e0d0baf0386f 100644 --- a/python/qemu/qmp/models.py +++ b/python/qemu/qmp/models.py @@ -54,7 +54,7 @@ def __repr__(self) -> str: class Greeting(Model): """ - Defined in qmp-spec.rst, section "Server Greeting". + Defined in `interop/qmp-spec`, "Server Greeting" section. :param raw: The raw Greeting object. :raise KeyError: If any required fields are absent. @@ -82,7 +82,7 @@ def _asdict(self) -> Dict[str, object]: class QMPGreeting(Model): """ - Defined in qmp-spec.rst, section "Server Greeting". + Defined in `interop/qmp-spec`, "Server Greeting" section. :param raw: The raw QMPGreeting object. :raise KeyError: If any required fields are absent. @@ -104,7 +104,7 @@ def __init__(self, raw: Mapping[str, Any]): class ErrorResponse(Model): """ - Defined in qmp-spec.rst, section "Error". + Defined in `interop/qmp-spec`, "Error" section. :param raw: The raw ErrorResponse object. :raise KeyError: If any required fields are absent. @@ -126,7 +126,7 @@ def __init__(self, raw: Mapping[str, Any]): class ErrorInfo(Model): """ - Defined in qmp-spec.rst, section "Error". + Defined in `interop/qmp-spec`, "Error" section. :param raw: The raw ErrorInfo object. :raise KeyError: If any required fields are absent. diff --git a/python/qemu/qmp/protocol.py b/python/qemu/qmp/protocol.py index 4d8a39f014b42..219d092a79200 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -79,6 +79,12 @@ class ConnectError(QMPError): This Exception always wraps a "root cause" exception that can be interrogated for additional information. + For example, when connecting to a non-existent socket:: + + await qmp.connect('not_found.sock') + # ConnectError: Failed to establish connection: + # [Errno 2] No such file or directory + :param error_message: Human-readable string describing the error. :param exc: The root-cause exception. """ @@ -102,8 +108,8 @@ class StateError(QMPError): An API command (connect, execute, etc) was issued at an inappropriate time. This error is raised when a command like - :py:meth:`~AsyncProtocol.connect()` is issued at an inappropriate - time. + :py:meth:`~AsyncProtocol.connect()` is called when the client is + already connected. :param error_message: Human-readable string describing the state violation. :param state: The actual `Runstate` seen at the time of the violation. @@ -298,7 +304,7 @@ def runstate(self) -> Runstate: @upper_half async def runstate_changed(self) -> Runstate: """ - Wait for the `runstate` to change, then return that runstate. + Wait for the `runstate` to change, then return that `Runstate`. """ await self._runstate_event.wait() return self.runstate @@ -312,9 +318,9 @@ async def start_server_and_accept( """ Accept a connection and begin processing message queues. - If this call fails, `runstate` is guaranteed to be set back to `IDLE`. - This method is precisely equivalent to calling `start_server()` - followed by `accept()`. + If this call fails, `runstate` is guaranteed to be set back to + `IDLE`. This method is precisely equivalent to calling + `start_server()` followed by :py:meth:`~AsyncProtocol.accept()`. :param address: Address to listen on; UNIX socket path or TCP address/port. @@ -327,7 +333,8 @@ async def start_server_and_accept( This exception will wrap a more concrete one. In most cases, the wrapped exception will be `OSError` or `EOFError`. If a protocol-level failure occurs while establishing a new - session, the wrapped error may also be an `QMPError`. + session, the wrapped error may also be a `QMPError`. + """ await self.start_server(address, ssl) await self.accept() @@ -343,8 +350,8 @@ async def start_server(self, address: SocketAddrT, This method starts listening for an incoming connection, but does not block waiting for a peer. This call will return immediately after binding and listening on a socket. A later - call to `accept()` must be made in order to finalize the - incoming connection. + call to :py:meth:`~AsyncProtocol.accept()` must be made in order + to finalize the incoming connection. :param address: Address to listen on; UNIX socket path or TCP address/port. @@ -367,10 +374,12 @@ async def accept(self) -> None: """ Accept an incoming connection and begin processing message queues. - If this call fails, `runstate` is guaranteed to be set back to `IDLE`. + Used after a previous call to `start_server()` to accept an + incoming connection. If this call fails, `runstate` is + guaranteed to be set back to `IDLE`. :raise StateError: When the `Runstate` is not `CONNECTING`. - :raise QMPError: When `start_server()` was not called yet. + :raise QMPError: When `start_server()` was not called first. :raise ConnectError: When a connection or session cannot be established. @@ -423,7 +432,11 @@ async def disconnect(self) -> None: If there was an exception that caused the reader/writers to terminate prematurely, it will be raised here. - :raise Exception: When the reader or writer terminate unexpectedly. + :raise Exception: + When the reader or writer terminate unexpectedly. You can + expect to see `EOFError` if the server hangs up, or + `OSError` for connection-related issues. If there was a QMP + protocol-level problem, `ProtocolError` will be seen. """ self.logger.debug("disconnect() called.") self._schedule_disconnect() diff --git a/python/qemu/qmp/qmp_client.py b/python/qemu/qmp/qmp_client.py index d826331b6d5f2..8beccfe29d347 100644 --- a/python/qemu/qmp/qmp_client.py +++ b/python/qemu/qmp/qmp_client.py @@ -70,6 +70,17 @@ class ExecuteError(QMPError): """ Exception raised by `QMPClient.execute()` on RPC failure. + This exception is raised when the server received, interpreted, and + replied to a command successfully; but the command itself returned a + failure status. + + For example:: + + await qmp.execute('block-dirty-bitmap-add', + {'node': 'foo', 'name': 'my_bitmap'}) + # qemu.qmp.qmp_client.ExecuteError: + # Cannot find device='foo' nor node-name='foo' + :param error_response: The RPC error response object. :param sent: The sent RPC message that caused the failure. :param received: The raw RPC error reply received. @@ -99,9 +110,22 @@ class ExecInterruptedError(QMPError): This error is raised when an `execute()` statement could not be completed. This can occur because the connection itself was - terminated before a reply was received. + terminated before a reply was received. The true cause of the + interruption will be available via `disconnect()`. + + The QMP protocol does not make it possible to know if a command + succeeded or failed after such an event; the client will need to + query the server to determine the state of the server on a + case-by-case basis. + + For example, ECONNRESET might look like this:: - The true cause of the interruption will be available via `disconnect()`. + try: + await qmp.execute('query-block') + # ExecInterruptedError: Disconnected + except ExecInterruptedError: + await qmp.disconnect() + # ConnectionResetError: [Errno 104] Connection reset by peer """ @@ -162,13 +186,14 @@ def __init__(self, error_message: str, msg: Message, sent: Message): class QMPClient(AsyncProtocol[Message], Events): - """ - Implements a QMP client connection. + """Implements a QMP client connection. - QMP can be used to establish a connection as either the transport - client or server, though this class always acts as the QMP client. + `QMPClient` can be used to either connect or listen to a QMP server, + but always acts as the QMP client. - :param name: Optional nickname for the connection, used for logging. + :param name: + Optional nickname for the connection, used to differentiate + instances when logging. :param readbuflen: The maximum buffer length for reads and writes to and from the QMP @@ -178,14 +203,21 @@ class QMPClient(AsyncProtocol[Message], Events): Basic script-style usage looks like this:: - qmp = QMPClient('my_virtual_machine_name') - await qmp.connect(('127.0.0.1', 1234)) - ... - res = await qmp.execute('block-query') - ... - await qmp.disconnect() + import asyncio + from qemu.qmp import QMPClient + + async def main(): + qmp = QMPClient('my_virtual_machine_name') + await qmp.connect(('127.0.0.1', 1234)) + ... + res = await qmp.execute('query-block') + ... + await qmp.disconnect() - Basic async client-style usage looks like this:: + asyncio.run(main()) + + A more advanced example that starts to take advantage of asyncio + might look like this:: class Client: def __init__(self, name: str): @@ -205,6 +237,7 @@ async def run(self, address='/tmp/qemu.socket'): await self.disconnect() See `qmp.events` for more detail on event handling patterns. + """ #: Logger object used for debugging messages. logger = logging.getLogger(__name__) @@ -224,10 +257,12 @@ def __init__( Events.__init__(self) #: Whether or not to await a greeting after establishing a connection. + #: Defaults to True; QGA servers expect this to be False. self.await_greeting: bool = True - #: Whether or not to perform capabilities negotiation upon connection. - #: Implies `await_greeting`. + #: Whether or not to perform capabilities negotiation upon + #: connection. Implies `await_greeting`. Defaults to True; QGA + #: servers expect this to be False. self.negotiate: bool = True # Cached Greeting, if one was awaited. @@ -244,7 +279,13 @@ def __init__( @property def greeting(self) -> Optional[Greeting]: - """The `Greeting` from the QMP server, if any.""" + """ + The `Greeting` from the QMP server, if any. + + Defaults to ``None``, and will be set after a greeting is + received during the connection process. It is reset at the start + of each connection attempt. + """ return self._greeting @upper_half @@ -385,7 +426,7 @@ async def _on_message(self, msg: Message) -> None: # This is very likely a server parsing error. # It doesn't inherently belong to any pending execution. # Instead of performing clever recovery, just terminate. - # See "NOTE" in qmp-spec.rst, section "Error". + # See "NOTE" in interop/qmp-spec, "Error" section. raise ServerParseError( ("Server sent an error response without an ID, " "but there are no ID-less executions pending. " @@ -393,7 +434,7 @@ async def _on_message(self, msg: Message) -> None: msg ) - # qmp-spec.rst, section "Commands Responses": + # qmp-spec.rst, "Commands Responses" section: # 'Clients should drop all the responses # that have an unknown "id" field.' self.logger.log( @@ -566,7 +607,7 @@ async def _raw( @require(Runstate.RUNNING) async def execute_msg(self, msg: Message) -> object: """ - Execute a QMP command and return its value. + Execute a QMP command on the server and return its value. :param msg: The QMP `Message` to execute. @@ -578,7 +619,9 @@ async def execute_msg(self, msg: Message) -> object: If the QMP `Message` does not have either the 'execute' or 'exec-oob' fields set. :raise ExecuteError: When the server returns an error response. - :raise ExecInterruptedError: if the connection was terminated early. + :raise ExecInterruptedError: + If the connection was disrupted before + receiving a reply from the server. """ if not ('execute' in msg or 'exec-oob' in msg): raise ValueError("Requires 'execute' or 'exec-oob' message") @@ -617,9 +660,11 @@ def make_execute_msg(cls, cmd: str, :param cmd: QMP command name. :param arguments: Arguments (if any). Must be JSON-serializable. - :param oob: If `True`, execute "out of band". + :param oob: + If `True`, execute "out of band". See `interop/qmp-spec` + section "Out-of-band execution". - :return: An executable QMP `Message`. + :return: A QMP `Message` that can be executed with `execute_msg()`. """ msg = Message({'exec-oob' if oob else 'execute': cmd}) if arguments is not None: @@ -631,18 +676,22 @@ async def execute(self, cmd: str, arguments: Optional[Mapping[str, object]] = None, oob: bool = False) -> object: """ - Execute a QMP command and return its value. + Execute a QMP command on the server and return its value. :param cmd: QMP command name. :param arguments: Arguments (if any). Must be JSON-serializable. - :param oob: If `True`, execute "out of band". + :param oob: + If `True`, execute "out of band". See `interop/qmp-spec` + section "Out-of-band execution". :return: The command execution return value from the server. The type of object returned depends on the command that was issued, though most in QEMU return a `dict`. :raise ExecuteError: When the server returns an error response. - :raise ExecInterruptedError: if the connection was terminated early. + :raise ExecInterruptedError: + If the connection was disrupted before + receiving a reply from the server. """ msg = self.make_execute_msg(cmd, arguments, oob=oob) return await self.execute_msg(msg) @@ -650,8 +699,20 @@ async def execute(self, cmd: str, @upper_half @require(Runstate.RUNNING) def send_fd_scm(self, fd: int) -> None: - """ - Send a file descriptor to the remote via SCM_RIGHTS. + """Send a file descriptor to the remote via SCM_RIGHTS. + + This method does not close the file descriptor. + + :param fd: The file descriptor to send to QEMU. + + This is an advanced feature of QEMU where file descriptors can + be passed from client to server. This is usually used as a + security measure to isolate the QEMU process from being able to + open its own files. See the QMP commands ``getfd`` and + ``add-fd`` for more information. + + See `socket.socket.sendmsg` for more information on the Python + implementation for sending file descriptors over a UNIX socket. """ assert self._writer is not None sock = self._writer.transport.get_extra_info('socket') diff --git a/python/qemu/qmp/qmp_shell.py b/python/qemu/qmp/qmp_shell.py index c923ff09e1f4d..f818800568567 100644 --- a/python/qemu/qmp/qmp_shell.py +++ b/python/qemu/qmp/qmp_shell.py @@ -10,9 +10,15 @@ # """ -Low-level QEMU shell on top of QMP. +qmp-shell - An interactive QEMU shell powered by QMP -usage: qmp-shell [-h] [-H] [-N] [-v] [-p] qmp_server +qmp-shell offers a simple shell with a convenient shorthand syntax as an +alternative to typing JSON by hand. This syntax is not standardized and +is not meant to be used as a scriptable interface. This shorthand *may* +change incompatibly in the future, and it is strongly encouraged to use +the QMP library to provide API-stable scripting when needed. + +usage: qmp-shell [-h] [-H] [-v] [-p] [-l LOGFILE] [-N] qmp_server positional arguments: qmp_server < UNIX socket path | TCP address:port > @@ -20,41 +26,52 @@ optional arguments: -h, --help show this help message and exit -H, --hmp Use HMP interface - -N, --skip-negotiation - Skip negotiate (for qemu-ga) -v, --verbose Verbose (echo commands sent and received) -p, --pretty Pretty-print JSON + -l LOGFILE, --logfile LOGFILE + Save log of all QMP messages to PATH + -N, --skip-negotiation + Skip negotiate (for qemu-ga) +Usage +----- -Start QEMU with: +First, start QEMU with:: -# qemu [...] -qmp unix:./qmp-sock,server + > qemu [...] -qmp unix:./qmp-sock,server=on[,wait=off] -Run the shell: +Then run the shell, passing the address of the socket:: -$ qmp-shell ./qmp-sock + > qmp-shell ./qmp-sock -Commands have the following format: +Syntax +------ - < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] +Commands have the following format:: -For example: + < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] -(QEMU) device_add driver=e1000 id=net1 -{'return': {}} -(QEMU) +For example, to add a network device:: -key=value pairs also support Python or JSON object literal subset notations, -without spaces. Dictionaries/objects {} are supported as are arrays []. + (QEMU) device_add driver=e1000 id=net1 + {'return': {}} + (QEMU) - example-command arg-name1={'key':'value','obj'={'prop':"value"}} +key=value pairs support either Python or JSON object literal notations, +**without spaces**. Dictionaries/objects ``{}`` are supported, as are +arrays ``[]``:: -Both JSON and Python formatting should work, including both styles of -string literal quotes. Both paradigms of literal values should work, -including null/true/false for JSON and None/True/False for Python. + example-command arg-name1={'key':'value','obj'={'prop':"value"}} +Either JSON or Python formatting for compound values works, including +both styles of string literal quotes (either single or double +quotes). Both paradigms of literal values are accepted, including +``null/true/false`` for JSON and ``None/True/False`` for Python. -Transactions have the following multi-line format: +Transactions +------------ + +Transactions have the following multi-line format:: transaction( action-name1 [ arg-name1=arg1 ] ... [arg-nameN=argN ] @@ -62,11 +79,11 @@ action-nameN [ arg-name1=arg1 ] ... [arg-nameN=argN ] ) -One line transactions are also supported: +One line transactions are also supported:: transaction( action-name1 ... ) -For example: +For example:: (QEMU) transaction( TRANS> block-dirty-bitmap-add node=drive0 name=bitmap1 @@ -75,9 +92,35 @@ {"return": {}} (QEMU) -Use the -v and -p options to activate the verbose and pretty-print options, -which will echo back the properly formatted JSON-compliant QMP that is being -sent to QEMU, which is useful for debugging and documentation generation. +Commands +-------- + +Autocomplete of command names using is supported. Pressing +at a blank CLI prompt will show you a list of all available commands +that the connected QEMU instance supports. + +For documentation on QMP commands and their arguments, please see +`qmp ref`. + +Events +------ + +qmp-shell will display events received from the server, but this version +does not do so asynchronously. To check for new events from the server, +press on a blank line:: + + (QEMU) ⏎ + {'timestamp': {'seconds': 1660071944, 'microseconds': 184667}, + 'event': 'STOP'} + +Display options +--------------- + +Use the -v and -p options to activate the verbose and pretty-print +options, which will echo back the properly formatted JSON-compliant QMP +that is being sent to QEMU. This is useful for debugging to see the +wire-level QMP data being exchanged, and generating output for use in +writing documentation for QEMU. """ import argparse @@ -525,6 +568,8 @@ def common_parser() -> argparse.ArgumentParser: help='Pretty-print JSON') parser.add_argument('-l', '--logfile', help='Save log of all QMP messages to PATH') + # NOTE: When changing arguments, update both this module docstring + # and the manpage synopsis in docs/man/qmp_shell.rst. return parser @@ -567,8 +612,35 @@ def main() -> None: def main_wrap() -> None: """ - qmp-shell-wrap entry point: parse command line arguments and - start the REPL. + qmp-shell-wrap - QEMU + qmp-shell launcher utility + + Launch QEMU and connect to it with `qmp-shell` in a single command. + CLI arguments will be forwarded to qemu, with additional arguments + added to allow `qmp-shell` to then connect to the recently launched + QEMU instance. + + usage: qmp-shell-wrap [-h] [-H] [-v] [-p] [-l LOGFILE] ... + + positional arguments: + command QEMU command line to invoke + + optional arguments: + -h, --help show this help message and exit + -H, --hmp Use HMP interface + -v, --verbose Verbose (echo commands sent and received) + -p, --pretty Pretty-print JSON + -l LOGFILE, --logfile LOGFILE + Save log of all QMP messages to PATH + + Usage + ----- + + Prepend "qmp-shell-wrap" to your usual QEMU command line:: + + > qmp-shell-wrap qemu-system-x86_64 -M q35 -m 4096 -display none + Welcome to the QMP low-level shell! + Connected + (QEMU) """ parser = common_parser() parser.add_argument('command', nargs=argparse.REMAINDER, diff --git a/python/qemu/qmp/util.py b/python/qemu/qmp/util.py index 47ec39a8b5e84..a8229e5524560 100644 --- a/python/qemu/qmp/util.py +++ b/python/qemu/qmp/util.py @@ -49,7 +49,7 @@ def get_or_create_event_loop() -> asyncio.AbstractEventLoop: async def flush(writer: asyncio.StreamWriter) -> None: """ - Utility function to ensure a StreamWriter is *fully* drained. + Utility function to ensure an `asyncio.StreamWriter` is *fully* drained. `asyncio.StreamWriter.drain` only promises we will return to below the "high-water mark". This function ensures we flush the entire @@ -89,7 +89,7 @@ def bottom_half(func: T) -> T: These methods do not, in general, have the ability to directly report information to a caller’s context and will usually be - collected as a Task result instead. + collected as an `asyncio.Task` result instead. They must not call upper-half functions directly. """ @@ -105,8 +105,11 @@ def exception_summary(exc: BaseException) -> str: """ Return a summary string of an arbitrary exception. - It will be of the form "ExceptionType: Error Message", if the error + It will be of the form "ExceptionType: Error Message" if the error string is non-empty, and just "ExceptionType" otherwise. + + This code is based on CPython's implementation of + `traceback.TracebackException.format_exception_only`. """ name = type(exc).__qualname__ smod = type(exc).__module__ From 82c7cb93c750196f513a1b11cb85e0328bad444f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 15 Jul 2025 15:30:16 +0100 Subject: [PATCH 0655/1794] iotests: drop compat for old version context manager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our minimum python is now 3.9, so back compat with prior python versions is no longer required. Signed-off-by: Daniel P. Berrangé --- tests/qemu-iotests/testenv.py | 7 ++----- tests/qemu-iotests/testrunner.py | 9 ++------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py index 6326e46b7b100..29caaa8a349c3 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py @@ -22,15 +22,12 @@ from pathlib import Path import shutil import collections +import contextlib import random import subprocess import glob from typing import List, Dict, Any, Optional -if sys.version_info >= (3, 9): - from contextlib import AbstractContextManager as ContextManager -else: - from typing import ContextManager DEF_GDB_OPTIONS = 'localhost:12345' @@ -58,7 +55,7 @@ def get_default_machine(qemu_prog: str) -> str: return default_machine -class TestEnv(ContextManager['TestEnv']): +class TestEnv(contextlib.AbstractContextManager['TestEnv']): """ Manage system environment for running tests diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py index 2e236c8fa3906..14cc8492f9fb0 100644 --- a/tests/qemu-iotests/testrunner.py +++ b/tests/qemu-iotests/testrunner.py @@ -30,11 +30,6 @@ from typing import List, Optional, Any, Sequence, Dict from testenv import TestEnv -if sys.version_info >= (3, 9): - from contextlib import AbstractContextManager as ContextManager -else: - from typing import ContextManager - def silent_unlink(path: Path) -> None: try: @@ -57,7 +52,7 @@ def file_diff(file1: str, file2: str) -> List[str]: return res -class LastElapsedTime(ContextManager['LastElapsedTime']): +class LastElapsedTime(contextlib.AbstractContextManager['LastElapsedTime']): """ Cache for elapsed time for tests, to show it during new test run It is safe to use get() at any time. To use update(), you must either @@ -112,7 +107,7 @@ def __init__(self, status: str, description: str = '', self.interrupted = interrupted -class TestRunner(ContextManager['TestRunner']): +class TestRunner(contextlib.AbstractContextManager['TestRunner']): shared_self = None @staticmethod From 6ccb48ffc19fe25511313a246d4a8bad51114ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 15 Jul 2025 15:30:20 +0100 Subject: [PATCH 0656/1794] python: ensure QEMUQtestProtocol closes its socket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While QEMUQtestMachine closes the socket that was passed to QEMUQtestProtocol, the python resource leak manager still believes that the copy QEMUQtestProtocol holds is open. We must explicitly call close to avoid this leak warnnig. Signed-off-by: Daniel P. Berrangé --- python/qemu/machine/qtest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/qemu/machine/qtest.py b/python/qemu/machine/qtest.py index 4f5ede85b2372..781f674ffafd6 100644 --- a/python/qemu/machine/qtest.py +++ b/python/qemu/machine/qtest.py @@ -177,6 +177,8 @@ def _post_shutdown(self) -> None: self._qtest_sock_pair[0].close() self._qtest_sock_pair[1].close() self._qtest_sock_pair = None + if self._qtest is not None: + self._qtest.close() super()._post_shutdown() def qtest(self, cmd: str) -> str: From d4d0ebfcc926c11d16320d0d5accf22e3441c115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 15 Jul 2025 15:30:21 +0100 Subject: [PATCH 0657/1794] iotests/147: ensure temporary sockets are closed before exiting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids the python resource leak detector from issuing warnings in the iotests. Signed-off-by: Daniel P. Berrangé --- tests/qemu-iotests/147 | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147 index 6d6f077a14d42..3e14bd389a4ed 100755 --- a/tests/qemu-iotests/147 +++ b/tests/qemu-iotests/147 @@ -277,6 +277,7 @@ class BuiltinNBD(NBDBlockdevAddBase): } } self.client_test(filename, flatten_sock_addr(address), 'nbd-export') + sockfd.close() self._server_down() From 2b2fb25c2aaf5b2e8172d845db39cc50a951a12e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 15 Jul 2025 15:30:22 +0100 Subject: [PATCH 0658/1794] iotests/151: ensure subprocesses are cleaned up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The iotest 151 creates a bunch of subprocesses, with their stdout connected to a pipe but never reads any data from them and does not gurantee the processes are killed on cleanup. This triggers resource leak warnings from python when the subprocess.Popen object is garbage collected. Signed-off-by: Daniel P. Berrangé --- tests/qemu-iotests/151 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/qemu-iotests/151 b/tests/qemu-iotests/151 index f2ff9c5dac221..06ee3585db904 100755 --- a/tests/qemu-iotests/151 +++ b/tests/qemu-iotests/151 @@ -263,6 +263,11 @@ class TestThrottledWithNbdExportBase(iotests.QMPTestCase): break except subprocess.TimeoutExpired: self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') + try: + p.kill() + p.stdout.close() + except: + pass except IndexError: pass From 9a494d83538680651197651031375c2b6fa2490b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 15 Jul 2025 15:30:23 +0100 Subject: [PATCH 0659/1794] iotests/check: always enable all python warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Of most importance is that this gives us a heads-up if anything we rely on has been deprecated. The default python behaviour only emits a warning if triggered from __main__ which is very limited. Setting the env variable further ensures that any python child processes will also display warnings. Signed-off-by: Daniel P. Berrangé --- tests/qemu-iotests/check | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index 545f9ec7bdd8c..d9b7c1d598924 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -21,6 +21,7 @@ import sys import argparse import shutil from pathlib import Path +import warnings from findtests import TestFinder from testenv import TestEnv @@ -137,6 +138,9 @@ def make_argparser() -> argparse.ArgumentParser: if __name__ == '__main__': + warnings.simplefilter("default") + os.environ["PYTHONWARNINGS"] = "default" + args = make_argparser().parse_args() env = TestEnv(source_dir=args.source_dir, From 424dc390ec68263b5fc82b88f0f81bc3f374ad44 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 11 Sep 2025 13:14:15 +0100 Subject: [PATCH 0660/1794] tests, scripts: Don't import print_function from __future__ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of our Python scripts still include the line from __future__ import print_function which is intended to allow a Python 2 to handle the Python 3 print() syntax. This particular part of the future arrived many years ago, and our minimum Python version is 3.9, so we don't need to keep this line around. NB: the scripts in tests/tcg/*/gdbstub/ are run with whatever Python gdb was built against, but we can safely assume that that was a Python 3 because our supported distros are all on Python 3. In any case these are only run as part of "make check-tcg", not by end-users. Commit created with: sed -i -e '/import print_function/d' $(git grep -l 'from __future__') Signed-off-by: Peter Maydell Reviewed-by: Daniel P. Berrangé Reviewed-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250819102409.2117969-1-peter.maydell@linaro.org --- scripts/userfaultfd-wrlat.py | 1 - tests/guest-debug/test_gdbstub.py | 1 - tests/tcg/aarch64/gdbstub/test-mte.py | 1 - tests/tcg/aarch64/gdbstub/test-sve-ioctl.py | 1 - tests/tcg/aarch64/gdbstub/test-sve.py | 1 - tests/tcg/multiarch/gdbstub/interrupt.py | 1 - tests/tcg/multiarch/gdbstub/memory.py | 1 - tests/tcg/multiarch/gdbstub/sha1.py | 1 - tests/tcg/multiarch/gdbstub/test-proc-mappings.py | 1 - tests/tcg/multiarch/gdbstub/test-qxfer-auxv-read.py | 1 - tests/tcg/multiarch/gdbstub/test-qxfer-siginfo-read.py | 1 - tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py | 1 - tests/tcg/s390x/gdbstub/test-signals-s390x.py | 1 - tests/tcg/s390x/gdbstub/test-svc.py | 1 - 14 files changed, 14 deletions(-) diff --git a/scripts/userfaultfd-wrlat.py b/scripts/userfaultfd-wrlat.py index 0684be4e04487..a61a9abbfcb25 100755 --- a/scripts/userfaultfd-wrlat.py +++ b/scripts/userfaultfd-wrlat.py @@ -17,7 +17,6 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from __future__ import print_function from bcc import BPF from ctypes import c_ushort, c_int, c_ulonglong from time import sleep diff --git a/tests/guest-debug/test_gdbstub.py b/tests/guest-debug/test_gdbstub.py index 4f08089e6a982..e017ccb55d790 100644 --- a/tests/guest-debug/test_gdbstub.py +++ b/tests/guest-debug/test_gdbstub.py @@ -1,7 +1,6 @@ """Helper functions for gdbstub testing """ -from __future__ import print_function import argparse import gdb import os diff --git a/tests/tcg/aarch64/gdbstub/test-mte.py b/tests/tcg/aarch64/gdbstub/test-mte.py index 9ad98e7a54c84..f4a7d7b446501 100644 --- a/tests/tcg/aarch64/gdbstub/test-mte.py +++ b/tests/tcg/aarch64/gdbstub/test-mte.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test GDB memory-tag commands that exercise the stubs for the qIsAddressTagged, # qMemTag, and QMemTag packets, which are used for manipulating allocation tags. diff --git a/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py b/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py index a78a3a2514da2..2c5c2180319f4 100644 --- a/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py +++ b/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test the SVE ZReg reports the right amount of data. It uses the # sve-ioctl test and examines the register data each time the diff --git a/tests/tcg/aarch64/gdbstub/test-sve.py b/tests/tcg/aarch64/gdbstub/test-sve.py index 84cdcd4a32ea1..7b0489a622b8f 100644 --- a/tests/tcg/aarch64/gdbstub/test-sve.py +++ b/tests/tcg/aarch64/gdbstub/test-sve.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test the SVE registers are visible and changeable via gdbstub # diff --git a/tests/tcg/multiarch/gdbstub/interrupt.py b/tests/tcg/multiarch/gdbstub/interrupt.py index 2d5654d154025..4eccdb41b9744 100644 --- a/tests/tcg/multiarch/gdbstub/interrupt.py +++ b/tests/tcg/multiarch/gdbstub/interrupt.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test some of the system debug features with the multiarch memory # test. It is a port of the original vmlinux focused test case but diff --git a/tests/tcg/multiarch/gdbstub/memory.py b/tests/tcg/multiarch/gdbstub/memory.py index 532b92e7fb31e..76d75e52512ed 100644 --- a/tests/tcg/multiarch/gdbstub/memory.py +++ b/tests/tcg/multiarch/gdbstub/memory.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test some of the system debug features with the multiarch memory # test. It is a port of the original vmlinux focused test case but diff --git a/tests/tcg/multiarch/gdbstub/sha1.py b/tests/tcg/multiarch/gdbstub/sha1.py index 1ce711a402ced..3403b82fd4a77 100644 --- a/tests/tcg/multiarch/gdbstub/sha1.py +++ b/tests/tcg/multiarch/gdbstub/sha1.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # A very simple smoke test for debugging the SHA1 userspace test on # each target. diff --git a/tests/tcg/multiarch/gdbstub/test-proc-mappings.py b/tests/tcg/multiarch/gdbstub/test-proc-mappings.py index 6eb6ebf7b170b..796dca75f0cb8 100644 --- a/tests/tcg/multiarch/gdbstub/test-proc-mappings.py +++ b/tests/tcg/multiarch/gdbstub/test-proc-mappings.py @@ -1,7 +1,6 @@ """Test that gdbstub has access to proc mappings. This runs as a sourced script (via -x, via run-test.py).""" -from __future__ import print_function import gdb from test_gdbstub import gdb_exit, main, report diff --git a/tests/tcg/multiarch/gdbstub/test-qxfer-auxv-read.py b/tests/tcg/multiarch/gdbstub/test-qxfer-auxv-read.py index 00c26ab4a9567..fa36c943d6681 100644 --- a/tests/tcg/multiarch/gdbstub/test-qxfer-auxv-read.py +++ b/tests/tcg/multiarch/gdbstub/test-qxfer-auxv-read.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test auxiliary vector is loaded via gdbstub # diff --git a/tests/tcg/multiarch/gdbstub/test-qxfer-siginfo-read.py b/tests/tcg/multiarch/gdbstub/test-qxfer-siginfo-read.py index 862596b07a765..b18fa1234fb54 100644 --- a/tests/tcg/multiarch/gdbstub/test-qxfer-siginfo-read.py +++ b/tests/tcg/multiarch/gdbstub/test-qxfer-siginfo-read.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test gdbstub Xfer:siginfo:read stub. # diff --git a/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py b/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py index 4d6b6b9fbe7b5..49cbc3548f607 100644 --- a/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py +++ b/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test auxiliary vector is loaded via gdbstub # diff --git a/tests/tcg/s390x/gdbstub/test-signals-s390x.py b/tests/tcg/s390x/gdbstub/test-signals-s390x.py index b6b7b39fc46dc..398ad534ebf6c 100644 --- a/tests/tcg/s390x/gdbstub/test-signals-s390x.py +++ b/tests/tcg/s390x/gdbstub/test-signals-s390x.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test that signals and debugging mix well together on s390x. diff --git a/tests/tcg/s390x/gdbstub/test-svc.py b/tests/tcg/s390x/gdbstub/test-svc.py index 17210b4e02083..29a0aa0ede453 100644 --- a/tests/tcg/s390x/gdbstub/test-svc.py +++ b/tests/tcg/s390x/gdbstub/test-svc.py @@ -1,7 +1,6 @@ """Test single-stepping SVC. This runs as a sourced script (via -x, via run-test.py).""" -from __future__ import print_function import gdb from test_gdbstub import main, report From 71eba045758289e12133c4977f81c9132325c648 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:05 +1000 Subject: [PATCH 0661/1794] linux-user/aarch64: Split out signal_for_exception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/aarch64/cpu_loop.c | 84 ++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 4c4921152e84a..3b2782581b608 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -27,11 +27,56 @@ #include "target/arm/syndrome.h" #include "target/arm/cpu-features.h" +/* Use the exception syndrome to map a cpu exception to a signal. */ +static void signal_for_exception(CPUARMState *env, vaddr addr) +{ + uint32_t syn = env->exception.syndrome; + int si_code, si_signo; + + switch (syn_get_ec(syn)) { + case EC_DATAABORT: + case EC_INSNABORT: + /* Both EC have the same format for FSC, or close enough. */ + switch (extract32(syn, 0, 6)) { + case 0x04 ... 0x07: /* Translation fault, level {0-3} */ + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_MAPERR; + break; + case 0x09 ... 0x0b: /* Access flag fault, level {1-3} */ + case 0x0d ... 0x0f: /* Permission fault, level {1-3} */ + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_ACCERR; + break; + case 0x11: /* Synchronous Tag Check Fault */ + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_MTESERR; + break; + case 0x21: /* Alignment fault */ + si_signo = TARGET_SIGBUS; + si_code = TARGET_BUS_ADRALN; + break; + default: + g_assert_not_reached(); + } + break; + + case EC_PCALIGNMENT: + si_signo = TARGET_SIGBUS; + si_code = TARGET_BUS_ADRALN; + break; + + default: + g_assert_not_reached(); + } + + force_sig_fault(si_signo, si_code, addr); +} + /* AArch64 main loop */ void cpu_loop(CPUARMState *env) { CPUState *cs = env_cpu(env); - int trapnr, ec, fsc, si_code, si_signo; + int trapnr; abi_long ret; for (;;) { @@ -67,42 +112,7 @@ void cpu_loop(CPUARMState *env) break; case EXCP_PREFETCH_ABORT: case EXCP_DATA_ABORT: - ec = syn_get_ec(env->exception.syndrome); - switch (ec) { - case EC_DATAABORT: - case EC_INSNABORT: - /* Both EC have the same format for FSC, or close enough. */ - fsc = extract32(env->exception.syndrome, 0, 6); - switch (fsc) { - case 0x04 ... 0x07: /* Translation fault, level {0-3} */ - si_signo = TARGET_SIGSEGV; - si_code = TARGET_SEGV_MAPERR; - break; - case 0x09 ... 0x0b: /* Access flag fault, level {1-3} */ - case 0x0d ... 0x0f: /* Permission fault, level {1-3} */ - si_signo = TARGET_SIGSEGV; - si_code = TARGET_SEGV_ACCERR; - break; - case 0x11: /* Synchronous Tag Check Fault */ - si_signo = TARGET_SIGSEGV; - si_code = TARGET_SEGV_MTESERR; - break; - case 0x21: /* Alignment fault */ - si_signo = TARGET_SIGBUS; - si_code = TARGET_BUS_ADRALN; - break; - default: - g_assert_not_reached(); - } - break; - case EC_PCALIGNMENT: - si_signo = TARGET_SIGBUS; - si_code = TARGET_BUS_ADRALN; - break; - default: - g_assert_not_reached(); - } - force_sig_fault(si_signo, si_code, env->exception.vaddress); + signal_for_exception(env, env->exception.vaddress); break; case EXCP_DEBUG: case EXCP_BKPT: From 5fe3151c5e18eb15347c2ea572b551c7aed67a9f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:06 +1000 Subject: [PATCH 0662/1794] linux-user/aarch64: Check syndrome for EXCP_UDEF Note that we have been passing the incorrect code for most exception codes: uncategorized (do_el0_undef), systemregistertrap (do_el0_sys), smetrap (do_sme_acc), btitrap (do_el0_bti) and illegalstate (bad_el0_sync). Only pacfail uses ILL_ILLOPN (do_el0_fpac). Note that EC_MOP (do_el0_mops) ought not signal at all. For now, preserve existing behavior signalling ILL_ILLOPN. List all other exception codes and document why they do not apply to user-only. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/aarch64/cpu_loop.c | 75 ++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 3b2782581b608..7ad26316dead3 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -65,6 +65,79 @@ static void signal_for_exception(CPUARMState *env, vaddr addr) si_code = TARGET_BUS_ADRALN; break; + case EC_UNCATEGORIZED: /* E.g. undefined instruction */ + case EC_SYSTEMREGISTERTRAP: /* E.g. inaccessible register */ + case EC_SMETRAP: /* E.g. invalid insn in streaming state */ + case EC_BTITRAP: /* E.g. invalid guarded branch target */ + case EC_ILLEGALSTATE: + /* + * Illegal state happens via an ERET from a privileged mode, + * so is not normally possible from user-only. However, gdbstub + * is not prevented from writing CPSR_IL, aka PSTATE.IL, which + * would generate a trap from the next translated block. + * In the kernel, default case -> el0_inv -> bad_el0_sync. + */ + si_signo = TARGET_SIGILL; + si_code = TARGET_ILL_ILLOPC; + break; + + case EC_PACFAIL: + si_signo = TARGET_SIGILL; + si_code = TARGET_ILL_ILLOPN; + break; + + case EC_MOP: + /* + * FIXME: The kernel fixes up wrong-option exceptions. + * For QEMU linux-user mode, you can only get these if + * the process is doing something silly (not executing + * the MOPS instructions in the required P/M/E sequence), + * so it is not a problem in practice that we do not. + * + * We ought ideally to implement the same "rewind to the + * start of the sequence" logic that the kernel does in + * arm64_mops_reset_regs(). In the meantime, deliver + * the guest a SIGILL, with the same ILLOPN si_code + * we've always used for this. + */ + si_signo = TARGET_SIGILL; + si_code = TARGET_ILL_ILLOPN; + break; + + case EC_WFX_TRAP: /* user-only WFI implemented as NOP */ + case EC_CP15RTTRAP: /* AArch32 */ + case EC_CP15RRTTRAP: /* AArch32 */ + case EC_CP14RTTRAP: /* AArch32 */ + case EC_CP14DTTRAP: /* AArch32 */ + case EC_ADVSIMDFPACCESSTRAP: /* user-only does not disable fpu */ + case EC_FPIDTRAP: /* AArch32 */ + case EC_PACTRAP: /* user-only does not disable pac regs */ + case EC_BXJTRAP: /* AArch32 */ + case EC_CP14RRTTRAP: /* AArch32 */ + case EC_AA32_SVC: /* AArch32 */ + case EC_AA32_HVC: /* AArch32 */ + case EC_AA32_SMC: /* AArch32 */ + case EC_AA64_SVC: /* generates EXCP_SWI */ + case EC_AA64_HVC: /* user-only generates EC_UNCATEGORIZED */ + case EC_AA64_SMC: /* user-only generates EC_UNCATEGORIZED */ + case EC_SVEACCESSTRAP: /* user-only does not disable sve */ + case EC_ERETTRAP: /* user-only generates EC_UNCATEGORIZED */ + case EC_GPC: /* user-only has no EL3 gpc tables */ + case EC_INSNABORT_SAME_EL: /* el0 cannot trap to el0 */ + case EC_DATAABORT_SAME_EL: /* el0 cannot trap to el0 */ + case EC_SPALIGNMENT: /* sp alignment checks not implemented */ + case EC_AA32_FPTRAP: /* fp exceptions not implemented */ + case EC_AA64_FPTRAP: /* fp exceptions not implemented */ + case EC_SERROR: /* user-only does not have hw faults */ + case EC_BREAKPOINT: /* user-only does not have hw debug */ + case EC_BREAKPOINT_SAME_EL: /* user-only does not have hw debug */ + case EC_SOFTWARESTEP: /* user-only does not have hw debug */ + case EC_SOFTWARESTEP_SAME_EL: /* user-only does not have hw debug */ + case EC_WATCHPOINT: /* user-only does not have hw debug */ + case EC_WATCHPOINT_SAME_EL: /* user-only does not have hw debug */ + case EC_AA32_BKPT: /* AArch32 */ + case EC_VECTORCATCH: /* AArch32 */ + case EC_AA64_BKPT: /* generates EXCP_BKPT */ default: g_assert_not_reached(); } @@ -108,7 +181,7 @@ void cpu_loop(CPUARMState *env) /* just indicate that signals should be handled asap */ break; case EXCP_UDEF: - force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPN, env->pc); + signal_for_exception(env, env->pc); break; case EXCP_PREFETCH_ABORT: case EXCP_DATA_ABORT: From e1b31ba94d143c5026b199c5df7523fa4d9fa4cf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:07 +1000 Subject: [PATCH 0663/1794] linux-user/aarch64: Generate ESR signal records Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/aarch64/cpu_loop.c | 3 +++ linux-user/aarch64/signal.c | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 7ad26316dead3..6060572eed3bd 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -33,6 +33,9 @@ static void signal_for_exception(CPUARMState *env, vaddr addr) uint32_t syn = env->exception.syndrome; int si_code, si_signo; + /* Let signal delivery see that ESR is live. */ + env->cp15.esr_el[1] = syn; + switch (syn_get_ec(syn)) { case EC_DATAABORT: case EC_INSNABORT: diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index 668353bbda4bb..ef97be3ac7bd3 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -65,6 +65,13 @@ struct target_fpsimd_context { uint64_t vregs[32 * 2]; /* really uint128_t vregs[32] */ }; +#define TARGET_ESR_MAGIC 0x45535201 + +struct target_esr_context { + struct target_aarch64_ctx head; + uint64_t esr; +}; + #define TARGET_EXTRA_MAGIC 0x45585401 struct target_extra_context { @@ -201,6 +208,14 @@ static void target_setup_fpsimd_record(struct target_fpsimd_context *fpsimd, } } +static void target_setup_esr_record(struct target_esr_context *ctx, + CPUARMState *env) +{ + __put_user(TARGET_ESR_MAGIC, &ctx->head.magic); + __put_user(sizeof(*ctx), &ctx->head.size); + __put_user(env->cp15.esr_el[1], &ctx->esr); +} + static void target_setup_extra_record(struct target_extra_context *extra, uint64_t datap, uint32_t extra_size) { @@ -531,6 +546,9 @@ static int target_restore_sigframe(CPUARMState *env, fpsimd = (struct target_fpsimd_context *)ctx; break; + case TARGET_ESR_MAGIC: + break; /* ignore */ + case TARGET_SVE_MAGIC: if (sve || size < sizeof(struct target_sve_context)) { goto err; @@ -683,7 +701,7 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, uc.tuc_mcontext.__reserved), }; int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0, tpidr2_ofs = 0; - int zt_ofs = 0; + int zt_ofs = 0, esr_ofs = 0; int sve_size = 0, za_size = 0, tpidr2_size = 0, zt_size = 0; struct target_rt_sigframe *frame; struct target_rt_frame_record *fr; @@ -693,6 +711,15 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, fpsimd_ofs = alloc_sigframe_space(sizeof(struct target_fpsimd_context), &layout); + /* + * In user mode, ESR_EL1 is only set by cpu_loop while queueing the + * signal, and it's only valid for the one sync insn. + */ + if (env->cp15.esr_el[1]) { + esr_ofs = alloc_sigframe_space(sizeof(struct target_esr_context), + &layout); + } + /* SVE state needs saving only if it exists. */ if (cpu_isar_feature(aa64_sve, env_archcpu(env)) || cpu_isar_feature(aa64_sme, env_archcpu(env))) { @@ -754,6 +781,11 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, target_setup_general_frame(frame, env, set); target_setup_fpsimd_record((void *)frame + fpsimd_ofs, env); + if (esr_ofs) { + target_setup_esr_record((void *)frame + esr_ofs, env); + /* Leave ESR_EL1 clear while it's not relevant. */ + env->cp15.esr_el[1] = 0; + } target_setup_end_record((void *)frame + layout.std_end_ofs); if (layout.extra_ofs) { target_setup_extra_record((void *)frame + layout.extra_ofs, From 76fea609082d9673449a1f6aca9a28af6f20f8cf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:08 +1000 Subject: [PATCH 0664/1794] target/arm: Add prot_check parameter to pmsav8_mpu_lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Separate the access_type from the protection check. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/internals.h | 5 +++-- target/arm/ptw.c | 11 ++++++----- target/arm/tcg/m_helper.c | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index f5a1e75db376d..899242e572ff2 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1624,8 +1624,9 @@ bool get_phys_addr_with_space_nogpc(CPUARMState *env, vaddr address, __attribute__((nonnull)); bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - bool is_secure, GetPhysAddrResult *result, + MMUAccessType access_type, unsigned prot_check, + ARMMMUIdx mmu_idx, bool is_secure, + GetPhysAddrResult *result, ARMMMUFaultInfo *fi, uint32_t *mregion); void arm_log_exception(CPUState *cs); diff --git a/target/arm/ptw.c b/target/arm/ptw.c index ed5c728eab6fa..9652f40ff8236 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -2561,8 +2561,9 @@ static uint32_t *regime_rlar(CPUARMState *env, ARMMMUIdx mmu_idx, } bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - bool secure, GetPhysAddrResult *result, + MMUAccessType access_type, unsigned prot_check, + ARMMMUIdx mmu_idx, bool secure, + GetPhysAddrResult *result, ARMMMUFaultInfo *fi, uint32_t *mregion) { /* @@ -2750,7 +2751,7 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, if (arm_feature(env, ARM_FEATURE_M)) { fi->level = 1; } - return !(result->f.prot & (1 << access_type)); + return (prot_check & ~result->f.prot) != 0; } static bool v8m_is_sau_exempt(CPUARMState *env, @@ -2952,8 +2953,8 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, } } - ret = pmsav8_mpu_lookup(env, address, access_type, mmu_idx, secure, - result, fi, NULL); + ret = pmsav8_mpu_lookup(env, address, access_type, 1 << access_type, + mmu_idx, secure, result, fi, NULL); if (sattrs.subpage) { result->f.lg_page_size = 0; } diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index 28307b56151b9..d856e3bc8e2ab 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -2829,8 +2829,8 @@ uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) ARMMMUFaultInfo fi = {}; /* We can ignore the return value as prot is always set */ - pmsav8_mpu_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, targetsec, - &res, &fi, &mregion); + pmsav8_mpu_lookup(env, addr, MMU_DATA_LOAD, PAGE_READ, mmu_idx, + targetsec, &res, &fi, &mregion); if (mregion == -1) { mrvalid = false; mregion = 0; From 015cefc0ed65ef054616171128f5962f1cccb2d1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:09 +1000 Subject: [PATCH 0665/1794] target/arm: Add in_prot_check to S1Translate Separate the access_type from the protection check. Save the trouble of modifying all helper functions by passing the new data in the control structure. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 9652f40ff8236..d37c0ce0f1d3f 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -64,6 +64,12 @@ typedef struct S1Translate { * Stage 2 is indicated by in_mmu_idx set to ARMMMUIdx_Stage2{,_S}. */ bool in_s1_is_el0; + /* + * The set of PAGE_* bits to be use in the permission check. + * This is normally directly related to the access_type, but + * may be suppressed for debug or AT insns. + */ + uint8_t in_prot_check; bool out_rw; bool out_be; ARMSecuritySpace out_space; @@ -581,6 +587,7 @@ static bool S1_ptw_translate(CPUARMState *env, S1Translate *ptw, .in_ptw_idx = ptw_idx_for_stage_2(env, s2_mmu_idx), .in_space = s2_space, .in_debug = true, + .in_prot_check = PAGE_READ, }; GetPhysAddrResult s2 = { }; @@ -1089,7 +1096,7 @@ static bool get_phys_addr_v5(CPUARMState *env, S1Translate *ptw, } result->f.prot = ap_to_rw_prot(env, ptw->in_mmu_idx, ap, domain_prot); result->f.prot |= result->f.prot ? PAGE_EXEC : 0; - if (!(result->f.prot & (1 << access_type))) { + if (ptw->in_prot_check & ~result->f.prot) { /* Access permission fault. */ fi->type = ARMFault_Permission; goto do_fault; @@ -1243,7 +1250,7 @@ static bool get_phys_addr_v6(CPUARMState *env, S1Translate *ptw, result->f.prot = get_S1prot(env, mmu_idx, false, user_rw, prot_rw, xn, pxn, result->f.attrs.space, out_space); - if (!(result->f.prot & (1 << access_type))) { + if (ptw->in_prot_check & ~result->f.prot) { /* Access permission fault. */ fi->type = ARMFault_Permission; goto do_fault; @@ -2123,7 +2130,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, result->f.tlb_fill_flags = 0; } - if (!(result->f.prot & (1 << access_type))) { + if (ptw->in_prot_check & ~result->f.prot) { fi->type = ARMFault_Permission; goto do_fault; } @@ -2537,7 +2544,7 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, fi->type = ARMFault_Permission; fi->level = 1; - return !(result->f.prot & (1 << access_type)); + return (ptw->in_prot_check & ~result->f.prot) != 0; } static uint32_t *regime_rbar(CPUARMState *env, ARMMMUIdx mmu_idx, @@ -2953,7 +2960,7 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, } } - ret = pmsav8_mpu_lookup(env, address, access_type, 1 << access_type, + ret = pmsav8_mpu_lookup(env, address, access_type, ptw->in_prot_check, mmu_idx, secure, result, fi, NULL); if (sattrs.subpage) { result->f.lg_page_size = 0; @@ -3625,6 +3632,7 @@ bool get_phys_addr(CPUARMState *env, vaddr address, S1Translate ptw = { .in_mmu_idx = mmu_idx, .in_space = arm_mmu_idx_to_security_space(env, mmu_idx), + .in_prot_check = 1 << access_type, }; return get_phys_addr_gpc(env, &ptw, address, access_type, @@ -3638,6 +3646,7 @@ static hwaddr arm_cpu_get_phys_page(CPUARMState *env, vaddr addr, .in_mmu_idx = mmu_idx, .in_space = arm_mmu_idx_to_security_space(env, mmu_idx), .in_debug = true, + .in_prot_check = PAGE_READ, }; GetPhysAddrResult res = {}; ARMMMUFaultInfo fi = {}; From 7e130764415e0a15faf09c5c26043b016ba798fc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:10 +1000 Subject: [PATCH 0666/1794] target/arm: Skip permission check from arm_cpu_get_phys_page_attrs_debug Do not require read permission when translating addresses for debugging purposes. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index d37c0ce0f1d3f..5d85610de29d7 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -3646,7 +3646,7 @@ static hwaddr arm_cpu_get_phys_page(CPUARMState *env, vaddr addr, .in_mmu_idx = mmu_idx, .in_space = arm_mmu_idx_to_security_space(env, mmu_idx), .in_debug = true, - .in_prot_check = PAGE_READ, + .in_prot_check = 0, }; GetPhysAddrResult res = {}; ARMMMUFaultInfo fi = {}; From 8818b2d91363dc6b478edc4e6325e958e7348648 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:11 +1000 Subject: [PATCH 0667/1794] target/arm: Introduce get_phys_addr_for_at Rename get_phys_addr_with_space_nogpc for its only caller, do_ats_write. Drop the MemOp memop argument as it doesn't make sense in the new context. Replace the access_type parameter with prot_check. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/internals.h | 20 ++++++++------------ target/arm/ptw.c | 21 ++++++++++++++------- target/arm/tcg/cpregs-at.c | 11 ++--------- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index 899242e572ff2..8782594b774ca 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1602,25 +1602,21 @@ bool get_phys_addr(CPUARMState *env, vaddr address, __attribute__((nonnull)); /** - * get_phys_addr_with_space_nogpc: get the physical address for a virtual - * address + * get_phys_addr_for_at: * @env: CPUARMState * @address: virtual address to get physical address for - * @access_type: 0 for read, 1 for write, 2 for execute - * @memop: memory operation feeding this access, or 0 for none + * @prot_check: PAGE_{READ,WRITE,EXEC}, or 0 * @mmu_idx: MMU index indicating required translation regime * @space: security space for the access * @result: set on translation success. * @fi: set to fault info if the translation fails * - * Similar to get_phys_addr, but use the given security space and don't perform - * a Granule Protection Check on the resulting address. - */ -bool get_phys_addr_with_space_nogpc(CPUARMState *env, vaddr address, - MMUAccessType access_type, MemOp memop, - ARMMMUIdx mmu_idx, ARMSecuritySpace space, - GetPhysAddrResult *result, - ARMMMUFaultInfo *fi) + * Similar to get_phys_addr, but for use by AccessType_AT, i.e. + * system instructions for address translation. + */ +bool get_phys_addr_for_at(CPUARMState *env, vaddr address, unsigned prot_check, + ARMMMUIdx mmu_idx, ARMSecuritySpace space, + GetPhysAddrResult *result, ARMMMUFaultInfo *fi) __attribute__((nonnull)); bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 5d85610de29d7..8925c9a6100cf 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -3545,18 +3545,25 @@ static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw, return false; } -bool get_phys_addr_with_space_nogpc(CPUARMState *env, vaddr address, - MMUAccessType access_type, MemOp memop, - ARMMMUIdx mmu_idx, ARMSecuritySpace space, - GetPhysAddrResult *result, - ARMMMUFaultInfo *fi) +bool get_phys_addr_for_at(CPUARMState *env, vaddr address, + unsigned prot_check, ARMMMUIdx mmu_idx, + ARMSecuritySpace space, GetPhysAddrResult *result, + ARMMMUFaultInfo *fi) { S1Translate ptw = { .in_mmu_idx = mmu_idx, .in_space = space, + .in_prot_check = prot_check, }; - return get_phys_addr_nogpc(env, &ptw, address, access_type, - memop, result, fi); + /* + * I_MXTJT: Granule protection checks are not performed on the final + * address of a successful translation. This is a translation not a + * memory reference, so MMU_DATA_LOAD is arbitrary (the exact protection + * check is handled or bypassed by .in_prot_check) and "memop = MO_8" + * bypasses any alignment check. + */ + return get_phys_addr_nogpc(env, &ptw, address, + MMU_DATA_LOAD, MO_8, result, fi); } static ARMSecuritySpace diff --git a/target/arm/tcg/cpregs-at.c b/target/arm/tcg/cpregs-at.c index 398a61d39891d..2ff0b3e76f7a4 100644 --- a/target/arm/tcg/cpregs-at.c +++ b/target/arm/tcg/cpregs-at.c @@ -27,19 +27,12 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, MMUAccessType access_type, ARMMMUIdx mmu_idx, ARMSecuritySpace ss) { - bool ret; uint64_t par64; bool format64 = false; ARMMMUFaultInfo fi = {}; GetPhysAddrResult res = {}; - - /* - * I_MXTJT: Granule protection checks are not performed on the final - * address of a successful translation. This is a translation not a - * memory reference, so "memop = none = 0". - */ - ret = get_phys_addr_with_space_nogpc(env, value, access_type, 0, - mmu_idx, ss, &res, &fi); + bool ret = get_phys_addr_for_at(env, value, 1 << access_type, + mmu_idx, ss, &res, &fi); /* * ATS operations only do S1 or S1+S2 translations, so we never From efebeec13d076100191a4a5a98f047c46c0d592c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:12 +1000 Subject: [PATCH 0668/1794] target/arm: Skip AF and DB updates for AccessType_AT We are required to skip DB update for AT instructions, and we are allowed to skip AF updates. Choose to skip both. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 8925c9a6100cf..089eeff845c65 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -58,6 +58,12 @@ typedef struct S1Translate { * and will not change the state of the softmmu TLBs. */ bool in_debug; + /* + * in_at: is this AccessType_AT? + * This is also set for debug, because at heart that is also + * an address translation, and simplifies a test. + */ + bool in_at; /* * If this is stage 2 of a stage 1+2 page table walk, then this must * be true if stage 1 is an EL0 access; otherwise this is ignored. @@ -1929,7 +1935,12 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, descaddr &= ~(hwaddr)(page_size - 1); descaddr |= (address & (page_size - 1)); - if (likely(!ptw->in_debug)) { + /* + * For AccessType_AT, DB is not updated (AArch64.SetDirtyFlag), + * and it is IMPLEMENTATION DEFINED whether AF is updated + * (AArch64.SetAccessFlag; qemu chooses to not update). + */ + if (likely(!ptw->in_at)) { /* * Access flag. * If HA is enabled, prepare to update the descriptor below. @@ -3553,6 +3564,7 @@ bool get_phys_addr_for_at(CPUARMState *env, vaddr address, S1Translate ptw = { .in_mmu_idx = mmu_idx, .in_space = space, + .in_at = true, .in_prot_check = prot_check, }; /* @@ -3653,6 +3665,7 @@ static hwaddr arm_cpu_get_phys_page(CPUARMState *env, vaddr addr, .in_mmu_idx = mmu_idx, .in_space = arm_mmu_idx_to_security_space(env, mmu_idx), .in_debug = true, + .in_at = true, .in_prot_check = 0, }; GetPhysAddrResult res = {}; From 95901c43a8845a86a6895a8bd5d141f3ce36b3d1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:13 +1000 Subject: [PATCH 0669/1794] target/arm: Add prot_check parameter to do_ats_write Separate protection check from access type, in preparation for skipping the protection check altogether. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/cpregs-at.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/target/arm/tcg/cpregs-at.c b/target/arm/tcg/cpregs-at.c index 2ff0b3e76f7a4..bebf1689970c8 100644 --- a/target/arm/tcg/cpregs-at.c +++ b/target/arm/tcg/cpregs-at.c @@ -24,14 +24,14 @@ static int par_el1_shareability(GetPhysAddrResult *res) } static uint64_t do_ats_write(CPUARMState *env, uint64_t value, - MMUAccessType access_type, ARMMMUIdx mmu_idx, + unsigned prot_check, ARMMMUIdx mmu_idx, ARMSecuritySpace ss) { uint64_t par64; bool format64 = false; ARMMMUFaultInfo fi = {}; GetPhysAddrResult res = {}; - bool ret = get_phys_addr_for_at(env, value, 1 << access_type, + bool ret = get_phys_addr_for_at(env, value, prot_check, mmu_idx, ss, &res, &fi); /* @@ -191,7 +191,7 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD; + unsigned access_perm = ri->opc2 & 1 ? PAGE_WRITE : PAGE_READ; uint64_t par64; ARMMMUIdx mmu_idx; int el = arm_current_el(env); @@ -253,7 +253,7 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) g_assert_not_reached(); } - par64 = do_ats_write(env, value, access_type, mmu_idx, ss); + par64 = do_ats_write(env, value, access_perm, mmu_idx, ss); A32_BANKED_CURRENT_REG_SET(env, par, par64); } @@ -261,11 +261,11 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) static void ats1h_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD; + unsigned access_perm = ri->opc2 & 1 ? PAGE_WRITE : PAGE_READ; uint64_t par64; /* There is no SecureEL2 for AArch32. */ - par64 = do_ats_write(env, value, access_type, ARMMMUIdx_E2, + par64 = do_ats_write(env, value, access_perm, ARMMMUIdx_E2, ARMSS_NonSecure); A32_BANKED_CURRENT_REG_SET(env, par, par64); @@ -309,7 +309,7 @@ static CPAccessResult at_s1e01_access(CPUARMState *env, const ARMCPRegInfo *ri, static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD; + unsigned access_perm = ri->opc2 & 1 ? PAGE_WRITE : PAGE_READ; ARMMMUIdx mmu_idx; uint64_t hcr_el2 = arm_hcr_el2_eff(env); bool regime_e20 = (hcr_el2 & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE); @@ -352,7 +352,7 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, } ss = for_el3 ? arm_security_space(env) : arm_security_space_below_el3(env); - env->cp15.par_el[1] = do_ats_write(env, value, access_type, mmu_idx, ss); + env->cp15.par_el[1] = do_ats_write(env, value, access_perm, mmu_idx, ss); } static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri, From b41cfb6d17ee29f3c5474d8a8f79260535a11335 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:14 +1000 Subject: [PATCH 0670/1794] target/arm: Fill in HFG[RWI]TR_EL2 bits for Arm v9.5 Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpregs.h | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index c9506aa6d5744..1d103b577f7df 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -408,10 +408,19 @@ FIELD(HFGRTR_EL2, ERXPFGCTL_EL1, 47, 1) FIELD(HFGRTR_EL2, ERXPFGCDN_EL1, 48, 1) FIELD(HFGRTR_EL2, ERXADDR_EL1, 49, 1) FIELD(HFGRTR_EL2, NACCDATA_EL1, 50, 1) -/* 51-53: RES0 */ +/* 51: RES0 */ +FIELD(HFGRTR_EL2, NGCS_EL0, 52, 1) +FIELD(HFGRTR_EL2, NGCS_EL1, 53, 1) FIELD(HFGRTR_EL2, NSMPRI_EL1, 54, 1) FIELD(HFGRTR_EL2, NTPIDR2_EL0, 55, 1) -/* 56-63: RES0 */ +FIELD(HFGRTR_EL2, NRCWMASK_EL1, 56, 1) +FIELD(HFGRTR_EL2, NPIRE0_EL1, 57, 1) +FIELD(HFGRTR_EL2, NPIR_EL1, 58, 1) +FIELD(HFGRTR_EL2, NPOR_EL0, 59, 1) +FIELD(HFGRTR_EL2, NPOR_EL1, 60, 1) +FIELD(HFGRTR_EL2, NS2POR_EL1, 61, 1) +FIELD(HFGRTR_EL2, NMAIR2_EL1, 62, 1) +FIELD(HFGRTR_EL2, NAMAIR2_EL1, 63, 1) /* These match HFGRTR but bits for RO registers are RES0 */ FIELD(HFGWTR_EL2, AFSR0_EL1, 0, 1) @@ -452,8 +461,18 @@ FIELD(HFGWTR_EL2, ERXPFGCTL_EL1, 47, 1) FIELD(HFGWTR_EL2, ERXPFGCDN_EL1, 48, 1) FIELD(HFGWTR_EL2, ERXADDR_EL1, 49, 1) FIELD(HFGWTR_EL2, NACCDATA_EL1, 50, 1) +FIELD(HFGWTR_EL2, NGCS_EL0, 52, 1) +FIELD(HFGWTR_EL2, NGCS_EL1, 53, 1) FIELD(HFGWTR_EL2, NSMPRI_EL1, 54, 1) FIELD(HFGWTR_EL2, NTPIDR2_EL0, 55, 1) +FIELD(HFGWTR_EL2, NRCWMASK_EL1, 56, 1) +FIELD(HFGWTR_EL2, NPIRE0_EL1, 57, 1) +FIELD(HFGWTR_EL2, NPIR_EL1, 58, 1) +FIELD(HFGWTR_EL2, NPOR_EL0, 59, 1) +FIELD(HFGWTR_EL2, NPOR_EL1, 60, 1) +FIELD(HFGWTR_EL2, NS2POR_EL1, 61, 1) +FIELD(HFGWTR_EL2, NMAIR2_EL1, 62, 1) +FIELD(HFGWTR_EL2, NAMAIR2_EL1, 63, 1) FIELD(HFGITR_EL2, ICIALLUIS, 0, 1) FIELD(HFGITR_EL2, ICIALLU, 1, 1) @@ -512,6 +531,11 @@ FIELD(HFGITR_EL2, SVC_EL1, 53, 1) FIELD(HFGITR_EL2, DCCVAC, 54, 1) FIELD(HFGITR_EL2, NBRBINJ, 55, 1) FIELD(HFGITR_EL2, NBRBIALL, 56, 1) +FIELD(HFGITR_EL2, NGCSPUSHM_EL1, 57, 1) +FIELD(HFGITR_EL2, NGCSSTR_EL1, 58, 1) +FIELD(HFGITR_EL2, NGCSEPP, 59, 1) +FIELD(HFGITR_EL2, COSPRCTX, 60, 1) +FIELD(HFGITR_EL2, ATS1E1A, 62, 1) FIELD(HDFGRTR_EL2, DBGBCRN_EL1, 0, 1) FIELD(HDFGRTR_EL2, DBGBVRN_EL1, 1, 1) From 171a302a041ed5532d997d40bb50f39b2b9435a3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:15 +1000 Subject: [PATCH 0671/1794] target/arm: Remove outdated comment for ZCR_EL12 The comment about not being included in the summary table has been out of date for quite a while. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 19637e7301b78..b641229ba0c43 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4563,11 +4563,6 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) { K(3, 0, 14, 1, 0), K(3, 4, 14, 1, 0), K(3, 5, 14, 1, 0), "CNTKCTL", "CNTHCTL_EL2", "CNTKCTL_EL12" }, - /* - * Note that redirection of ZCR is mentioned in the description - * of ZCR_EL2, and aliasing in the description of ZCR_EL1, but - * not in the summary table. - */ { K(3, 0, 1, 2, 0), K(3, 4, 1, 2, 0), K(3, 5, 1, 2, 0), "ZCR_EL1", "ZCR_EL2", "ZCR_EL12", isar_feature_aa64_sve }, { K(3, 0, 1, 2, 6), K(3, 4, 1, 2, 6), K(3, 5, 1, 2, 6), From 2b5daf79c32656264a23104c0693aa89c528cff8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:16 +1000 Subject: [PATCH 0672/1794] target/arm: Implement FEAT_ATS1A Implement FEAT_ATS1A and enable for -cpu max. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-13-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + target/arm/cpregs.h | 1 + target/arm/cpu-features.h | 5 ++++ target/arm/tcg/cpregs-at.c | 44 +++++++++++++++++++++++++++++++++++ target/arm/tcg/cpu64.c | 1 + 5 files changed, 52 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 4e8aca8b5d51c..6b04c96c8c466 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -23,6 +23,7 @@ the following architecture extensions: - FEAT_AFP (Alternate floating-point behavior) - FEAT_Armv9_Crypto (Armv9 Cryptographic Extension) - FEAT_ASID16 (16 bit ASID) +- FEAT_ATS1A (Address Translation operations that ignore stage 1 permissions) - FEAT_BBM at level 2 (Translation table break-before-make levels) - FEAT_BF16 (AArch64 BFloat16 instructions) - FEAT_BTI (Branch Target Identification) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 1d103b577f7df..2a4826f5c4ff1 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -854,6 +854,7 @@ typedef enum FGTBit { DO_BIT(HFGITR, DVPRCTX), DO_BIT(HFGITR, CPPRCTX), DO_BIT(HFGITR, DCCVAC), + DO_BIT(HFGITR, ATS1E1A), } FGTBit; #undef DO_BIT diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index e49e0ae3af009..512eeaf551e8c 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -619,6 +619,11 @@ static inline bool isar_feature_aa64_lut(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64ISAR2, LUT); } +static inline bool isar_feature_aa64_ats1a(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64ISAR2, ATS1A); +} + static inline bool isar_feature_aa64_fp_simd(const ARMISARegisters *id) { /* We always set the AdvSIMD and FP fields identically. */ diff --git a/target/arm/tcg/cpregs-at.c b/target/arm/tcg/cpregs-at.c index bebf1689970c8..0e8f229aa7f48 100644 --- a/target/arm/tcg/cpregs-at.c +++ b/target/arm/tcg/cpregs-at.c @@ -488,6 +488,47 @@ static const ARMCPRegInfo ats1cp_reginfo[] = { .writefn = ats_write }, }; +static void ats_s1e1a(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + uint64_t hcr_el2 = arm_hcr_el2_eff(env); + bool regime_e20 = (hcr_el2 & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE); + ARMMMUIdx mmu_idx = regime_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_Stage1_E1; + ARMSecuritySpace ss = arm_security_space_below_el3(env); + + env->cp15.par_el[1] = do_ats_write(env, value, 0, mmu_idx, ss); +} + +static void ats_s1e2a(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + uint64_t hcr_el2 = arm_hcr_el2_eff(env); + ARMMMUIdx mmu_idx = hcr_el2 & HCR_E2H ? ARMMMUIdx_E20_2 : ARMMMUIdx_E2; + ARMSecuritySpace ss = arm_security_space_below_el3(env); + + env->cp15.par_el[1] = do_ats_write(env, value, 0, mmu_idx, ss); +} + +static void ats_s1e3a(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + env->cp15.par_el[1] = do_ats_write(env, value, 0, ARMMMUIdx_E3, + arm_security_space(env)); +} + +static const ARMCPRegInfo ats1a_reginfo[] = { + { .name = "AT_S1E1A", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 2, + .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .fgt = FGT_ATS1E1A, + .accessfn = at_s1e01_access, .writefn = ats_s1e1a }, + { .name = "AT_S1E2A", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 9, .opc2 = 2, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .accessfn = at_s1e2_access, .writefn = ats_s1e2a }, + { .name = "AT_S1E3A", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 9, .opc2 = 2, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .writefn = ats_s1e3a }, +}; + void define_at_insn_regs(ARMCPU *cpu) { CPUARMState *env = &cpu->env; @@ -509,4 +550,7 @@ void define_at_insn_regs(ARMCPU *cpu) if (cpu_isar_feature(aa32_ats1e1, cpu)) { define_arm_cp_regs(cpu, ats1cp_reginfo); } + if (cpu_isar_feature(aa64_ats1a, cpu)) { + define_arm_cp_regs(cpu, ats1a_reginfo); + } } diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index b8b1981e702ce..abef6a246e8f0 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1179,6 +1179,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* FEAT_HBC */ t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* FEAT_WFxT */ t = FIELD_DP64(t, ID_AA64ISAR2, CSSC, 1); /* FEAT_CSSC */ + t = FIELD_DP64(t, ID_AA64ISAR2, ATS1A, 1); /* FEAT_ATS1A */ SET_IDREG(isar, ID_AA64ISAR2, t); t = GET_IDREG(isar, ID_AA64PFR0); From cc2c5027dc755b0b7e2a6531b089e5a239d3e0ce Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Tue, 2 Sep 2025 22:08:18 +0200 Subject: [PATCH 0673/1794] hw/arm/raspi4b: remove redundant check in raspi_add_memory_node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The if (acells == 0 || scells == 0) check is redundant in raspi_add_memory_node, since it is already checked in the call chain, arm_load_dtb. Also the return value of the function is not checked/used so it's removed. Signed-off-by: Osama Abdelkader Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-id: 20250902200818.43305-1-osama.abdelkader@gmail.com Signed-off-by: Peter Maydell --- hw/arm/raspi4b.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/hw/arm/raspi4b.c b/hw/arm/raspi4b.c index 20082d52667f7..4df951a0d827a 100644 --- a/hw/arm/raspi4b.c +++ b/hw/arm/raspi4b.c @@ -36,9 +36,8 @@ struct Raspi4bMachineState { * (see https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf * 1.2 Address Map) */ -static int raspi_add_memory_node(void *fdt, hwaddr mem_base, hwaddr mem_len) +static void raspi_add_memory_node(void *fdt, hwaddr mem_base, hwaddr mem_len) { - int ret; uint32_t acells, scells; char *nodename = g_strdup_printf("/memory@%" PRIx64, mem_base); @@ -46,19 +45,16 @@ static int raspi_add_memory_node(void *fdt, hwaddr mem_base, hwaddr mem_len) NULL, &error_fatal); scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells", NULL, &error_fatal); - if (acells == 0 || scells == 0) { - fprintf(stderr, "dtb file invalid (#address-cells or #size-cells 0)\n"); - ret = -1; - } else { - qemu_fdt_add_subnode(fdt, nodename); - qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); - ret = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", - acells, mem_base, - scells, mem_len); - } + /* validated by arm_load_dtb */ + g_assert(acells && scells); + + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); + qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", + acells, mem_base, + scells, mem_len); g_free(nodename); - return ret; } static void raspi4_modify_dtb(const struct arm_boot_info *info, void *fdt) From 5b3764d9e30627853c5b9171925c51232e56a293 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Aug 2025 15:04:18 +0100 Subject: [PATCH 0674/1794] target/arm: Remove deprecated pxa CPU family MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 10.0 we deprecated the pxa CPU family (pxa250, pxa255, pxa260 pxa261, pxa262, pxa270-a0, pxa270-a1, pxa270, pxa270-b0, pxa270-b1, pxa270-c0, pxa270-c5). Now we have released 10.1 we can remove them. This commit removes only the top level CPU definitions and updates the documentation. Removing the CPUs means that there is now a lot of dead iwMMXt code, which we will delete in subsequent commits. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250828140422.3271703-2-peter.maydell@linaro.org --- docs/about/deprecated.rst | 21 ---- docs/about/removed-features.rst | 14 +++ target/arm/tcg/cpu32.c | 163 -------------------------------- 3 files changed, 14 insertions(+), 184 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index b2420732e1d7a..f031414769808 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -236,27 +236,6 @@ Keeping 32-bit host support alive is a substantial burden for the QEMU project. Thus QEMU will in future drop the support for all 32-bit host systems. -linux-user mode CPUs --------------------- - -iwMMXt emulation and the ``pxa`` CPUs (since 10.0) -'''''''''''''''''''''''''''''''''''''''''''''''''' - -The ``pxa`` CPU family (``pxa250``, ``pxa255``, ``pxa260``, -``pxa261``, ``pxa262``, ``pxa270-a0``, ``pxa270-a1``, ``pxa270``, -``pxa270-b0``, ``pxa270-b1``, ``pxa270-c0``, ``pxa270-c5``) are no -longer used in system emulation, because all the machine types which -used these CPUs were removed in the QEMU 9.2 release. These CPUs can -now only be used in linux-user mode, and to do that you would have to -explicitly select one of these CPUs with the ``-cpu`` command line -option or the ``QEMU_CPU`` environment variable. - -We don't believe that anybody is using the iwMMXt emulation, and we do -not have any tests to validate it or any real hardware or similar -known-good implementation to test against. GCC is in the process of -dropping their support for iwMMXt codegen. These CPU types are -therefore deprecated in QEMU, and will be removed in a future release. - System emulator CPUs -------------------- diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index fff781d6b7c3b..65fd564d229be 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1138,6 +1138,20 @@ reason the maintainers strongly suspected no one actually used it. QEMU Nios II architecture was orphan; Intel has EOL'ed the Nios II processor IP (see `Intel discontinuance notification`_). +iwMMXt emulation and the ``pxa`` CPUs (removed in 10.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``pxa`` CPU family (``pxa250``, ``pxa255``, ``pxa260``, +``pxa261``, ``pxa262``, ``pxa270-a0``, ``pxa270-a1``, ``pxa270``, +``pxa270-b0``, ``pxa270-b1``, ``pxa270-c0``, ``pxa270-c5``) were +not available in system emulation, because all the machine types which +used these CPUs were removed in the QEMU 9.2 release. We don't +believe that anybody was using the iwMMXt emulation (which you +would have to explicitly enable on the command line), and we did +not have any tests to validate it or any real hardware or similar +known-good implementation to test against. These CPUs have +therefore been removed in linux-user mode as well. + TCG introspection features -------------------------- diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index a2a23eae0d7b3..f0761410ad0db 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -807,144 +807,6 @@ static void sa1110_initfn(Object *obj) cpu->reset_sctlr = 0x00000070; } -static void pxa250_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = 0x69052100; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa255_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = 0x69052d00; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa260_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = 0x69052903; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa261_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = 0x69052d05; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa262_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = 0x69052d06; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270a0_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054110; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270a1_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054111; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270b0_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054112; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270b1_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054113; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270c0_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054114; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270c5_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054117; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - #ifndef TARGET_AARCH64 /* * -cpu max: a CPU with as many features enabled as our emulation supports. @@ -1032,31 +894,6 @@ static const ARMCPUInfo arm_tcg_cpus[] = { { .name = "ti925t", .initfn = ti925t_initfn }, { .name = "sa1100", .initfn = sa1100_initfn }, { .name = "sa1110", .initfn = sa1110_initfn }, - { .name = "pxa250", .initfn = pxa250_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa255", .initfn = pxa255_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa260", .initfn = pxa260_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa261", .initfn = pxa261_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa262", .initfn = pxa262_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - /* "pxa270" is an alias for "pxa270-a0" */ - { .name = "pxa270", .initfn = pxa270a0_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa270-a0", .initfn = pxa270a0_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa270-a1", .initfn = pxa270a1_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa270-b0", .initfn = pxa270b0_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa270-b1", .initfn = pxa270b1_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa270-c0", .initfn = pxa270c0_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa270-c5", .initfn = pxa270c5_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, #ifndef TARGET_AARCH64 { .name = "max", .initfn = arm_max_initfn }, #endif From cdafe5bd90eef82bec798029ad5669bf2ee15023 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Aug 2025 15:04:19 +0100 Subject: [PATCH 0675/1794] target/arm: Remove XScale and iWMMXt translate.c code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove all the translator code that is accessible only via ARM_FEATURE_XSCALE or ARM_FEATURE_IWMMXT. This includes the xscale-only cp15_cpar TB flags and cpu_{V0,V1,M0} TCG temps. The no-longer-used helper functions will be removed in a separate commit. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250828140422.3271703-3-peter.maydell@linaro.org --- target/arm/cpu.h | 7 - target/arm/tcg/hflags.c | 13 +- target/arm/tcg/translate.c | 1324 +----------------------------------- target/arm/tcg/translate.h | 2 - 4 files changed, 7 insertions(+), 1339 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c15d79a106b73..f56fa6df8dd31 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3025,13 +3025,6 @@ FIELD(TBFLAG_AM32, THUMB, 23, 1) /* Not cached. */ */ FIELD(TBFLAG_A32, VECLEN, 0, 3) /* Not cached. */ FIELD(TBFLAG_A32, VECSTRIDE, 3, 2) /* Not cached. */ -/* - * We store the bottom two bits of the CPAR as TB flags and handle - * checks on the other bits at runtime. This shares the same bits as - * VECSTRIDE, which is OK as no XScale CPU has VFP. - * Not cached, because VECLEN+VECSTRIDE are not cached. - */ -FIELD(TBFLAG_A32, XSCALE_CPAR, 5, 2) FIELD(TBFLAG_A32, VFPEN, 7, 1) /* Partially cached, minus FPEXC. */ FIELD(TBFLAG_A32, SCTLR__B, 8, 1) /* Cannot overlap with SCTLR_B */ FIELD(TBFLAG_A32, HSTR_ACTIVE, 9, 1) diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index 59ab5263753ae..01894226cc9b6 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -624,16 +624,9 @@ TCGTBCPUState arm_get_tb_cpu_state(CPUState *cs) DP_TBFLAG_M32(flags, MVE_NO_PRED, 1); } } else { - /* - * Note that XSCALE_CPAR shares bits with VECSTRIDE. - * Note that VECLEN+VECSTRIDE are RES0 for M-profile. - */ - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - DP_TBFLAG_A32(flags, XSCALE_CPAR, env->cp15.c15_cpar); - } else { - DP_TBFLAG_A32(flags, VECLEN, env->vfp.vec_len); - DP_TBFLAG_A32(flags, VECSTRIDE, env->vfp.vec_stride); - } + /* Note that VECLEN+VECSTRIDE are RES0 for M-profile. */ + DP_TBFLAG_A32(flags, VECLEN, env->vfp.vec_len); + DP_TBFLAG_A32(flags, VECSTRIDE, env->vfp.vec_stride); if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) { DP_TBFLAG_A32(flags, VFPEN, 1); } diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index f7d6d8ce196e2..e62dcc5d85d5a 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -44,8 +44,6 @@ #define ENABLE_ARCH_7 arm_dc_feature(s, ARM_FEATURE_V7) #define ENABLE_ARCH_8 arm_dc_feature(s, ARM_FEATURE_V8) -/* These are TCG temporaries used only by the legacy iwMMXt decoder */ -static TCGv_i64 cpu_V0, cpu_V1, cpu_M0; /* These are TCG globals which alias CPUARMState fields */ static TCGv_i32 cpu_R[16]; TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF; @@ -1252,1263 +1250,6 @@ void write_neon_element64(TCGv_i64 src, int reg, int ele, MemOp memop) } } -#define ARM_CP_RW_BIT (1 << 20) - -static inline void iwmmxt_load_reg(TCGv_i64 var, int reg) -{ - tcg_gen_ld_i64(var, tcg_env, offsetof(CPUARMState, iwmmxt.regs[reg])); -} - -static inline void iwmmxt_store_reg(TCGv_i64 var, int reg) -{ - tcg_gen_st_i64(var, tcg_env, offsetof(CPUARMState, iwmmxt.regs[reg])); -} - -static inline TCGv_i32 iwmmxt_load_creg(int reg) -{ - TCGv_i32 var = tcg_temp_new_i32(); - tcg_gen_ld_i32(var, tcg_env, offsetof(CPUARMState, iwmmxt.cregs[reg])); - return var; -} - -static inline void iwmmxt_store_creg(int reg, TCGv_i32 var) -{ - tcg_gen_st_i32(var, tcg_env, offsetof(CPUARMState, iwmmxt.cregs[reg])); -} - -static inline void gen_op_iwmmxt_movq_wRn_M0(int rn) -{ - iwmmxt_store_reg(cpu_M0, rn); -} - -static inline void gen_op_iwmmxt_movq_M0_wRn(int rn) -{ - iwmmxt_load_reg(cpu_M0, rn); -} - -static inline void gen_op_iwmmxt_orq_M0_wRn(int rn) -{ - iwmmxt_load_reg(cpu_V1, rn); - tcg_gen_or_i64(cpu_M0, cpu_M0, cpu_V1); -} - -static inline void gen_op_iwmmxt_andq_M0_wRn(int rn) -{ - iwmmxt_load_reg(cpu_V1, rn); - tcg_gen_and_i64(cpu_M0, cpu_M0, cpu_V1); -} - -static inline void gen_op_iwmmxt_xorq_M0_wRn(int rn) -{ - iwmmxt_load_reg(cpu_V1, rn); - tcg_gen_xor_i64(cpu_M0, cpu_M0, cpu_V1); -} - -#define IWMMXT_OP(name) \ -static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \ -{ \ - iwmmxt_load_reg(cpu_V1, rn); \ - gen_helper_iwmmxt_##name(cpu_M0, cpu_M0, cpu_V1); \ -} - -#define IWMMXT_OP_ENV(name) \ -static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \ -{ \ - iwmmxt_load_reg(cpu_V1, rn); \ - gen_helper_iwmmxt_##name(cpu_M0, tcg_env, cpu_M0, cpu_V1); \ -} - -#define IWMMXT_OP_ENV_SIZE(name) \ -IWMMXT_OP_ENV(name##b) \ -IWMMXT_OP_ENV(name##w) \ -IWMMXT_OP_ENV(name##l) - -#define IWMMXT_OP_ENV1(name) \ -static inline void gen_op_iwmmxt_##name##_M0(void) \ -{ \ - gen_helper_iwmmxt_##name(cpu_M0, tcg_env, cpu_M0); \ -} - -IWMMXT_OP(maddsq) -IWMMXT_OP(madduq) -IWMMXT_OP(sadb) -IWMMXT_OP(sadw) -IWMMXT_OP(mulslw) -IWMMXT_OP(mulshw) -IWMMXT_OP(mululw) -IWMMXT_OP(muluhw) -IWMMXT_OP(macsw) -IWMMXT_OP(macuw) - -IWMMXT_OP_ENV_SIZE(unpackl) -IWMMXT_OP_ENV_SIZE(unpackh) - -IWMMXT_OP_ENV1(unpacklub) -IWMMXT_OP_ENV1(unpackluw) -IWMMXT_OP_ENV1(unpacklul) -IWMMXT_OP_ENV1(unpackhub) -IWMMXT_OP_ENV1(unpackhuw) -IWMMXT_OP_ENV1(unpackhul) -IWMMXT_OP_ENV1(unpacklsb) -IWMMXT_OP_ENV1(unpacklsw) -IWMMXT_OP_ENV1(unpacklsl) -IWMMXT_OP_ENV1(unpackhsb) -IWMMXT_OP_ENV1(unpackhsw) -IWMMXT_OP_ENV1(unpackhsl) - -IWMMXT_OP_ENV_SIZE(cmpeq) -IWMMXT_OP_ENV_SIZE(cmpgtu) -IWMMXT_OP_ENV_SIZE(cmpgts) - -IWMMXT_OP_ENV_SIZE(mins) -IWMMXT_OP_ENV_SIZE(minu) -IWMMXT_OP_ENV_SIZE(maxs) -IWMMXT_OP_ENV_SIZE(maxu) - -IWMMXT_OP_ENV_SIZE(subn) -IWMMXT_OP_ENV_SIZE(addn) -IWMMXT_OP_ENV_SIZE(subu) -IWMMXT_OP_ENV_SIZE(addu) -IWMMXT_OP_ENV_SIZE(subs) -IWMMXT_OP_ENV_SIZE(adds) - -IWMMXT_OP_ENV(avgb0) -IWMMXT_OP_ENV(avgb1) -IWMMXT_OP_ENV(avgw0) -IWMMXT_OP_ENV(avgw1) - -IWMMXT_OP_ENV(packuw) -IWMMXT_OP_ENV(packul) -IWMMXT_OP_ENV(packuq) -IWMMXT_OP_ENV(packsw) -IWMMXT_OP_ENV(packsl) -IWMMXT_OP_ENV(packsq) - -static void gen_op_iwmmxt_set_mup(void) -{ - TCGv_i32 tmp; - tmp = load_cpu_field(iwmmxt.cregs[ARM_IWMMXT_wCon]); - tcg_gen_ori_i32(tmp, tmp, 2); - store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCon]); -} - -static void gen_op_iwmmxt_set_cup(void) -{ - TCGv_i32 tmp; - tmp = load_cpu_field(iwmmxt.cregs[ARM_IWMMXT_wCon]); - tcg_gen_ori_i32(tmp, tmp, 1); - store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCon]); -} - -static void gen_op_iwmmxt_setpsr_nz(void) -{ - TCGv_i32 tmp = tcg_temp_new_i32(); - gen_helper_iwmmxt_setpsr_nz(tmp, cpu_M0); - store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCASF]); -} - -static inline void gen_op_iwmmxt_addl_M0_wRn(int rn) -{ - iwmmxt_load_reg(cpu_V1, rn); - tcg_gen_ext32u_i64(cpu_V1, cpu_V1); - tcg_gen_add_i64(cpu_M0, cpu_M0, cpu_V1); -} - -static inline int gen_iwmmxt_address(DisasContext *s, uint32_t insn, - TCGv_i32 dest) -{ - int rd; - uint32_t offset; - TCGv_i32 tmp; - - rd = (insn >> 16) & 0xf; - tmp = load_reg(s, rd); - - offset = (insn & 0xff) << ((insn >> 7) & 2); - if (insn & (1 << 24)) { - /* Pre indexed */ - if (insn & (1 << 23)) - tcg_gen_addi_i32(tmp, tmp, offset); - else - tcg_gen_addi_i32(tmp, tmp, -offset); - tcg_gen_mov_i32(dest, tmp); - if (insn & (1 << 21)) { - store_reg(s, rd, tmp); - } - } else if (insn & (1 << 21)) { - /* Post indexed */ - tcg_gen_mov_i32(dest, tmp); - if (insn & (1 << 23)) - tcg_gen_addi_i32(tmp, tmp, offset); - else - tcg_gen_addi_i32(tmp, tmp, -offset); - store_reg(s, rd, tmp); - } else if (!(insn & (1 << 23))) - return 1; - return 0; -} - -static inline int gen_iwmmxt_shift(uint32_t insn, uint32_t mask, TCGv_i32 dest) -{ - int rd = (insn >> 0) & 0xf; - TCGv_i32 tmp; - - if (insn & (1 << 8)) { - if (rd < ARM_IWMMXT_wCGR0 || rd > ARM_IWMMXT_wCGR3) { - return 1; - } else { - tmp = iwmmxt_load_creg(rd); - } - } else { - tmp = tcg_temp_new_i32(); - iwmmxt_load_reg(cpu_V0, rd); - tcg_gen_extrl_i64_i32(tmp, cpu_V0); - } - tcg_gen_andi_i32(tmp, tmp, mask); - tcg_gen_mov_i32(dest, tmp); - return 0; -} - -/* Disassemble an iwMMXt instruction. Returns nonzero if an error occurred - (ie. an undefined instruction). */ -static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) -{ - int rd, wrd; - int rdhi, rdlo, rd0, rd1, i; - TCGv_i32 addr; - TCGv_i32 tmp, tmp2, tmp3; - - if ((insn & 0x0e000e00) == 0x0c000000) { - if ((insn & 0x0fe00ff0) == 0x0c400000) { - wrd = insn & 0xf; - rdlo = (insn >> 12) & 0xf; - rdhi = (insn >> 16) & 0xf; - if (insn & ARM_CP_RW_BIT) { /* TMRRC */ - iwmmxt_load_reg(cpu_V0, wrd); - tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); - tcg_gen_extrh_i64_i32(cpu_R[rdhi], cpu_V0); - } else { /* TMCRR */ - tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); - iwmmxt_store_reg(cpu_V0, wrd); - gen_op_iwmmxt_set_mup(); - } - return 0; - } - - wrd = (insn >> 12) & 0xf; - addr = tcg_temp_new_i32(); - if (gen_iwmmxt_address(s, insn, addr)) { - return 1; - } - if (insn & ARM_CP_RW_BIT) { - if ((insn >> 28) == 0xf) { /* WLDRW wCx */ - tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); - iwmmxt_store_creg(wrd, tmp); - } else { - i = 1; - if (insn & (1 << 8)) { - if (insn & (1 << 22)) { /* WLDRD */ - gen_aa32_ld64(s, cpu_M0, addr, get_mem_index(s)); - i = 0; - } else { /* WLDRW wRd */ - tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); - } - } else { - tmp = tcg_temp_new_i32(); - if (insn & (1 << 22)) { /* WLDRH */ - gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); - } else { /* WLDRB */ - gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); - } - } - if (i) { - tcg_gen_extu_i32_i64(cpu_M0, tmp); - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - } - } else { - if ((insn >> 28) == 0xf) { /* WSTRW wCx */ - tmp = iwmmxt_load_creg(wrd); - gen_aa32_st32(s, tmp, addr, get_mem_index(s)); - } else { - gen_op_iwmmxt_movq_M0_wRn(wrd); - tmp = tcg_temp_new_i32(); - if (insn & (1 << 8)) { - if (insn & (1 << 22)) { /* WSTRD */ - gen_aa32_st64(s, cpu_M0, addr, get_mem_index(s)); - } else { /* WSTRW wRd */ - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - gen_aa32_st32(s, tmp, addr, get_mem_index(s)); - } - } else { - if (insn & (1 << 22)) { /* WSTRH */ - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - gen_aa32_st16(s, tmp, addr, get_mem_index(s)); - } else { /* WSTRB */ - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - gen_aa32_st8(s, tmp, addr, get_mem_index(s)); - } - } - } - } - return 0; - } - - if ((insn & 0x0f000000) != 0x0e000000) - return 1; - - switch (((insn >> 12) & 0xf00) | ((insn >> 4) & 0xff)) { - case 0x000: /* WOR */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 0) & 0xf; - rd1 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - gen_op_iwmmxt_orq_M0_wRn(rd1); - gen_op_iwmmxt_setpsr_nz(); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x011: /* TMCR */ - if (insn & 0xf) - return 1; - rd = (insn >> 12) & 0xf; - wrd = (insn >> 16) & 0xf; - switch (wrd) { - case ARM_IWMMXT_wCID: - case ARM_IWMMXT_wCASF: - break; - case ARM_IWMMXT_wCon: - gen_op_iwmmxt_set_cup(); - /* Fall through. */ - case ARM_IWMMXT_wCSSF: - tmp = iwmmxt_load_creg(wrd); - tmp2 = load_reg(s, rd); - tcg_gen_andc_i32(tmp, tmp, tmp2); - iwmmxt_store_creg(wrd, tmp); - break; - case ARM_IWMMXT_wCGR0: - case ARM_IWMMXT_wCGR1: - case ARM_IWMMXT_wCGR2: - case ARM_IWMMXT_wCGR3: - gen_op_iwmmxt_set_cup(); - tmp = load_reg(s, rd); - iwmmxt_store_creg(wrd, tmp); - break; - default: - return 1; - } - break; - case 0x100: /* WXOR */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 0) & 0xf; - rd1 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - gen_op_iwmmxt_xorq_M0_wRn(rd1); - gen_op_iwmmxt_setpsr_nz(); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x111: /* TMRC */ - if (insn & 0xf) - return 1; - rd = (insn >> 12) & 0xf; - wrd = (insn >> 16) & 0xf; - tmp = iwmmxt_load_creg(wrd); - store_reg(s, rd, tmp); - break; - case 0x300: /* WANDN */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 0) & 0xf; - rd1 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tcg_gen_neg_i64(cpu_M0, cpu_M0); - gen_op_iwmmxt_andq_M0_wRn(rd1); - gen_op_iwmmxt_setpsr_nz(); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x200: /* WAND */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 0) & 0xf; - rd1 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - gen_op_iwmmxt_andq_M0_wRn(rd1); - gen_op_iwmmxt_setpsr_nz(); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x810: case 0xa10: /* WMADD */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 0) & 0xf; - rd1 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - if (insn & (1 << 21)) - gen_op_iwmmxt_maddsq_M0_wRn(rd1); - else - gen_op_iwmmxt_madduq_M0_wRn(rd1); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x10e: case 0x50e: case 0x90e: case 0xd0e: /* WUNPCKIL */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - gen_op_iwmmxt_unpacklb_M0_wRn(rd1); - break; - case 1: - gen_op_iwmmxt_unpacklw_M0_wRn(rd1); - break; - case 2: - gen_op_iwmmxt_unpackll_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x10c: case 0x50c: case 0x90c: case 0xd0c: /* WUNPCKIH */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - gen_op_iwmmxt_unpackhb_M0_wRn(rd1); - break; - case 1: - gen_op_iwmmxt_unpackhw_M0_wRn(rd1); - break; - case 2: - gen_op_iwmmxt_unpackhl_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x012: case 0x112: case 0x412: case 0x512: /* WSAD */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - if (insn & (1 << 22)) - gen_op_iwmmxt_sadw_M0_wRn(rd1); - else - gen_op_iwmmxt_sadb_M0_wRn(rd1); - if (!(insn & (1 << 20))) - gen_op_iwmmxt_addl_M0_wRn(wrd); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x010: case 0x110: case 0x210: case 0x310: /* WMUL */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - if (insn & (1 << 21)) { - if (insn & (1 << 20)) - gen_op_iwmmxt_mulshw_M0_wRn(rd1); - else - gen_op_iwmmxt_mulslw_M0_wRn(rd1); - } else { - if (insn & (1 << 20)) - gen_op_iwmmxt_muluhw_M0_wRn(rd1); - else - gen_op_iwmmxt_mululw_M0_wRn(rd1); - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x410: case 0x510: case 0x610: case 0x710: /* WMAC */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - if (insn & (1 << 21)) - gen_op_iwmmxt_macsw_M0_wRn(rd1); - else - gen_op_iwmmxt_macuw_M0_wRn(rd1); - if (!(insn & (1 << 20))) { - iwmmxt_load_reg(cpu_V1, wrd); - tcg_gen_add_i64(cpu_M0, cpu_M0, cpu_V1); - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x006: case 0x406: case 0x806: case 0xc06: /* WCMPEQ */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - gen_op_iwmmxt_cmpeqb_M0_wRn(rd1); - break; - case 1: - gen_op_iwmmxt_cmpeqw_M0_wRn(rd1); - break; - case 2: - gen_op_iwmmxt_cmpeql_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x800: case 0x900: case 0xc00: case 0xd00: /* WAVG2 */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - if (insn & (1 << 22)) { - if (insn & (1 << 20)) - gen_op_iwmmxt_avgw1_M0_wRn(rd1); - else - gen_op_iwmmxt_avgw0_M0_wRn(rd1); - } else { - if (insn & (1 << 20)) - gen_op_iwmmxt_avgb1_M0_wRn(rd1); - else - gen_op_iwmmxt_avgb0_M0_wRn(rd1); - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x802: case 0x902: case 0xa02: case 0xb02: /* WALIGNR */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = iwmmxt_load_creg(ARM_IWMMXT_wCGR0 + ((insn >> 20) & 3)); - tcg_gen_andi_i32(tmp, tmp, 7); - iwmmxt_load_reg(cpu_V1, rd1); - gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, tmp); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x601: case 0x605: case 0x609: case 0x60d: /* TINSR */ - if (((insn >> 6) & 3) == 3) - return 1; - rd = (insn >> 12) & 0xf; - wrd = (insn >> 16) & 0xf; - tmp = load_reg(s, rd); - gen_op_iwmmxt_movq_M0_wRn(wrd); - switch ((insn >> 6) & 3) { - case 0: - tmp2 = tcg_constant_i32(0xff); - tmp3 = tcg_constant_i32((insn & 7) << 3); - break; - case 1: - tmp2 = tcg_constant_i32(0xffff); - tmp3 = tcg_constant_i32((insn & 3) << 4); - break; - case 2: - tmp2 = tcg_constant_i32(0xffffffff); - tmp3 = tcg_constant_i32((insn & 1) << 5); - break; - default: - g_assert_not_reached(); - } - gen_helper_iwmmxt_insr(cpu_M0, cpu_M0, tmp, tmp2, tmp3); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x107: case 0x507: case 0x907: case 0xd07: /* TEXTRM */ - rd = (insn >> 12) & 0xf; - wrd = (insn >> 16) & 0xf; - if (rd == 15 || ((insn >> 22) & 3) == 3) - return 1; - gen_op_iwmmxt_movq_M0_wRn(wrd); - tmp = tcg_temp_new_i32(); - switch ((insn >> 22) & 3) { - case 0: - tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 7) << 3); - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - if (insn & 8) { - tcg_gen_ext8s_i32(tmp, tmp); - } else { - tcg_gen_andi_i32(tmp, tmp, 0xff); - } - break; - case 1: - tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 3) << 4); - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - if (insn & 8) { - tcg_gen_ext16s_i32(tmp, tmp); - } else { - tcg_gen_andi_i32(tmp, tmp, 0xffff); - } - break; - case 2: - tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 1) << 5); - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - break; - } - store_reg(s, rd, tmp); - break; - case 0x117: case 0x517: case 0x917: case 0xd17: /* TEXTRC */ - if ((insn & 0x000ff008) != 0x0003f000 || ((insn >> 22) & 3) == 3) - return 1; - tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); - switch ((insn >> 22) & 3) { - case 0: - tcg_gen_shri_i32(tmp, tmp, ((insn & 7) << 2) + 0); - break; - case 1: - tcg_gen_shri_i32(tmp, tmp, ((insn & 3) << 3) + 4); - break; - case 2: - tcg_gen_shri_i32(tmp, tmp, ((insn & 1) << 4) + 12); - break; - } - tcg_gen_shli_i32(tmp, tmp, 28); - gen_set_nzcv(tmp); - break; - case 0x401: case 0x405: case 0x409: case 0x40d: /* TBCST */ - if (((insn >> 6) & 3) == 3) - return 1; - rd = (insn >> 12) & 0xf; - wrd = (insn >> 16) & 0xf; - tmp = load_reg(s, rd); - switch ((insn >> 6) & 3) { - case 0: - gen_helper_iwmmxt_bcstb(cpu_M0, tmp); - break; - case 1: - gen_helper_iwmmxt_bcstw(cpu_M0, tmp); - break; - case 2: - gen_helper_iwmmxt_bcstl(cpu_M0, tmp); - break; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x113: case 0x513: case 0x913: case 0xd13: /* TANDC */ - if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) - return 1; - tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); - tmp2 = tcg_temp_new_i32(); - tcg_gen_mov_i32(tmp2, tmp); - switch ((insn >> 22) & 3) { - case 0: - for (i = 0; i < 7; i ++) { - tcg_gen_shli_i32(tmp2, tmp2, 4); - tcg_gen_and_i32(tmp, tmp, tmp2); - } - break; - case 1: - for (i = 0; i < 3; i ++) { - tcg_gen_shli_i32(tmp2, tmp2, 8); - tcg_gen_and_i32(tmp, tmp, tmp2); - } - break; - case 2: - tcg_gen_shli_i32(tmp2, tmp2, 16); - tcg_gen_and_i32(tmp, tmp, tmp2); - break; - } - gen_set_nzcv(tmp); - break; - case 0x01c: case 0x41c: case 0x81c: case 0xc1c: /* WACC */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - gen_helper_iwmmxt_addcb(cpu_M0, cpu_M0); - break; - case 1: - gen_helper_iwmmxt_addcw(cpu_M0, cpu_M0); - break; - case 2: - gen_helper_iwmmxt_addcl(cpu_M0, cpu_M0); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x115: case 0x515: case 0x915: case 0xd15: /* TORC */ - if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) - return 1; - tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); - tmp2 = tcg_temp_new_i32(); - tcg_gen_mov_i32(tmp2, tmp); - switch ((insn >> 22) & 3) { - case 0: - for (i = 0; i < 7; i ++) { - tcg_gen_shli_i32(tmp2, tmp2, 4); - tcg_gen_or_i32(tmp, tmp, tmp2); - } - break; - case 1: - for (i = 0; i < 3; i ++) { - tcg_gen_shli_i32(tmp2, tmp2, 8); - tcg_gen_or_i32(tmp, tmp, tmp2); - } - break; - case 2: - tcg_gen_shli_i32(tmp2, tmp2, 16); - tcg_gen_or_i32(tmp, tmp, tmp2); - break; - } - gen_set_nzcv(tmp); - break; - case 0x103: case 0x503: case 0x903: case 0xd03: /* TMOVMSK */ - rd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - if ((insn & 0xf) != 0 || ((insn >> 22) & 3) == 3) - return 1; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = tcg_temp_new_i32(); - switch ((insn >> 22) & 3) { - case 0: - gen_helper_iwmmxt_msbb(tmp, cpu_M0); - break; - case 1: - gen_helper_iwmmxt_msbw(tmp, cpu_M0); - break; - case 2: - gen_helper_iwmmxt_msbl(tmp, cpu_M0); - break; - } - store_reg(s, rd, tmp); - break; - case 0x106: case 0x306: case 0x506: case 0x706: /* WCMPGT */ - case 0x906: case 0xb06: case 0xd06: case 0xf06: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - if (insn & (1 << 21)) - gen_op_iwmmxt_cmpgtsb_M0_wRn(rd1); - else - gen_op_iwmmxt_cmpgtub_M0_wRn(rd1); - break; - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_cmpgtsw_M0_wRn(rd1); - else - gen_op_iwmmxt_cmpgtuw_M0_wRn(rd1); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_cmpgtsl_M0_wRn(rd1); - else - gen_op_iwmmxt_cmpgtul_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x00e: case 0x20e: case 0x40e: case 0x60e: /* WUNPCKEL */ - case 0x80e: case 0xa0e: case 0xc0e: case 0xe0e: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpacklsb_M0(); - else - gen_op_iwmmxt_unpacklub_M0(); - break; - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpacklsw_M0(); - else - gen_op_iwmmxt_unpackluw_M0(); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpacklsl_M0(); - else - gen_op_iwmmxt_unpacklul_M0(); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x00c: case 0x20c: case 0x40c: case 0x60c: /* WUNPCKEH */ - case 0x80c: case 0xa0c: case 0xc0c: case 0xe0c: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpackhsb_M0(); - else - gen_op_iwmmxt_unpackhub_M0(); - break; - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpackhsw_M0(); - else - gen_op_iwmmxt_unpackhuw_M0(); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpackhsl_M0(); - else - gen_op_iwmmxt_unpackhul_M0(); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x204: case 0x604: case 0xa04: case 0xe04: /* WSRL */ - case 0x214: case 0x614: case 0xa14: case 0xe14: - if (((insn >> 22) & 3) == 0) - return 1; - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = tcg_temp_new_i32(); - if (gen_iwmmxt_shift(insn, 0xff, tmp)) { - return 1; - } - switch ((insn >> 22) & 3) { - case 1: - gen_helper_iwmmxt_srlw(cpu_M0, tcg_env, cpu_M0, tmp); - break; - case 2: - gen_helper_iwmmxt_srll(cpu_M0, tcg_env, cpu_M0, tmp); - break; - case 3: - gen_helper_iwmmxt_srlq(cpu_M0, tcg_env, cpu_M0, tmp); - break; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x004: case 0x404: case 0x804: case 0xc04: /* WSRA */ - case 0x014: case 0x414: case 0x814: case 0xc14: - if (((insn >> 22) & 3) == 0) - return 1; - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = tcg_temp_new_i32(); - if (gen_iwmmxt_shift(insn, 0xff, tmp)) { - return 1; - } - switch ((insn >> 22) & 3) { - case 1: - gen_helper_iwmmxt_sraw(cpu_M0, tcg_env, cpu_M0, tmp); - break; - case 2: - gen_helper_iwmmxt_sral(cpu_M0, tcg_env, cpu_M0, tmp); - break; - case 3: - gen_helper_iwmmxt_sraq(cpu_M0, tcg_env, cpu_M0, tmp); - break; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x104: case 0x504: case 0x904: case 0xd04: /* WSLL */ - case 0x114: case 0x514: case 0x914: case 0xd14: - if (((insn >> 22) & 3) == 0) - return 1; - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = tcg_temp_new_i32(); - if (gen_iwmmxt_shift(insn, 0xff, tmp)) { - return 1; - } - switch ((insn >> 22) & 3) { - case 1: - gen_helper_iwmmxt_sllw(cpu_M0, tcg_env, cpu_M0, tmp); - break; - case 2: - gen_helper_iwmmxt_slll(cpu_M0, tcg_env, cpu_M0, tmp); - break; - case 3: - gen_helper_iwmmxt_sllq(cpu_M0, tcg_env, cpu_M0, tmp); - break; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x304: case 0x704: case 0xb04: case 0xf04: /* WROR */ - case 0x314: case 0x714: case 0xb14: case 0xf14: - if (((insn >> 22) & 3) == 0) - return 1; - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = tcg_temp_new_i32(); - switch ((insn >> 22) & 3) { - case 1: - if (gen_iwmmxt_shift(insn, 0xf, tmp)) { - return 1; - } - gen_helper_iwmmxt_rorw(cpu_M0, tcg_env, cpu_M0, tmp); - break; - case 2: - if (gen_iwmmxt_shift(insn, 0x1f, tmp)) { - return 1; - } - gen_helper_iwmmxt_rorl(cpu_M0, tcg_env, cpu_M0, tmp); - break; - case 3: - if (gen_iwmmxt_shift(insn, 0x3f, tmp)) { - return 1; - } - gen_helper_iwmmxt_rorq(cpu_M0, tcg_env, cpu_M0, tmp); - break; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x116: case 0x316: case 0x516: case 0x716: /* WMIN */ - case 0x916: case 0xb16: case 0xd16: case 0xf16: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - if (insn & (1 << 21)) - gen_op_iwmmxt_minsb_M0_wRn(rd1); - else - gen_op_iwmmxt_minub_M0_wRn(rd1); - break; - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_minsw_M0_wRn(rd1); - else - gen_op_iwmmxt_minuw_M0_wRn(rd1); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_minsl_M0_wRn(rd1); - else - gen_op_iwmmxt_minul_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x016: case 0x216: case 0x416: case 0x616: /* WMAX */ - case 0x816: case 0xa16: case 0xc16: case 0xe16: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - if (insn & (1 << 21)) - gen_op_iwmmxt_maxsb_M0_wRn(rd1); - else - gen_op_iwmmxt_maxub_M0_wRn(rd1); - break; - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_maxsw_M0_wRn(rd1); - else - gen_op_iwmmxt_maxuw_M0_wRn(rd1); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_maxsl_M0_wRn(rd1); - else - gen_op_iwmmxt_maxul_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x002: case 0x102: case 0x202: case 0x302: /* WALIGNI */ - case 0x402: case 0x502: case 0x602: case 0x702: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - iwmmxt_load_reg(cpu_V1, rd1); - gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, - tcg_constant_i32((insn >> 20) & 3)); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x01a: case 0x11a: case 0x21a: case 0x31a: /* WSUB */ - case 0x41a: case 0x51a: case 0x61a: case 0x71a: - case 0x81a: case 0x91a: case 0xa1a: case 0xb1a: - case 0xc1a: case 0xd1a: case 0xe1a: case 0xf1a: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 20) & 0xf) { - case 0x0: - gen_op_iwmmxt_subnb_M0_wRn(rd1); - break; - case 0x1: - gen_op_iwmmxt_subub_M0_wRn(rd1); - break; - case 0x3: - gen_op_iwmmxt_subsb_M0_wRn(rd1); - break; - case 0x4: - gen_op_iwmmxt_subnw_M0_wRn(rd1); - break; - case 0x5: - gen_op_iwmmxt_subuw_M0_wRn(rd1); - break; - case 0x7: - gen_op_iwmmxt_subsw_M0_wRn(rd1); - break; - case 0x8: - gen_op_iwmmxt_subnl_M0_wRn(rd1); - break; - case 0x9: - gen_op_iwmmxt_subul_M0_wRn(rd1); - break; - case 0xb: - gen_op_iwmmxt_subsl_M0_wRn(rd1); - break; - default: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x01e: case 0x11e: case 0x21e: case 0x31e: /* WSHUFH */ - case 0x41e: case 0x51e: case 0x61e: case 0x71e: - case 0x81e: case 0x91e: case 0xa1e: case 0xb1e: - case 0xc1e: case 0xd1e: case 0xe1e: case 0xf1e: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = tcg_constant_i32(((insn >> 16) & 0xf0) | (insn & 0x0f)); - gen_helper_iwmmxt_shufh(cpu_M0, tcg_env, cpu_M0, tmp); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x018: case 0x118: case 0x218: case 0x318: /* WADD */ - case 0x418: case 0x518: case 0x618: case 0x718: - case 0x818: case 0x918: case 0xa18: case 0xb18: - case 0xc18: case 0xd18: case 0xe18: case 0xf18: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 20) & 0xf) { - case 0x0: - gen_op_iwmmxt_addnb_M0_wRn(rd1); - break; - case 0x1: - gen_op_iwmmxt_addub_M0_wRn(rd1); - break; - case 0x3: - gen_op_iwmmxt_addsb_M0_wRn(rd1); - break; - case 0x4: - gen_op_iwmmxt_addnw_M0_wRn(rd1); - break; - case 0x5: - gen_op_iwmmxt_adduw_M0_wRn(rd1); - break; - case 0x7: - gen_op_iwmmxt_addsw_M0_wRn(rd1); - break; - case 0x8: - gen_op_iwmmxt_addnl_M0_wRn(rd1); - break; - case 0x9: - gen_op_iwmmxt_addul_M0_wRn(rd1); - break; - case 0xb: - gen_op_iwmmxt_addsl_M0_wRn(rd1); - break; - default: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x008: case 0x108: case 0x208: case 0x308: /* WPACK */ - case 0x408: case 0x508: case 0x608: case 0x708: - case 0x808: case 0x908: case 0xa08: case 0xb08: - case 0xc08: case 0xd08: case 0xe08: case 0xf08: - if (!(insn & (1 << 20)) || ((insn >> 22) & 3) == 0) - return 1; - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_packsw_M0_wRn(rd1); - else - gen_op_iwmmxt_packuw_M0_wRn(rd1); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_packsl_M0_wRn(rd1); - else - gen_op_iwmmxt_packul_M0_wRn(rd1); - break; - case 3: - if (insn & (1 << 21)) - gen_op_iwmmxt_packsq_M0_wRn(rd1); - else - gen_op_iwmmxt_packuq_M0_wRn(rd1); - break; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x201: case 0x203: case 0x205: case 0x207: - case 0x209: case 0x20b: case 0x20d: case 0x20f: - case 0x211: case 0x213: case 0x215: case 0x217: - case 0x219: case 0x21b: case 0x21d: case 0x21f: - wrd = (insn >> 5) & 0xf; - rd0 = (insn >> 12) & 0xf; - rd1 = (insn >> 0) & 0xf; - if (rd0 == 0xf || rd1 == 0xf) - return 1; - gen_op_iwmmxt_movq_M0_wRn(wrd); - tmp = load_reg(s, rd0); - tmp2 = load_reg(s, rd1); - switch ((insn >> 16) & 0xf) { - case 0x0: /* TMIA */ - gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2); - break; - case 0x8: /* TMIAPH */ - gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2); - break; - case 0xc: case 0xd: case 0xe: case 0xf: /* TMIAxy */ - if (insn & (1 << 16)) - tcg_gen_shri_i32(tmp, tmp, 16); - if (insn & (1 << 17)) - tcg_gen_shri_i32(tmp2, tmp2, 16); - gen_helper_iwmmxt_muladdswl(cpu_M0, cpu_M0, tmp, tmp2); - break; - default: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - default: - return 1; - } - - return 0; -} - -/* Disassemble an XScale DSP instruction. Returns nonzero if an error occurred - (ie. an undefined instruction). */ -static int disas_dsp_insn(DisasContext *s, uint32_t insn) -{ - int acc, rd0, rd1, rdhi, rdlo; - TCGv_i32 tmp, tmp2; - - if ((insn & 0x0ff00f10) == 0x0e200010) { - /* Multiply with Internal Accumulate Format */ - rd0 = (insn >> 12) & 0xf; - rd1 = insn & 0xf; - acc = (insn >> 5) & 7; - - if (acc != 0) - return 1; - - tmp = load_reg(s, rd0); - tmp2 = load_reg(s, rd1); - switch ((insn >> 16) & 0xf) { - case 0x0: /* MIA */ - gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2); - break; - case 0x8: /* MIAPH */ - gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2); - break; - case 0xc: /* MIABB */ - case 0xd: /* MIABT */ - case 0xe: /* MIATB */ - case 0xf: /* MIATT */ - if (insn & (1 << 16)) - tcg_gen_shri_i32(tmp, tmp, 16); - if (insn & (1 << 17)) - tcg_gen_shri_i32(tmp2, tmp2, 16); - gen_helper_iwmmxt_muladdswl(cpu_M0, cpu_M0, tmp, tmp2); - break; - default: - return 1; - } - - gen_op_iwmmxt_movq_wRn_M0(acc); - return 0; - } - - if ((insn & 0x0fe00ff8) == 0x0c400000) { - /* Internal Accumulator Access Format */ - rdhi = (insn >> 16) & 0xf; - rdlo = (insn >> 12) & 0xf; - acc = insn & 7; - - if (acc != 0) - return 1; - - if (insn & ARM_CP_RW_BIT) { /* MRA */ - iwmmxt_load_reg(cpu_V0, acc); - tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); - tcg_gen_extrh_i64_i32(cpu_R[rdhi], cpu_V0); - tcg_gen_andi_i32(cpu_R[rdhi], cpu_R[rdhi], (1 << (40 - 32)) - 1); - } else { /* MAR */ - tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); - iwmmxt_store_reg(cpu_V0, acc); - } - return 0; - } - - return 1; -} - static void gen_goto_ptr(void) { tcg_gen_lookup_and_goto_ptr(); @@ -3048,13 +1789,10 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, } if ((s->hstr_active && s->current_el == 0) || ri->accessfn || - (ri->fgt && s->fgt_active) || - (arm_dc_feature(s, ARM_FEATURE_XSCALE) && cpnum < 14)) { + (ri->fgt && s->fgt_active)) { /* * Emit code to perform further access permissions checks at * runtime; this may result in an exception. - * Note that on XScale all cp0..c13 registers do an access check - * call in order to handle c15_cpar. */ gen_set_condexec(s); gen_update_pc(s, 0); @@ -3192,24 +1930,6 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, } } -/* Decode XScale DSP or iWMMXt insn (in the copro space, cp=0 or 1) */ -static void disas_xscale_insn(DisasContext *s, uint32_t insn) -{ - int cpnum = (insn >> 8) & 0xf; - - if (extract32(s->c15_cpar, cpnum, 1) == 0) { - unallocated_encoding(s); - } else if (arm_dc_feature(s, ARM_FEATURE_IWMMXT)) { - if (disas_iwmmxt_insn(s, insn)) { - unallocated_encoding(s); - } - } else if (arm_dc_feature(s, ARM_FEATURE_XSCALE)) { - if (disas_dsp_insn(s, insn)) { - unallocated_encoding(s); - } - } -} - /* Store a 64-bit value to a register pair. Clobbers val. */ static void gen_storeq_reg(DisasContext *s, int rlow, int rhigh, TCGv_i64 val) { @@ -3569,14 +2289,7 @@ static bool valid_cp(DisasContext *s, int cp) * only cp14 and cp15 are valid, and other values aren't considered * to be in the coprocessor-instruction space at all. v8M still * permits coprocessors 0..7. - * For XScale, we must not decode the XScale cp0, cp1 space as - * a standard coprocessor insn, because we want to fall through to - * the legacy disas_xscale_insn() decoder after decodetree is done. */ - if (arm_dc_feature(s, ARM_FEATURE_XSCALE) && (cp == 0 || cp == 1)) { - return false; - } - if (arm_dc_feature(s, ARM_FEATURE_V8) && !arm_dc_feature(s, ARM_FEATURE_M)) { return cp >= 14; @@ -7343,18 +6056,6 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) disas_neon_shared(s, insn)) { return; } - /* fall back to legacy decoder */ - - if ((insn & 0x0e000f00) == 0x0c000100) { - if (arm_dc_feature(s, ARM_FEATURE_IWMMXT)) { - /* iWMMXt register transfer. */ - if (extract32(s->c15_cpar, 1, 1)) { - if (!disas_iwmmxt_insn(s, insn)) { - return; - } - } - } - } goto illegal_op; } if (cond != 0xe) { @@ -7368,16 +6069,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) disas_vfp(s, insn)) { return; } - /* fall back to legacy decoder */ - /* TODO: convert xscale/iwmmxt decoder to decodetree ?? */ - if (arm_dc_feature(s, ARM_FEATURE_XSCALE)) { - if (((insn & 0x0c000e00) == 0x0c000000) - && ((insn & 0x03000000) != 0x03000000)) { - /* Coprocessor insn, coprocessor 0 or 1 */ - disas_xscale_insn(s, insn); - return; - } - } + /* We didn't match anything in the decoder: UNDEF */ illegal_op: unallocated_encoding(s); @@ -7606,12 +6298,8 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->hstr_active = EX_TBFLAG_A32(tb_flags, HSTR_ACTIVE); dc->ns = EX_TBFLAG_A32(tb_flags, NS); dc->vfp_enabled = EX_TBFLAG_A32(tb_flags, VFPEN); - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - dc->c15_cpar = EX_TBFLAG_A32(tb_flags, XSCALE_CPAR); - } else { - dc->vec_len = EX_TBFLAG_A32(tb_flags, VECLEN); - dc->vec_stride = EX_TBFLAG_A32(tb_flags, VECSTRIDE); - } + dc->vec_len = EX_TBFLAG_A32(tb_flags, VECLEN); + dc->vec_stride = EX_TBFLAG_A32(tb_flags, VECSTRIDE); dc->sme_trap_nonstreaming = EX_TBFLAG_A32(tb_flags, SME_TRAP_NONSTREAMING); } @@ -7651,10 +6339,6 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) int bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; dc->base.max_insns = MIN(dc->base.max_insns, bound); } - - cpu_V0 = tcg_temp_new_i64(); - cpu_V1 = tcg_temp_new_i64(); - cpu_M0 = tcg_temp_new_i64(); } static void arm_tr_tb_start(DisasContextBase *dcbase, CPUState *cpu) diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index f974996f3f857..ec4755ae3fdc8 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -175,8 +175,6 @@ typedef struct DisasContext { uint8_t gm_blocksize; /* True if the current insn_start has been updated. */ bool insn_start_updated; - /* Bottom two bits of XScale c15_cpar coprocessor access control reg */ - int c15_cpar; /* Offset from VNCR_EL2 when FEAT_NV2 redirects this reg to memory */ uint32_t nv2_redirect_offset; } DisasContext; From 144aac11d6e0600f6dc644ad0d7e47110d8484f1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Aug 2025 15:04:20 +0100 Subject: [PATCH 0676/1794] target/arm: Remove iwmmxt helper functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the iwmmxt helper functions which are no longer called now that we have removed the associated translate.c handling. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250828140422.3271703-4-peter.maydell@linaro.org --- target/arm/tcg/helper.h | 95 ----- target/arm/tcg/iwmmxt_helper.c | 672 --------------------------------- target/arm/tcg/meson.build | 2 - 3 files changed, 769 deletions(-) delete mode 100644 target/arm/tcg/iwmmxt_helper.c diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h index 4da32db9021c9..4636d1bc039c5 100644 --- a/target/arm/tcg/helper.h +++ b/target/arm/tcg/helper.h @@ -444,101 +444,6 @@ DEF_HELPER_3(neon_acgt_f32, i32, i32, i32, fpst) DEF_HELPER_3(neon_acge_f64, i64, i64, i64, fpst) DEF_HELPER_3(neon_acgt_f64, i64, i64, i64, fpst) -/* iwmmxt_helper.c */ -DEF_HELPER_2(iwmmxt_maddsq, i64, i64, i64) -DEF_HELPER_2(iwmmxt_madduq, i64, i64, i64) -DEF_HELPER_2(iwmmxt_sadb, i64, i64, i64) -DEF_HELPER_2(iwmmxt_sadw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_mulslw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_mulshw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_mululw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_muluhw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_macsw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_macuw, i64, i64, i64) -DEF_HELPER_1(iwmmxt_setpsr_nz, i32, i64) - -#define DEF_IWMMXT_HELPER_SIZE_ENV(name) \ -DEF_HELPER_3(iwmmxt_##name##b, i64, env, i64, i64) \ -DEF_HELPER_3(iwmmxt_##name##w, i64, env, i64, i64) \ -DEF_HELPER_3(iwmmxt_##name##l, i64, env, i64, i64) \ - -DEF_IWMMXT_HELPER_SIZE_ENV(unpackl) -DEF_IWMMXT_HELPER_SIZE_ENV(unpackh) - -DEF_HELPER_2(iwmmxt_unpacklub, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackluw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklul, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhub, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhuw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhul, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklsb, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklsw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklsl, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhsb, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhsw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhsl, i64, env, i64) - -DEF_IWMMXT_HELPER_SIZE_ENV(cmpeq) -DEF_IWMMXT_HELPER_SIZE_ENV(cmpgtu) -DEF_IWMMXT_HELPER_SIZE_ENV(cmpgts) - -DEF_IWMMXT_HELPER_SIZE_ENV(mins) -DEF_IWMMXT_HELPER_SIZE_ENV(minu) -DEF_IWMMXT_HELPER_SIZE_ENV(maxs) -DEF_IWMMXT_HELPER_SIZE_ENV(maxu) - -DEF_IWMMXT_HELPER_SIZE_ENV(subn) -DEF_IWMMXT_HELPER_SIZE_ENV(addn) -DEF_IWMMXT_HELPER_SIZE_ENV(subu) -DEF_IWMMXT_HELPER_SIZE_ENV(addu) -DEF_IWMMXT_HELPER_SIZE_ENV(subs) -DEF_IWMMXT_HELPER_SIZE_ENV(adds) - -DEF_HELPER_3(iwmmxt_avgb0, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_avgb1, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_avgw0, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_avgw1, i64, env, i64, i64) - -DEF_HELPER_3(iwmmxt_align, i64, i64, i64, i32) -DEF_HELPER_4(iwmmxt_insr, i64, i64, i32, i32, i32) - -DEF_HELPER_1(iwmmxt_bcstb, i64, i32) -DEF_HELPER_1(iwmmxt_bcstw, i64, i32) -DEF_HELPER_1(iwmmxt_bcstl, i64, i32) - -DEF_HELPER_1(iwmmxt_addcb, i64, i64) -DEF_HELPER_1(iwmmxt_addcw, i64, i64) -DEF_HELPER_1(iwmmxt_addcl, i64, i64) - -DEF_HELPER_1(iwmmxt_msbb, i32, i64) -DEF_HELPER_1(iwmmxt_msbw, i32, i64) -DEF_HELPER_1(iwmmxt_msbl, i32, i64) - -DEF_HELPER_3(iwmmxt_srlw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_srll, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_srlq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sllw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_slll, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sllq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sraw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sral, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sraq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_rorw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_rorl, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_rorq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_shufh, i64, env, i64, i32) - -DEF_HELPER_3(iwmmxt_packuw, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packul, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packuq, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packsw, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packsl, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packsq, i64, env, i64, i64) - -DEF_HELPER_3(iwmmxt_muladdsl, i64, i64, i32, i32) -DEF_HELPER_3(iwmmxt_muladdsw, i64, i64, i32, i32) -DEF_HELPER_3(iwmmxt_muladdswl, i64, i64, i32, i32) - DEF_HELPER_FLAGS_2(neon_unzip8, TCG_CALL_NO_RWG, void, ptr, ptr) DEF_HELPER_FLAGS_2(neon_unzip16, TCG_CALL_NO_RWG, void, ptr, ptr) DEF_HELPER_FLAGS_2(neon_qunzip8, TCG_CALL_NO_RWG, void, ptr, ptr) diff --git a/target/arm/tcg/iwmmxt_helper.c b/target/arm/tcg/iwmmxt_helper.c deleted file mode 100644 index ba054b6b4db23..0000000000000 --- a/target/arm/tcg/iwmmxt_helper.c +++ /dev/null @@ -1,672 +0,0 @@ -/* - * iwMMXt micro operations for XScale. - * - * Copyright (c) 2007 OpenedHand, Ltd. - * Written by Andrzej Zaborowski - * Copyright (c) 2008 CodeSourcery - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" - -#include "cpu.h" - -#define HELPER_H "tcg/helper.h" -#include "exec/helper-proto.h.inc" - -/* iwMMXt macros extracted from GNU gdb. */ - -/* Set the SIMD wCASF flags for 8, 16, 32 or 64-bit operations. */ -#define SIMD8_SET(v, n, b) ((v != 0) << ((((b) + 1) * 4) + (n))) -#define SIMD16_SET(v, n, h) ((v != 0) << ((((h) + 1) * 8) + (n))) -#define SIMD32_SET(v, n, w) ((v != 0) << ((((w) + 1) * 16) + (n))) -#define SIMD64_SET(v, n) ((v != 0) << (32 + (n))) -/* Flags to pass as "n" above. */ -#define SIMD_NBIT -1 -#define SIMD_ZBIT -2 -#define SIMD_CBIT -3 -#define SIMD_VBIT -4 -/* Various status bit macros. */ -#define NBIT8(x) ((x) & 0x80) -#define NBIT16(x) ((x) & 0x8000) -#define NBIT32(x) ((x) & 0x80000000) -#define NBIT64(x) ((x) & 0x8000000000000000ULL) -#define ZBIT8(x) (((x) & 0xff) == 0) -#define ZBIT16(x) (((x) & 0xffff) == 0) -#define ZBIT32(x) (((x) & 0xffffffff) == 0) -#define ZBIT64(x) (x == 0) -/* Sign extension macros. */ -#define EXTEND8H(a) ((uint16_t) (int8_t) (a)) -#define EXTEND8(a) ((uint32_t) (int8_t) (a)) -#define EXTEND16(a) ((uint32_t) (int16_t) (a)) -#define EXTEND16S(a) ((int32_t) (int16_t) (a)) -#define EXTEND32(a) ((uint64_t) (int32_t) (a)) - -uint64_t HELPER(iwmmxt_maddsq)(uint64_t a, uint64_t b) -{ - a = (( - EXTEND16S((a >> 0) & 0xffff) * EXTEND16S((b >> 0) & 0xffff) + - EXTEND16S((a >> 16) & 0xffff) * EXTEND16S((b >> 16) & 0xffff) - ) & 0xffffffff) | ((uint64_t) ( - EXTEND16S((a >> 32) & 0xffff) * EXTEND16S((b >> 32) & 0xffff) + - EXTEND16S((a >> 48) & 0xffff) * EXTEND16S((b >> 48) & 0xffff) - ) << 32); - return a; -} - -uint64_t HELPER(iwmmxt_madduq)(uint64_t a, uint64_t b) -{ - a = (( - ((a >> 0) & 0xffff) * ((b >> 0) & 0xffff) + - ((a >> 16) & 0xffff) * ((b >> 16) & 0xffff) - ) & 0xffffffff) | (( - ((a >> 32) & 0xffff) * ((b >> 32) & 0xffff) + - ((a >> 48) & 0xffff) * ((b >> 48) & 0xffff) - ) << 32); - return a; -} - -uint64_t HELPER(iwmmxt_sadb)(uint64_t a, uint64_t b) -{ -#define abs(x) (((x) >= 0) ? x : -x) -#define SADB(SHR) abs((int) ((a >> SHR) & 0xff) - (int) ((b >> SHR) & 0xff)) - return - SADB(0) + SADB(8) + SADB(16) + SADB(24) + - SADB(32) + SADB(40) + SADB(48) + SADB(56); -#undef SADB -} - -uint64_t HELPER(iwmmxt_sadw)(uint64_t a, uint64_t b) -{ -#define SADW(SHR) \ - abs((int) ((a >> SHR) & 0xffff) - (int) ((b >> SHR) & 0xffff)) - return SADW(0) + SADW(16) + SADW(32) + SADW(48); -#undef SADW -} - -uint64_t HELPER(iwmmxt_mulslw)(uint64_t a, uint64_t b) -{ -#define MULS(SHR) ((uint64_t) ((( \ - EXTEND16S((a >> SHR) & 0xffff) * EXTEND16S((b >> SHR) & 0xffff) \ - ) >> 0) & 0xffff) << SHR) - return MULS(0) | MULS(16) | MULS(32) | MULS(48); -#undef MULS -} - -uint64_t HELPER(iwmmxt_mulshw)(uint64_t a, uint64_t b) -{ -#define MULS(SHR) ((uint64_t) ((( \ - EXTEND16S((a >> SHR) & 0xffff) * EXTEND16S((b >> SHR) & 0xffff) \ - ) >> 16) & 0xffff) << SHR) - return MULS(0) | MULS(16) | MULS(32) | MULS(48); -#undef MULS -} - -uint64_t HELPER(iwmmxt_mululw)(uint64_t a, uint64_t b) -{ -#define MULU(SHR) ((uint64_t) ((( \ - ((a >> SHR) & 0xffff) * ((b >> SHR) & 0xffff) \ - ) >> 0) & 0xffff) << SHR) - return MULU(0) | MULU(16) | MULU(32) | MULU(48); -#undef MULU -} - -uint64_t HELPER(iwmmxt_muluhw)(uint64_t a, uint64_t b) -{ -#define MULU(SHR) ((uint64_t) ((( \ - ((a >> SHR) & 0xffff) * ((b >> SHR) & 0xffff) \ - ) >> 16) & 0xffff) << SHR) - return MULU(0) | MULU(16) | MULU(32) | MULU(48); -#undef MULU -} - -uint64_t HELPER(iwmmxt_macsw)(uint64_t a, uint64_t b) -{ -#define MACS(SHR) ( \ - EXTEND16((a >> SHR) & 0xffff) * EXTEND16S((b >> SHR) & 0xffff)) - return (int64_t) (MACS(0) + MACS(16) + MACS(32) + MACS(48)); -#undef MACS -} - -uint64_t HELPER(iwmmxt_macuw)(uint64_t a, uint64_t b) -{ -#define MACU(SHR) ( \ - (uint32_t) ((a >> SHR) & 0xffff) * \ - (uint32_t) ((b >> SHR) & 0xffff)) - return MACU(0) + MACU(16) + MACU(32) + MACU(48); -#undef MACU -} - -#define NZBIT8(x, i) \ - SIMD8_SET(NBIT8((x) & 0xff), SIMD_NBIT, i) | \ - SIMD8_SET(ZBIT8((x) & 0xff), SIMD_ZBIT, i) -#define NZBIT16(x, i) \ - SIMD16_SET(NBIT16((x) & 0xffff), SIMD_NBIT, i) | \ - SIMD16_SET(ZBIT16((x) & 0xffff), SIMD_ZBIT, i) -#define NZBIT32(x, i) \ - SIMD32_SET(NBIT32((x) & 0xffffffff), SIMD_NBIT, i) | \ - SIMD32_SET(ZBIT32((x) & 0xffffffff), SIMD_ZBIT, i) -#define NZBIT64(x) \ - SIMD64_SET(NBIT64(x), SIMD_NBIT) | \ - SIMD64_SET(ZBIT64(x), SIMD_ZBIT) -#define IWMMXT_OP_UNPACK(S, SH0, SH1, SH2, SH3) \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, b)))(CPUARMState *env, \ - uint64_t a, uint64_t b) \ -{ \ - a = \ - (((a >> SH0) & 0xff) << 0) | (((b >> SH0) & 0xff) << 8) | \ - (((a >> SH1) & 0xff) << 16) | (((b >> SH1) & 0xff) << 24) | \ - (((a >> SH2) & 0xff) << 32) | (((b >> SH2) & 0xff) << 40) | \ - (((a >> SH3) & 0xff) << 48) | (((b >> SH3) & 0xff) << 56); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \ - NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \ - NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \ - NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ - return a; \ -} \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, w)))(CPUARMState *env, \ - uint64_t a, uint64_t b) \ -{ \ - a = \ - (((a >> SH0) & 0xffff) << 0) | \ - (((b >> SH0) & 0xffff) << 16) | \ - (((a >> SH2) & 0xffff) << 32) | \ - (((b >> SH2) & 0xffff) << 48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT8(a >> 0, 0) | NZBIT8(a >> 16, 1) | \ - NZBIT8(a >> 32, 2) | NZBIT8(a >> 48, 3); \ - return a; \ -} \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, l)))(CPUARMState *env, \ - uint64_t a, uint64_t b) \ -{ \ - a = \ - (((a >> SH0) & 0xffffffff) << 0) | \ - (((b >> SH0) & 0xffffffff) << 32); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ - return a; \ -} \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ub)))(CPUARMState *env, \ - uint64_t x) \ -{ \ - x = \ - (((x >> SH0) & 0xff) << 0) | \ - (((x >> SH1) & 0xff) << 16) | \ - (((x >> SH2) & 0xff) << 32) | \ - (((x >> SH3) & 0xff) << 48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \ - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ - return x; \ -} \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, uw)))(CPUARMState *env, \ - uint64_t x) \ -{ \ - x = \ - (((x >> SH0) & 0xffff) << 0) | \ - (((x >> SH2) & 0xffff) << 32); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ - return x; \ -} \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ul)))(CPUARMState *env, \ - uint64_t x) \ -{ \ - x = (((x >> SH0) & 0xffffffff) << 0); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ - return x; \ -} \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sb)))(CPUARMState *env, \ - uint64_t x) \ -{ \ - x = \ - ((uint64_t) EXTEND8H((x >> SH0) & 0xff) << 0) | \ - ((uint64_t) EXTEND8H((x >> SH1) & 0xff) << 16) | \ - ((uint64_t) EXTEND8H((x >> SH2) & 0xff) << 32) | \ - ((uint64_t) EXTEND8H((x >> SH3) & 0xff) << 48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \ - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ - return x; \ -} \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sw)))(CPUARMState *env, \ - uint64_t x) \ -{ \ - x = \ - ((uint64_t) EXTEND16((x >> SH0) & 0xffff) << 0) | \ - ((uint64_t) EXTEND16((x >> SH2) & 0xffff) << 32); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ - return x; \ -} \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sl)))(CPUARMState *env, \ - uint64_t x) \ -{ \ - x = EXTEND32((x >> SH0) & 0xffffffff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ - return x; \ -} -IWMMXT_OP_UNPACK(l, 0, 8, 16, 24) -IWMMXT_OP_UNPACK(h, 32, 40, 48, 56) - -#define IWMMXT_OP_CMP(SUFF, Tb, Tw, Tl, O) \ -uint64_t HELPER(glue(iwmmxt_, glue(SUFF, b)))(CPUARMState *env, \ - uint64_t a, uint64_t b) \ -{ \ - a = \ - CMP(0, Tb, O, 0xff) | CMP(8, Tb, O, 0xff) | \ - CMP(16, Tb, O, 0xff) | CMP(24, Tb, O, 0xff) | \ - CMP(32, Tb, O, 0xff) | CMP(40, Tb, O, 0xff) | \ - CMP(48, Tb, O, 0xff) | CMP(56, Tb, O, 0xff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \ - NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \ - NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \ - NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ - return a; \ -} \ -uint64_t HELPER(glue(iwmmxt_, glue(SUFF, w)))(CPUARMState *env, \ - uint64_t a, uint64_t b) \ -{ \ - a = CMP(0, Tw, O, 0xffff) | CMP(16, Tw, O, 0xffff) | \ - CMP(32, Tw, O, 0xffff) | CMP(48, Tw, O, 0xffff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) | \ - NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3); \ - return a; \ -} \ -uint64_t HELPER(glue(iwmmxt_, glue(SUFF, l)))(CPUARMState *env, \ - uint64_t a, uint64_t b) \ -{ \ - a = CMP(0, Tl, O, 0xffffffff) | \ - CMP(32, Tl, O, 0xffffffff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ - return a; \ -} -#define CMP(SHR, TYPE, OPER, MASK) ((((TYPE) ((a >> SHR) & MASK) OPER \ - (TYPE) ((b >> SHR) & MASK)) ? (uint64_t) MASK : 0) << SHR) -IWMMXT_OP_CMP(cmpeq, uint8_t, uint16_t, uint32_t, ==) -IWMMXT_OP_CMP(cmpgts, int8_t, int16_t, int32_t, >) -IWMMXT_OP_CMP(cmpgtu, uint8_t, uint16_t, uint32_t, >) -#undef CMP -#define CMP(SHR, TYPE, OPER, MASK) ((((TYPE) ((a >> SHR) & MASK) OPER \ - (TYPE) ((b >> SHR) & MASK)) ? a : b) & ((uint64_t) MASK << SHR)) -IWMMXT_OP_CMP(mins, int8_t, int16_t, int32_t, <) -IWMMXT_OP_CMP(minu, uint8_t, uint16_t, uint32_t, <) -IWMMXT_OP_CMP(maxs, int8_t, int16_t, int32_t, >) -IWMMXT_OP_CMP(maxu, uint8_t, uint16_t, uint32_t, >) -#undef CMP -#define CMP(SHR, TYPE, OPER, MASK) ((uint64_t) (((TYPE) ((a >> SHR) & MASK) \ - OPER (TYPE) ((b >> SHR) & MASK)) & MASK) << SHR) -IWMMXT_OP_CMP(subn, uint8_t, uint16_t, uint32_t, -) -IWMMXT_OP_CMP(addn, uint8_t, uint16_t, uint32_t, +) -#undef CMP -/* TODO Signed- and Unsigned-Saturation */ -#define CMP(SHR, TYPE, OPER, MASK) ((uint64_t) (((TYPE) ((a >> SHR) & MASK) \ - OPER (TYPE) ((b >> SHR) & MASK)) & MASK) << SHR) -IWMMXT_OP_CMP(subu, uint8_t, uint16_t, uint32_t, -) -IWMMXT_OP_CMP(addu, uint8_t, uint16_t, uint32_t, +) -IWMMXT_OP_CMP(subs, int8_t, int16_t, int32_t, -) -IWMMXT_OP_CMP(adds, int8_t, int16_t, int32_t, +) -#undef CMP -#undef IWMMXT_OP_CMP - -#define AVGB(SHR) ((( \ - ((a >> SHR) & 0xff) + ((b >> SHR) & 0xff) + round) >> 1) << SHR) -#define IWMMXT_OP_AVGB(r) \ -uint64_t HELPER(iwmmxt_avgb##r)(CPUARMState *env, uint64_t a, uint64_t b) \ -{ \ - const int round = r; \ - a = AVGB(0) | AVGB(8) | AVGB(16) | AVGB(24) | \ - AVGB(32) | AVGB(40) | AVGB(48) | AVGB(56); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - SIMD8_SET(ZBIT8((a >> 0) & 0xff), SIMD_ZBIT, 0) | \ - SIMD8_SET(ZBIT8((a >> 8) & 0xff), SIMD_ZBIT, 1) | \ - SIMD8_SET(ZBIT8((a >> 16) & 0xff), SIMD_ZBIT, 2) | \ - SIMD8_SET(ZBIT8((a >> 24) & 0xff), SIMD_ZBIT, 3) | \ - SIMD8_SET(ZBIT8((a >> 32) & 0xff), SIMD_ZBIT, 4) | \ - SIMD8_SET(ZBIT8((a >> 40) & 0xff), SIMD_ZBIT, 5) | \ - SIMD8_SET(ZBIT8((a >> 48) & 0xff), SIMD_ZBIT, 6) | \ - SIMD8_SET(ZBIT8((a >> 56) & 0xff), SIMD_ZBIT, 7); \ - return a; \ -} -IWMMXT_OP_AVGB(0) -IWMMXT_OP_AVGB(1) -#undef IWMMXT_OP_AVGB -#undef AVGB - -#define AVGW(SHR) ((( \ - ((a >> SHR) & 0xffff) + ((b >> SHR) & 0xffff) + round) >> 1) << SHR) -#define IWMMXT_OP_AVGW(r) \ -uint64_t HELPER(iwmmxt_avgw##r)(CPUARMState *env, uint64_t a, uint64_t b) \ -{ \ - const int round = r; \ - a = AVGW(0) | AVGW(16) | AVGW(32) | AVGW(48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - SIMD16_SET(ZBIT16((a >> 0) & 0xffff), SIMD_ZBIT, 0) | \ - SIMD16_SET(ZBIT16((a >> 16) & 0xffff), SIMD_ZBIT, 1) | \ - SIMD16_SET(ZBIT16((a >> 32) & 0xffff), SIMD_ZBIT, 2) | \ - SIMD16_SET(ZBIT16((a >> 48) & 0xffff), SIMD_ZBIT, 3); \ - return a; \ -} -IWMMXT_OP_AVGW(0) -IWMMXT_OP_AVGW(1) -#undef IWMMXT_OP_AVGW -#undef AVGW - -uint64_t HELPER(iwmmxt_align)(uint64_t a, uint64_t b, uint32_t n) -{ - a >>= n << 3; - a |= b << (64 - (n << 3)); - return a; -} - -uint64_t HELPER(iwmmxt_insr)(uint64_t x, uint32_t a, uint32_t b, uint32_t n) -{ - x &= ~((uint64_t) b << n); - x |= (uint64_t) (a & b) << n; - return x; -} - -uint32_t HELPER(iwmmxt_setpsr_nz)(uint64_t x) -{ - return SIMD64_SET((x == 0), SIMD_ZBIT) | - SIMD64_SET((x & (1ULL << 63)), SIMD_NBIT); -} - -uint64_t HELPER(iwmmxt_bcstb)(uint32_t arg) -{ - arg &= 0xff; - return - ((uint64_t) arg << 0 ) | ((uint64_t) arg << 8 ) | - ((uint64_t) arg << 16) | ((uint64_t) arg << 24) | - ((uint64_t) arg << 32) | ((uint64_t) arg << 40) | - ((uint64_t) arg << 48) | ((uint64_t) arg << 56); -} - -uint64_t HELPER(iwmmxt_bcstw)(uint32_t arg) -{ - arg &= 0xffff; - return - ((uint64_t) arg << 0 ) | ((uint64_t) arg << 16) | - ((uint64_t) arg << 32) | ((uint64_t) arg << 48); -} - -uint64_t HELPER(iwmmxt_bcstl)(uint32_t arg) -{ - return arg | ((uint64_t) arg << 32); -} - -uint64_t HELPER(iwmmxt_addcb)(uint64_t x) -{ - return - ((x >> 0) & 0xff) + ((x >> 8) & 0xff) + - ((x >> 16) & 0xff) + ((x >> 24) & 0xff) + - ((x >> 32) & 0xff) + ((x >> 40) & 0xff) + - ((x >> 48) & 0xff) + ((x >> 56) & 0xff); -} - -uint64_t HELPER(iwmmxt_addcw)(uint64_t x) -{ - return - ((x >> 0) & 0xffff) + ((x >> 16) & 0xffff) + - ((x >> 32) & 0xffff) + ((x >> 48) & 0xffff); -} - -uint64_t HELPER(iwmmxt_addcl)(uint64_t x) -{ - return (x & 0xffffffff) + (x >> 32); -} - -uint32_t HELPER(iwmmxt_msbb)(uint64_t x) -{ - return - ((x >> 7) & 0x01) | ((x >> 14) & 0x02) | - ((x >> 21) & 0x04) | ((x >> 28) & 0x08) | - ((x >> 35) & 0x10) | ((x >> 42) & 0x20) | - ((x >> 49) & 0x40) | ((x >> 56) & 0x80); -} - -uint32_t HELPER(iwmmxt_msbw)(uint64_t x) -{ - return - ((x >> 15) & 0x01) | ((x >> 30) & 0x02) | - ((x >> 45) & 0x04) | ((x >> 52) & 0x08); -} - -uint32_t HELPER(iwmmxt_msbl)(uint64_t x) -{ - return ((x >> 31) & 0x01) | ((x >> 62) & 0x02); -} - -/* FIXME: Split wCASF setting into a separate op to avoid env use. */ -uint64_t HELPER(iwmmxt_srlw)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = (((x & (0xffffll << 0)) >> n) & (0xffffll << 0)) | - (((x & (0xffffll << 16)) >> n) & (0xffffll << 16)) | - (((x & (0xffffll << 32)) >> n) & (0xffffll << 32)) | - (((x & (0xffffll << 48)) >> n) & (0xffffll << 48)); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); - return x; -} - -uint64_t HELPER(iwmmxt_srll)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = ((x & (0xffffffffll << 0)) >> n) | - ((x >> n) & (0xffffffffll << 32)); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); - return x; -} - -uint64_t HELPER(iwmmxt_srlq)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x >>= n; - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x); - return x; -} - -uint64_t HELPER(iwmmxt_sllw)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = (((x & (0xffffll << 0)) << n) & (0xffffll << 0)) | - (((x & (0xffffll << 16)) << n) & (0xffffll << 16)) | - (((x & (0xffffll << 32)) << n) & (0xffffll << 32)) | - (((x & (0xffffll << 48)) << n) & (0xffffll << 48)); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); - return x; -} - -uint64_t HELPER(iwmmxt_slll)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = ((x << n) & (0xffffffffll << 0)) | - ((x & (0xffffffffll << 32)) << n); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); - return x; -} - -uint64_t HELPER(iwmmxt_sllq)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x <<= n; - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x); - return x; -} - -uint64_t HELPER(iwmmxt_sraw)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = ((uint64_t) ((EXTEND16(x >> 0) >> n) & 0xffff) << 0) | - ((uint64_t) ((EXTEND16(x >> 16) >> n) & 0xffff) << 16) | - ((uint64_t) ((EXTEND16(x >> 32) >> n) & 0xffff) << 32) | - ((uint64_t) ((EXTEND16(x >> 48) >> n) & 0xffff) << 48); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); - return x; -} - -uint64_t HELPER(iwmmxt_sral)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = (((EXTEND32(x >> 0) >> n) & 0xffffffff) << 0) | - (((EXTEND32(x >> 32) >> n) & 0xffffffff) << 32); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); - return x; -} - -uint64_t HELPER(iwmmxt_sraq)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = (int64_t) x >> n; - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x); - return x; -} - -uint64_t HELPER(iwmmxt_rorw)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = ((((x & (0xffffll << 0)) >> n) | - ((x & (0xffffll << 0)) << (16 - n))) & (0xffffll << 0)) | - ((((x & (0xffffll << 16)) >> n) | - ((x & (0xffffll << 16)) << (16 - n))) & (0xffffll << 16)) | - ((((x & (0xffffll << 32)) >> n) | - ((x & (0xffffll << 32)) << (16 - n))) & (0xffffll << 32)) | - ((((x & (0xffffll << 48)) >> n) | - ((x & (0xffffll << 48)) << (16 - n))) & (0xffffll << 48)); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); - return x; -} - -uint64_t HELPER(iwmmxt_rorl)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = ((x & (0xffffffffll << 0)) >> n) | - ((x >> n) & (0xffffffffll << 32)) | - ((x << (32 - n)) & (0xffffffffll << 0)) | - ((x & (0xffffffffll << 32)) << (32 - n)); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); - return x; -} - -uint64_t HELPER(iwmmxt_rorq)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = ror64(x, n); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x); - return x; -} - -uint64_t HELPER(iwmmxt_shufh)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = (((x >> ((n << 4) & 0x30)) & 0xffff) << 0) | - (((x >> ((n << 2) & 0x30)) & 0xffff) << 16) | - (((x >> ((n << 0) & 0x30)) & 0xffff) << 32) | - (((x >> ((n >> 2) & 0x30)) & 0xffff) << 48); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); - return x; -} - -/* TODO: Unsigned-Saturation */ -uint64_t HELPER(iwmmxt_packuw)(CPUARMState *env, uint64_t a, uint64_t b) -{ - a = (((a >> 0) & 0xff) << 0) | (((a >> 16) & 0xff) << 8) | - (((a >> 32) & 0xff) << 16) | (((a >> 48) & 0xff) << 24) | - (((b >> 0) & 0xff) << 32) | (((b >> 16) & 0xff) << 40) | - (((b >> 32) & 0xff) << 48) | (((b >> 48) & 0xff) << 56); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | - NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | - NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | - NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); - return a; -} - -uint64_t HELPER(iwmmxt_packul)(CPUARMState *env, uint64_t a, uint64_t b) -{ - a = (((a >> 0) & 0xffff) << 0) | (((a >> 32) & 0xffff) << 16) | - (((b >> 0) & 0xffff) << 32) | (((b >> 32) & 0xffff) << 48); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) | - NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3); - return a; -} - -uint64_t HELPER(iwmmxt_packuq)(CPUARMState *env, uint64_t a, uint64_t b) -{ - a = (a & 0xffffffff) | ((b & 0xffffffff) << 32); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); - return a; -} - -/* TODO: Signed-Saturation */ -uint64_t HELPER(iwmmxt_packsw)(CPUARMState *env, uint64_t a, uint64_t b) -{ - a = (((a >> 0) & 0xff) << 0) | (((a >> 16) & 0xff) << 8) | - (((a >> 32) & 0xff) << 16) | (((a >> 48) & 0xff) << 24) | - (((b >> 0) & 0xff) << 32) | (((b >> 16) & 0xff) << 40) | - (((b >> 32) & 0xff) << 48) | (((b >> 48) & 0xff) << 56); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | - NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | - NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | - NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); - return a; -} - -uint64_t HELPER(iwmmxt_packsl)(CPUARMState *env, uint64_t a, uint64_t b) -{ - a = (((a >> 0) & 0xffff) << 0) | (((a >> 32) & 0xffff) << 16) | - (((b >> 0) & 0xffff) << 32) | (((b >> 32) & 0xffff) << 48); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) | - NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3); - return a; -} - -uint64_t HELPER(iwmmxt_packsq)(CPUARMState *env, uint64_t a, uint64_t b) -{ - a = (a & 0xffffffff) | ((b & 0xffffffff) << 32); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); - return a; -} - -uint64_t HELPER(iwmmxt_muladdsl)(uint64_t c, uint32_t a, uint32_t b) -{ - return c + ((int32_t) EXTEND32(a) * (int32_t) EXTEND32(b)); -} - -uint64_t HELPER(iwmmxt_muladdsw)(uint64_t c, uint32_t a, uint32_t b) -{ - c += EXTEND32(EXTEND16S((a >> 0) & 0xffff) * - EXTEND16S((b >> 0) & 0xffff)); - c += EXTEND32(EXTEND16S((a >> 16) & 0xffff) * - EXTEND16S((b >> 16) & 0xffff)); - return c; -} - -uint64_t HELPER(iwmmxt_muladdswl)(uint64_t c, uint32_t a, uint32_t b) -{ - return c + (EXTEND32(EXTEND16S(a & 0xffff) * - EXTEND16S(b & 0xffff))); -} diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 895facdc30b3d..1b115656c464f 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -66,7 +66,6 @@ arm_common_ss.add(files( arm_common_system_ss.add(files( 'cpregs-at.c', 'hflags.c', - 'iwmmxt_helper.c', 'neon_helper.c', 'tlb_helper.c', 'tlb-insns.c', @@ -74,7 +73,6 @@ arm_common_system_ss.add(files( )) arm_user_ss.add(files( 'hflags.c', - 'iwmmxt_helper.c', 'neon_helper.c', 'tlb_helper.c', 'vfp_helper.c', From 4f0fa4bfd98f3ccdcdeed6f43a2340084a6e0a92 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Aug 2025 15:04:21 +0100 Subject: [PATCH 0677/1794] target/arm: Drop ARM_FEATURE_XSCALE handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have now removed all the CPU types which had the Intel XScale extensions indicated via ARM_FEATURE_XSCALE, so this feature bit is never set. Remove all the code that can only be reached when using this flag. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250828140422.3271703-5-peter.maydell@linaro.org --- target/arm/cpu.c | 13 --------- target/arm/cpu.h | 3 --- target/arm/helper.c | 54 -------------------------------------- target/arm/ptw.c | 7 +++-- target/arm/tcg/op_helper.c | 6 ----- 5 files changed, 3 insertions(+), 80 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index d0f6fcdfcea93..9781055bdc1bf 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -349,11 +349,6 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) env->uncached_cpsr = ARM_CPU_MODE_USR; /* For user mode we must enable access to coprocessors */ env->vfp.xregs[ARM_VFP_FPEXC] = 1 << 30; - if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - env->cp15.c15_cpar = 3; - } else if (arm_feature(env, ARM_FEATURE_XSCALE)) { - env->cp15.c15_cpar = 1; - } #else /* @@ -2259,14 +2254,6 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } - /* - * We rely on no XScale CPU having VFP so we can use the same bits in the - * TB flags field for VECSTRIDE and XSCALE_CPAR. - */ - assert(arm_feature(env, ARM_FEATURE_AARCH64) || - !cpu_isar_feature(aa32_vfp_simd, cpu) || - !arm_feature(env, ARM_FEATURE_XSCALE)); - #ifndef CONFIG_USER_ONLY { int pagebits; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index f56fa6df8dd31..92fcb96671e52 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -341,7 +341,6 @@ typedef struct CPUArchState { uint64_t vsctlr; /* Virtualization System control register. */ uint64_t cpacr_el1; /* Architectural feature access control register */ uint64_t cptr_el[4]; /* ARMv8 feature trap registers */ - uint32_t c1_xscaleauxcr; /* XScale auxiliary control register. */ uint64_t sder; /* Secure debug enable register. */ uint32_t nsacr; /* Non-secure access control register. */ union { /* MMU translation table base 0. */ @@ -513,7 +512,6 @@ typedef struct CPUArchState { uint64_t cntvoff_el2; /* Counter Virtual Offset register */ uint64_t cntpoff_el2; /* Counter Physical Offset register */ ARMGenericTimer c14_timer[NUM_GTIMERS]; - uint32_t c15_cpar; /* XScale Coprocessor Access Register */ uint32_t c15_ticonfig; /* TI925T configuration byte. */ uint32_t c15_i_max; /* Maximum D-cache dirty line index. */ uint32_t c15_i_min; /* Minimum D-cache dirty line index. */ @@ -2444,7 +2442,6 @@ QEMU_BUILD_BUG_ON(ARRAY_SIZE(((ARMCPU *)0)->ccsidr) <= R_V7M_CSSELR_INDEX_MASK); */ enum arm_features { ARM_FEATURE_AUXCR, /* ARM1026 Auxiliary control register. */ - ARM_FEATURE_XSCALE, /* Intel XScale extensions. */ ARM_FEATURE_IWMMXT, /* Intel iwMMXt extension. */ ARM_FEATURE_V6, ARM_FEATURE_V6K, diff --git a/target/arm/helper.c b/target/arm/helper.c index b641229ba0c43..fa8dfac2998cf 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2923,39 +2923,6 @@ static const ARMCPRegInfo omap_cp_reginfo[] = { .type = ARM_CP_CONST | ARM_CP_OVERRIDE, .resetvalue = 0 }, }; -static void xscale_cpar_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - env->cp15.c15_cpar = value & 0x3fff; -} - -static const ARMCPRegInfo xscale_cp_reginfo[] = { - { .name = "XSCALE_CPAR", - .cp = 15, .crn = 15, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, - .fieldoffset = offsetof(CPUARMState, cp15.c15_cpar), .resetvalue = 0, - .writefn = xscale_cpar_write, }, - { .name = "XSCALE_AUXCR", - .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_RW, - .fieldoffset = offsetof(CPUARMState, cp15.c1_xscaleauxcr), - .resetvalue = 0, }, - /* - * XScale specific cache-lockdown: since we have no cache we NOP these - * and hope the guest does not really rely on cache behaviour. - */ - { .name = "XSCALE_LOCK_ICACHE_LINE", - .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 0, - .access = PL1_W, .type = ARM_CP_NOP }, - { .name = "XSCALE_UNLOCK_ICACHE", - .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NOP }, - { .name = "XSCALE_DCACHE_LOCK", - .cp = 15, .opc1 = 0, .crn = 9, .crm = 2, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_NOP }, - { .name = "XSCALE_UNLOCK_DCACHE", - .cp = 15, .opc1 = 0, .crn = 9, .crm = 2, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NOP }, -}; - static const ARMCPRegInfo dummy_c15_cp_reginfo[] = { /* * RAZ/WI the whole crn=15 space, when we don't have a more specific @@ -3346,16 +3313,6 @@ static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, /* This may enable/disable the MMU, so do a TLB flush. */ tlb_flush(CPU(cpu)); - - if (tcg_enabled() && ri->type & ARM_CP_SUPPRESS_TB_END) { - /* - * Normally we would always end the TB on an SCTLR write; see the - * comment in ARMCPRegInfo sctlr initialization below for why Xscale - * is special. Setting ARM_CP_SUPPRESS_TB_END also stops the rebuild - * of hflags from the translator, so do it here. - */ - arm_rebuild_hflags(env); - } } static void mdcr_el3_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -6894,9 +6851,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_STRONGARM)) { define_arm_cp_regs(cpu, strongarm_cp_reginfo); } - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - define_arm_cp_regs(cpu, xscale_cp_reginfo); - } if (arm_feature(env, ARM_FEATURE_DUMMY_C15_REGS)) { define_arm_cp_regs(cpu, dummy_c15_cp_reginfo); } @@ -7245,14 +7199,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) .writefn = sctlr_write, .resetvalue = cpu->reset_sctlr, .raw_writefn = raw_write, }; - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - /* - * Normally we would always end the TB on an SCTLR write, but Linux - * arch/arm/mach-pxa/sleep.S expects two instructions following - * an MMU enable to execute from cache. Imitate this behaviour. - */ - sctlr.type |= ARM_CP_SUPPRESS_TB_END; - } define_one_arm_cp_reg(cpu, &sctlr); if (arm_feature(env, ARM_FEATURE_PMSA) && diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 089eeff845c65..6344971fa6425 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -1074,11 +1074,10 @@ static bool get_phys_addr_v5(CPUARMState *env, S1Translate *ptw, ap = (desc >> (4 + ((address >> 9) & 6))) & 3; result->f.lg_page_size = 12; break; - case 3: /* 1k page, or ARMv6/XScale "extended small (4k) page" */ + case 3: /* 1k page, or ARMv6 "extended small (4k) page" */ if (type == 1) { - /* ARMv6/XScale extended small page format */ - if (arm_feature(env, ARM_FEATURE_XSCALE) - || arm_feature(env, ARM_FEATURE_V6)) { + /* ARMv6 extended small page format */ + if (arm_feature(env, ARM_FEATURE_V6)) { phys_addr = (desc & 0xfffff000) | (address & 0xfff); result->f.lg_page_size = 12; } else { diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index 575e566280ba0..5373e0e998c8d 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -768,12 +768,6 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, assert(ri != NULL); - if (arm_feature(env, ARM_FEATURE_XSCALE) && ri->cp < 14 - && extract32(env->cp15.c15_cpar, ri->cp, 1) == 0) { - res = CP_ACCESS_UNDEFINED; - goto fail; - } - if (ri->accessfn) { res = ri->accessfn(env, ri, isread); } From effe47ff4840860811e2e1cccfd60cb1cea8e459 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Aug 2025 15:04:22 +0100 Subject: [PATCH 0678/1794] target/arm: Drop ARM_FEATURE_IWMMXT handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have now removed all the CPU types which had the Intel XScale extensions indicated via ARM_FEATURE_IWMMXT, so this feature bit is never set. Remove all the code that can only be reached when using this flag. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250828140422.3271703-6-peter.maydell@linaro.org --- bsd-user/arm/target_arch_elf.h | 1 - linux-user/arm/elfload.c | 1 - linux-user/arm/signal.c | 67 ---------------------------------- target/arm/cpu.c | 8 ---- target/arm/cpu.h | 19 ---------- target/arm/machine.c | 21 ----------- 6 files changed, 117 deletions(-) diff --git a/bsd-user/arm/target_arch_elf.h b/bsd-user/arm/target_arch_elf.h index b1c0fd2b3209e..b54bf5fbc69be 100644 --- a/bsd-user/arm/target_arch_elf.h +++ b/bsd-user/arm/target_arch_elf.h @@ -86,7 +86,6 @@ static uint32_t get_elf_hwcap(void) /* probe for the extra features */ /* EDSP is in v5TE and above */ GET_FEATURE(ARM_FEATURE_V5, ARM_HWCAP_ARM_EDSP); - GET_FEATURE(ARM_FEATURE_IWMMXT, ARM_HWCAP_ARM_IWMMXT); GET_FEATURE(ARM_FEATURE_THUMB2EE, ARM_HWCAP_ARM_THUMBEE); GET_FEATURE(ARM_FEATURE_NEON, ARM_HWCAP_ARM_NEON); GET_FEATURE(ARM_FEATURE_V6K, ARM_HWCAP_ARM_TLS); diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index 308ed23fcbd7c..b1a4db44660b3 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -76,7 +76,6 @@ abi_ulong get_elf_hwcap(CPUState *cs) /* EDSP is in v5TE and above, but all our v5 CPUs are v5TE */ GET_FEATURE(ARM_FEATURE_V5, ARM_HWCAP_ARM_EDSP); - GET_FEATURE(ARM_FEATURE_IWMMXT, ARM_HWCAP_ARM_IWMMXT); GET_FEATURE(ARM_FEATURE_THUMB2EE, ARM_HWCAP_ARM_THUMBEE); GET_FEATURE(ARM_FEATURE_NEON, ARM_HWCAP_ARM_NEON); GET_FEATURE(ARM_FEATURE_V6K, ARM_HWCAP_ARM_TLS); diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index 8db1c4b233877..3b387cd6d78fd 100644 --- a/linux-user/arm/signal.c +++ b/linux-user/arm/signal.c @@ -76,21 +76,7 @@ struct target_vfp_sigframe { struct target_user_vfp_exc ufp_exc; } __attribute__((__aligned__(8))); -struct target_iwmmxt_sigframe { - abi_ulong magic; - abi_ulong size; - uint64_t regs[16]; - /* Note that not all the coprocessor control registers are stored here */ - uint32_t wcssf; - uint32_t wcasf; - uint32_t wcgr0; - uint32_t wcgr1; - uint32_t wcgr2; - uint32_t wcgr3; -} __attribute__((__aligned__(8))); - #define TARGET_VFP_MAGIC 0x56465001 -#define TARGET_IWMMXT_MAGIC 0x12ef842a struct sigframe { @@ -267,25 +253,6 @@ static abi_ulong *setup_sigframe_vfp(abi_ulong *regspace, CPUARMState *env) return (abi_ulong*)(vfpframe+1); } -static abi_ulong *setup_sigframe_iwmmxt(abi_ulong *regspace, CPUARMState *env) -{ - int i; - struct target_iwmmxt_sigframe *iwmmxtframe; - iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace; - __put_user(TARGET_IWMMXT_MAGIC, &iwmmxtframe->magic); - __put_user(sizeof(*iwmmxtframe), &iwmmxtframe->size); - for (i = 0; i < 16; i++) { - __put_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]); - } - __put_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3); - return (abi_ulong*)(iwmmxtframe+1); -} - static void setup_sigframe(struct target_ucontext *uc, target_sigset_t *set, CPUARMState *env) { @@ -306,9 +273,6 @@ static void setup_sigframe(struct target_ucontext *uc, if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) { regspace = setup_sigframe_vfp(regspace, env); } - if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - regspace = setup_sigframe_iwmmxt(regspace, env); - } /* Write terminating magic word */ __put_user(0, regspace); @@ -435,31 +399,6 @@ static abi_ulong *restore_sigframe_vfp(CPUARMState *env, abi_ulong *regspace) return (abi_ulong*)(vfpframe + 1); } -static abi_ulong *restore_sigframe_iwmmxt(CPUARMState *env, - abi_ulong *regspace) -{ - int i; - abi_ulong magic, sz; - struct target_iwmmxt_sigframe *iwmmxtframe; - iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace; - - __get_user(magic, &iwmmxtframe->magic); - __get_user(sz, &iwmmxtframe->size); - if (magic != TARGET_IWMMXT_MAGIC || sz != sizeof(*iwmmxtframe)) { - return 0; - } - for (i = 0; i < 16; i++) { - __get_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]); - } - __get_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3); - return (abi_ulong*)(iwmmxtframe + 1); -} - static int do_sigframe_return(CPUARMState *env, target_ulong context_addr, struct target_ucontext *uc) @@ -482,12 +421,6 @@ static int do_sigframe_return(CPUARMState *env, return 1; } } - if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - regspace = restore_sigframe_iwmmxt(env, regspace); - if (!regspace) { - return 1; - } - } target_restore_altstack(&uc->tuc_stack, env); diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 9781055bdc1bf..02e2a31a86357 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -247,10 +247,6 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) cpu->power_state = cs->start_powered_off ? PSCI_OFF : PSCI_ON; - if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - env->iwmmxt.cregs[ARM_IWMMXT_wCID] = 0x69051000 | 'Q'; - } - if (arm_feature(env, ARM_FEATURE_AARCH64)) { /* 64 bit CPUs always start in 64 bit mode */ env->aarch64 = true; @@ -2610,14 +2606,10 @@ static const Property arm_cpu_properties[] = { static const gchar *arm_gdb_arch_name(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; if (arm_gdbstub_is_aarch64(cpu)) { return "aarch64"; } - if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - return "iwmmxt"; - } return "arm"; } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 92fcb96671e52..6644043f4c240 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -697,14 +697,6 @@ typedef struct CPUArchState { */ uint64_t exclusive_high; - /* iwMMXt coprocessor state. */ - struct { - uint64_t regs[16]; - uint64_t val; - - uint32_t cregs[16]; - } iwmmxt; - struct { ARMPACKey apia; ARMPACKey apib; @@ -1863,16 +1855,6 @@ enum arm_cpu_mode { /* QEMU-internal value meaning "FPSCR, but we care only about NZCV" */ #define QEMU_VFP_FPSCR_NZCV 0xffff -/* iwMMXt coprocessor control registers. */ -#define ARM_IWMMXT_wCID 0 -#define ARM_IWMMXT_wCon 1 -#define ARM_IWMMXT_wCSSF 2 -#define ARM_IWMMXT_wCASF 3 -#define ARM_IWMMXT_wCGR0 8 -#define ARM_IWMMXT_wCGR1 9 -#define ARM_IWMMXT_wCGR2 10 -#define ARM_IWMMXT_wCGR3 11 - /* V7M CCR bits */ FIELD(V7M_CCR, NONBASETHRDENA, 0, 1) FIELD(V7M_CCR, USERSETMPEND, 1, 1) @@ -2442,7 +2424,6 @@ QEMU_BUILD_BUG_ON(ARRAY_SIZE(((ARMCPU *)0)->ccsidr) <= R_V7M_CSSELR_INDEX_MASK); */ enum arm_features { ARM_FEATURE_AUXCR, /* ARM1026 Auxiliary control register. */ - ARM_FEATURE_IWMMXT, /* Intel iwMMXt extension. */ ARM_FEATURE_V6, ARM_FEATURE_V6K, ARM_FEATURE_V7, diff --git a/target/arm/machine.c b/target/arm/machine.c index 6986915bee876..6666a0c50c455 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -221,26 +221,6 @@ static const VMStateDescription vmstate_vfp = { } }; -static bool iwmmxt_needed(void *opaque) -{ - ARMCPU *cpu = opaque; - CPUARMState *env = &cpu->env; - - return arm_feature(env, ARM_FEATURE_IWMMXT); -} - -static const VMStateDescription vmstate_iwmmxt = { - .name = "cpu/iwmmxt", - .version_id = 1, - .minimum_version_id = 1, - .needed = iwmmxt_needed, - .fields = (const VMStateField[]) { - VMSTATE_UINT64_ARRAY(env.iwmmxt.regs, ARMCPU, 16), - VMSTATE_UINT32_ARRAY(env.iwmmxt.cregs, ARMCPU, 16), - VMSTATE_END_OF_LIST() - } -}; - /* The expression ARM_MAX_VQ - 2 is 0 for pure AArch32 build, * and ARMPredicateReg is actively empty. This triggers errors * in the expansion of the VMSTATE macros. @@ -1102,7 +1082,6 @@ const VMStateDescription vmstate_arm_cpu = { }, .subsections = (const VMStateDescription * const []) { &vmstate_vfp, - &vmstate_iwmmxt, &vmstate_m, &vmstate_thumb2ee, /* pmsav7_rnr must come before pmsav7 so that we have the From 116c2c21d521eb31fffb44eb74d918975c9a5658 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Aug 2025 17:27:00 +0100 Subject: [PATCH 0679/1794] system: drop the -old-param option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We deprecated the command line option -old-param for the 10.0 release, which allows us to drop it in 10.2. This option was used to boot Arm targets with a very old boot protocol using the 'param_struct' ABI. We only ever needed this on a handful of board types which have all now been removed from QEMU. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Reviewed-by: Manos Pitsidianakis Reviewed-by: Alex Bennée Message-id: 20250828162700.3308812-1-peter.maydell@linaro.org --- docs/about/deprecated.rst | 13 ------ docs/about/removed-features.rst | 12 +++++ hw/arm/boot.c | 81 +-------------------------------- include/system/system.h | 1 - qemu-options.hx | 7 --- system/globals.c | 1 - system/vl.c | 4 -- 7 files changed, 13 insertions(+), 106 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index f031414769808..03f7cabf7301e 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -68,19 +68,6 @@ configurations (e.g. -smp drawers=1,books=1,clusters=1 for x86 PC machine) is marked deprecated since 9.0, users have to ensure that all the topology members described with -smp are supported by the target machine. -``-old-param`` option for booting Arm kernels via param_struct (since 10.0) -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -The ``-old-param`` command line option is specific to Arm targets: -it is used when directly booting a guest kernel to pass it the -command line and other information via the old ``param_struct`` ABI, -rather than the newer ATAGS or DTB mechanisms. This option was only -ever needed to support ancient kernels on some old board types -like the ``akita`` or ``terrier``; it has been deprecated in the -kernel since 2001. None of the board types QEMU supports need -``param_struct`` support, so this option has been deprecated and will -be removed in a future QEMU version. - QEMU Machine Protocol (QMP) commands ------------------------------------ diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 65fd564d229be..07ca4079d4b01 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -560,6 +560,18 @@ the options along with the machine models they were intended for. Use ``-run-with user=..`` instead. +``-old-param`` option for booting Arm kernels via param_struct (removed in 10.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``-old-param`` command line option was specific to Arm targets: +it was used when directly booting a guest kernel to pass it the +command line and other information via the old ``param_struct`` ABI, +rather than the newer ATAGS or DTB mechanisms. This option was only +ever needed to support ancient kernels on some old board types +like the ``akita`` or ``terrier``; it has been deprecated in the +kernel since 2001. None of the board types QEMU supports need +``param_struct`` support, so this option has been removed. + User-mode emulator command line arguments ----------------------------------------- diff --git a/hw/arm/boot.c b/hw/arm/boot.c index d0840308f5aa1..e77d8679d8872 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -337,81 +337,6 @@ static void set_kernel_args(const struct arm_boot_info *info, AddressSpace *as) WRITE_WORD(p, 0); } -static void set_kernel_args_old(const struct arm_boot_info *info, - AddressSpace *as) -{ - hwaddr p; - const char *s; - int initrd_size = info->initrd_size; - hwaddr base = info->loader_start; - - /* see linux/include/asm-arm/setup.h */ - p = base + KERNEL_ARGS_ADDR; - /* page_size */ - WRITE_WORD(p, 4096); - /* nr_pages */ - WRITE_WORD(p, info->ram_size / 4096); - /* ramdisk_size */ - WRITE_WORD(p, 0); -#define FLAG_READONLY 1 -#define FLAG_RDLOAD 4 -#define FLAG_RDPROMPT 8 - /* flags */ - WRITE_WORD(p, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT); - /* rootdev */ - WRITE_WORD(p, (31 << 8) | 0); /* /dev/mtdblock0 */ - /* video_num_cols */ - WRITE_WORD(p, 0); - /* video_num_rows */ - WRITE_WORD(p, 0); - /* video_x */ - WRITE_WORD(p, 0); - /* video_y */ - WRITE_WORD(p, 0); - /* memc_control_reg */ - WRITE_WORD(p, 0); - /* unsigned char sounddefault */ - /* unsigned char adfsdrives */ - /* unsigned char bytes_per_char_h */ - /* unsigned char bytes_per_char_v */ - WRITE_WORD(p, 0); - /* pages_in_bank[4] */ - WRITE_WORD(p, 0); - WRITE_WORD(p, 0); - WRITE_WORD(p, 0); - WRITE_WORD(p, 0); - /* pages_in_vram */ - WRITE_WORD(p, 0); - /* initrd_start */ - if (initrd_size) { - WRITE_WORD(p, info->initrd_start); - } else { - WRITE_WORD(p, 0); - } - /* initrd_size */ - WRITE_WORD(p, initrd_size); - /* rd_start */ - WRITE_WORD(p, 0); - /* system_rev */ - WRITE_WORD(p, 0); - /* system_serial_low */ - WRITE_WORD(p, 0); - /* system_serial_high */ - WRITE_WORD(p, 0); - /* mem_fclk_21285 */ - WRITE_WORD(p, 0); - /* zero unused fields */ - while (p < base + KERNEL_ARGS_ADDR + 256 + 1024) { - WRITE_WORD(p, 0); - } - s = info->kernel_cmdline; - if (s) { - address_space_write(as, p, MEMTXATTRS_UNSPECIFIED, s, strlen(s) + 1); - } else { - WRITE_WORD(p, 0); - } -} - static int fdt_add_memory_node(void *fdt, uint32_t acells, hwaddr mem_base, uint32_t scells, hwaddr mem_len, int numa_node_id) @@ -802,11 +727,7 @@ static void do_cpu_reset(void *opaque) cpu_set_pc(cs, info->loader_start); if (!have_dtb(info)) { - if (old_param) { - set_kernel_args_old(info, as); - } else { - set_kernel_args(info, as); - } + set_kernel_args(info, as); } } else if (info->secondary_cpu_reset_hook) { info->secondary_cpu_reset_hook(cpu, info); diff --git a/include/system/system.h b/include/system/system.h index a7effe7dfd8b2..03a2d0e9005d6 100644 --- a/include/system/system.h +++ b/include/system/system.h @@ -42,7 +42,6 @@ extern int graphic_height; extern int graphic_depth; extern int display_opengl; extern const char *keyboard_layout; -extern int old_param; extern uint8_t *boot_splash_filedata; extern bool enable_cpu_pm; extern QEMUClockType rtc_clock; diff --git a/qemu-options.hx b/qemu-options.hx index ab23f14d21782..aa44b0e34ae90 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -5347,13 +5347,6 @@ SRST specified, the former is passed to semihosting as it always takes precedence. ERST -DEF("old-param", 0, QEMU_OPTION_old_param, - "-old-param old param mode\n", QEMU_ARCH_ARM) -SRST -``-old-param`` - Old param mode (ARM only). -ERST - DEF("sandbox", HAS_ARG, QEMU_OPTION_sandbox, \ "-sandbox on[,obsolete=allow|deny][,elevateprivileges=allow|deny|children]\n" \ " [,spawn=allow|deny][,resourcecontrol=allow|deny]\n" \ diff --git a/system/globals.c b/system/globals.c index 9640c9511e9aa..98f9876d5d4fc 100644 --- a/system/globals.c +++ b/system/globals.c @@ -52,7 +52,6 @@ bool vga_interface_created; Chardev *parallel_hds[MAX_PARALLEL_PORTS]; QEMUOptionRom option_rom[MAX_OPTION_ROMS]; int nb_option_roms; -int old_param; const char *qemu_name; unsigned int nb_prom_envs; const char *prom_envs[MAX_PROM_ENVS]; diff --git a/system/vl.c b/system/vl.c index 3b7057e6c669f..00f36947257a5 100644 --- a/system/vl.c +++ b/system/vl.c @@ -3524,10 +3524,6 @@ void qemu_init(int argc, char **argv) prom_envs[nb_prom_envs] = optarg; nb_prom_envs++; break; - case QEMU_OPTION_old_param: - warn_report("-old-param is deprecated"); - old_param = 1; - break; case QEMU_OPTION_rtc: opts = qemu_opts_parse_noisily(qemu_find_opts("rtc"), optarg, false); From 19f6dcfe6b8b2a3523362812fc696ab83050d316 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 11 Sep 2025 17:41:59 +0200 Subject: [PATCH 0680/1794] arm/kvm: report registers we failed to set If we fail migration because of a mismatch of some registers between source and destination, the error message is not very informative: qemu-system-aarch64: error while loading state for instance 0x0 ofdevice 'cpu' qemu-system-aarch64: Failed to put registers after init: Invalid argument At least try to give the user a hint which registers had a problem, even if they cannot really do anything about it right now. Sample output: Could not set register op0:3 op1:0 crn:0 crm:0 op2:0 to c00fac31 (is 413fd0c1) We could be even more helpful once we support writable ID registers, at which point the user might actually be able to configure something that is migratable. Suggested-by: Eric Auger Reviewed-by: Sebastian Ott Signed-off-by: Cornelia Huck Message-id: 20250911154159.158046-1-cohuck@redhat.com Signed-off-by: Peter Maydell --- target/arm/kvm.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 667234485547a..c1ec6654ca677 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -900,6 +900,58 @@ bool write_kvmstate_to_list(ARMCPU *cpu) return ok; } +/* pretty-print a KVM register */ +#define CP_REG_ARM64_SYSREG_OP(_reg, _op) \ + ((uint8_t)((_reg & CP_REG_ARM64_SYSREG_ ## _op ## _MASK) >> \ + CP_REG_ARM64_SYSREG_ ## _op ## _SHIFT)) + +static gchar *kvm_print_sve_register_name(uint64_t regidx) +{ + uint16_t sve_reg = regidx & 0x000000000000ffff; + + if (regidx == KVM_REG_ARM64_SVE_VLS) { + return g_strdup_printf("SVE VLS"); + } + /* zreg, preg, ffr */ + switch (sve_reg & 0xfc00) { + case 0: + return g_strdup_printf("SVE zreg n:%d slice:%d", + (sve_reg & 0x03e0) >> 5, sve_reg & 0x001f); + case 0x04: + return g_strdup_printf("SVE preg n:%d slice:%d", + (sve_reg & 0x01e0) >> 5, sve_reg & 0x001f); + case 0x06: + return g_strdup_printf("SVE ffr slice:%d", sve_reg & 0x001f); + default: + return g_strdup_printf("SVE ???"); + } +} + +static gchar *kvm_print_register_name(uint64_t regidx) +{ + switch ((regidx & KVM_REG_ARM_COPROC_MASK)) { + case KVM_REG_ARM_CORE: + return g_strdup_printf("core reg %"PRIx64, regidx); + case KVM_REG_ARM_DEMUX: + return g_strdup_printf("demuxed reg %"PRIx64, regidx); + case KVM_REG_ARM64_SYSREG: + return g_strdup_printf("op0:%d op1:%d crn:%d crm:%d op2:%d", + CP_REG_ARM64_SYSREG_OP(regidx, OP0), + CP_REG_ARM64_SYSREG_OP(regidx, OP1), + CP_REG_ARM64_SYSREG_OP(regidx, CRN), + CP_REG_ARM64_SYSREG_OP(regidx, CRM), + CP_REG_ARM64_SYSREG_OP(regidx, OP2)); + case KVM_REG_ARM_FW: + return g_strdup_printf("fw reg %d", (int)(regidx & 0xffff)); + case KVM_REG_ARM64_SVE: + return kvm_print_sve_register_name(regidx); + case KVM_REG_ARM_FW_FEAT_BMAP: + return g_strdup_printf("fw feat reg %d", (int)(regidx & 0xffff)); + default: + return g_strdup_printf("%"PRIx64, regidx); + } +} + bool write_list_to_kvmstate(ARMCPU *cpu, int level) { CPUState *cs = CPU(cpu); @@ -927,11 +979,45 @@ bool write_list_to_kvmstate(ARMCPU *cpu, int level) g_assert_not_reached(); } if (ret) { + gchar *reg_str = kvm_print_register_name(regidx); + /* We might fail for "unknown register" and also for * "you tried to set a register which is constant with * a different value from what it actually contains". */ ok = false; + switch (ret) { + case -ENOENT: + error_report("Could not set register %s: unknown to KVM", + reg_str); + break; + case -EINVAL: + if ((regidx & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32) { + if (!kvm_get_one_reg(cs, regidx, &v32)) { + error_report("Could not set register %s to %x (is %x)", + reg_str, (uint32_t)cpu->cpreg_values[i], + v32); + } else { + error_report("Could not set register %s to %x", + reg_str, (uint32_t)cpu->cpreg_values[i]); + } + } else /* U64 */ { + uint64_t v64; + + if (!kvm_get_one_reg(cs, regidx, &v64)) { + error_report("Could not set register %s to %"PRIx64" (is %"PRIx64")", + reg_str, cpu->cpreg_values[i], v64); + } else { + error_report("Could not set register %s to %"PRIx64, + reg_str, cpu->cpreg_values[i]); + } + } + break; + default: + error_report("Could not set register %s: %s", + reg_str, strerror(-ret)); + } + g_free(reg_str); } } return ok; From 71cda981208ebcfa403d8e3451d1d5bdef0b0931 Mon Sep 17 00:00:00 2001 From: Vacha Bhavsar Date: Tue, 9 Sep 2025 16:10:10 +0000 Subject: [PATCH 0681/1794] target/arm: Increase MAX_PACKET_LENGTH for SME ZA remote gdb debugging This patch increases the value of the MAX_PACKET_LEGNTH to 131104 from 4096 to allow the GDBState.line_buf to be large enough to accommodate the full contents of the SME ZA storage when the vector length is maximal. This is in preparation for a related patch that allows SME register visibility through remote GDB debugging. Signed-off-by: Vacha Bhavsar Reviewed-by: Peter Maydell Message-id: 20250909161012.2561593-2-vacha.bhavsar@oss.qualcomm.com [PMM: fixed up comment formatting] Signed-off-by: Peter Maydell --- gdbstub/internals.h | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/gdbstub/internals.h b/gdbstub/internals.h index bf5a5c63029c9..92466b28c187b 100644 --- a/gdbstub/internals.h +++ b/gdbstub/internals.h @@ -11,7 +11,27 @@ #include "exec/cpu-common.h" -#define MAX_PACKET_LENGTH 4096 +/* + * Most "large" transfers (e.g. memory reads, feature XML + * transfer) have mechanisms in the gdb protocol for splitting + * them. However, register values in particular cannot currently + * be split. This packet size must therefore be at least big enough + * for the worst-case register size. Currently that is Arm SME + * ZA storage with a 256x256 byte value. We also must account + * for the conversion from raw data to hex in gdb_memtohex(), + * which writes 2 * size bytes, and for other protocol overhead + * including command, register number and checksum which add + * another 4 bytes of overhead. However, to be consistent with + * the changes made in gdbserver to address this same requirement, + * we add a total of 32 bytes to account for protocol overhead + * (unclear why specifically 32 bytes), bringing the value of + * MAX_PACKET_LENGTH to 2 * 256 * 256 + 32 = 131104. + * + * The commit making this change for gdbserver can be found here: + * https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h= + * b816042e88583f280ad186ff124ab84d31fb592b + */ +#define MAX_PACKET_LENGTH 131104 /* * Shared structures and definitions From 030f0ba11767c7bd4148d9cd4f63e299d38139b3 Mon Sep 17 00:00:00 2001 From: Vacha Bhavsar Date: Tue, 9 Sep 2025 16:10:11 +0000 Subject: [PATCH 0682/1794] target/arm: Added support for SME register exposure to GDB The QEMU GDB stub does not expose the ZA storage SME register to GDB via the remote serial protocol, which can be a useful functionality to debug SME code. To provide this functionality for AArch64 targets, this patch registers the SME register set with the GDB stub. To do so, this patch implements the aarch64_gdb_get_sme_reg() and aarch64_gdb_set_sme_reg() functions to specify how to get and set the SME registers, and the arm_gen_dynamic_smereg_feature() function to generate the target description in XML format to indicate the target architecture supports SME. Finally, this patch includes a dyn_smereg_feature structure to hold this GDB XML description of the SME registers for each CPU. Note that according to the GDB documentation the ZA register is defined as a vector of bytes; however the target description xml retrieved when using gdb natively on a host with SME capabilities represents the ZA register as a vector of vectors of bytes, so this is a GDB documentation error. We follow GDB's own gdbstub implementation and represent the ZA register as a vector of vectors of bytes as is done by GDB here: https://github.com/bminor/binutils-gdb/blob/5cce2b7006daa7073b98e3d1a3b176199d1381d7/gdb/features/aarch64-sme.c#L50 Signed-off-by: Vacha Bhavsar Message-id: 20250909161012.2561593-3-vacha.bhavsar@oss.qualcomm.com Reviewed-by: Peter Maydell [PMM: fixed minor checkpatch nits] Signed-off-by: Peter Maydell --- target/arm/cpu.h | 1 + target/arm/gdbstub.c | 10 +++- target/arm/gdbstub64.c | 119 +++++++++++++++++++++++++++++++++++++++++ target/arm/internals.h | 3 ++ 4 files changed, 132 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 6644043f4c240..1c0deb723d705 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -925,6 +925,7 @@ struct ArchCPU { DynamicGDBFeatureInfo dyn_sysreg_feature; DynamicGDBFeatureInfo dyn_svereg_feature; + DynamicGDBFeatureInfo dyn_smereg_feature; DynamicGDBFeatureInfo dyn_m_systemreg_feature; DynamicGDBFeatureInfo dyn_m_secextreg_feature; diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index ce4497ad7c3e4..2d331fff445b5 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -527,7 +527,8 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) * registers so we don't need to include both. */ #ifdef TARGET_AARCH64 - if (isar_feature_aa64_sve(&cpu->isar)) { + if (isar_feature_aa64_sve(&cpu->isar) || + isar_feature_aa64_sme(&cpu->isar)) { GDBFeature *feature = arm_gen_dynamic_svereg_feature(cs, cs->gdb_num_regs); gdb_register_coprocessor(cs, aarch64_gdb_get_sve_reg, aarch64_gdb_set_sve_reg, feature, 0); @@ -537,6 +538,13 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) gdb_find_static_feature("aarch64-fpu.xml"), 0); } + + if (isar_feature_aa64_sme(&cpu->isar)) { + GDBFeature *sme_feature = + arm_gen_dynamic_smereg_feature(cs, cs->gdb_num_regs); + gdb_register_coprocessor(cs, aarch64_gdb_get_sme_reg, + aarch64_gdb_set_sme_reg, sme_feature, 0); + } /* * Note that we report pauth information via the feature name * org.gnu.gdb.aarch64.pauth_v2, not org.gnu.gdb.aarch64.pauth. diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index 08e2858539681..3bccde2bf2595 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -249,6 +249,90 @@ int aarch64_gdb_set_sve_reg(CPUState *cs, uint8_t *buf, int reg) return 0; } +int aarch64_gdb_get_sme_reg(CPUState *cs, GByteArray *buf, int reg) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + switch (reg) { + case 0: /* svg register */ + { + int vq = 0; + if (FIELD_EX64(env->svcr, SVCR, SM)) { + vq = sve_vqm1_for_el_sm(env, arm_current_el(env), + FIELD_EX64(env->svcr, SVCR, SM)) + 1; + } + /* svg = vector granules (2 * vector quardwords) in streaming mode */ + return gdb_get_reg64(buf, vq * 2); + } + case 1: /* svcr register */ + return gdb_get_reg64(buf, env->svcr); + case 2: /* za register */ + { + int len = 0; + int vq = cpu->sme_max_vq; + int svl = vq * 16; + for (int i = 0; i < svl; i++) { + for (int q = 0; q < vq; q++) { + len += gdb_get_reg128(buf, + env->za_state.za[i].d[q * 2 + 1], + env->za_state.za[i].d[q * 2]); + } + } + return len; + } + default: + /* gdbstub asked for something out of range */ + qemu_log_mask(LOG_UNIMP, "%s: out of range register %d", __func__, reg); + break; + } + + return 0; +} + +int aarch64_gdb_set_sme_reg(CPUState *cs, uint8_t *buf, int reg) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + switch (reg) { + case 0: /* svg register */ + /* cannot set svg via gdbstub */ + return 8; + case 1: /* svcr register */ + aarch64_set_svcr(env, ldq_le_p(buf), + R_SVCR_SM_MASK | R_SVCR_ZA_MASK); + return 8; + case 2: /* za register */ + { + int len = 0; + int vq = cpu->sme_max_vq; + int svl = vq * 16; + for (int i = 0; i < svl; i++) { + for (int q = 0; q < vq; q++) { + if (target_big_endian()) { + env->za_state.za[i].d[q * 2 + 1] = ldq_p(buf); + buf += 8; + env->za_state.za[i].d[q * 2] = ldq_p(buf); + } else{ + env->za_state.za[i].d[q * 2] = ldq_p(buf); + buf += 8; + env->za_state.za[i].d[q * 2 + 1] = ldq_p(buf); + } + buf += 8; + len += 16; + } + } + return len; + } + default: + /* gdbstub asked for something out of range */ + break; + } + + return 0; +} + int aarch64_gdb_get_pauth_reg(CPUState *cs, GByteArray *buf, int reg) { ARMCPU *cpu = ARM_CPU(cs); @@ -413,6 +497,41 @@ GDBFeature *arm_gen_dynamic_svereg_feature(CPUState *cs, int base_reg) return &cpu->dyn_svereg_feature.desc; } +GDBFeature *arm_gen_dynamic_smereg_feature(CPUState *cs, int base_reg) +{ + ARMCPU *cpu = ARM_CPU(cs); + int vq = cpu->sme_max_vq; + int svl = vq * 16; + GDBFeatureBuilder builder; + int reg = 0; + + gdb_feature_builder_init(&builder, &cpu->dyn_smereg_feature.desc, + "org.gnu.gdb.aarch64.sme", "sme-registers.xml", + base_reg); + + + /* Create the sme_bv vector type. */ + gdb_feature_builder_append_tag( + &builder, "", + svl); + + /* Create the sme_bvv vector type. */ + gdb_feature_builder_append_tag( + &builder, "", + svl); + + /* Define the svg, svcr, and za registers. */ + + gdb_feature_builder_append_reg(&builder, "svg", 64, reg++, "int", NULL); + gdb_feature_builder_append_reg(&builder, "svcr", 64, reg++, "int", NULL); + gdb_feature_builder_append_reg(&builder, "za", svl * svl * 8, reg++, + "sme_bvv", NULL); + + gdb_feature_builder_end(&builder); + + return &cpu->dyn_smereg_feature.desc; +} + #ifdef CONFIG_USER_ONLY int aarch64_gdb_get_tag_ctl_reg(CPUState *cs, GByteArray *buf, int reg) { diff --git a/target/arm/internals.h b/target/arm/internals.h index 8782594b774ca..532fabcafc1d4 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1817,8 +1817,11 @@ static inline uint64_t pmu_counter_mask(CPUARMState *env) } GDBFeature *arm_gen_dynamic_svereg_feature(CPUState *cpu, int base_reg); +GDBFeature *arm_gen_dynamic_smereg_feature(CPUState *cpu, int base_reg); int aarch64_gdb_get_sve_reg(CPUState *cs, GByteArray *buf, int reg); int aarch64_gdb_set_sve_reg(CPUState *cs, uint8_t *buf, int reg); +int aarch64_gdb_get_sme_reg(CPUState *cs, GByteArray *buf, int reg); +int aarch64_gdb_set_sme_reg(CPUState *cs, uint8_t *buf, int reg); int aarch64_gdb_get_fpu_reg(CPUState *cs, GByteArray *buf, int reg); int aarch64_gdb_set_fpu_reg(CPUState *cs, uint8_t *buf, int reg); int aarch64_gdb_get_pauth_reg(CPUState *cs, GByteArray *buf, int reg); From 904b8aae52a7b23fa00285decba535bd1e8f8d50 Mon Sep 17 00:00:00 2001 From: Vacha Bhavsar Date: Tue, 9 Sep 2025 16:10:12 +0000 Subject: [PATCH 0683/1794] target/arm: Added test case for SME register exposure to GDB This patch adds a test case to test SME register exposure to a remote gdb debugging session. This test simply sets and reads SME registers. Signed-off-by: Vacha Bhavsar Message-id: 20250909161012.2561593-4-vacha.bhavsar@oss.qualcomm.com [PMM: fixed various python formatting nits] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- configure | 6 ++ tests/tcg/aarch64/Makefile.target | 29 +++++++ tests/tcg/aarch64/gdbstub/test-sme.py | 117 ++++++++++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 tests/tcg/aarch64/gdbstub/test-sme.py diff --git a/configure b/configure index 274a7787642e2..9aea02cf6a23c 100755 --- a/configure +++ b/configure @@ -1839,6 +1839,12 @@ for target in $target_list; do echo "GDB=$gdb_bin" >> $config_target_mak fi + if test "${gdb_arches#*$arch}" != "$gdb_arches" && version_ge $gdb_version 14.1; then + echo "GDB_HAS_SME_TILES=y" >> $config_target_mak + else + echo "GDB_HAS_SME_TILES=n" >> $config_target_mak + fi + if test "${gdb_arches#*aarch64}" != "$gdb_arches" && version_ge $gdb_version 15.1; then echo "GDB_HAS_MTE=y" >> $config_target_mak fi diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 16ddcf4f8831d..1755874beed90 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -134,6 +134,35 @@ run-gdbstub-sve-ioctls: sve-ioctls EXTRA_RUNS += run-gdbstub-sysregs run-gdbstub-sve-ioctls +ifneq ($(CROSS_AS_HAS_ARMV9_SME),) +# SME gdbstub tests + +run-gdbstub-sysregs-sme: sysregs + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(AARCH64_SRC)/gdbstub/test-sme.py \ + -- test_sme --gdb_basic_za_test, \ + basic gdbstub SME support) + +ifeq ($(GDB_HAS_SME_TILES),y) +run-gdbstub-sysregs-sme-tile-slice: sysregs + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(AARCH64_SRC)/gdbstub/test-sme.py \ + -- test_sme --gdb_tile_slice_test, \ + gdbstub SME ZA tile slice support) +else +run-gdbstub-sysregs-sme-tile-slice: sysregs + $(call skip-test,"gdbstub SME ZA tile slice support", \ + "selected gdb ($(GDB)) does not support SME ZA tile slices") +endif + +EXTRA_RUNS += run-gdbstub-sysregs-sme run-gdbstub-sysregs-sme-tile-slice + +endif + ifeq ($(GDB_HAS_MTE),y) run-gdbstub-mte: mte-8 $(call run-test, $@, $(GDB_SCRIPT) \ diff --git a/tests/tcg/aarch64/gdbstub/test-sme.py b/tests/tcg/aarch64/gdbstub/test-sme.py new file mode 100644 index 0000000000000..ec03189642756 --- /dev/null +++ b/tests/tcg/aarch64/gdbstub/test-sme.py @@ -0,0 +1,117 @@ +# +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +# +# Test the SME registers are visible and changeable via gdbstub +# +# This is launched via tests/guest-debug/run-test.py +# + +import argparse +import gdb +from test_gdbstub import main, report + +MAGIC = 0x01020304 +BASIC_ZA_TEST = 0 +TILE_SLICE_TEST = 0 + + +def run_test(): + """Run the requested test(s) for SME ZA gdbstub support""" + + if BASIC_ZA_TEST: + run_basic_sme_za_gdbstub_support_test() + if TILE_SLICE_TEST: + run_basic_sme_za_tile_slice_gdbstub_support_test() + + +def run_basic_sme_za_gdbstub_support_test(): + """Test reads and writes to the SME ZA register at the byte level""" + + frame = gdb.selected_frame() + rname = "za" + za = frame.read_register(rname) + report(True, "Reading %s" % rname) + + # Writing to the ZA register, byte by byte. + for i in range(0, 16): + for j in range(0, 16): + cmd = "set $za[%d][%d] = 0x01" % (i, j) + gdb.execute(cmd) + report(True, "%s" % cmd) + + # Reading from the ZA register, byte by byte. + for i in range(0, 16): + for j in range(0, 16): + reg = "$za[%d][%d]" % (i, j) + v = gdb.parse_and_eval(reg) + report(str(v.type) == "uint8_t", "size of %s" % (reg)) + report(v == 0x1, "%s is 0x%x" % (reg, 0x1)) + + +def run_basic_sme_za_tile_slice_gdbstub_support_test(): + """Test reads and writes of SME ZA horizontal and vertical tile slices + + Test if SME ZA tile slices, both horizontal and vertical, + can be correctly read and written to. The sizes to test + are quadwords and doublewords. + """ + + sizes = {} + sizes["q"] = "uint128_t" + sizes["d"] = "uint64_t" + + # Accessing requested sizes of elements of ZA + for size in sizes: + + # Accessing various ZA tiles + for i in range(0, 4): + + # Accessing various horizontal slices for each ZA tile + for j in range(0, 4): + # Writing to various elements in each tile slice + for k in range(0, 4): + cmd = "set $za%dh%c%d[%d] = 0x%x" % (i, size, j, k, MAGIC) + gdb.execute(cmd) + report(True, "%s" % cmd) + + # Reading from the written elements in each tile slice + for k in range(0, 4): + reg = "$za%dh%c%d[%d]" % (i, size, j, k) + v = gdb.parse_and_eval(reg) + report(str(v.type) == sizes[size], "size of %s" % (reg)) + report(v == MAGIC, "%s is 0x%x" % (reg, MAGIC)) + + # Accessing various vertical slices for each ZA tile + for j in range(0, 4): + # Writing to various elements in each tile slice + for k in range(0, 4): + cmd = "set $za%dv%c%d[%d] = 0x%x" % (i, size, j, k, MAGIC) + gdb.execute(cmd) + report(True, "%s" % cmd) + + # Reading from the written elements in each tile slice + for k in range(0, 4): + reg = "$za%dv%c%d[%d]" % (i, size, j, k) + v = gdb.parse_and_eval(reg) + report(str(v.type) == sizes[size], "size of %s" % (reg)) + report(v == MAGIC, "%s is 0x%x" % (reg, MAGIC)) + + +parser = argparse.ArgumentParser(description="A gdbstub test for SME support") +parser.add_argument("--gdb_basic_za_test", + help="Enable test for basic SME ZA support", + action="store_true") +parser.add_argument("--gdb_tile_slice_test", + help="Enable test for ZA tile slice support", + action="store_true") +args = parser.parse_args() + +if args.gdb_basic_za_test: + BASIC_ZA_TEST = 1 +if args.gdb_tile_slice_test: + TILE_SLICE_TEST = 1 + +main(run_test, expected_arch="aarch64") From d9e6b8424fd2523a0361972d5dd841471879479c Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:23 +0100 Subject: [PATCH 0684/1794] hw/arm/smmu-common: Check SMMU has PCIe Root Complex association We only allow default PCIe Root Complex(pcie.0) or pxb-pcie based extra root complexes to be associated with SMMU. Although this change does not affect functionality at present, it is required when we add support for user-creatable SMMUv3 devices in future patches. Note: Added a specific check to identify pxb-pcie to avoid matching pxb-cxl host bridges, which are also of type PCI_HOST_BRIDGE. This restriction can be relaxed once support for CXL devices on arm/virt is added and validated with SMMUv3. Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Nathan Chen Tested-by: Eric Auger Reviewed-by: Nicolin Chen Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Message-id: 20250829082543.7680-2-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- hw/arm/smmu-common.c | 31 ++++++++++++++++++++++++++--- hw/pci-bridge/pci_expander_bridge.c | 1 - include/hw/pci/pci_bridge.h | 1 + 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 0dcaf2f589710..7f64ea48d0348 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -20,6 +20,7 @@ #include "trace.h" #include "exec/target_page.h" #include "hw/core/cpu.h" +#include "hw/pci/pci_bridge.h" #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu/jhash.h" @@ -925,6 +926,7 @@ static void smmu_base_realize(DeviceState *dev, Error **errp) { SMMUState *s = ARM_SMMU(dev); SMMUBaseClass *sbc = ARM_SMMU_GET_CLASS(dev); + PCIBus *pci_bus = s->primary_bus; Error *local_err = NULL; sbc->parent_realize(dev, &local_err); @@ -937,11 +939,34 @@ static void smmu_base_realize(DeviceState *dev, Error **errp) g_free, g_free); s->smmu_pcibus_by_busptr = g_hash_table_new(NULL, NULL); - if (s->primary_bus) { - pci_setup_iommu(s->primary_bus, &smmu_ops, s); - } else { + if (!pci_bus) { error_setg(errp, "SMMU is not attached to any PCI bus!"); + return; + } + + /* + * We only allow default PCIe Root Complex(pcie.0) or pxb-pcie based extra + * root complexes to be associated with SMMU. + */ + if (pci_bus_is_express(pci_bus) && pci_bus_is_root(pci_bus) && + object_dynamic_cast(OBJECT(pci_bus)->parent, TYPE_PCI_HOST_BRIDGE)) { + /* + * This condition matches either the default pcie.0, pxb-pcie, or + * pxb-cxl. For both pxb-pcie and pxb-cxl, parent_dev will be set. + * Currently, we don't allow pxb-cxl as it requires further + * verification. Therefore, make sure this is indeed pxb-pcie. + */ + if (pci_bus->parent_dev) { + if (!object_dynamic_cast(OBJECT(pci_bus), TYPE_PXB_PCIE_BUS)) { + goto out_err; + } + } + pci_setup_iommu(pci_bus, &smmu_ops, s); + return; } +out_err: + error_setg(errp, "SMMU should be attached to a default PCIe root complex" + "(pcie.0) or a pxb-pcie based root complex"); } /* diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 3a29dfefc2c35..1bcceddbc4d7d 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -34,7 +34,6 @@ typedef struct PXBBus PXBBus; DECLARE_INSTANCE_CHECKER(PXBBus, PXB_BUS, TYPE_PXB_BUS) -#define TYPE_PXB_PCIE_BUS "pxb-pcie-bus" DECLARE_INSTANCE_CHECKER(PXBBus, PXB_PCIE_BUS, TYPE_PXB_PCIE_BUS) diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h index 8cdacbc4e16d4..a055fd8d32189 100644 --- a/include/hw/pci/pci_bridge.h +++ b/include/hw/pci/pci_bridge.h @@ -104,6 +104,7 @@ typedef struct PXBPCIEDev { PXBDev parent_obj; } PXBPCIEDev; +#define TYPE_PXB_PCIE_BUS "pxb-pcie-bus" #define TYPE_PXB_CXL_BUS "pxb-cxl-bus" #define TYPE_PXB_DEV "pxb" OBJECT_DECLARE_SIMPLE_TYPE(PXBDev, PXB_DEV) From 0e6a5bfb0eb17f57fb923b7905bd1435204bdd62 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:24 +0100 Subject: [PATCH 0685/1794] hw/arm/virt-acpi-build: Re-arrange SMMUv3 IORT build Introduce a new struct AcpiIortSMMUv3Dev to hold all the information required for SMMUv3 IORT node and use that for populating the node. The current machine wide SMMUv3 is named as legacy SMMUv3 as we will soon add support for user-creatable SMMUv3 devices. These changes will be useful to have common code paths when we add that support. Tested-by: Nathan Chen Reviewed-by: Nicolin Chen Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Message-id: 20250829082543.7680-3-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 137 ++++++++++++++++++++++++++------------- hw/arm/virt.c | 1 + include/hw/arm/virt.h | 1 + 3 files changed, 94 insertions(+), 45 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index b01fc4f8ef08b..bef4fabe56fcc 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -305,29 +305,65 @@ static int iort_idmap_compare(gconstpointer a, gconstpointer b) return idmap_a->input_base - idmap_b->input_base; } +typedef struct AcpiIortSMMUv3Dev { + int irq; + hwaddr base; + GArray *rc_smmu_idmaps; + /* Offset of the SMMUv3 IORT Node relative to the start of the IORT */ + size_t offset; +} AcpiIortSMMUv3Dev; + +/* + * Populate the struct AcpiIortSMMUv3Dev for the legacy SMMUv3 and + * return the total number of associated idmaps. + */ +static int populate_smmuv3_legacy_dev(GArray *sdev_blob) +{ + VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine()); + AcpiIortSMMUv3Dev sdev; + + sdev.rc_smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); + object_child_foreach_recursive(object_get_root(), iort_host_bridges, + sdev.rc_smmu_idmaps); + /* + * There can be only one legacy SMMUv3("iommu=smmuv3") as it is a machine + * wide one. Since it may cover multiple PCIe RCs(based on "bypass_iommu" + * property), may have multiple SMMUv3 idmaps. Sort it by input_base. + */ + g_array_sort(sdev.rc_smmu_idmaps, iort_idmap_compare); + + sdev.base = vms->memmap[VIRT_SMMU].base; + sdev.irq = vms->irqmap[VIRT_SMMU] + ARM_SPI_BASE; + g_array_append_val(sdev_blob, sdev); + return sdev.rc_smmu_idmaps->len; +} + /* Compute ID ranges (RIDs) from RC that are directed to the ITS Group node */ -static void create_rc_its_idmaps(GArray *its_idmaps, GArray *smmu_idmaps) +static void create_rc_its_idmaps(GArray *its_idmaps, GArray *smmuv3_devs) { AcpiIortIdMapping *idmap; AcpiIortIdMapping next_range = {0}; + AcpiIortSMMUv3Dev *sdev; - /* - * Based on the RID ranges that are directed to the SMMU, determine the - * bypassed RID ranges, i.e., the ones that are directed to the ITS Group - * node and do not pass through the SMMU, by subtracting the SMMU-bound - * ranges from the full RID range (0x0000–0xFFFF). - */ - for (int i = 0; i < smmu_idmaps->len; i++) { - idmap = &g_array_index(smmu_idmaps, AcpiIortIdMapping, i); + for (int i = 0; i < smmuv3_devs->len; i++) { + sdev = &g_array_index(smmuv3_devs, AcpiIortSMMUv3Dev, i); + /* + * Based on the RID ranges that are directed to the SMMU, determine the + * bypassed RID ranges, i.e., the ones that are directed to the ITS + * Group node and do not pass through the SMMU, by subtracting the + * SMMU-bound ranges from the full RID range (0x0000–0xFFFF). + */ + for (int j = 0; j < sdev->rc_smmu_idmaps->len; j++) { + idmap = &g_array_index(sdev->rc_smmu_idmaps, AcpiIortIdMapping, j); - if (next_range.input_base < idmap->input_base) { - next_range.id_count = idmap->input_base - next_range.input_base; - g_array_append_val(its_idmaps, next_range); - } + if (next_range.input_base < idmap->input_base) { + next_range.id_count = idmap->input_base - next_range.input_base; + g_array_append_val(its_idmaps, next_range); + } - next_range.input_base = idmap->input_base + idmap->id_count; + next_range.input_base = idmap->input_base + idmap->id_count; + } } - /* * Append the last RC -> ITS ID mapping. * @@ -341,7 +377,6 @@ static void create_rc_its_idmaps(GArray *its_idmaps, GArray *smmu_idmaps) } } - /* * Input Output Remapping Table (IORT) * Conforms to "IO Remapping Table System Software on ARM Platforms", @@ -351,9 +386,12 @@ static void build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { int i, nb_nodes, rc_mapping_count; - size_t node_size, smmu_offset = 0; + AcpiIortSMMUv3Dev *sdev; + size_t node_size; + int num_smmus = 0; uint32_t id = 0; - GArray *rc_smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); + int rc_smmu_idmaps_len = 0; + GArray *smmuv3_devs = g_array_new(false, true, sizeof(AcpiIortSMMUv3Dev)); GArray *rc_its_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); AcpiTable table = { .sig = "IORT", .rev = 3, .oem_id = vms->oem_id, @@ -361,22 +399,21 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) /* Table 2 The IORT */ acpi_table_begin(&table, table_data); - if (vms->iommu == VIRT_IOMMU_SMMUV3) { - object_child_foreach_recursive(object_get_root(), - iort_host_bridges, rc_smmu_idmaps); - - /* Sort the smmu idmap by input_base */ - g_array_sort(rc_smmu_idmaps, iort_idmap_compare); + if (vms->legacy_smmuv3_present) { + rc_smmu_idmaps_len = populate_smmuv3_legacy_dev(smmuv3_devs); + } - nb_nodes = 2; /* RC and SMMUv3 */ - rc_mapping_count = rc_smmu_idmaps->len; + num_smmus = smmuv3_devs->len; + if (num_smmus) { + nb_nodes = num_smmus + 1; /* RC and SMMUv3 */ + rc_mapping_count = rc_smmu_idmaps_len; if (vms->its) { /* * Knowing the ID ranges from the RC to the SMMU, it's possible to * determine the ID ranges from RC that go directly to ITS. */ - create_rc_its_idmaps(rc_its_idmaps, rc_smmu_idmaps); + create_rc_its_idmaps(rc_its_idmaps, smmuv3_devs); nb_nodes++; /* ITS */ rc_mapping_count += rc_its_idmaps->len; @@ -411,9 +448,10 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, 0 /* MADT translation_id */, 4); } - if (vms->iommu == VIRT_IOMMU_SMMUV3) { - int irq = vms->irqmap[VIRT_SMMU] + ARM_SPI_BASE; + for (i = 0; i < num_smmus; i++) { + sdev = &g_array_index(smmuv3_devs, AcpiIortSMMUv3Dev, i); int smmu_mapping_count, offset_to_id_array; + int irq = sdev->irq; if (vms->its) { smmu_mapping_count = 1; /* ITS Group node */ @@ -422,7 +460,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) smmu_mapping_count = 0; /* No ID mappings */ offset_to_id_array = 0; /* No ID mappings array */ } - smmu_offset = table_data->len - table.table_offset; + sdev->offset = table_data->len - table.table_offset; /* Table 9 SMMUv3 Format */ build_append_int_noprefix(table_data, 4 /* SMMUv3 */, 1); /* Type */ node_size = SMMU_V3_ENTRY_SIZE + @@ -435,7 +473,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) /* Reference to ID Array */ build_append_int_noprefix(table_data, offset_to_id_array, 4); /* Base address */ - build_append_int_noprefix(table_data, vms->memmap[VIRT_SMMU].base, 8); + build_append_int_noprefix(table_data, sdev->base, 8); /* Flags */ build_append_int_noprefix(table_data, 1 /* COHACC Override */, 4); build_append_int_noprefix(table_data, 0, 4); /* Reserved */ @@ -486,21 +524,26 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, 0, 3); /* Reserved */ /* Output Reference */ - if (vms->iommu == VIRT_IOMMU_SMMUV3) { + if (num_smmus) { AcpiIortIdMapping *range; - /* - * Map RIDs (input) from RC to SMMUv3 nodes: RC -> SMMUv3. - * - * N.B.: The mapping from SMMUv3 to ITS Group node (SMMUv3 -> ITS) is - * defined in the SMMUv3 table, where all SMMUv3 IDs are mapped to the - * ITS Group node, if ITS is available. - */ - for (i = 0; i < rc_smmu_idmaps->len; i++) { - range = &g_array_index(rc_smmu_idmaps, AcpiIortIdMapping, i); - /* Output IORT node is the SMMUv3 node. */ - build_iort_id_mapping(table_data, range->input_base, - range->id_count, smmu_offset); + for (i = 0; i < num_smmus; i++) { + sdev = &g_array_index(smmuv3_devs, AcpiIortSMMUv3Dev, i); + + /* + * Map RIDs (input) from RC to SMMUv3 nodes: RC -> SMMUv3. + * + * N.B.: The mapping from SMMUv3 to ITS Group node (SMMUv3 -> ITS) + * is defined in the SMMUv3 table, where all SMMUv3 IDs are mapped + * to the ITS Group node, if ITS is available. + */ + for (int j = 0; j < sdev->rc_smmu_idmaps->len; j++) { + range = &g_array_index(sdev->rc_smmu_idmaps, + AcpiIortIdMapping, j); + /* Output IORT node is the SMMUv3 node. */ + build_iort_id_mapping(table_data, range->input_base, + range->id_count, sdev->offset); + } } if (vms->its) { @@ -525,8 +568,12 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } acpi_table_end(linker, &table); - g_array_free(rc_smmu_idmaps, true); g_array_free(rc_its_idmaps, true); + for (i = 0; i < num_smmus; i++) { + sdev = &g_array_index(smmuv3_devs, AcpiIortSMMUv3Dev, i); + g_array_free(sdev->rc_smmu_idmaps, true); + } + g_array_free(smmuv3_devs, true); } /* diff --git a/hw/arm/virt.c b/hw/arm/virt.c index e5c4142e822d7..16a1ac3c2d92b 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1651,6 +1651,7 @@ static void create_pcie(VirtMachineState *vms) qemu_fdt_setprop_cells(ms->fdt, nodename, "iommu-map", 0x0, vms->iommu_phandle, 0x0, 0x10000); } + vms->legacy_smmuv3_present = true; break; default: g_assert_not_reached(); diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 365a28b082cae..ea2cff05b02f2 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -179,6 +179,7 @@ struct VirtMachineState { char *oem_table_id; bool ns_el2_virt_timer_irq; CXLState cxl_devices_state; + bool legacy_smmuv3_present; }; #define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM) From 01e9a18730e6f56f713ed074603a8b0f2982ed26 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:25 +0100 Subject: [PATCH 0686/1794] hw/arm/virt-acpi-build: Update IORT for multiple smmuv3 devices With the soon to be introduced user-creatable SMMUv3 devices for virt, it is possible to have multiple SMMUv3 devices associated with different PCIe root complexes. Update IORT nodes accordingly. An example IORT Id mappings for a Qemu virt machine with two PCIe Root Complexes each assocaited with a SMMUv3 will be something like below, -device arm-smmuv3,primary-bus=pcie.0,id=smmuv3.0 -device arm-smmuv3,primary-bus=pcie.1,id=smmuv3.1 ... +--------------------+ +--------------------+ | Root Complex 0 | | Root Complex 1 | | | | | | Requestor IDs | | Requestor IDs | | 0x0000 - 0x00FF | | 0x0100 - 0x01FF | +---------+----------+ +---------+----------+ | | | | | Stream ID Mapping | v v +--------------------+ +--------------------+ | SMMUv3 Node 0 | | SMMUv3 Node 1 | | | | | | Stream IDs 0x0000- | | Stream IDs 0x0100- | | 0x00FF mapped from | | 0x01FF mapped from | | RC0 Requestor IDs | | RC1 Requestor IDs | +--------------------+ +--------------------+ | | | | +----------------+---------------+ | |Device ID Mapping v +----------------------------+ | ITS Node 0 | | | | Device IDs: | | 0x0000 - 0x00FF (from RC0) | | 0x0100 - 0x01FF (from RC1) | | 0x0200 - 0xFFFF (No SMMU) | +----------------------------+ Tested-by: Nathan Chen Reviewed-by: Nicolin Chen Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Message-id: 20250829082543.7680-4-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 64 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index bef4fabe56fcc..96830f7c4ecfe 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -45,6 +45,7 @@ #include "hw/acpi/generic_event_device.h" #include "hw/acpi/tpm.h" #include "hw/acpi/hmat.h" +#include "hw/arm/smmuv3.h" #include "hw/cxl/cxl.h" #include "hw/pci/pcie_host.h" #include "hw/pci/pci.h" @@ -338,6 +339,67 @@ static int populate_smmuv3_legacy_dev(GArray *sdev_blob) return sdev.rc_smmu_idmaps->len; } +static int smmuv3_dev_idmap_compare(gconstpointer a, gconstpointer b) +{ + AcpiIortSMMUv3Dev *sdev_a = (AcpiIortSMMUv3Dev *)a; + AcpiIortSMMUv3Dev *sdev_b = (AcpiIortSMMUv3Dev *)b; + AcpiIortIdMapping *map_a = &g_array_index(sdev_a->rc_smmu_idmaps, + AcpiIortIdMapping, 0); + AcpiIortIdMapping *map_b = &g_array_index(sdev_b->rc_smmu_idmaps, + AcpiIortIdMapping, 0); + return map_a->input_base - map_b->input_base; +} + +static int iort_smmuv3_devices(Object *obj, void *opaque) +{ + VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine()); + GArray *sdev_blob = opaque; + AcpiIortIdMapping idmap; + PlatformBusDevice *pbus; + AcpiIortSMMUv3Dev sdev; + int min_bus, max_bus; + SysBusDevice *sbdev; + PCIBus *bus; + + if (!object_dynamic_cast(obj, TYPE_ARM_SMMUV3)) { + return 0; + } + + bus = PCI_BUS(object_property_get_link(obj, "primary-bus", &error_abort)); + pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); + sbdev = SYS_BUS_DEVICE(obj); + sdev.base = platform_bus_get_mmio_addr(pbus, sbdev, 0); + sdev.base += vms->memmap[VIRT_PLATFORM_BUS].base; + sdev.irq = platform_bus_get_irqn(pbus, sbdev, 0); + sdev.irq += vms->irqmap[VIRT_PLATFORM_BUS]; + sdev.irq += ARM_SPI_BASE; + + pci_bus_range(bus, &min_bus, &max_bus); + sdev.rc_smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); + idmap.input_base = min_bus << 8, + idmap.id_count = (max_bus - min_bus + 1) << 8, + g_array_append_val(sdev.rc_smmu_idmaps, idmap); + g_array_append_val(sdev_blob, sdev); + return 0; +} + +/* + * Populate the struct AcpiIortSMMUv3Dev for all SMMUv3 devices and + * return the total number of idmaps. + */ +static int populate_smmuv3_dev(GArray *sdev_blob) +{ + object_child_foreach_recursive(object_get_root(), + iort_smmuv3_devices, sdev_blob); + /* Sort the smmuv3 devices(if any) by smmu idmap input_base */ + g_array_sort(sdev_blob, smmuv3_dev_idmap_compare); + /* + * Since each SMMUv3 dev is assocaited with specific host bridge, + * total number of idmaps equals to total number of smmuv3 devices. + */ + return sdev_blob->len; +} + /* Compute ID ranges (RIDs) from RC that are directed to the ITS Group node */ static void create_rc_its_idmaps(GArray *its_idmaps, GArray *smmuv3_devs) { @@ -401,6 +463,8 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) if (vms->legacy_smmuv3_present) { rc_smmu_idmaps_len = populate_smmuv3_legacy_dev(smmuv3_devs); + } else { + rc_smmu_idmaps_len = populate_smmuv3_dev(smmuv3_devs); } num_smmus = smmuv3_devs->len; From 7a276b7570266ec39611f9d91089741ec7e9295b Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:26 +0100 Subject: [PATCH 0687/1794] hw/arm/virt: Factor out common SMMUV3 dt bindings code No functional changes intended. This will be useful when we add support for user-creatable smmuv3 device. Reviewed-by: Nicolin Chen Reviewed-by: Eric Auger Tested-by: Nathan Chen Reviewed-by: Jonathan Cameron Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Message-id: 20250829082543.7680-5-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 54 +++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 16a1ac3c2d92b..bebe2d8cea955 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1444,19 +1444,43 @@ static void create_pcie_irq_map(const MachineState *ms, 0x7 /* PCI irq */); } +static void create_smmuv3_dt_bindings(const VirtMachineState *vms, hwaddr base, + hwaddr size, int irq) +{ + char *node; + const char compat[] = "arm,smmu-v3"; + const char irq_names[] = "eventq\0priq\0cmdq-sync\0gerror"; + MachineState *ms = MACHINE(vms); + + node = g_strdup_printf("/smmuv3@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, node); + qemu_fdt_setprop(ms->fdt, node, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_sized_cells(ms->fdt, node, "reg", 2, base, 2, size); + + qemu_fdt_setprop_cells(ms->fdt, node, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irq , GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, + GIC_FDT_IRQ_TYPE_SPI, irq + 1, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, + GIC_FDT_IRQ_TYPE_SPI, irq + 2, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, + GIC_FDT_IRQ_TYPE_SPI, irq + 3, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); + + qemu_fdt_setprop(ms->fdt, node, "interrupt-names", irq_names, + sizeof(irq_names)); + + qemu_fdt_setprop(ms->fdt, node, "dma-coherent", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, node, "#iommu-cells", 1); + qemu_fdt_setprop_cell(ms->fdt, node, "phandle", vms->iommu_phandle); + g_free(node); +} + static void create_smmu(const VirtMachineState *vms, PCIBus *bus) { VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); - char *node; - const char compat[] = "arm,smmu-v3"; int irq = vms->irqmap[VIRT_SMMU]; int i; hwaddr base = vms->memmap[VIRT_SMMU].base; hwaddr size = vms->memmap[VIRT_SMMU].size; - const char irq_names[] = "eventq\0priq\0cmdq-sync\0gerror"; DeviceState *dev; - MachineState *ms = MACHINE(vms); if (vms->iommu != VIRT_IOMMU_SMMUV3 || !vms->iommu_phandle) { return; @@ -1475,27 +1499,7 @@ static void create_smmu(const VirtMachineState *vms, sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, qdev_get_gpio_in(vms->gic, irq + i)); } - - node = g_strdup_printf("/smmuv3@%" PRIx64, base); - qemu_fdt_add_subnode(ms->fdt, node); - qemu_fdt_setprop(ms->fdt, node, "compatible", compat, sizeof(compat)); - qemu_fdt_setprop_sized_cells(ms->fdt, node, "reg", 2, base, 2, size); - - qemu_fdt_setprop_cells(ms->fdt, node, "interrupts", - GIC_FDT_IRQ_TYPE_SPI, irq , GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, - GIC_FDT_IRQ_TYPE_SPI, irq + 1, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, - GIC_FDT_IRQ_TYPE_SPI, irq + 2, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, - GIC_FDT_IRQ_TYPE_SPI, irq + 3, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); - - qemu_fdt_setprop(ms->fdt, node, "interrupt-names", irq_names, - sizeof(irq_names)); - - qemu_fdt_setprop(ms->fdt, node, "dma-coherent", NULL, 0); - - qemu_fdt_setprop_cell(ms->fdt, node, "#iommu-cells", 1); - - qemu_fdt_setprop_cell(ms->fdt, node, "phandle", vms->iommu_phandle); - g_free(node); + create_smmuv3_dt_bindings(vms, base, size, irq); } static void create_virtio_iommu_dt_bindings(VirtMachineState *vms) From 466197fc7a25658f9187d538c26887f5738d1ac9 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 29 Aug 2025 09:25:27 +0100 Subject: [PATCH 0688/1794] hw/arm/virt: Add an SMMU_IO_LEN macro This is useful as the subsequent support for new SMMUv3 dev will also use the same. Signed-off-by: Nicolin Chen Reviewed-by: Donald Dutile Reviewed-by: Eric Auger Tested-by: Nathan Chen Reviewed-by: Jonathan Cameron Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Nicolin Chen Message-id: 20250829082543.7680-6-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index bebe2d8cea955..64b4dcf6071d1 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -151,6 +151,9 @@ static void arm_virt_compat_set(MachineClass *mc) #define LEGACY_RAMLIMIT_GB 255 #define LEGACY_RAMLIMIT_BYTES (LEGACY_RAMLIMIT_GB * GiB) +/* MMIO region size for SMMUv3 */ +#define SMMU_IO_LEN 0x20000 + /* Addresses and sizes of our components. * 0..128MB is space for a flash device so we can run bootrom code such as UEFI. * 128MB..256MB is used for miscellaneous device I/O. @@ -182,7 +185,7 @@ static const MemMapEntry base_memmap[] = { [VIRT_FW_CFG] = { 0x09020000, 0x00000018 }, [VIRT_GPIO] = { 0x09030000, 0x00001000 }, [VIRT_UART1] = { 0x09040000, 0x00001000 }, - [VIRT_SMMU] = { 0x09050000, 0x00020000 }, + [VIRT_SMMU] = { 0x09050000, SMMU_IO_LEN }, [VIRT_PCDIMM_ACPI] = { 0x09070000, MEMORY_HOTPLUG_IO_LEN }, [VIRT_ACPI_GED] = { 0x09080000, ACPI_GED_EVT_SEL_LEN }, [VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN}, From 951bc76fb669eab96cc60e38a50097ad4435163e Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:28 +0100 Subject: [PATCH 0689/1794] hw/pci: Introduce pci_setup_iommu_per_bus() for per-bus IOMMU ops retrieval Currently, pci_setup_iommu() registers IOMMU ops for a given PCIBus. However, when retrieving IOMMU ops for a device using pci_device_get_iommu_bus_devfn(), the function checks the parent_dev and fetches IOMMU ops from the parent device, even if the current bus does not have any associated IOMMU ops. This behavior works for now because QEMU's IOMMU implementations are globally scoped, and host bridges rely on the bypass_iommu property to skip IOMMU translation when needed. However, this model will break with the soon to be introduced arm-smmuv3 device, which allows users to associate the IOMMU with a specific PCIe root complex (e.g., the default pcie.0 or a pxb-pcie root complex). For example, consider the following setup with multiple root complexes: -device arm-smmuv3,primary-bus=pcie.0,id=smmuv3.0 \ ... -device pxb-pcie,id=pcie.1,bus_nr=8,bus=pcie.0 \ -device pcie-root-port,id=pcie.port1,bus=pcie.1 \ -device virtio-net-pci,bus=pcie.port1 In Qemu, pxb-pcie acts as a special root complex whose parent is effectively the default root complex(pcie.0). Hence, though pcie.1 has no associated SMMUv3 as per above, pci_device_get_iommu_bus_devfn() will incorrectly return the IOMMU ops from pcie.0 due to the fallback via parent_dev. To fix this, introduce a new helper pci_setup_iommu_per_bus() that explicitly sets the new iommu_per_bus field in the PCIBus structure. This helper will be used in a subsequent patch that adds support for the new arm-smmuv3 device. Update pci_device_get_iommu_bus_devfn() to use iommu_per_bus when determining the correct IOMMU ops, ensuring accurate behavior for per-bus IOMMUs. Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Nathan Chen Tested-by: Eric Auger Reviewed-by: Nicolin Chen Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Message-id: 20250829082543.7680-7-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- hw/pci/pci.c | 31 +++++++++++++++++++++++++++++++ include/hw/pci/pci.h | 2 ++ include/hw/pci/pci_bus.h | 1 + 3 files changed, 34 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 297196b2421a7..c3df9d6656bf8 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2912,6 +2912,19 @@ static void pci_device_get_iommu_bus_devfn(PCIDevice *dev, } } + /* + * When multiple PCI Express Root Buses are defined using pxb-pcie, + * the IOMMU configuration may be specific to each root bus. However, + * pxb-pcie acts as a special root complex whose parent is effectively + * the default root complex(pcie.0). Ensure that we retrieve the + * correct IOMMU ops(if any) in such cases. + */ + if (pci_bus_is_express(iommu_bus) && pci_bus_is_root(iommu_bus)) { + if (parent_bus->iommu_per_bus) { + break; + } + } + iommu_bus = parent_bus; } @@ -3172,6 +3185,24 @@ void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque) bus->iommu_opaque = opaque; } +/* + * Similar to pci_setup_iommu(), but sets iommu_per_bus to true, + * indicating that the IOMMU is specific to this bus. This is used by + * IOMMU implementations that are tied to a specific PCIe root complex. + * + * In QEMU, pxb-pcie behaves as a special root complex whose parent is + * effectively the default root complex (pcie.0). The iommu_per_bus + * is checked in pci_device_get_iommu_bus_devfn() to ensure the correct + * IOMMU ops are returned, avoiding the use of the parent’s IOMMU when + * it's not appropriate. + */ +void pci_setup_iommu_per_bus(PCIBus *bus, const PCIIOMMUOps *ops, + void *opaque) +{ + pci_setup_iommu(bus, ops, opaque); + bus->iommu_per_bus = true; +} + static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) { Range *range = opaque; diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 6b7d3ac8a3611..6bccb25ac2f51 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -773,6 +773,8 @@ int pci_iommu_unregister_iotlb_notifier(PCIDevice *dev, uint32_t pasid, */ void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque); +void pci_setup_iommu_per_bus(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque); + pcibus_t pci_bar_address(PCIDevice *d, int reg, uint8_t type, pcibus_t size); diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index 226131254621f..c73844678886e 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -35,6 +35,7 @@ struct PCIBus { enum PCIBusFlags flags; const PCIIOMMUOps *iommu_ops; void *iommu_opaque; + bool iommu_per_bus; uint8_t devfn_min; uint32_t slot_reserved_mask; pci_set_irq_fn set_irq; From 66d2f665e163cf1afccd171e3c16f8d3acb3d94a Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:29 +0100 Subject: [PATCH 0690/1794] hw/arm/virt: Allow user-creatable SMMUv3 dev instantiation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow cold-plugging of an SMMUv3 device on the virt machine when no global (legacy) SMMUv3 is present or when a virtio-iommu is specified. This user-created SMMUv3 device is tied to a specific PCI bus provided by the user, so ensure the IOMMU ops are configured accordingly. Due to current limitations in QEMU’s device tree support, specifically its inability to properly present pxb-pcie based root complexes and their devices, the device tree support for the new SMMUv3 device is limited to cases where it is attached to the default pcie.0 root complex. Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Nathan Chen Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Reviewed-by: Nicolin Chen Message-id: 20250829082543.7680-8-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- hw/arm/smmu-common.c | 8 +++++- hw/arm/smmuv3.c | 2 ++ hw/arm/virt.c | 51 ++++++++++++++++++++++++++++++++++++ hw/core/sysbus-fdt.c | 3 +++ include/hw/arm/smmu-common.h | 1 + 5 files changed, 64 insertions(+), 1 deletion(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 7f64ea48d0348..62a7612184198 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -961,7 +961,12 @@ static void smmu_base_realize(DeviceState *dev, Error **errp) goto out_err; } } - pci_setup_iommu(pci_bus, &smmu_ops, s); + + if (s->smmu_per_bus) { + pci_setup_iommu_per_bus(pci_bus, &smmu_ops, s); + } else { + pci_setup_iommu(pci_bus, &smmu_ops, s); + } return; } out_err: @@ -986,6 +991,7 @@ static void smmu_base_reset_exit(Object *obj, ResetType type) static const Property smmu_dev_properties[] = { DEFINE_PROP_UINT8("bus_num", SMMUState, bus_num, 0), + DEFINE_PROP_BOOL("smmu_per_bus", SMMUState, smmu_per_bus, false), DEFINE_PROP_LINK("primary-bus", SMMUState, primary_bus, TYPE_PCI_BUS, PCIBus *), }; diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index ab67972353315..bcf8af8dc731b 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1996,6 +1996,8 @@ static void smmuv3_class_init(ObjectClass *klass, const void *data) device_class_set_parent_realize(dc, smmu_realize, &c->parent_realize); device_class_set_props(dc, smmuv3_properties); + dc->hotpluggable = false; + dc->user_creatable = true; } static int smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu, diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 64b4dcf6071d1..7b3f9b1cdfe75 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -57,6 +57,7 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/module.h" +#include "hw/pci/pci_bus.h" #include "hw/pci-host/gpex.h" #include "hw/pci-bridge/pci_expander_bridge.h" #include "hw/virtio/virtio-pci.h" @@ -1475,6 +1476,29 @@ static void create_smmuv3_dt_bindings(const VirtMachineState *vms, hwaddr base, g_free(node); } +static void create_smmuv3_dev_dtb(VirtMachineState *vms, + DeviceState *dev, PCIBus *bus) +{ + PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); + SysBusDevice *sbdev = SYS_BUS_DEVICE(dev); + int irq = platform_bus_get_irqn(pbus, sbdev, 0); + hwaddr base = platform_bus_get_mmio_addr(pbus, sbdev, 0); + MachineState *ms = MACHINE(vms); + + if (!(vms->bootinfo.firmware_loaded && virt_is_acpi_enabled(vms)) && + strcmp("pcie.0", bus->qbus.name)) { + warn_report("SMMUv3 device only supported with pcie.0 for DT"); + return; + } + base += vms->memmap[VIRT_PLATFORM_BUS].base; + irq += vms->irqmap[VIRT_PLATFORM_BUS]; + + vms->iommu_phandle = qemu_fdt_alloc_phandle(ms->fdt); + create_smmuv3_dt_bindings(vms, base, SMMU_IO_LEN, irq); + qemu_fdt_setprop_cells(ms->fdt, vms->pciehb_nodename, "iommu-map", + 0x0, vms->iommu_phandle, 0x0, 0x10000); +} + static void create_smmu(const VirtMachineState *vms, PCIBus *bus) { @@ -3006,6 +3030,16 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, qlist_append_str(reserved_regions, resv_prop_str); qdev_prop_set_array(dev, "reserved-regions", reserved_regions); g_free(resv_prop_str); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_ARM_SMMUV3)) { + if (vms->legacy_smmuv3_present || vms->iommu == VIRT_IOMMU_VIRTIO) { + error_setg(errp, "virt machine already has %s set. " + "Doesn't support incompatible iommus", + (vms->legacy_smmuv3_present) ? + "iommu=smmuv3" : "virtio-iommu"); + } else if (vms->iommu == VIRT_IOMMU_NONE) { + /* The new SMMUv3 device is specific to the PCI bus */ + object_property_set_bool(OBJECT(dev), "smmu_per_bus", true, NULL); + } } } @@ -3029,6 +3063,22 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, virtio_md_pci_plug(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), errp); } + if (object_dynamic_cast(OBJECT(dev), TYPE_ARM_SMMUV3)) { + if (!vms->legacy_smmuv3_present && vms->platform_bus_dev) { + PCIBus *bus; + + bus = PCI_BUS(object_property_get_link(OBJECT(dev), "primary-bus", + &error_abort)); + if (pci_bus_bypass_iommu(bus)) { + error_setg(errp, "Bypass option cannot be set for SMMUv3 " + "associated PCIe RC"); + return; + } + + create_smmuv3_dev_dtb(vms, dev, bus); + } + } + if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { PCIDevice *pdev = PCI_DEVICE(dev); @@ -3231,6 +3281,7 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_PLATFORM); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_ARM_SMMUV3); #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c index c339a27875cbe..e80776080be9e 100644 --- a/hw/core/sysbus-fdt.c +++ b/hw/core/sysbus-fdt.c @@ -31,6 +31,7 @@ #include "qemu/error-report.h" #include "system/device_tree.h" #include "system/tpm.h" +#include "hw/arm/smmuv3.h" #include "hw/platform-bus.h" #include "hw/vfio/vfio-platform.h" #include "hw/vfio/vfio-calxeda-xgmac.h" @@ -518,6 +519,8 @@ static const BindingEntry bindings[] = { #ifdef CONFIG_TPM TYPE_BINDING(TYPE_TPM_TIS_SYSBUS, add_tpm_tis_fdt_node), #endif + /* No generic DT support for smmuv3 dev. Support added for arm virt only */ + TYPE_BINDING(TYPE_ARM_SMMUV3, no_fdt_node), TYPE_BINDING(TYPE_RAMFB_DEVICE, no_fdt_node), TYPE_BINDING(TYPE_UEFI_VARS_SYSBUS, add_uefi_vars_node), TYPE_BINDING("", NULL), /* last element */ diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index e5e2d09294d73..80d0fecfde8ad 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -161,6 +161,7 @@ struct SMMUState { QLIST_HEAD(, SMMUDevice) devices_with_notifiers; uint8_t bus_num; PCIBus *primary_bus; + bool smmu_per_bus; /* SMMU is specific to the primary_bus */ }; struct SMMUBaseClass { From 73d3d0187bc6b482d8b15116edce1475c7975b89 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:30 +0100 Subject: [PATCH 0691/1794] qemu-options.hx: Document the arm-smmuv3 device Now that arm,virt can have user-creatable smmuv3 devices, document it. Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Reviewed-by: Nicolin Chen Message-id: 20250829082543.7680-9-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- qemu-options.hx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/qemu-options.hx b/qemu-options.hx index aa44b0e34ae90..075f4be2e3e67 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1231,6 +1231,13 @@ SRST ``aw-bits=val`` (val between 32 and 64, default depends on machine) This decides the address width of the IOVA address space. +``-device arm-smmuv3,primary-bus=id`` + This is only supported by ``-machine virt`` (ARM). + + ``primary-bus=id`` + Accepts either the default root complex (pcie.0) or a + pxb-pcie based root complex. + ERST DEF("name", HAS_ARG, QEMU_OPTION_name, From c69520c13d6ea45a69a7a49361806fa05b19046d Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:31 +0100 Subject: [PATCH 0692/1794] bios-tables-test: Allow for smmuv3 test data. The tests to be added exercise both legacy(iommu=smmuv3) and new -device arm-smmuv3,.. cases. Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Reviewed-by: Nicolin Chen Message-id: 20250829082543.7680-10-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev | 0 tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy | 0 tests/data/acpi/aarch64/virt/IORT.smmuv3-dev | 0 tests/data/acpi/aarch64/virt/IORT.smmuv3-legacy | 0 tests/qtest/bios-tables-test-allowed-diff.h | 4 ++++ 5 files changed, 4 insertions(+) create mode 100644 tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev create mode 100644 tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy create mode 100644 tests/data/acpi/aarch64/virt/IORT.smmuv3-dev create mode 100644 tests/data/acpi/aarch64/virt/IORT.smmuv3-legacy diff --git a/tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev b/tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy b/tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/data/acpi/aarch64/virt/IORT.smmuv3-dev b/tests/data/acpi/aarch64/virt/IORT.smmuv3-dev new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/data/acpi/aarch64/virt/IORT.smmuv3-legacy b/tests/data/acpi/aarch64/virt/IORT.smmuv3-legacy new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8bf45..2e3e3ccdcec2e 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,5 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy", +"tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev", +"tests/data/acpi/aarch64/virt/IORT.smmuv3-legacy", +"tests/data/acpi/aarch64/virt/IORT.smmuv3-dev", From 3f8cd046c151c471d9a34181320f4a7d3f72b32a Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:32 +0100 Subject: [PATCH 0693/1794] qtest/bios-tables-test: Add tests for legacy smmuv3 and smmuv3 device For the legacy SMMUv3 test, the setup includes three PCIe Root Complexes, one of which has bypass_iommu enabled. The generated IORT table contains a single SMMUv3 node, a Root Complex(RC) node and 1 ITS node. RC node features 4 ID mappings, of which 2 points to SMMU node and the remaining ones points to ITS. pcie.0 -> {SMMU0} -> {ITS} {RC} pcie.1 -> {SMMU0} -> {ITS} pcie.2 -> {ITS} [all other ids] -> {ITS} For the -device arm-smmuv3,... test, the configuration also includes three Root Complexes, with two connected to separate SMMUv3 devices. The resulting IORT table contains 1 RC node, 2 SMMU nodes and 1 ITS node. RC node features 4 ID mappings. 2 of them target the 2 SMMU nodes while the others targets the ITS. pcie.0 -> {SMMU0} -> {ITS} {RC} pcie.1 -> {SMMU1} -> {ITS} pcie.2 -> {ITS} [all other ids] -> {ITS} Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Reviewed-by: Nicolin Chen Message-id: 20250829082543.7680-11-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- tests/qtest/bios-tables-test.c | 86 ++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index e7e6926c816f1..4fa8ac5096a6e 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -2337,6 +2337,86 @@ static void test_acpi_aarch64_virt_viot(void) free_test_data(&data); } +static void test_acpi_aarch64_virt_smmuv3_legacy(void) +{ + test_data data = { + .machine = "virt", + .arch = "aarch64", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .ram_start = 0x40000000ULL, + .scan_len = 128ULL * MiB, + }; + + /* + * cdrom is plugged into scsi controller to avoid conflict + * with pxb-pcie. See comments in test_acpi_aarch64_virt_tcg_pxb() for + * details. + * + * The setup includes three PCIe root complexes, one of which has + * bypass_iommu enabled. The generated IORT table contains a single + * SMMUv3 node and a Root Complex node with three ID mappings. Two + * of the ID mappings have output references pointing to the SMMUv3 + * node and the remaining one points to ITS. + */ + data.variant = ".smmuv3-legacy"; + test_acpi_one(" -device pcie-root-port,chassis=1,id=pci.1" + " -device virtio-scsi-pci,id=scsi0,bus=pci.1" + " -drive file=" + "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2," + "if=none,media=cdrom,id=drive-scsi0-0-0-1,readonly=on" + " -device scsi-cd,bus=scsi0.0,scsi-id=0," + "drive=drive-scsi0-0-0-1,id=scsi0-0-0-1,bootindex=1" + " -cpu cortex-a57" + " -M iommu=smmuv3" + " -device pxb-pcie,id=pcie.1,bus=pcie.0,bus_nr=0x10" + " -device pxb-pcie,id=pcie.2,bus=pcie.0,bus_nr=0x20,bypass_iommu=on", + &data); + free_test_data(&data); +} + +static void test_acpi_aarch64_virt_smmuv3_dev(void) +{ + test_data data = { + .machine = "virt", + .arch = "aarch64", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .ram_start = 0x40000000ULL, + .scan_len = 128ULL * MiB, + }; + + /* + * cdrom is plugged into scsi controller to avoid conflict + * with pxb-pcie. See comments in test_acpi_aarch64_virt_tcg_pxb() + * for details. + * + * The setup includes three PCie root complexes, two of which are + * connected to separate SMMUv3 devices. The resulting IORT table + * contains two SMMUv3 nodes and a Root Complex node with ID mappings + * of which two of the ID mappings have output references pointing + * to two different SMMUv3 nodes and the remaining ones pointing to + * ITS. + */ + data.variant = ".smmuv3-dev"; + test_acpi_one(" -device pcie-root-port,chassis=1,id=pci.1" + " -device virtio-scsi-pci,id=scsi0,bus=pci.1" + " -drive file=" + "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2," + "if=none,media=cdrom,id=drive-scsi0-0-0-1,readonly=on" + " -device scsi-cd,bus=scsi0.0,scsi-id=0," + "drive=drive-scsi0-0-0-1,id=scsi0-0-0-1,bootindex=1" + " -cpu cortex-a57" + " -device arm-smmuv3,primary-bus=pcie.0,id=smmuv3.0" + " -device pxb-pcie,id=pcie.1,bus=pcie.0,bus_nr=0x10" + " -device arm-smmuv3,primary-bus=pcie.1,id=smmuv3.1" + " -device pxb-pcie,id=pcie.2,bus=pcie.0,bus_nr=0x20", + &data); + free_test_data(&data); +} + #ifndef _WIN32 # define DEV_NULL "/dev/null" #else @@ -2768,6 +2848,12 @@ int main(int argc, char *argv[]) if (qtest_has_device("virtio-iommu-pci")) { qtest_add_func("acpi/virt/viot", test_acpi_aarch64_virt_viot); } + qtest_add_func("acpi/virt/smmuv3-legacy", + test_acpi_aarch64_virt_smmuv3_legacy); + if (qtest_has_device("arm-smmuv3")) { + qtest_add_func("acpi/virt/smmuv3-dev", + test_acpi_aarch64_virt_smmuv3_dev); + } } } else if (strcmp(arch, "riscv64") == 0) { if (has_tcg && qtest_has_device("virtio-blk-pci")) { From d35146a6606cf6ebb4e24bb97dfc0330f074f6e3 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:33 +0100 Subject: [PATCH 0694/1794] qtest/bios-tables-test: Update tables for smmuv3 tests For the legacy smmuv3 test case, generated IORT has a single SMMUv3 node, a Root Complex(RC) node and 1 ITS node. RC node features 4 ID mappings, of which 2 points to SMMU node and the remaining ones points to ITS. pcie.0 -> {SMMU0} -> {ITS} {RC} pcie.1 -> {SMMU0} -> {ITS} pcie.2 -> {ITS} [all other ids] -> {ITS} ... [030h 0048 1] Type : 00 [031h 0049 2] Length : 0018 [033h 0051 1] Revision : 01 [034h 0052 4] Identifier : 00000000 [038h 0056 4] Mapping Count : 00000000 [03Ch 0060 4] Mapping Offset : 00000000 [040h 0064 4] ItsCount : 00000001 [044h 0068 4] Identifiers : 00000000 [048h 0072 1] Type : 04 [049h 0073 2] Length : 0058 [04Bh 0075 1] Revision : 04 [04Ch 0076 4] Identifier : 00000001 [050h 0080 4] Mapping Count : 00000001 [054h 0084 4] Mapping Offset : 00000044 [058h 0088 8] Base Address : 0000000009050000 [060h 0096 4] Flags (decoded below) : 00000001 COHACC Override : 1 HTTU Override : 0 Proximity Domain Valid : 0 [064h 0100 4] Reserved : 00000000 [068h 0104 8] VATOS Address : 0000000000000000 [070h 0112 4] Model : 00000000 [074h 0116 4] Event GSIV : 0000006A [078h 0120 4] PRI GSIV : 0000006B [07Ch 0124 4] GERR GSIV : 0000006D [080h 0128 4] Sync GSIV : 0000006C [084h 0132 4] Proximity Domain : 00000000 [088h 0136 4] Device ID Mapping Index : 00000000 [08Ch 0140 4] Input base : 00000000 [090h 0144 4] ID Count : 0000FFFF [094h 0148 4] Output Base : 00000000 [098h 0152 4] Output Reference : 00000030 [09Ch 0156 4] Flags (decoded below) : 00000000 Single Mapping : 0 [0A0h 0160 1] Type : 02 [0A1h 0161 2] Length : 0074 [0A3h 0163 1] Revision : 03 [0A4h 0164 4] Identifier : 00000002 [0A8h 0168 4] Mapping Count : 00000004 [0ACh 0172 4] Mapping Offset : 00000024 [0B0h 0176 8] Memory Properties : [IORT Memory Access Properties] [0B0h 0176 4] Cache Coherency : 00000001 [0B4h 0180 1] Hints (decoded below) : 00 Transient : 0 Write Allocate : 0 Read Allocate : 0 Override : 0 [0B5h 0181 2] Reserved : 0000 [0B7h 0183 1] Memory Flags (decoded below) : 03 Coherency : 1 Device Attribute : 1 [0B8h 0184 4] ATS Attribute : 00000000 [0BCh 0188 4] PCI Segment Number : 00000000 [0C0h 0192 1] Memory Size Limit : 40 [0C1h 0193 2] PASID Capabilities : 0000 [0C3h 0195 1] Reserved : 00 [0C4h 0196 4] Input base : 00000000 [0C8h 0200 4] ID Count : 000001FF [0CCh 0204 4] Output Base : 00000000 [0D0h 0208 4] Output Reference : 00000048 [0D4h 0212 4] Flags (decoded below) : 00000000 Single Mapping : 0 [0D8h 0216 4] Input base : 00001000 [0DCh 0220 4] ID Count : 000000FF [0E0h 0224 4] Output Base : 00001000 [0E4h 0228 4] Output Reference : 00000048 [0E8h 0232 4] Flags (decoded below) : 00000000 Single Mapping : 0 [0ECh 0236 4] Input base : 00000200 [0F0h 0240 4] ID Count : 00000DFF [0F4h 0244 4] Output Base : 00000200 [0F8h 0248 4] Output Reference : 00000030 [0FCh 0252 4] Flags (decoded below) : 00000000 Single Mapping : 0 [100h 0256 4] Input base : 00001100 [104h 0260 4] ID Count : 0000EEFF [108h 0264 4] Output Base : 00001100 [10Ch 0268 4] Output Reference : 00000030 [110h 0272 4] Flags (decoded below) : 00000000 Single Mapping : 0 For the smmuv3-dev test case, IORT has 2 SMMUV3 nodes, 1 RC node and 1 ITS node. RC node features 4 ID mappings. 2 of them target the 2 SMMU nodes while the others targets the ITS. pcie.0 -> {SMMU0} -> {ITS} {RC} pcie.1 -> {SMMU1} -> {ITS} pcie.2 -> {ITS} [all other ids] -> {ITS} ... [030h 0048 1] Type : 00 [031h 0049 2] Length : 0018 [033h 0051 1] Revision : 01 [034h 0052 4] Identifier : 00000000 [038h 0056 4] Mapping Count : 00000000 [03Ch 0060 4] Mapping Offset : 00000000 [040h 0064 4] ItsCount : 00000001 [044h 0068 4] Identifiers : 00000000 [048h 0072 1] Type : 04 [049h 0073 2] Length : 0058 [04Bh 0075 1] Revision : 04 [04Ch 0076 4] Identifier : 00000001 [050h 0080 4] Mapping Count : 00000001 [054h 0084 4] Mapping Offset : 00000044 [058h 0088 8] Base Address : 000000000C000000 [060h 0096 4] Flags (decoded below) : 00000001 COHACC Override : 1 HTTU Override : 0 Proximity Domain Valid : 0 [064h 0100 4] Reserved : 00000000 [068h 0104 8] VATOS Address : 0000000000000000 [070h 0112 4] Model : 00000000 [074h 0116 4] Event GSIV : 00000090 [078h 0120 4] PRI GSIV : 00000091 [07Ch 0124 4] GERR GSIV : 00000093 [080h 0128 4] Sync GSIV : 00000092 [084h 0132 4] Proximity Domain : 00000000 [088h 0136 4] Device ID Mapping Index : 00000000 [08Ch 0140 4] Input base : 00000000 [090h 0144 4] ID Count : 0000FFFF [094h 0148 4] Output Base : 00000000 [098h 0152 4] Output Reference : 00000030 [09Ch 0156 4] Flags (decoded below) : 00000000 Single Mapping : 0 [0A0h 0160 1] Type : 04 [0A1h 0161 2] Length : 0058 [0A3h 0163 1] Revision : 04 [0A4h 0164 4] Identifier : 00000002 [0A8h 0168 4] Mapping Count : 00000001 [0ACh 0172 4] Mapping Offset : 00000044 [0B0h 0176 8] Base Address : 000000000C020000 [0B8h 0184 4] Flags (decoded below) : 00000001 COHACC Override : 1 HTTU Override : 0 Proximity Domain Valid : 0 [0BCh 0188 4] Reserved : 00000000 [0C0h 0192 8] VATOS Address : 0000000000000000 [0C8h 0200 4] Model : 00000000 [0CCh 0204 4] Event GSIV : 00000094 [0D0h 0208 4] PRI GSIV : 00000095 [0D4h 0212 4] GERR GSIV : 00000097 [0D8h 0216 4] Sync GSIV : 00000096 [0DCh 0220 4] Proximity Domain : 00000000 [0E0h 0224 4] Device ID Mapping Index : 00000000 [0E4h 0228 4] Input base : 00000000 [0E8h 0232 4] ID Count : 0000FFFF [0ECh 0236 4] Output Base : 00000000 [0F0h 0240 4] Output Reference : 00000030 [0F4h 0244 4] Flags (decoded below) : 00000000 Single Mapping : 0 [0F8h 0248 1] Type : 02 [0F9h 0249 2] Length : 0074 [0FBh 0251 1] Revision : 03 [0FCh 0252 4] Identifier : 00000003 [100h 0256 4] Mapping Count : 00000004 [104h 0260 4] Mapping Offset : 00000024 [108h 0264 8] Memory Properties : [IORT Memory Access Properties] [108h 0264 4] Cache Coherency : 00000001 [10Ch 0268 1] Hints (decoded below) : 00 Transient : 0 Write Allocate : 0 Read Allocate : 0 Override : 0 [10Dh 0269 2] Reserved : 0000 [10Fh 0271 1] Memory Flags (decoded below) : 03 Coherency : 1 Device Attribute : 1 [110h 0272 4] ATS Attribute : 00000000 [114h 0276 4] PCI Segment Number : 00000000 [118h 0280 1] Memory Size Limit : 40 [119h 0281 2] PASID Capabilities : 0000 [11Bh 0283 1] Reserved : 00 [11Ch 0284 4] Input base : 00000000 [120h 0288 4] ID Count : 000001FF [124h 0292 4] Output Base : 00000000 [128h 0296 4] Output Reference : 00000048 [12Ch 0300 4] Flags (decoded below) : 00000000 Single Mapping : 0 [130h 0304 4] Input base : 00001000 [134h 0308 4] ID Count : 000000FF [138h 0312 4] Output Base : 00001000 [13Ch 0316 4] Output Reference : 000000A0 [140h 0320 4] Flags (decoded below) : 00000000 Single Mapping : 0 [144h 0324 4] Input base : 00000200 [148h 0328 4] ID Count : 00000DFF [14Ch 0332 4] Output Base : 00000200 [150h 0336 4] Output Reference : 00000030 [154h 0340 4] Flags (decoded below) : 00000000 Single Mapping : 0 [158h 0344 4] Input base : 00001100 [15Ch 0348 4] ID Count : 0000EEFF [160h 0352 4] Output Base : 00001100 [164h 0356 4] Output Reference : 00000030 [168h 0360 4] Flags (decoded below) : 00000000 Single Mapping : 0 Note: DSDT changes are not described here as it is not impacted by the way the SMMUv3 is instantiated. Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Reviewed-by: Nicolin Chen Message-id: 20250829082543.7680-12-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev | Bin 0 -> 10230 bytes tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy | Bin 0 -> 10230 bytes tests/data/acpi/aarch64/virt/IORT.smmuv3-dev | Bin 0 -> 364 bytes tests/data/acpi/aarch64/virt/IORT.smmuv3-legacy | Bin 0 -> 276 bytes tests/qtest/bios-tables-test-allowed-diff.h | 4 ---- 5 files changed, 4 deletions(-) diff --git a/tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev b/tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..53d4c07f423886d8c4b57f1da6498eef5a08b556 100644 GIT binary patch literal 10230 zcmdU#&2OAn8O866$FcqTBjb<6vE#%pl=2+@XYf~qK+TFQ8yd7pcpb7rpN z-`w#=cjNBAz8pgJ@9TH+o86`L?{4Qy_EiZX;6A?BfB%!}JH76DZ|Bi`-e@$2dp9>X z#@F)gO)IX1;oe)D8)4_s)%;Goyw+*oY&4q9{M!)v7X>8X^)*PKSnYozz- z>6FGdoW^ypk>01XDUAzG={3^(bS|Z_>@+sLMtYylr!+1)jayzL zy-&}kG%h=hH@!xBpDv^{R-ML{*GTWvb199hPNVHKR?_?Qd`jarr*YeBr1$A!O5+Wu zai_1bS=(AJ>0|r$b|Y_GKZcKOW1fj@l$`TL_M7T6kIkIubvxb4&Lc|o?L<-rAztd` zgjDg^vD7WN>i_gaQh*V!^`(SX@mQ?Y|0#;K3`)G#D+#UQv16_FZ?^ODe-~)2HCFA} z$_*xqYQ?Jc_1oDJkrLm!cRQ1F+3CIT^YFc}6gI*S!q39}@O}Fpu#|AN-MKrx_0#O( z)M#z==qTV73B~=2-EW4eF+84C@uZZ+fE6oYYP>dfbYw_c^z|#Ovc`$x8rNBCSz@K> zv)4$n=oVW`h(rO!DO2r=n zIme=$hQ&Uol|JWqlyf}FIUeV<(&wCra!y1!4WoTdD}BzBQO=W5&XaLYD}7FT%UA4i z?&5ls)3Dp;w9@CCjB-v!IVaC-r(wF!X{FCO9p#*ka!$uNt@Jr(qMS2P zPQ!Yi(@LN7RFv~nl=D=a(@LN7bd>XSl+)e@_?%YyoU>8R*(m31oYP95)82r)*Yix2 z)801toL2grb5YK@DCb<9(@LLnKFT>C<+QgMKBtvF=h-Od*(m4PIH#39=R%ZoA@H&T~=Db8${9ea`by&ht@DdmH0(TIq8xMmZOwoQrWzD~p^3mYm9_9*>8r z{dZ))XqW7^Vka{*TSL7&-MseXLC81mH4knN?*C|VI;-!r|F-h)#f57h-+O=U&#!;_ z!zT~2nl&uj_hI|KvWJ`94|cP~-Glnm{ri<)VL7ig52l~)^K$W69ar{t@yMUXiiaT8 zJbW6h(4%>J`I_m{kVFtYz%IGh})RB&U5NJdc27YAd_ z#tVb7<_m6GLf6UHXjA;sBOb!5JY9)-xJYh^z0Aq3h7*i`@Oy&t= zngSS;1HhPC31c!(7}FHMm>dAc)JhnWdBT{c0LJ71Fs4?*n9LK#GzBmw2Y@lP62@en zFs3PhF*yK?sg*D$^Mo-?0gTB3U`(xqF_|ZfX$oLW4gh0nC5*{DVN6p1V{!l(Q!8Oi z<_Tk(0vMA6z?fPIV=_+|(-gp%9011DN*I%Q!kDH2#^eAnrdGn3%oD~m1u!NDfHAca z#$=u_hAGd$SREgheJ)|__@lz;+{hC8W24V@GG-qp79YSwQqOfVW*;pkq@M3&tns2? z?AFume6xR^`M+SS+qz-Tot=-_Ek1EAfNU{a*baHvv=gzV(6E1>?TpM`n`11qCs$a8 zPhR^6W&>N6&tVz+j-uNu}o8dWpV&4Q!BAd=80vR0xXjQV3}HpWin4J(-dHt z901GIN-UFkVwt7@%j5u9rdDE^%oEEr1z08rz%sQG%VeHdrYXQOIRKWal~^Y8#4=3* zmdOFIOs&K+nJ1QM3b0HLfMseWmdQM^OjCemasVt-E3r)GiDjAsERzFZnOccuGEXei z6kwSg0L#=$ER%U+nWg~C<8aq?YFjH#wvCf zU-hG-|KJZ6?Ca=)U8#)OZ?)pfKB}^>BfDA&PrmNf@bW`k!xt;v;PKDdxm>%}X>}dk z`#Oom6Y+RlfgDDzggwY4;JuGf0|?#LbrJF(pVU^l~&(MQ|&I_ufP zokw%HW`%ldv~UA{n|X7vWha*72bb++au0`+y9e`6)2Ek*y9Yymyq{GMclWXn zAMb`>4=n7zmksSr4OYW=*xmc};~$6c?%un_#hpi^-A2O#uWfYftaG8;SS<>RgTv40 T>d9`i-)dK@cGVtG`#|_F_?bX? literal 0 HcmV?d00001 diff --git a/tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy b/tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..53d4c07f423886d8c4b57f1da6498eef5a08b556 100644 GIT binary patch literal 10230 zcmdU#&2OAn8O866$FcqTBjb<6vE#%pl=2+@XYf~qK+TFQ8yd7pcpb7rpN z-`w#=cjNBAz8pgJ@9TH+o86`L?{4Qy_EiZX;6A?BfB%!}JH76DZ|Bi`-e@$2dp9>X z#@F)gO)IX1;oe)D8)4_s)%;Goyw+*oY&4q9{M!)v7X>8X^)*PKSnYozz- z>6FGdoW^ypk>01XDUAzG={3^(bS|Z_>@+sLMtYylr!+1)jayzL zy-&}kG%h=hH@!xBpDv^{R-ML{*GTWvb199hPNVHKR?_?Qd`jarr*YeBr1$A!O5+Wu zai_1bS=(AJ>0|r$b|Y_GKZcKOW1fj@l$`TL_M7T6kIkIubvxb4&Lc|o?L<-rAztd` zgjDg^vD7WN>i_gaQh*V!^`(SX@mQ?Y|0#;K3`)G#D+#UQv16_FZ?^ODe-~)2HCFA} z$_*xqYQ?Jc_1oDJkrLm!cRQ1F+3CIT^YFc}6gI*S!q39}@O}Fpu#|AN-MKrx_0#O( z)M#z==qTV73B~=2-EW4eF+84C@uZZ+fE6oYYP>dfbYw_c^z|#Ovc`$x8rNBCSz@K> zv)4$n=oVW`h(rO!DO2r=n zIme=$hQ&Uol|JWqlyf}FIUeV<(&wCra!y1!4WoTdD}BzBQO=W5&XaLYD}7FT%UA4i z?&5ls)3Dp;w9@CCjB-v!IVaC-r(wF!X{FCO9p#*ka!$uNt@Jr(qMS2P zPQ!Yi(@LN7RFv~nl=D=a(@LN7bd>XSl+)e@_?%YyoU>8R*(m31oYP95)82r)*Yix2 z)801toL2grb5YK@DCb<9(@LLnKFT>C<+QgMKBtvF=h-Od*(m4PIH#39=R%ZoA@H&T~=Db8${9ea`by&ht@DdmH0(TIq8xMmZOwoQrWzD~p^3mYm9_9*>8r z{dZ))XqW7^Vka{*TSL7&-MseXLC81mH4knN?*C|VI;-!r|F-h)#f57h-+O=U&#!;_ z!zT~2nl&uj_hI|KvWJ`94|cP~-Glnm{ri<)VL7ig52l~)^K$W69ar{t@yMUXiiaT8 zJbW6h(4%>J`I_m{kVFtYz%IGh})RB&U5NJdc27YAd_ z#tVb7<_m6GLf6UHXjA;sBOb!5JY9)-xJYh^z0Aq3h7*i`@Oy&t= zngSS;1HhPC31c!(7}FHMm>dAc)JhnWdBT{c0LJ71Fs4?*n9LK#GzBmw2Y@lP62@en zFs3PhF*yK?sg*D$^Mo-?0gTB3U`(xqF_|ZfX$oLW4gh0nC5*{DVN6p1V{!l(Q!8Oi z<_Tk(0vMA6z?fPIV=_+|(-gp%9011DN*I%Q!kDH2#^eAnrdGn3%oD~m1u!NDfHAca z#$=u_hAGd$SREgheJ)|__@lz;+{hC8W24V@GG-qp79YSwQqOfVW*;pkq@M3&tns2? z?AFume6xR^`M+SS+qz-Tot=-_Ek1EAfNU{a*baHvv=gzV(6E1>?TpM`n`11qCs$a8 zPhR^6W&>N6&tVz+j-uNu}o8dWpV&4Q!BAd=80vR0xXjQV3}HpWin4J(-dHt z901GIN-UFkVwt7@%j5u9rdDE^%oEEr1z08rz%sQG%VeHdrYXQOIRKWal~^Y8#4=3* zmdOFIOs&K+nJ1QM3b0HLfMseWmdQM^OjCemasVt-E3r)GiDjAsERzFZnOccuGEXei z6kwSg0L#=$ER%U+nWg~C<8aq?YFjH#wvCf zU-hG-|KJZ6?Ca=)U8#)OZ?)pfKB}^>BfDA&PrmNf@bW`k!xt;v;PKDdxm>%}X>}dk z`#Oom6Y+RlfgDDzggwY4;JuGf0|?#LbrJF(pVU^l~&(MQ|&I_ufP zokw%HW`%ldv~UA{n|X7vWha*72bb++au0`+y9e`6)2Ek*y9Yymyq{GMclWXn zAMb`>4=n7zmksSr4OYW=*xmc};~$6c?%un_#hpi^-A2O#uWfYftaG8;SS<>RgTv40 T>d9`i-)dK@cGVtG`#|_F_?bX? literal 0 HcmV?d00001 diff --git a/tests/data/acpi/aarch64/virt/IORT.smmuv3-dev b/tests/data/acpi/aarch64/virt/IORT.smmuv3-dev index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..67be268f62afbf2d9459540984da5e9340afdaaa 100644 GIT binary patch literal 364 zcmebD4+_a)WME)E<>c?|5v<@85#X!<1VAAM5F13Z0I>lOgMkDCNC*yK9F_^a7X|(1HJXfgB(Wb2oz^ MQ0yI03`oPo01?F*0RR91 literal 0 HcmV?d00001 diff --git a/tests/data/acpi/aarch64/virt/IORT.smmuv3-legacy b/tests/data/acpi/aarch64/virt/IORT.smmuv3-legacy index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..41981a449fc306b80cccd87ddec3c593a8d72c07 100644 GIT binary patch literal 276 zcmX|*K@NgI3`M`puy8?wi3^u3IDkhWzycE!jI!Vis5{Ti6^7s1@{>QmeVugXHa@5G z0SNbY?1op>&X2C5h#<9Ops%#*0ztdHi8G?q;$EluQNrhn>{ys@`b&R|d8G8O{Jrdl mkP$_?rfr{mN!3^;8w}Q?1auX1XIzvDUSRruoXA!(rn3!nx)K2Z literal 0 HcmV?d00001 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 2e3e3ccdcec2e..dfb8523c8bf45 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,5 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy", -"tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev", -"tests/data/acpi/aarch64/virt/IORT.smmuv3-legacy", -"tests/data/acpi/aarch64/virt/IORT.smmuv3-dev", From aaf042299acf83919862c7d7dd5fc36acf4e0671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 11 Sep 2025 13:14:15 +0100 Subject: [PATCH 0695/1794] hw/usb/network: Remove hardcoded 0x40 prefix in STRING_ETHADDR response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit USB NICs have a "40:" prefix hardcoded for all MAC addresses when we return the guest the MAC address if it queries the STRING_ETHADDR USB string property. This doesn't match what we use for the OID_802_3_PERMANENT_ADDRESS or OID_802_3_CURRENT_ADDRESS OIDs for NDIS, or the MAC address we actually use in the QEMU networking code to send/receive packets for this device, or the NIC info string we print for users. In all those other places we directly use s->conf.macaddr.a, which is the full thing the user asks for. This overrides user-provided configuration and leads to an inconsistent experience. I couldn't find any documented reason (comment or git commits) for this behavior. It seems like everyone is just expecting the MAC address to be fully passed through to the guest, but it isn't. This may have been a debugging hack that accidentally made it through to the accepted patch: it has been in the code since it was originally added back in 2008. This is also particularly problematic as the "40:" prefix isn't a reserved prefix for MAC addresses (IEEE OUI). There are a number of valid allocations out there which use this prefix, meaning that QEMU may be causing MAC address conflicts. Cc: qemu-stable@nongnu.org Fixes: 6c9f886ceae5b ("Add CDC-Ethernet usb NIC (original patch from Thomas Sailer)" Signed-off-by: Stéphane Graber Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2951 Reviewed-by: Daniel P. Berrangé [PMM: beef up commit message based on mailing list discussion] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/usb/dev-network.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 81cc09dcac90f..1df24541814c2 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -1383,7 +1383,7 @@ static void usb_net_realize(USBDevice *dev, Error **errp) qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); snprintf(s->usbstring_mac, sizeof(s->usbstring_mac), "%02x%02x%02x%02x%02x%02x", - 0x40, + s->conf.macaddr.a[0], s->conf.macaddr.a[1], s->conf.macaddr.a[2], s->conf.macaddr.a[3], From aa3c761c7095476acde9b7140cc1dfff2ee0e170 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 15 Sep 2025 11:48:03 -0700 Subject: [PATCH 0696/1794] tests/functional/x86_64: Accept a few locked pages in test_memlock.py Startup of libgcrypt locks a small pool of pages -- by default 16k. Testing for zero locked pages is isn't correct, while testing for 32k is a decent compromise. Reviewed-by: Thomas Huth Signed-off-by: Richard Henderson --- tests/functional/x86_64/test_memlock.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/x86_64/test_memlock.py b/tests/functional/x86_64/test_memlock.py index 2b515ff979ff3..81bce80b0c4ec 100755 --- a/tests/functional/x86_64/test_memlock.py +++ b/tests/functional/x86_64/test_memlock.py @@ -37,7 +37,8 @@ def test_memlock_off(self): status = self.get_process_status_values(self.vm.get_pid()) - self.assertTrue(status['VmLck'] == 0) + # libgcrypt may mlock a few pages + self.assertTrue(status['VmLck'] < 32) def test_memlock_on(self): self.common_vm_setup_with_memlock('on') From a11d1847d5ef8a7db58e6d4e44f36fec708f0981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 8 Sep 2025 15:19:11 +0100 Subject: [PATCH 0697/1794] .gitmodules: move u-boot mirrors to qemu-project-mirrors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To continue our GitLab Open Source Program license we need to pass an automated license check for all repos under qemu-project. While U-Boot is clearly GPLv2 rather than fight with the automated validation script just move the mirror across to a separate project. Signed-off-by: Alex Bennée Suggested-by: Daniel P. Berrangé Cc: qemu-stable@nongnu.org Reviewed-by: Daniel P. Berrangé Signed-off-by: Richard Henderson Message-ID: <20250908141911.2546063-1-alex.bennee@linaro.org> Signed-off-by: Richard Henderson --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 73cae4cd4da00..e27dfe8c2c11d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -15,7 +15,7 @@ url = https://gitlab.com/qemu-project/qemu-palcode.git [submodule "roms/u-boot"] path = roms/u-boot - url = https://gitlab.com/qemu-project/u-boot.git + url = https://gitlab.com/qemu-project-mirrors/u-boot.git [submodule "roms/skiboot"] path = roms/skiboot url = https://gitlab.com/qemu-project/skiboot.git @@ -27,7 +27,7 @@ url = https://gitlab.com/qemu-project/seabios-hppa.git [submodule "roms/u-boot-sam460ex"] path = roms/u-boot-sam460ex - url = https://gitlab.com/qemu-project/u-boot-sam460ex.git + url = https://gitlab.com/qemu-project-mirrors/u-boot-sam460ex.git [submodule "roms/edk2"] path = roms/edk2 url = https://gitlab.com/qemu-project/edk2.git From 4b948222c0801ddd428ca2e4260181ba35b219a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 16 Sep 2025 09:16:31 +0100 Subject: [PATCH 0698/1794] checkpatch: cull trailing '*/' in SPDX check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes SPDX expressions appear inside C comments, and this confuses checkpatch.pl. Drop the closing C comment characters to avoid this. Reviewed-by: Stefan Hajnoczi Signed-off-by: Daniel P. Berrangé Message-id: 20250916081638.764020-2-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- scripts/checkpatch.pl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 833f20f555531..91616c974f2c3 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1368,6 +1368,9 @@ sub checkspdx { $expr =~ s/^\s*//g; $expr =~ s/\s*$//g; + # Cull C comment end + $expr =~ s/\*\/.*//; + my @bits = split / +/, $expr; my $prefer = "GPL-2.0-or-later"; From acf7882d194562983fd7a85df5d4b99d15c81766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 16 Sep 2025 09:16:32 +0100 Subject: [PATCH 0699/1794] tracetool: eliminate trailing whitespace in C format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Signed-off-by: Daniel P. Berrangé Message-id: 20250916081638.764020-3-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/format/c.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tracetool/format/c.py b/scripts/tracetool/format/c.py index 69edf0d588ee8..7aa51cd41a63d 100644 --- a/scripts/tracetool/format/c.py +++ b/scripts/tracetool/format/c.py @@ -36,7 +36,7 @@ def generate(events, backend, group): ' .id = 0,', ' .name = \"%(name)s\",', ' .sstate = %(sstate)s,', - ' .dstate = &%(dstate)s ', + ' .dstate = &%(dstate)s', '};', event = e.api(e.QEMU_EVENT), name = e.name, From 69e22a5e20518fbb68b892b2189d525cf9d90a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 16 Sep 2025 09:16:33 +0100 Subject: [PATCH 0700/1794] tracetool: avoid space after "*" in arg types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU code style is to have no whitespace between "*" and the arg name. Since generated trace code will soon be added to git, make it comply with code style. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Signed-off-by: Daniel P. Berrangé Message-id: 20250916081638.764020-4-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 2ae2e562d6444..0f33758870627 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -170,10 +170,16 @@ def __len__(self): def __str__(self): """String suitable for declaring function arguments.""" + def onearg(t, n): + if t[-1] == '*': + return "".join([t, n]) + else: + return " ".join([t, n]) + if len(self._args) == 0: return "void" else: - return ", ".join([ " ".join([t, n]) for t,n in self._args ]) + return ", ".join([ onearg(t, n) for t,n in self._args ]) def __repr__(self): """Evaluable string representation for this object.""" From 52d1ec2929e5f43fe650bdbc85653e05f0aa9ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 16 Sep 2025 09:16:34 +0100 Subject: [PATCH 0701/1794] tracetool: include SPDX-License-Identifier in generated files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While these files are auto-generated, a later commit will add reference output to git, so having SPDX-License-Identifier is desirable. Reviewed-by: Philippe Mathieu-Daudé Acked-by: Thomas Huth Reviewed-by: Stefan Hajnoczi Signed-off-by: Daniel P. Berrangé Message-id: 20250916081638.764020-5-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/format/c.py | 1 + scripts/tracetool/format/d.py | 3 ++- scripts/tracetool/format/h.py | 1 + scripts/tracetool/format/log_stap.py | 1 + scripts/tracetool/format/simpletrace_stap.py | 1 + scripts/tracetool/format/stap.py | 1 + scripts/tracetool/format/ust_events_c.py | 1 + scripts/tracetool/format/ust_events_h.py | 1 + 8 files changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/tracetool/format/c.py b/scripts/tracetool/format/c.py index 7aa51cd41a63d..e473fb6c6eb05 100644 --- a/scripts/tracetool/format/c.py +++ b/scripts/tracetool/format/c.py @@ -22,6 +22,7 @@ def generate(events, backend, group): header = "trace-" + group + ".h" out('/* This file is autogenerated by tracetool, do not edit. */', + '/* SPDX-License-Identifier: GPL-2.0-or-later */', '', '#include "qemu/osdep.h"', '#include "qemu/module.h"', diff --git a/scripts/tracetool/format/d.py b/scripts/tracetool/format/d.py index ebfb714200266..a5e096e214b81 100644 --- a/scripts/tracetool/format/d.py +++ b/scripts/tracetool/format/d.py @@ -39,7 +39,8 @@ def generate(events, backend, group): if not events and platform != "darwin": return - out('/* This file is autogenerated by tracetool, do not edit. */' + out('/* This file is autogenerated by tracetool, do not edit. */', + '/* SPDX-License-Identifier: GPL-2.0-or-later */', '', 'provider qemu {') diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py index ea126b07ea57b..a00ae475f7606 100644 --- a/scripts/tracetool/format/h.py +++ b/scripts/tracetool/format/h.py @@ -19,6 +19,7 @@ def generate(events, backend, group): header = "trace/control.h" out('/* This file is autogenerated by tracetool, do not edit. */', + '/* SPDX-License-Identifier: GPL-2.0-or-later */', '', '#ifndef TRACE_%s_GENERATED_TRACERS_H' % group.upper(), '#define TRACE_%s_GENERATED_TRACERS_H' % group.upper(), diff --git a/scripts/tracetool/format/log_stap.py b/scripts/tracetool/format/log_stap.py index b49afababd676..710d62bffe422 100644 --- a/scripts/tracetool/format/log_stap.py +++ b/scripts/tracetool/format/log_stap.py @@ -88,6 +88,7 @@ def c_fmt_to_stap(fmt): def generate(events, backend, group): out('/* This file is autogenerated by tracetool, do not edit. */', + '/* SPDX-License-Identifier: GPL-2.0-or-later */', '') for event_id, e in enumerate(events): diff --git a/scripts/tracetool/format/simpletrace_stap.py b/scripts/tracetool/format/simpletrace_stap.py index 4f4633b4e689c..72971133bf016 100644 --- a/scripts/tracetool/format/simpletrace_stap.py +++ b/scripts/tracetool/format/simpletrace_stap.py @@ -22,6 +22,7 @@ def global_var_name(name): def generate(events, backend, group): out('/* This file is autogenerated by tracetool, do not edit. */', + '/* SPDX-License-Identifier: GPL-2.0-or-later */', '') for event_id, e in enumerate(events): diff --git a/scripts/tracetool/format/stap.py b/scripts/tracetool/format/stap.py index a218b0445c9b1..4d77fbc11a9d9 100644 --- a/scripts/tracetool/format/stap.py +++ b/scripts/tracetool/format/stap.py @@ -38,6 +38,7 @@ def generate(events, backend, group): if "disable" not in e.properties] out('/* This file is autogenerated by tracetool, do not edit. */', + '/* SPDX-License-Identifier: GPL-2.0-or-later */', '') for e in events: diff --git a/scripts/tracetool/format/ust_events_c.py b/scripts/tracetool/format/ust_events_c.py index deced9533ddfa..569754a30482e 100644 --- a/scripts/tracetool/format/ust_events_c.py +++ b/scripts/tracetool/format/ust_events_c.py @@ -20,6 +20,7 @@ def generate(events, backend, group): if "disabled" not in e.properties] out('/* This file is autogenerated by tracetool, do not edit. */', + '/* SPDX-License-Identifier: GPL-2.0-or-later */', '', '#include "qemu/osdep.h"', '', diff --git a/scripts/tracetool/format/ust_events_h.py b/scripts/tracetool/format/ust_events_h.py index b99fe6896bafa..2a31fefeca1e2 100644 --- a/scripts/tracetool/format/ust_events_h.py +++ b/scripts/tracetool/format/ust_events_h.py @@ -25,6 +25,7 @@ def generate(events, backend, group): include = "trace-ust.h" out('/* This file is autogenerated by tracetool, do not edit. */', + '/* SPDX-License-Identifier: GPL-2.0-or-later */', '', '#undef TRACEPOINT_PROVIDER', '#define TRACEPOINT_PROVIDER qemu', From da949d495ddd4d36f2c9750eb4d70f4135018199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 16 Sep 2025 09:16:35 +0100 Subject: [PATCH 0702/1794] tracetool: add test suite for tracetool with reference output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When reviewing tracetool patches it is often very unclear what the expected output will be for the generated backends. Compounding this is that a default build will only enable the 'log' trace backend, so developers won't see generated code for other backends without making a special effort. Some backends are also platform specific, so can't be enabled in QEMU builds, even though tracetool could generate the code. To address this, introduce a test suite for tracetool which is conceptually similar to the qapi-schema test. It is a simple python program that runs tracetool and compares the actual output to historical reference output kept in git. The test directly emits TAP format logs for ease of integration with meson. This can be run with make check-tracetool to make it easier for developers changing generated output, the sample expected content can be auto-recreated QEMU_TEST_REGENERATE=1 make check-tracetool and the changes reviewed and added to the commit. This will also assist reviewers interpreting the change. Developers are reminded of this in the test output on failure: $ make check-tracetool 1/6 qemu:tracetool / dtrace OK 0.14s 2/6 qemu:tracetool / ftrace FAIL 0.06s exit status 1 ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― 1..2 ok 1 - ftrace.c # not ok 1 - ftrace.h (set QEMU_TEST_REGENERATE=1 to recreate reference output if tracetool generator was intentionally changed) ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― 3/6 qemu:tracetool / log OK 0.06s 4/6 qemu:tracetool / simple OK 0.06s 5/6 qemu:tracetool / syslog OK 0.06s 6/6 qemu:tracetool / ust OK 0.11s Summary of Failures: 2/6 qemu:tracetool / ftrace FAIL 0.06s exit status 1 Signed-off-by: Daniel P. Berrangé Message-id: 20250916081638.764020-6-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- MAINTAINERS | 1 + docs/devel/testing/main.rst | 28 +++++++ tests/Makefile.include | 1 + tests/meson.build | 1 + tests/tracetool/dtrace.c | 32 ++++++++ tests/tracetool/dtrace.d | 10 +++ tests/tracetool/dtrace.h | 59 +++++++++++++ tests/tracetool/dtrace.log-stap | 15 ++++ tests/tracetool/dtrace.simpletrace-stap | 16 ++++ tests/tracetool/dtrace.stap | 14 ++++ tests/tracetool/ftrace.c | 32 ++++++++ tests/tracetool/ftrace.h | 73 ++++++++++++++++ tests/tracetool/log.c | 32 ++++++++ tests/tracetool/log.h | 57 +++++++++++++ tests/tracetool/meson.build | 28 +++++++ tests/tracetool/simple.c | 61 ++++++++++++++ tests/tracetool/simple.h | 54 ++++++++++++ tests/tracetool/syslog.c | 32 ++++++++ tests/tracetool/syslog.h | 57 +++++++++++++ tests/tracetool/trace-events | 5 ++ tests/tracetool/tracetool-test.py | 105 ++++++++++++++++++++++++ tests/tracetool/ust.c | 32 ++++++++ tests/tracetool/ust.h | 55 +++++++++++++ tests/tracetool/ust.ust-events-c | 14 ++++ tests/tracetool/ust.ust-events-h | 56 +++++++++++++ 25 files changed, 870 insertions(+) create mode 100644 tests/tracetool/dtrace.c create mode 100644 tests/tracetool/dtrace.d create mode 100644 tests/tracetool/dtrace.h create mode 100644 tests/tracetool/dtrace.log-stap create mode 100644 tests/tracetool/dtrace.simpletrace-stap create mode 100644 tests/tracetool/dtrace.stap create mode 100644 tests/tracetool/ftrace.c create mode 100644 tests/tracetool/ftrace.h create mode 100644 tests/tracetool/log.c create mode 100644 tests/tracetool/log.h create mode 100644 tests/tracetool/meson.build create mode 100644 tests/tracetool/simple.c create mode 100644 tests/tracetool/simple.h create mode 100644 tests/tracetool/syslog.c create mode 100644 tests/tracetool/syslog.h create mode 100644 tests/tracetool/trace-events create mode 100755 tests/tracetool/tracetool-test.py create mode 100644 tests/tracetool/ust.c create mode 100644 tests/tracetool/ust.h create mode 100644 tests/tracetool/ust.ust-events-c create mode 100644 tests/tracetool/ust.ust-events-h diff --git a/MAINTAINERS b/MAINTAINERS index fb045388b92c2..f8cd513d8b569 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3562,6 +3562,7 @@ F: scripts/tracetool/ F: scripts/qemu-trace-stap* F: docs/tools/qemu-trace-stap.rst F: docs/devel/tracing.rst +F: tests/tracetool/ T: git https://github.com/stefanha/qemu.git tracing Simpletrace diff --git a/docs/devel/testing/main.rst b/docs/devel/testing/main.rst index 2b5cb0c148048..11f05c0006553 100644 --- a/docs/devel/testing/main.rst +++ b/docs/devel/testing/main.rst @@ -178,6 +178,34 @@ parser (either fixing a bug or extending/modifying the syntax). To do this: ``qapi-schema += foo.json`` +.. _tracetool-tests: + +Tracetool tests +~~~~~~~~~~~~~~~ + +The tracetool tests validate the generated source files used for defining +probes for various tracing backends and source formats. The test operates +by running the tracetool program against a sample trace-events file, and +comparing the generated output against known good reference output. The +tests can be run with: + +.. code:: + + make check-tracetool + +The reference output is stored in files under tests/tracetool, and when +the tracetool backend/format output is intentionally changed, the reference +files need to be updated. This can be automated by setting the +QEMU_TEST_REGENERATE=1 environment variable: + +.. code:: + + QEMU_TEST_REGENERATE=1 make check-tracetool + +The resulting changes must be reviewed by the author to ensure they match +the intended results, before adding the updated reference output to the +same commit that alters the generator code. + check-block ~~~~~~~~~~~ diff --git a/tests/Makefile.include b/tests/Makefile.include index 23fb722d4260f..3538c0c7407b8 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -13,6 +13,7 @@ check-help: @echo " $(MAKE) check-functional-TARGET Run functional tests for a given target" @echo " $(MAKE) check-unit Run qobject tests" @echo " $(MAKE) check-qapi-schema Run QAPI schema tests" + @echo " $(MAKE) check-tracetool Run tracetool generator tests" @echo " $(MAKE) check-block Run block tests" ifneq ($(filter $(all-check-targets), check-softfloat),) @echo " $(MAKE) check-tcg Run TCG tests" diff --git a/tests/meson.build b/tests/meson.build index c59619220f71f..cbe79162411b0 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -88,3 +88,4 @@ subdir('qapi-schema') subdir('qtest') subdir('migration-stress') subdir('functional') +subdir('tracetool') diff --git a/tests/tracetool/dtrace.c b/tests/tracetool/dtrace.c new file mode 100644 index 0000000000000..9f862fa14d299 --- /dev/null +++ b/tests/tracetool/dtrace.c @@ -0,0 +1,32 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "trace-testsuite.h" + +uint16_t _TRACE_TEST_BLAH_DSTATE; +uint16_t _TRACE_TEST_WIBBLE_DSTATE; +TraceEvent _TRACE_TEST_BLAH_EVENT = { + .id = 0, + .name = "test_blah", + .sstate = TRACE_TEST_BLAH_ENABLED, + .dstate = &_TRACE_TEST_BLAH_DSTATE +}; +TraceEvent _TRACE_TEST_WIBBLE_EVENT = { + .id = 0, + .name = "test_wibble", + .sstate = TRACE_TEST_WIBBLE_ENABLED, + .dstate = &_TRACE_TEST_WIBBLE_DSTATE +}; +TraceEvent *testsuite_trace_events[] = { + &_TRACE_TEST_BLAH_EVENT, + &_TRACE_TEST_WIBBLE_EVENT, + NULL, +}; + +static void trace_testsuite_register_events(void) +{ + trace_event_register_group(testsuite_trace_events); +} +trace_init(trace_testsuite_register_events) diff --git a/tests/tracetool/dtrace.d b/tests/tracetool/dtrace.d new file mode 100644 index 0000000000000..5cc06f9f4f510 --- /dev/null +++ b/tests/tracetool/dtrace.d @@ -0,0 +1,10 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +provider qemu { + +probe test_blah(void * context,const char * filename); + +probe test_wibble(void * context,int value); + +}; diff --git a/tests/tracetool/dtrace.h b/tests/tracetool/dtrace.h new file mode 100644 index 0000000000000..c2e5110672530 --- /dev/null +++ b/tests/tracetool/dtrace.h @@ -0,0 +1,59 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef TRACE_TESTSUITE_GENERATED_TRACERS_H +#define TRACE_TESTSUITE_GENERATED_TRACERS_H + +#include "trace/control.h" + +extern TraceEvent _TRACE_TEST_BLAH_EVENT; +extern TraceEvent _TRACE_TEST_WIBBLE_EVENT; +extern uint16_t _TRACE_TEST_BLAH_DSTATE; +extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; +#define TRACE_TEST_BLAH_ENABLED 1 +#define TRACE_TEST_WIBBLE_ENABLED 1 +#ifndef SDT_USE_VARIADIC +#define SDT_USE_VARIADIC 1 +#endif +#include "trace-dtrace-testsuite.h" + +#undef SDT_USE_VARIADIC +#ifndef QEMU_TEST_BLAH_ENABLED +#define QEMU_TEST_BLAH_ENABLED() true +#endif +#ifndef QEMU_TEST_WIBBLE_ENABLED +#define QEMU_TEST_WIBBLE_ENABLED() true +#endif + +#define TRACE_TEST_BLAH_BACKEND_DSTATE() ( \ + QEMU_TEST_BLAH_ENABLED() || \ + false) + +static inline void _nocheck__trace_test_blah(void *context, const char *filename) +{ + QEMU_TEST_BLAH(context, filename); +} + +static inline void trace_test_blah(void *context, const char *filename) +{ + if (true) { + _nocheck__trace_test_blah(context, filename); + } +} + +#define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ + QEMU_TEST_WIBBLE_ENABLED() || \ + false) + +static inline void _nocheck__trace_test_wibble(void *context, int value) +{ + QEMU_TEST_WIBBLE(context, value); +} + +static inline void trace_test_wibble(void *context, int value) +{ + if (true) { + _nocheck__trace_test_wibble(context, value); + } +} +#endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/dtrace.log-stap b/tests/tracetool/dtrace.log-stap new file mode 100644 index 0000000000000..092986e0b61e5 --- /dev/null +++ b/tests/tracetool/dtrace.log-stap @@ -0,0 +1,15 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +probe qemu.log.test_blah = qemu.test_blah ? +{ + try { + argfilename_str = filename ? user_string_n(filename, 512) : "" + } catch {} + printf("%d@%d test_blah Blah context=%p filename=%s\n", pid(), gettimeofday_ns(), context, argfilename_str) +} +probe qemu.log.test_wibble = qemu.test_wibble ? +{ + printf("%d@%d test_wibble Wibble context=%p value=%d\n", pid(), gettimeofday_ns(), context, value) +} + diff --git a/tests/tracetool/dtrace.simpletrace-stap b/tests/tracetool/dtrace.simpletrace-stap new file mode 100644 index 0000000000000..d064e3e286a8a --- /dev/null +++ b/tests/tracetool/dtrace.simpletrace-stap @@ -0,0 +1,16 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +probe qemu.simpletrace.test_blah = qemu.test_blah ? +{ + try { + argfilename_str = filename ? user_string_n(filename, 512) : "" + } catch {} + argfilename_len = strlen(argfilename_str) + printf("%8b%8b%8b%4b%4b%8b%4b%.*s", 1, 0, gettimeofday_ns(), 24 + 8 + 4 + argfilename_len, pid(), context, argfilename_len, argfilename_len, argfilename_str) +} +probe qemu.simpletrace.test_wibble = qemu.test_wibble ? +{ + printf("%8b%8b%8b%4b%4b%8b%8b", 1, 1, gettimeofday_ns(), 24 + 8 + 8, pid(), context, value) +} + diff --git a/tests/tracetool/dtrace.stap b/tests/tracetool/dtrace.stap new file mode 100644 index 0000000000000..9c5d8a527ce4c --- /dev/null +++ b/tests/tracetool/dtrace.stap @@ -0,0 +1,14 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +probe qemu.test_blah = process("qemu").mark("test_blah") +{ + context = $arg1; + filename = $arg2; +} +probe qemu.test_wibble = process("qemu").mark("test_wibble") +{ + context = $arg1; + value = $arg2; +} + diff --git a/tests/tracetool/ftrace.c b/tests/tracetool/ftrace.c new file mode 100644 index 0000000000000..9f862fa14d299 --- /dev/null +++ b/tests/tracetool/ftrace.c @@ -0,0 +1,32 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "trace-testsuite.h" + +uint16_t _TRACE_TEST_BLAH_DSTATE; +uint16_t _TRACE_TEST_WIBBLE_DSTATE; +TraceEvent _TRACE_TEST_BLAH_EVENT = { + .id = 0, + .name = "test_blah", + .sstate = TRACE_TEST_BLAH_ENABLED, + .dstate = &_TRACE_TEST_BLAH_DSTATE +}; +TraceEvent _TRACE_TEST_WIBBLE_EVENT = { + .id = 0, + .name = "test_wibble", + .sstate = TRACE_TEST_WIBBLE_ENABLED, + .dstate = &_TRACE_TEST_WIBBLE_DSTATE +}; +TraceEvent *testsuite_trace_events[] = { + &_TRACE_TEST_BLAH_EVENT, + &_TRACE_TEST_WIBBLE_EVENT, + NULL, +}; + +static void trace_testsuite_register_events(void) +{ + trace_event_register_group(testsuite_trace_events); +} +trace_init(trace_testsuite_register_events) diff --git a/tests/tracetool/ftrace.h b/tests/tracetool/ftrace.h new file mode 100644 index 0000000000000..f1ff3b0a62178 --- /dev/null +++ b/tests/tracetool/ftrace.h @@ -0,0 +1,73 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef TRACE_TESTSUITE_GENERATED_TRACERS_H +#define TRACE_TESTSUITE_GENERATED_TRACERS_H + +#include "trace/control.h" + +extern TraceEvent _TRACE_TEST_BLAH_EVENT; +extern TraceEvent _TRACE_TEST_WIBBLE_EVENT; +extern uint16_t _TRACE_TEST_BLAH_DSTATE; +extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; +#define TRACE_TEST_BLAH_ENABLED 1 +#define TRACE_TEST_WIBBLE_ENABLED 1 +#include "trace/ftrace.h" + + +#define TRACE_TEST_BLAH_BACKEND_DSTATE() ( \ + trace_event_get_state_dynamic_by_id(TRACE_TEST_BLAH) || \ + false) + +static inline void _nocheck__trace_test_blah(void *context, const char *filename) +{ + { + char ftrace_buf[MAX_TRACE_STRLEN]; + int unused __attribute__ ((unused)); + int trlen; + if (trace_event_get_state(TRACE_TEST_BLAH)) { +#line 4 "trace-events" + trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN, + "test_blah " "Blah context=%p filename=%s" "\n" , context, filename); +#line 33 "ftrace.h" + trlen = MIN(trlen, MAX_TRACE_STRLEN - 1); + unused = write(trace_marker_fd, ftrace_buf, trlen); + } + } +} + +static inline void trace_test_blah(void *context, const char *filename) +{ + if (true) { + _nocheck__trace_test_blah(context, filename); + } +} + +#define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ + trace_event_get_state_dynamic_by_id(TRACE_TEST_WIBBLE) || \ + false) + +static inline void _nocheck__trace_test_wibble(void *context, int value) +{ + { + char ftrace_buf[MAX_TRACE_STRLEN]; + int unused __attribute__ ((unused)); + int trlen; + if (trace_event_get_state(TRACE_TEST_WIBBLE)) { +#line 5 "trace-events" + trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN, + "test_wibble " "Wibble context=%p value=%d" "\n" , context, value); +#line 61 "ftrace.h" + trlen = MIN(trlen, MAX_TRACE_STRLEN - 1); + unused = write(trace_marker_fd, ftrace_buf, trlen); + } + } +} + +static inline void trace_test_wibble(void *context, int value) +{ + if (true) { + _nocheck__trace_test_wibble(context, value); + } +} +#endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/log.c b/tests/tracetool/log.c new file mode 100644 index 0000000000000..9f862fa14d299 --- /dev/null +++ b/tests/tracetool/log.c @@ -0,0 +1,32 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "trace-testsuite.h" + +uint16_t _TRACE_TEST_BLAH_DSTATE; +uint16_t _TRACE_TEST_WIBBLE_DSTATE; +TraceEvent _TRACE_TEST_BLAH_EVENT = { + .id = 0, + .name = "test_blah", + .sstate = TRACE_TEST_BLAH_ENABLED, + .dstate = &_TRACE_TEST_BLAH_DSTATE +}; +TraceEvent _TRACE_TEST_WIBBLE_EVENT = { + .id = 0, + .name = "test_wibble", + .sstate = TRACE_TEST_WIBBLE_ENABLED, + .dstate = &_TRACE_TEST_WIBBLE_DSTATE +}; +TraceEvent *testsuite_trace_events[] = { + &_TRACE_TEST_BLAH_EVENT, + &_TRACE_TEST_WIBBLE_EVENT, + NULL, +}; + +static void trace_testsuite_register_events(void) +{ + trace_event_register_group(testsuite_trace_events); +} +trace_init(trace_testsuite_register_events) diff --git a/tests/tracetool/log.h b/tests/tracetool/log.h new file mode 100644 index 0000000000000..4293f1010e05d --- /dev/null +++ b/tests/tracetool/log.h @@ -0,0 +1,57 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef TRACE_TESTSUITE_GENERATED_TRACERS_H +#define TRACE_TESTSUITE_GENERATED_TRACERS_H + +#include "trace/control.h" + +extern TraceEvent _TRACE_TEST_BLAH_EVENT; +extern TraceEvent _TRACE_TEST_WIBBLE_EVENT; +extern uint16_t _TRACE_TEST_BLAH_DSTATE; +extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; +#define TRACE_TEST_BLAH_ENABLED 1 +#define TRACE_TEST_WIBBLE_ENABLED 1 +#include "qemu/log-for-trace.h" + + +#define TRACE_TEST_BLAH_BACKEND_DSTATE() ( \ + trace_event_get_state_dynamic_by_id(TRACE_TEST_BLAH) || \ + false) + +static inline void _nocheck__trace_test_blah(void *context, const char *filename) +{ + if (trace_event_get_state(TRACE_TEST_BLAH) && qemu_loglevel_mask(LOG_TRACE)) { +#line 4 "trace-events" + qemu_log("test_blah " "Blah context=%p filename=%s" "\n", context, filename); +#line 28 "log.h" + } +} + +static inline void trace_test_blah(void *context, const char *filename) +{ + if (true) { + _nocheck__trace_test_blah(context, filename); + } +} + +#define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ + trace_event_get_state_dynamic_by_id(TRACE_TEST_WIBBLE) || \ + false) + +static inline void _nocheck__trace_test_wibble(void *context, int value) +{ + if (trace_event_get_state(TRACE_TEST_WIBBLE) && qemu_loglevel_mask(LOG_TRACE)) { +#line 5 "trace-events" + qemu_log("test_wibble " "Wibble context=%p value=%d" "\n", context, value); +#line 48 "log.h" + } +} + +static inline void trace_test_wibble(void *context, int value) +{ + if (true) { + _nocheck__trace_test_wibble(context, value); + } +} +#endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/meson.build b/tests/tracetool/meson.build new file mode 100644 index 0000000000000..09bbaaa86bf49 --- /dev/null +++ b/tests/tracetool/meson.build @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_env = environment() +test_env.set('PYTHONPATH', meson.project_source_root() / 'scripts') +test_env.set('PYTHONIOENCODING', 'utf-8') + +backends = [ + 'dtrace', + 'ftrace', + 'log', + 'simple', + 'syslog', + 'ust' +] + +# The tracetool-test.py program has portability problems on Windows. +if host_machine.system() != 'windows' + foreach backend: backends + test(backend, + python, + args: [meson.current_source_dir() / 'tracetool-test.py', + meson.project_source_root() / 'scripts' / 'tracetool.py', + backend, + meson.current_source_dir(), + meson.current_build_dir()], + suite: ['tracetool']) + endforeach +endif diff --git a/tests/tracetool/simple.c b/tests/tracetool/simple.c new file mode 100644 index 0000000000000..0484177481ce3 --- /dev/null +++ b/tests/tracetool/simple.c @@ -0,0 +1,61 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "trace-testsuite.h" + +uint16_t _TRACE_TEST_BLAH_DSTATE; +uint16_t _TRACE_TEST_WIBBLE_DSTATE; +TraceEvent _TRACE_TEST_BLAH_EVENT = { + .id = 0, + .name = "test_blah", + .sstate = TRACE_TEST_BLAH_ENABLED, + .dstate = &_TRACE_TEST_BLAH_DSTATE +}; +TraceEvent _TRACE_TEST_WIBBLE_EVENT = { + .id = 0, + .name = "test_wibble", + .sstate = TRACE_TEST_WIBBLE_ENABLED, + .dstate = &_TRACE_TEST_WIBBLE_DSTATE +}; +TraceEvent *testsuite_trace_events[] = { + &_TRACE_TEST_BLAH_EVENT, + &_TRACE_TEST_WIBBLE_EVENT, + NULL, +}; + +static void trace_testsuite_register_events(void) +{ + trace_event_register_group(testsuite_trace_events); +} +trace_init(trace_testsuite_register_events) +#include "qemu/osdep.h" +#include "trace/control.h" +#include "trace/simple.h" + +void _simple_trace_test_blah(void *context, const char *filename) +{ + TraceBufferRecord rec; + size_t argfilename_len = filename ? MIN(strlen(filename), MAX_TRACE_STRLEN) : 0; + + if (trace_record_start(&rec, _TRACE_TEST_BLAH_EVENT.id, 8 + 4 + argfilename_len)) { + return; /* Trace Buffer Full, Event Dropped ! */ + } + trace_record_write_u64(&rec, (uintptr_t)(uint64_t *)context); + trace_record_write_str(&rec, filename, argfilename_len); + trace_record_finish(&rec); +} + +void _simple_trace_test_wibble(void *context, int value) +{ + TraceBufferRecord rec; + + if (trace_record_start(&rec, _TRACE_TEST_WIBBLE_EVENT.id, 8 + 8)) { + return; /* Trace Buffer Full, Event Dropped ! */ + } + trace_record_write_u64(&rec, (uintptr_t)(uint64_t *)context); + trace_record_write_u64(&rec, (uint64_t)value); + trace_record_finish(&rec); +} + diff --git a/tests/tracetool/simple.h b/tests/tracetool/simple.h new file mode 100644 index 0000000000000..3c9de68c43ac6 --- /dev/null +++ b/tests/tracetool/simple.h @@ -0,0 +1,54 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef TRACE_TESTSUITE_GENERATED_TRACERS_H +#define TRACE_TESTSUITE_GENERATED_TRACERS_H + +#include "trace/control.h" + +extern TraceEvent _TRACE_TEST_BLAH_EVENT; +extern TraceEvent _TRACE_TEST_WIBBLE_EVENT; +extern uint16_t _TRACE_TEST_BLAH_DSTATE; +extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; +#define TRACE_TEST_BLAH_ENABLED 1 +#define TRACE_TEST_WIBBLE_ENABLED 1 +void _simple_trace_test_blah(void *context, const char *filename); +void _simple_trace_test_wibble(void *context, int value); + + +#define TRACE_TEST_BLAH_BACKEND_DSTATE() ( \ + trace_event_get_state_dynamic_by_id(TRACE_TEST_BLAH) || \ + false) + +static inline void _nocheck__trace_test_blah(void *context, const char *filename) +{ + if (trace_event_get_state(TRACE_TEST_BLAH)) { + _simple_trace_test_blah(context, filename); + } +} + +static inline void trace_test_blah(void *context, const char *filename) +{ + if (true) { + _nocheck__trace_test_blah(context, filename); + } +} + +#define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ + trace_event_get_state_dynamic_by_id(TRACE_TEST_WIBBLE) || \ + false) + +static inline void _nocheck__trace_test_wibble(void *context, int value) +{ + if (trace_event_get_state(TRACE_TEST_WIBBLE)) { + _simple_trace_test_wibble(context, value); + } +} + +static inline void trace_test_wibble(void *context, int value) +{ + if (true) { + _nocheck__trace_test_wibble(context, value); + } +} +#endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/syslog.c b/tests/tracetool/syslog.c new file mode 100644 index 0000000000000..9f862fa14d299 --- /dev/null +++ b/tests/tracetool/syslog.c @@ -0,0 +1,32 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "trace-testsuite.h" + +uint16_t _TRACE_TEST_BLAH_DSTATE; +uint16_t _TRACE_TEST_WIBBLE_DSTATE; +TraceEvent _TRACE_TEST_BLAH_EVENT = { + .id = 0, + .name = "test_blah", + .sstate = TRACE_TEST_BLAH_ENABLED, + .dstate = &_TRACE_TEST_BLAH_DSTATE +}; +TraceEvent _TRACE_TEST_WIBBLE_EVENT = { + .id = 0, + .name = "test_wibble", + .sstate = TRACE_TEST_WIBBLE_ENABLED, + .dstate = &_TRACE_TEST_WIBBLE_DSTATE +}; +TraceEvent *testsuite_trace_events[] = { + &_TRACE_TEST_BLAH_EVENT, + &_TRACE_TEST_WIBBLE_EVENT, + NULL, +}; + +static void trace_testsuite_register_events(void) +{ + trace_event_register_group(testsuite_trace_events); +} +trace_init(trace_testsuite_register_events) diff --git a/tests/tracetool/syslog.h b/tests/tracetool/syslog.h new file mode 100644 index 0000000000000..498bbfb99e013 --- /dev/null +++ b/tests/tracetool/syslog.h @@ -0,0 +1,57 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef TRACE_TESTSUITE_GENERATED_TRACERS_H +#define TRACE_TESTSUITE_GENERATED_TRACERS_H + +#include "trace/control.h" + +extern TraceEvent _TRACE_TEST_BLAH_EVENT; +extern TraceEvent _TRACE_TEST_WIBBLE_EVENT; +extern uint16_t _TRACE_TEST_BLAH_DSTATE; +extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; +#define TRACE_TEST_BLAH_ENABLED 1 +#define TRACE_TEST_WIBBLE_ENABLED 1 +#include + + +#define TRACE_TEST_BLAH_BACKEND_DSTATE() ( \ + trace_event_get_state_dynamic_by_id(TRACE_TEST_BLAH) || \ + false) + +static inline void _nocheck__trace_test_blah(void *context, const char *filename) +{ + if (trace_event_get_state(TRACE_TEST_BLAH)) { +#line 4 "trace-events" + syslog(LOG_INFO, "test_blah " "Blah context=%p filename=%s" , context, filename); +#line 28 "syslog.h" + } +} + +static inline void trace_test_blah(void *context, const char *filename) +{ + if (true) { + _nocheck__trace_test_blah(context, filename); + } +} + +#define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ + trace_event_get_state_dynamic_by_id(TRACE_TEST_WIBBLE) || \ + false) + +static inline void _nocheck__trace_test_wibble(void *context, int value) +{ + if (trace_event_get_state(TRACE_TEST_WIBBLE)) { +#line 5 "trace-events" + syslog(LOG_INFO, "test_wibble " "Wibble context=%p value=%d" , context, value); +#line 48 "syslog.h" + } +} + +static inline void trace_test_wibble(void *context, int value) +{ + if (true) { + _nocheck__trace_test_wibble(context, value); + } +} +#endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/trace-events b/tests/tracetool/trace-events new file mode 100644 index 0000000000000..72cf4d6f70d18 --- /dev/null +++ b/tests/tracetool/trace-events @@ -0,0 +1,5 @@ +# See docs/devel/tracing.rst for syntax documentation. +# SPDX-License-Identifier: GPL-2.0-or-later + +test_blah(void *context, const char *filename) "Blah context=%p filename=%s" +test_wibble(void *context, int value) "Wibble context=%p value=%d" diff --git a/tests/tracetool/tracetool-test.py b/tests/tracetool/tracetool-test.py new file mode 100755 index 0000000000000..a420597fc48a2 --- /dev/null +++ b/tests/tracetool/tracetool-test.py @@ -0,0 +1,105 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +from pathlib import Path +from shutil import copyfile +from subprocess import check_call +import sys + + +def get_formats(backend): + formats = [ + "c", + "h", + ] + if backend == "dtrace": + formats += [ + "d", + "log-stap", + "simpletrace-stap", + "stap", + ] + if backend == "ust": + formats += [ + "ust-events-c", + "ust-events-h", + ] + return formats + + +def test_tracetool_one(tracetool, backend, fmt, src_dir, build_dir): + rel_filename = backend + "." + fmt + actual_file = Path(build_dir, rel_filename) + expect_file = Path(src_dir, rel_filename) + + args = [tracetool, f"--format={fmt}", f"--backends={backend}", "--group=testsuite"] + + if fmt.find("stap") != -1: + args += ["--binary=qemu", "--probe-prefix=qemu"] + + # Use relative files for both, as these filenames end + # up in '#line' statements in the output + args += ["trace-events", rel_filename] + + try: + check_call(args, cwd=build_dir) + actual = actual_file.read_text() + finally: + actual_file.unlink() + + if os.getenv("QEMU_TEST_REGENERATE", False): + print(f"# regenerate {expect_file}") + expect_file.write_text(actual) + + expect = expect_file.read_text() + + assert expect == actual + + +def test_tracetool(tracetool, backend, source_dir, build_dir): + fail = False + scenarios = len(get_formats(backend)) + + print(f"1..{scenarios}") + + src_events = Path(source_dir, "trace-events") + build_events = Path(build_dir, "trace-events") + + try: + # We need a stable relative filename under build dir + # for the '#line' statements, so copy over the input + copyfile(src_events, build_events) + + num = 1 + for fmt in get_formats(backend): + status = "not ok" + hint = "" + try: + test_tracetool_one(tracetool, backend, fmt, source_dir, build_dir) + status = "ok" + except Exception as e: + print(f"# {e}") + fail = True + hint = ( + " (set QEMU_TEST_REGENERATE=1 to recreate reference " + + "output if tracetool generator was intentionally changed)" + ) + finally: + print(f"{status} {num} - {backend}.{fmt}{hint}") + finally: + build_events.unlink() + + return fail + + +if __name__ == "__main__": + if len(sys.argv) != 5: + argv0 = sys.argv[0] + print("syntax: {argv0} TRACE-TOOL BACKEND SRC-DIR BUILD-DIR", file=sys.stderr) + sys.exit(1) + + fail = test_tracetool(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]) + if fail: + sys.exit(1) + sys.exit(0) diff --git a/tests/tracetool/ust.c b/tests/tracetool/ust.c new file mode 100644 index 0000000000000..9f862fa14d299 --- /dev/null +++ b/tests/tracetool/ust.c @@ -0,0 +1,32 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "trace-testsuite.h" + +uint16_t _TRACE_TEST_BLAH_DSTATE; +uint16_t _TRACE_TEST_WIBBLE_DSTATE; +TraceEvent _TRACE_TEST_BLAH_EVENT = { + .id = 0, + .name = "test_blah", + .sstate = TRACE_TEST_BLAH_ENABLED, + .dstate = &_TRACE_TEST_BLAH_DSTATE +}; +TraceEvent _TRACE_TEST_WIBBLE_EVENT = { + .id = 0, + .name = "test_wibble", + .sstate = TRACE_TEST_WIBBLE_ENABLED, + .dstate = &_TRACE_TEST_WIBBLE_DSTATE +}; +TraceEvent *testsuite_trace_events[] = { + &_TRACE_TEST_BLAH_EVENT, + &_TRACE_TEST_WIBBLE_EVENT, + NULL, +}; + +static void trace_testsuite_register_events(void) +{ + trace_event_register_group(testsuite_trace_events); +} +trace_init(trace_testsuite_register_events) diff --git a/tests/tracetool/ust.h b/tests/tracetool/ust.h new file mode 100644 index 0000000000000..1184ddd870ed0 --- /dev/null +++ b/tests/tracetool/ust.h @@ -0,0 +1,55 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef TRACE_TESTSUITE_GENERATED_TRACERS_H +#define TRACE_TESTSUITE_GENERATED_TRACERS_H + +#include "trace/control.h" + +extern TraceEvent _TRACE_TEST_BLAH_EVENT; +extern TraceEvent _TRACE_TEST_WIBBLE_EVENT; +extern uint16_t _TRACE_TEST_BLAH_DSTATE; +extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; +#define TRACE_TEST_BLAH_ENABLED 1 +#define TRACE_TEST_WIBBLE_ENABLED 1 +#include +#include "trace-ust-testsuite.h" + +/* tracepoint_enabled() was introduced in LTTng UST 2.7 */ +#ifndef tracepoint_enabled +#define tracepoint_enabled(a, b) true +#endif + + +#define TRACE_TEST_BLAH_BACKEND_DSTATE() ( \ + tracepoint_enabled(qemu, test_blah) || \ + false) + +static inline void _nocheck__trace_test_blah(void *context, const char *filename) +{ + tracepoint(qemu, test_blah, context, filename); +} + +static inline void trace_test_blah(void *context, const char *filename) +{ + if (true) { + _nocheck__trace_test_blah(context, filename); + } +} + +#define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ + tracepoint_enabled(qemu, test_wibble) || \ + false) + +static inline void _nocheck__trace_test_wibble(void *context, int value) +{ + tracepoint(qemu, test_wibble, context, value); +} + +static inline void trace_test_wibble(void *context, int value) +{ + if (true) { + _nocheck__trace_test_wibble(context, value); + } +} +#endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/ust.ust-events-c b/tests/tracetool/ust.ust-events-c new file mode 100644 index 0000000000000..db232240568b3 --- /dev/null +++ b/tests/tracetool/ust.ust-events-c @@ -0,0 +1,14 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" + +#define TRACEPOINT_DEFINE +#define TRACEPOINT_CREATE_PROBES + +/* If gcc version 4.7 or older is used, LTTng ust gives a warning when compiling with + -Wredundant-decls. + */ +#pragma GCC diagnostic ignored "-Wredundant-decls" + +#include "trace-ust-all.h" diff --git a/tests/tracetool/ust.ust-events-h b/tests/tracetool/ust.ust-events-h new file mode 100644 index 0000000000000..4621a995fc1a2 --- /dev/null +++ b/tests/tracetool/ust.ust-events-h @@ -0,0 +1,56 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#undef TRACEPOINT_PROVIDER +#define TRACEPOINT_PROVIDER qemu + +#undef TRACEPOINT_INCLUDE +#define TRACEPOINT_INCLUDE "./trace-ust.h" + +#if !defined (TRACE_TESTSUITE_GENERATED_UST_H) || \ + defined(TRACEPOINT_HEADER_MULTI_READ) +#define TRACE_TESTSUITE_GENERATED_UST_H + +#include + +/* + * LTTng ust 2.0 does not allow you to use TP_ARGS(void) for tracepoints + * requiring no arguments. We define these macros introduced in more recent * versions of LTTng ust as a workaround + */ +#ifndef _TP_EXPROTO1 +#define _TP_EXPROTO1(a) void +#endif +#ifndef _TP_EXDATA_PROTO1 +#define _TP_EXDATA_PROTO1(a) void *__tp_data +#endif +#ifndef _TP_EXDATA_VAR1 +#define _TP_EXDATA_VAR1(a) __tp_data +#endif +#ifndef _TP_EXVAR1 +#define _TP_EXVAR1(a) +#endif + +TRACEPOINT_EVENT( + qemu, + test_blah, + TP_ARGS(void *, context, const char *, filename), + TP_FIELDS( + ctf_integer_hex(void *, context, context) + ctf_string(filename, filename) + ) +) + +TRACEPOINT_EVENT( + qemu, + test_wibble, + TP_ARGS(void *, context, int, value), + TP_FIELDS( + ctf_integer_hex(void *, context, context) + ctf_integer(int, value, value) + ) +) + +#endif /* TRACE_TESTSUITE_GENERATED_UST_H */ + +/* This part must be outside ifdef protection */ +#include From c47db9b1db906bd05cf83e0d70df2beba2ef9eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 16 Sep 2025 09:16:36 +0100 Subject: [PATCH 0703/1794] tracetool: drop the probe "__nocheck__" wrapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every generated inline probe function is wrapped with a trivial caller that has a hard-coded condition test: static inline void _nocheck__trace_test_wibble(void * context, int value) { tracepoint(qemu, test_wibble, context, value); } static inline void trace_test_wibble(void * context, int value) { if (true) { _nocheck__trace_test_wibble(context, value); } } This was introduced for TCG probes back in 864a2178: trace: [tcg] Do not generate TCG code to trace dynamically-disabled events but is obsolete since 126d4123 tracing: excise the tcg related from tracetool This commit removes the wrapping such that we have static inline void trace_test_wibble(void * context, int value) { tracepoint(qemu, test_wibble, context, value); } The default build of qemu-system-x86_64 on Fedora with the 'log' backend, has its size reduced by 1 MB Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Signed-off-by: Daniel P. Berrangé Message-id: 20250916081638.764020-7-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/__init__.py | 1 - scripts/tracetool/format/h.py | 16 +--------------- tests/tracetool/dtrace.h | 18 ++---------------- tests/tracetool/ftrace.h | 20 +++----------------- tests/tracetool/log.h | 20 +++----------------- tests/tracetool/simple.h | 18 ++---------------- tests/tracetool/syslog.h | 20 +++----------------- tests/tracetool/ust.h | 18 ++---------------- 8 files changed, 16 insertions(+), 115 deletions(-) diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 0f33758870627..1d5238a084329 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -338,7 +338,6 @@ def formats(self): return self._FMT.findall(self.fmt) QEMU_TRACE = "trace_%(name)s" - QEMU_TRACE_NOCHECK = "_nocheck__" + QEMU_TRACE QEMU_TRACE_TCG = QEMU_TRACE + "_tcg" QEMU_DSTATE = "_TRACE_%(NAME)s_DSTATE" QEMU_BACKEND_DSTATE = "TRACE_%(NAME)s_BACKEND_DSTATE" diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py index a00ae475f7606..b42a8268a81f2 100644 --- a/scripts/tracetool/format/h.py +++ b/scripts/tracetool/format/h.py @@ -64,7 +64,7 @@ def generate(events, backend, group): out('', 'static inline void %(api)s(%(args)s)', '{', - api=e.api(e.QEMU_TRACE_NOCHECK), + api=e.api(), args=e.args) if "disable" not in e.properties: @@ -72,20 +72,6 @@ def generate(events, backend, group): out('}') - cond = "true" - - out('', - 'static inline void %(api)s(%(args)s)', - '{', - ' if (%(cond)s) {', - ' %(api_nocheck)s(%(names)s);', - ' }', - '}', - api=e.api(), - api_nocheck=e.api(e.QEMU_TRACE_NOCHECK), - args=e.args, - names=", ".join(e.args.names()), - cond=cond) backend.generate_end(events, group) diff --git a/tests/tracetool/dtrace.h b/tests/tracetool/dtrace.h index c2e5110672530..c8931a8d7b3d6 100644 --- a/tests/tracetool/dtrace.h +++ b/tests/tracetool/dtrace.h @@ -29,31 +29,17 @@ extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; QEMU_TEST_BLAH_ENABLED() || \ false) -static inline void _nocheck__trace_test_blah(void *context, const char *filename) -{ - QEMU_TEST_BLAH(context, filename); -} - static inline void trace_test_blah(void *context, const char *filename) { - if (true) { - _nocheck__trace_test_blah(context, filename); - } + QEMU_TEST_BLAH(context, filename); } #define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ QEMU_TEST_WIBBLE_ENABLED() || \ false) -static inline void _nocheck__trace_test_wibble(void *context, int value) -{ - QEMU_TEST_WIBBLE(context, value); -} - static inline void trace_test_wibble(void *context, int value) { - if (true) { - _nocheck__trace_test_wibble(context, value); - } + QEMU_TEST_WIBBLE(context, value); } #endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/ftrace.h b/tests/tracetool/ftrace.h index f1ff3b0a62178..fe22ea0f09ffd 100644 --- a/tests/tracetool/ftrace.h +++ b/tests/tracetool/ftrace.h @@ -19,7 +19,7 @@ extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; trace_event_get_state_dynamic_by_id(TRACE_TEST_BLAH) || \ false) -static inline void _nocheck__trace_test_blah(void *context, const char *filename) +static inline void trace_test_blah(void *context, const char *filename) { { char ftrace_buf[MAX_TRACE_STRLEN]; @@ -36,18 +36,11 @@ static inline void _nocheck__trace_test_blah(void *context, const char *filename } } -static inline void trace_test_blah(void *context, const char *filename) -{ - if (true) { - _nocheck__trace_test_blah(context, filename); - } -} - #define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ trace_event_get_state_dynamic_by_id(TRACE_TEST_WIBBLE) || \ false) -static inline void _nocheck__trace_test_wibble(void *context, int value) +static inline void trace_test_wibble(void *context, int value) { { char ftrace_buf[MAX_TRACE_STRLEN]; @@ -57,17 +50,10 @@ static inline void _nocheck__trace_test_wibble(void *context, int value) #line 5 "trace-events" trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN, "test_wibble " "Wibble context=%p value=%d" "\n" , context, value); -#line 61 "ftrace.h" +#line 54 "ftrace.h" trlen = MIN(trlen, MAX_TRACE_STRLEN - 1); unused = write(trace_marker_fd, ftrace_buf, trlen); } } } - -static inline void trace_test_wibble(void *context, int value) -{ - if (true) { - _nocheck__trace_test_wibble(context, value); - } -} #endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/log.h b/tests/tracetool/log.h index 4293f1010e05d..edcc7f9d47cfe 100644 --- a/tests/tracetool/log.h +++ b/tests/tracetool/log.h @@ -19,7 +19,7 @@ extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; trace_event_get_state_dynamic_by_id(TRACE_TEST_BLAH) || \ false) -static inline void _nocheck__trace_test_blah(void *context, const char *filename) +static inline void trace_test_blah(void *context, const char *filename) { if (trace_event_get_state(TRACE_TEST_BLAH) && qemu_loglevel_mask(LOG_TRACE)) { #line 4 "trace-events" @@ -28,30 +28,16 @@ static inline void _nocheck__trace_test_blah(void *context, const char *filename } } -static inline void trace_test_blah(void *context, const char *filename) -{ - if (true) { - _nocheck__trace_test_blah(context, filename); - } -} - #define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ trace_event_get_state_dynamic_by_id(TRACE_TEST_WIBBLE) || \ false) -static inline void _nocheck__trace_test_wibble(void *context, int value) +static inline void trace_test_wibble(void *context, int value) { if (trace_event_get_state(TRACE_TEST_WIBBLE) && qemu_loglevel_mask(LOG_TRACE)) { #line 5 "trace-events" qemu_log("test_wibble " "Wibble context=%p value=%d" "\n", context, value); -#line 48 "log.h" - } -} - -static inline void trace_test_wibble(void *context, int value) -{ - if (true) { - _nocheck__trace_test_wibble(context, value); +#line 41 "log.h" } } #endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/simple.h b/tests/tracetool/simple.h index 3c9de68c43ac6..ec6fcb22c3c8d 100644 --- a/tests/tracetool/simple.h +++ b/tests/tracetool/simple.h @@ -20,35 +20,21 @@ void _simple_trace_test_wibble(void *context, int value); trace_event_get_state_dynamic_by_id(TRACE_TEST_BLAH) || \ false) -static inline void _nocheck__trace_test_blah(void *context, const char *filename) +static inline void trace_test_blah(void *context, const char *filename) { if (trace_event_get_state(TRACE_TEST_BLAH)) { _simple_trace_test_blah(context, filename); } } -static inline void trace_test_blah(void *context, const char *filename) -{ - if (true) { - _nocheck__trace_test_blah(context, filename); - } -} - #define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ trace_event_get_state_dynamic_by_id(TRACE_TEST_WIBBLE) || \ false) -static inline void _nocheck__trace_test_wibble(void *context, int value) +static inline void trace_test_wibble(void *context, int value) { if (trace_event_get_state(TRACE_TEST_WIBBLE)) { _simple_trace_test_wibble(context, value); } } - -static inline void trace_test_wibble(void *context, int value) -{ - if (true) { - _nocheck__trace_test_wibble(context, value); - } -} #endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/syslog.h b/tests/tracetool/syslog.h index 498bbfb99e013..ed4305554c174 100644 --- a/tests/tracetool/syslog.h +++ b/tests/tracetool/syslog.h @@ -19,7 +19,7 @@ extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; trace_event_get_state_dynamic_by_id(TRACE_TEST_BLAH) || \ false) -static inline void _nocheck__trace_test_blah(void *context, const char *filename) +static inline void trace_test_blah(void *context, const char *filename) { if (trace_event_get_state(TRACE_TEST_BLAH)) { #line 4 "trace-events" @@ -28,30 +28,16 @@ static inline void _nocheck__trace_test_blah(void *context, const char *filename } } -static inline void trace_test_blah(void *context, const char *filename) -{ - if (true) { - _nocheck__trace_test_blah(context, filename); - } -} - #define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ trace_event_get_state_dynamic_by_id(TRACE_TEST_WIBBLE) || \ false) -static inline void _nocheck__trace_test_wibble(void *context, int value) +static inline void trace_test_wibble(void *context, int value) { if (trace_event_get_state(TRACE_TEST_WIBBLE)) { #line 5 "trace-events" syslog(LOG_INFO, "test_wibble " "Wibble context=%p value=%d" , context, value); -#line 48 "syslog.h" - } -} - -static inline void trace_test_wibble(void *context, int value) -{ - if (true) { - _nocheck__trace_test_wibble(context, value); +#line 41 "syslog.h" } } #endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/ust.h b/tests/tracetool/ust.h index 1184ddd870ed0..b7acd0c39b155 100644 --- a/tests/tracetool/ust.h +++ b/tests/tracetool/ust.h @@ -25,31 +25,17 @@ extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; tracepoint_enabled(qemu, test_blah) || \ false) -static inline void _nocheck__trace_test_blah(void *context, const char *filename) -{ - tracepoint(qemu, test_blah, context, filename); -} - static inline void trace_test_blah(void *context, const char *filename) { - if (true) { - _nocheck__trace_test_blah(context, filename); - } + tracepoint(qemu, test_blah, context, filename); } #define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ tracepoint_enabled(qemu, test_wibble) || \ false) -static inline void _nocheck__trace_test_wibble(void *context, int value) -{ - tracepoint(qemu, test_wibble, context, value); -} - static inline void trace_test_wibble(void *context, int value) { - if (true) { - _nocheck__trace_test_wibble(context, value); - } + tracepoint(qemu, test_wibble, context, value); } #endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ From 4b1e71591254a7e924b6b295868edc6550ed0420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 16 Sep 2025 09:16:37 +0100 Subject: [PATCH 0704/1794] qapi: switch to use QEMU_TEST_REGENERATE env var MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QAPI_TEST_UPDATE env var can be set when running the QAPI schema tests to regenerate the reference output. For consistent naming with the tracetool test, change the env var name to QEMU_TEST_REGENERATE. The test is modified to provide a hint about use of the new env var and it is also added to the developer documentation.document its usage. Reviewed-by: Stefan Hajnoczi Signed-off-by: Daniel P. Berrangé Reviewed-by: Markus Armbruster Message-id: 20250916081638.764020-8-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- docs/devel/testing/main.rst | 12 ++++++++++++ tests/qapi-schema/test-qapi.py | 7 +++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/devel/testing/main.rst b/docs/devel/testing/main.rst index 11f05c0006553..0662766b5c915 100644 --- a/docs/devel/testing/main.rst +++ b/docs/devel/testing/main.rst @@ -178,6 +178,18 @@ parser (either fixing a bug or extending/modifying the syntax). To do this: ``qapi-schema += foo.json`` +The reference output can be automatically updated to match the latest QAPI +code generator by running the tests with the QEMU_TEST_REGENERATE environment +variable set. + +.. code:: + + QEMU_TEST_REGENERATE=1 make check-qapi-schema + +The resulting changes must be reviewed by the author to ensure they match +the intended results before adding the updated reference output to the +same commit that alters the generator code. + .. _tracetool-tests: Tracetool tests diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 4be930228cc31..cf7fb8a6df5cc 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -165,7 +165,7 @@ def test_and_diff(test_name, dir_name, update): if actual_out == expected_out and actual_err == expected_err: return 0 - print("%s %s" % (test_name, 'UPDATE' if update else 'FAIL'), + print("%s: %s" % (test_name, 'UPDATE' if update else 'FAIL'), file=sys.stderr) out_diff = difflib.unified_diff(expected_out, actual_out, outfp.name) err_diff = difflib.unified_diff(expected_err, actual_err, errfp.name) @@ -173,6 +173,9 @@ def test_and_diff(test_name, dir_name, update): sys.stdout.writelines(err_diff) if not update: + print(("\n%s: set QEMU_TEST_REGENERATE=1 to recreate reference output" + + "if the QAPI schema generator was intentionally changed") % test_name, + file=sys.stderr) return 1 try: @@ -197,7 +200,7 @@ def main(argv): parser.add_argument('-d', '--dir', action='store', default='', help="directory containing tests") parser.add_argument('-u', '--update', action='store_true', - default='QAPI_TEST_UPDATE' in os.environ, + default='QEMU_TEST_REGENERATE' in os.environ, help="update expected test results") parser.add_argument('tests', nargs='*', metavar='TEST', action='store') args = parser.parse_args() From 2c27d8523927b0965b7b3d265eee3baf9a15c9c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 16 Sep 2025 09:16:38 +0100 Subject: [PATCH 0705/1794] tracetool-test: allow to run in parallel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a temporary build subdirectory, to avoid conflicting with other running tests. This fixes "meson test" with tracetool-test which is parallel default. Signed-off-by: Marc-André Lureau Signed-off-by: Stefan Hajnoczi Message-id: 20250916081638.764020-9-berrange@redhat.com Message-ID: <20250908114652.1880366-1-marcandre.lureau@redhat.com> Signed-off-by: Stefan Hajnoczi --- tests/tracetool/tracetool-test.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/tracetool/tracetool-test.py b/tests/tracetool/tracetool-test.py index a420597fc48a2..65430fdedc1d9 100755 --- a/tests/tracetool/tracetool-test.py +++ b/tests/tracetool/tracetool-test.py @@ -6,6 +6,7 @@ from shutil import copyfile from subprocess import check_call import sys +import tempfile def get_formats(backend): @@ -99,7 +100,8 @@ def test_tracetool(tracetool, backend, source_dir, build_dir): print("syntax: {argv0} TRACE-TOOL BACKEND SRC-DIR BUILD-DIR", file=sys.stderr) sys.exit(1) - fail = test_tracetool(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]) - if fail: - sys.exit(1) + with tempfile.TemporaryDirectory(prefix=sys.argv[4]) as tmpdir: + fail = test_tracetool(sys.argv[1], sys.argv[2], sys.argv[3], tmpdir) + if fail: + sys.exit(1) sys.exit(0) From 588ffa75eba30baf75d1ba246c5f917e8716fcaa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 10:53:55 +0200 Subject: [PATCH 0706/1794] target/ppc: limit cpu_interrupt_exittb to system emulation It is not used by user-mode emulation and is the only caller of cpu_interrupt() in qemu-ppc* binaries. Reviewed-by: Igor Mammedov Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/ppc/helper_regs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 7e5726871e5b2..5f217397490ab 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -274,6 +274,7 @@ TCGTBCPUState ppc_get_tb_cpu_state(CPUState *cs) return (TCGTBCPUState){ .pc = env->nip, .flags = hflags_current }; } +#ifndef CONFIG_USER_ONLY void cpu_interrupt_exittb(CPUState *cs) { /* @@ -285,6 +286,7 @@ void cpu_interrupt_exittb(CPUState *cs) cpu_interrupt(cs, CPU_INTERRUPT_EXITTB); } } +#endif int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv) { From a445d3b85c4119aac7ecb7771b5da0709ee1f1a4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 10:53:55 +0200 Subject: [PATCH 0707/1794] target/sparc: limit cpu_check_irqs to system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is not used by user-mode emulation and is the only caller of cpu_interrupt() in qemu-sparc* binaries. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/sparc/int32_helper.c | 2 ++ target/sparc/int64_helper.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/target/sparc/int32_helper.c b/target/sparc/int32_helper.c index 39db4ffa70a88..fdcaa0a578b2c 100644 --- a/target/sparc/int32_helper.c +++ b/target/sparc/int32_helper.c @@ -65,6 +65,7 @@ static const char *excp_name_str(int32_t exception_index) return excp_names[exception_index]; } +#if !defined(CONFIG_USER_ONLY) void cpu_check_irqs(CPUSPARCState *env) { CPUState *cs; @@ -96,6 +97,7 @@ void cpu_check_irqs(CPUSPARCState *env) cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } } +#endif void sparc_cpu_do_interrupt(CPUState *cs) { diff --git a/target/sparc/int64_helper.c b/target/sparc/int64_helper.c index 49e4e51c6dcc6..23adda4cad7bc 100644 --- a/target/sparc/int64_helper.c +++ b/target/sparc/int64_helper.c @@ -62,6 +62,7 @@ static const char * const excp_names[0x80] = { }; #endif +#if !defined(CONFIG_USER_ONLY) void cpu_check_irqs(CPUSPARCState *env) { CPUState *cs; @@ -127,6 +128,7 @@ void cpu_check_irqs(CPUSPARCState *env) cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } } +#endif void sparc_cpu_do_interrupt(CPUState *cs) { From 3efe1a0f604cb7c4afd0381294acbdec75c65325 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 10:43:44 +0200 Subject: [PATCH 0708/1794] target/i386: limit a20 to system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is not used by user-mode emulation and is the only caller of cpu_interrupt() in qemu-i386 and qemu-x86_64. Reviewed-by: Igor Mammedov Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/i386/helper.c b/target/i386/helper.c index e0aaed3c4c4e0..651041ccfa683 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -110,6 +110,7 @@ int cpu_x86_support_mca_broadcast(CPUX86State *env) /* x86 mmu */ /* XXX: add PGE support */ +#ifndef CONFIG_USER_ONLY void x86_cpu_set_a20(X86CPU *cpu, int a20_state) { CPUX86State *env = &cpu->env; @@ -129,6 +130,7 @@ void x86_cpu_set_a20(X86CPU *cpu, int a20_state) env->a20_mask = ~(1 << 20) | (a20_state << 20); } } +#endif void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0) { From bd1cefdd9f18bfbdcb597d7d552fbf31dee47a28 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 12:43:55 +0200 Subject: [PATCH 0709/1794] target-arm: remove uses of cpu_interrupt() for user-mode emulation Arm leaves around some functions that use cpu_interrupt(), even for user-mode emulation when the code is unreachable. Pull out the system-mode implementation to a separate file, and add stubs for CONFIG_USER_ONLY. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/arm/cpu-irq.c | 381 +++++++++++++++++++++++++++++++++++++++++ target/arm/cpu.c | 370 --------------------------------------- target/arm/el2-stubs.c | 37 ++++ target/arm/helper.c | 4 + target/arm/internals.h | 5 + target/arm/meson.build | 2 + 6 files changed, 429 insertions(+), 370 deletions(-) create mode 100644 target/arm/cpu-irq.c create mode 100644 target/arm/el2-stubs.c diff --git a/target/arm/cpu-irq.c b/target/arm/cpu-irq.c new file mode 100644 index 0000000000000..fe514cc93af88 --- /dev/null +++ b/target/arm/cpu-irq.c @@ -0,0 +1,381 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * QEMU ARM CPU - interrupt_request handling + * + * Copyright (c) 2003-2025 QEMU contributors + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "accel/tcg/cpu-ops.h" +#include "internals.h" + +#ifdef CONFIG_TCG +static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, + unsigned int target_el, + unsigned int cur_el, bool secure, + uint64_t hcr_el2) +{ + CPUARMState *env = cpu_env(cs); + bool pstate_unmasked; + bool unmasked = false; + bool allIntMask = false; + + /* + * Don't take exceptions if they target a lower EL. + * This check should catch any exceptions that would not be taken + * but left pending. + */ + if (cur_el > target_el) { + return false; + } + + if (cpu_isar_feature(aa64_nmi, env_archcpu(env)) && + env->cp15.sctlr_el[target_el] & SCTLR_NMI && cur_el == target_el) { + allIntMask = env->pstate & PSTATE_ALLINT || + ((env->cp15.sctlr_el[target_el] & SCTLR_SPINTMASK) && + (env->pstate & PSTATE_SP)); + } + + switch (excp_idx) { + case EXCP_NMI: + pstate_unmasked = !allIntMask; + break; + + case EXCP_VINMI: + if (!(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { + /* VINMIs are only taken when hypervized. */ + return false; + } + return !allIntMask; + case EXCP_VFNMI: + if (!(hcr_el2 & HCR_FMO) || (hcr_el2 & HCR_TGE)) { + /* VFNMIs are only taken when hypervized. */ + return false; + } + return !allIntMask; + case EXCP_FIQ: + pstate_unmasked = (!(env->daif & PSTATE_F)) && (!allIntMask); + break; + + case EXCP_IRQ: + pstate_unmasked = (!(env->daif & PSTATE_I)) && (!allIntMask); + break; + + case EXCP_VFIQ: + if (!(hcr_el2 & HCR_FMO) || (hcr_el2 & HCR_TGE)) { + /* VFIQs are only taken when hypervized. */ + return false; + } + return !(env->daif & PSTATE_F) && (!allIntMask); + case EXCP_VIRQ: + if (!(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { + /* VIRQs are only taken when hypervized. */ + return false; + } + return !(env->daif & PSTATE_I) && (!allIntMask); + case EXCP_VSERR: + if (!(hcr_el2 & HCR_AMO) || (hcr_el2 & HCR_TGE)) { + /* VIRQs are only taken when hypervized. */ + return false; + } + return !(env->daif & PSTATE_A); + default: + g_assert_not_reached(); + } + + /* + * Use the target EL, current execution state and SCR/HCR settings to + * determine whether the corresponding CPSR bit is used to mask the + * interrupt. + */ + if ((target_el > cur_el) && (target_el != 1)) { + /* Exceptions targeting a higher EL may not be maskable */ + if (arm_feature(env, ARM_FEATURE_AARCH64)) { + switch (target_el) { + case 2: + /* + * According to ARM DDI 0487H.a, an interrupt can be masked + * when HCR_E2H and HCR_TGE are both set regardless of the + * current Security state. Note that we need to revisit this + * part again once we need to support NMI. + */ + if ((hcr_el2 & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { + unmasked = true; + } + break; + case 3: + /* Interrupt cannot be masked when the target EL is 3 */ + unmasked = true; + break; + default: + g_assert_not_reached(); + } + } else { + /* + * The old 32-bit-only environment has a more complicated + * masking setup. HCR and SCR bits not only affect interrupt + * routing but also change the behaviour of masking. + */ + bool hcr, scr; + + switch (excp_idx) { + case EXCP_FIQ: + /* + * If FIQs are routed to EL3 or EL2 then there are cases where + * we override the CPSR.F in determining if the exception is + * masked or not. If neither of these are set then we fall back + * to the CPSR.F setting otherwise we further assess the state + * below. + */ + hcr = hcr_el2 & HCR_FMO; + scr = (env->cp15.scr_el3 & SCR_FIQ); + + /* + * When EL3 is 32-bit, the SCR.FW bit controls whether the + * CPSR.F bit masks FIQ interrupts when taken in non-secure + * state. If SCR.FW is set then FIQs can be masked by CPSR.F + * when non-secure but only when FIQs are only routed to EL3. + */ + scr = scr && !((env->cp15.scr_el3 & SCR_FW) && !hcr); + break; + case EXCP_IRQ: + /* + * When EL3 execution state is 32-bit, if HCR.IMO is set then + * we may override the CPSR.I masking when in non-secure state. + * The SCR.IRQ setting has already been taken into consideration + * when setting the target EL, so it does not have a further + * affect here. + */ + hcr = hcr_el2 & HCR_IMO; + scr = false; + break; + default: + g_assert_not_reached(); + } + + if ((scr || hcr) && !secure) { + unmasked = true; + } + } + } + + /* + * The PSTATE bits only mask the interrupt if we have not overridden the + * ability above. + */ + return unmasked || pstate_unmasked; +} + +bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + CPUARMState *env = cpu_env(cs); + uint32_t cur_el = arm_current_el(env); + bool secure = arm_is_secure(env); + uint64_t hcr_el2 = arm_hcr_el2_eff(env); + uint32_t target_el; + uint32_t excp_idx; + + /* The prioritization of interrupts is IMPLEMENTATION DEFINED. */ + + if (cpu_isar_feature(aa64_nmi, env_archcpu(env)) && + (arm_sctlr(env, cur_el) & SCTLR_NMI)) { + if (interrupt_request & CPU_INTERRUPT_NMI) { + excp_idx = EXCP_NMI; + target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_VINMI) { + excp_idx = EXCP_VINMI; + target_el = 1; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_VFNMI) { + excp_idx = EXCP_VFNMI; + target_el = 1; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + } else { + /* + * NMI disabled: interrupts with superpriority are handled + * as if they didn't have it + */ + if (interrupt_request & CPU_INTERRUPT_NMI) { + interrupt_request |= CPU_INTERRUPT_HARD; + } + if (interrupt_request & CPU_INTERRUPT_VINMI) { + interrupt_request |= CPU_INTERRUPT_VIRQ; + } + if (interrupt_request & CPU_INTERRUPT_VFNMI) { + interrupt_request |= CPU_INTERRUPT_VFIQ; + } + } + + if (interrupt_request & CPU_INTERRUPT_FIQ) { + excp_idx = EXCP_FIQ; + target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_HARD) { + excp_idx = EXCP_IRQ; + target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_VIRQ) { + excp_idx = EXCP_VIRQ; + target_el = 1; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_VFIQ) { + excp_idx = EXCP_VFIQ; + target_el = 1; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_VSERR) { + excp_idx = EXCP_VSERR; + target_el = 1; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + /* Taking a virtual abort clears HCR_EL2.VSE */ + env->cp15.hcr_el2 &= ~HCR_VSE; + cpu_reset_interrupt(cs, CPU_INTERRUPT_VSERR); + goto found; + } + } + return false; + + found: + cs->exception_index = excp_idx; + env->exception.target_el = target_el; + cs->cc->tcg_ops->do_interrupt(cs); + return true; +} +#endif /* CONFIG_TCG */ + +void arm_cpu_update_virq(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VIRQ, which is the logical OR of + * the HCR_EL2.VI bit and the input line level from the GIC. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = ((arm_hcr_el2_eff(env) & HCR_VI) && + !(arm_hcrx_el2_eff(env) & HCRX_VINMI)) || + (env->irq_line_state & CPU_INTERRUPT_VIRQ); + + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VIRQ)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VIRQ); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VIRQ); + } + } +} + +void arm_cpu_update_vfiq(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VFIQ, which is the logical OR of + * the HCR_EL2.VF bit and the input line level from the GIC. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = ((arm_hcr_el2_eff(env) & HCR_VF) && + !(arm_hcrx_el2_eff(env) & HCRX_VFNMI)) || + (env->irq_line_state & CPU_INTERRUPT_VFIQ); + + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VFIQ)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VFIQ); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VFIQ); + } + } +} + +void arm_cpu_update_vinmi(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VINMI, which is the logical OR of + * the HCRX_EL2.VINMI bit and the input line level from the GIC. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = ((arm_hcr_el2_eff(env) & HCR_VI) && + (arm_hcrx_el2_eff(env) & HCRX_VINMI)) || + (env->irq_line_state & CPU_INTERRUPT_VINMI); + + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VINMI)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VINMI); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VINMI); + } + } +} + +void arm_cpu_update_vfnmi(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VFNMI, which is the HCRX_EL2.VFNMI bit. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = (arm_hcr_el2_eff(env) & HCR_VF) && + (arm_hcrx_el2_eff(env) & HCRX_VFNMI); + + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VFNMI)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VFNMI); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VFNMI); + } + } +} + +void arm_cpu_update_vserr(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VSERR, which is the HCR_EL2.VSE bit. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = env->cp15.hcr_el2 & HCR_VSE; + + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VSERR)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VSERR); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VSERR); + } + } +} + diff --git a/target/arm/cpu.c b/target/arm/cpu.c index d0f6fcdfcea93..633ec55a57c4c 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -686,376 +686,6 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el) } -#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) - -static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, - unsigned int target_el, - unsigned int cur_el, bool secure, - uint64_t hcr_el2) -{ - CPUARMState *env = cpu_env(cs); - bool pstate_unmasked; - bool unmasked = false; - bool allIntMask = false; - - /* - * Don't take exceptions if they target a lower EL. - * This check should catch any exceptions that would not be taken - * but left pending. - */ - if (cur_el > target_el) { - return false; - } - - if (cpu_isar_feature(aa64_nmi, env_archcpu(env)) && - env->cp15.sctlr_el[target_el] & SCTLR_NMI && cur_el == target_el) { - allIntMask = env->pstate & PSTATE_ALLINT || - ((env->cp15.sctlr_el[target_el] & SCTLR_SPINTMASK) && - (env->pstate & PSTATE_SP)); - } - - switch (excp_idx) { - case EXCP_NMI: - pstate_unmasked = !allIntMask; - break; - - case EXCP_VINMI: - if (!(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { - /* VINMIs are only taken when hypervized. */ - return false; - } - return !allIntMask; - case EXCP_VFNMI: - if (!(hcr_el2 & HCR_FMO) || (hcr_el2 & HCR_TGE)) { - /* VFNMIs are only taken when hypervized. */ - return false; - } - return !allIntMask; - case EXCP_FIQ: - pstate_unmasked = (!(env->daif & PSTATE_F)) && (!allIntMask); - break; - - case EXCP_IRQ: - pstate_unmasked = (!(env->daif & PSTATE_I)) && (!allIntMask); - break; - - case EXCP_VFIQ: - if (!(hcr_el2 & HCR_FMO) || (hcr_el2 & HCR_TGE)) { - /* VFIQs are only taken when hypervized. */ - return false; - } - return !(env->daif & PSTATE_F) && (!allIntMask); - case EXCP_VIRQ: - if (!(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { - /* VIRQs are only taken when hypervized. */ - return false; - } - return !(env->daif & PSTATE_I) && (!allIntMask); - case EXCP_VSERR: - if (!(hcr_el2 & HCR_AMO) || (hcr_el2 & HCR_TGE)) { - /* VIRQs are only taken when hypervized. */ - return false; - } - return !(env->daif & PSTATE_A); - default: - g_assert_not_reached(); - } - - /* - * Use the target EL, current execution state and SCR/HCR settings to - * determine whether the corresponding CPSR bit is used to mask the - * interrupt. - */ - if ((target_el > cur_el) && (target_el != 1)) { - /* Exceptions targeting a higher EL may not be maskable */ - if (arm_feature(env, ARM_FEATURE_AARCH64)) { - switch (target_el) { - case 2: - /* - * According to ARM DDI 0487H.a, an interrupt can be masked - * when HCR_E2H and HCR_TGE are both set regardless of the - * current Security state. Note that we need to revisit this - * part again once we need to support NMI. - */ - if ((hcr_el2 & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { - unmasked = true; - } - break; - case 3: - /* Interrupt cannot be masked when the target EL is 3 */ - unmasked = true; - break; - default: - g_assert_not_reached(); - } - } else { - /* - * The old 32-bit-only environment has a more complicated - * masking setup. HCR and SCR bits not only affect interrupt - * routing but also change the behaviour of masking. - */ - bool hcr, scr; - - switch (excp_idx) { - case EXCP_FIQ: - /* - * If FIQs are routed to EL3 or EL2 then there are cases where - * we override the CPSR.F in determining if the exception is - * masked or not. If neither of these are set then we fall back - * to the CPSR.F setting otherwise we further assess the state - * below. - */ - hcr = hcr_el2 & HCR_FMO; - scr = (env->cp15.scr_el3 & SCR_FIQ); - - /* - * When EL3 is 32-bit, the SCR.FW bit controls whether the - * CPSR.F bit masks FIQ interrupts when taken in non-secure - * state. If SCR.FW is set then FIQs can be masked by CPSR.F - * when non-secure but only when FIQs are only routed to EL3. - */ - scr = scr && !((env->cp15.scr_el3 & SCR_FW) && !hcr); - break; - case EXCP_IRQ: - /* - * When EL3 execution state is 32-bit, if HCR.IMO is set then - * we may override the CPSR.I masking when in non-secure state. - * The SCR.IRQ setting has already been taken into consideration - * when setting the target EL, so it does not have a further - * affect here. - */ - hcr = hcr_el2 & HCR_IMO; - scr = false; - break; - default: - g_assert_not_reached(); - } - - if ((scr || hcr) && !secure) { - unmasked = true; - } - } - } - - /* - * The PSTATE bits only mask the interrupt if we have not overridden the - * ability above. - */ - return unmasked || pstate_unmasked; -} - -static bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) -{ - CPUARMState *env = cpu_env(cs); - uint32_t cur_el = arm_current_el(env); - bool secure = arm_is_secure(env); - uint64_t hcr_el2 = arm_hcr_el2_eff(env); - uint32_t target_el; - uint32_t excp_idx; - - /* The prioritization of interrupts is IMPLEMENTATION DEFINED. */ - - if (cpu_isar_feature(aa64_nmi, env_archcpu(env)) && - (arm_sctlr(env, cur_el) & SCTLR_NMI)) { - if (interrupt_request & CPU_INTERRUPT_NMI) { - excp_idx = EXCP_NMI; - target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); - if (arm_excp_unmasked(cs, excp_idx, target_el, - cur_el, secure, hcr_el2)) { - goto found; - } - } - if (interrupt_request & CPU_INTERRUPT_VINMI) { - excp_idx = EXCP_VINMI; - target_el = 1; - if (arm_excp_unmasked(cs, excp_idx, target_el, - cur_el, secure, hcr_el2)) { - goto found; - } - } - if (interrupt_request & CPU_INTERRUPT_VFNMI) { - excp_idx = EXCP_VFNMI; - target_el = 1; - if (arm_excp_unmasked(cs, excp_idx, target_el, - cur_el, secure, hcr_el2)) { - goto found; - } - } - } else { - /* - * NMI disabled: interrupts with superpriority are handled - * as if they didn't have it - */ - if (interrupt_request & CPU_INTERRUPT_NMI) { - interrupt_request |= CPU_INTERRUPT_HARD; - } - if (interrupt_request & CPU_INTERRUPT_VINMI) { - interrupt_request |= CPU_INTERRUPT_VIRQ; - } - if (interrupt_request & CPU_INTERRUPT_VFNMI) { - interrupt_request |= CPU_INTERRUPT_VFIQ; - } - } - - if (interrupt_request & CPU_INTERRUPT_FIQ) { - excp_idx = EXCP_FIQ; - target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); - if (arm_excp_unmasked(cs, excp_idx, target_el, - cur_el, secure, hcr_el2)) { - goto found; - } - } - if (interrupt_request & CPU_INTERRUPT_HARD) { - excp_idx = EXCP_IRQ; - target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); - if (arm_excp_unmasked(cs, excp_idx, target_el, - cur_el, secure, hcr_el2)) { - goto found; - } - } - if (interrupt_request & CPU_INTERRUPT_VIRQ) { - excp_idx = EXCP_VIRQ; - target_el = 1; - if (arm_excp_unmasked(cs, excp_idx, target_el, - cur_el, secure, hcr_el2)) { - goto found; - } - } - if (interrupt_request & CPU_INTERRUPT_VFIQ) { - excp_idx = EXCP_VFIQ; - target_el = 1; - if (arm_excp_unmasked(cs, excp_idx, target_el, - cur_el, secure, hcr_el2)) { - goto found; - } - } - if (interrupt_request & CPU_INTERRUPT_VSERR) { - excp_idx = EXCP_VSERR; - target_el = 1; - if (arm_excp_unmasked(cs, excp_idx, target_el, - cur_el, secure, hcr_el2)) { - /* Taking a virtual abort clears HCR_EL2.VSE */ - env->cp15.hcr_el2 &= ~HCR_VSE; - cpu_reset_interrupt(cs, CPU_INTERRUPT_VSERR); - goto found; - } - } - return false; - - found: - cs->exception_index = excp_idx; - env->exception.target_el = target_el; - cs->cc->tcg_ops->do_interrupt(cs); - return true; -} - -#endif /* CONFIG_TCG && !CONFIG_USER_ONLY */ - -void arm_cpu_update_virq(ARMCPU *cpu) -{ - /* - * Update the interrupt level for VIRQ, which is the logical OR of - * the HCR_EL2.VI bit and the input line level from the GIC. - */ - CPUARMState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - bool new_state = ((arm_hcr_el2_eff(env) & HCR_VI) && - !(arm_hcrx_el2_eff(env) & HCRX_VINMI)) || - (env->irq_line_state & CPU_INTERRUPT_VIRQ); - - if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VIRQ)) { - if (new_state) { - cpu_interrupt(cs, CPU_INTERRUPT_VIRQ); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_VIRQ); - } - } -} - -void arm_cpu_update_vfiq(ARMCPU *cpu) -{ - /* - * Update the interrupt level for VFIQ, which is the logical OR of - * the HCR_EL2.VF bit and the input line level from the GIC. - */ - CPUARMState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - bool new_state = ((arm_hcr_el2_eff(env) & HCR_VF) && - !(arm_hcrx_el2_eff(env) & HCRX_VFNMI)) || - (env->irq_line_state & CPU_INTERRUPT_VFIQ); - - if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VFIQ)) { - if (new_state) { - cpu_interrupt(cs, CPU_INTERRUPT_VFIQ); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_VFIQ); - } - } -} - -void arm_cpu_update_vinmi(ARMCPU *cpu) -{ - /* - * Update the interrupt level for VINMI, which is the logical OR of - * the HCRX_EL2.VINMI bit and the input line level from the GIC. - */ - CPUARMState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - bool new_state = ((arm_hcr_el2_eff(env) & HCR_VI) && - (arm_hcrx_el2_eff(env) & HCRX_VINMI)) || - (env->irq_line_state & CPU_INTERRUPT_VINMI); - - if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VINMI)) { - if (new_state) { - cpu_interrupt(cs, CPU_INTERRUPT_VINMI); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_VINMI); - } - } -} - -void arm_cpu_update_vfnmi(ARMCPU *cpu) -{ - /* - * Update the interrupt level for VFNMI, which is the HCRX_EL2.VFNMI bit. - */ - CPUARMState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - bool new_state = (arm_hcr_el2_eff(env) & HCR_VF) && - (arm_hcrx_el2_eff(env) & HCRX_VFNMI); - - if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VFNMI)) { - if (new_state) { - cpu_interrupt(cs, CPU_INTERRUPT_VFNMI); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_VFNMI); - } - } -} - -void arm_cpu_update_vserr(ARMCPU *cpu) -{ - /* - * Update the interrupt level for VSERR, which is the HCR_EL2.VSE bit. - */ - CPUARMState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - bool new_state = env->cp15.hcr_el2 & HCR_VSE; - - if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VSERR)) { - if (new_state) { - cpu_interrupt(cs, CPU_INTERRUPT_VSERR); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_VSERR); - } - } -} - #ifndef CONFIG_USER_ONLY static void arm_cpu_set_irq(void *opaque, int irq, int level) { diff --git a/target/arm/el2-stubs.c b/target/arm/el2-stubs.c new file mode 100644 index 0000000000000..972023c337fd5 --- /dev/null +++ b/target/arm/el2-stubs.c @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* QEMU ARM CPU - user-mode emulation stubs for EL2 interrupts + * + * These should not really be needed, but CP registers for EL2 + * are not elided by user-mode emulation and they call these + * functions. Leave them as stubs until it's cleaned up. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "internals.h" + +void arm_cpu_update_virq(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void arm_cpu_update_vfiq(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void arm_cpu_update_vinmi(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void arm_cpu_update_vfnmi(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void arm_cpu_update_vserr(ARMCPU *cpu) +{ + g_assert_not_reached(); +} diff --git a/target/arm/helper.c b/target/arm/helper.c index 19637e7301b78..fb62742d983cd 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2868,8 +2868,12 @@ static void omap_threadid_write(CPUARMState *env, const ARMCPRegInfo *ri, static void omap_wfi_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else /* Wait-for-interrupt (deprecated) */ cpu_interrupt(env_cpu(env), CPU_INTERRUPT_HALT); +#endif } static void omap_cachemaint_write(CPUARMState *env, const ARMCPRegInfo *ri, diff --git a/target/arm/internals.h b/target/arm/internals.h index f5a1e75db376d..f0aa26c5117bd 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1292,6 +1292,11 @@ static inline const char *aarch32_mode_name(uint32_t psr) return cpu_mode_names[psr & 0xf]; } +/** + * arm_cpu_exec_interrupt(): Implementation of the cpu_exec_inrerrupt hook. + */ +bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request); + /** * arm_cpu_update_virq: Update CPU_INTERRUPT_VIRQ bit in cs->interrupt_request * diff --git a/target/arm/meson.build b/target/arm/meson.build index 07d9271aa4d88..914f1498fc54d 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -26,6 +26,7 @@ arm_user_ss.add(files( 'debug_helper.c', 'helper.c', 'vfp_fpscr.c', + 'el2-stubs.c', )) arm_common_system_ss.add(files('cpu.c')) @@ -38,6 +39,7 @@ arm_common_system_ss.add(files( 'arm-powerctl.c', 'cortex-regs.c', 'cpregs-pmu.c', + 'cpu-irq.c', 'debug_helper.c', 'helper.c', 'machine.c', From 11a73c6ea37483f7be85f6afebb4334d97d3050c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 12:49:00 +0200 Subject: [PATCH 0710/1794] user-exec: remove cpu_interrupt() stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/tcg/user-exec.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 748bfab04a70c..66c25fba7dde6 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -46,11 +46,6 @@ __thread uintptr_t helper_retaddr; //#define DEBUG_SIGNAL -void cpu_interrupt(CPUState *cpu, int mask) -{ - g_assert_not_reached(); -} - /* * Adjust the pc to pass to cpu_restore_state; return the memop type. */ From 602d5ebba26b245730a0b6a4855b1812d587725c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 12:09:09 +0200 Subject: [PATCH 0711/1794] treewide: clear bits of cs->interrupt_request with cpu_reset_interrupt() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Open coding cpu_reset_interrupt() can cause bugs if the BQL is not taken, for example i386 has the call chain kvm_cpu_exec() -> kvm_put_vcpu_events() -> kvm_arch_put_registers(). Reviewed-by: Igor Mammedov Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/tcg/cpu-exec.c | 6 +++--- hw/core/cpu-system.c | 2 +- target/avr/helper.c | 4 ++-- target/i386/hvf/x86hvf.c | 8 ++++---- target/i386/kvm/kvm.c | 14 +++++++------- target/i386/nvmm/nvmm-all.c | 10 +++++----- target/i386/tcg/system/seg_helper.c | 13 ++++++------- target/i386/tcg/system/svm_helper.c | 2 +- target/i386/whpx/whpx-all.c | 12 ++++++------ target/openrisc/sys_helper.c | 2 +- target/rx/helper.c | 4 ++-- target/s390x/tcg/excp_helper.c | 2 +- 12 files changed, 39 insertions(+), 40 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 8491e5badd18d..508d2d2d9e247 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -784,7 +784,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, if (unlikely(cpu_test_interrupt(cpu, ~0))) { bql_lock(); if (cpu_test_interrupt(cpu, CPU_INTERRUPT_DEBUG)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_DEBUG); cpu->exception_index = EXCP_DEBUG; bql_unlock(); return true; @@ -793,7 +793,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, /* Do nothing */ } else if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HALT)) { replay_interrupt(); - cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_HALT); cpu->halted = 1; cpu->exception_index = EXCP_HLT; bql_unlock(); @@ -840,7 +840,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, } } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_EXITTB)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_EXITTB); /* ensure that no TB jump will be modified as the program flow was changed */ *last_tb = NULL; diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index a975405d3a0a2..09c928c1f9234 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -204,7 +204,7 @@ static int cpu_common_post_load(void *opaque, int version_id) * 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the * version_id is increased. */ - cpu->interrupt_request &= ~0x01; + cpu_reset_interrupt(cpu, 0x01); tlb_flush(cpu); diff --git a/target/avr/helper.c b/target/avr/helper.c index b9cd6d5ef278b..4b29ab35263e1 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -47,7 +47,7 @@ bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) cs->exception_index = EXCP_RESET; avr_cpu_do_interrupt(cs); - cs->interrupt_request &= ~CPU_INTERRUPT_RESET; + cpu_reset_interrupt(cs, CPU_INTERRUPT_RESET); return true; } } @@ -59,7 +59,7 @@ bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) env->intsrc &= env->intsrc - 1; /* clear the interrupt */ if (!env->intsrc) { - cs->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } return true; } diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index 9e05e0e5765bf..a502437c3031e 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -397,7 +397,7 @@ bool hvf_inject_interrupts(CPUState *cs) if (cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { if (!(env->hflags2 & HF2_NMI_MASK) && !(info & VMCS_INTR_VALID)) { - cs->interrupt_request &= ~CPU_INTERRUPT_NMI; + cpu_reset_interrupt(cs, CPU_INTERRUPT_NMI); info = VMCS_INTR_VALID | VMCS_INTR_T_NMI | EXCP02_NMI; wvmcs(cs->accel->fd, VMCS_ENTRY_INTR_INFO, info); } else { @@ -409,7 +409,7 @@ bool hvf_inject_interrupts(CPUState *cs) cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK) && !(info & VMCS_INTR_VALID)) { int line = cpu_get_pic_interrupt(env); - cs->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); if (line >= 0) { wvmcs(cs->accel->fd, VMCS_ENTRY_INTR_INFO, line | VMCS_INTR_VALID | VMCS_INTR_T_HWINTR); @@ -437,7 +437,7 @@ int hvf_process_events(CPUState *cs) } if (cpu_test_interrupt(cs, CPU_INTERRUPT_POLL)) { - cs->interrupt_request &= ~CPU_INTERRUPT_POLL; + cpu_reset_interrupt(cs, CPU_INTERRUPT_POLL); apic_poll_irq(cpu->apic_state); } if ((cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && @@ -450,7 +450,7 @@ int hvf_process_events(CPUState *cs) do_cpu_sipi(cpu); } if (cpu_test_interrupt(cs, CPU_INTERRUPT_TPR)) { - cs->interrupt_request &= ~CPU_INTERRUPT_TPR; + cpu_reset_interrupt(cs, CPU_INTERRUPT_TPR); cpu_synchronize_state(cs); apic_handle_tpr_access_report(cpu->apic_state, env->eip, env->tpr_access_type); diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 306430a052133..8420c4090ef36 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5066,7 +5066,7 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level) */ events.smi.pending = cs->interrupt_request & CPU_INTERRUPT_SMI; events.smi.latched_init = cs->interrupt_request & CPU_INTERRUPT_INIT; - cs->interrupt_request &= ~(CPU_INTERRUPT_INIT | CPU_INTERRUPT_SMI); + cpu_reset_interrupt(cs, CPU_INTERRUPT_INIT | CPU_INTERRUPT_SMI); } else { /* Keep these in cs->interrupt_request. */ events.smi.pending = 0; @@ -5456,7 +5456,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) if (cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { if (cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { bql_lock(); - cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_NMI); bql_unlock(); DPRINTF("injected NMI\n"); ret = kvm_vcpu_ioctl(cpu, KVM_NMI); @@ -5467,7 +5467,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SMI)) { bql_lock(); - cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_SMI); bql_unlock(); DPRINTF("injected SMI\n"); ret = kvm_vcpu_ioctl(cpu, KVM_SMI); @@ -5502,7 +5502,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) bql_lock(); - cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD); irq = cpu_get_pic_interrupt(env); if (irq >= 0) { struct kvm_interrupt intr; @@ -5597,7 +5597,7 @@ int kvm_arch_process_async_events(CPUState *cs) /* We must not raise CPU_INTERRUPT_MCE if it's not supported. */ assert(env->mcg_cap); - cs->interrupt_request &= ~CPU_INTERRUPT_MCE; + cpu_reset_interrupt(cs, CPU_INTERRUPT_MCE); kvm_cpu_synchronize_state(cs); @@ -5627,7 +5627,7 @@ int kvm_arch_process_async_events(CPUState *cs) } if (cpu_test_interrupt(cs, CPU_INTERRUPT_POLL)) { - cs->interrupt_request &= ~CPU_INTERRUPT_POLL; + cpu_reset_interrupt(cs, CPU_INTERRUPT_POLL); apic_poll_irq(cpu->apic_state); } if ((cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && @@ -5640,7 +5640,7 @@ int kvm_arch_process_async_events(CPUState *cs) do_cpu_sipi(cpu); } if (cpu_test_interrupt(cs, CPU_INTERRUPT_TPR)) { - cs->interrupt_request &= ~CPU_INTERRUPT_TPR; + cpu_reset_interrupt(cs, CPU_INTERRUPT_TPR); kvm_cpu_synchronize_state(cs); apic_handle_tpr_access_report(cpu->apic_state, env->eip, env->tpr_access_type); diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index c1ac74c4f04dd..e1151b04c6e74 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -419,7 +419,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) if (!has_event && cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { if (nvmm_can_take_nmi(cpu)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_NMI); event->type = NVMM_VCPU_EVENT_INTR; event->vector = 2; has_event = true; @@ -428,7 +428,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) if (!has_event && cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { if (nvmm_can_take_int(cpu)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD); event->type = NVMM_VCPU_EVENT_INTR; event->vector = cpu_get_pic_interrupt(env); has_event = true; @@ -437,7 +437,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) /* Don't want SMIs. */ if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SMI)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_SMI); } if (sync_tpr) { @@ -697,7 +697,7 @@ nvmm_vcpu_loop(CPUState *cpu) /* set int/nmi windows back to the reset state */ } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_POLL)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_POLL; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); apic_poll_irq(x86_cpu->apic_state); } if ((cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && @@ -710,7 +710,7 @@ nvmm_vcpu_loop(CPUState *cpu) do_cpu_sipi(x86_cpu); } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_TPR; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_TPR); nvmm_cpu_synchronize_state(cpu); apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, env->tpr_access_type); diff --git a/target/i386/tcg/system/seg_helper.c b/target/i386/tcg/system/seg_helper.c index 794a23ddfc4b0..38072e51d721c 100644 --- a/target/i386/tcg/system/seg_helper.c +++ b/target/i386/tcg/system/seg_helper.c @@ -178,7 +178,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) */ switch (interrupt_request) { case CPU_INTERRUPT_POLL: - cs->interrupt_request &= ~CPU_INTERRUPT_POLL; + cpu_reset_interrupt(cs, CPU_INTERRUPT_POLL); apic_poll_irq(cpu->apic_state); break; case CPU_INTERRUPT_SIPI: @@ -186,23 +186,22 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) break; case CPU_INTERRUPT_SMI: cpu_svm_check_intercept_param(env, SVM_EXIT_SMI, 0, 0); - cs->interrupt_request &= ~CPU_INTERRUPT_SMI; + cpu_reset_interrupt(cs, CPU_INTERRUPT_SMI); do_smm_enter(cpu); break; case CPU_INTERRUPT_NMI: cpu_svm_check_intercept_param(env, SVM_EXIT_NMI, 0, 0); - cs->interrupt_request &= ~CPU_INTERRUPT_NMI; + cpu_reset_interrupt(cs, CPU_INTERRUPT_NMI); env->hflags2 |= HF2_NMI_MASK; do_interrupt_x86_hardirq(env, EXCP02_NMI, 1); break; case CPU_INTERRUPT_MCE: - cs->interrupt_request &= ~CPU_INTERRUPT_MCE; + cpu_reset_interrupt(cs, CPU_INTERRUPT_MCE); do_interrupt_x86_hardirq(env, EXCP12_MCHK, 0); break; case CPU_INTERRUPT_HARD: cpu_svm_check_intercept_param(env, SVM_EXIT_INTR, 0, 0); - cs->interrupt_request &= ~(CPU_INTERRUPT_HARD | - CPU_INTERRUPT_VIRQ); + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_VIRQ); intno = cpu_get_pic_interrupt(env); qemu_log_mask(CPU_LOG_INT, "Servicing hardware INT=0x%02x\n", intno); @@ -215,7 +214,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) qemu_log_mask(CPU_LOG_INT, "Servicing virtual hardware INT=0x%02x\n", intno); do_interrupt_x86_hardirq(env, intno, 1); - cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; + cpu_reset_interrupt(cs, CPU_INTERRUPT_VIRQ); env->int_ctl &= ~V_IRQ_MASK; break; } diff --git a/target/i386/tcg/system/svm_helper.c b/target/i386/tcg/system/svm_helper.c index 3569196bddadb..505788b0e26c6 100644 --- a/target/i386/tcg/system/svm_helper.c +++ b/target/i386/tcg/system/svm_helper.c @@ -824,7 +824,7 @@ void do_vmexit(CPUX86State *env) env->intercept_exceptions = 0; /* Clears the V_IRQ and V_INTR_MASKING bits inside the processor. */ - cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; + cpu_reset_interrupt(cs, CPU_INTERRUPT_VIRQ); env->int_ctl = 0; /* Clears the TSC_OFFSET inside the processor. */ diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 878cdd1668c5e..c09a0a64f229c 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -1471,14 +1471,14 @@ static void whpx_vcpu_pre_run(CPUState *cpu) if (!vcpu->interruption_pending && cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { if (cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_NMI); vcpu->interruptable = false; new_int.InterruptionType = WHvX64PendingNmi; new_int.InterruptionPending = 1; new_int.InterruptionVector = 2; } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SMI)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_SMI); } } @@ -1502,7 +1502,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) vcpu->interruptable && (env->eflags & IF_MASK)) { assert(!new_int.InterruptionPending); if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD); irq = cpu_get_pic_interrupt(env); if (irq >= 0) { new_int.InterruptionType = WHvX64PendingInterrupt; @@ -1520,7 +1520,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) } } else if (vcpu->ready_for_pic_interrupt && cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD); irq = cpu_get_pic_interrupt(env); if (irq >= 0) { reg_names[reg_count] = WHvRegisterPendingEvent; @@ -1607,7 +1607,7 @@ static void whpx_vcpu_process_async_events(CPUState *cpu) } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_POLL)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_POLL; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); apic_poll_irq(x86_cpu->apic_state); } @@ -1623,7 +1623,7 @@ static void whpx_vcpu_process_async_events(CPUState *cpu) } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_TPR; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_TPR); whpx_cpu_synchronize_state(cpu); apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, env->tpr_access_type); diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index d96b41a01c2b3..b091a9c6685a4 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -196,7 +196,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) env->ttmr = (rb & ~TTMR_IP) | ip; } else { /* Clear IP bit. */ env->ttmr = rb & ~TTMR_IP; - cs->interrupt_request &= ~CPU_INTERRUPT_TIMER; + cpu_reset_interrupt(cs, CPU_INTERRUPT_TIMER); } cpu_openrisc_timer_update(cpu); bql_unlock(); diff --git a/target/rx/helper.c b/target/rx/helper.c index ce003af421900..41c9606fd1d27 100644 --- a/target/rx/helper.c +++ b/target/rx/helper.c @@ -63,7 +63,7 @@ void rx_cpu_do_interrupt(CPUState *cs) env->bpsw = save_psw; env->pc = env->fintv; env->psw_ipl = 15; - cs->interrupt_request &= ~CPU_INTERRUPT_FIR; + cpu_reset_interrupt(cs, CPU_INTERRUPT_FIR); qemu_set_irq(env->ack, env->ack_irq); qemu_log_mask(CPU_LOG_INT, "fast interrupt raised\n"); } else if (do_irq & CPU_INTERRUPT_HARD) { @@ -73,7 +73,7 @@ void rx_cpu_do_interrupt(CPUState *cs) 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; - cs->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); qemu_set_irq(env->ack, env->ack_irq); qemu_log_mask(CPU_LOG_INT, "interrupt 0x%02x raised\n", env->ack_irq); diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index e4c75d0ce01b9..4c7faeee82b10 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -559,7 +559,7 @@ void s390_cpu_do_interrupt(CPUState *cs) /* we might still have pending interrupts, but not deliverable */ if (!env->pending_int && !qemu_s390_flic_has_any(flic)) { - cs->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } /* WAIT PSW during interrupt injection or STOP interrupt */ From 27e76d010104646c997d20ca0996fb5a046587b0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 11:26:05 +0200 Subject: [PATCH 0712/1794] cpu-common: use atomic access for interrupt_request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Writes to interrupt_request used non-atomic accesses, but there are a few cases where the access was not protected by the BQL. Now that there is a full set of helpers, it's easier to guarantee that interrupt_request accesses are fully atomic, so just drop the requirement instead of fixing them. Reviewed-by: Igor Mammedov Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- hw/core/cpu-common.c | 12 +----------- include/hw/core/cpu.h | 1 - system/cpus.c | 3 +-- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 259cf2a3c3614..152abc9024bc8 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -67,19 +67,9 @@ CPUState *cpu_create(const char *typename) return cpu; } -/* Resetting the IRQ comes from across the code base so we take the - * BQL here if we need to. cpu_interrupt assumes it is held.*/ void cpu_reset_interrupt(CPUState *cpu, int mask) { - bool need_lock = !bql_locked(); - - if (need_lock) { - bql_lock(); - } - cpu->interrupt_request &= ~mask; - if (need_lock) { - bql_unlock(); - } + qatomic_and(&cpu->interrupt_request, ~mask); } void cpu_exit(CPUState *cpu) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index b01a0cffd6460..23bd02277f484 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -495,7 +495,6 @@ struct CPUState { bool exit_request; int exclusive_context_count; uint32_t cflags_next_tb; - /* updates protected by BQL */ uint32_t interrupt_request; int singlestep_enabled; int64_t icount_budget; diff --git a/system/cpus.c b/system/cpus.c index 437848b5eb448..9bfbe2b0607ab 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -257,8 +257,7 @@ int64_t cpus_get_elapsed_ticks(void) void cpu_set_interrupt(CPUState *cpu, int mask) { /* Pairs with cpu_test_interrupt(). */ - qatomic_store_release(&cpu->interrupt_request, - cpu->interrupt_request | mask); + qatomic_or(&cpu->interrupt_request, mask); } void generic_handle_interrupt(CPUState *cpu, int mask) From 9e1ecd4aaaf9aa2f5b7caf364a10241a2cba02a8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 10:31:34 +0200 Subject: [PATCH 0713/1794] cpus: document that qemu_cpu_kick() can be used for BQL-less operation Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- include/hw/core/cpu.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 23bd02277f484..8b57bcd92c9b8 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -829,7 +829,8 @@ bool qemu_cpu_is_self(CPUState *cpu); * qemu_cpu_kick: * @cpu: The vCPU to kick. * - * Kicks @cpu's thread. + * Kicks @cpu's thread to exit the accelerator. For accelerators that + * can do that, the target vCPU thread will try not to take the BQL. */ void qemu_cpu_kick(CPUState *cpu); From ac6c8a390b451913995ee784ef7261b8928e5ace Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 1 Aug 2025 14:57:51 +0200 Subject: [PATCH 0714/1794] accel: use store_release/load_acquire for cross-thread exit_request Reads and writes cpu->exit_request do not use a load-acquire/store-release pair right now, but this means that cpu_exit() may not write cpu->exit_request after any flags that are read by the vCPU thread. Probably everything is protected one way or the other by the BQL, because cpu->exit_request leads to the slow path, where the CPU thread often takes the BQL (for example, to go to sleep by waiting on the BQL-protected cpu->halt_cond); but it's not clear, so use load-acquire/store-release consistently. Reviewed-by: Richard Henderson Reviewed-by: Peter Xu Reviewed-by: Igor Mammedov Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 19 +++++++++---------- accel/tcg/cpu-exec.c | 7 +++++-- accel/tcg/tcg-accel-ops-rr.c | 11 +++++++++-- hw/core/cpu-common.c | 3 ++- target/i386/nvmm/nvmm-all.c | 5 ++--- target/i386/whpx/whpx-all.c | 3 ++- 6 files changed, 29 insertions(+), 19 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index f36dfe3349236..bd9e5e3886ded 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3029,10 +3029,6 @@ static void kvm_eat_signals(CPUState *cpu) if (kvm_immediate_exit) { qatomic_set(&cpu->kvm_run->immediate_exit, 0); - /* Write kvm_run->immediate_exit before the cpu->exit_request - * write in kvm_cpu_exec. - */ - smp_wmb(); return; } @@ -3187,7 +3183,8 @@ int kvm_cpu_exec(CPUState *cpu) } kvm_arch_pre_run(cpu, run); - if (qatomic_read(&cpu->exit_request)) { + /* Corresponding store-release is in cpu_exit. */ + if (qatomic_load_acquire(&cpu->exit_request)) { trace_kvm_interrupt_exit_request(); /* * KVM requires us to reenter the kernel after IO exits to complete @@ -3197,13 +3194,15 @@ int kvm_cpu_exec(CPUState *cpu) kvm_cpu_kick_self(); } - /* Read cpu->exit_request before KVM_RUN reads run->immediate_exit. - * Matching barrier in kvm_eat_signals. - */ - smp_rmb(); - run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0); + /* + * After writing cpu->exit_request, cpu_exit() sends a signal that writes + * kvm->run->immediate_exit. The signal is already happening after the + * write to cpu->exit_request so, if KVM read kvm->run->immediate_exit + * as true, cpu->exit_request will always read as true. + */ + attrs = kvm_arch_post_run(cpu, run); #ifdef KVM_HAVE_MCE_INJECTION diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 508d2d2d9e247..f838535d111fa 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -851,8 +851,11 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, } #endif /* !CONFIG_USER_ONLY */ - /* Finally, check if we need to exit to the main loop. */ - if (unlikely(qatomic_read(&cpu->exit_request)) || icount_exit_request(cpu)) { + /* + * Finally, check if we need to exit to the main loop. + * The corresponding store-release is in cpu_exit. + */ + if (unlikely(qatomic_load_acquire(&cpu->exit_request)) || icount_exit_request(cpu)) { qatomic_set(&cpu->exit_request, 0); if (cpu->exception_index == -1) { cpu->exception_index = EXCP_INTERRUPT; diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 6eec5c9eee913..e8b0e370a8d60 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -242,10 +242,17 @@ static void *rr_cpu_thread_fn(void *arg) cpu = first_cpu; } - while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) { - /* Store rr_current_cpu before evaluating cpu_can_run(). */ + while (cpu && cpu_work_list_empty(cpu)) { + /* + * Store rr_current_cpu before evaluating cpu->exit_request. + * Pairs with rr_kick_next_cpu(). + */ qatomic_set_mb(&rr_current_cpu, cpu); + /* Pairs with store-release in cpu_exit. */ + if (qatomic_load_acquire(&cpu->exit_request)) { + break; + } current_cpu = cpu; qemu_clock_enable(QEMU_CLOCK_VIRTUAL, diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 152abc9024bc8..42463e6258df2 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -74,7 +74,8 @@ void cpu_reset_interrupt(CPUState *cpu, int mask) void cpu_exit(CPUState *cpu) { - qatomic_set(&cpu->exit_request, 1); + /* Ensure cpu_exec will see the reason why the exit request was set. */ + qatomic_store_release(&cpu->exit_request, true); /* Ensure cpu_exec will see the exit request after TCG has exited. */ smp_wmb(); qatomic_set(&cpu->neg.icount_decr.u16.high, -1); diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index e1151b04c6e74..10bd51d9b593b 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -743,7 +743,8 @@ nvmm_vcpu_loop(CPUState *cpu) nvmm_vcpu_pre_run(cpu); - if (qatomic_read(&cpu->exit_request)) { + /* Corresponding store-release is in cpu_exit. */ + if (qatomic_load_acquire(&cpu->exit_request)) { #if NVMM_USER_VERSION >= 2 nvmm_vcpu_stop(vcpu); #else @@ -751,8 +752,6 @@ nvmm_vcpu_loop(CPUState *cpu) #endif } - /* Read exit_request before the kernel reads the immediate exit flag */ - smp_rmb(); ret = nvmm_vcpu_run(mach, vcpu); if (ret == -1) { error_report("NVMM: Failed to exec a virtual processor," diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index c09a0a64f229c..2106c29c3a00a 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -1714,7 +1714,8 @@ static int whpx_vcpu_run(CPUState *cpu) if (exclusive_step_mode == WHPX_STEP_NONE) { whpx_vcpu_pre_run(cpu); - if (qatomic_read(&cpu->exit_request)) { + /* Corresponding store-release is in cpu_exit. */ + if (qatomic_load_acquire(&cpu->exit_request)) { whpx_vcpu_kick(cpu); } } From f084ff128b6136e1aebfd73e1cf7066a077a79bf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 Aug 2025 18:55:48 +0200 Subject: [PATCH 0715/1794] accel: use atomic accesses for exit_request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPU threads write exit_request as a "note to self" that they need to go out to a slow path. This write happens out of the BQL and can be a data race with another threads' cpu_exit(); use atomic accesses consistently. While at it, change the source argument from int ("1") to bool ("true"). Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu Reviewed-by: Igor Mammedov Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 2 +- accel/tcg/tcg-accel-ops-mttcg.c | 2 +- accel/tcg/tcg-accel-ops-rr.c | 4 ++-- hw/ppc/spapr_hcall.c | 6 +++--- include/hw/core/cpu.h | 9 +++++++++ target/i386/kvm/kvm.c | 6 +++--- target/i386/nvmm/nvmm-accel-ops.c | 2 +- target/i386/nvmm/nvmm-all.c | 2 +- target/i386/whpx/whpx-all.c | 6 +++--- 9 files changed, 24 insertions(+), 15 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index bd9e5e3886ded..e4167d94b4f01 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3730,7 +3730,7 @@ int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr) have_sigbus_pending = true; pending_sigbus_addr = addr; pending_sigbus_code = code; - qatomic_set(&cpu->exit_request, 1); + qatomic_set(&cpu->exit_request, true); return 0; #else return 1; diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index 337b993d3da91..b12b7a36b5df6 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -85,7 +85,7 @@ static void *mttcg_cpu_thread_fn(void *arg) qemu_guest_random_seed_thread_part2(cpu->random_seed); /* process any pending work */ - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, true); do { if (cpu_can_run(cpu)) { diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index e8b0e370a8d60..d13e0d8b44dc1 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -212,7 +212,7 @@ static void *rr_cpu_thread_fn(void *arg) cpu = first_cpu; /* process any pending work */ - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, true); while (1) { /* Only used for icount_enabled() */ @@ -293,7 +293,7 @@ static void *rr_cpu_thread_fn(void *arg) /* Does not need a memory barrier because a spurious wakeup is okay. */ qatomic_set(&rr_current_cpu, NULL); - if (cpu && cpu->exit_request) { + if (cpu && qatomic_read(&cpu->exit_request)) { qatomic_set_mb(&cpu->exit_request, 0); } diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 1e936f35e4488..51875e32a09bd 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -509,7 +509,7 @@ static target_ulong h_cede(PowerPCCPU *cpu, SpaprMachineState *spapr, if (!cpu_has_work(cs)) { cs->halted = 1; cs->exception_index = EXCP_HLT; - cs->exit_request = 1; + qatomic_set(&cs->exit_request, true); ppc_maybe_interrupt(env); } @@ -531,7 +531,7 @@ static target_ulong h_confer_self(PowerPCCPU *cpu) } cs->halted = 1; cs->exception_index = EXCP_HALTED; - cs->exit_request = 1; + qatomic_set(&cs->exit_request, true); ppc_maybe_interrupt(&cpu->env); return H_SUCCESS; @@ -624,7 +624,7 @@ static target_ulong h_confer(PowerPCCPU *cpu, SpaprMachineState *spapr, } cs->exception_index = EXCP_YIELD; - cs->exit_request = 1; + qatomic_set(&cs->exit_request, true); cpu_loop_exit(cs); return H_SUCCESS; diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 8b57bcd92c9b8..338757e5254e3 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -422,6 +422,15 @@ struct qemu_work_item; * valid under cpu_list_lock. * @created: Indicates whether the CPU thread has been successfully created. * @halt_cond: condition variable sleeping threads can wait on. + * @exit_request: Another thread requests the CPU to call qemu_wait_io_event(). + * Should be read only by CPU thread with load-acquire, to synchronize with + * other threads' store-release operation. + * + * In some cases, accelerator-specific code will write exit_request from + * within the same thread, to "bump" the effect of qemu_cpu_kick() to + * the one provided by cpu_exit(), especially when processing interrupt + * flags. In this case, the write and read happen in the same thread + * and the write therefore can use qemu_atomic_set(). * @interrupt_request: Indicates a pending interrupt request. * Only used by system emulation. * @halted: Nonzero if the CPU is in suspended state. diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 8420c4090ef36..34e74f2447096 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5486,10 +5486,10 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { - qatomic_set(&cpu->exit_request, 1); + qatomic_set(&cpu->exit_request, true); } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { - qatomic_set(&cpu->exit_request, 1); + qatomic_set(&cpu->exit_request, true); } } @@ -5604,7 +5604,7 @@ int kvm_arch_process_async_events(CPUState *cs) if (env->exception_nr == EXCP08_DBLE) { /* this means triple fault */ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - cs->exit_request = 1; + qatomic_set(&cs->exit_request, true); return 0; } kvm_queue_exception(env, EXCP12_MCHK, 0, 0); diff --git a/target/i386/nvmm/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c index 3799260bbdef9..86869f133e97e 100644 --- a/target/i386/nvmm/nvmm-accel-ops.c +++ b/target/i386/nvmm/nvmm-accel-ops.c @@ -77,7 +77,7 @@ static void nvmm_start_vcpu_thread(CPUState *cpu) */ static void nvmm_kick_vcpu_thread(CPUState *cpu) { - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, true); cpus_kick_thread(cpu); } diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 10bd51d9b593b..7e36c42fbb48c 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -414,7 +414,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) * or commit pending TPR access. */ if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, true); } if (!has_event && cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 2106c29c3a00a..00fb7e231006e 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -1489,10 +1489,10 @@ static void whpx_vcpu_pre_run(CPUState *cpu) if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, true); } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, true); } } @@ -1539,7 +1539,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) if (tpr != vcpu->tpr) { vcpu->tpr = tpr; reg_values[reg_count].Reg64 = tpr; - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, true); reg_names[reg_count] = WHvX64RegisterCr8; reg_count += 1; } From 9cf342b491f6872d316b7b0c3cbc0f6157f28797 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 11 Aug 2025 08:28:31 +0200 Subject: [PATCH 0716/1794] accel/tcg: create a thread-kick function for TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round-robin TCG is calling into cpu_exit() directly. In preparation for making cpu_exit() usable from all accelerators, define a generic thread-kick function for TCG which is used directly in the multi-threaded case, and through CPU_FOREACH in the round-robin case. Use it also for user-mode emulation, and take the occasion to move the implementation to accel/tcg/user-exec.c. Reviewed-by: Igor Mammedov Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/tcg/cpu-exec.c | 6 ++++++ accel/tcg/tcg-accel-ops-mttcg.c | 5 ----- accel/tcg/tcg-accel-ops-mttcg.h | 3 --- accel/tcg/tcg-accel-ops-rr.c | 2 +- accel/tcg/tcg-accel-ops.c | 2 +- accel/tcg/tcg-accel-ops.h | 1 + accel/tcg/user-exec.c | 6 ++++++ bsd-user/main.c | 5 ----- docs/devel/tcg-icount.rst | 2 +- linux-user/main.c | 5 ----- 10 files changed, 16 insertions(+), 21 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index f838535d111fa..9241bcadb5f13 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -40,6 +40,7 @@ #include "exec/replay-core.h" #include "system/tcg.h" #include "exec/helper-proto-common.h" +#include "tcg-accel-ops.h" #include "tb-jmp-cache.h" #include "tb-hash.h" #include "tb-context.h" @@ -748,6 +749,11 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) return false; } +void tcg_kick_vcpu_thread(CPUState *cpu) +{ + cpu_exit(cpu); +} + static inline bool icount_exit_request(CPUState *cpu) { if (!icount_enabled()) { diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index b12b7a36b5df6..1148ebcaae5ef 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -123,11 +123,6 @@ static void *mttcg_cpu_thread_fn(void *arg) return NULL; } -void mttcg_kick_vcpu_thread(CPUState *cpu) -{ - cpu_exit(cpu); -} - void mttcg_start_vcpu_thread(CPUState *cpu) { char thread_name[VCPU_THREAD_NAME_SIZE]; diff --git a/accel/tcg/tcg-accel-ops-mttcg.h b/accel/tcg/tcg-accel-ops-mttcg.h index 8ffa7a9a9fe0d..5c145cc85955a 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.h +++ b/accel/tcg/tcg-accel-ops-mttcg.h @@ -10,9 +10,6 @@ #ifndef TCG_ACCEL_OPS_MTTCG_H #define TCG_ACCEL_OPS_MTTCG_H -/* kick MTTCG vCPU thread */ -void mttcg_kick_vcpu_thread(CPUState *cpu); - /* start an mttcg vCPU thread */ void mttcg_start_vcpu_thread(CPUState *cpu); diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index d13e0d8b44dc1..a1d75fd341948 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -43,7 +43,7 @@ void rr_kick_vcpu_thread(CPUState *unused) CPUState *cpu; CPU_FOREACH(cpu) { - cpu_exit(cpu); + tcg_kick_vcpu_thread(cpu); }; } diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 9c37266c1e046..1f662a9c7452b 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -206,7 +206,7 @@ static void tcg_accel_ops_init(AccelClass *ac) if (qemu_tcg_mttcg_enabled()) { ops->create_vcpu_thread = mttcg_start_vcpu_thread; - ops->kick_vcpu_thread = mttcg_kick_vcpu_thread; + ops->kick_vcpu_thread = tcg_kick_vcpu_thread; ops->handle_interrupt = tcg_handle_interrupt; } else { ops->create_vcpu_thread = rr_start_vcpu_thread; diff --git a/accel/tcg/tcg-accel-ops.h b/accel/tcg/tcg-accel-ops.h index 6feeb3f3e9b20..aecce605d7b7a 100644 --- a/accel/tcg/tcg-accel-ops.h +++ b/accel/tcg/tcg-accel-ops.h @@ -18,5 +18,6 @@ void tcg_cpu_destroy(CPUState *cpu); int tcg_cpu_exec(CPUState *cpu); void tcg_handle_interrupt(CPUState *cpu, int mask); void tcg_cpu_init_cflags(CPUState *cpu, bool parallel); +void tcg_kick_vcpu_thread(CPUState *cpu); #endif /* TCG_ACCEL_OPS_H */ diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 66c25fba7dde6..3c072fd868fb1 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -38,6 +38,7 @@ #include "qemu/int128.h" #include "trace.h" #include "tcg/tcg-ldst.h" +#include "tcg-accel-ops.h" #include "backend-ldst.h" #include "internal-common.h" #include "tb-internal.h" @@ -46,6 +47,11 @@ __thread uintptr_t helper_retaddr; //#define DEBUG_SIGNAL +void qemu_cpu_kick(CPUState *cpu) +{ + tcg_kick_vcpu_thread(cpu); +} + /* * Adjust the pc to pass to cpu_restore_state; return the memop type. */ diff --git a/bsd-user/main.c b/bsd-user/main.c index 9ba69642f500d..73aae8c3274d9 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -214,11 +214,6 @@ bool qemu_cpu_is_self(CPUState *cpu) return thread_cpu == cpu; } -void qemu_cpu_kick(CPUState *cpu) -{ - cpu_exit(cpu); -} - /* Assumes contents are already zeroed. */ static void init_task_state(TaskState *ts) { diff --git a/docs/devel/tcg-icount.rst b/docs/devel/tcg-icount.rst index 7df883446a74f..a1dcd79e0fdd5 100644 --- a/docs/devel/tcg-icount.rst +++ b/docs/devel/tcg-icount.rst @@ -37,7 +37,7 @@ translator starts by allocating a budget of instructions to be executed. The budget of instructions is limited by how long it will be until the next timer will expire. We store this budget as part of a vCPU icount_decr field which shared with the machinery for handling -cpu_exit(). The whole field is checked at the start of every +qemu_cpu_kick(). The whole field is checked at the start of every translated block and will cause a return to the outer loop to deal with whatever caused the exit. diff --git a/linux-user/main.c b/linux-user/main.c index 7b0ccb6fd6090..4ddfc9a619cbb 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -189,11 +189,6 @@ bool qemu_cpu_is_self(CPUState *cpu) return thread_cpu == cpu; } -void qemu_cpu_kick(CPUState *cpu) -{ - cpu_exit(cpu); -} - void task_settid(TaskState *ts) { if (ts->ts_tid == 0) { From 61d996da508fe4082f1cbfd9b51c8c47f535a993 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 11 Aug 2025 08:33:40 +0200 Subject: [PATCH 0717/1794] accel/tcg: inline cpu_exit() Right now, cpu_exit() is not usable from all accelerators because it includes a TCG-specific thread kick. In fact, cpu_exit() doubles as the TCG thread-kick via tcg_kick_vcpu_thread(). In preparation for changing that, inline cpu_exit() into tcg_kick_vcpu_thread(). The direction of the calls can then be reversed, with an accelerator-independent cpu_exit() calling into qemu_vcpu_kick() rather than the opposite. Reviewed-by: Igor Mammedov Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/tcg/cpu-exec.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 9241bcadb5f13..3ae545e888f29 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -751,7 +751,16 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) void tcg_kick_vcpu_thread(CPUState *cpu) { - cpu_exit(cpu); + /* + * Ensure cpu_exec will see the reason why the exit request was set. + * FIXME: this is not always needed. Other accelerators instead + * read interrupt_request and set exit_request on demand from the + * CPU thread; see kvm_arch_pre_run() for example. + */ + qatomic_store_release(&cpu->exit_request, true); + + /* Ensure cpu_exec will see the exit request after TCG has exited. */ + qatomic_store_release(&cpu->neg.icount_decr.u16.high, -1); } static inline bool icount_exit_request(CPUState *cpu) @@ -780,7 +789,8 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, /* Clear the interrupt flag now since we're processing * cpu->interrupt_request and cpu->exit_request. * Ensure zeroing happens before reading cpu->exit_request or - * cpu->interrupt_request (see also smp_wmb in cpu_exit()) + * cpu->interrupt_request (see also store-release in + * tcg_kick_vcpu_thread()) */ qatomic_set_mb(&cpu->neg.icount_decr.u16.high, 0); From dcb46ecb2e90d532fcdc04702c92e732a0ef77e8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 1 Aug 2025 13:24:48 +0200 Subject: [PATCH 0718/1794] cpus: remove TCG-ism from cpu_exit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that TCG has its own kick function, make cpu_exit() do the right kick for all accelerators. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Igor Mammedov Signed-off-by: Paolo Bonzini --- hw/core/cpu-common.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 42463e6258df2..41a339903ca79 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -76,9 +76,7 @@ void cpu_exit(CPUState *cpu) { /* Ensure cpu_exec will see the reason why the exit request was set. */ qatomic_store_release(&cpu->exit_request, true); - /* Ensure cpu_exec will see the exit request after TCG has exited. */ - smp_wmb(); - qatomic_set(&cpu->neg.icount_decr.u16.high, -1); + qemu_cpu_kick(cpu); } static int cpu_common_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) From f8217ae54e4c44a7f0d20d56a5368ec1818f1cc2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 1 Aug 2025 13:50:04 +0200 Subject: [PATCH 0719/1794] cpus: properly kick CPUs out of inner execution loop Now that cpu_exit() actually kicks all accelerators, use it whenever the message to another thread is processed in qemu_wait_io_event(). Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- cpu-common.c | 3 ++- hw/ppc/ppc.c | 2 ++ hw/ppc/spapr_hcall.c | 7 +++---- hw/ppc/spapr_rtas.c | 2 +- replay/replay-events.c | 3 ++- system/cpu-timers.c | 6 +++--- system/cpus.c | 5 +++-- target/arm/tcg/mte_helper.c | 2 +- target/i386/kvm/hyperv.c | 1 - 9 files changed, 17 insertions(+), 14 deletions(-) diff --git a/cpu-common.c b/cpu-common.c index ef5757d23bf68..152661df8e9cd 100644 --- a/cpu-common.c +++ b/cpu-common.c @@ -137,7 +137,8 @@ static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi) wi->done = false; qemu_mutex_unlock(&cpu->work_mutex); - qemu_cpu_kick(cpu); + /* exit the inner loop and reach qemu_wait_io_event_common(). */ + cpu_exit(cpu); } void do_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data, diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 43d0d0e7553d9..3e436c704139e 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -190,6 +190,7 @@ static void ppc970_set_irq(void *opaque, int pin, int level) if (level) { trace_ppc_irq_cpu("stop"); cs->halted = 1; + cpu_exit(cs); } else { trace_ppc_irq_cpu("restart"); cs->halted = 0; @@ -386,6 +387,7 @@ static void ppc40x_set_irq(void *opaque, int pin, int level) if (level) { trace_ppc_irq_cpu("stop"); cs->halted = 1; + cpu_exit(cs); } else { trace_ppc_irq_cpu("restart"); cs->halted = 0; diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 51875e32a09bd..c594d4b916eef 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -509,8 +509,8 @@ static target_ulong h_cede(PowerPCCPU *cpu, SpaprMachineState *spapr, if (!cpu_has_work(cs)) { cs->halted = 1; cs->exception_index = EXCP_HLT; - qatomic_set(&cs->exit_request, true); ppc_maybe_interrupt(env); + cpu_exit(cs); } return H_SUCCESS; @@ -531,8 +531,8 @@ static target_ulong h_confer_self(PowerPCCPU *cpu) } cs->halted = 1; cs->exception_index = EXCP_HALTED; - qatomic_set(&cs->exit_request, true); ppc_maybe_interrupt(&cpu->env); + cpu_exit(cs); return H_SUCCESS; } @@ -624,8 +624,7 @@ static target_ulong h_confer(PowerPCCPU *cpu, SpaprMachineState *spapr, } cs->exception_index = EXCP_YIELD; - qatomic_set(&cs->exit_request, true); - cpu_loop_exit(cs); + cpu_exit(cs); return H_SUCCESS; } diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 78309dbb09dc4..143bc8c379479 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -221,7 +221,7 @@ static void rtas_stop_self(PowerPCCPU *cpu, SpaprMachineState *spapr, cs->halted = 1; ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm); kvmppc_set_reg_ppc_online(cpu, 0); - qemu_cpu_kick(cs); + cpu_exit(cs); } static void rtas_ibm_suspend_me(PowerPCCPU *cpu, SpaprMachineState *spapr, diff --git a/replay/replay-events.c b/replay/replay-events.c index 8959da9f1fab7..a96e47e774036 100644 --- a/replay/replay-events.c +++ b/replay/replay-events.c @@ -118,7 +118,8 @@ void replay_add_event(ReplayAsyncEventKind event_kind, g_assert(replay_mutex_locked()); QTAILQ_INSERT_TAIL(&events_list, event, events); - qemu_cpu_kick(first_cpu); + /* Kick the TCG thread out of tcg_cpu_exec(). */ + cpu_exit(first_cpu); } void replay_bh_schedule_event(QEMUBH *bh) diff --git a/system/cpu-timers.c b/system/cpu-timers.c index cb35fa62b8a41..9919b46230f1c 100644 --- a/system/cpu-timers.c +++ b/system/cpu-timers.c @@ -246,14 +246,14 @@ void qemu_timer_notify_cb(void *opaque, QEMUClockType type) if (qemu_in_vcpu_thread()) { /* - * A CPU is currently running; kick it back out to the + * A CPU is currently running; send it out of the * tcg_cpu_exec() loop so it will recalculate its * icount deadline immediately. */ - qemu_cpu_kick(current_cpu); + cpu_exit(current_cpu); } else if (first_cpu) { /* - * qemu_cpu_kick is not enough to kick a halted CPU out of + * cpu_exit() is not enough to kick a halted CPU out of * qemu_tcg_wait_io_event. async_run_on_cpu, instead, * causes cpu_thread_is_idle to return false. This way, * handle_icount_deadline can run. diff --git a/system/cpus.c b/system/cpus.c index 9bfbe2b0607ab..bb13942cbb7dc 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -604,7 +604,7 @@ void cpu_pause(CPUState *cpu) qemu_cpu_stop(cpu, true); } else { cpu->stop = true; - qemu_cpu_kick(cpu); + cpu_exit(cpu); } } @@ -644,6 +644,7 @@ void pause_all_vcpus(void) while (!all_vcpus_paused()) { qemu_cond_wait(&qemu_pause_cond, &bql); + /* FIXME: is this needed? */ CPU_FOREACH(cpu) { qemu_cpu_kick(cpu); } @@ -672,7 +673,7 @@ void cpu_remove_sync(CPUState *cpu) { cpu->stop = true; cpu->unplug = true; - qemu_cpu_kick(cpu); + cpu_exit(cpu); bql_unlock(); qemu_thread_join(cpu->thread); bql_lock(); diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 0efc18a181ea4..302e899287c73 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -591,7 +591,7 @@ static void mte_async_check_fail(CPUARMState *env, uint64_t dirty_ptr, * which is rather sooner than "normal". But the alternative * is waiting until the next syscall. */ - qemu_cpu_kick(env_cpu(env)); + cpu_exit(env_cpu(env)); #endif } diff --git a/target/i386/kvm/hyperv.c b/target/i386/kvm/hyperv.c index 9865120cc434d..f7a81bd270036 100644 --- a/target/i386/kvm/hyperv.c +++ b/target/i386/kvm/hyperv.c @@ -81,7 +81,6 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit) * necessary because memory hierarchy is being changed */ async_safe_run_on_cpu(CPU(cpu), async_synic_update, RUN_ON_CPU_NULL); - cpu_exit(CPU(cpu)); return EXCP_INTERRUPT; case KVM_EXIT_HYPERV_HCALL: { From 871de7078fcaf597605576b97b32fab14722ea43 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 2 Sep 2025 07:17:09 +0200 Subject: [PATCH 0720/1794] treewide: rename qemu_wait_io_event/qemu_wait_io_event_common Do so before extending it to the user-mode emulators, where there is no such thing as an "I/O thread". Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/dummy-cpus.c | 2 +- accel/hvf/hvf-accel-ops.c | 2 +- accel/kvm/kvm-accel-ops.c | 2 +- accel/tcg/tcg-accel-ops-mttcg.c | 2 +- accel/tcg/tcg-accel-ops-rr.c | 4 ++-- cpu-common.c | 2 +- include/hw/core/cpu.h | 2 +- include/system/cpus.h | 4 ++-- system/cpus.c | 6 +++--- target/i386/nvmm/nvmm-accel-ops.c | 2 +- target/i386/whpx/whpx-accel-ops.c | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/accel/dummy-cpus.c b/accel/dummy-cpus.c index 03cfc0fa01e65..225a47c31fdb5 100644 --- a/accel/dummy-cpus.c +++ b/accel/dummy-cpus.c @@ -57,7 +57,7 @@ static void *dummy_cpu_thread_fn(void *arg) qemu_sem_wait(&cpu->sem); #endif bql_lock(); - qemu_wait_io_event(cpu); + qemu_process_cpu_events(cpu); } while (!cpu->unplug); bql_unlock(); diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index d488d6afbacf6..7a27bdadb4fde 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -198,7 +198,7 @@ static void *hvf_cpu_thread_fn(void *arg) cpu_handle_guest_debug(cpu); } } - qemu_wait_io_event(cpu); + qemu_process_cpu_events(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); hvf_vcpu_destroy(cpu); diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index b709187c7d769..65a7f76a69ad6 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -53,7 +53,7 @@ static void *kvm_vcpu_thread_fn(void *arg) cpu_handle_guest_debug(cpu); } } - qemu_wait_io_event(cpu); + qemu_process_cpu_events(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); kvm_destroy_vcpu(cpu); diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index 1148ebcaae5ef..342917c5f6e8c 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -113,7 +113,7 @@ static void *mttcg_cpu_thread_fn(void *arg) } } - qemu_wait_io_event(cpu); + qemu_process_cpu_events(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); tcg_cpu_destroy(cpu); diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index a1d75fd341948..813b313859ad9 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -117,7 +117,7 @@ static void rr_wait_io_event(void) rr_start_kick_timer(); CPU_FOREACH(cpu) { - qemu_wait_io_event_common(cpu); + qemu_process_cpu_events_common(cpu); } } @@ -203,7 +203,7 @@ static void *rr_cpu_thread_fn(void *arg) /* process any pending work */ CPU_FOREACH(cpu) { current_cpu = cpu; - qemu_wait_io_event_common(cpu); + qemu_process_cpu_events_common(cpu); } } diff --git a/cpu-common.c b/cpu-common.c index 152661df8e9cd..0eb5c7b8f247d 100644 --- a/cpu-common.c +++ b/cpu-common.c @@ -137,7 +137,7 @@ static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi) wi->done = false; qemu_mutex_unlock(&cpu->work_mutex); - /* exit the inner loop and reach qemu_wait_io_event_common(). */ + /* exit the inner loop and reach qemu_process_cpu_events_common(). */ cpu_exit(cpu); } diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 338757e5254e3..6075be0b59fa1 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -422,7 +422,7 @@ struct qemu_work_item; * valid under cpu_list_lock. * @created: Indicates whether the CPU thread has been successfully created. * @halt_cond: condition variable sleeping threads can wait on. - * @exit_request: Another thread requests the CPU to call qemu_wait_io_event(). + * @exit_request: Another thread requests the CPU to call qemu_process_cpu_events(). * Should be read only by CPU thread with load-acquire, to synchronize with * other threads' store-release operation. * diff --git a/include/system/cpus.h b/include/system/cpus.h index 69be6a77a75ac..4aebec48705d7 100644 --- a/include/system/cpus.h +++ b/include/system/cpus.h @@ -17,8 +17,8 @@ bool cpu_work_list_empty(CPUState *cpu); bool cpu_thread_is_idle(CPUState *cpu); bool all_cpu_threads_idle(void); bool cpu_can_run(CPUState *cpu); -void qemu_wait_io_event_common(CPUState *cpu); -void qemu_wait_io_event(CPUState *cpu); +void qemu_process_cpu_events_common(CPUState *cpu); +void qemu_process_cpu_events(CPUState *cpu); void cpu_thread_signal_created(CPUState *cpu); void cpu_thread_signal_destroyed(CPUState *cpu); void cpu_handle_guest_debug(CPUState *cpu); diff --git a/system/cpus.c b/system/cpus.c index bb13942cbb7dc..fd804e073278a 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -450,7 +450,7 @@ static void qemu_cpu_stop(CPUState *cpu, bool exit) qemu_cond_broadcast(&qemu_pause_cond); } -void qemu_wait_io_event_common(CPUState *cpu) +void qemu_process_cpu_events_common(CPUState *cpu) { qatomic_set_mb(&cpu->thread_kicked, false); if (cpu->stop) { @@ -459,7 +459,7 @@ void qemu_wait_io_event_common(CPUState *cpu) process_queued_cpu_work(cpu); } -void qemu_wait_io_event(CPUState *cpu) +void qemu_process_cpu_events(CPUState *cpu) { bool slept = false; @@ -474,7 +474,7 @@ void qemu_wait_io_event(CPUState *cpu) qemu_plugin_vcpu_resume_cb(cpu); } - qemu_wait_io_event_common(cpu); + qemu_process_cpu_events_common(cpu); } void cpus_kick_thread(CPUState *cpu) diff --git a/target/i386/nvmm/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c index 86869f133e97e..d066364b98934 100644 --- a/target/i386/nvmm/nvmm-accel-ops.c +++ b/target/i386/nvmm/nvmm-accel-ops.c @@ -51,7 +51,7 @@ static void *qemu_nvmm_cpu_thread_fn(void *arg) while (cpu_thread_is_idle(cpu)) { qemu_cond_wait_bql(cpu->halt_cond); } - qemu_wait_io_event_common(cpu); + qemu_process_cpu_events_common(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); nvmm_destroy_vcpu(cpu); diff --git a/target/i386/whpx/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c index da58805b1a65e..2ca4ee02636c8 100644 --- a/target/i386/whpx/whpx-accel-ops.c +++ b/target/i386/whpx/whpx-accel-ops.c @@ -51,7 +51,7 @@ static void *whpx_cpu_thread_fn(void *arg) while (cpu_thread_is_idle(cpu)) { qemu_cond_wait_bql(cpu->halt_cond); } - qemu_wait_io_event_common(cpu); + qemu_process_cpu_events_common(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); whpx_destroy_vcpu(cpu); From 758e5de501f78d4ef53a43e0c5a4783cd807b5be Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 16:53:05 +0200 Subject: [PATCH 0721/1794] bsd-user, linux-user: introduce qemu_process_cpu_events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a user-mode emulation version of the function. More will be added later, for now it is just process_queued_cpu_work. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/tcg/user-exec.c | 5 +++++ bsd-user/aarch64/target_arch_cpu.h | 2 +- bsd-user/arm/target_arch_cpu.h | 2 +- bsd-user/i386/target_arch_cpu.h | 2 +- bsd-user/riscv/target_arch_cpu.h | 2 +- bsd-user/x86_64/target_arch_cpu.h | 2 +- include/hw/core/cpu.h | 9 +++++++++ include/system/cpus.h | 1 - linux-user/aarch64/cpu_loop.c | 2 +- linux-user/alpha/cpu_loop.c | 2 +- linux-user/arm/cpu_loop.c | 2 +- linux-user/hexagon/cpu_loop.c | 2 +- linux-user/hppa/cpu_loop.c | 2 +- linux-user/i386/cpu_loop.c | 2 +- linux-user/loongarch64/cpu_loop.c | 2 +- linux-user/m68k/cpu_loop.c | 2 +- linux-user/microblaze/cpu_loop.c | 2 +- linux-user/mips/cpu_loop.c | 2 +- linux-user/openrisc/cpu_loop.c | 2 +- linux-user/ppc/cpu_loop.c | 2 +- linux-user/riscv/cpu_loop.c | 2 +- linux-user/s390x/cpu_loop.c | 2 +- linux-user/sh4/cpu_loop.c | 2 +- linux-user/sparc/cpu_loop.c | 2 +- linux-user/xtensa/cpu_loop.c | 2 +- 25 files changed, 36 insertions(+), 23 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 3c072fd868fb1..65f5da6c5068c 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -52,6 +52,11 @@ void qemu_cpu_kick(CPUState *cpu) tcg_kick_vcpu_thread(cpu); } +void qemu_process_cpu_events(CPUState *cpu) +{ + process_queued_cpu_work(cpu); +} + /* * Adjust the pc to pass to cpu_restore_state; return the memop type. */ diff --git a/bsd-user/aarch64/target_arch_cpu.h b/bsd-user/aarch64/target_arch_cpu.h index 87fbf6d67755d..15df84fda2194 100644 --- a/bsd-user/aarch64/target_arch_cpu.h +++ b/bsd-user/aarch64/target_arch_cpu.h @@ -54,7 +54,7 @@ static inline G_NORETURN void target_cpu_loop(CPUARMState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_SWI: diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index bc2eaa0bf4e7f..9a952ef0ff7d2 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -46,7 +46,7 @@ static inline G_NORETURN void target_cpu_loop(CPUARMState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_UDEF: case EXCP_NOCP: diff --git a/bsd-user/i386/target_arch_cpu.h b/bsd-user/i386/target_arch_cpu.h index 5d4c931decdbd..f147d5b6f85f5 100644 --- a/bsd-user/i386/target_arch_cpu.h +++ b/bsd-user/i386/target_arch_cpu.h @@ -113,7 +113,7 @@ static inline G_NORETURN void target_cpu_loop(CPUX86State *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case 0x80: { diff --git a/bsd-user/riscv/target_arch_cpu.h b/bsd-user/riscv/target_arch_cpu.h index ef92f004803ba..ad428d0263d9b 100644 --- a/bsd-user/riscv/target_arch_cpu.h +++ b/bsd-user/riscv/target_arch_cpu.h @@ -49,7 +49,7 @@ static inline G_NORETURN void target_cpu_loop(CPURISCVState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); signo = 0; diff --git a/bsd-user/x86_64/target_arch_cpu.h b/bsd-user/x86_64/target_arch_cpu.h index f82042e30afae..1fa71d87f1249 100644 --- a/bsd-user/x86_64/target_arch_cpu.h +++ b/bsd-user/x86_64/target_arch_cpu.h @@ -121,7 +121,7 @@ static inline G_NORETURN void target_cpu_loop(CPUX86State *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_SYSCALL: diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 6075be0b59fa1..fb788ca1107b1 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1145,6 +1145,15 @@ AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx); G_NORETURN void cpu_abort(CPUState *cpu, const char *fmt, ...) G_GNUC_PRINTF(2, 3); +/** + * qemu_process_cpu_events: + * @cpu: CPU that left the execution loop + * + * Perform accelerator-independent work after the CPU has left + * the inner execution loop. + */ +void qemu_process_cpu_events(CPUState *cpu); + /* $(top_srcdir)/cpu.c */ void cpu_class_init_props(DeviceClass *dc); void cpu_exec_class_post_init(CPUClass *cc); diff --git a/include/system/cpus.h b/include/system/cpus.h index 4aebec48705d7..508444ccf1c30 100644 --- a/include/system/cpus.h +++ b/include/system/cpus.h @@ -18,7 +18,6 @@ bool cpu_thread_is_idle(CPUState *cpu); bool all_cpu_threads_idle(void); bool cpu_can_run(CPUState *cpu); void qemu_process_cpu_events_common(CPUState *cpu); -void qemu_process_cpu_events(CPUState *cpu); void cpu_thread_signal_created(CPUState *cpu); void cpu_thread_signal_destroyed(CPUState *cpu); void cpu_handle_guest_debug(CPUState *cpu); diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 4c4921152e84a..f6b498c6c432e 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -38,7 +38,7 @@ void cpu_loop(CPUARMState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_SWI: diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c index 728b64906d945..bb8346b509456 100644 --- a/linux-user/alpha/cpu_loop.c +++ b/linux-user/alpha/cpu_loop.c @@ -35,7 +35,7 @@ void cpu_loop(CPUAlphaState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_RESET: diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 9aeb9b0087f5f..cd89b7d6f5e88 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -295,7 +295,7 @@ void cpu_loop(CPUARMState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch(trapnr) { case EXCP_UDEF: diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index 25c97edcaef0c..1941f4c9c1625 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -36,7 +36,7 @@ void cpu_loop(CPUHexagonState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_INTERRUPT: diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c index 3af50653bb775..356cb48acc300 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -119,7 +119,7 @@ void cpu_loop(CPUHPPAState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_SYSCALL: diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c index 7b2d8b03d8413..f3f58576af5c6 100644 --- a/linux-user/i386/cpu_loop.c +++ b/linux-user/i386/cpu_loop.c @@ -214,7 +214,7 @@ void cpu_loop(CPUX86State *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch(trapnr) { case 0x80: diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c index a0a4cbb7cc312..26a5ce3a936c4 100644 --- a/linux-user/loongarch64/cpu_loop.c +++ b/linux-user/loongarch64/cpu_loop.c @@ -27,7 +27,7 @@ void cpu_loop(CPULoongArchState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_INTERRUPT: diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index aca0bf23dc66e..2c9f628241f4c 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -33,7 +33,7 @@ void cpu_loop(CPUM68KState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch(trapnr) { case EXCP_ILLEGAL: diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c index d8277961c7368..78506ab23d9bb 100644 --- a/linux-user/microblaze/cpu_loop.c +++ b/linux-user/microblaze/cpu_loop.c @@ -32,7 +32,7 @@ void cpu_loop(CPUMBState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_INTERRUPT: diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index e67b8a2e46320..2365de1de1a2f 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -74,7 +74,7 @@ void cpu_loop(CPUMIPSState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch(trapnr) { case EXCP_SYSCALL: diff --git a/linux-user/openrisc/cpu_loop.c b/linux-user/openrisc/cpu_loop.c index 8c72347a99a43..2167d880d5551 100644 --- a/linux-user/openrisc/cpu_loop.c +++ b/linux-user/openrisc/cpu_loop.c @@ -33,7 +33,7 @@ void cpu_loop(CPUOpenRISCState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_SYSCALL: diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index 22885ffd90606..b0b0cb14b41d9 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -77,7 +77,7 @@ void cpu_loop(CPUPPCState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); arch_interrupt = true; switch (trapnr) { diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index b3162815320b9..ce542540c28bb 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -36,7 +36,7 @@ void cpu_loop(CPURISCVState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_INTERRUPT: diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c index 49e44548f85a0..4929b32e1fcce 100644 --- a/linux-user/s390x/cpu_loop.c +++ b/linux-user/s390x/cpu_loop.c @@ -64,7 +64,7 @@ void cpu_loop(CPUS390XState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_INTERRUPT: diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c index 259ea1cc8bb47..0c9d7e9c46b5f 100644 --- a/linux-user/sh4/cpu_loop.c +++ b/linux-user/sh4/cpu_loop.c @@ -34,7 +34,7 @@ void cpu_loop(CPUSH4State *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case 0x160: diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index 7d30cd1ff22e6..7391e2add8d4a 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -220,7 +220,7 @@ void cpu_loop (CPUSPARCState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case TARGET_TT_SYSCALL: diff --git a/linux-user/xtensa/cpu_loop.c b/linux-user/xtensa/cpu_loop.c index 43a194fc4a4ad..a0ff10eff82f2 100644 --- a/linux-user/xtensa/cpu_loop.c +++ b/linux-user/xtensa/cpu_loop.c @@ -133,7 +133,7 @@ void cpu_loop(CPUXtensaState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); env->sregs[PS] &= ~PS_EXCM; switch (trapnr) { From 9a191d3782d707062c42b9fc9f22d9f55d4ed375 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 21 Aug 2025 18:56:55 +0200 Subject: [PATCH 0722/1794] cpus: clear exit_request in qemu_process_cpu_events Make the code common to all accelerators: after seeing cpu->exit_request set to true, accelerator code needs to reach qemu_process_cpu_events_common(). So for the common cases where they use qemu_process_cpu_events(), go ahead and clear it in there. Note that the cheap qatomic_set() is enough because at this point the thread has taken the BQL; qatomic_set_mb() is not needed. In particular, this is the ordering of the communication between I/O and vCPU threads is always the same. In the I/O thread: (a) store other memory locations that will be checked if cpu->exit_request or cpu->interrupt_request is 1 (for example cpu->stop or cpu->work_list for cpu->exit_request) (b) cpu_exit(): store-release cpu->exit_request, or (b) cpu_interrupt(): store-release cpu->interrupt_request >>> at this point, cpu->halt_cond is broadcast and the BQL released (c) do the accelerator-specific kick (e.g. write icount_decr for TCG, pthread_kill for KVM, etc.) In the vCPU thread instead the opposite order is respected: (c) the accelerator's execution loop exits thanks to the kick (b) then the inner execution loop checks cpu->interrupt_request and cpu->exit_request. If needed cpu->interrupt_request is converted into cpu->exit_request when work is needed outside the execution loop. (a) then the other memory locations are checked. Some may need to be read under the BQL, but the vCPU thread may also take other locks (e.g. for queued work items) or none at all. qatomic_set_mb() would only be needed if the halt sleep was done outside the BQL (though in that case, cpu->exit_request probably would be replaced by a QemuEvent or something like that). Reviewed-by: Igor Mammedov Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 2 -- accel/tcg/cpu-exec.c | 1 - accel/tcg/tcg-accel-ops-rr.c | 9 +++++++-- accel/tcg/tcg-accel-ops.c | 2 -- accel/tcg/user-exec.c | 1 + system/cpus.c | 1 + target/i386/nvmm/nvmm-all.c | 2 -- target/i386/whpx/whpx-all.c | 2 -- 8 files changed, 9 insertions(+), 11 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index e4167d94b4f01..d13156bee8712 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3155,7 +3155,6 @@ int kvm_cpu_exec(CPUState *cpu) trace_kvm_cpu_exec(); if (kvm_arch_process_async_events(cpu)) { - qatomic_set(&cpu->exit_request, 0); return EXCP_HLT; } @@ -3345,7 +3344,6 @@ int kvm_cpu_exec(CPUState *cpu) vm_stop(RUN_STATE_INTERNAL_ERROR); } - qatomic_set(&cpu->exit_request, 0); return ret; } diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 3ae545e888f29..ad94f96b2521f 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -872,7 +872,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, * The corresponding store-release is in cpu_exit. */ if (unlikely(qatomic_load_acquire(&cpu->exit_request)) || icount_exit_request(cpu)) { - qatomic_set(&cpu->exit_request, 0); if (cpu->exception_index == -1) { cpu->exception_index = EXCP_INTERRUPT; } diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 813b313859ad9..7dbdba7b51449 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -293,8 +293,13 @@ static void *rr_cpu_thread_fn(void *arg) /* Does not need a memory barrier because a spurious wakeup is okay. */ qatomic_set(&rr_current_cpu, NULL); - if (cpu && qatomic_read(&cpu->exit_request)) { - qatomic_set_mb(&cpu->exit_request, 0); + if (cpu) { + /* + * This could even reset exit_request for all CPUs, but in practice + * races between CPU exits and changes to "cpu" are so rare that + * there's no advantage in doing so. + */ + qatomic_set(&cpu->exit_request, false); } if (icount_enabled() && all_cpu_threads_idle()) { diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 1f662a9c7452b..3bd980050423c 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -82,8 +82,6 @@ int tcg_cpu_exec(CPUState *cpu) ret = cpu_exec(cpu); cpu_exec_end(cpu); - qatomic_set_mb(&cpu->exit_request, 0); - return ret; } diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 65f5da6c5068c..916f18754f685 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -54,6 +54,7 @@ void qemu_cpu_kick(CPUState *cpu) void qemu_process_cpu_events(CPUState *cpu) { + qatomic_set(&cpu->exit_request, false); process_queued_cpu_work(cpu); } diff --git a/system/cpus.c b/system/cpus.c index fd804e073278a..aa7bfcf56e5c5 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -463,6 +463,7 @@ void qemu_process_cpu_events(CPUState *cpu) { bool slept = false; + qatomic_set(&cpu->exit_request, false); while (cpu_thread_is_idle(cpu)) { if (!slept) { slept = true; diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 7e36c42fbb48c..ed424251673b9 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -817,8 +817,6 @@ nvmm_vcpu_loop(CPUState *cpu) cpu_exec_end(cpu); bql_lock(); - qatomic_set(&cpu->exit_request, false); - return ret < 0; } diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 00fb7e231006e..2a85168ed51a5 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -2050,8 +2050,6 @@ static int whpx_vcpu_run(CPUState *cpu) whpx_last_vcpu_stopping(cpu); } - qatomic_set(&cpu->exit_request, false); - return ret < 0; } From d5e33b5f8f5a787bc4bb38bd0b9a8c3e3a0aa9f0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 11 Aug 2025 09:52:46 +0200 Subject: [PATCH 0723/1794] accel: make all calls to qemu_process_cpu_events look the same MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no reason for some accelerators to use qemu_process_cpu_events_common (which is separated from qemu_process_cpu_events() specifically for round robin TCG). They can also check for events directly on the first pass through the loop, instead of setting cpu->exit_request to true. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/dummy-cpus.c | 2 +- accel/hvf/hvf-accel-ops.c | 2 +- accel/kvm/kvm-accel-ops.c | 3 ++- accel/tcg/tcg-accel-ops-mttcg.c | 7 ++--- accel/tcg/tcg-accel-ops-rr.c | 43 ++++++++++++++----------------- target/i386/nvmm/nvmm-accel-ops.c | 6 ++--- target/i386/whpx/whpx-accel-ops.c | 6 ++--- 7 files changed, 30 insertions(+), 39 deletions(-) diff --git a/accel/dummy-cpus.c b/accel/dummy-cpus.c index 225a47c31fdb5..5752f6302c870 100644 --- a/accel/dummy-cpus.c +++ b/accel/dummy-cpus.c @@ -43,6 +43,7 @@ static void *dummy_cpu_thread_fn(void *arg) qemu_guest_random_seed_thread_part2(cpu->random_seed); do { + qemu_process_cpu_events(cpu); bql_unlock(); #ifndef _WIN32 do { @@ -57,7 +58,6 @@ static void *dummy_cpu_thread_fn(void *arg) qemu_sem_wait(&cpu->sem); #endif bql_lock(); - qemu_process_cpu_events(cpu); } while (!cpu->unplug); bql_unlock(); diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 7a27bdadb4fde..8b794c2d418a9 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -192,13 +192,13 @@ static void *hvf_cpu_thread_fn(void *arg) qemu_guest_random_seed_thread_part2(cpu->random_seed); do { + qemu_process_cpu_events(cpu); if (cpu_can_run(cpu)) { r = hvf_vcpu_exec(cpu); if (r == EXCP_DEBUG) { cpu_handle_guest_debug(cpu); } } - qemu_process_cpu_events(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); hvf_vcpu_destroy(cpu); diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index 65a7f76a69ad6..8ed6945c2f78e 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -47,13 +47,14 @@ static void *kvm_vcpu_thread_fn(void *arg) qemu_guest_random_seed_thread_part2(cpu->random_seed); do { + qemu_process_cpu_events(cpu); + if (cpu_can_run(cpu)) { r = kvm_cpu_exec(cpu); if (r == EXCP_DEBUG) { cpu_handle_guest_debug(cpu); } } - qemu_process_cpu_events(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); kvm_destroy_vcpu(cpu); diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index 342917c5f6e8c..cf1ee7ac2587c 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -84,10 +84,9 @@ static void *mttcg_cpu_thread_fn(void *arg) cpu_thread_signal_created(cpu); qemu_guest_random_seed_thread_part2(cpu->random_seed); - /* process any pending work */ - qatomic_set(&cpu->exit_request, true); - do { + qemu_process_cpu_events(cpu); + if (cpu_can_run(cpu)) { int r; bql_unlock(); @@ -112,8 +111,6 @@ static void *mttcg_cpu_thread_fn(void *arg) break; } } - - qemu_process_cpu_events(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); tcg_cpu_destroy(cpu); diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 7dbdba7b51449..2fb464399710b 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -211,13 +211,30 @@ static void *rr_cpu_thread_fn(void *arg) cpu = first_cpu; - /* process any pending work */ - qatomic_set(&cpu->exit_request, true); - while (1) { /* Only used for icount_enabled() */ int64_t cpu_budget = 0; + if (cpu) { + /* + * This could even reset exit_request for all CPUs, but in practice + * races between CPU exits and changes to "cpu" are so rare that + * there's no advantage in doing so. + */ + qatomic_set(&cpu->exit_request, false); + } + + if (icount_enabled() && all_cpu_threads_idle()) { + /* + * When all cpus are sleeping (e.g in WFI), to avoid a deadlock + * in the main_loop, wake it up in order to start the warp timer. + */ + qemu_notify_event(); + } + + rr_wait_io_event(); + rr_deal_with_unplugged_cpus(); + bql_unlock(); replay_mutex_lock(); bql_lock(); @@ -292,26 +309,6 @@ static void *rr_cpu_thread_fn(void *arg) /* Does not need a memory barrier because a spurious wakeup is okay. */ qatomic_set(&rr_current_cpu, NULL); - - if (cpu) { - /* - * This could even reset exit_request for all CPUs, but in practice - * races between CPU exits and changes to "cpu" are so rare that - * there's no advantage in doing so. - */ - qatomic_set(&cpu->exit_request, false); - } - - if (icount_enabled() && all_cpu_threads_idle()) { - /* - * When all cpus are sleeping (e.g in WFI), to avoid a deadlock - * in the main_loop, wake it up in order to start the warp timer. - */ - qemu_notify_event(); - } - - rr_wait_io_event(); - rr_deal_with_unplugged_cpus(); } g_assert_not_reached(); diff --git a/target/i386/nvmm/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c index d066364b98934..dd5d5428b1c00 100644 --- a/target/i386/nvmm/nvmm-accel-ops.c +++ b/target/i386/nvmm/nvmm-accel-ops.c @@ -42,16 +42,14 @@ static void *qemu_nvmm_cpu_thread_fn(void *arg) qemu_guest_random_seed_thread_part2(cpu->random_seed); do { + qemu_process_cpu_events(cpu); + if (cpu_can_run(cpu)) { r = nvmm_vcpu_exec(cpu); if (r == EXCP_DEBUG) { cpu_handle_guest_debug(cpu); } } - while (cpu_thread_is_idle(cpu)) { - qemu_cond_wait_bql(cpu->halt_cond); - } - qemu_process_cpu_events_common(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); nvmm_destroy_vcpu(cpu); diff --git a/target/i386/whpx/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c index 2ca4ee02636c8..f75886128d0d3 100644 --- a/target/i386/whpx/whpx-accel-ops.c +++ b/target/i386/whpx/whpx-accel-ops.c @@ -42,16 +42,14 @@ static void *whpx_cpu_thread_fn(void *arg) qemu_guest_random_seed_thread_part2(cpu->random_seed); do { + qemu_process_cpu_events(cpu); + if (cpu_can_run(cpu)) { r = whpx_vcpu_exec(cpu); if (r == EXCP_DEBUG) { cpu_handle_guest_debug(cpu); } } - while (cpu_thread_is_idle(cpu)) { - qemu_cond_wait_bql(cpu->halt_cond); - } - qemu_process_cpu_events_common(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); whpx_destroy_vcpu(cpu); From b422a7bff64eaf55b8250225533ca1df42c3777e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 1 Aug 2025 14:46:27 +0200 Subject: [PATCH 0724/1794] tcg/user: do not set exit_request gratuitously Whenever user-mode emulation needs to go all the way out of the cpu exec loop, it uses cpu_exit(), which already sets cpu->exit_request. Therefore, there is no need for tcg_kick_vcpu_thread() to set cpu->exit_request again outside system emulation. Reviewed-by: Igor Mammedov Signed-off-by: Paolo Bonzini --- accel/tcg/cpu-exec.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index ad94f96b2521f..7c20d9db122e5 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -751,6 +751,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) void tcg_kick_vcpu_thread(CPUState *cpu) { +#ifndef CONFIG_USER_ONLY /* * Ensure cpu_exec will see the reason why the exit request was set. * FIXME: this is not always needed. Other accelerators instead @@ -758,6 +759,7 @@ void tcg_kick_vcpu_thread(CPUState *cpu) * CPU thread; see kvm_arch_pre_run() for example. */ qatomic_store_release(&cpu->exit_request, true); +#endif /* Ensure cpu_exec will see the exit request after TCG has exited. */ qatomic_store_release(&cpu->neg.icount_decr.u16.high, -1); From 614fff7c0a2e28ed8675bbc3385ad88a76d4ee3a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Sep 2025 12:49:33 +0200 Subject: [PATCH 0725/1794] ci: temporarily remove rust from Ubuntu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is for the purpose of getting an easy-to-use base for future development. The plan is: - that Debian will require trixie to enable Rust usage - that Ubuntu will backport 1.83 to its 22.04 and 24.04 versions (https://bugs.launchpad.net/ubuntu/+source/rustc-1.83/+bug/2120318) Marc-André is working on adding Rust to other CI jobs. Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250908105005.2119297-2-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 83c2867295afd..f01978fb40cfe 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -39,9 +39,9 @@ build-system-ubuntu: job: amd64-ubuntu2204-container variables: IMAGE: ubuntu2204 - CONFIGURE_ARGS: --enable-docs --enable-rust + CONFIGURE_ARGS: --enable-docs TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu - MAKE_CHECK_ARGS: check-build check-doc + MAKE_CHECK_ARGS: check-build check-system-ubuntu: extends: .native_test_job_template From 091f115ea5d40880e74123f2a7cd12f3dd32d624 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Sep 2025 12:49:34 +0200 Subject: [PATCH 0726/1794] configure: bump Meson to 1.9.0 for use with Rust Meson 1.9.0 provides mixed linking of Rust and C objects. As a side effect, this also allows adding dependencies with "sources: ..." files to Rust crates that use structured_sources(). It can also clean up up the meson.build files for Rust noticeably, but due to an issue with doctests (see https://github.com/mesonbuild/meson/pull/14973) that will have to wait for 1.9.1. Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250908105005.2119297-3-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- configure | 4 ++-- python/scripts/vendor.py | 4 ++-- python/wheels/meson-1.8.1-py3-none-any.whl | Bin 1013001 -> 0 bytes python/wheels/meson-1.9.0-py3-none-any.whl | Bin 0 -> 1029634 bytes pythondeps.toml | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 python/wheels/meson-1.8.1-py3-none-any.whl create mode 100644 python/wheels/meson-1.9.0-py3-none-any.whl diff --git a/configure b/configure index 274a7787642e2..3053c23fbe178 100755 --- a/configure +++ b/configure @@ -1184,12 +1184,12 @@ fi # detect rust triple meson_version=$($meson --version) -if test "$rust" != disabled && ! version_ge "$meson_version" 1.8.1; then +if test "$rust" != disabled && ! version_ge "$meson_version" 1.9.0; then if test "$rust" = enabled; then $mkvenv ensuregroup --dir "${source_path}/python/wheels" \ ${source_path}/pythondeps.toml meson-rust || exit 1 else - echo "Rust needs Meson 1.8.1, disabling" 2>&1 + echo "Rust needs Meson 1.9.0, disabling" 2>&1 rust=disabled fi fi diff --git a/python/scripts/vendor.py b/python/scripts/vendor.py index b47db00743a5b..33ac7a45de0fd 100755 --- a/python/scripts/vendor.py +++ b/python/scripts/vendor.py @@ -41,8 +41,8 @@ def main() -> int: parser.parse_args() packages = { - "meson==1.8.1": - "374bbf71247e629475fc10b0bd2ef66fc418c2d8f4890572f74de0f97d0d42da", + "meson==1.9.0": + "45e51ddc41e37d961582d06e78c48e0f9039011587f3495c4d6b0781dad92357", } vendor_dir = Path(__file__, "..", "..", "wheels").resolve() diff --git a/python/wheels/meson-1.8.1-py3-none-any.whl b/python/wheels/meson-1.8.1-py3-none-any.whl deleted file mode 100644 index a885f0e18cea1d1c32987540dc12d91cb74bcb9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1013001 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%0AX@<|mWCOhR9Kd-*SZ#hE=mvYd<)?0!5Y z0lTpmv^9VQK$YQ%U5crwnkXXuM;|%{>W6y!zI2{B z%YsZE%S|Ad`v{fLDd9d{>dp&VCQ=KE@#28m$~HGM{YTAX_+Zq@^MeR zRxxqM>!fN1u;#&xK^~h<%sMlqj139xP2uptK(vR!u9@4xJJ00Fi~$3}Pv}>S@?s|| zgOZBr5NpczW?|}nDD?f%Eze&7G-T^BN0v!lcs1^}h!P57hECXm!hnYL%-(pK;fksp z%xrVcaT`=80En3vtc52QfTOWoVZAE=ecD0sa~dN|bW1w~$wziZ>i6IqrfO4}Ne0e0 zSog)nq6Ed6X3*me%MH&M2tZW<U>aHCoO~VlGL_(88>I?3m)vTq_Vhf?CpcFpa zl?eVIKcmaFxn905Oo0e~BiZk4M)S}8<8YW56#<7wRE%?DPiRPR2Xnp{J=(xl5Sl{i zCJ^eSYalG}10GgJ;&*9F(We_b$N3X8B?aU;h;@ zz@vnj%YO``1iULM;)1MSZIpA+5)3>r)#FwCLyx$-_)jB-;ToD>m^HZZ68h5W$|&*g0>TU+;0lPNeFJpj9#e;W7WI*ANg^ zq-fv^I8dBd2Ldiz@2SLe13AG3kO;;4`Grm@tYhY=SOTFoghcMm0Y9DeJO%`$)q+*Uppy`7pJ{5Hj7%HH5kXZWQd`vZ(}f_ zu$8cgbf->^5Z&~5utkR>C6P9Pjmklxm!YDdW2Xs(f$Kp=J5VF;m@{f1RiqtPZsjCF zMnv*ImV|vLCZK&IKK9#y{!&1DN+{Ek;49c8R@6i{m{K2~ilUBswo!O+jqRz|>*ZGd zLqPdqMX3V;#ev&4NoP{UN@sB{oB@njt_8ivWc-$|MPXoRvI>?wqYAlc;?SN1%bc>M zRvgrV*Nk#Bn2YP@6+%lD0H!BRs~A|Ona6y*1Jpt32TvVs1#DDhGd@GmLHj0_B=)us zNoEgd#wBFd!y~oo*3)wPypBK>pS>HCPpZBWo29TnrMngWHIc zj{~}_Ohu3mG_S=|t2!bJ@KIF)Lfv;p{bN}-XhkVEJa6jwL7M^AUVW)j;L#YX@&=m9 zn{5_xltmPsw>N~X(8YA?Uw1o&Z?fe>9wr{W&$KI2CNyosoP^m~$B`P_=IG4+AV>

^XZU0Nied<4Yg$j!TQP+`JQ@%9 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

LS}0NPNaD>+mmB*>o10Nr&MjkmMv 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¨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 z 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⁢-dxb+^ugsMOW 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�yEUe>lb zu#0bjSDsD35QNIsJARa0tHh0~d|Fr4+GYwv?6eruO*c86zabUKZ>P%xPAz zzN!3`d-7yCT{|wcoJ?!3X>$#HQue>Lwu@(U!*tteT=Q>=z3-$1UP7UKFSMCR*{E3{ z=?KtGWop4Awcbr*5VEddcJpx`ZR-uy0f`iC3NW|Tg4#mBb)G|^F&{qiEF;9IrV{<~ z1_gzyZD<~D$)I+FuX74}qmc&puaz=8I_93|Dx}R#Tawd0oNu!m+ZwmLh0|?&U$WJ* z8Q-u*v0S)SsLW+a55QJMt-IBvh0peAJm^;jEopSHvkF@qopa&I5uNPFmDQ6#XI<;C zBl?X=(kF* z60axq>qZ^g`JkwQ87om98mWh=piw;4)M3L6Z(ZG5=g&$ed3xCRyM_Od{J=%_vb_m; zSi3hZ=N=$fgpV(eq3fvZ1fi*_e9E`YX(*zss_A%pb6cM5gAzY3?_h<&MhX!*ojT+j8zJZC0SNGU(*! z<+;V8?d&;-EPgn%N;}26j6R<|*_o*00}xW81SB1DOJh>~5eI}0rn12`X-~nQI{j_p z5=Os;43sQ5W*Or}UVMJAPOq>#sp31{F!KS*qAuk*&4c7SR2z>Ti9CtVp9L#d}N(i0jvK1 z7ZVhRCLQEK^F%0?4<{%I3rsf2lAhc(bz9TXdbq(xHd<=yihBvA3p8wemv*F7@RHu9 zS`4rhd;x-{ngB!2HKh(LuFom*LH0Lu%Ds)A*((xIFJi-@{1h;;-iRa2x5li@u^{|P z#S8e=oBD@LocVK{0k>mXM@?ZtmLDBL{s`RvjsT@?p<^hi^ye%?rl( z*_M?or0_D41}jTKxQA@XNA6N5t=o#e-u)IVgAC5!`nnQl!nO#b8s|k%aAIm z*tX$IgRWlz!##Niz5cfIpZXdv2b)f@cK8iXBVq1C&?sOBM`$5Of5dY7mFo-p-4mlF z@J$tQnRissNDo?)+!=E_gE|FUg@{3|9pajmW9-19st5~&h{<1+AAlk=F@wM)%L|V8 zF`FgC%T0ijQ>Qs&l7Z}!7tzuY<{2l7>4R~M$hDn3H-h#J!6*5zAO0Lld^_=!Hp)G$ zog94;{n?f4l|@4Fswsx(jJEwjjNa%c@?f!S z>5)Px7OK>>Khp}u>+1MX_nkqwnwoi*g>S1w6k)IAz06 z?<$qCjmhVTJQOlFXEfp!^#?_I_IiVy3Q9TEvIq?9!V%&Q+&!`3+TB?@-8Maxjs7Xx z+v@O#+4*g7x_KDe{#f{GSO}M<(|;F8QgszkTmfHDXV4kMdjsgZeBo40te2(|Hh1lM zc3xF>g54ZST5NmV2WoEUM~rwkbUiYP*&M>~-gqcx3!@T$VI@d2`|xn_iT|w1@!2u^ z$*!r~P4x%eeF0TZ{aUBs%5`o!vvE!B;thRPh4pSi#P@%QpoK2_s6Kv0gmcsY0672q zY%>0Zkp9a*c%rxz*tXzt0bpd?lIdT47;?)be}8?eO;cIJ$Veck@L8r9v(HVKy{{mmp4nAF>zq zWk11p1x@pE?5$D8nLY&K;Bj+Z;^X1qCEjHxK$E&l3jWh)m_b^Vh7wJZHOqVme*JEu z9?yQ`jYy0>0WGi?ht?G4M$H_~lJA?m&#Ih2SD36(wGc@2++vE7m^3@q2pk!n8?18S z0{Nbv7fF6V8DiH0qaJYbbUaElqkaO-X~=zA1CM1wdmgRjmh{P-5srfpebB$AQ>WE; z8@Y0RMC4$~8{{^XdMH*mQQ#z2j%SFM9b3iz@%A{*`?PnxKbTNE0ru!jz^ePpievXePbr{T9KR?Y+bt_FQ=n(h zXABArC9};!in&U8iRj!CBUT9Ep#c-V$mad4qV8$^`-8q8-KF*k8{{sDd!!PP9zZg^ zkt}N{3MISPCY&Qx!cBH;eLpcG*W)cd1&Gf#Hk0@>A{Pt(7m`L9VQ+^MZ@E9IR8fBg z!s5{BC^uUaVNhmo1(c95^lzl;ALRZX;AKsw-DTi?^^IO5&8CpJKK!zHPV8Dn*I%QF ztF5Qin3bPnQmk>cN^5x@xf}lCSGQPhQym590@8=Zbjh3}KWX+z{)dBZe>b_#tyY~k z#!>#ayNaIe@mQ*etj_^(ZE=mccNd~@t^0WLoz?8_YMEi6dQp@%;1CefA@yI-Cv%MV z`KT2zmB`0i0|~RqWm_2`9%GPYp;Q2ytQV5?KIRTgs@rfR*fVIzJ)Ja|J|X0Kh^EPT zE#Z=ty-1eP7uuTboHFdeK%eEGZqUU*uJPHN&x8?HO8x2H#y_%qwDX?D}Po!)Xpgi|VzuwzWEDUxaK zci<@Cg!MZsCqT|+m4&t0x(jeb9vk-IE)$XbTLLU(lLsGZxB5 zbM(+K`5_fE>%Hwxy`66>$%pvjaMm~w3?XC#@swX6F6=Iu;fY8l1qJtpd8_p#higM8*npu3WfaYHLOUx zw#mz`8HmSQa;O*rtR2=}6Fz9VEz&qg(3EP0dOdtNL@n5i-1GS7{shRJCZ=*roSs8w z)M3g?LgXI;lYDYaSpZm!SS{3;DzDf|HSHj`DH_u|2s{Yp!|0q4K{?XCHrZ=e@u_bV zhFsdC^BYQZ`9eJT(1(q1(8rs(IgNPD`}};(-Q2|G{`h_#1O50tZa(Gu>j6Tqh>Ep! z#NrTuE-MICNwzy~WgT@+w$E6S8>cxm+f3ho5SK2LC{cs-^y;j*=dZHq&k=JtXYmO5 z!5{Uehu1++Mv-qzid-as)YRntetSZVL!VWtwzs%}lCW(6RFzrR=Q@{Mx?cj*{F6u- z4BMpZEJ05Pkg#M->Eh!=qvf{4oNI@z?6|>StHV)Ilx)6j?(;k#Np%yF3ELfS(Q<`8 zBN zwNvR2DER

0Yo(R}oYpZ^2u@?XMfU9Z(=i;D#-vQ`*AM*V#b4H7yss{sF_M)qgD6 z6@Xq|LV8?3Y97XjTeH-HrSYz&ouglT{WNV)Ir~vDt2E7J$nzk~N^Ybr1O0B&mig0+ zcf+Q(%!88$m!lN%9d5`ujgIKjR7*|oV|(bb*u$wX2S55e`|UL00>ciTPbFu#Rotj4 z^^O`#xs(>H`SU(+Rmt5o;)X6G+i}(Ug4?i>rUifJ6~BGc`bl?d$!daRiwUQ^rO$0q z$PU6$VXVcpB=frw7$`cyo0VcB2Ruh#?&PQJIKt1tRzt_i)Ok|8 zloEqfO{&rND*G;fPYBU*&@E<$cH#u?P0*#-Faj^$dYqS(_9^yDW(H$O^g1T|=>yFq zsbLc_H3l`wuwwI8hstl%7Cw8EOLtJpW)n!kD}n3YLB^CbacLXYG6xgrwv_)gyXoaR z)u2{j(3l=z^yiinVDW$x$l*5#9)+agYcf=~EX)HkN{)2CIfn6ud2bx6-R(zXqlN}) zOG3Vg27Se2b@pY%P-&pWrGz4vG_P=&7gK6fM>1^&QtauhQQgFG_ zkR@X^sx=xpHj|}Z7O)*6glIs7qQoXg2#8hw7uP`e74Oc@Ie0P7%dft4!-Gr@Qy=YX7%VF=IyUB-_P!V^h^{zhc%U_5_oO1nf`I9j*@K znGJW0+AB_{X6o_zQ{g{&RxE^NB`#^{1X?}*v6^_J*K>ocqhrq+;F@Y()%9>a{04nF zt^@TW>lyWm=Ju4QW*V*Dp_za>8ba%w%%<#g?_~>guGsa*QBjSb=lv#+pY^fFf8cKA zRCq#Rw9c*qEe(0<2e8b3(bX8-*Y~B9&Ew^;pykxO1Zv)zn+pE3PN@yr0h=2@1g+ue z`*AQAUNXCTksJoQtt(+{y3t*R%PuXaWE@2;T*|SZ*^FH>Z#tGuyG#WTk^0=mQF z;;#9*-hTDr)3hj)e9^hz6=c9sIW~xNkq#rMV8b};|YEOzAgT~N)FNl-wQ8AIlvwE{pgl4(doe=f!Ohxm`5ZDd=Bdg**_>x~YMrK`}i zdmYk78htSee6vEquj{WZc#{^756f$yt9S03X^=w=fkGQ;2!Nw>H%}Y$OkIO=LP;FP zA>6T+`Tha^wxsJ_$DM`xZ7y(mM)ITR9^n&mKD`~@tf*Zv{ zHPj!8*|Sm19GI&9Oi~sTOR)!65c5{hO?k@Hz9`5N$a1;}gNR=musa zEEtMNx)o(g__H?W*7PbFdBuU{gA;GAh!Fq~B!YBx0p4Egkb9}D;s(5>-Wr}_4RT^Tssq#-U6?KN3z{}pb&NyoAb?LXSb0xv%IFuo-4e4%W1HY zrL}Q&HFag<>M*s&5I5l8_L=&z=MIvh(Vvr`u;=&9x47mz4Hp#ram$SrmXPFbaC^B% z03}wnf%{hi>sRFJLF}GJPX^H+9630XTW;3X{f;qwuEdDx)fdn4+n*9gMBBH{Lv{?c zZ|i)xbxTawx~CA8lhfmI^!DkEL^9n)ZBtoDjFWKlwCMDt)5Qm2+eHpzUj_c+!#iyI zZ;;z~M@5GW>s>dytio&09Og>2V-5kFRUiF%XlDn<`NfxK^`mwmX(h)D#@ARUw?%2L zNw;Bjz(}2qZr5vtu&P40F3q#MQnG814t-Y07T{uncX&va&+EDBhRb1c+QH)8kd|wv ztmed3JzID`Y685jM0fYkmM!t|cmB{T&xprquYcU`&7b5h%gDKXc(eu+%zs6aC$src zIx~ONWtP9GSX0zu*$Lzb^xbPTAPJ@vr2e_Jd8DkUF3g5NMjnyh62t8!FR$Lw(OnJ% zi^0TgvENC^o)J!MZD1?LfBk$~df?5v#fsxo5`}qlfy!x!Bmye00tf zND-@lr*G*z9A=8a_%Z-O_b~#(V@o3@8Kw@mM$|3R%|b+3XsxEqn*5*+ujy`fJpi$F zMq$f1ojbhDK{R1b_9}mp1V`YSTDV)tR1=1Ruq;~3(!qT|j$8gZV>1@%=hNl?qGO^B zx_XGyEK4D?LJWNlH&GbpT}D!LbW#?O66tjRO~EbaS6VGNkZ|~SFi0coI^?efvrGlJ zK1&Faxn^BILrCyF`oW*vwwV2f1P@XO8O4@JdcZtKNVVt=(d6q6&E>2vJQT8vuy!eG zMAnwe(oWM@0=z2e z+c)5ptNlz08CN^*G}{_X#2|+y1G|xU5zd9Q2G-4F!itu8U_Ti&*9xNm{7T?l&A(_V zsnnM3u#PRZ*W=E|aLW6yX5rm~^9dR^mpFg4AnS<{w$tlelMS*1&^kwJ`}D)Xck!_F zsLHi&*u+fJ2*`IC=U-Ri@S^hqcSmO$95_Fd0XwnnBs=fY-CX*#A~JiM(q5Ws6GTtp z$3>zCAm>(8_GpmMJS--C*5!KJ(dE>)RH*#EM*7jK+cb$U0f&N7Uy%|1=G`lu#($Ey8ptJJ%z?$H*w)@N*-~@Sguy5?qnx_un_U zH3j|AqU4sk2!4K8b~mHcV9YW+h7;bA>ftR(TVVTiVDQKbrfLO(#$~awA0Nf+SaO5| zfFX&iP;R(y1BTVvmfVSqqTvx84lhx*mXjc-lEiep3N|kfLaY2a&dB^ zcK<9~1(-7}IE{_{Rqd0cW{`koa>H zz{tkMeKH8!p!W_bpMaZYFf?hFqbJMO6szvlF*uRtJClZmeDZ?c^m^kZd)1%D-OH-> zVp+nC@vIW^k{_mM6KLg`9`knu`v>Ct0@Y2nU!QkMVOzeRgWqp=3w%G8Yk^IyiDT;H zZ?0*tv9>KH(al5DrfWY?UHJtwj*V|3E->m^t_UEl5W%-+{$Q%aeAc|@2Oo<~cuMWb zW@pT(gYE3+@g6`>eVEo`kWxnMS~u~a1aF|tF#c{g!TkW!J>9^6R&hU_P}VLm??XfYM^JKcxNoyzyX?E_!U3^X{0;i%M4KQf*3EHjmHUib$E z@vk%8LSq?Hd8k^tcTluDs*@x@2R*PCK%T>znXl7IaBuDY%(0Nddh5adZ#j;}!RzOh zZc_W834Rv=q=NmP(XZ{9qJIfu5_BGl@JVQZjlNC1SQuS>RY=&d43!A0!W{Ox5<@~0 z46L0#5J}t~!+r(_YJf~8>JqfYAP>dowGban0_9!&lvn`D=^x?=$~#G^ONH#4LrToW zB`(<&+c_|C@{c~ZIX)Cd9eFqQ9wCdtkE8>oFk(5%3V-qux$DI%MT-?Yezv9^y#<8r zX0k|jiGF20{Jsn1r-?^4L0+;-&mBv48*Rc?*aZnQf6=VW?_ck-Ld^$UG?yIeXrs$J z56+%xTGCO{QfHAoURz2{gp|P$~hm>RfENMko!n!wQ-&cFN z!LrYXz^c1}$O3BhRD=Ss&mf)nP}Gp>ySlH)7|eka2u`tWqq;_CnrS>#xP{7=HUGAX5LT*{3Ok}jre&>)J2B)mCBC1; zbYLGIl7JF35%5rf=QR=v5DGM6Fk_eCrt?oQyX&lE^WQpgyU8X8(2tUR43G80S69%` zllI}B!1IHJ7$~Y|S~;(2T##-7e2t81fVR#tj5WjMb<>>%ZIdNGrkoze)VAJctw>=4 z4!sPw0HjBA(XnQoABKnkP?NIlw8**kkl=Gh&+jXtQXU|BOyQx#WkTY-08D)2UWV_h zHGLr}#ZR<#8rFt56O`P$T6OIRyOB~vN1=m4RZPA@GNab~OOeq$V)ifOp!{iff#+~kw8e*BT{20y?;SuBVArRyIynFHdv59W|OI z!*Qoc;4IS5|L-Mop2)>3ebKviFaBw&G7^<^M`i7@Ja4L;5!A zZ?D;9zV=QTw`OfIrADXHOgC;8`40P`?qC$JcC%A;*j&K2tN#yK=fETgw4~YUvTbzP zwr$(CZQHhO+qP}nwyUORVs|6n?4P)KGrv4}{O6VXEp#JE)Y{*XE9 z-w)aV+?5s8kLqFrmoBS7?8gGI4r<;@0?N64${K9}h25vaP2L%UfE;uWt&Yb4y@c|0 zJ`Y@w0h$JajRe1Z$pDpeF%)#E25fOKLvXnpPpdr#ryxyXx@TjgdyjAqr;TK@4{q_m zw`9t=!o}p6)#a86q_a`#6iTM&q)dL?aCQb>Eh58*`~qcPlJ$26RZMwHoXU;^4kwxd z4|hUuJ2Yy?!@{(nCI~%>uHF_zZJH%@Ah6L0!{FZ?>}nxZ~&bppPli*Y+1Sr2WB{5sg*SFoK;&D`ZE zdh-ODV>?-)s;sG!R43su%h0pP!8|8VxvAp$eb(b)O5M=>w~ez3Jat#&2HE=<07i>^ z1Qx&;Vqiv8{#Maml4}iS#{w)L7glgBLj^0g8pqtN5A`Ua>(;jW%|X%uuDTTl_N|<1 z;sTBEVia8J1{mJ{)h)@Zz5KSY><5^%2j@bOw)FDgZcQkrLV>m-SIw;or(Y_q?QM3A>`PDqz{ zmhq4H=z%lUl$xs1SGDjU`jr~h428{z(b(;KGTGPLjfW!7Scc}Jt~}t9gqEO9f%37P zG_JdQm8_nR0w;&|yn{S=V~3oEVCMBX>zRO#~d zP9!P!!LyNRTAj7>#nfOUYD?1sjn)C0&{!+4(Y{5j}10o zHzdV2!N|@%y`OGgfP3-%hx!zo*0!6$zC$svN@Rc|NuEWyC-~yc^OD;nI=EqQG_FEq zT?W=hNLe^~3;M(1%feP1MrTU^(&PVX8_N64Mj)TZ8SAIPzm9`wyPd=pc9RxreaKL$ z#5{qB&|)2$Ky0Z|$y0s(8ze=M%sIyyR#N}DxZrM%H3Di7g?Y{%7;56?wrNq@?zHMX zRY_pgzuLw+c{jZMYmrR#5~6&dGpJjWR?5&xeuO%K z2njV-E;uYlm@86tV(Zzz1RA@k>`9@1*bTm9@X}zYZc938L424tK0)(obTo+!T>^$o zQcfrLi{!5YH!VIG)f!Zq1Y|t}s8ou*d>u;*=5^JhgH5b_qV{CvGG`0z8NO|^a5sVH zyPWPU_6>r?gHf^QLq^Km8;6*@f*K+rCB1OS0uVi@00V z03{@jD9?rY=$_{FBDJLmp3m!7YwD>lZ!a4gjgtmP(^0lIk`Ig}PD@L33Xpc~0K71(8vI1^%D>{gM?5)pS?m))ehZ_NWxW*P0DgZwKtECgv<)$;?nYs{2YXnFk z6JM!j_moSy6k7q>>@(VuKs_$)O4PX~UQ~;UP!1yp2`QaizF6~Lcn*1l7Nh1(nJv^f z2?R1p5fwKegHQ@Z=p+Y1scQn~0&41J6-6W7u;f>zayNKXODfzbdS_4solGr+&0_RO zu#rc#>px3%D()F$&+y6>s6jhQ^93mM}*RGNVPToF3wV~u8Mqf6m`0uP|u*!`$7yPO9NQj$NEK|07imOsdHygt%sCrCg4`5oNCrhPvYM z6qc>%o7AnWL#Y0hvZ+{7jIVnB+KOlt$J%NDv3La-g&mS(t2Tug2 z16p-ypH2QDpOvZP&s!sYN)k}cnjEb|lVe3MGs)IE6?p{5kr$lR(v}H+K(?!}Hz#wR zSd7TX)r!{4nx(upD+v$6AXL2xi`AQSg&rD~l4=|4z^3Rk$)?H^1rgh{-yT^z`2#A& zT*ZX(Wybt0?2%5WdEyLO#o=Y;7Vegy(ABjFgh46owzxvCIjH=CE4B$a4_;Ba^ly`M zkt`)zic&w3piJMnYCuKPu53e1SgUZXgp?5dGaOwzwvqWtco5`;4jT05=r@1Uwh^t< z(wDI;;N&+cIanb zA7u(l&2voywx~O!cua00hbtyM(X#rPFQ{!fNdS_*XA%l5|1qC42w}z(3su7HxWWQr z!DaRmML+t@tL#**LIc!&w7s98!*;o6Q$37LJK=g}>r*{y&qDg;5K`_&p zcYG@w=~>x3(3sOm4B)J8VxYM(76c?gm$5&pMYk0761W-$j%adca{rk9JpS_%f*5Xr zivUH37n9cmRT1#>H)?(P0eiD|61r!Ir|g9gv$$dx=D$GH@{G=QPZA^P2Es*j3oHnI zY0iKo8`Gg>V(3?Y`O6!k*O(@qigB#KU<_dDm78&vSjvL!wbGfRRTc4|r%Zfng3N8w z=&vlZbkL_gBfSSPlef*V{G=ZUWcMHw%4d_UM%{E>?wKw3c5Urb_Fe0Lq>u}LT7{Ig z(uK{HslF;%$Gp`G73>~zbg-S)Fye&=(%^RLc1^!6cY6J=w@Sg%XU8+nAT*k6oOo{> zXV#q)q7G+9M&MmsHwN8GV9G8y`{93W!n7>0GFvz z$uvDsvRO;cr7~~@AwPOMUyP)f38ETsD-$PEQgE4M$Si~W3K-bP6B(}6GSShK?fkOb zgcSZP&_tHjL1Us5_mb`ASbKhoa>MgUn_!p7=LRB=T+?CNJT2^lQyuV5=?RwlVKB94G^pSusr8Rs()X}7mGxI|=C@n>T17@iZ(4c(J#RZ^d z$=Kf0k4EMCj9F#`A(x8E7*lc*CftR9*_#Oct!p{BzAFnKwQI6(E&dZB_=FyZk)CYn zHG}VAM6pD-RdWezqiatqS^$z88Ds_RYEKni(p!3#y%)+*idK8D3&z#bm3LcO0}FjDupUV7WP;}|WzPG^WKjYaQvbr)TO8fq z?2pTuU?<48wg8E|Yn}L>4@t!(x&U>GP!l=k0rH)Qf%66LI~OmoW7FV7$~m0l{FFm} zYd7_VUKcAS>j8wey|{;@5mY5ha9v0sOkqjlek{z8MmhWZ&RAd=zdD7(z{KC;^mj`1 zd>Ai&YZWW{nNiP1qOE`mVN~btBOA|h;2wnM2vobzGN}G$57-{5`@NrrF$5)ez*<~1HEtkLc$3hKQH zn7VLf{CvPflt3AW_&x7bnMu9Sm@c-9`(bX?kH_}utFHAPd+a1~I;Zpb;PZCtClc4! zp|#hsC6{;i%jYipanbQ=-|=)<&r|TmpbC@d<@sa&#g*5(HMXU9cZxipgItWY5OLEY z&AOQkvqm6PCRT$OfS@=FIbEX;s>K17Y|mA|C+<9jc@q0XJ(0piP&eN~3?eVf6%#UD zyNufWX>SvUrH%tPx+0gVuun0G9mmZKmTptm}7~t45PDX zkOy(;H;hL-7jueC09n`y3?qorh-ExFK89o$P(4T7dQnC04@OxvSYUeirll93hRFu# z^p*@ARYWbLB5|@o#WvcmdaU7!qr4>CaJT(xc3)De(*zt{KsDp-6W3&m@)&u_F*m_* zjM1}1XuyOqOyKK4TgGr#R)m=1NY-<4v6YD3%XZOW0*AMT&Co?^%|AcpdaW{30M= zz_Fh$zy%XFfnd)`CrAqv2OX8>37}^ufEfE3lq(SIN3#y2= zk!Qz%@H<}=sWoz)*i@1Fr%xn?<^zymDq-xQW|^7QVEHL1p|dC!lEepvCr*y@4P^0< z9@#Z!4m`?6I&vm$vhd9&Mtm?Phnvn0@j)rs7CxOO@@jIr1Pk7f@-^TmTYd5s)!>74 zAN+g^!prgiQ$@`Hgl&AHs$6I zTj;9-G3MG+|1Uau<*EfIT^ki`p8_8MDNKF4VPpVJRn_7_hFR+8(k2OBkk31ys`$$* zw4*3LF!>h22IlV^k3QHueJEI>;-$c7!Id2fZIgp1Tf0zbzjdPf&)4W{v`t={pS!Qo zD!1l$4clLPtF_!|%&stEW}HsD3)Twh;*w@IkGOWWidHmArHsWIgy>WSvuc9QH%xO3 z5MX^;iGh`jruM2WC^=T|sX5jkXH(BlPS46R`>gAR)oJaQh`?!_w|TT5v)^IYl8F2& zXET?G2*Ax(ZAmy_I{qaQ(58z3wF?w<(=o$Q^#D^m^npGD%2A*4ag>j!F45Y3^>S6k zP-5hP%2ArgI?Joly8F$2HgkkJ7QN?~@( zLJA)lYz2Kh8iNLfJrsd_K;5IY2?IGV45ZjoO6^a)M#jNPxuICS9aay%xCVI2_2_}B z&ygbE?m8Qnyq5TFSzJ}g+scd0n24z)>MJ9vmV9vZ@<=NK^J$^=F7d2RXc&q;%{J=K;p4H%k|L5S zsGS~eMls=1*P}spw=&6h?z-$qb1&>Rb!d#3Fd^y>6X^P1xm(UFLYDXOge>mEX$8RV z5xr`y>4fK%5+yCxIv^BtvxdpSZT$fQ)3HeJC&0aBe=~?g>(J!Rv}HA&>RP3>W=}K! zvd|8>G=XE|8-MzaN|sdu?WKe45+_S};qo2o?i)aE${>uXw8q(C>N9H^^YIbKUN2S{ zC_O>pv%lqdXiZ!1IHhpRqce0v1@lU2%7F>QEi+vl+jSbo%G7I$m-6{$2FfqXU4xa3 zbtzMon6@m7Z4?;ezb_%4o+PB|2h`C;4C#^U7xWyaw!7Ckyab+}bP=ENtf2Z>-obv9 z=9Knh=p;Sl8g97Q-eCw=V5X*}rrV2&H@2joV70KaurHneS|J~I52v2S_JFri3~8%m zgVW!zkCTZ^X^LAPRVE3wNJra~1(Xy?9ix}h#cGx9m;&NmrdR9jzsPVwo~&?gSA56a zXFQKjlsBUW2Gd|h^t7@QtkuOuRg&|RMsd}Z0aeER4!SamSE^9zk)ma)McZqt;-lfzk( z5>&j#~SuI|3% zHs}mm_ySC!hc#(~Ln9ITqT-AW=c7tmX6W!%($%?LJPlMMkQ|fC70fz>3hmPF;?v94 zDqRmmBhH(Rk1JGB}u!xzVB8)kY~H3khw4%uPhl*4J zm}!ZvrETv%`6`b7TV9;(@li(AV5Vj3jzHe?9zXSGL`@GF3y&@Ftqq}htNHfSeO2OK z_wFWA1_V#LqYcl(djtzw?tR7%!6c`J*S}Aqjk@>>DTh+C0Na%<*wUG(?p3t?;!

AH+A|N^<~C2Gilr(u>{_=2rdOczN0vXN98N_PKpRE5@@)Jw;ufg znSi5*Z={zLgIYe*)yBkYXo?N28N#SL=V@bGy*;Ry?GNh^cruSex$%zLXGQ#EAh ziv4z)mb6icPy)YG9jn6gl)bgpjCGgN74#0XYt(CCTaPUf0~udYr`yH8aT|;Ao9~LC zg-gcO`DE~Xq3L2Grm~4V1+JO$L}#Mr>~ogx?v-=2G@NIL7>yGL!{tOf`?DilH@@O_ zb{aPet$dUOWTI%h?1q_piiTPK=1GI+$;0wIsXA5650Pf(KZ+}nTUKEe>M@f{)u}!T z$cq8wD$Y~{8vlq26gOZ`4d-HDcJm2HO_TP zk~}}gUHtg|eY4wSbSN0yQ5po_7=*p(^4}tKffuQpK;yg-S3Z+8g0}kTjgZfvJ{hg* zRL$C8u5n?XghUfGokF)4)THc*C4KOn%D;U2#1RU6O@=4G0IGd#5mFPzFT$QcVGUkNT zN+1o6J?XqiLX>!0f_B>%-IFq9`lPOI6NJ_jW|W#nMd;mV?kU5s7cWmo%Gr3z_Nu5b zLg{1IU$Q8>%ao18qi6Q;wopS9HSX`@g zqb9CkcqjF?=jOMx?YBAjQ*hpoSg63+g-y?OD}wFPzT=*}U@3bdE4lwYLL2!j2%y1 z^K*wLCy}8{RZ-Y+OUsUcb8{>F@TAj&lU(BK;?u;oZ`6?>A_ZbY_!~w2ybL_)+w(NA z2hU)vn68j9jq0RdBU|SjMG{TZ&eoXILPYFl2n&wZmMiP{4OrlXtsI`4!n&)FC(jvZ z(d6x4yXkLxdYB^FQQ1DsHQ<#XmJABfaozv}NC7Fgg&#guW4R5aDjgZ_fa1HJyAQ;T z2{Bd!DE*r-&5=lAPlSK3+&R07qqYAIg3*K z0axMq*XTw-wI4L}7B?{+^v<|v{r*N$CcIM4r^S>1OkT#xDfjZZ3qNek(NX|+3}E>w zNVINrAT#0Z*Dj~5CBH%i7_1gcbzZ>mA|85?V%|ZGy30^Tiz~QjIn4D>LG-IA-nsM9 zb%l@1p??>=g7@-`yb-Npy1JT^1Rs9vr|-C@gmvgSicbHyz1{8Jv`z&W0koeNZNmrz ztdsRwFkZxZ<_p|rX66g(c1mG$>7No2%1h2S^l^)qZuyX{B(V4zY9#A1DIWyf5@cD& z*z)w{o{>l+AtULiR-L1-K&^j_%pQx_GR);dPlr(=od+VHh2@JacDdSS{l}JDt45Co?*wrJk*HaMO)pv z)NoU47G7UoK&K^5O;Fbf(vvjhA@#ODRL}&?dVuuqIB&LBQsa~ILdZi-8KTfr(n<*# zGk{6RS65tD#MH{ehd%yq-2C5FxgxV-4m*)kt$J26zASQF^kH25D5DRUdXhMD*Io<7 zmzYYgGCcG>>9KgWm^hZhqc33&TKpUGD#KQpBleT*Dn6%p?}$4Z<21PoQ5oJPBsD=N zpwy%|t&I+a*6=B$hTP}^%QpJxu`5bF>8>oh#jF5l78nXQ#< z6G&-f`Qg7yQ$1a*#|s-`hh{*vB0{1h9R&6oBbr7KX9B7iaikr^+s7rWIFXtce(1z! zsF@P)0=;sXW**H+6(w~F>m~4`k@_>3hmiZtQC5cyuHMBfbzp{hA=LP7$isss>~7@k zBZLtN9kgfz9H?haUYyI3A|kL&e;Y9?&ai&c|zAq5JY$T)5h>s&*iaX3aB z&Rb8~+rcqzBm!Cfeue8`s!OwzJkyUQH;T8{Oy| zwvw)em8*i%WFrzdXA1{Qh0mxd=i3st4a-g&99>t3wD%4n2hjumkUBN~iPC4ZBhF`< zV32dMofor9K_p^`vJQ@CA50TMNzLutQv1pUxK^|J`_KKsYO~jW5odH*llfPZS_$?c^v?cAsMB+ zE0h8Y%BeCopW{9~ym_3yc~LQaES-6Aakc#|h{SsmsWZ0^8k1ZYoEz<|3+hDN+-PFe zyrTZ8Z`J9VvlwU*(ayJH2j}+*R6h%r6Nbnc*s^UbZ1O$K_pGv)zt|)Y5U}SINd;Zo zi(d-sX`Wj=%;~wdVy4~H6mH7R&H^;mn%yajae@aP<`i2(gsI>Jkf(>xWXjt)yhJn>$(@muf5%vgO(h@Yq^EC=o$oHH^!TlP1qLOAgO!4(i7}mxJ&jgV;x4A{md8olyuj zL15Nq>HR5;iI2tU$OEuP&rzm_P0KP4ZKUGB12_-i{tp*v{J$}Qu-aVwY2+Cq%mPs1 z)f8Ps+Fkw-TU^`D!(YG+sv z`xnc$q`Z#-%MsaHNmVOe)9G1>sW8+nS4W*!o8zoUQcRxXteVn2pD)9&M)k%=IEn=% z#GS^;W-fiAOog2g@U$&j@yxa8fa*EUb-qb|0u0%mA&2>>02R6w{iv+33hog6nJ5z$4Q zzXgx7c|y{al%Bo}`<4x3sJ}fNv$H!{x!v7vzl&Z1d#7^GKK?%|2ov-)uLH4C6m&b6 zEi^H7SlDbNg{#!bjGyNtYh*`QG`9~a;^UGl)UVp;lcKzw3zRTO2p-DilxvJ3j zlE*t7KZMm9ev3o@pfMz4&3ZULP7|2dt98apgay1Ci2{&(Wnq^P_`2fO0;TmK_)IlF zU5OuOdtn_NZFseLYO20EbyNV5RUvC(C_GL2fxh78!nFm>wm*?OmUrJHV^tV~r4x^d z3Vpa?FneZ^)I;Mr;Na{Km;)a}8z7bTfO0Szm!&Xw8k_KVE`63@dvAnB%tnYJtM@1$rYIe$U)TK;#?KEvzb}pu1Ve_+2uB z+$8k*h}BeTG$2&m1(Rytlf`B8wd*10TkG3tFaEoDchNy%oiOaH7;{q(`_b&df6d0u! zhqVU^^5wT^X*SgK|s#+ z(4kbf`HK+`lmLN0q8_N^G=({8(pcKF238b~;}E)HHvogu3W=bzX;xz~`~G+hKDTHa z2d{CnGWVaE^YWZZ@XL7o9Ng0O;P$Pa)2S77V{#XE65m>m7+bNnSh z2Z;q1%eAvl-)ccR$>zD=(1Z`PfL;CsB+?_a>mKu|{u8n)F`0gC+>nQ*PGcJ9wVvUd zfs_XTlaL6NS&6nji>(Lin#%o<$Jm#F&`mvIlIKBb>Cg?mP(x)kgD&!XPi6V!6+}Mf z`|T;0j=hu}S`i35S4`NygD|f5C${Pj>NrYMUd9TD76_Oa@*QDEL4B(#fFrPM6Z0J zw$tE%mVQoNe)d7Xse*XePP*#gLD(P16XG{sE*suy!ZqwLv^gsT(H*BtKbKv07QfR1 zlDVL{r9i-zcjfkvD#_$%TE zF0}}+zkedIw~d5+Pe~bZ!kC!Y?WzQn`qo+ABUbBpC--BvN<;IFzCH7*lRwzK!FtA` zn6EPBE~?O&VOE`5jEAG$7pF_$pEGR#>9#nz`H)3fUx%StL~P7`5<6sZpyi#d*koZA zKKdHMNn!%c{QGjNL7cMAkzKrs+gCnA+CwlJQdOzKSG^;#wC2Fs>%^QOOLO*@gomBf zBw;S~k1zRI=u9loVJXum)naNjNAvZ**2$;T&Na^n9A?2=EUnb<8wZuVG=*Z;>k17+f(0F0PXT zJhw+f=@zmm;!|FzSHmftASPOz{3&&Gzg}*?QHL!2nqSMKkLPw;9G69uNn9VDm*QEo3G9ugZW0>iW`^*f`FM z^>A7VX*t1=EF-yVlLrtMaJEeg>%y^s^~S;E!dy7zM6l`d3e+m4`N5`?90ypN-yU@(fl^NF`*iPSAdZ3 zT%XDpk;t}6=_^73G|pBSektdlLi9(5D%cs1&4KDeT$w(Q{#e8X!p@F~(opmf@M!`a z&|3$dJB>){SXuFj82a?#fvUJmxO z*#3O|@M1_;>vo7@;Kt30CJL@~qQGymQ4IKV;$bqRT(kIbhSqRDpHkJl1q~+G6vC~{ zD8tI6#98Or`6u^o!1^wbnd%BQVfa}3n&uX)__rLZj1aBE0*dp*i z8ct@R29et6%yi|i?Wle02zWc!4io8}QcN6W$E`nVd`?G;esp@yP@J&=!mhHF$$h#3 zEK6(t&E^me!8~`Ejugk(#>J&qpAZb)gn&awavHB*ICpM`E(lH8^9C@HWSx3nIap$Q zcsnyF8pH14L5BSl{R%^wzcM@cc6Tc?iJf0~%W}MsUH2Jk*;ptnug|r=v3m=B>fCS* z)%#(q)U@^N;KZ>RvXcs zf|Me;7K72{O{Cwx7#zx+df|va9)L1s@h%EwHKGQEsG{5&wde?qZO+*)Tjv?HlGsK#-fMa~__jPl^x*uP_kces5EW!tY%)Ae<@Fx$G)&faR{A9L6jeJK5ibh)kQ$Bt!i`edV+*pz>H}{ntJa z2B}xDlR=mEpQx+kiEng)Pc>Y-T%PL+tiDcfFdXco0F>Q-82rRrWO8a-X`9>)Z_cS4 zwS#QJK2MBx(tA|zI8pDUjtBSy|3#bPZG-T8mj5|k(E}}6pjSkdNBr>D)-R@No3xI% zUMb4;3dFmw)P>^(1+T^3hQU6&TATREloG<*rK4pd=5GN*#lCl%dL1HE!5hfpOPn`0)Mk5 zwp|utp-)Lh#3j>s!uj(!l;&E8lpaLBYlkdLooFy(S@jZ8ncJX%JMxN(#f^z4e>HgH zvi_=0Bj+k*`qzv9_dLVds#oNuW1!Ct%Jqk|d59?leM=lH(i&{3`oiscstgd~=NqTqzRG;?rr5#ZjLN*>&_gP2Z`} z2K2SsJ6gObfH&r(=*AuC{O|ud(=}or3Ft%#006oL0D$qIH1PjxeC(b6hXlU#*i=c_ z^W_H7*ue3#hHPP!G0DIpcA1pWz}cmiovoT00gKNILMI{vXsTK<+cniS%(beV`Bnhn z7kA0Jwe$&>0z6%6U;bVyZ$#?yetzP0%{@-Ek~i%PU9_4%YUHXE=}$D~V4rm94ac>* z2L3#LXqb?l&DhHXgX@KfnQ7Q_S4oZ3%kZ9xa9|Jh;7=@eti1aD?3Me<4DGx-)eRgJ zPDHtD&`43ux5_NpAj9$*s=litEjo zH11)8S>Rwxh~=4%WV5WGiiC@R=5ztxtsnBIP%tjIS8I9i%BqRDk_P2g{}R=xYS6d4 zEB6=o*ST{o2&k}cn20R@tkZh1;fy^McBjadZZvN3bI&$ZqT4X3bY zht9#=g%k2OeHTwwxdSLg#yt~gh-asfiO((D-@gr%O^kZBMgK}d!}x^?479~fWz`@k0MxUwNcx0&LNpc9<{bNpuYVYQ(O>5ICvu4-bDBD+%9)1NV8 z6(&loW{Oe=xXuL?dsy`M`2uhKce4M7Hk8)quQkYBe%uZ9o`*?SGaV6+toK_m*ZXeboURsU6wCg1yEPa?z4#XQ+S4$m|x3@sP=LI>9jg~bbbnG zz1ZnGc*tvlyoCn#sx-5TEqf3qR2N7=V7&3D1I5%Zy2A_dZS6%#7B0VOLx_h9c;lmZ z&l@|CfIx&hs2cZ5Gy{Wak>B-!WT&DRdLh8y45ZhayA{P3n%kVJS|5%hM!YJHN*%uFia2-TD!c7qK~U7Xo%Be(DLHoW ztpx8>YpXzdy1f#;naV2NR#kU~K(yAIMgcq-Lnc(^#2gS+_t}88rrsf4tdO0c{%_*y z*u8i@sT%bmFfCs%!X;58$#NDpvZt{%~KV02d zZtZmxw*XsAm5+*UsY{i_7HjKE#K0wPt$1eScbw-`#Ai0z_(oRFuJ}ig>}ugydow+I zl-l4+beQeHXL?MBli4A0Yu%?Rml`tV*K-GsP6qh3d!!7JTkT|=igKhH{c|8PR zwR5S6K$mkMcEOy29Uc5NWr9MM$vrS?=hu4bR!=8+NhgwuyCEOWmW-|inm)=~Ue#CY zVQ5?3@_J^)-IQt`mc1GzNQT(`8G+7JGEQ^uU&Pkn(RrY%mZkxketi52e4%~-iI{CG zqI2k_wZ=VR79zayWK8H_8_(PsU`|D>obU}yy(LLDCkA~ z;;2%SpdOu47@RYE(^oPcJU~~H&s<9a{GF} z?}@F66uK<_JI|0hd{Fl*Gl`fNOVV-ECTD0of#$&z{k3=@GK_ zm=2e_Ph6MBtHCcgY6qliVCChlZy4z4?dbiP?|1I+`(ZEi?YH-B@9*8u4{qyjXukIK z^q-~l;8EY+hkR|`weQ6=AI2ZUryuVw_v(J!!D?#!kxbq|`w?RIqpMsPwiDv12r$cb z{SKt3C(^ihuU|s`6Y@1?RCR{WC7J$#|Zh~)MHpLwxao{EvAg^UCT0%!h6ZAfSqzRy6fQ42d>7JRVk!w9>tUlFxP$>9k zIOTtS8mj;;n~0*e`pI`}E8^&z7xFjugxUugKF8b$a+LUIr)D?K`BV^h}Da@f$ zldFWfEz4_|q+4q|<`wLdJDFA7CFO|(OR2aVAT1B#jh~UUpFHx!CHg9ebP<^34reaL zwoHtLK(q{xuE`nlBN@aM(Z?=Jp|K$g&k*uh;PEU11fR}e<(CDo_?~J~*MsOE_lR`; zA_9!}mn%s{ed85ydC~Z!qfVy>xcek`Qq|!1oXezfu#qW30IFkxc<`KF8vu!q&6=qg zWT47$?!Fy(T~dG`7;%X{Wb!b zoLZpAk0^cRfAq)*RV!0N{E)>ucYPU`fl$Z1iWloKhdHEUxktfO`ZNdp?3@L2Pd@CpnafTnTQe~25AOyJqYyR z8!WCEf1VMf3&kVb*_j-B({KXi2XxtkbKIF5!d6tp?eP-OxVkk#M3uFuXT zPsq6m+ZqoL;}d{p|CJM?z||4mNEzgN6`yyWVPr+_Q^p=7(35TGkMP=01ipX1gWjw= zbQii3iO>U?myk5K4+$HP=NkDfGxwtiF?!Ieh8n6GLCuA6JgYo9z{TvM?A7YdVE_Hi zpgnX#vsk|kBv{#JFwhyb8t8nV&J^*67MZXCYAtbq7C{~?20-(uQaAp(^eLgdQ;UYw zOH}i1IZ|yJXFFylJ&fO6>UXTSmMsEu%VB)6+M8W`+a!EZE zsw>L~uZDSkrdNzr=pY}Mz#5rHkVhQ5S_x@IMC)7ivvpfPx+vRoga?<6DxKQ$6p8_b z=ZHnE=@QP7D(Z5P@!Rnn{$hF)RB;X z?p5=CjH7?10{Fkq(=wTxdm@9v)juhN-b|3Ib@nSq;_6a`avqT~Dy4WKcHjf%P729r z&Mv>t^SQg@^YRp@+yOsQp)NpP7sU6Be&~R-u6(NKx-C$PFte}2)0_SrN`r9Cje{wTR>ArH(A#13P&o|@x#osJQ)pV+{Wf5oE%ek+Nwp}(O5`^Z#Kee<1t zcI4tU%;TG1Cazo+xo5M7V)H3}{LTF4w^1O2;)FpL8Nk+lzWH!IbM4tC+OI?j+yFBm zS}3%GhSbkNKjTDJX6CQoXA^XEGQsZB%&)BCmA^$bBPVEZY)bjd+cMIh+^3!eG`*oF zKC;KB;i3jr9Uv!Tvw+j+5V^oXCO+E zW3$I0t%d|Hf`aQ>r=YBrY+}LzCs!eDiGtxz734-UVo(?m?kpuzPc6Juid`;D@39=w zmn4*cTq#O+(ZAcE7ysuHfQVB&KOx&O7xU2%UoxI8HF23g0UDl1b;&qwfVjqv2Tb#C zM{dRS6}g}m;sit>L570fSV7{SMs%9B7hD=qJQ#**JRJu_fBtMgW#MLVKOco9;(zyY z8F8d0As7YH9RI?|IV}c?jGG`2D`;pH{k!8n$I#MRtmqnpT3+UrKu`J}F&zy-_69_Z zN2EJtnfE=WafG@$4O<2G4w$F@EG-*nB|?WTfHt?#Q>^_(_D1I;T8bchh5w{P8D;`q z69bHM>>PlS$<4$JK$VE&iJ!qKrSIgqLICI*KxJA&p+`Dl*P+zerM}8}lkzY8JZuPTPk&uuVspk*sh``YLtzVNj4}qg!%x{>!+K!y1=( z#)~~c*mU2?Q7Po>Mg)!COTZzO)=R{4q zew9E7Sg+Jj?0kwaqGXIx*tCk+ab&{-M@E+CE!Fp5be&_5AWXMK+qP|M+O}=mwr$(C zZQHhO+wSQ*U!UZ@zoC+r33fF?NIW8f1&WuNMp2k9x`;XNb4DxpSTq;gIUPc@icL?yS7mtjDF zT6te)21b8|0C`7}2~DX8I*o1HqE5UI?%knd*7@q%UIp&V9_^_A0C&F!S|6{ZVj79m z!2ee7iZaKZdWZ+&LBN@K7K!Q+i26ZIfk!8p)mdykZVElQVUwUL?>8R6AuZNO-9A#l zEq#c&Z%+{rTI(>O9uR9}us*CT;3d}Y{M*%qhgFQpIS}Sc6F%J_x`bukeF4|xe{qu1 z!Ra*kyg}VqezY>ULy1OQn$7dxW~7y9L1oBP)R%$TYa1Mfsb$m;sg;mz_z>SgDFJpJ zW@<0`U4~6>2!u8Qt6&+k=XVbnyKL|7U%HBMh|rHo-4#m+rl z#fq6CeAF;Rz-%)5oy-q37)iFMKyNTZ{=E%i`CP{68phI23uOzApS`qw^$N>g!4ux+ z1%Ts^L?Oaq;94vXN?uh7R6m`$CJmXRzqtD58K>|PV*FwVy>OzA7br{#( zZo<$TwRuL8%0F>BgEPryuctjNJRjD8aeEjCrP>I}QMkPnd%LGQ4@~P;ys_6rvh+b? zFNphqhttbU3rwO}dBY8apJaJN?a2A9j6jB0Q(qh_ZEWbaELLYh3GmcTY*mP5`s$$DP6$kW*O}pw{)n}D+?AE zuOtbUlm57bRSjG~%+tr95K?&-bwoT^c8%piKP9rzKXMFr90C|kD~53Iu#X=_HJ|>k zppb@XilgfYTFIdUtJU^m#?A>^YpMiYEt?loVkU2>eCqWI7_LFJjz6hl3HnnPAZ|U7mfL@$>8B&O()&n7HkmG|C`U9K2q+ z8W|+=jFQ2`J4bGa+WIa_A>cnjz%MP|Z!bA|Fioh)D@r>hVSbs$%xpsbGHgflA)$Dv zr?bnAa2ENGR(efH!KqC1|Itit7r zUN=v_6K~uQSE=TAIqaWB+|?lehoz)&k%gDwY3N=5^oOG(9dL!N&t{6v8xZvqH~k;t zc)!fYz6?sa)%v*}Lt|d2V6EPPg4bK#!tO3lA2zqU&WKIcd%$ik=5r#wd?EB_ItL=2 zzdaiFRo`wFopq0M22IrEHRsC_e8Yx5F0C9sWh_3d!J;r6SBNAL} zK9s)n+g@{{?Tw8lnQhmsEsCMkYhl(S;C)geW1;@~$TiF(X10K>m|V~mNst7|JdPm@ zBS1GDp~xQJGBm^S>cawURD`V*b`?_e6nagJ3tCR|j6N}&`tUO29eWe=zI;%K3DKoR zatUdPp>Iw{v6%|LIyWxH>>J$+S=4@T1jKg7niRjQC4xX2cC|vDkk)II33{^l$;zD_ zybw$cx`ZhEe$*lNZ~1C(nPYYG4Wq=$c>+zV`fHV|%o-Y9#IEB{YGp=EPr`j(xL+Sr zTIcJAszh;wgs9a;bh|v1hw8%X$Q?_lA##}jV%zDEWz1riB-FU_z|_ACd65^Rxr}F0 z=CS$=3TAl=W)z^zOR%sOI(HKX@Sp|=gFTbOY46<+KRZR}GRpiM+xf+pL<51Cji zFrRKIdSN(J@DF(?{7w=xtY{h-gnKTGbqDn|>~A(w+b^jT#2Qq~aDR`i=VYb$@>&|? z%r9wHC7`-;1*cP!YI2?>7Vh>TOdSB&%+3j8gq&ANVGR!z`y9Wntt7us$0!!G#lW2f1S* zZi9z7XV8+&;xeefyG8hRSM)4b^FDVg_Hj$T#A@kVJOCVEI_(xQ<9;OxUua<$U7mE| z5CvP#Yzx+ac)rwhGFrEWW7Dy#gKXJlJ)Y}a)g|X#-Mm|_2?(jK4juOLvl;-}p2n2UC;{W(2;nPW}YJp=UcEh%)ywcx&=`Ey-1M*F4 z)4rj!!`nFSz!dcEQoR5>p26f02*ewX+x(Fb zCOYD>XuuVVuu|l8iu!ey@kLxKcB+|~*-px=c)gche9O?6G`rAn_#>rxR+pR&R!o~D z2rrg8kQl$9wyP6d8zv1;1#pq{6g(GTY8hRzw_QO>WcNUj#4D;|*eZAhpo(+VFU}*K zQE0hyL!SyLzUMf9GN^>?6?Y$^p6mY_FLhwBK|fV3p6=TE}<{dJt-|(Q-Jg>}>Ie&ypWn&PUFr*kkj3%vj*DVXj9wSCCb+YJ;+7x$IXa30y zi;9vN?7d2rSDr4Yqb6lce5R@&WX&uh%W=5qGkXM9vTCtaQm;NxWXW-~CU>tMl7qUA zl0$=8@T=}&*eHAA;0_O23|=tg)gAK$!u!6uH+CK5PkmxA6X?-`Ozg|jKE=sg4?*1O zUzm}FcCbw^vf9XAY#rSR`rqy<177d1oix@2PR(SuSCyMpl1jhR8^?|F(XQju?wUQiw3+Wv2k^|krWuuZf5*I0kD%;5)SzdL z&((`{R{jqm)lCdMN+YiA#&1?EKG=L)YYOwodsyv->3%^ppM%n>lFBP4E$OITp)EOd zYrCi$Pdy+f>se;Tqv*?853U5;S|JtZe#S3EjUU$Q>|CnWqmg8L){X1G@UwyciUv}o zs9ifN`0tY>y`tGhXoz8_42JNyCf=UeuMz*KZ#F#_@6y$G4dX((z zB8i9H5OSz^hwq2-A#gVss|dmT^H z%uyX%;90@kS)F0!!wPv_W2(FHZfU=cB1IJ+h02a^SpKCN3QPMUq=d1G9`mtV1L0Kao+#HoI6tmgb)^ zsUzf|k_J{^!!!+w3F=Em-x!N#H*FtsT7H_T*YK@W>@gD7)-iN*h>||J9I&$%ir#}3 z={M4F`O{cGD7Cq+LgwPXCh0B$DlLR*Wm!QU7S6fO^ov5~3{$uM!1UuW};4D&9=0FA*MCA{uDaKNRH?EnLV^|V5 z3#GZ$V`+KGPm&iq=X{T2n#lgLvm6X)ce5~bj35$R+kQsbTqB}ze21H4X)9Jj#=KBR@2fAB#Trny}UOPpNLrwymQ zdKDXcYoXvz$|L6igDpIk$;{GIzyX5`%ZGIn5owb7SQdojX|=_WJl4fFNUuoHKh2zy zFj?kxN=LufR5YBYS)B;i<4`A6D0&9zIS+dzyt}A(?pnla6n{lq6|x3`DTEc;@%fjSmwRdNzP=F`#eobuVG|boeoe z44|t3uyQtDmd1j1IDzJtvA=NQ1mC)WJPRCWs!=bJGVqc`SLG=#wc+$=ntGgsVD2FB z_K4uB3GaoISu~9GTf_8}Zuz3|0Z!8hsH(o& zLkRuMX9sN0WYjk|sFFhMltcXD;)!;k3+_Nw*u!td7QQZe&V69~!1>_*ViUrH@%@Eo z>i)^2Su)NzkA=E76i$-#m(T_X1K6p{4*a(WUz@pc+ZP7h4dKj+yWz{^d%~m$tJf%A zjq}eVBN)e*@YYY=qPPPpr{_Hw>uayh4HAG0ypS*6z&&Q9S5E-tWB5wa2+%^NJr-eh z!j+h^)XVJB_kB_*!Pn3WjEuMgQCodl(WR=shnXVDX!sK@SLN6ejkk!3gJ-bnK8$n| z*6S27bFJ(+dOxXNbG0Ao2G}c3{odok48rHxQ?^mue|6yiHmgSO-EF8#)3MQ_3bMXl zn~xYGBxY$4fl~at+T_80wd(UguA)K6I)0n2I6aTUo$_5+f9>&!fbrKiv=TFJZ6Rtd5Vht=cwQJ1-bYRmzRvb^rO!*uAqT z5cRRq^BN)}q=ahkRs3Otpq%gVxWjXf&%3ok@?Ft3v?kc8Q_(7a_DB#PXpfaKjs1(dzo0j%$t*8ftpAh zNKpyvSK?MWwko-)PYJAq(kkDsJT<~W5@jX*_h-3&QsVH5cZOI8s^tcjg`U~Jjxely-`_Z$u~^Y*<0KBFqVP6a_mn=6IcxoZ z6Uk)9JhtYOW?PrB4%X4=>HU+AK=zt`djksJBj4rnMRd*IKmsRAsWw{J>HF#$rt#)G zp4o$dPaKh)+f|s>b$Ab&GNU9{w&Sm@Nxp#^uI^NiR<=KNdaaQDh1?`x57Df#zF<^J zPq;Q@4J{vhAiL!Z=WQlTWao2xlemtK`dy8scwzgNzg5iM>!fSCZTMs2k-2k^`@m}C z7GzK|je2{cij5sBOdZe;(jP5);D6VsA1eb0i#phF1G~}2d5A#d5`~{`$S*mE@ z@{h_urLk57J8WUFX!WTKD(*%$fBEA4RZ|3Zg9%{JMeWmO7a@hPAUT=pLxnpx90v~t!riT3iJP`cKkY?hG12_NX*T5>oHd4rW% zf5?N|VYTU>O~!;woS}ZsrlgZhjmvl29;9Ke`WY?UFs%*_f8{8ch&5Kc%>X)vDUWgC zw11r@*_owcPrhlA@9K~BK#G~Nd9DlK9EqEAdxB(v>E%Xgsz(KzVRDaQfeQQYN9YZG4&$-O6e4~@{^mYtYwG8#5q03XOZ=`Yq_ zU2iC2xMTRe9WY;1!UFr;@Q`_(K~Qh{-jDo)ehBM2zaN<8?i72Yb4 z!SJ;Zc5G^nc(%15=ScFkfmf@XOMZ9wSHi5=*a8R(Y?4x1RZz=N8mBjjpdNv)%gc`4 zOw&spxi$)c&+CdC5c&%@gi_vw0%X$yJUFDD#`1Cv%V!7tp2w%^Q% zSGqA)4RO=q@*d5WhZ`zpHy0s-&=+ER8WN|-8Ly7mre7zoz$Cr=E{i@`FMssK$rH{Y zC5^T|rgex1z5;|7v2XBg_Xtv+12yp{YGK1v;?llcL!gIVmcbIvIB(_qbW4ok+b-8< zo1t}y z-v_;Crfw_9nQhP(!XXS?hnvUU<^J$uAnIo*sO|HZNXoYTm;0wb_KG#YV^b%`$v8}? zk|swt@bI0G*lcmfwJYktt3c*z*7mL*n2#<9$XMV<4_9=MJ;TF7`_#;sq`$$JCJNQL zrxS)>YH|ZHjk7*jw0^via%zHzNL(?2Hy&%uE~(*)Z4$-gQjRsE6_+w?6jG(vhV$-n z`Rj~NZEsR6<@czm&A5uoZ5?MNw*t8c>s*nBOHA^0d8zbEO=Y$wmFnjEQmUBQN(xVa zG_zerFoG9ysk<3cQ{ys=MgRvkd6#S+(qrS68`AC6DDfZIn=PE>nPS|J7P5tob)NFfnX5J$DK#fw_8CHmfi4e6=duQ}2NAmYXpsukI@!|;3p2*_P zTPyG~*cq}!wvie$(87f+wGp40{wx(uy86J8qnEI2(wTpm!c(+#D3TY&p` zi8rvyN(AM?GGCY~EvT#;zyoaJhdmEu|65fcAt*!Fl^`5_En@oHN`tjxT?OFw*q*WV zs;hY|Z(9Nq#`KlvGfZ>jDgs%%b=QBpc^A)OeKDz*kVv4gLLO4wMW!@fE_o-21XxTR z3O3e0`M#c`cDZ0IOuymZFsNQ?pGtrAW^FH5Anz-W`uH|}_AYMu@UH(_M2C759<9Ig zX70*VlW}updvv^$0S>*Ml6>OjH?hI-u`*SpZ>5ZmCS$vubu$uOZ0kl$1K!qb=VX-? zUiJr-3?<^4HMLQ9qI4BXGdD<4RR2|{a~it1w$@Ll<3O8_a&3%i1_6M!kik!kNo#$B zgIAhJFak>lp9MYhW|KR~V~U$ws5l!Qh*jWkKQ!9LxSnP=n?YAl%w59rs1hP~5F)}w z4Bkz-lZ)0kh~m_yNK92%It16syoi3rqw|?)Ef%{VL9O?~Jx*Q~S4&6g5Vp3R<|#^; z&^{l^u+(xR8QW8s)tXIhbVca@G*LvIcn;M;)12~|=S0k}8vi`0>-kq}v@W@L7MA8v z?4UY@1f3oy20FhM?Y2+pz1&p619QUf8pyh>%o7P*EHjA=wmB?fpUCUcUk%TwT94Cc zPX7$ac;5znYbn=wLj^~DL*lbC2is_2>CM}lb)D8GIoAO=`*+lW^^Pv0)K98JCYH0~ zbd!3+o15S+1wpw6kSlsO>f|QUqHMtu7_Hi3Hn9{-C65jV!iUVG*hqv^P1&W ziLF>+Cl3?f(3o1b_snJ2U^EgrmW>vWC05oGA6Bmo)1H-wxo7tc?x4M?pazEdyzo_0LYaB z0KoqLQC%Y&11pn%u)sPJ^F>W#j|~h@7gI}0=LR(J#f9=xe~7c(9<)YU z5=8{~*z6~uG39I99fzlSB0eF#cZV`t1Z@uSMR%O@Fqfl?!=uj~bM_*8k=GDr%#%Id zVzXH6>VUJvo9{Cm=iRtN<8JYRVMY=@Jnwf*bZWA9fd*0%W>1kqqb?xMghl5xi|**@k2!V{(mN_!^@@K&qzZehW7%?DfDvWe9M-!d0~${h#XSJ$wV+_ zv>OLS295W%l4-QnZ!<^vi!H>NAQP)MDoKtxyuPK`8htvUY%8pBK`#PNyWwdvTRxaL z;f*psJPw7NBG{vyGHaEf`bghZ7&OWtXa6c8&6sb_uK*({;H$Ish)6>APYwKfY0jvbNA0_R&sLLb%T$dYF#C9r+&MFseMTiAblxW8J(y_ilNH=Za_ijhhoj$D&u zpW?1`B%{y?s}XD|Tb*dXl}=cKY7%vTpA%b#`@;zybu2A=w1sR(^J3*F7 zK?l$Bi$V#A{Iy31=njp(g3^0uhH8xdP7USwAsrvQ5TWmZMSI4UNzle03g0BH$i@pB z$Ro(1`=L*rOnMt9ktl4$3-X5&dbvDiQv)xZ-@;Da6@is6=RCg=?1J!(|K zsV;|f9Wxu&vs3~vNDqUKS7C7ayUqJhaXxpACcAG1qgR{Za3A|JnnVEK&kgXcp7q^1 zj*2oZf{6kTj@##&vzeGIQqO_UQy*kOKlA5;FN(f|$%ki98B++y4^TiwJy^R^p(A3E zKyHZU40)+^AmsDOZpL|9!^+1bCzg-1^V@|Uh)MWUGQ=iINS&?$bE7!Kkv}YMu1^~$ zCbSPmF0ANaA8Nn0rpO)mG^|t*1#q?#$kGSWSI+WoaNV8F9l&tHEPatneTp`wt39-n$cuX&8hy zsVo6P23?T508=74iC!q?KyzGHa8F04olOW)$!d5_*gYA@wjy4=GTwf6z(GDLEo*&02%<0F~ z$;rO;aUNWq==x)`p5lR{J=FCtn7vq$T$qmI^VG`6#o9vu1py-LAzW`9iOqysy(i=_ zJ)wBsG8+)9EgoMRp=Z zw4H>ibQ(tDb&z3FPaksd(4Fqq77TPvwo{w{+m2+z$M;J@OtpWW+sJ8;S zfoC~zn3GL+6{0ARSXptf3%}IB8Jpvg{QN~=&1e0UbH6_@Z<*3CL=oYF?mj>4bUP3W zA1BvAu3EhbDu9L!382Qgp00tv%@(x2gk&xcaJ=w#f30ck6nE5Mo|Bg}9z!oP4w^lv@{Hq34U{Nz`< zwC*TB8DX=GPzU_!457fN)*ud71S$~MV5T!{L`brf%^N0f)d0ZeT)G?#A&_O~gn03g z-}Km2?O!s)nF!RGf08s#QLP{hDt^pq)rF@pEnnQpe{!@2+5W1V%=abCU(Pty8*s-& zV!o6N3+xgkFf;Rn$+(LI`19R>9U1jo12w8~35pRsqndla;5>6v9%cj^!SkNYGWpBN zibXJjv7}>#U|`sL%%XEZ2jeui62lL{;^`VF7Zh`X=G?GFBxnN*L5?TW3kLej6AV)H z@)%`WAOyl&NGe}D?xC!?$bdKn-07?H}1-OFD|4Y_B1?~0M=%uUngMmgZCW=y?G&x zzWP15Gf=~h?YRUr;EpxmpknkA!`OiZz|b)7JN1=h!8n3N?s5znkAu|`z-lZ5{Q`;4 zp|dD~1=Sz3+a}nT=P`3C}oJazQ8PzLO=mNQ)q~#K!21(;Yt0_m6JOQ zw!B|9k}@?bht~48h>KZS=N3a5=i*uvV#4*LAqyw!WZ8LTMR_?jY@Kt zaAMabMY8Ppg9wrHi>fpp%)i(4C_71rvbO*w$z`Fb<=xJUfqjaIT^n*3&l~LU93UH& z&9x@#Wnuf~U*nD9o|n!zCTu`pz9pFjDFD3p42)d9FiN-`;l9nwcm?!_!MR{yx(TZ^ zN|SHbcBO6l@rEoIGMuEv0)XAAuH^zq4?oXRRTg>yA!BP)HwZ$Tw^jxfGYQ|{tE8WC zvCVCGfcvLKLS!2c)CIK5S_|X1iHBx4>t==U20xE`J0L)qNlXpwma62Jh62F?%mxhv z;(}`YW>egDsu^d@n1oFa##_KB6G+6T)}?o}8ckb;I^10M3^-GecjmpIkj>enW^|2) zcPsaROIe5)h9r37-Q^Nb&MjWmxqcGbCQ1GLq7h?6U=Nzkvqi(M`UZfWFl~8tDVqNt zE?;-7({XKLKUMy`%>Y3Ua*xgnRgS=MBY+%Z?|KyZ`~s_KQ&RK%M@*bL+X>l6V+k7( zQ&Uf5OVKwuidv02mKh7Jt|nlleXh13dNg<8(2})iu<6t}RBWAV`duD(V_nrmD&Wkb zHOK*an9uLQw;84C8Rr&uHDV0l=Ao=1VZITNsI|&%6Wl;36R_WD3+X0nul*0?&#p7eS-cn2l>iYn2eC^Vgamq?OKfCOLbk;x=Zs=ZUaqy1}w zQI&hvQ4>ZN*4Au^Hd%a5rMLvcPRf)zQ%gziv3JiqxAyvbR@q_^Jz=)ZxS`8PK}>hD zmi5_tBbR>!|B`WwpN;e?u-CitN;@$!&f({vggJ?9(4_Q-)LAj!<{6R4wG?`GAAU&~ z?$Z&`lF?rI4BOPU8Ce*y#E;UQuprIHSDOM)v882ti9 z-Up^I+ET_MQ9pB7NX(mu`^z2fkMcy?xPoxH*FLVgC%t}do<)PJ+@9bLY2mUy^>aC% za>iDMJBHj3_=W2(HHD`)P-->A8^&Ee?fIsZ9O{6Y5FC?BoZl~JHKP}*!I}nUe&=< zu+JYy$$0yTZBeR3KCWC}0%9U*$dia#w7aNDLk3GOeHTk>1E>{CT`b04`&w-HirJWe zM+(|<)$|a6B>K^AJO?j?`+y_yBoVCYWK%PBri8Z)hw>(={dh>bqx$XIWLhijbs9>? zSI7q}>3ric5unwLxj>EV2MPSs~A{Sr`w%N~~IPRX3RS&p6Ig@`moe z#J}E1Q^s@mAI2=V9jfIZMbc^f8Oee$AtQdRF33-eX;V*6h#egln`^)4v3p!Q*m!D3 z>UiyA_BLEBq72ToQ!SC{d5&b#5K8vH$~6DP)Urz^JAVRk%4j0vdZ1NF(*M}7wzr8V z8QS>SW3_p4l#>QS|uioiEzo@IDi|}Q8B;b(4?ApJtPxS^Vw9{_ z69}Lg#-*aGv0$ShThs-AO09eR)|<)-umiAWwo_4Khb|~K!q(RaQ1iXYSX1k7&CL;9 z0lo&SagI24Wp$)Ib%c@#5pT8+XUYd^?{-%~G8ZOp8lg zyJr!YoXpT)BNn?qQCT2AhUDB5dp{hr5~PZt98Ic^*wW~4X3L;)7SbrV{DucR;f1vF zY+${T9fT9MvS!@M5Q8qUux$v+vZ%orpud}l`#Z{(>hZ0WQBWb+d4F-5FPfIQZ@h$p z8#}ko(uS8XDFhOJ2^lM}9!NScKq^>>Pg4F6X z=fm>dH^AY{i=X?Bjz-B4$-Ps#aLd!53p=10PVnyAJA`HK5HfM-wy(c zF3tI_%VrSpM|yD|I~UfzVBcvmj&Nf|5<+z~3zfvL$ORT3^eT1!S{()9-`O{>q^yy?=sDN6~r6S7A#q)?wF z1N0b>F;MyTO1`@a#Rg(swbUQ2%3g`#FsQuNphX*Td^RY^3ltwBWIN_7J(QuWn`{c> z>EKg;J2acn%iRa5m8WFWx9I})L;0(OPpWF1gMkM6s4sk7Inkfdpcq`VWKpvcK=izn z?D8BY3Awn3<6Xz%=V?~KN1UU1(UwxMwc0b%MTo-k!LHm$yqAJjl*b z&C7BscL=8c8p=FG$z?cnef(cY@f?M>o&;dGv4fgxB2vyyx`*qeghz~LsX+Hy;7+1bZZJcDjIyfByRKEm5K4(Xt&bXRJvX7j8)rM zK?|8S4(hRgg&{g%w0)x%XPIBp_=ZS+5$PbtL9vfcv4(4MQLnROwq}JJLR!ZCU7Dfl1KUyC!<|meF1K+k+Fu+(-}Oa^&E-n`&)o8?+K0jmxU2HaCy$!TSEdx3 z=5p2VLVT&d9NwYe^p19Fh0gYxES&FW#m#a5HVLC76xD#c*6_@9;et{wmv-FD+d5yi z9Tw38CTMR!mjAfQY->%>AKUR4TGt-!zoZzx4?;neIxh;(zA6(SD^EGRd^HAyWlCbqu zc_#%C8^x@GCn*<|Eg7VnH&R;haG+x&H7Mba9y=0yiP#gF?DtZ#NiVBR4o8Wt?rJ@0 zu|C-~tJ0LLls19ef99oDW_Ey;+(=^CC@d^l^;-^~Qy-nwsruHCPYI3he+vt*%XDdr zS6Cg6ks4H=h7JB5FTj6ONG|!xejj)M01OEL0IdI!Lb@0m{0kvZw6&bJ+EIPq>iWsx zNvtqeM>=mgwbmS5nKq1>bj-SNHHr!()(wRcsQ$DrN&oel19S%zmoK%J;mWxrEacxG z>cHOzn3a0m8e8+j`8njE3Ex=IN&}5>J8Cc&on>upG8FUKgCPV*?4pg_js`BOl(NpYA|JR%&7P_75Hs0;=>cud`B~OZdAl z_I5Bh*0&Xx>4*7~fBzXb4AK^~dNA^SJ)8&j_D1hnu1Fgt6yt5Y+ncL1wJsRpFJO## zW!HE0_h#@z;xBJKe>K~2qVC_Hqmjq&v#2LGJHBt`BPGYi;P5n>GQQafLC)>9+$5aj z9Ozh3x4wYW^k>ym`n!)-Ys+)WsX!$UGciNiv9_c1Y?0Z^7`wgEK!YZNX(a3YF;D_> zgIgH(AD5t0SL4e#6iE#U4lusY1eC*yEgqE{`tEj1{r>Us5DL22Y z6vn(Wp?R2P25119&kRx7m<2*pnL08o>#%1lb>0R=WWuwezQK+@v1rV(zXnqgV7j?dKgr+ zDe!}{j@Xkmz6~zn_x3o!qj}i38uum!2}Nq=@+GDDk$j|ct49NVHM4-D3Us^WA*^S{@m9`2%?g!}T3=wRRUiPhH6 zV1Mk>&-bntY|DOVCL$@Hv@sb5;^Cixn}$L3`CJ&n{oa4Rw~5%zM4_Cvh;w(0?p z2_eG38NedqgX#T*rMc`SQjnyzSUdDW_A8dht)ci3;(XB3%Vnax-6TChP(Ur>-3h?z zNf1E&IPU0#v(qV=MO(oEnbzzfqb_RX&nYJmCJ`#ky(fwQowKOd^C&Jb_4>Q*OkiY~o(QS=I+o zJj}tlmgT(|aSLWm){`#cU(0S(SeNCa>Ip{9`g7+6a9y{@9o-tac&g8 zsa5Qts*-~!d%?E$1*N?>FT;sBD=KW*LoBzBF2_Tq2T8y-?WNAv&LW2PV13nV2CIhm z*%+{}0sxS!D1usRwcS{jKuK0BabdKSpqZnR)eYt@8_0E<58u!W=;+{;n@sNp%dVh= z$^=yM4qD&7s3w}}M0ROAeQ-wriAWzE71XEH+&0ePe zs&Qrpb&w6klAd2^4utG(8@^tAD39g9Tr>!ooq+tbftFTh?hh=E<-iNbs7gOFklIB| zg02nZ$XpD_tN|->km`wJ(6MsIhdVDXd^5nGsX`@VPZYhYXT_}WWoE}zac-tSuS)qD zqhwgzcS6_&7hO03QyrnZ7H^?ebCH0dzLb0!OQyU-aI0->EU&I;A?bKdB_-S2R6yJI zP{7zn3$Haefam&5uN`DBct~S6u;LBT_c2(>uQN#c=rG26U9nI;q26+;e8Emi(%CsN z)CsWrJ*G-sA*>G>7AR{si(eR?Z9tTwa#J5sZCkvN_}cdF^USdIR!x>OF8ugwT7agM z`S6HqUqUq8)`uimv6%>Y4Ni69^-H>k0DK|!(nCpQZiptes>;wvmBLp6k=lJQ*$kt1 zqnw3K^jfR~d5Z{o#*?QtwVsyw%f`I3RcL`ya2D>Uu3>qkz|S8}6b^|~$co(|C`u%H z^t{P@dQ8<3SsOU}79|wjv^T^@bt5h$jp%7KXcaYzy(jg4q}uuDc!Gp6JUPnvCnIZC zx+ZCoQ-?VT_CTG=gdJ6?R+$N8$0j$tOV+5MawAX8@IY4Mkey&o!KKr zBgcSD#gY+=StT>_9O`$2Xh3p@9AD_EJMqoPcUm)s2J)nFWbj#bg>DM_r zK?#eoySrdwjNRs?Mh}H0hPx-KUHZ|3BYCkq^BA)sLj}K+B}gYl_GT7rQvi7$KFob% zUa9jZ?zDkLh$?pwsX2&!B&p!C?uLS{UHC@$pbL>Qp>1zy8AnUeMn>LaKQGX z^U^H7WDMHO`lCPXjB7rIPwh6&zMo>-N_k1G0Q89q6oZ7o&FB-dI?N-X$Q(^^MGO;1 zwR1TBBgrBmwH(I}lARGY#leTSRiVejp~%B`s1lLn20P`Og&`Or2t*p_Yv#ycm|j3v z*qgQ?U;lNaK6LXHJ*@V4%lecFov_5*0I18tfwTg+z9R`3+u~12pn@wQ)4H{fAsuNvBrAP5Gy2KK~T9{&MuUi zvWnv;5YRcaH07JYGv@Y6?EKNh3pylagtbG$Atwj`1Qvpxd@d!10?32O4JiX4=I`@E z5|z3p$L=9%;K>b{PNhP2ei^o%dh;uNdLOVvBShZr7W^9w(KpZp#Gv0MljFkOo3e7) zm@h6(VdN)=_X8(>t}+eDueqp>ht#YFEV{hM<<6sp=*y6)GH}(loM4NO9`#Bj$S0=& zMY*Hwo^B$8Z$B{0j;3qYzW!!!!xT-oVM$KuLMmK>nSo$f+BIvVR8rtxgPl}piiw|s%3swa9GPu^G(vI@WZ+0RYyG`RTH|_ zDr1t+Mm^FFW8c=;xR#4Av!xZl8yZDDX6DZC_)8ScjOue_^+!%P>Puwaz!B7s*jp2+ zBW70W{fyc_P@O-2hGUPMX-6Zv%pMaz+urE^=zSh8h1YYT0Vt;L)=qBO4Z~{SU_efI zD?HcGtiKOWS^e3kW{Hz2u%76SVbkY?$L4C=E%jKr#-B#ck#sqZJ!W-;Q!_8h!eK z+(5e0douS{pXCWvb91{*@D+TkPAb!^mzHc@ z4?v7UP|D1!JFRaJe8!yCtAr`p5AvMX>&a2Y2_KMy>l)^kDGUb=dn(S~@sXb+KLIIv zC2szWTaAU(SJ&X@liLa^v&j0Rg1^MNn$)%uZ#ClXP+zFO?3EMAl%ZjA+b|nJ@=v(H z&j}nt$%1TJ#@CcXZViPHH#<(sur)~pLle2(nc3?spZ$jHC%WwXSNw;$Wb5`SN&m6h*O<&6bk3nb6p)ngNwzo-2F=$%qrFt$H-JM#Sbkg%(d5(R zFG<5HS5}XR-P#PRD=0@YsBQEaQ|W*jp(;zVvC*Zef+NYmjG34_G~tlu@}unVxycL+z0inDogqRai!p zo(#cd#3+nFsPlYo_;)#NSzz2BPMh@i$aRW@^+FiNn9fYUgrNTNX1T$6C?@xV{r- za=P7A%33nCqQrmS`eT^KJ(gA3A2goWAs1wBqZU9`{rs8>f!%(0fbb{qHi!k zRlOR^0WN52_^Q!UwV0{QF3$Ae;O;93zhy}9icfv%6<>2Q_q_zWHR$Na!Rw6gjU3n( zxZl|S9oj?v3*J3J0RW;A{wF$QY;9m`_HSw*QM0kzVnz6St2ai>>(?Y&&1D^2-_d(*-oa7JX1NMz(vZtA33*+^;wf4ig@;-p6+Jcv-0AsA6_kk$S0{u8b8J&D4x#^t3l?rRtAc^AhM_};jy;fu zyU|j&8nRUaiKV493yW^1P{7dqA6@6z9EcWX>p1Dywr$%T+qP}nwr$(CZQHgneLmi) zxj$m>s(0a8*v}s)7d#3tK2UC=vUg$kjc?DHZ&9H%reGW+Q|978z#_N|vu8K8#_m^HT_3G zaAi~8t|LUF`Ui=p^kj|pePmW?jWN##AUpfm0d`u0*X!G2H{^NAwGQxr&`0B;v1OWH zXk=L)ecz$iB;B?+*IHk_n!*hCLn9wQ*Eun$3w;7KU|$c9wKB}A(#gZ#`d{3%4>f)z z;u93W&7UZ+9Gv?Pjw}X6^aii7Pp(<*L4&U%xtB~mj~96`vwZ>9JgHbu7Xru11w{sh zs^1{qoJmp^`mN+OQc}4GwGt}+i5||OFd(^6<^GRze|MM$*{rNd@0f!P?_v>?6)m6K z2}4+l7}c#+y6WE_VBpf}rQ&Z(?3+B;NMfkTyzKLZQQ1-UgRr!|*6|9=_NrGTW~qwa z5e>5_g;DRQ{5u#;P>7bVmylKTMvZb5!9<8tm!>f@zfq47R%mAXiZnW=3Qe?{60>g$ z1q{)=r8(h_$lhhFlIX`aP7*FHbzx!d*ED{mq;WFe$wb9iQ13ZDG*B>t`GmWS7?+`F z@m>uAv4YBa>?T+GIJ4s3aq$gr^wH|UA0BbdsA-Z&A;?Gxm^7>DN4qGdOWm?L17WXE z%=RDJ2l(CY&4D&}8FEn|lrXn4tI8B0Ew(NZqx`F(59iGmv9RDPIJs&e42WnStu`L6opJk> z3=amb$;;$j;hu?@hE8tX8`xjo9ad)LB4@(J<0)_DV~x*PVwHwT0@dc7YNGib@$K|t zQ}`^Y0s3Dms4K2`_ZA&Tx_m8LDwvtYqLL;zNSgUVJtXGi&|WL~U+9Y<+%3Q_B28jU zuiF;+vyo}%DJrL|@dU(uA5jeC-o0EMcObK3CbP46|0vi}<4xIuJ3>$}Kq z-a4~W3Gx45-i8huDUW2{pBc*4h$zs&FGzo;*z>cg3tchH8!vK!gl=Qf4PQP-Yb z|7!)tKB)CCE^p{JD0O^o5rrmdLuplU+NMDx43P&#;w#nY+-zz-*-? zmWG0LukIh*{j$MB@J-_X>8!16005-_x6W$p=49+(qi>~a=U{7LZ1^9g zwHnLvpXPes=>uxvi@#pzo@1H0VdrON; z%rDrGm2*NHKejz=u-nP+GCur1Z|hk~T`VH~D#NJ3Y&CAiLSj2f+@0^qC-Rx7?Jzbu z8lk<*d{p|K`!aqELxN{M4V5s@mGS5R>QEYhD@ zq~BGBB9+d}#-VBE=lI4Uzw2JzyUfPnedf~?*>FHn-MKn#I38h{uymv#AI;o-7z*|R za_N1fp1Bw|*2|~3SNWE4M8nNl_-ffF6G5U1!WB`Or5kEA-@v_CtI$KKE>qd@TapjA zo89B>i{R_!>Eq+@qC+%yKzlr#pzUgv;~m}G^NpSB`}2hB&!Q(Pso}?|Aj*|MCL&Zx zI3D}d`4`PeX-8+%GHX`{F9JNEk5RmQ#PBVziu|ix%%TEeXC9;({JI|r-Rvp+)+C7Hu`(_+CgtlDSK6qw4-MD{$?AQ@voPf6G5~5DRt&US=#~ zmc{y@v&dkau+(0p_FPDe5q5V1`zYcv_{eo8Lajk_9{ZYmF3O?aq4iPr;1v9WJ-v>k zJ&N|%-3J@*$HVIl(8`1|4L)pP#;_u~naB@uGDdAf%42Uvub4vDZ&Uuz8kwqgUZq*1 z8OG)`X?fax7%S&tD+mp>Hs7}&FSU*X5{t*`GNks14rx%X-MI5%9#d=)AQ+o}iu6ow zG$f*zGI)v9YqJjfyP*I$N>-V!j#>61;|ccI6$qvzaRvha&IsDTU|WfFW0M-#{aa>n zB$~7velSnr6Im%rp$ACt5x45V8OQdI7kIz{OLhXNs>T3ZQ1ZEl|K4BGu$(jFr(!?l znw1Mj-drhChg@_bhMTkLn8A{rXUcZal`K?}R)+X4Y679U02ceJRc2kzt!mNAY{c0* z{aL^jNf@9z%73)KqG&(b`wsY)xd>qEYEtaF6k6>j+ziU_Rr47prPJUcrsUJ<)BYv; z-2@dMcxOhq4^@2cyQ-5AQ|F}=jIedzhYqKQGiegBU*eRxOQNDTZg3<|m~%lR(uXd( zrpg?cG>YQ*VSMZ+y1ux+z*C}&3vcjUz6JpS0UYS-XCvU~DM&fd*_He=bUrMsZGd5G zgRnCn!$PV@K)dH@pY4^Vh+~`m*?i6lX4dC?VkCW!N_Wj-WZh2zGYi{1ig>gYh4UTQ zFu&pm)g3=fDtz!yQK?-Ioyb?7aiJ+! z{ZJkX@ZaRbuqseD5lJ8R7zROrxZHiDm7@>ySeWz*^PXXnf%RWp7=cybT;BfmUnC>M z$uad`aC%n#5qy3O;=|+UpE4tA@h8@zx+XE*Lt3)shH)@btzs%lkLB2s+?U=qsa}+0 zY-^;wp$pdHm}YrN*>xFLMT>Z;S-0@e%zsDGQvZ_WK0k^mTn8QPrm6i}$90;z%Op+4 zkuc-(x0tnwzRN3i_3EF)EJ#2C0La%%G3&$&Tw>MQh%4Nqs@U2fCdqCqG`Iy_I1k^} zDGB>DMr_NgYl*LTV%8B2J`n+OG$RHLGAWr~Vbu;Wq~p^Un?2QxZcwA-DpwgOpalIoed2ZQhIqfJ7kYiBr!sZ~q=4HRVO z+6?ZC^aRJ`aO@l#cgu70=kM$F6v~wKPajfIVP+3EtI?Avn&c6z+P5liEN0Q>!q@zGOliXSLcNS~d9ekZ>`3jzG|99GEkA@y$C+4dG{)t3+0F_-X{W zjOJwBWzs>GmIj%rGLBZ->*j)@5u0qDw|Hc3C{ zW6#sv*xl?e(kWBfkMApfUaq?C+_6*~j&~oTc3qhn zP6tMN%%h{pm@Kc}_^t8v{qxj9Lvh5zzwA<;SaYX=Z)6WpAx-kf7p9b)AT=*)?^VP= z?rzDNWbp!S~GYoAbLCo_)34n zkcy2z1t$ynbkaQn3R5XYCXI$4{yx2wW{2kP@|TSE9{NgBs@` zuM*4;>LaZDUAAK5>}fEGLzwWuWsno$VK$_%EpmH`=j@jNKVz)N9jyJaE5n8@neX?@ zTF@GdNZGg$o@bY|i^}TYhX;BZoeDMhY{7|^yi<~qke9z<^ePLi+<&RDOMmaGto&JY z2or!`G|U?M(CCRU)khq(GMaA2(zx!Pf^EZK}|NM>g{eDEN+LL5`ai!D70gR)O%w@Bh5gUTS4)2|RBeNzbx zJ!FXW@YU6~)`gkO6M+)pQxb$Zb%%LY5+X3;PLj)F>h53=8*cWte`wzm<%uM3aaY?RWrmvcprxLXNt;R0ppJcFVu$~b=dSY3!9R- z!#C|H2Gj0j%M`@};RgEss8^3ORU4FfWk_%kTHZXo*`s3y|CxEkU#r;QSDJ*3;=ZSZ zL!zK$Q@#d#2p9*#_ZLOzC;`(5!&wl)@QO>{17Fd5MB?e1>SZ~GGp zHQ{A+y&cKhy+63zcx(2M>G2LX3wLfEd>XK7jA6h9LD;|trhtGaD;`Y$ChHAdHvU=+ z3h5ZNez*4e`nE>ny|NNEL&5D`i!yX&*lQ3CvCh z9j>S1#>pKPGz`TUL?_wXVPHF8RI3-?p%$eh;$W9L!blc-mTgr$*c}&C<$SajGgxah z?w(C4e%#>(3jvAeUg{KxVUXSk9qHkeILUKv53V!ZUa24~gtL^YV8tI(xx`qx-Srot zBF+@ggRe9I`Gm(<@f)7d75`Ipq*P#GR+5UNB(FSxZ{9hx^Br$Y>)KJs@KChsGeW}Y zxI6&zK5w(j)lUyLC<+5SLYy~h)RZ*pU&Mvo1g&-)Se_QbUO=kz7PjZ>dyWHVqJLDL z`NbAKjV*)H%&lj36b3Zf1oRZ zH>cN*KH6jPL_NY004~ovAn7dE@B}UTniJPY|jkKeCi;=q-HmlDsc z506aZn-Txr-y_VJD1o$6MV(^;(gqNV>?TW^d^#rML02JP102mc_8XwUOdWQ*@g>#V zp|#y37x=(dvhw1j`Em5?T4a5MAghaTALT?y79L|bcu!o9g>-w(;9L!unl40_Ux}sJ-y*BDCkR?2B`gNwAOLMp z@h~HCS6UP2&T(;Xb(7#s$dpP=z~Ai#33;v_Udm`AG+Te=Y=oUnhbl=wo5hDC0&yU% zF9jAKIm@?w=)Ka_&dKZN=IH6I4@(QF9rLHfzzIY{Gg%1R4&7-m9VP>23JKA48c?E1 z!Ue;kV|(g@MN15q-;Zsk>$NFQc|q?4GY2%j)YQ=45t-`mqXHk<0$8ltu`QAN>R%&P z_W?Xu*~zk!6yd}4w8Wk;LA;?VZ%MPT$*QPMh5mf&Rq$Es z=uDhO*FQz`G^m$fdaQR)LZ=n5os3${$RT+*o;Yi%tlzW~BM~X~-D0?lprkonAMc1N zu7*pmMd|Jppz_i)Q;TeX^jy(5OxElg!~TBQ@UGIp$gv4j0M~SI6EmJ(lC7~$b0n1! z|9HGFr`_<0A~>0Jwm>2Mo}rwK2McEUt;YAtlhR+BYDj8bE5 z*T-!jCQgn95*2@=;W=Mra;!pfc}AUd`*V1osfE4*4lL+=6*w90i*lU;Z%cajD6hfk z(4&{HsBDCw?@=rDXT{kgerMnm+7zs&NCduq&)a`}Sn!+$`_?-oGZ*r{SzcLXXLh^; zc%EH%zk3cD{_qQCleORFNoRQ0F9{f-0T4xw9j zUiKV&tJc)JgmcF}~Uqzp)4D)O3g%!}8s*F=&>Eh@%mu+Iq^7#kj{gyiB zD3oa~+eE|DmDVocDz9xnu9^+Eo1&^YUFu0kBKY9; z(QvK+L#(6ke`fHmCpP^prVRckSk&D7L%dKlySl!lAP0{B{;%*9-YX|PBMbmQhX#ozlGgZ(#DbrWfeY3xBzAo&B2U0%Lb5mA#s*JUcx3#l{O4jF2GwkBAw> zLQBT@uTLkG9|&GU@kaak^n0K*kRNz#aFaej& zy&J(xZm{fM*)^zk4YYGF`36&2I3K;lU_Cx4633J<%lPqS#2&-qy~%kql`)dY0(E7T z;4s1zTGa+}j9EwZ2gc;~1!5{4+6E_!&)osbzMTQK>BU;}oH49m7$TvTJ;>gMT7~se zhi-{Sj>8D)DNI4Gr*Z2)oq_4TDdNfDe&@ZnAXW6g!KTXFP8qXB?`=OG>ErFznJVTf zYcalilR;>X@?cjY#S-n<9$eE%_E>d`Um2$64q8)GMkx(KCGHdv$^?6^5T5V|^gKU7 znhc9sIoeV&yItWrznDw2e7U{c<}6Y%zD2K0>5+e;80kNfsPsp@NZ2J#rM5t>o?)nw z*l_XTL=e3Pz>IfQ6huCFQiU~Yy}}{)#O{ibI(oae9+~ zY&GBDKX-^xDdRX&fV)5FY9oh&H{!Rgb6~LGoxi~RQ<+hb1JM4%7%eG{ZK_4_B6D~2 z409IqaQRbz0{3%uecg?EiKR|lwL-_52%1@5=O-(KkG{M_7;6lah{vdmdP8k?RBNlj z)KrqEy=iZVZv&HgQgNj2GFGBsvb{ns-d`*=;J5&pqdl;s%2PmccUHHr(%O0S_=8>; zuk%$2RgL8{x^Yb^Gpm!`NHjX*ZCldi=PoC?w) za!MSlT@*Yyvla?tK>#H)>*;Eo1m>Syq(Zf9yb5VRVU_V|#Fm88WZh{!2u_ z@eT1&u*$u7&oePB4blUi6p-cJ=LeyE_KE#834`q#QVnf5vo&M-hEEnZ1)HF;|2b0_0COsES3VdL^2OoFozuChr>QVquTY z4&@Ic%m~v{^QA{!zj9nD$HGkQ;enlLQ+-ITalMV@A=1d98ZBT807IoiHwEu>Q>TJd z(Bs?+4VSZk1$6{XU+4M?OhC_8(6wtCXVMbOCB z+Fe)3^V3Ng!yeJ-C%PkH@lw(CSDtJPZk`IsX!LfW7$nY8k%n;qfKDIt#)t|?aq7`1 z>ArnW4)_4H9B(z0a;j|YhK*C#^o%xqui=cW=B@Sz7Jf{RouK-P*~}+v3Yk^H&yA#>j?+4s4|DoTFU8!XxI| z#~Px!W|%VuGOz|+GOAU8m$9;r1g1< z`A;Bus`fN(=Rj9`Z#hlfhOez`5{z{x38#u#x`e5#*j*=0@W{rz_OFd)e==`R_MRGx zR(If|Hg(i43+BrBgjYQIIvmv;4A7FnjDeH{o%?T3b(x^#ru z=Rskq%Jy5pFkNL5QB8E<0s2HH4+&qSH`fl@;0st3c-g00c+kDrec7O0_D1Tfd)#<^ z3B-9bpWiS&1ll3%gc-m|TU#cljz)qmyT?i+>>IHEhKxRSpHi({`hFPp9NNoZt~jIzjYiB=RYmP{*xIV=<6x%#wEV z_JPJK2g`Lq@=G5V`6ujYV<%9?ihru70ryz=2BX=scNeTWSGIP3%ceD}zl-EgCRP&o zUAKNa?&ZDucbZnsYbTBCGq~nsB5f6JaHbRSSLjLc`ejR&@WXXHicSSDt+DphfoIE<0GSUUW4fxk z`;qdo)vxW&n)4F3J9O3+i!r87ZmXxK=b9}-K@E^P0CCYJKSYS&py=RelhPF?0d5Mj zy&Bn-DDksdgb7^cXXzqFw27q4qS(#QF|Y!{M0NdBc2Uhy)4)5Yf`t&U8(Cb?{9{S4PX&bF=lf_AW47 z%xXKujuT+TtOq#1Dd*y=h##wcTEn(v2zwI1Q`y}pCah!;{kf#nUW6#AF(7eh>IgpO zaS;vdD2XVu+U(!-lOBB|z1k)z3}8@1vV9bQS&x5r#r17z{LG!!$smsEsa=oIWNCy~ zPRYgHHigb2o0Krh8Pumn&0(L89j# zsKtA{H+msn`g42cUBcjrv$@3Y3j)iXR7$N;GTA0+CeuUQRU^X)U9^Y11H4DKI(|z2 zth>03&8PVoMS=4yyHuK-xdu-q(FPftB zSo~uG@M}0rTqDWZgIkZw2(XmO0f9UbJ{NDX!&GVJE*$CT;~+isS~I!QCFW~*_3Yv( z%nlvdEv?M#Y}Y-wlpx;{)yg*g$eYk%V42x?InJ@T!FNR3Io$@N)d8f91v}+xu@Q3m zufr=%hR~IzT~NC$0A&~p=tUT8N@##{cg_pBG`8i0wx@N=Rr9L;gzlvaI2b!<7)wH( za>HT$Pmw>6i-0xzUYk&Bo?#pkcR@LaF8w%~ zRp*oqf7&qj$c6JbVYM*_doT_CT%#Z&=NG+{lR>N+q;|~o8uv=9piclikNe0`05T;G z@~R}hjK){T!uv2}#hE>(O=`;ML%5+>)%K7aPqB&mFCvLNa{{niW6{qp+6=%4*^y_Q zXb;53X_8*-$NJ0W(S{`Pr5&$boS^45F}Hdrf6>vdNg1r;{3OjTYa?A)pVow`{dK@W zwimDUh^uMbf?AJ};Zj zIZ7|bP9V{{Hi<>^V?9_|#6m{wojA;lv3UCs&u;dX+RQ@|WfALwEtU7W^1l5@Zx@AV!TX=_Cyby6zsNLctTOur}VAln|%CyZeBsIgYuKSJ8Z?%X$x$ZTe zdz}tFod4e?6|q`hss}{?0JI#y{~s#H)W+8OKUB`O_ol<9Si@dtFAz~~BYvs)M#J{D zO>QB_%YWY5gE39JDd!S22t)v(C~-d^|2SqaubHo5uNBi}hI4;#!qs`GaS#n+*+hr2 z@o_gU*eKrXFE95*f|GK|;GlU&MT2?@DqVo;C3|}5AX(uk94=VxC8c)Xq(L^Cee!W7 zINSg|ce<%@-M?4+ud^HZMVMJ)OfQRe*#u#dsT=@0sh5J*zp30Kh1bn9K`IbWdqA1Z z8wYwIqM{9YOqvdEkCAnp=vyStGNqy%pL&^uf&pR*>g1+K48W%&9oTr`#XgO6HV~*D zPZvm@VO=2|FDf5LlOKtaq;ZULY5unowZSE+kA43?LpRlO*}8qeYry_){^lSlVa%*ncL6j}Za zU&2GPA_DolS(_5Z2(abeGd6p~1g6;I)=g3AMe!-kzrNgHgH0uT?~3K zEUzMXKDZ3JH~ge*-y(2DZ4A8Vlr4CyoB&7~=JImfSP3||2qY)L_qdE*Jv*D=#WFcV z92I+NTDuMmDaB(XXKJ4yp=XpKTK8fO!bQvR7dg(G^Udwf=a>EC*cSMn$HURhsSgjk z>o@tE$II92;o^*ZgYV1eYvlI!Q8}WLq7tvm_gmBHYyYuy6-cZlVxlQxjd@sE$E|}k zd{W89EZKKTGVRL&K-ahZXm_WG;U?qty!Zq5kZvK!c23lf{mR^l7M~#un#EtNs)9Xm zdFbY_5R<=-@DE~Q7rAuWWmgn@MHLys?3Vnk--G~F?J37xA0U|-xNd^e^q-MXpcVyA z9?_-Gw80EfW9~qQ-!c6Ix7^?M@k-?0%kKDEj&Vd9Z#_O=aTG*A40fu6Vp_X+R8YC@ z*speK6;hFwnhAtFP6bHy4!>_48URUF2E<}@J4Vj;>qCL3kJG@_UhE#<&j6UH{yh*9 z36ga=8eD??2sE!s+1JJ*k37qAo^kO(2Kc_DE|%6)-Fxue*e0n2y47{U<;VvO?!4eJ zg$aYuAcr?eg)9BTp0G&3-;w#Sl}5C07^i(9do2`tnx?F7LNT*=fH4$(<3tgy6hAN$ zxtxXww8)%{Zr)s{uPgz_cyu47ff${U<48Rq(*W9p<9R+dbNgX99yf1B!2o9sm0`8i zikZS`Oc!HJV}6qY5I7^K1UESF6Zhm=qBn%YuQy4WMq>*!0$?O6r>Rb>C}{x$Nj&MA zykYe&btM&{EKbqJDAP2(ZPu=jt?vvwX#FuGz;)_)& zKW=(kHc;`Vh*XM}*;el^`cHm1_~psO`BRi6p5!ym0S>Gt9B)GymhS*FOJ~p)Zv-Yv z5=%YU*-%Gk#BtCd=5hqYKQ`9Kz`TAsvzzW|L%5RT#dx4S)&F_ zcSJzL_NdB_{=4>QKRn*rzvC_KIsGk~!u+`^bx>9w_@(2j42s2>MXSw zYxmr=arc?X_JCHYZed%Vgt!SYhom(6;(+J*2;Xf%h3K*pkM+{ZMuSOB)lY|T{(e>Oj7YQY)3=i(6lffL+FK>~LNjMVmu7nmPFcp1B z{YqIO0F2lm`t96?zK7Duhr1~Bh50v2T^h8QE>h`3l_{(EN6FHjw4V>4q@DRg4_=(+ zZZM^^^fJlLIq7Pqw6A@8^kfF1;trtg8he z@_9E~DJ1@w0cY6nr6al|=4l7CPL(L#ShGr$5!M3aXDL}StjCu8_y5*Q8yRRm_#1XkV*l->6JKZ>HLO>)Qj#d z8cC7C_9!EOeBbpXzbHkKH@=2C+sIGv3&OL*&{FS})pt=eCXfBR3 zU{vg|usf(cQ~qp+KD&V$%p$u~KDWUrPCi=U++Xh-18=@WwaPE4B!z8SiK9j3ZA^6) zO_p_Cw3v4?a%G>aoZoW}dNcz`qZI0`$br0-g|xytT0oB|o2G-%dbs-3ZBYA<(4@M< zgm-?bygD?ns2Mg&m@q6j&p#T*ZK9daVvoHbXTZrcOR8fRn=s-@J~y_`5a;S(&~3%o z&L3+sWQ!{m)3e-7q>W_k+bXjA!~PRp1?)7?-kW}&p-nsYoh7d+XHcJO=`#FvnSp!F zz-IombTSH~t%$gp-pT0U?H7Q0`hy6eY@`rc=v_hrP6USb`>t!pTHPf8c*yo(>j%pVjt?e1hBRc=K|)=QlTeZ3X01kZFZu zZiM1s52-of_umqjG2?QF!T$I>oGU+FENU+CDXt~;-!bRI^~7z`V1wG*_B;Im&o`1-hFcmLeoY=^$i6&f~|RRFJLDuVG_uKYb0>1h-591FV+ zCbfnZc}&sC;aMz%#BqQ~TroV-puGO{E=>?X??wS2(A!E1UYz*O$*m_M1Jw?)=R^M) z-T~H{ZEtLRqyobDPW9`QK@BY+Q=K?9)ndo864?C#zxr`@+qW78?wQ81u&f?Id%>K<~) z?g$r2@TW~4=pTp)>b&AJ0&kZYcU}q}ML`cNu1TwR$3rJx8f`-}!UBXu8Nud-A;@ zO}PQcaWf^yH%~2rIN$y>tHogLICdoP>m(O2?|FEysHQdODIA^pJuON{JR$v|8v~OA z>{WMO2q3usw0e$Bs|R?*&pE;>8iB(VOmS(v~ypWvftnu_g1RA(>bEyE}Hk5YcWSzAYbRMjO znW?xWpu9lI!lCMos(DJslXczLRs$Fgs9Oo@{_`d!E4MEVja6SFnn(gU+ifq+lWSek zUXH1#Tq+TAJfRDmEvJGu0!@YRq30Zr@(%g>S!*A^zm)QmuGPQNBla##30me1;z1Pv z!Zura9Ol~QY7L)b2Q%PEJc8V_$8y%_udYR#|DY1@oQ0+`%tQc|x1zn0KWnjpP-f6d zh^v`gvoNfYGIZ;LR^jMW9v|JPyF-Yu$RQ*CcY4%?#E6No-=vK4DPLLNJ%%g_GwJP| z4mnxMr)eUh^adMh7&u-3zQ~3{?#p!r81Dj-ElC4t71v!(UT(I*08vA1>M_d($OV&I z5=Y{2-grUns}7N$2e6mNhnjzBpXVbmxB$FR$+ptpagNoiMremD4Phh#TtEKDF%jh) zIj64Ldy#FgU(V=Mw9%_E_nHa7&%Ndogvp*EdYwk@jSh_}@K zl{h7ss)TCadf$GR@F@@y-C!`U-IBKaEE6fBeK1eV*hh_F5 zE;4E%6{(AqzO_Git#;6yrNsbASz1ET(R4%duk8#7Uv;+2Y!XsSwm#eG#=7coGoSa5 z;dRiiNrUOS1!jJu`E2Vq7+{Y_|1qJOznn#eU^lE9p~i^V15FGl`XGTZv_E5`Y1EIV z$h{VPr*WBjxXds`sG_DnCHe*cRw)xl1GflEpq#Ev8j27BZk+>f>%qQ_w8;32Yqh%& zN>rJ8fN?yPlLiK9A=DaIM_-xPY9#v5o?RGeU04=M!b2mlCXp0LlVer7KM2`;G^gm|M{2~l_;PYi?I zIJ~&tzev=sL}EfS=E1LnOfa#vZ<5EklfD+STRE=JKv&DL5F+v@8hQJ6cpxD#j7d3Z z1k`|kvCH_iNI2N`-ZGG?w}Zh-K-owj@k;Sq`!^VuCC$Q1*bUDFk!9t!rYGk)Yfgxs z=C3)jC=UVKf0|Zb?HaDS0oE=rqd}^m@K($5dPk+Y1G@w&#cUdIwTazI(o2))bWw5~ zea1P#!B~jbjqryR(TjPEjab2xn8EaMh@BKicIw)*hW@~>P-KoORqHA(fL>)E0Du)C z8RAVLG~${vZCRk)mNOv@50mQ<1+E-AGh;7-%E+UYtxT6t8UEcIQ>E8zHWUAxj<&~Hqm5ke%5#G9Fn8O=ed1JLvl_rWR=K8K z3az{&G7X(-Bmid=#usv*5R+GBbVJ5Xve`_iZA?{z2M@rF!{={z1d}fGi5s)7aM64YYKIeScn4sYTlc z+Z{UiLoT!|?P#OdR!s$AjH*5`&G6do(e-WHphrrz+@)cnCw!@}zHz2mg%r>>NvQw* zshL&VLM`}Vx7LGO0qlpWlZF@yy`C^tQ3-B}-OKE0i$x1dkun*6u9?s!j?Ed0`_QKx82m#C;meZNhdt)hsLYbrd_B)?HSJSXh2 z6h=a?Bwcx!#@Gq1`UbeMfr&vo7^hp&*)`#K>E302gB}4S5V5$|-0a|Z|1&mMR!5@L zfRzLir_T1_jzH|dRhG;++FBIfG1zE`g=yWQdtc{8F7eMx#nvGI`<|R!M`&jFA-%=; zG>eZ_b&Vw{I;mN)C2}UTtXC!ri4{#!<5}x$!610vP^lBoL2O5w&Cr^dajf0}s~^)W zp5lHkbiurj0D@;-^tXUdidkp>6eO1uscRAy3T2gLOo zKB!h&+LND5C1{mTKdCvQQ1u=oOr6k~aL|TH3w70TSDhZlJ9xk>Ne5bfK5CjN z4Wk|=eA*RLhu26lsD!f7q1jqh23Yg$dZ=@o^gyyM|ts|0>_4-#wHRGBR z(qMP#NcIM@i$|*Jk=%uOyznN%vAbF<+!>%-&V|-0L%|^ zD)qu--$RoCVOP0+J5zO0eR6NKrfE0kLk;&%2EO6SYtd3x>I98gz;5ezt5~EQThmb2 zO-Jh&m-q6+tM!rf1Y8&EhC$ZWs;RB~I;tXU-q zQmA{(L&*70ua14r+9cR+lLz^{%kwV!if?5d>U&AJ%>750NV6GsYh1}QRTTlGOn$$9 zaUPgZA)OGY{En|Ry@(<|f}$ULnmQx$B_Nk*2GGzX6;yaacOEee9x+hN2aN*6#eLZJ zqQG5UpEoZ;V(Q8i8}(sd__+-x%~Yo#A4BGgYK|Z;ML>T*^2UGlL_Ibl<(;DBfe)MH z817>21AuOb#JK9VL40D4JY}t6PX8IRoK)PB!j{2s$t6op%VeWapR}q~-C9e4PRG-$ zhM&U0SqVE7a88BtCsQ#GzpetTKaLfeH1wKVnDZRQLH8_*2)#cyTuQqSb_YGKn&Ox3 zmOMk*3W2)M-Xi9*en9&)${$$vPlh zAX_qaIXc=W!%C9njF=$eZ^zu)p)a@Kp{S9c+5-Fe`F|}Q1Z*zwSlo++mMtg$CrW^PfDG+$xD`BADDcL9EGw_iVbgB_e@=LuK8wjVKXZ?!*f*FSV zX1+7`djfs$J6)PE!(jCtUsfXgtOo9W?{T?Ql=EuCAYgLMy}WPP~v*b z>OD$JjG_bgzuk%h_7P8efnReVZ)rlCZ|sz(+Qjgj0{9NvQL$J1s|}lamg?85=SvA^ zUUQ8xFt$rSOtMEYmj_lq0>VMBZ300U(y$y37@H@)Ev+9L(Sh}se{sIRTi8H6$$~UA zKX(cB)JW*nD&9oIS{wXS=Sak_K^2<;_Jx6=IxZq7mnX_qOLR(x0qg2i*9v-SoI1_U zgOs1lG^WS7ze&IBvtrGil%KGo?*0o**2hmF)#{_g<3(!*7Q?^TMjZf==tQSEh_Gl^ z51$+aY@~h5ENP;2SVkGa1;&bS0)vTDk+n6Dfd3HQA@*=m zWL%vyP=W@T2$l=p$Q#(o*_q3ETR>6QNcCBk%%u%5O#PIAA{u0;qqZjq#A(UAes!2` zL$cTwM*+fkgkjx2Uchw3;^@&Y;J>AzPF4wrMDv`v>Kiy`A-i&mTRxQ%B}VIz5%nad zN4=DG@71Fe4z@L6Dqm5X)N@MVsi#X3(r&U$O`hMpoebYrTJl=6ktUi77g2xa$X`5d z7p_L({Eqq7yPsoZ_W$q$urk0%pmhwfa^EPO12t?b&0vY2YIH} zeX!E$4t4nSn${^@ZK#;Luo&ubK#cZtLEV+UITx~JZ*tZ@BM36F?;Ww1T!evDP~1E2 zDPrlh#VxS#uu?LF4H*lR&MaPtw22Mh92vK+SHkhLa{u^6K;5=_RjTL3OO;~44R7W1 zaJNzci%^<|dcY{d<0Z>_%K+|O?NKx*&=JltP)u0~TM$6`PW`U{%La&N)MGH{u(1Ps zT=Sr0V&g-Irpey`zb)v{>cIjyMvSu!m@g}g%w47~5F;XK;%KCOQ>yDg>fubAob}RK zzp|a3D%ZJ>l1D;*&jf(S8*?B{JT!LJrT)0ZpaE?IfC8edMsCFKII2nus9Nn2X$>I4 z@5yOSC5?KGhxXaspc8Hdf3W5T6gzOJIt)gZ<|yS32BaJZ@E~xf4;d$dXAIblE@t-w zZj0vDC3;+@IX!`B{+2A}OcHI89~#}Wo^8nZr=_hQWs``(@(b2Ll=-Mkz1u*nczV-U*_KlDj7iyyGN4NPyMxhPMK zoNnes{2A;-bNThFfN!Pnco;`^gPxSv8vMu-2UWmeR1KgAOUPtF(iUF9p@;!Q=q2nE z_Oq7LKDmN6&H%J>&9ec=o`GC5qz;{})J<FN}me`_B!Db!4FQ zPZir=Rb0Kj4(^g;m%9|g{o{z%%6b!ws}Fo#Ue)wFXKx+b%6CwoLmXO#3-ov-{opsqq4#I9?IR{PTnD9^ zYboZ7`dhe+Y(A_k+bdjYWZMnHj${I3s7Ghj*w&Fl`mVDFv7zWZ1Bq^y{>oK5*S7k;j_*mS5-%~F#3-mVmM0zesMO}e?0Y-pgH_;Z*>^#1xAE9)xfDj3 z!hVq@CpsH)Zq!-@iw#ssKk}hfopn!wkpu^m3!XwJX$yLRKiiAgS2Em!3VY9+>G{o2^Trs~v7Vy>YpsP&K}M@i`;9XW*WA4y-YsUsZ`kdPiUJKF{J;J*e3}nM6{#bzs7m zkLpl-eQ>}#2?^`;SN{$y&USz_Vn>|&b|D9Kg^LB+_=0+P+Hhp+zUu};uDMKt+2P4$ zZ(U{(I#W8rwp3QPZK$ZFiT?N&z<&K4Sh|`G3_s!qv@v`CKx3(U{XPZEi|gyNGZ3wR z*4`oJBlnGPwnVBrHlmVIH-Y)_;zfUzk>msvi$wzDN!k; zq=|iQ0nN&Y9SSYeU|QM!0yI;84TYsn5A2`Ir}YjROKALngE?>Dyy!YOH~)oYfk9t| zr$B?tv?atdm0Cfz!-1>{SRTD%Wd-D`4*yN+8JpiXE8V(q0!HA+Yg94V6MEBJ~GB4~Z(&*VxGGo@U zS8tF~;S7KE;hM?7Rp`cvlKq&5KCF!26i|%L+)BD&(eUwN3ANhGtkR;(1*zM^1TI4c z273f(%$6)QUxD`X%ejB;G~mNz(tYiKg;PHpbZ!>=3(u(|IevLS2H8m)^H9pmeNE2Q zIx)o}SX)_J-MrADp;lT}-bi~;6!2)yl2t)wjVa8tR7Xm=8-o8uW(9FV?nUMMDbKz&zfx$2uU7aL;pE!bL4Cj? zBw@pc7DIUF_q&3N|1^_Z^FIJ}K#ITo+}Q7O(j?jlDs+c7ox2=Nt#~C!P0s_|aBu0T z-thFxW@Eh$ShMggu!zFjN!?Ah3UnsKfa|LfB6l4ugPm(Nf}0(-XYpRfYy4l24ey~s z=_HD7Xw>EL$*YsY*CHq2r-T3f`S8=he)Q|Xr{lv9?}2#i{AF)<^!@YE?hc*6n#uEy zn(r{;@bB=4gc$$^&=h1Pv&$e&oZpf%??Z2#Q4S(o}5x6ZjZAl|5?tXjV9HQWf% z_W_blLk)Ql>17pO7)nLd0z_S0;oc)bMm4ayHn5u!ad+&BWN4!tEs70Krrle{U&`!X z=^kQUpWu@zk8_mbKlFY?CR@)nP;@yQ)sq?$3q_q9^_(#DpSkOH&WpiX89rM^VPO~M z{X^d8+EE}d(N!23hAc62R+OO`&LF1NNUIBzn-7A>jO~K{OVv5L0rh0~`5bR#8_RgG zJrbyNe6s)H^NF*HYZ_>^w--ESXNw+3&Q}gI6QJxR(k9W>@k$7|I-l z>`a)t6=f6X_iuJu6_ZfQWflr#z39*_mc9wc7x79u95@)x-We)F#2$gS3Y*%{(Ui6T zB-79_(8g$$eN3G>-Y|T_??Bts-T`sWR_SY=5b@^^$0xl1ZZMv#!@42sd&BYMThpX) zRA)&bNFi%_*Q$L{wA@vSho)l81+#8@qZKxr1Dj-u2S_pGbx56&v-TmyX|VgtD( zqMld)tyn7#M^&~7hCi(9ufpLDBqQPuy@)3y-Qe5zAKo22^71y1mMOFqp?_XlR^_7~ zpLp8fAyr`&2+zJ?wplEA)=tcxu~0!S445b02u@yB+Hzr5L$wHq`CP_{`3%x<8loJD z{W8*xmH`X2lHQ1hhc|O51U}INES^qrd`o$8v`7xZhsthA&zREaDlAf!AaKNt_#S99 z9*udFLr-1`G3mpHlb22vPOI5PdOkv7k#pa^_9J0;FRq8mnH|IJ@WM-xH%wpbHv=kM zp&O><`3}=k15yV*X<<_a1)_Oyb%k8ed;0v*8ZrsJTN2w6; z#ZkmLok(Q>?A`JRrB@idmeOYZ5n5B+O+?V?1eGk*K>JM4(j>oO24tc1T{iqM`1{}g z{vvoCukfzkKoEYezCt3yy{WK~O?;OAkC!hJOx$&tpqPmH4&e_RoQeF)pG?D>R!*sB z4yPuFn+I^;5&ZG~Z(XDgXl;DhU7_C+=L7{csX699VoWag{B5sb zxeDKPDW#;dRNnYZROK$s^Q`nk)UAE5oV4EqFjw-%f zLl;@twgyk@`QejHb(5%!S6P+)3su}Oa5-e=p(u2izPlntrDh4;O_XOg=zw*jRAga( z2~nLRI1XO*TvjPe|6KXlCl#H{(=t^yHqNqZ?;+pG7lQ0lq-DwT3=7aTZbr5X4(CD_ zF;olPjfKFgIUIA-JHW2$VI%iA1BUJ{;5QRLeC6YL1SbOW%3KcO8EApm{&R_b*_?}o z7x{9DXVR1zFKboQ9I%)BodrkxZ;oEQJst(yTp#fK>GPj_f;$<+u7l} z2+z2Gqt#TlU2Z|^!;UTJgWdvk=7q0%Z90KkT zI}~1?d&~KIDm6bIy^&FUfH^`}!~wM5^t^2Wmv9M)n4%p9HvC?= zsb<6HaF)cS(K%OoDY@EpQX9Tvg|6Hou@|(Ku)-o67bARTcCMy47(I&7WdY*UwswRr zyp#9Sn8TvH17Kq|&;-YF_IcDX&Kh({MCX|?On$dIIs??)8&!f}c-LF=)TWxaX_4}T7a$D`_%8TAUN|I6SyUw)oe$126ewI|y zhyK;-iVMBUU`l*I#H|f(ChW7!e_4UwU8oigO?caydb_q!o~*W^VJoDpfijCrh*9T= z@%5Is3MVyYqs$e$Zn!+hlY05k)Y)!I;9A$`XG0$3k8Cjds}Rk;%;$g!1<^E;UIFj9 zSxgDU2~VoWtOzMU z{>|ar=;-jT`-h(f1m5v)$7t#SexgdWp`zHVBJPH!q7_+Afmtl#Md78p*n_}l38sd5 z7J1pYav!gNg08|6QGk?@0M33F_$~)p(*<$?8QP|9u(izeZ|$)|1u6o>)Yhv%ZysKwn~v zdX@g*UddDz;_OztU>c|z;xZTincm1a&@;Uuy&wPld)Nr)({f^;@JZ=@YKvL?_9u5! z!}oZ4^OSk=@J`oxnW35eo=lZksIH3|>zi(Uei zG7gunwCXHI`=z2*(or!2@*i;%jHJTb3PQCJhSV?o?(v1+I(pD({Ew2Il>1>I*Hr-J zFUkxKvMc6z#PWRQgtZ9NJqks>L}wfV#K_={s$<6ADn?G;=t(aPT^ar#1eU-kx$V&3 znCnwytj;7|R#wK4FHNmd&ZlRXSx!PLY?QWW6ffDtD1q%$?U(=we}@NjyxH`68a z69dsgrkT^uO2P+B&W;?dZF{;^cCdR+dc&jVIXo8swNjg{_O$loW#tgsvea+y3$vwK z&zCr-76e)sdAG#C9qepu-7c-B!gF=Q!(cyG3EBD8xXvZfxp^7hNLb?HBDFw?bcQ0m zJrk&mTYc5uhZY#Rv;4!jPkKlWENG1`3_(1}FVgG3^9-TO z949{0G+DqHbS&^y&z>6~KN(96scrygTT2?uIwUOW=BCJ$ppm(G)Ua`&q>P!_dJgO6j>B>sUF_nXJi!evFv`e^{32e;*Db0+oyOZn zG|0Ct0S&?r%1KGuHMpTUuHjbBLPE_Iaf5YFd*ApXJd9_0XhyW2xbZi^fY~w_9zGiG zKN^1iFRnw(&Q?e@QP$#Kg{-;In7jM?X1+2}Z z0Mj3#S`J1vXYT6NJQ#i$;7dlG*;CBE!-%#8`5&xztBd?R<(Erf&?*l+%Ms_VF5@eE zd(UVTNG}}jvk1A*?Ek~CPm)lX;UfFiFnrM?S6R9Mp0o4vHD&Qp3&>J7oGvsAufBF% z%%excg}IW$H$2>qyNHsE$Dxgkrf_vS(`zlgEj&|t#3s|1H3#O`xlOVYNSgnv?-3%A z@Jyq1Uh>^0u8o!_B{NL07;AjH*P0r@8}vU2mf0$jFhuSj(J&Wrdt^C0?W?zk?~hMj z9UVo#{7pX-5#@jVE!sbL^Xl`_$tbyd#3^)fgW;& zyuXD!iFiZ;!0$0F87h;o0ZcY4yZF4ei~P#zXdNf#@mXUp`I)#~_hvw{7m>JnkJI*SuNPQx~TVf^uuoLn%V|VH3b$h5B~%^3#OFm_85Ox?p<>|X3&#u0*(A!>TBRJ ztsIErQ%nWAP>-3KBJAJz+5o^A@Em!uw}_XMX&ih3PAM)~_fn8gXrhZ5HXb5<{en7G zoHu@JGz)@n9KBQw-p4EwSYK}@r6%JKHJeOM#WSr5={uiDTW<&SF)4wFGUr2_d2v(0 zw5g7CGTWk#x^lN=XE%BcwkceQ*4~yVknw~dZpx~drD$cTf*7wTV!oMC;qln8SS?Z#XIu7zzXZEn zF}k|y(KH+jBUYKKofc-~N2jzxbm>j5J4UK_Mw78!;>CqP8DuIp`GQ}VM-xMZ<`nTv zCnU*cT z4DKk`P2Xf(x@cJGvMI*&qHR56RRs#=gwUy zq@2WHJqjxz#zvd=&`RB0cOQB4QjGfWCMTZBxedm|@tQC|tJZfKMGXOG>=Zi{Q>)*& zYAh#JWOwsqsgg!&IMi=R&X%;vERj&bOu{jRs`1Gc%QY-T`yPfv0Ll>n<$6LQrP+hyQaL9k^KY;UehY#_PH{9L?UP z#1r{8?%Q4PWE8e%$+M50BC89!%kL%23$P2OnKW5skt8kBjSa`>nYY@6$b+E1un(2( zAO!!MVT6(FoLihu4wO`Vzw6+fgw1V(b1L`-=dAV}2S<(*mBFcwf_tKWY2I8_TYD_I znhKNeXiAi2?M3U()LKWiZh83vc8-Wuu*nW@O6IQs@hPps<}#~%C@SvO}$hIO#X=ZIeu5jaqt#D!Dq zL<9Q!&AohpWoE9kce(CG#oIJ*}ihcQ?$ch#B7^gPn6z^9}5c zUeaq>{Ov7C<`KI8Rf#i}YE2fgm}S3 z{yR)qmyD%uWP2%`Mn;kxHTrxJh0sqww5~9QS1kUa&0Wjt!0C+$|636B73=zaprnD)OdAf}8py}-Q5$qp)Jb1q!y*WHOK!*=) zf3kpfGC?tsH^jL`j}}bbtt>_&ZPc;oVw=&ZNrg}I1JtSP0h?%!4*r^4^0RbB^za0@ zIaIKR;#x6nJ@TZJhb5Ki$*M;Mx^ct&tz1s zY*dpQH=#gF)ghB0N%Q(rvPVdYBEpG^+!AHSK+qm?tOJ>D2cAf-&g2~`E}gvBX@yc5$@cE25=7%Q_UMyJ(hN3T0?0NZgtx- zs!@}>VT`vIS+JsvR6cchO5!-OOv@6wvB&tvI5>jU`TXjbMYqdf0O4q_BW6UtRl4wA z*McP+v~jCTLEV^c>qT)Zs8>s-^^rX`ecikc4!F)+I5vo*UZ)8phs$q>GHpkSOo5jn zS0>E-db>4PpkTQ9W`wtax7Ug0PEMg~Zhhck~ME z2v_BjPQDz4_MBio6>Za3Vm=G1v*AUxXlA$}jW7gADiyGZhfHgg!|jtec}@ax{sIdY z-X-l+Unl4iLmgY50A%xM;yku7$#=JdsEzpzQF#ni%eH)4WU^|xaYl?tuQ+ico1POb zW7ivWjL2fKN{DANjoj^mb4AKAV;^LG<&nW5;v?G^W1ZVzd6iF_mH&>F3{z*an>hK9 z8DA={J?MqvEy2lUdB!qv)KrkK%^I_1RbL)nQfcx|iFVlaKEwqiYJf&wF-#s1a!x@Y ze5k9Jp=a#Nf!T`vvvGGA`x(xe)ZnBP?WK|Ls33EgX%+3ndu!vXPCdae!f4eKVYFl^ zb+Dq9O^;sC_6;ad8{r9-rk#h3Ro#_im1x2TYZ>iZ2$p>tmWvptGhiW>CY)t=bl`)y zD@x$ULW`x17C|u zemzDYJkfAVt1Z3dJw+S3x3G7<6Sd1`wYc(!ur4E>_wZ!I6KBT@msH<{{;CCWC?|eW z#xPsVb5lp5H)s%g;1pRfYNIdgZDwGc>Zr()T$BuP6jS5v4P&m<4m1`-{wQD*!e5)j zv&g+YV@cgp#T=E|<75au(gS^NsOx8z+q1|^r=Q!AfBe)Z1r=O#)12~7h*c)%>S@u~(-A-GTn>LBj=9i&f1gK@OK1cmjfc}eLvchorJB!I zz2C)XJeasO1kW+a=VPlw@Op7GhxVrcZ-9rurN|jkur|##&{zd+)Yn z|AyrvEb-E|0_bffmw;QRko5~0KSow||L_xAYq{*6g)qhFy4UUtGqvTZT7Y_l;2P&z zbYY3-4Cd_*f?EWK2zEg)9BTV&k;P>Ly(P3()2Z%fugS8zhi-Qvf^^;-SBNY(BfuU<|I?% zV7~TP=GAiXH3q`rmL-sYuRR2A%!#bx+!Eeo7TtD6ZDNZ%;J!@8+hAw)*NC`V z%6+c)5&%UY|18`jEDaUDY*GGvI{6BSYA)~+L{#aoAi;$3rotmwQTL}mI)+)D*#T#P z-)P1k^sYlb$WTm9xPo!wUk3K3ZnW%FjjZels!O4Lj&)4Q;03_OQ)ACZ7q+iJ9S|;1 zy7H^QE|M1fZpboYZa`l)3X+L%cx7GPR>N;8+7C0&hH!j;@f$bt8m@50G z+KN^=Kt#LP7o$`*(*=qDxO2s!Znd@)ezaK$#mD`{6ZdA}FBMN}NBzO#_w^b0Hw z=JnN9c}q1!$1ed{Gv}rsur`-%-W5|#=KZ^%#|3>8BF=>IfF)=NLv}F_y2_Kw9)ufx z^JV0sRFCFYkXyJW&4McU>#E7KkUMx?ia5DU+qjGiN3;FmeV0ZM1R*;zzl$b67q_ zv+2^_<9?^{*x!jm-DT zM?-@KOl7m2bCcmfS9-2|MwY-4Z$%kCdgjBge0ICf5CD+zsSWjOGwx#h4EgqKhhe82 zpBJ1KRyKV3GURwpCd`G7bQ`xjtD^|ITTId8Jxz*TZ(~)GMkyOleC0VzazLasc#$P~ zM~@rW8mEkTVGJGoMR^UsBX`RGqTqwygpg=Q`Uo$YNV#RRC1-~Vw%{x4cf+B8ES)xx z7p|wq0&H;^!81#V%)hKKImHLT&Iv}$lykhX3FBxc%tx`zFv%I?8CAb{8J4Xq->jxP zrdo_!;iXdtO!GDE^3C2gHhn|RbF^{ElkG(VEH^M+{vuw1U(yIuJ+0HC_RJ+^dPXnE ziUvvTx(|-Do|)?Y zkvGkD;amS*W4-G*OXu`dIUo3lhCC3671b`q+XnUiiUrTG)<*@@bm z2@z7I?yxRF@8=Q494O-2>kh{MZyn|+6EKK@w!}Qn!fP!V-3;-znNjt{iC+}cw786~ z$_No8_1N1@qdl3$hR{Z0Kn_}_U(;y>= zdjXj-rht^TYBeBVXYq=R&S4u3IZY{K{-#RHs;4{1Qqk?H85#tl*|L!EXvDrpaT&aMu$ z?_-BZT&wRzD*Y_fEg*z&a|1= z#PQg;@38e$`!`;Q=+!q8nNX(GPr!o`9DnUi06@bRTJ0B~QWbWf2MD ztdWqjymWHInMKN$tY5kwxhr6$+!J+9Y&Z9oIW1cPMB44w{yuSV}t3CZ_VSw z5N49S|187cKLb31`P|D&=8f-G+A#`aF>QvyFJ$t9!ajQL*fM&^-n%Vugf_F~Pg`J# z0uC^_J=`2Qj;Nw?<1q8Tks8PEX&5m@8}|-SE{EEduU_BYsL!(A@L$}r5AR>t12G-= zOFR-EK@ZSm{7BdjgrUF1eR4Y4;8vKXXQ^@$$=4OrUY>eg2)DbVT<)J1uS~NW>h<>5 zPVqEE2k&3Yi_nhU+oqt_4-I!YsU)u59YKG*g(D~<`?EMn^^G5$+?c3KPNxb&7Y^KF ze8z5n^0=OM@N9}#tGr^oXQMx!o}j)hO0z z)D!5HM#7-ntT8EXI{hD20JD}w+AT`6Rx8-P7imP_%L`v*T^WcdPZd#&@ndj|^=Mrf z&R_O|C!=RSjdtziq7Q?=#RYSrLMOWvZym>Nu>g*7aiuTSVy)Q*DLH<+*T%u0_5!uT zJn_Ob8O$tvi??^lDd=6(Uf=Z59uA8VpXY$(tR@7t%SG+8M9?i zDVA)UKL(1CACsbMtYrM*P^M}enuIV;#%5|?tyQtk(Q&;`q1BkGI#fq+J=C#R0Z2UQ zIyajP=^5^U+V7gNtx$aAm1oocY}0)^3o0fiY)S)r+gV;poDw$vu8{{*qp) zTPqP5k*?i4x~)ia>!NN|QKhR@2P1~560cNP-YGPP3o);%b@}4aqqD49#FG(c?DOL6 z(cAYQ-W@zb`tt~tmRa>EeK!5!dGh1rY5IKfcsHKx#?PKS-JQ&Sn8r_br;nd~pG>}w zpYQ$<^qycM<|p(%qBq%>t=mhcyR+v{rq5@|kI#1FCr=(f{r>rrWcNvuJlUN*o5soP z$8`7kk55>8KhXBjL3f*j3TxX3RU%9QEc-1JTqpVTDy+-fOT#A{=)0MuF#K>vX$=&uia5h884A}G!M6gcebvE7(tWM5Hv|Q#&Jfe`b~js!#L6B+ z&HbA!BPTbjI;$*mCGS;RR#y8#NBbi#k<7YTV}-PHak-P)Z=8(lnlX4d*h_-Q+pEWq7=MeYCq3>7HJs zi^dGZ&NdN@P`7R3S!q9a-{EX)I@f5qhrtQRU$V5=ig>vx^I-`7<66AB8UDvL?$0Qn zMRI*_Ja}hqV|6@widx$TKPpZ|D;At1KvY)D>-EhLrgy||%IJyqJjsWEq3g~1{l1l) z;y#EAnQ({1Y;lk}tVAvS;fUv@GPz2NF(?^s$)BJ;ZfKrw~|lXkShq_Cf-T?^0VET?f3ZzBF1G2$ zMPoO#O+oCK+uKs#ogc6**QWQECnwSSYcWaT3gIva1{yYNqGHiSnmz9g`1(hy+r68( zn!Z(zcA8YqeFK@d4`kkPAO`&TR~U$15@I89-D}QQ`Q=IwVdh3jHmll*YLlrABsWJb zne*Ith%tP|)rt+Z<#tkAMQj5zt_C{Iw3=iLwqF08b3+vz+)|MW{4DOJDc))XfPiiz zi<{&{!Ms}W3QjDI_ki$|7N-%TsTVv(5jbQVBmhNPU88Y=|Hd!s2I_nsva5?;h?MzF zdJfcO0Bn7YOvKnmOmjf z=JG+gUcjt>smSK)8!kkA;{KLO>7)dfQ#BozY@udZQC7eMr{W$4dPhiH6!Db>t;~br zPytEuRk>N>C2!Tp>c7p{lf$xxOP|XO$&T%k8b|9rBynx@RMf!kV5Oi7YhD`0&ypyE z7F=ds*iKbT1GR_)J{>B<=w6+g-`)9C@b+r^8@brIS1e9C;9s&0{w04r{B!l??f946 z1O6p7_?L9RzZ!j)8HoZVZI@ZGtO{kD75HZOn#K^bx3|UqL}P!Vu%ByU151$eV=f?f zT2#Hq1J#Kp~=23l&%yuzOX71b0L2Ds1RA z#S&uhHkN~mNyaj1nuF1Ck(wlf;>(F6{5>q23))`HvDs;-JYB_cSDA?V%ec3;H#^DL zx4}~{8P5&yUf&h#0{X@VjSKBbWA-KXR3OxcgMGq+ycAzeSW+vIZRD~5)(2#!A^;gn zGc`QXLAI)L#>X&50k7I4ki=DI=;9{?t};!RCD4NNG+1tuIlq|=Bhd!IDoYrrlvVJ6 zXE`A*EQnC(4`rDC=z{;ry~&eT0$C~i&+_*b`n;1058P4@g*QmB^PlAouRwQ;A$P(H z7VwV8Ci8s)sPMlO&V?5aUY5${$-@I#LJ%}0(#Y8NF~BSQ&OHSgSK1g0sKzrR1G<** zZ*XAl*pauzEGnaEiYRIw2BB%Ac?i@C4uRk`J6C!a{h-%7gGuNI7yX)oZ;oEQJr*T5 zzM{}Z-$YM};!^p*{yKI=IXIPQn z+W7dt>WZRNQUz~cVwYaBqb6XER^=&D)az^StdNye${p1!z?PR7)A2Isz5Mmn(dUEr zuihQJ1eD()KX>paYY523IV{G_AZ)@5t~$CUUi|J1o_7=&_xGQVj`;t~Srw&7I)dK+l5^ef2kxrihpjn_#9_<17o0_mA$%DX=^311aH5D60r)&Z zPCO8_DCq6HL_Xm?L*D*CEK}d@BYtQf7b$wY>OKxsH$0WySrDvrkBy+Ou*$4@!|_D^ zG?!0`F2?$b6qGe`OM{p~IU6Cb8Lr%;Iln_z*j{1t6BU?qY#PcggBg;y@)%D?C=H@g zu@?dzw$A{gKAuQ;2w|Q{G!6#vk6&R7)4xEn%JK?TSBvz4*+WVXEvhr$it3n%|8QNz zXUjN1%&;8U--?0RnQKs%cM#UwYz@Ju{;8~yd*?cw`q|KRn}aT8T_#%Iy{?&G^} zde05Ga%pT%#pHXCs}8$wy5*wVM~(5`Z0)<|lix*xYjY&lGBP67^)NCifaDck zB2V%0)4`j=zrT!FY#)1a{PTxTCvYUR0`K+vZ8&3{1-HkUx4;1HbEGxt*Sw8>&0FY~ z(M&xtqwJ~FyqdbQt+Xv2G%c!pt%l^3)8>H~;}~;^m``ZtgqD$`Ozh4@V?F|+*mQCb z+&wykqH z>sy5t*f-eK#TAIn_J{E7lymCcbzR&#lX#7)QjaFka?fn=%WSxYsDoo>2WKn8H-EmF zU@nD6jB+q+=3gIWWeGg=qo00!YE(8X_@!r{b#7d5KQPvbaUz@xwcJD5*SgsBbhV6& za?UavcfH<+)KTxa9~Dc5Q!-l?dJl+{IfdSUBas*SC^YY6(b*L|o=(OqM} z!&P2Zz_2CT)Y&eey*$s>!t}IFBTA#cl@=ePI1y0RThM=1G1U-0ZOZsf87kfe2o>D9Hi!*Q3G zVdaSY6oYvDy6)S>NaOiRXa&F04pu#_La)-ilj7RZSU2m$Y4EiZ+htCqH-4!(n41>% zyGa`X%^~{6C!3-`Q@#vq&S$~fqihmP^OW1AWgs(aN?)lFP#vuI!hdXA{>h0re z1s^8G4XOk4poSYI`gk@;etQ1+rypl=@@)Dv{o&bvv-fn@@gl>p5&1tPF!RS*;-PNvWocY6Qj9- zZ%-St^~25qVVG+|Q@5JXFuieW1Ef8H&{EWCbn;X)a$h_2>FAl8f8-c7RcPd0T?OJC zEkRa7%DV(f3TFcFugcji1m-f(ts#VKz11jQ`Q<$A1ICJyEDbPmUAL2cP0Y88<@Il; zS$NHRhb|dk6>m*>S7a)dQP{ktaWrF&dbp~>H zavqM2=oDJR@9Tri7@iBONz+f|090kEXQh24WVqEDS}H4sHkfD5{HIDWEV1wXAd6#?Ndu=Eyogvi znXt93oD@iF$`mPPq{d6TWSoXK5W+tJW`IhJ36fl-_C&6dOfD5i3~U2K4y1HzLIz%1 zPnwl8Q+8B(?ygkyfp-^`MbbG-_HA!V;2q!~7#a6nFNYtXv$smw{gwJ6cv4bDUAb}k zczpU|`284<+X|?0Fhb<=0#aB!?wNDO;#Uy3`hB+39JPMyjLgK8pwHz^==Rk+GAQeoG^vJBl!$vL zAND`I;9@dfM21R>k{uU-V&{0DsLDsqUHP&K&J*DLnCD(PB{YiBSv8)4v1S9a-ve{H z2UcH=?CRVA$9Rs$BYeRVnsalUd!Wk{?8K7B(p@SW)qY&XhH8)RdQS{q0v$@`5%qVR z313-*MsOIaXH0;^VUSft?L&~m6!m7@jvHQvn7cp8(eS?)0>%!Xhkf*o%n^6p`#a_7 zT1)@gXt3^xIdk@8h3}ZNfs_7TPe$+5PzU$8f=3RD^|x4)dhdW7MFMWD4XyEl6nFZpcgeo-NO`CNFPwfol_*6x_!Tqc@#oP zZw7wPDXTiDBJ$I9^W9Ah_}i=yv(Nw%g(fYzSrcotBi3HMZL>A;Er}4fGB?p`>a{it zZDaT`Mt!S@1wDmcJBI=3o&p#+Ma+u(wpA_DDn{${*MJQt`_}*~X;rI9%EPO+H)dFb zG}hh8D8#@ZhudlvN{SR+M!0)GmuotZ+K%mu55%G28kZE4d2iRqXde0&Ja-LD;9+<; z+%{~05=158s*&VPrznL>^!@>l+3P^uw(yZ(E;I=AjWvVd1MoCOHciz_CU8@D%t+?B zq6quO(gzd67@Uuobf2%`-nIC0Bc7|QKYQ0|b#6kVF=Z>85zhR2dK2<9ie~fFRAp>V zfB{3&h6JN6ysTHw7no*1nh|9z>M}+leca+mB z!4VOLO@Xq+Q(l`f8mgQ?uI5`)+TFVZm}cKDQK&)w>zLrym@@?xjiNC(j^c4tlj^zj zU7*F{)b(}KTs4To-y%<-_!X1!E>qb*5ch2LacKulTOt~#%?o+wltu8#2(4Ib?wyj}HY(ta)6}F@es|v{Ki}J@ zI5boU$2n6Mz^=nU!AV+H_#0P;VHSM<2gq$qVIbAfFG%$vBW_$2M&+|G%}W$AN=p4O zmcyxFkrq-gtN0@soG2&W01!{7z_TyXix`TND>Si}RQXIcYQl7&&h>0P1CQVg8^MqDM_?gw&8tl)^Bz4cgvwH@JOb%?F`vt0rY&VDkV*v1j z{|8V@0|XQR000O8Vy?GZ;z56hIurl^xKaQB8~^|SZDn(BZen$5Y-BHOZ)A0BWpgiR zZ**jDV=i!cy*z7g+eWtE^($r-EJ$rA*4ZrByhYUVTGuHkABG+qxn-y{Bcp+rl`~(R4eRtebjEl4RX? zeM6Ik6x&_hbR?~+x=XvFuG+~Y2!fkU(UNVQ_a!B}roJz7+L8?|ceEkvzRFlnvaTC) zM=RRE$m)*db=Kol3=ZjAe7C7PcnuII6ERiYMx^a;0cu9uR=(}_JM5meh36_OfFxq*g?zV<$W_(Rrb^4F<5VEU_t?W zxq;qDl!&nL{3{2stJ?&DOlBVR^zZfUuQcn_QWKppKh$MT8y^6iBmoJ{J|SDh*!Ois z5hXc5GDWt3S1|VC+sn@x-Ju-Gz927mO}cG$sX#LS#NW$q^^S|C=ylB=-_kvZ-m;_rE4`;iG1>EKcs0^fmmRhqDGlu5K5aOv$qiZHieUl2q)iwkH^GdzWM$g6FyjL{agWImTlKWf>B2lW~G1>`1oZYfc+?j$vth5Kj#F1 zsK!mVEs=Lknsghwr8pJmd|KjcIwo1!=QP=+-KNEE?~1lFyXg9@dPvlDMQ6q%M(!8c zyW+MNlw=UCcw?8gkj-5+VeqFlAA~ZOm0@I8JYiGd*)&a ziVd3=DOv`)$|wgC5d^4-a>F(ZvSh6sA@Pd6nM@`>UcFC#xW2i#ev|xi`QhW$^?OjE z;4JZy zZT~=72WMoW&`pw}D!L>Q$W|%pBBN7_M)F*@n33=e&izD*h-UK6b+Z@|Zt!#~g{$M> zfHVAy*~s)Vx!os#X4;n>bmLIsUA+qfyEy>Pre#ZKCKNtIjWru_wa|or%m%i0Xld0( zp_=p!V4<(r?q--Rxo^hT4bY$FA1N0G0O4s0h1oZ0kr$0bN#C%TjBsi|kI-5_w=0K+ z3d`dg+u!N~`J<0XLPC#Vr{vgLoUR~pLqx(XgRHLBz}H|t7shIitO~f8Mr1ml=S7>| zmUIr}ZYc9)%Q>5E8mUPRe*icr)2>GWszJ7ZU-7SXA@olYKGG=?Y0~o&0HC`(TUo10 zyw(^=#Q1CU)lK`M9%8RS+N`YEG=t8Ym4*u&k$kG^hYB4?q3Yc463Py*x$0ZnFu+0= zFyaO`5wPmU=y(&qf&BsbAp0{LhhGMT7dBr`WN=5kguxss(7&eGh12;IE!wV3VTXVR z;CM3Cq=(_e=%o~$*#-e_rq9j$LWqP&AZ7#w)?Sr?l>~2f;(L`X#K1G8ZNeHuz6?RN z*}x04mxm8>GZkv!r=wbn*@7Q#|0jBHcbCO|d}fZ5P|qE$kt!OeI}|>#w1Kv?^v+@J zpyawX$yA8$}5O&h-uG_N#?TI+O0ROSuwdY@bb=xD)0Tu{N-Q0auv~5rS z_LskWO+w~|Lv(#c&OyWTT~k`#Z_1CNxHo)?)tiK zJrKSn4|Ve?ZR#Fm2}A1+gYb-b`z`5e@Wr<~fB_rCx)8IZq}_o$d!Rw&-|Pi*ftU@8 z(8ZWMZ8y#k+GZ&RhEh;9qaZ(s6d+TLQBu%Z;~&MH9|RB8gT@Sx7Lf>pc`)+>aB+Ew z)|>pXg@1&)Sg`@HS3d`w2Mi!Cl*bE4NiMU&dAQ`AS75UZ@UCo(<7UGg0ptRBfktkZ zHXs^zXytKh>sy*DDG%Ej=eo}F*n;uT4XuDPV%gyE@0h^{nF3iUDG|yCArCAs7w z-CKZAHeqbB-jf0yaX#t7)<0LTQ8o@32}$a8@W^p|T0F9mPr=Hhv!BKzJQId-rI}Ke zsSukinpAZ1g++zO=a|!zj29}>{Q*Vdi2HDYFMx=-c-u^~M3NP!pp}-<3Xf5*xKDXe zH0{vuPq`X23mN1HU8FP&d?iA zySmvgSmYETp)wxT^EWfcebYon;NZ~}DaT9BG}YEMd$VJ2xLBunasriuej9#H_1&a+ zFiB(Ry0E+^!d|o1d7vWOL{G|e*dH_V5eZ6pyXzSdZ%3}WW{x&oPag$wpUAqPrSOG` zu-4L3X37^$kH=E$Deo(opEdxBeSOmOgtF<$Te`n&8k=oEIVF%TVU8|BjTOs5?APd+ zO)YlAJTjx8g#(@$IbB6xrDFpy(Shq6{y*qzn&)=pCp1^(zmip$jXFhG3D!j{CxH9 z!t5tQe$Q6pWNOEDoMNpfxMD3QxD4j=P1+7bS=<%F12{?K4-0a3IH()5mVtv`Ff#`! z=9rtEllD-wlC)!K9R!*16v!+(0>Mt4H~`f6l_wv>1Bm$?Xp_x3GIPEl^xj4C*XE91 z1`xsnum0aE0(zz!b$H-mwl32~W}ZBKz7zAC0? ze<`Xg5eZR^FX&##ge7yN0HDfX0<$26fc1K$uB*4cmGrsl|8iLfSLj{EIjDngJ$`KS|?sh2-Iv%DZ5Xx3v`4ycKG zM8`TCWko*$MtO}$awuk-M}(a8t+dfh@`l$VBgcjI=uo3X!FKgSW*lL1`XrdU5UKF7 z1!*>%I{`eoy$Ha3@3<=?WkFAVyEz^P&F4-HL#>umMBg;#ZlwHW67QYUEipcGj<^$1 zyzTug*CJmY%&&qOBkmfB%azAd3D7A%=E3L=pjMc$91gub&@f{WH+!4-oKjK5KGA>V zy}>=xAg(;#nI`dLs~Yv3w6THhk9bwgiswhQ*pB3ZCml2}9V+frO-$kIcObU8ncfu{ z4X^7U2^q<`7B@{#5Bd)*LxGtlk4)k;3zq*#Z`-=;J8m#S&3!mjQ9QK_rx#uiRW}c7 z8e52yEc2{NY)A+biHOTHHkOg?NGpRJ@|h(#*jP9bZ& z>?Aw3ZQHhO+qP}nwrx9k`@V0d>YVi#rlzN+S5J2UygzR2Wy}J#(^al{b&e^lepsg% zosi4F{2l2=Sq70^y8Aw`Nq~T+_PVY&-*jW)0*T2F&jM?JMGPx8C}g;6kA_(MM~SBE zmNsl=^L5}2FiJKC-bER=69p!k3MNlHh?gn=*jU|>J9?NNe3=-B)(E?;j}UwUZ9{#I zJ#e8&=faS14hq4-3w-Y#69tqj}s;YphgP@;}dVD)Z-fe^61ps`w&JX+Lkk8i-H9 zw#!4G>ya^75h$I=qr~n2{)*lk(YxtJ=H0G5+7hhE_UEaCqaK8UJ+Z;y@m73|0TNvk*`Fx-_daZm_xezM81d813AM-C%0AD>8;d`_)}=9} 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 IfrZ2V50f4auK)l5 diff --git a/python/wheels/meson-1.9.0-py3-none-any.whl b/python/wheels/meson-1.9.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..57cc75cb13866054d42e3f37c895e4959ebe11b6 GIT binary patch literal 1029634 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≺M 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

Qz^?YPVG=CPC+rQ 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ª+O=82R~5z@}px~pBT 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

2kdDZcw? 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 z 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∓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

0l=LR>n3*I}d3Yd|^Y_1c+M`BQFzpv_LNW%EE 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�e76@FG-GjYOPGy=LOGD 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 Date: Mon, 8 Sep 2025 12:49:35 +0200 Subject: [PATCH 0727/1794] meson, cargo: require Rust 1.83.0 Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250908105005.2119297-4-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- clippy.toml | 2 +- docs/about/build-platforms.rst | 17 +++++++++-------- docs/devel/rust.rst | 14 +++++--------- meson.build | 6 +++--- rust/Cargo.toml | 2 +- 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/clippy.toml b/clippy.toml index 9016172983839..204f5713c0007 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,3 +1,3 @@ doc-valid-idents = ["IrDA", "PrimeCell", ".."] allow-mixed-uninlined-format-args = false -msrv = "1.77.0" +msrv = "1.83.0" diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index 8671c3be9cdbe..0160d3adb8312 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -118,14 +118,15 @@ Rust build dependencies include bindgen or have an older version, it is recommended to install a newer version using ``cargo install bindgen-cli``. - QEMU requires Rust 1.77.0. This is available on all supported platforms - with one exception, namely the ``mips64el`` architecture on Debian bookworm. - For all other architectures, Debian bookworm provides a new-enough Rust - compiler in the ``rustc-web`` package. - - Also, on Ubuntu 22.04 or 24.04 this requires the ``rustc-1.77`` - (or newer) package. The path to ``rustc`` and ``rustdoc`` must be - provided manually to the configure script. + QEMU requires Rust 1.83.0. This is available on all supported platforms + with two exception: Ubuntu LTS releases 22.04 and 24.04, and the + ``mips64el`` architecture on Debian bookworm. For all other + architectures, Debian bookworm provides a new-enough Rust compiler + in the ``rustc-web`` package. + + It is expected that in the future Ubuntu will provide updated packages + like the existing ``rustc-1.82`` package. The path to ``rustc`` and + ``rustdoc`` will have to be provided manually to the configure script. Some distros prefer to avoid vendored crate sources, and instead use local sources from e.g. ``/usr/share/cargo/registry``. QEMU includes a diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index b6737536c694f..e0ee4a9837d7d 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -75,21 +75,17 @@ Note that doctests require all ``.o`` files from the build to be available. Supported tools ''''''''''''''' -QEMU supports rustc version 1.77.0 and newer. Notably, the following features -are missing: +QEMU supports rustc version 1.83.0 and newer. The following features +from relatively new versions of Rust are not used for historical reasons; +patches are welcome: * inline const expression (stable in 1.79.0), currently worked around with associated constants in the ``FnCall`` trait. -* associated constants have to be explicitly marked ``'static`` (`changed in +* associated constants are still explicitly marked ``'static`` (`changed in 1.81.0`__) -* ``&raw`` (stable in 1.82.0). Use ``addr_of!`` and ``addr_of_mut!`` instead, - though hopefully the need for raw pointers will go down over time. - -* ``new_uninit`` (stable in 1.82.0). This is used internally by the ``pinned_init`` - crate, which is planned for inclusion in QEMU, but it can be easily patched - out. +* ``&raw`` (stable in 1.82.0). * referencing statics in constants (stable in 1.83.0). For now use a const function; this is an important limitation for QEMU's migration stream diff --git a/meson.build b/meson.build index 3d7387335665c..a238df3f5e3c1 100644 --- a/meson.build +++ b/meson.build @@ -94,12 +94,12 @@ have_rust = have_rust and add_languages('rust', native: true, required: get_option('rust').disable_auto_if(not have_system)) if have_rust rustc = meson.get_compiler('rust') - if rustc.version().version_compare('<1.77.0') + if rustc.version().version_compare('<1.83.0') if get_option('rust').enabled() - error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.77.0') + error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.83.0') else warning('rustc version ' + rustc.version() + ' is unsupported, disabling Rust compilation.') - message('Please upgrade to at least 1.77.0 to use Rust.') + message('Please upgrade to at least 1.83.0 to use Rust.') have_rust = false endif endif diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 6f8884eb30b42..99c275f2d9f52 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -13,7 +13,7 @@ edition = "2021" homepage = "https://www.qemu.org" license = "GPL-2.0-or-later" repository = "https://gitlab.com/qemu-project/qemu/" -rust-version = "1.77.0" +rust-version = "1.83.0" [workspace.dependencies] anyhow = "~1.0" From 71e84e5ae87b8e6adce3af7dd2a490c7b2f39772 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Sep 2025 12:49:36 +0200 Subject: [PATCH 0728/1794] rust: add missing const markers for MSRV==1.83.0 Rust 1.83 allows more functions to be marked const. Fix clippy with bumped minimum supported Rust version. Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250908105005.2119297-5-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- rust/bits/src/lib.rs | 6 +++--- rust/qemu-api/src/assertions.rs | 4 ++-- rust/qemu-api/src/cell.rs | 6 ++---- rust/qemu-api/src/qom.rs | 6 +++--- rust/qemu-api/src/timer.rs | 2 +- rust/qemu-api/src/uninit.rs | 6 +++--- 6 files changed, 14 insertions(+), 16 deletions(-) diff --git a/rust/bits/src/lib.rs b/rust/bits/src/lib.rs index d485d6bd11050..e9d15ad0cb578 100644 --- a/rust/bits/src/lib.rs +++ b/rust/bits/src/lib.rs @@ -165,19 +165,19 @@ macro_rules! bits { #[allow(dead_code)] #[inline(always)] - pub fn set(&mut self, rhs: Self) { + pub const fn set(&mut self, rhs: Self) { self.0 |= rhs.0; } #[allow(dead_code)] #[inline(always)] - pub fn clear(&mut self, rhs: Self) { + pub const fn clear(&mut self, rhs: Self) { self.0 &= !rhs.0; } #[allow(dead_code)] #[inline(always)] - pub fn toggle(&mut self, rhs: Self) { + pub const fn toggle(&mut self, rhs: Self) { self.0 ^= rhs.0; } diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs index a2d38c877df55..e74fa3ef82645 100644 --- a/rust/qemu-api/src/assertions.rs +++ b/rust/qemu-api/src/assertions.rs @@ -81,8 +81,8 @@ macro_rules! assert_field_type { (@internal $param_name:ident, $ti:ty, $t:ty, $($field:tt)*) => { const _: () = { #[allow(unused)] - fn assert_field_type($param_name: &$t) { - fn types_must_be_equal(_: &T) + const fn assert_field_type($param_name: &$t) { + const fn types_must_be_equal(_: &T) where T: $crate::assertions::EqType, { diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index 27063b049d55f..148a13e3f5661 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -980,8 +980,7 @@ impl Opaque { /// but the functions containing the dereference are usually safe. The /// value returned from `uninit()` must be initialized and pinned before /// calling them. - #[allow(clippy::missing_const_for_fn)] - pub unsafe fn uninit() -> Self { + pub const unsafe fn uninit() -> Self { Self { value: UnsafeCell::new(MaybeUninit::uninit()), _pin: PhantomPinned, @@ -997,8 +996,7 @@ impl Opaque { /// but the functions containing the dereference are usually safe. The /// value returned from `uninit()` must be pinned (and possibly initialized) /// before calling them. - #[allow(clippy::missing_const_for_fn)] - pub unsafe fn zeroed() -> Self { + pub const unsafe fn zeroed() -> Self { Self { value: UnsafeCell::new(MaybeUninit::zeroed()), _pin: PhantomPinned, diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index e20ee014cb18c..014ffb1fd88fe 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -307,7 +307,7 @@ impl ParentInit<'_, T> { /// Fields beyond `Object` could be uninitialized and it's your /// responsibility to avoid that they're used when the pointer is /// dereferenced, either directly or through a cast. - pub fn as_object_mut_ptr(&self) -> *mut bindings::Object { + pub const fn as_object_mut_ptr(&self) -> *mut bindings::Object { self.as_object_ptr().cast_mut() } @@ -318,7 +318,7 @@ impl ParentInit<'_, T> { /// Fields beyond `Object` could be uninitialized and it's your /// responsibility to avoid that they're used when the pointer is /// dereferenced, either directly or through a cast. - pub fn as_object_ptr(&self) -> *const bindings::Object { + pub const fn as_object_ptr(&self) -> *const bindings::Object { self.0.as_ptr().cast() } } @@ -336,7 +336,7 @@ impl<'a, T: ObjectImpl> ParentInit<'a, T> { /// However, while the fields of the resulting reference are initialized, /// calls might use uninitialized fields of the subclass. It is your /// responsibility to avoid this. - pub unsafe fn upcast(&self) -> &'a U + pub const unsafe fn upcast(&self) -> &'a U where T::ParentType: IsA, { diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs index 0a2d111d49098..0daec62f92613 100644 --- a/rust/qemu-api/src/timer.rs +++ b/rust/qemu-api/src/timer.rs @@ -39,7 +39,7 @@ impl Timer { /// /// The timer must be initialized before it is armed with /// [`modify`](Self::modify). - pub unsafe fn new() -> Self { + pub const unsafe fn new() -> Self { // SAFETY: requirements relayed to callers of Timer::new Self(unsafe { Opaque::zeroed() }) } diff --git a/rust/qemu-api/src/uninit.rs b/rust/qemu-api/src/uninit.rs index 04123b4ae99d1..b0a829729dd74 100644 --- a/rust/qemu-api/src/uninit.rs +++ b/rust/qemu-api/src/uninit.rs @@ -12,7 +12,7 @@ pub struct MaybeUninitField<'a, T, U> { impl<'a, T, U> MaybeUninitField<'a, T, U> { #[doc(hidden)] - pub fn new(parent: &'a mut MaybeUninit, child: *mut U) -> Self { + pub const fn new(parent: &'a mut MaybeUninit, child: *mut U) -> Self { MaybeUninitField { parent, child } } @@ -21,7 +21,7 @@ impl<'a, T, U> MaybeUninitField<'a, T, U> { /// Because the `MaybeUninitField` remembers the containing object, /// it is possible to use it in foreign APIs that initialize the /// child. - pub fn parent(f: &Self) -> *const T { + pub const fn parent(f: &Self) -> *const T { f.parent.as_ptr() } @@ -30,7 +30,7 @@ impl<'a, T, U> MaybeUninitField<'a, T, U> { /// Because the `MaybeUninitField` remembers the containing object, /// it is possible to use it in foreign APIs that initialize the /// child. - pub fn parent_mut(f: &mut Self) -> *mut T { + pub const fn parent_mut(f: &mut Self) -> *mut T { f.parent.as_mut_ptr() } } From aecca0676ddd9e032de4eeda371b81598d3257bb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Sep 2025 12:49:37 +0200 Subject: [PATCH 0729/1794] rust: use inline const expressions They were stabilized in Rust 1.79.0. Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250908105005.2119297-6-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 9 +++------ rust/qemu-api/src/callbacks.rs | 27 +-------------------------- rust/qemu-api/src/chardev.rs | 2 +- rust/qemu-api/src/qdev.rs | 2 +- rust/qemu-api/src/timer.rs | 2 +- rust/qemu-api/src/vmstate.rs | 2 +- 6 files changed, 8 insertions(+), 36 deletions(-) diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index e0ee4a9837d7d..98e3a33a3ceb1 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -79,9 +79,6 @@ QEMU supports rustc version 1.83.0 and newer. The following features from relatively new versions of Rust are not used for historical reasons; patches are welcome: -* inline const expression (stable in 1.79.0), currently worked around with - associated constants in the ``FnCall`` trait. - * associated constants are still explicitly marked ``'static`` (`changed in 1.81.0`__) @@ -97,9 +94,9 @@ patches are welcome: before QEMU can use them. For now, there is special code in ``util/error.c`` to support non-NUL-terminated file names. -* associated const equality would be nice to have for some users of - ``callbacks::FnCall``, but is still experimental. ``ASSERT_IS_SOME`` - replaces it. +Associated const equality would be nice to have for some users of +``callbacks::FnCall``, but is still experimental. Const assertions +are used instead. __ https://github.com/rust-lang/rust/pull/125258 diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/callbacks.rs index 9642a16eb89bb..dbe2305f50939 100644 --- a/rust/qemu-api/src/callbacks.rs +++ b/rust/qemu-api/src/callbacks.rs @@ -113,31 +113,6 @@ use std::{mem, ptr::NonNull}; /// This is always true for zero-capture closures and function pointers, as long /// as the code is able to name the function in the first place. pub unsafe trait FnCall: 'static + Sync + Sized { - /// Referring to this internal constant asserts that the `Self` type is - /// zero-sized. Can be replaced by an inline const expression in - /// Rust 1.79.0+. - const ASSERT_ZERO_SIZED: () = { assert!(mem::size_of::() == 0) }; - - /// Referring to this constant asserts that the `Self` type is an actual - /// function type, which can be used to catch incorrect use of `()` - /// at compile time. - /// - /// # Examples - /// - /// ```compile_fail - /// # use qemu_api::callbacks::FnCall; - /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { - /// let _: () = F::ASSERT_IS_SOME; - /// F::call((s,)) - /// } - /// - /// let s: String = call_it((), "hello world"); // does not compile - /// ``` - /// - /// Note that this can be more simply `const { assert!(F::IS_SOME) }` in - /// Rust 1.79.0 or newer. - const ASSERT_IS_SOME: () = { assert!(Self::IS_SOME) }; - /// `true` if `Self` is an actual function type and not `()`. /// /// # Examples @@ -195,7 +170,7 @@ macro_rules! impl_call { #[inline(always)] fn call(a: ($($args,)*)) -> R { - let _: () = Self::ASSERT_ZERO_SIZED; + const { assert!(mem::size_of::() == 0) }; // SAFETY: the safety of this method is the condition for implementing // `FnCall`. As to the `NonNull` idiom to create a zero-sized type, diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs index 6e0590d758ed5..cb27be52569cb 100644 --- a/rust/qemu-api/src/chardev.rs +++ b/rust/qemu-api/src/chardev.rs @@ -138,7 +138,7 @@ impl CharBackend { F::call((owner, event)) } - let _: () = CanReceiveFn::ASSERT_IS_SOME; + const { assert!(CanReceiveFn::IS_SOME) }; let receive_cb: Option = if ReceiveFn::is_some() { Some(rust_receive_cb::) diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 36f02fb57dbff..52d54a4494edf 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -373,7 +373,7 @@ where } } - let _: () = F::ASSERT_IS_SOME; + const { assert!(F::IS_SOME) }; unsafe extern "C" fn rust_irq_handler FnCall<(&'a T, u32, u32)>>( opaque: *mut c_void, line: c_int, diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs index 0daec62f92613..1e639eaf221f7 100644 --- a/rust/qemu-api/src/timer.rs +++ b/rust/qemu-api/src/timer.rs @@ -56,7 +56,7 @@ impl Timer { ) where F: for<'a> FnCall<(&'a T,)>, { - let _: () = F::ASSERT_IS_SOME; + const { assert!(F::IS_SOME) }; /// timer expiration callback unsafe extern "C" fn rust_timer_handler FnCall<(&'a T,)>>( diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 812f390d78028..8515e3821351c 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -457,7 +457,7 @@ macro_rules! vmstate_exist_fn { const fn test_cb_builder__ $crate::callbacks::FnCall<(&'a T, u8), bool>>( _phantom: ::core::marker::PhantomData, ) -> $crate::vmstate::VMSFieldExistCb { - let _: () = F::ASSERT_IS_SOME; + const { assert!(F::IS_SOME) }; $crate::vmstate::rust_vms_test_field_exists:: } From a71df7e143b57427c1f8a917654e7b0ed1ceb919 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 8 Sep 2025 12:49:38 +0200 Subject: [PATCH 0730/1794] rust: add qdev Device derive macro Add derive macro for declaring qdev properties directly above the field definitions. To do this, we split DeviceImpl::properties method on a separate trait so we can implement only that part in the derive macro expansion (we cannot partially implement the DeviceImpl trait). Adding a `property` attribute above the field declaration will generate a `qemu_api::bindings::Property` array member in the device's property list. Signed-off-by: Manos Pitsidianakis Link: https://lore.kernel.org/r/20250711-rust-qdev-properties-v3-1-e198624416fb@linaro.org Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 31 +----- rust/hw/timer/hpet/src/device.rs | 6 +- rust/qemu-api-macros/src/lib.rs | 152 +++++++++++++++++++++++++++++- rust/qemu-api-macros/src/tests.rs | 111 ++++++++++++++++++++++ rust/qemu-api/src/qdev.rs | 70 ++++++++++++-- rust/qemu-api/tests/tests.rs | 24 +---- 6 files changed, 336 insertions(+), 58 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index ceb71dd99b661..37944635202f4 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -9,7 +9,6 @@ use std::{ }; use qemu_api::{ - bindings::{qdev_prop_bool, qdev_prop_chr}, chardev::{CharBackend, Chardev, Event}, impl_vmstate_forward, irq::{IRQState, InterruptSource}, @@ -17,7 +16,7 @@ use qemu_api::{ log_mask_ln, memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, - qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, + qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, qom::{ObjectImpl, Owned, ParentField, ParentInit}, static_assert, sysbus::{SysBusDevice, SysBusDeviceImpl}, @@ -105,12 +104,13 @@ pub struct PL011Registers { } #[repr(C)] -#[derive(qemu_api_macros::Object)] +#[derive(qemu_api_macros::Object, qemu_api_macros::Device)] /// PL011 Device Model in QEMU pub struct PL011State { pub parent_obj: ParentField, pub iomem: MemoryRegion, #[doc(alias = "chr")] + #[property(rename = "chardev")] pub char_backend: CharBackend, pub regs: BqlRefCell, /// QEMU interrupts @@ -129,6 +129,7 @@ pub struct PL011State { #[doc(alias = "clk")] pub clock: Owned, #[doc(alias = "migrate_clk")] + #[property(rename = "migrate-clk", default = true)] pub migrate_clock: bool, } @@ -176,9 +177,6 @@ impl ObjectImpl for PL011State { } impl DeviceImpl for PL011State { - fn properties() -> &'static [Property] { - &PL011_PROPERTIES - } fn vmsd() -> Option<&'static VMStateDescription> { Some(&VMSTATE_PL011) } @@ -690,7 +688,7 @@ pub unsafe extern "C" fn pl011_create( } #[repr(C)] -#[derive(qemu_api_macros::Object)] +#[derive(qemu_api_macros::Object, qemu_api_macros::Device)] /// PL011 Luminary device model. pub struct PL011Luminary { parent_obj: ParentField, @@ -782,22 +780,3 @@ pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { }, ..Zeroable::ZERO }; - -qemu_api::declare_properties! { - PL011_PROPERTIES, - qemu_api::define_property!( - c"chardev", - PL011State, - char_backend, - unsafe { &qdev_prop_chr }, - CharBackend - ), - qemu_api::define_property!( - c"migrate-clk", - PL011State, - migrate_clock, - unsafe { &qdev_prop_bool }, - bool, - default = true - ), -} diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index acf7251029e91..01d5a0dd70c19 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -1031,11 +1031,15 @@ static VMSTATE_HPET: VMStateDescription = VMStateDescription { ..Zeroable::ZERO }; -impl DeviceImpl for HPETState { +// SAFETY: HPET_PROPERTIES is a valid Property array constructed with the +// qemu_api::declare_properties macro. +unsafe impl qemu_api::qdev::DevicePropertiesImpl for HPETState { fn properties() -> &'static [Property] { &HPET_PROPERTIES } +} +impl DeviceImpl for HPETState { fn vmsd() -> Option<&'static VMStateDescription> { Some(&VMSTATE_HPET) } diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 959726efe6dc5..97b2c214b62eb 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -3,10 +3,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later use proc_macro::TokenStream; -use quote::quote; +use quote::{quote, quote_spanned, ToTokens}; use syn::{ - parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data, - DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, Variant, + parse::Parse, parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, + token::Comma, Data, DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, + Variant, }; mod bits; use bits::BitsConstInternal; @@ -152,6 +153,151 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result, + defval: Option, +} + +impl Parse for DeviceProperty { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let _: syn::Token![#] = input.parse()?; + let bracketed; + _ = syn::bracketed!(bracketed in input); + let attribute = bracketed.parse::()?; + debug_assert_eq!(&attribute.to_string(), "property"); + let mut retval = Self { + rename: None, + defval: None, + }; + let content; + _ = syn::parenthesized!(content in bracketed); + while !content.is_empty() { + let value: syn::Ident = content.parse()?; + if value == "rename" { + let _: syn::Token![=] = content.parse()?; + if retval.rename.is_some() { + return Err(syn::Error::new( + value.span(), + "`rename` can only be used at most once", + )); + } + if content.peek(syn::LitStr) { + retval.rename = Some(DevicePropertyName::Str(content.parse::()?)); + } else { + retval.rename = + Some(DevicePropertyName::CStr(content.parse::()?)); + } + } else if value == "default" { + let _: syn::Token![=] = content.parse()?; + if retval.defval.is_some() { + return Err(syn::Error::new( + value.span(), + "`default` can only be used at most once", + )); + } + retval.defval = Some(content.parse()?); + } else { + return Err(syn::Error::new( + value.span(), + format!("unrecognized field `{value}`"), + )); + } + + if !content.is_empty() { + let _: syn::Token![,] = content.parse()?; + } + } + Ok(retval) + } +} + +#[proc_macro_derive(Device, attributes(property))] +pub fn derive_device(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + derive_device_or_error(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} + +fn derive_device_or_error(input: DeriveInput) -> Result { + is_c_repr(&input, "#[derive(Device)]")?; + let properties: Vec<(syn::Field, DeviceProperty)> = get_fields(&input, "#[derive(Device)]")? + .iter() + .flat_map(|f| { + f.attrs + .iter() + .filter(|a| a.path().is_ident("property")) + .map(|a| Ok((f.clone(), syn::parse2(a.to_token_stream())?))) + }) + .collect::, Error>>()?; + let name = &input.ident; + let mut properties_expanded = vec![]; + + for (field, prop) in properties { + let DeviceProperty { rename, defval } = prop; + let field_name = field.ident.unwrap(); + macro_rules! str_to_c_str { + ($value:expr, $span:expr) => {{ + let (value, span) = ($value, $span); + let cstr = std::ffi::CString::new(value.as_str()).map_err(|err| { + Error::new( + span, + format!( + "Property name `{value}` cannot be represented as a C string: {err}" + ), + ) + })?; + let cstr_lit = syn::LitCStr::new(&cstr, span); + Ok(quote! { #cstr_lit }) + }}; + } + + let prop_name = rename.map_or_else( + || str_to_c_str!(field_name.to_string(), field_name.span()), + |rename| -> Result { + match rename { + DevicePropertyName::CStr(cstr_lit) => Ok(quote! { #cstr_lit }), + DevicePropertyName::Str(str_lit) => { + str_to_c_str!(str_lit.value(), str_lit.span()) + } + } + }, + )?; + let field_ty = field.ty.clone(); + let qdev_prop = quote! { <#field_ty as ::qemu_api::qdev::QDevProp>::VALUE }; + let set_default = defval.is_some(); + let defval = defval.unwrap_or(syn::Expr::Verbatim(quote! { 0 })); + properties_expanded.push(quote! { + ::qemu_api::bindings::Property { + name: ::std::ffi::CStr::as_ptr(#prop_name), + info: #qdev_prop , + offset: ::core::mem::offset_of!(#name, #field_name) as isize, + set_default: #set_default, + defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: #defval as u64 }, + ..::qemu_api::zeroable::Zeroable::ZERO + } + }); + } + + Ok(quote_spanned! {input.span() => + unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for #name { + fn properties() -> &'static [::qemu_api::bindings::Property] { + static PROPERTIES: &[::qemu_api::bindings::Property] = &[#(#properties_expanded),*]; + + PROPERTIES + } + } + }) +} + #[proc_macro_derive(Wrapper)] pub fn derive_opaque(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs index 6028cdbc4c331..aafffcdce912e 100644 --- a/rust/qemu-api-macros/src/tests.rs +++ b/rust/qemu-api-macros/src/tests.rs @@ -36,6 +36,117 @@ macro_rules! derive_compile { }}; } +#[test] +fn test_derive_device() { + // Check that repr(C) is used + derive_compile_fail!( + derive_device_or_error, + quote! { + #[derive(Device)] + struct Foo { + _unused: [u8; 0], + } + }, + "#[repr(C)] required for #[derive(Device)]" + ); + // Check that invalid/misspelled attributes raise an error + derive_compile_fail!( + derive_device_or_error, + quote! { + #[repr(C)] + #[derive(Device)] + struct DummyState { + #[property(defalt = true)] + migrate_clock: bool, + } + }, + "unrecognized field `defalt`" + ); + // Check that repeated attributes are not allowed: + derive_compile_fail!( + derive_device_or_error, + quote! { + #[repr(C)] + #[derive(Device)] + struct DummyState { + #[property(rename = "migrate-clk", rename = "migrate-clk", default = true)] + migrate_clock: bool, + } + }, + "`rename` can only be used at most once" + ); + derive_compile_fail!( + derive_device_or_error, + quote! { + #[repr(C)] + #[derive(Device)] + struct DummyState { + #[property(default = true, default = true)] + migrate_clock: bool, + } + }, + "`default` can only be used at most once" + ); + // Check that the field name is preserved when `rename` isn't used: + derive_compile!( + derive_device_or_error, + quote! { + #[repr(C)] + #[derive(Device)] + pub struct DummyState { + parent: ParentField, + #[property(default = true)] + migrate_clock: bool, + } + }, + quote! { + unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState { + fn properties() -> &'static [::qemu_api::bindings::Property] { + static PROPERTIES: &[::qemu_api::bindings::Property] = + &[::qemu_api::bindings::Property { + name: ::std::ffi::CStr::as_ptr(c"migrate_clock"), + info: ::VALUE, + offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, + set_default: true, + defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 }, + ..::qemu_api::zeroable::Zeroable::ZERO + }]; + PROPERTIES + } + } + } + ); + // Check that `rename` value is used for the property name when used: + derive_compile!( + derive_device_or_error, + quote! { + #[repr(C)] + #[derive(Device)] + pub struct DummyState { + parent: ParentField, + #[property(rename = "migrate-clk", default = true)] + migrate_clock: bool, + } + }, + quote! { + unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState { + fn properties() -> &'static [::qemu_api::bindings::Property] { + static PROPERTIES: &[::qemu_api::bindings::Property] = + &[::qemu_api::bindings::Property { + name: ::std::ffi::CStr::as_ptr(c"migrate-clk"), + info: ::VALUE, + offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, + set_default: true, + defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 }, + ..::qemu_api::zeroable::Zeroable::ZERO + }]; + PROPERTIES + } + } + } + ); +} + #[test] fn test_derive_object() { derive_compile_fail!( diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 52d54a4494edf..6a58a00e3fc46 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -101,8 +101,67 @@ unsafe extern "C" fn rust_resettable_exit_fn( T::EXIT.unwrap()(unsafe { state.as_ref() }, typ); } +/// Helper trait to return pointer to a [`bindings::PropertyInfo`] for a type. +/// +/// This trait is used by [`qemu_api_macros::Device`] derive macro. +/// +/// Base types that already have `qdev_prop_*` globals in the QEMU API should +/// use those values as exported by the [`bindings`] module, instead of +/// redefining them. +/// +/// # Safety +/// +/// This trait is marked as `unsafe` because currently having a `const` refer to +/// an `extern static` as a reference instead of a raw pointer results in this +/// compiler error: +/// +/// ```text +/// constructing invalid value: encountered reference to `extern` static in `const` +/// ``` +/// +/// This is because the compiler generally might dereference a normal reference +/// during const evaluation, but not in this case (if it did, it'd need to +/// dereference the raw pointer so this would fail to compile). +/// +/// It is the implementer's responsibility to provide a valid +/// [`bindings::PropertyInfo`] pointer for the trait implementation to be safe. +pub unsafe trait QDevProp { + const VALUE: *const bindings::PropertyInfo; +} + +/// Use [`bindings::qdev_prop_bool`] for `bool`. +unsafe impl QDevProp for bool { + const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_bool }; +} + +/// Use [`bindings::qdev_prop_uint64`] for `u64`. +unsafe impl QDevProp for u64 { + const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_uint64 }; +} + +/// Use [`bindings::qdev_prop_chr`] for [`crate::chardev::CharBackend`]. +unsafe impl QDevProp for crate::chardev::CharBackend { + const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_chr }; +} + +/// Trait to define device properties. +/// +/// # Safety +/// +/// Caller is responsible for the validity of properties array. +pub unsafe trait DevicePropertiesImpl { + /// An array providing the properties that the user can set on the + /// device. Not a `const` because referencing statics in constants + /// is unstable until Rust 1.83.0. + fn properties() -> &'static [Property] { + &[] + } +} + /// Trait providing the contents of [`DeviceClass`]. -pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA { +pub trait DeviceImpl: + ObjectImpl + ResettablePhasesImpl + DevicePropertiesImpl + IsA +{ /// _Realization_ is the second stage of device creation. It contains /// all operations that depend on device properties and can fail (note: /// this is not yet supported for Rust devices). @@ -111,13 +170,6 @@ pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA { /// with the function pointed to by `REALIZE`. const REALIZE: Option Result<()>> = None; - /// An array providing the properties that the user can set on the - /// device. Not a `const` because referencing statics in constants - /// is unstable until Rust 1.83.0. - fn properties() -> &'static [Property] { - &[] - } - /// A `VMStateDescription` providing the migration format for the device /// Not a `const` because referencing statics in constants is unstable /// until Rust 1.83.0. @@ -175,7 +227,7 @@ impl DeviceClass { if let Some(vmsd) = ::vmsd() { self.vmsd = vmsd; } - let prop = ::properties(); + let prop = ::properties(); if !prop.is_empty() { unsafe { bindings::device_class_set_props_n(self, prop.as_ptr(), prop.len()); diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index a658a49fcfdda..aff3eecd654e1 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -5,11 +5,10 @@ use std::{ffi::CStr, ptr::addr_of}; use qemu_api::{ - bindings::{module_call_init, module_init_type, qdev_prop_bool}, + bindings::{module_call_init, module_init_type}, cell::{self, BqlCell}, - declare_properties, define_property, prelude::*, - qdev::{DeviceImpl, DeviceState, Property, ResettablePhasesImpl}, + qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl}, qom::{ObjectImpl, ParentField}, sysbus::SysBusDevice, vmstate::VMStateDescription, @@ -26,9 +25,10 @@ pub static VMSTATE: VMStateDescription = VMStateDescription { }; #[repr(C)] -#[derive(qemu_api_macros::Object)] +#[derive(qemu_api_macros::Object, qemu_api_macros::Device)] pub struct DummyState { parent: ParentField, + #[property(rename = "migrate-clk", default = true)] migrate_clock: bool, } @@ -44,17 +44,6 @@ impl DummyClass { } } -declare_properties! { - DUMMY_PROPERTIES, - define_property!( - c"migrate-clk", - DummyState, - migrate_clock, - unsafe { &qdev_prop_bool }, - bool - ), -} - unsafe impl ObjectType for DummyState { type Class = DummyClass; const TYPE_NAME: &'static CStr = c"dummy"; @@ -69,16 +58,13 @@ impl ObjectImpl for DummyState { impl ResettablePhasesImpl for DummyState {} impl DeviceImpl for DummyState { - fn properties() -> &'static [Property] { - &DUMMY_PROPERTIES - } fn vmsd() -> Option<&'static VMStateDescription> { Some(&VMSTATE) } } #[repr(C)] -#[derive(qemu_api_macros::Object)] +#[derive(qemu_api_macros::Object, qemu_api_macros::Device)] pub struct DummyChildState { parent: ParentField, } From 7da9ee9207c55a783567eb46c71fa89cb5b43461 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 8 Sep 2025 12:49:39 +0200 Subject: [PATCH 0731/1794] rust: vmstate: convert to use builder pattern Similar to MemoryRegionOps, the builder pattern has two advantages: 1) it makes it possible to build a VMStateDescription that knows which types it will be invoked on; 2) it provides a way to wrap the callbacks and let devices avoid "unsafe". Unfortunately, building a static VMStateDescription requires the builder methods to be "const", and because the VMStateFields are *also* static, this requires const_refs_static. So this requires Rust 1.83.0. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250908105005.2119297-8-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 2 +- rust/hw/char/pl011/src/device.rs | 135 +++++++-------- rust/hw/timer/hpet/src/device.rs | 166 ++++++++---------- rust/qemu-api/src/errno.rs | 11 +- rust/qemu-api/src/qdev.rs | 6 +- rust/qemu-api/src/vmstate.rs | 242 ++++++++++++++++++++++++++- rust/qemu-api/tests/tests.rs | 16 +- rust/qemu-api/tests/vmstate_tests.rs | 124 +++++++------- 8 files changed, 454 insertions(+), 248 deletions(-) diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 98e3a33a3ceb1..aca29e55c0570 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -162,7 +162,7 @@ module status ``qom`` stable ``sysbus`` stable ``timer`` stable -``vmstate`` proof of concept +``vmstate`` stable ``zeroable`` stable ================ ====================== diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 37944635202f4..21611d9c099ef 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -3,9 +3,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later use std::{ - ffi::{c_int, c_void, CStr}, - mem::size_of, - ptr::NonNull, + ffi::CStr, + mem::size_of }; use qemu_api::{ @@ -21,9 +20,8 @@ use qemu_api::{ static_assert, sysbus::{SysBusDevice, SysBusDeviceImpl}, uninit_field_mut, - vmstate::VMStateDescription, + vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused, - zeroable::Zeroable, }; use crate::registers::{self, Interrupt, RegisterOffset}; @@ -177,8 +175,8 @@ impl ObjectImpl for PL011State { } impl DeviceImpl for PL011State { - fn vmsd() -> Option<&'static VMStateDescription> { - Some(&VMSTATE_PL011) + fn vmsd() -> Option> { + Some(VMSTATE_PL011) } const REALIZE: Option qemu_api::Result<()>> = Some(Self::realize); } @@ -467,10 +465,10 @@ impl PL011Registers { false } - pub fn post_load(&mut self) -> Result<(), ()> { + pub fn post_load(&mut self) -> Result<(), vmstate::InvalidError> { /* Sanity-check input state */ if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() { - return Err(()); + return Err(vmstate::InvalidError); } if !self.fifo_enabled() && self.read_count > 0 && self.read_pos > 0 { @@ -529,6 +527,10 @@ impl PL011State { /* pl011_trace_baudrate_change(s); */ } + pub fn clock_needed(&self) -> bool { + self.migrate_clock + } + fn post_init(&self) { self.init_mmio(&self.iomem); for irq in self.interrupts.iter() { @@ -645,7 +647,7 @@ impl PL011State { } } - pub fn post_load(&self, _version_id: u32) -> Result<(), ()> { + pub fn post_load(&self, _version_id: u8) -> Result<(), vmstate::InvalidError> { self.regs.borrow_mut().post_load() } } @@ -715,68 +717,53 @@ impl DeviceImpl for PL011Luminary {} impl ResettablePhasesImpl for PL011Luminary {} impl SysBusDeviceImpl for PL011Luminary {} -extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { - let state = NonNull::new(opaque).unwrap().cast::(); - unsafe { state.as_ref().migrate_clock } -} - /// Migration subsection for [`PL011State`] clock. -static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { - name: c"pl011/clock".as_ptr(), - version_id: 1, - minimum_version_id: 1, - needed: Some(pl011_clock_needed), - fields: vmstate_fields! { - vmstate_clock!(PL011State, clock), - }, - ..Zeroable::ZERO -}; - -extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { - let state = NonNull::new(opaque).unwrap().cast::(); - let result = unsafe { state.as_ref().post_load(version_id as u32) }; - if result.is_err() { - -1 - } else { - 0 - } -} - -static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription { - name: c"pl011/regs".as_ptr(), - version_id: 2, - minimum_version_id: 2, - fields: vmstate_fields! { - vmstate_of!(PL011Registers, flags), - vmstate_of!(PL011Registers, line_control), - vmstate_of!(PL011Registers, receive_status_error_clear), - vmstate_of!(PL011Registers, control), - vmstate_of!(PL011Registers, dmacr), - vmstate_of!(PL011Registers, int_enabled), - vmstate_of!(PL011Registers, int_level), - vmstate_of!(PL011Registers, read_fifo), - vmstate_of!(PL011Registers, ilpr), - vmstate_of!(PL011Registers, ibrd), - vmstate_of!(PL011Registers, fbrd), - vmstate_of!(PL011Registers, ifl), - vmstate_of!(PL011Registers, read_pos), - vmstate_of!(PL011Registers, read_count), - vmstate_of!(PL011Registers, read_trigger), - }, - ..Zeroable::ZERO -}; - -pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { - name: c"pl011".as_ptr(), - version_id: 2, - minimum_version_id: 2, - post_load: Some(pl011_post_load), - fields: vmstate_fields! { - vmstate_unused!(core::mem::size_of::()), - vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell), - }, - subsections: vmstate_subsections! { - VMSTATE_PL011_CLOCK - }, - ..Zeroable::ZERO -}; +static VMSTATE_PL011_CLOCK: VMStateDescription = + VMStateDescriptionBuilder::::new() + .name(c"pl011/clock") + .version_id(1) + .minimum_version_id(1) + .needed(&PL011State::clock_needed) + .fields(vmstate_fields! { + vmstate_clock!(PL011State, clock), + }) + .build(); + +static VMSTATE_PL011_REGS: VMStateDescription = + VMStateDescriptionBuilder::::new() + .name(c"pl011/regs") + .version_id(2) + .minimum_version_id(2) + .fields(vmstate_fields! { + vmstate_of!(PL011Registers, flags), + vmstate_of!(PL011Registers, line_control), + vmstate_of!(PL011Registers, receive_status_error_clear), + vmstate_of!(PL011Registers, control), + vmstate_of!(PL011Registers, dmacr), + vmstate_of!(PL011Registers, int_enabled), + vmstate_of!(PL011Registers, int_level), + vmstate_of!(PL011Registers, read_fifo), + vmstate_of!(PL011Registers, ilpr), + vmstate_of!(PL011Registers, ibrd), + vmstate_of!(PL011Registers, fbrd), + vmstate_of!(PL011Registers, ifl), + vmstate_of!(PL011Registers, read_pos), + vmstate_of!(PL011Registers, read_count), + vmstate_of!(PL011Registers, read_trigger), + }) + .build(); + +pub const VMSTATE_PL011: VMStateDescription = + VMStateDescriptionBuilder::::new() + .name(c"pl011") + .version_id(2) + .minimum_version_id(2) + .post_load(&PL011State::post_load) + .fields(vmstate_fields! { + vmstate_unused!(core::mem::size_of::()), + vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell), + }) + .subsections(vmstate_subsections! { + VMSTATE_PL011_CLOCK + }) + .build(); diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 01d5a0dd70c19..955cf869ff696 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later use std::{ - ffi::{c_int, c_void, CStr}, + ffi::CStr, mem::MaybeUninit, pin::Pin, ptr::{addr_of_mut, null_mut, NonNull}, @@ -27,9 +27,8 @@ use qemu_api::{ sysbus::{SysBusDevice, SysBusDeviceImpl}, timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}, uninit_field_mut, - vmstate::VMStateDescription, + vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate, - zeroable::Zeroable, }; use crate::fw_cfg::HPETFwConfig; @@ -213,6 +212,10 @@ pub struct HPETTimer { last: u64, } +// SAFETY: Sync is not automatically derived due to the `state` field, +// which is always dereferenced to a shared reference. +unsafe impl Sync for HPETTimer {} + impl HPETTimer { fn new(index: u8, state: *const HPETState) -> HPETTimer { HPETTimer { @@ -841,7 +844,7 @@ impl HPETState { } } - fn pre_save(&self) -> i32 { + fn pre_save(&self) -> Result<(), vmstate::Infallible> { if self.is_hpet_enabled() { self.counter.set(self.get_ticks()); } @@ -852,10 +855,10 @@ impl HPETState { * that was configured. */ self.num_timers_save.set(self.num_timers as u8); - 0 + Ok(()) } - fn post_load(&self, _version_id: u8) -> i32 { + fn post_load(&self, _version_id: u8) -> Result<(), vmstate::Infallible> { for timer in self.timers.iter().take(self.num_timers) { let mut t = timer.borrow_mut(); @@ -869,7 +872,7 @@ impl HPETState { .set(ticks_to_ns(self.counter.get()) - CLOCK_VIRTUAL.get_ns()); } - 0 + Ok(()) } fn is_rtc_irq_level_needed(&self) -> bool { @@ -939,97 +942,66 @@ qemu_api::declare_properties! { ), } -unsafe extern "C" fn hpet_rtc_irq_level_needed(opaque: *mut c_void) -> bool { - // SAFETY: - // the pointer is convertible to a reference - let state: &HPETState = unsafe { NonNull::new(opaque.cast::()).unwrap().as_ref() }; - state.is_rtc_irq_level_needed() -} - -unsafe extern "C" fn hpet_offset_needed(opaque: *mut c_void) -> bool { - // SAFETY: - // the pointer is convertible to a reference - let state: &HPETState = unsafe { NonNull::new(opaque.cast::()).unwrap().as_ref() }; - state.is_offset_needed() -} - -unsafe extern "C" fn hpet_pre_save(opaque: *mut c_void) -> c_int { - // SAFETY: - // the pointer is convertible to a reference - let state: &mut HPETState = - unsafe { NonNull::new(opaque.cast::()).unwrap().as_mut() }; - state.pre_save() as c_int -} - -unsafe extern "C" fn hpet_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { - // SAFETY: - // the pointer is convertible to a reference - let state: &mut HPETState = - unsafe { NonNull::new(opaque.cast::()).unwrap().as_mut() }; - let version: u8 = version_id.try_into().unwrap(); - state.post_load(version) as c_int -} - -static VMSTATE_HPET_RTC_IRQ_LEVEL: VMStateDescription = VMStateDescription { - name: c"hpet/rtc_irq_level".as_ptr(), - version_id: 1, - minimum_version_id: 1, - needed: Some(hpet_rtc_irq_level_needed), - fields: vmstate_fields! { - vmstate_of!(HPETState, rtc_irq_level), - }, - ..Zeroable::ZERO -}; - -static VMSTATE_HPET_OFFSET: VMStateDescription = VMStateDescription { - name: c"hpet/offset".as_ptr(), - version_id: 1, - minimum_version_id: 1, - needed: Some(hpet_offset_needed), - fields: vmstate_fields! { - vmstate_of!(HPETState, hpet_offset), - }, - ..Zeroable::ZERO -}; - -static VMSTATE_HPET_TIMER: VMStateDescription = VMStateDescription { - name: c"hpet_timer".as_ptr(), - version_id: 1, - minimum_version_id: 1, - fields: vmstate_fields! { - vmstate_of!(HPETTimer, index), - vmstate_of!(HPETTimer, config), - vmstate_of!(HPETTimer, cmp), - vmstate_of!(HPETTimer, fsb), - vmstate_of!(HPETTimer, period), - vmstate_of!(HPETTimer, wrap_flag), - vmstate_of!(HPETTimer, qemu_timer), - }, - ..Zeroable::ZERO -}; +static VMSTATE_HPET_RTC_IRQ_LEVEL: VMStateDescription = + VMStateDescriptionBuilder::::new() + .name(c"hpet/rtc_irq_level") + .version_id(1) + .minimum_version_id(1) + .needed(&HPETState::is_rtc_irq_level_needed) + .fields(vmstate_fields! { + vmstate_of!(HPETState, rtc_irq_level), + }) + .build(); + +static VMSTATE_HPET_OFFSET: VMStateDescription = + VMStateDescriptionBuilder::::new() + .name(c"hpet/offset") + .version_id(1) + .minimum_version_id(1) + .needed(&HPETState::is_offset_needed) + .fields(vmstate_fields! { + vmstate_of!(HPETState, hpet_offset), + }) + .build(); + +static VMSTATE_HPET_TIMER: VMStateDescription = + VMStateDescriptionBuilder::::new() + .name(c"hpet_timer") + .version_id(1) + .minimum_version_id(1) + .fields(vmstate_fields! { + vmstate_of!(HPETTimer, index), + vmstate_of!(HPETTimer, config), + vmstate_of!(HPETTimer, cmp), + vmstate_of!(HPETTimer, fsb), + vmstate_of!(HPETTimer, period), + vmstate_of!(HPETTimer, wrap_flag), + vmstate_of!(HPETTimer, qemu_timer), + }) + .build(); const VALIDATE_TIMERS_NAME: &CStr = c"num_timers must match"; -static VMSTATE_HPET: VMStateDescription = VMStateDescription { - name: c"hpet".as_ptr(), - version_id: 2, - minimum_version_id: 2, - pre_save: Some(hpet_pre_save), - post_load: Some(hpet_post_load), - fields: vmstate_fields! { - vmstate_of!(HPETState, config), - vmstate_of!(HPETState, int_status), - vmstate_of!(HPETState, counter), - vmstate_of!(HPETState, num_timers_save), - vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers), - vmstate_struct!(HPETState, timers[0 .. num_timers_save], &VMSTATE_HPET_TIMER, BqlRefCell, HPETState::validate_num_timers).with_version_id(0), - }, - subsections: vmstate_subsections! { - VMSTATE_HPET_RTC_IRQ_LEVEL, - VMSTATE_HPET_OFFSET, - }, - ..Zeroable::ZERO -}; +const VMSTATE_HPET: VMStateDescription = + VMStateDescriptionBuilder::::new() + .name(c"hpet") + .version_id(2) + .minimum_version_id(2) + .pre_save(&HPETState::pre_save) + .post_load(&HPETState::post_load) + .fields(vmstate_fields! { + vmstate_of!(HPETState, config), + vmstate_of!(HPETState, int_status), + vmstate_of!(HPETState, counter), + vmstate_of!(HPETState, num_timers_save), + vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers), + vmstate_struct!(HPETState, timers[0 .. num_timers_save], &VMSTATE_HPET_TIMER, BqlRefCell, HPETState::validate_num_timers).with_version_id(0), + }) + .subsections(vmstate_subsections!( + VMSTATE_HPET_RTC_IRQ_LEVEL, + VMSTATE_HPET_OFFSET, + )) + .build(); // SAFETY: HPET_PROPERTIES is a valid Property array constructed with the // qemu_api::declare_properties macro. @@ -1040,8 +1012,8 @@ unsafe impl qemu_api::qdev::DevicePropertiesImpl for HPETState { } impl DeviceImpl for HPETState { - fn vmsd() -> Option<&'static VMStateDescription> { - Some(&VMSTATE_HPET) + fn vmsd() -> Option> { + Some(VMSTATE_HPET) } const REALIZE: Option qemu_api::Result<()>> = Some(Self::realize); diff --git a/rust/qemu-api/src/errno.rs b/rust/qemu-api/src/errno.rs index 18d101448b936..507850fe33cb6 100644 --- a/rust/qemu-api/src/errno.rs +++ b/rust/qemu-api/src/errno.rs @@ -7,7 +7,10 @@ //! convention. This module provides functions to portably convert an integer //! into an [`io::Result`] and back. -use std::{convert::TryFrom, io, io::ErrorKind}; +use std::{ + convert::{self, TryFrom}, + io::{self, ErrorKind}, +}; /// An `errno` value that can be converted into an [`io::Error`] pub struct Errno(pub u16); @@ -99,6 +102,12 @@ impl From for Errno { } } +impl From for Errno { + fn from(_value: convert::Infallible) -> Errno { + panic!("unreachable") + } +} + /// Internal traits; used to enable [`into_io_result`] and [`into_neg_errno`] /// for the "right" set of types. mod traits { diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 6a58a00e3fc46..4dda8c8113143 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -173,7 +173,7 @@ pub trait DeviceImpl: /// A `VMStateDescription` providing the migration format for the device /// Not a `const` because referencing statics in constants is unstable /// until Rust 1.83.0. - fn vmsd() -> Option<&'static VMStateDescription> { + fn vmsd() -> Option> { None } } @@ -225,7 +225,9 @@ impl DeviceClass { self.realize = Some(rust_realize_fn::); } if let Some(vmsd) = ::vmsd() { - self.vmsd = vmsd; + // Give a 'static lifetime to the return value of vmsd(). + // Temporary until vmsd() can be changed into a const. + self.vmsd = Box::leak(Box::new(vmsd.get())); } let prop = ::properties(); if !prop.is_empty() { diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 8515e3821351c..f5f1ea5590f67 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -24,12 +24,24 @@ //! `include/migration/vmstate.h`. These are not type-safe and only provide //! functionality that is missing from `vmstate_of!`. -use core::{marker::PhantomData, mem, ptr::NonNull}; -use std::ffi::{c_int, c_void}; +pub use std::convert::Infallible; +use std::{ + error::Error, + ffi::{c_int, c_void, CStr}, + fmt, io, + marker::PhantomData, + mem, + ptr::NonNull, +}; -pub use crate::bindings::{VMStateDescription, VMStateField}; +pub use crate::bindings::{MigrationPriority, VMStateField}; use crate::{ - bindings::VMStateFlags, callbacks::FnCall, prelude::*, qom::Owned, zeroable::Zeroable, + bindings::{self, VMStateFlags}, + callbacks::FnCall, + errno::{into_neg_errno, Errno}, + prelude::*, + qom::Owned, + zeroable::Zeroable, }; /// This macro is used to call a function with a generic argument bound @@ -440,7 +452,7 @@ pub extern "C" fn rust_vms_test_field_exists FnCall<(&'a T, u8), b opaque: *mut c_void, version_id: c_int, ) -> bool { - // SAFETY: the opaque was passed as a reference to `T`. + // SAFETY: assumes vmstate_struct! is used correctly let owner: &T = unsafe { &*(opaque.cast::()) }; let version: u8 = version_id.try_into().unwrap(); F::call((owner, version)) @@ -490,7 +502,7 @@ macro_rules! vmstate_struct { }, size: ::core::mem::size_of::<$type>(), flags: $crate::bindings::VMStateFlags::VMS_STRUCT, - vmsd: $vmsd, + vmsd: $vmsd.as_ref(), $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)? ..$crate::zeroable::Zeroable::ZERO } $(.with_varray_flag_unchecked( @@ -594,11 +606,225 @@ macro_rules! vmstate_subsections { ($($subsection:expr),*$(,)*) => {{ static _SUBSECTIONS: $crate::vmstate::VMStateSubsectionsWrapper = $crate::vmstate::VMStateSubsectionsWrapper(&[ $({ - static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection; + static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection.get(); ::core::ptr::addr_of!(_SUBSECTION) }),*, ::core::ptr::null() ]); - _SUBSECTIONS.0.as_ptr() + &_SUBSECTIONS }} } + +pub struct VMStateDescription(bindings::VMStateDescription, PhantomData); + +// SAFETY: When a *const T is passed to the callbacks, the call itself +// is done in a thread-safe manner. The invocation is okay as long as +// T itself is `Sync`. +unsafe impl Sync for VMStateDescription {} + +#[derive(Clone)] +pub struct VMStateDescriptionBuilder(bindings::VMStateDescription, PhantomData); + +#[derive(Debug)] +pub struct InvalidError; + +impl Error for InvalidError {} + +impl std::fmt::Display for InvalidError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "invalid migration data") + } +} + +impl From for Errno { + fn from(_value: InvalidError) -> Errno { + io::ErrorKind::InvalidInput.into() + } +} + +unsafe extern "C" fn vmstate_no_version_cb< + T, + F: for<'a> FnCall<(&'a T,), Result<(), impl Into>>, +>( + opaque: *mut c_void, +) -> c_int { + // SAFETY: assumes vmstate_struct! is used correctly + let result = F::call((unsafe { &*(opaque.cast::()) },)); + into_neg_errno(result) +} + +unsafe extern "C" fn vmstate_post_load_cb< + T, + F: for<'a> FnCall<(&'a T, u8), Result<(), impl Into>>, +>( + opaque: *mut c_void, + version_id: c_int, +) -> c_int { + // SAFETY: assumes vmstate_struct! is used correctly + let owner: &T = unsafe { &*(opaque.cast::()) }; + let version: u8 = version_id.try_into().unwrap(); + let result = F::call((owner, version)); + into_neg_errno(result) +} + +unsafe extern "C" fn vmstate_needed_cb FnCall<(&'a T,), bool>>( + opaque: *mut c_void, +) -> bool { + // SAFETY: assumes vmstate_struct! is used correctly + F::call((unsafe { &*(opaque.cast::()) },)) +} + +unsafe extern "C" fn vmstate_dev_unplug_pending_cb FnCall<(&'a T,), bool>>( + opaque: *mut c_void, +) -> bool { + // SAFETY: assumes vmstate_struct! is used correctly + F::call((unsafe { &*(opaque.cast::()) },)) +} + +impl VMStateDescriptionBuilder { + #[must_use] + pub const fn name(mut self, name_str: &CStr) -> Self { + self.0.name = ::std::ffi::CStr::as_ptr(name_str); + self + } + + #[must_use] + pub const fn unmigratable(mut self) -> Self { + self.0.unmigratable = true; + self + } + + #[must_use] + pub const fn early_setup(mut self) -> Self { + self.0.early_setup = true; + self + } + + #[must_use] + pub const fn version_id(mut self, version: u8) -> Self { + self.0.version_id = version as c_int; + self + } + + #[must_use] + pub const fn minimum_version_id(mut self, min_version: u8) -> Self { + self.0.minimum_version_id = min_version as c_int; + self + } + + #[must_use] + pub const fn priority(mut self, priority: MigrationPriority) -> Self { + self.0.priority = priority; + self + } + + #[must_use] + pub const fn pre_load FnCall<(&'a T,), Result<(), impl Into>>>( + mut self, + _f: &F, + ) -> Self { + self.0.pre_load = if F::IS_SOME { + Some(vmstate_no_version_cb::) + } else { + None + }; + self + } + + #[must_use] + pub const fn post_load FnCall<(&'a T, u8), Result<(), impl Into>>>( + mut self, + _f: &F, + ) -> Self { + self.0.post_load = if F::IS_SOME { + Some(vmstate_post_load_cb::) + } else { + None + }; + self + } + + #[must_use] + pub const fn pre_save FnCall<(&'a T,), Result<(), impl Into>>>( + mut self, + _f: &F, + ) -> Self { + self.0.pre_save = if F::IS_SOME { + Some(vmstate_no_version_cb::) + } else { + None + }; + self + } + + #[must_use] + pub const fn post_save FnCall<(&'a T,), Result<(), impl Into>>>( + mut self, + _f: &F, + ) -> Self { + self.0.post_save = if F::IS_SOME { + Some(vmstate_no_version_cb::) + } else { + None + }; + self + } + + #[must_use] + pub const fn needed FnCall<(&'a T,), bool>>(mut self, _f: &F) -> Self { + self.0.needed = if F::IS_SOME { + Some(vmstate_needed_cb::) + } else { + None + }; + self + } + + #[must_use] + pub const fn unplug_pending FnCall<(&'a T,), bool>>(mut self, _f: &F) -> Self { + self.0.dev_unplug_pending = if F::IS_SOME { + Some(vmstate_dev_unplug_pending_cb::) + } else { + None + }; + self + } + + #[must_use] + pub const fn fields(mut self, fields: *const VMStateField) -> Self { + self.0.fields = fields; + self + } + + #[must_use] + pub const fn subsections(mut self, subs: &'static VMStateSubsectionsWrapper) -> Self { + self.0.subsections = subs.0.as_ptr(); + self + } + + #[must_use] + pub const fn build(self) -> VMStateDescription { + VMStateDescription::(self.0, PhantomData) + } + + #[must_use] + pub const fn new() -> Self { + Self(bindings::VMStateDescription::ZERO, PhantomData) + } +} + +impl Default for VMStateDescriptionBuilder { + fn default() -> Self { + Self::new() + } +} + +impl VMStateDescription { + pub const fn get(&self) -> bindings::VMStateDescription { + self.0 + } + + pub const fn as_ref(&self) -> &bindings::VMStateDescription { + &self.0 + } +} diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index aff3eecd654e1..4d4e4653f38e7 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -11,18 +11,16 @@ use qemu_api::{ qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl}, qom::{ObjectImpl, ParentField}, sysbus::SysBusDevice, - vmstate::VMStateDescription, - zeroable::Zeroable, + vmstate::{VMStateDescription, VMStateDescriptionBuilder}, }; mod vmstate_tests; // Test that macros can compile. -pub static VMSTATE: VMStateDescription = VMStateDescription { - name: c"name".as_ptr(), - unmigratable: true, - ..Zeroable::ZERO -}; +pub const VMSTATE: VMStateDescription = VMStateDescriptionBuilder::::new() + .name(c"name") + .unmigratable() + .build(); #[repr(C)] #[derive(qemu_api_macros::Object, qemu_api_macros::Device)] @@ -58,8 +56,8 @@ impl ObjectImpl for DummyState { impl ResettablePhasesImpl for DummyState {} impl DeviceImpl for DummyState { - fn vmsd() -> Option<&'static VMStateDescription> { - Some(&VMSTATE) + fn vmsd() -> Option> { + Some(VMSTATE) } } diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs index bded836eb608d..8ffc2779d4613 100644 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -16,9 +16,8 @@ use qemu_api::{ }, cell::{BqlCell, Opaque}, impl_vmstate_forward, - vmstate::{VMStateDescription, VMStateField}, + vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField}, vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, vmstate_validate, - zeroable::Zeroable, }; const FOO_ARRAY_MAX: usize = 3; @@ -41,22 +40,22 @@ struct FooA { elem: i8, } -static VMSTATE_FOOA: VMStateDescription = VMStateDescription { - name: c"foo_a".as_ptr(), - version_id: 1, - minimum_version_id: 1, - fields: vmstate_fields! { +static VMSTATE_FOOA: VMStateDescription = VMStateDescriptionBuilder::::new() + .name(c"foo_a") + .version_id(1) + .minimum_version_id(1) + .fields(vmstate_fields! { vmstate_of!(FooA, elem), vmstate_unused!(size_of::()), vmstate_of!(FooA, arr[0 .. num]).with_version_id(0), vmstate_of!(FooA, arr_mul[0 .. num_mul * 16]), - }, - ..Zeroable::ZERO -}; + }) + .build(); #[test] fn test_vmstate_uint16() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) }; // 1st VMStateField ("elem") in VMSTATE_FOOA (corresponding to VMSTATE_UINT16) assert_eq!( @@ -76,7 +75,8 @@ fn test_vmstate_uint16() { #[test] fn test_vmstate_unused() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) }; // 2nd VMStateField ("unused") in VMSTATE_FOOA (corresponding to VMSTATE_UNUSED) assert_eq!( @@ -96,7 +96,8 @@ fn test_vmstate_unused() { #[test] fn test_vmstate_varray_uint16_unsafe() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) }; // 3rd VMStateField ("arr") in VMSTATE_FOOA (corresponding to // VMSTATE_VARRAY_UINT16_UNSAFE) @@ -117,7 +118,8 @@ fn test_vmstate_varray_uint16_unsafe() { #[test] fn test_vmstate_varray_multiply() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) }; // 4th VMStateField ("arr_mul") in VMSTATE_FOOA (corresponding to // VMSTATE_VARRAY_MULTIPLY) @@ -171,24 +173,25 @@ fn validate_foob(_state: &FooB, _version_id: u8) -> bool { true } -static VMSTATE_FOOB: VMStateDescription = VMStateDescription { - name: c"foo_b".as_ptr(), - version_id: 2, - minimum_version_id: 1, - fields: vmstate_fields! { - vmstate_of!(FooB, val).with_version_id(2), - vmstate_of!(FooB, wrap), - vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1), - vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2), - vmstate_of!(FooB, arr_i64), - vmstate_struct!(FooB, arr_a_wrap[0 .. num_a_wrap], &VMSTATE_FOOA, FooA, validate_foob), - }, - ..Zeroable::ZERO -}; +static VMSTATE_FOOB: VMStateDescription = + VMStateDescriptionBuilder::::new() + .name(c"foo_b") + .version_id(2) + .minimum_version_id(1) + .fields(vmstate_fields! { + vmstate_of!(FooB, val).with_version_id(2), + vmstate_of!(FooB, wrap), + vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1), + vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2), + vmstate_of!(FooB, arr_i64), + vmstate_struct!(FooB, arr_a_wrap[0 .. num_a_wrap], &VMSTATE_FOOA, FooA, validate_foob), + }) + .build(); #[test] fn test_vmstate_bool_v() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; // 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V) assert_eq!( @@ -208,7 +211,8 @@ fn test_vmstate_bool_v() { #[test] fn test_vmstate_uint64() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; // 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64) assert_eq!( @@ -228,7 +232,8 @@ fn test_vmstate_uint64() { #[test] fn test_vmstate_struct_varray_uint8() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; // 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to // VMSTATE_STRUCT_VARRAY_UINT8) @@ -246,13 +251,14 @@ fn test_vmstate_struct_varray_uint8() { foo_fields[2].flags.0, VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_VARRAY_UINT8.0 ); - assert_eq!(foo_fields[2].vmsd, &VMSTATE_FOOA); + assert_eq!(foo_fields[2].vmsd, VMSTATE_FOOA.as_ref()); assert!(foo_fields[2].field_exists.is_none()); } #[test] fn test_vmstate_struct_varray_uint32_multiply() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; // 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to // (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32) @@ -272,13 +278,14 @@ fn test_vmstate_struct_varray_uint32_multiply() { | VMStateFlags::VMS_VARRAY_UINT32.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 ); - assert_eq!(foo_fields[3].vmsd, &VMSTATE_FOOA); + assert_eq!(foo_fields[3].vmsd, VMSTATE_FOOA.as_ref()); assert!(foo_fields[3].field_exists.is_none()); } #[test] fn test_vmstate_macro_array() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; // 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to // VMSTATE_ARRAY) @@ -299,7 +306,8 @@ fn test_vmstate_macro_array() { #[test] fn test_vmstate_struct_varray_uint8_wrapper() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; let mut foo_b: FooB = Default::default(); let foo_b_p = std::ptr::addr_of_mut!(foo_b).cast::(); @@ -335,26 +343,28 @@ struct FooC { arr_ptr_wrap: FooCWrapper, } -static VMSTATE_FOOC: VMStateDescription = VMStateDescription { - name: c"foo_c".as_ptr(), - version_id: 3, - minimum_version_id: 1, - fields: vmstate_fields! { +unsafe impl Sync for FooC {} + +static VMSTATE_FOOC: VMStateDescription = VMStateDescriptionBuilder::::new() + .name(c"foo_c") + .version_id(3) + .minimum_version_id(1) + .fields(vmstate_fields! { vmstate_of!(FooC, ptr).with_version_id(2), // FIXME: Currently vmstate_struct doesn't support the pointer to structure. // VMSTATE_STRUCT_POINTER: vmstate_struct!(FooC, ptr_a, VMSTATE_FOOA, NonNull) vmstate_unused!(size_of::>()), vmstate_of!(FooC, arr_ptr), vmstate_of!(FooC, arr_ptr_wrap), - }, - ..Zeroable::ZERO -}; + }) + .build(); const PTR_SIZE: usize = size_of::<*mut ()>(); #[test] fn test_vmstate_pointer() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) }; // 1st VMStateField ("ptr") in VMSTATE_FOOC (corresponding to VMSTATE_POINTER) assert_eq!( @@ -377,7 +387,8 @@ fn test_vmstate_pointer() { #[test] fn test_vmstate_macro_array_of_pointer() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) }; // 3rd VMStateField ("arr_ptr") in VMSTATE_FOOC (corresponding to // VMSTATE_ARRAY_OF_POINTER) @@ -401,7 +412,8 @@ fn test_vmstate_macro_array_of_pointer() { #[test] fn test_vmstate_macro_array_of_pointer_wrapped() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) }; // 4th VMStateField ("arr_ptr_wrap") in VMSTATE_FOOC (corresponding to // VMSTATE_ARRAY_OF_POINTER) @@ -450,21 +462,21 @@ fn validate_food_2(_state: &FooD, _version_id: u8) -> bool { true } -static VMSTATE_FOOD: VMStateDescription = VMStateDescription { - name: c"foo_d".as_ptr(), - version_id: 3, - minimum_version_id: 1, - fields: vmstate_fields! { +static VMSTATE_FOOD: VMStateDescription = VMStateDescriptionBuilder::::new() + .name(c"foo_d") + .version_id(3) + .minimum_version_id(1) + .fields(vmstate_fields! { vmstate_validate!(FooD, c"foo_d_0", FooD::validate_food_0), vmstate_validate!(FooD, c"foo_d_1", FooD::validate_food_1), vmstate_validate!(FooD, c"foo_d_2", validate_food_2), - }, - ..Zeroable::ZERO -}; + }) + .build(); #[test] fn test_vmstate_validate() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOD.fields, 4) }; + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOD.as_ref().fields, 4) }; let mut foo_d = FooD; let foo_d_p = std::ptr::addr_of_mut!(foo_d).cast::(); From dcdee1e718f9400a2bf523f2b009fb936370ae4b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Sep 2025 12:49:40 +0200 Subject: [PATCH 0732/1794] rust: vmstate: use const_refs_to_static The VMStateDescriptionBuilder already needs const_refs_static, so use it to remove the need for vmstate_clock! and vmstate_struct!, as well as to simplify the implementation for scalars. If the consts in the VMState trait can reference to static VMStateDescription, scalars do not need the info_enum_to_ref! indirection and structs can implement the VMState trait themselves. Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250908105005.2119297-9-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 5 - rust/hw/char/pl011/src/device.rs | 19 ++- rust/hw/timer/hpet/src/device.rs | 8 +- rust/qemu-api/src/assertions.rs | 4 - rust/qemu-api/src/vmstate.rs | 229 +++++++-------------------- rust/qemu-api/tests/vmstate_tests.rs | 65 +++++--- 6 files changed, 113 insertions(+), 217 deletions(-) diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index aca29e55c0570..10b0590b56cbb 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -84,11 +84,6 @@ patches are welcome: * ``&raw`` (stable in 1.82.0). -* referencing statics in constants (stable in 1.83.0). For now use a const - function; this is an important limitation for QEMU's migration stream - architecture (VMState). Right now, VMState lacks type safety because - it is hard to place the ``VMStateField`` definitions in traits. - * NUL-terminated file names with ``#[track_caller]`` are scheduled for inclusion as ``#![feature(location_file_nul)]``, but it will be a while before QEMU can use them. For now, there is special code in diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 21611d9c099ef..87a17716fed4d 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -2,14 +2,11 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use std::{ - ffi::CStr, - mem::size_of -}; +use std::{ffi::CStr, mem::size_of}; use qemu_api::{ chardev::{CharBackend, Chardev, Event}, - impl_vmstate_forward, + impl_vmstate_forward, impl_vmstate_struct, irq::{IRQState, InterruptSource}, log::Log, log_mask_ln, @@ -21,7 +18,7 @@ use qemu_api::{ sysbus::{SysBusDevice, SysBusDeviceImpl}, uninit_field_mut, vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, - vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused, + vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused, }; use crate::registers::{self, Interrupt, RegisterOffset}; @@ -725,11 +722,12 @@ static VMSTATE_PL011_CLOCK: VMStateDescription = .minimum_version_id(1) .needed(&PL011State::clock_needed) .fields(vmstate_fields! { - vmstate_clock!(PL011State, clock), + vmstate_of!(PL011State, clock), }) .build(); -static VMSTATE_PL011_REGS: VMStateDescription = +impl_vmstate_struct!( + PL011Registers, VMStateDescriptionBuilder::::new() .name(c"pl011/regs") .version_id(2) @@ -751,7 +749,8 @@ static VMSTATE_PL011_REGS: VMStateDescription = vmstate_of!(PL011Registers, read_count), vmstate_of!(PL011Registers, read_trigger), }) - .build(); + .build() +); pub const VMSTATE_PL011: VMStateDescription = VMStateDescriptionBuilder::::new() @@ -761,7 +760,7 @@ pub const VMSTATE_PL011: VMStateDescription = .post_load(&PL011State::post_load) .fields(vmstate_fields! { vmstate_unused!(core::mem::size_of::()), - vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell), + vmstate_of!(PL011State, regs), }) .subsections(vmstate_subsections! { VMSTATE_PL011_CLOCK diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 955cf869ff696..eb5bd042b1cbc 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -16,6 +16,7 @@ use qemu_api::{ qdev_prop_uint32, qdev_prop_usize, }, cell::{BqlCell, BqlRefCell}, + impl_vmstate_struct, irq::InterruptSource, memory::{ hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED, @@ -28,7 +29,7 @@ use qemu_api::{ timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}, uninit_field_mut, vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, - vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate, + vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, }; use crate::fw_cfg::HPETFwConfig; @@ -964,7 +965,7 @@ static VMSTATE_HPET_OFFSET: VMStateDescription = }) .build(); -static VMSTATE_HPET_TIMER: VMStateDescription = +const VMSTATE_HPET_TIMER: VMStateDescription = VMStateDescriptionBuilder::::new() .name(c"hpet_timer") .version_id(1) @@ -979,6 +980,7 @@ static VMSTATE_HPET_TIMER: VMStateDescription = vmstate_of!(HPETTimer, qemu_timer), }) .build(); +impl_vmstate_struct!(HPETTimer, VMSTATE_HPET_TIMER); const VALIDATE_TIMERS_NAME: &CStr = c"num_timers must match"; @@ -995,7 +997,7 @@ const VMSTATE_HPET: VMStateDescription = vmstate_of!(HPETState, counter), vmstate_of!(HPETState, num_timers_save), vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers), - vmstate_struct!(HPETState, timers[0 .. num_timers_save], &VMSTATE_HPET_TIMER, BqlRefCell, HPETState::validate_num_timers).with_version_id(0), + vmstate_of!(HPETState, timers[0 .. num_timers_save], HPETState::validate_num_timers).with_version_id(0), }) .subsections(vmstate_subsections!( VMSTATE_HPET_RTC_IRQ_LEVEL, diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs index e74fa3ef82645..e4fe23b674e26 100644 --- a/rust/qemu-api/src/assertions.rs +++ b/rust/qemu-api/src/assertions.rs @@ -95,10 +95,6 @@ macro_rules! assert_field_type { ($t:ty, $i:tt, $ti:ty) => { $crate::assert_field_type!(@internal v, $ti, $t, v.$i); }; - - ($t:ty, $i:tt, $ti:ty, num = $num:ident) => { - $crate::assert_field_type!(@internal v, $ti, $t, v.$i[0]); - }; } /// Assert that an expression matches a pattern. This can also be diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index f5f1ea5590f67..b5c6b764fbba8 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -11,10 +11,11 @@ //! migration format for a struct. This is based on the [`VMState`] trait, //! which is defined by all migratable types. //! -//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward) and -//! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), which help with -//! the definition of the [`VMState`] trait (respectively for transparent -//! structs and for `bilge`-defined types) +//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward), +//! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and +//! [`impl_vmstate_struct`](crate::impl_vmstate_struct), which help with the +//! definition of the [`VMState`] trait (respectively for transparent structs, +//! nested structs and `bilge`-defined types) //! //! * helper macros to declare a device model state struct, in particular //! [`vmstate_subsections`](crate::vmstate_subsections) and @@ -31,7 +32,7 @@ use std::{ fmt, io, marker::PhantomData, mem, - ptr::NonNull, + ptr::{addr_of, NonNull}, }; pub use crate::bindings::{MigrationPriority, VMStateField}; @@ -40,6 +41,7 @@ use crate::{ callbacks::FnCall, errno::{into_neg_errno, Errno}, prelude::*, + qdev, qom::Owned, zeroable::Zeroable, }; @@ -81,70 +83,6 @@ macro_rules! call_func_with_field { }; } -/// Workaround for lack of `const_refs_static`: references to global variables -/// can be included in a `static`, but not in a `const`; unfortunately, this -/// is exactly what would go in the `VMStateField`'s `info` member. -/// -/// This enum contains the contents of the `VMStateField`'s `info` member, -/// but as an `enum` instead of a pointer. -#[allow(non_camel_case_types)] -pub enum VMStateFieldType { - null, - vmstate_info_bool, - vmstate_info_int8, - vmstate_info_int16, - vmstate_info_int32, - vmstate_info_int64, - vmstate_info_uint8, - vmstate_info_uint16, - vmstate_info_uint32, - vmstate_info_uint64, - vmstate_info_timer, -} - -/// Workaround for lack of `const_refs_static`. Converts a `VMStateFieldType` -/// to a `*const VMStateInfo`, for inclusion in a `VMStateField`. -#[macro_export] -macro_rules! info_enum_to_ref { - ($e:expr) => { - unsafe { - match $e { - $crate::vmstate::VMStateFieldType::null => ::core::ptr::null(), - $crate::vmstate::VMStateFieldType::vmstate_info_bool => { - ::core::ptr::addr_of!($crate::bindings::vmstate_info_bool) - } - $crate::vmstate::VMStateFieldType::vmstate_info_int8 => { - ::core::ptr::addr_of!($crate::bindings::vmstate_info_int8) - } - $crate::vmstate::VMStateFieldType::vmstate_info_int16 => { - ::core::ptr::addr_of!($crate::bindings::vmstate_info_int16) - } - $crate::vmstate::VMStateFieldType::vmstate_info_int32 => { - ::core::ptr::addr_of!($crate::bindings::vmstate_info_int32) - } - $crate::vmstate::VMStateFieldType::vmstate_info_int64 => { - ::core::ptr::addr_of!($crate::bindings::vmstate_info_int64) - } - $crate::vmstate::VMStateFieldType::vmstate_info_uint8 => { - ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint8) - } - $crate::vmstate::VMStateFieldType::vmstate_info_uint16 => { - ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint16) - } - $crate::vmstate::VMStateFieldType::vmstate_info_uint32 => { - ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32) - } - $crate::vmstate::VMStateFieldType::vmstate_info_uint64 => { - ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint64) - } - $crate::vmstate::VMStateFieldType::vmstate_info_timer => { - ::core::ptr::addr_of!($crate::bindings::vmstate_info_timer) - } - } - } - }; -} - /// A trait for types that can be included in a device's migration stream. It /// provides the base contents of a `VMStateField` (minus the name and offset). /// @@ -155,12 +93,6 @@ macro_rules! info_enum_to_ref { /// to implement it except via macros that do it for you, such as /// `impl_vmstate_bitsized!`. pub unsafe trait VMState { - /// The `info` member of a `VMStateField` is a pointer and as such cannot - /// yet be included in the [`BASE`](VMState::BASE) associated constant; - /// this is only allowed by Rust 1.83.0 and newer. For now, include the - /// member as an enum which is stored in a separate constant. - const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::null; - /// The base contents of a `VMStateField` (minus the name and offset) for /// the type that is implementing the trait. const BASE: VMStateField; @@ -175,12 +107,6 @@ pub unsafe trait VMState { }; } -/// Internal utility function to retrieve a type's `VMStateFieldType`; -/// used by [`vmstate_of!`](crate::vmstate_of). -pub const fn vmstate_scalar_type(_: PhantomData) -> VMStateFieldType { - T::SCALAR_TYPE -} - /// Internal utility function to retrieve a type's `VMStateField`; /// used by [`vmstate_of!`](crate::vmstate_of). pub const fn vmstate_base(_: PhantomData) -> VMStateField { @@ -207,9 +133,9 @@ pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags /// * an array of any of the above /// /// In order to support other types, the trait `VMState` must be implemented -/// for them. The macros -/// [`impl_vmstate_bitsized!`](crate::impl_vmstate_bitsized) -/// and [`impl_vmstate_forward!`](crate::impl_vmstate_forward) help with this. +/// for them. The macros [`impl_vmstate_forward`](crate::impl_vmstate_forward), +/// [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and +/// [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this. #[macro_export] macro_rules! vmstate_of { ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => { @@ -222,11 +148,6 @@ macro_rules! vmstate_of { $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)? // The calls to `call_func_with_field!` are the magic that // computes most of the VMStateField from the type of the field. - info: $crate::info_enum_to_ref!($crate::call_func_with_field!( - $crate::vmstate::vmstate_scalar_type, - $struct_name, - $field_name - )), ..$crate::call_func_with_field!( $crate::vmstate::vmstate_base, $struct_name, @@ -327,8 +248,6 @@ macro_rules! impl_vmstate_forward { // the first field of the tuple ($tuple:ty) => { unsafe impl $crate::vmstate::VMState for $tuple { - const SCALAR_TYPE: $crate::vmstate::VMStateFieldType = - $crate::call_func_with_field!($crate::vmstate::vmstate_scalar_type, $tuple, 0); const BASE: $crate::bindings::VMStateField = $crate::call_func_with_field!($crate::vmstate::vmstate_base, $tuple, 0); } @@ -340,7 +259,6 @@ macro_rules! impl_vmstate_forward { macro_rules! impl_vmstate_transparent { ($type:ty where $base:tt: VMState $($where:tt)*) => { unsafe impl<$base> VMState for $type where $base: VMState $($where)* { - const SCALAR_TYPE: VMStateFieldType = <$base as VMState>::SCALAR_TYPE; const BASE: VMStateField = VMStateField { size: mem::size_of::<$type>(), ..<$base as VMState>::BASE @@ -361,10 +279,6 @@ impl_vmstate_transparent!(crate::cell::Opaque where T: VMState); macro_rules! impl_vmstate_bitsized { ($type:ty) => { unsafe impl $crate::vmstate::VMState for $type { - const SCALAR_TYPE: $crate::vmstate::VMStateFieldType = - <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt - as ::bilge::prelude::Number>::UnderlyingType - as $crate::vmstate::VMState>::SCALAR_TYPE; const BASE: $crate::bindings::VMStateField = <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt as ::bilge::prelude::Number>::UnderlyingType @@ -382,8 +296,8 @@ macro_rules! impl_vmstate_bitsized { macro_rules! impl_vmstate_scalar { ($info:ident, $type:ty$(, $varray_flag:ident)?) => { unsafe impl VMState for $type { - const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::$info; const BASE: VMStateField = VMStateField { + info: addr_of!(bindings::$info), size: mem::size_of::<$type>(), flags: VMStateFlags::VMS_SINGLE, ..Zeroable::ZERO @@ -404,6 +318,21 @@ impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32); impl_vmstate_scalar!(vmstate_info_uint64, u64); impl_vmstate_scalar!(vmstate_info_timer, crate::timer::Timer); +macro_rules! impl_vmstate_c_struct { + ($type:ty, $vmsd:expr) => { + unsafe impl VMState for $type { + const BASE: VMStateField = $crate::bindings::VMStateField { + vmsd: addr_of!($vmsd), + size: mem::size_of::<$type>(), + flags: VMStateFlags::VMS_STRUCT, + ..Zeroable::ZERO + }; + } + }; +} + +impl_vmstate_c_struct!(qdev::Clock, bindings::vmstate_clock); + // Pointer types using the underlying type's VMState plus VMS_POINTER // Note that references are not supported, though references to cells // could be allowed. @@ -411,7 +340,6 @@ impl_vmstate_scalar!(vmstate_info_timer, crate::timer::Timer); macro_rules! impl_vmstate_pointer { ($type:ty where $base:tt: VMState $($where:tt)*) => { unsafe impl<$base> VMState for $type where $base: VMState $($where)* { - const SCALAR_TYPE: VMStateFieldType = ::SCALAR_TYPE; const BASE: VMStateField = <$base as VMState>::BASE.with_pointer_flag(); } }; @@ -430,7 +358,6 @@ impl_vmstate_pointer!(Owned where T: VMState + ObjectType); // VMS_ARRAY/VMS_ARRAY_OF_POINTER unsafe impl VMState for [T; N] { - const SCALAR_TYPE: VMStateFieldType = ::SCALAR_TYPE; const BASE: VMStateField = ::BASE.with_array_flag(N); } @@ -452,7 +379,7 @@ pub extern "C" fn rust_vms_test_field_exists FnCall<(&'a T, u8), b opaque: *mut c_void, version_id: c_int, ) -> bool { - // SAFETY: assumes vmstate_struct! is used correctly + // SAFETY: the function is used in T's implementation of VMState let owner: &T = unsafe { &*(opaque.cast::()) }; let version: u8 = version_id.try_into().unwrap(); F::call((owner, version)) @@ -480,76 +407,6 @@ macro_rules! vmstate_exist_fn { }}; } -// FIXME: including the `vmsd` field in a `const` is not possible without -// the const_refs_static feature (stabilized in Rust 1.83.0). Without it, -// it is not possible to use VMS_STRUCT in a transparent manner using -// `vmstate_of!`. While VMSTATE_CLOCK can at least try to be type-safe, -// VMSTATE_STRUCT includes $type only for documentation purposes; it -// is checked against $field_name and $struct_name, but not against $vmsd -// which is what really would matter. -#[doc(alias = "VMSTATE_STRUCT")] -#[macro_export] -macro_rules! vmstate_struct { - ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?, $vmsd:expr, $type:ty $(, $test_fn:expr)? $(,)?) => { - $crate::bindings::VMStateField { - name: ::core::concat!(::core::stringify!($field_name), "\0") - .as_bytes() - .as_ptr() as *const ::std::os::raw::c_char, - $(num_offset: ::std::mem::offset_of!($struct_name, $num),)? - offset: { - $crate::assert_field_type!($struct_name, $field_name, $type $(, num = $num)?); - ::std::mem::offset_of!($struct_name, $field_name) - }, - size: ::core::mem::size_of::<$type>(), - flags: $crate::bindings::VMStateFlags::VMS_STRUCT, - vmsd: $vmsd.as_ref(), - $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)? - ..$crate::zeroable::Zeroable::ZERO - } $(.with_varray_flag_unchecked( - $crate::call_func_with_field!( - $crate::vmstate::vmstate_varray_flag, - $struct_name, - $num - ) - ) - $(.with_varray_multiply($factor))?)? - }; -} - -#[doc(alias = "VMSTATE_CLOCK")] -#[macro_export] -macro_rules! vmstate_clock { - ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?) => {{ - $crate::bindings::VMStateField { - name: ::core::concat!(::core::stringify!($field_name), "\0") - .as_bytes() - .as_ptr() as *const ::std::os::raw::c_char, - offset: { - $crate::assert_field_type!( - $struct_name, - $field_name, - $crate::qom::Owned<$crate::qdev::Clock> $(, num = $num)? - ); - ::std::mem::offset_of!($struct_name, $field_name) - }, - size: ::core::mem::size_of::<*const $crate::qdev::Clock>(), - flags: $crate::bindings::VMStateFlags( - $crate::bindings::VMStateFlags::VMS_STRUCT.0 - | $crate::bindings::VMStateFlags::VMS_POINTER.0, - ), - vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) }, - ..$crate::zeroable::Zeroable::ZERO - } $(.with_varray_flag_unchecked( - $crate::call_func_with_field!( - $crate::vmstate::vmstate_varray_flag, - $struct_name, - $num - ) - ) - $(.with_varray_multiply($factor))?)? - }}; -} - /// Helper macro to declare a list of /// ([`VMStateField`](`crate::bindings::VMStateField`)) into a static and return /// a pointer to the array of values it created. @@ -584,6 +441,30 @@ macro_rules! vmstate_validate { }; } +/// Helper macro to allow using a struct in [`vmstate_of!`] +/// +/// # Safety +/// +/// The [`VMStateDescription`] constant `$vmsd` must be an accurate +/// description of the struct. +#[macro_export] +macro_rules! impl_vmstate_struct { + ($type:ty, $vmsd:expr) => { + unsafe impl $crate::vmstate::VMState for $type { + const BASE: $crate::bindings::VMStateField = { + static VMSD: &$crate::bindings::VMStateDescription = $vmsd.as_ref(); + + $crate::bindings::VMStateField { + vmsd: ::core::ptr::addr_of!(*VMSD), + size: ::core::mem::size_of::<$type>(), + flags: $crate::bindings::VMStateFlags::VMS_STRUCT, + ..$crate::zeroable::Zeroable::ZERO + } + }; + } + }; +} + /// A transparent wrapper type for the `subsections` field of /// [`VMStateDescription`]. /// @@ -648,7 +529,7 @@ unsafe extern "C" fn vmstate_no_version_cb< >( opaque: *mut c_void, ) -> c_int { - // SAFETY: assumes vmstate_struct! is used correctly + // SAFETY: the function is used in T's implementation of VMState let result = F::call((unsafe { &*(opaque.cast::()) },)); into_neg_errno(result) } @@ -660,7 +541,7 @@ unsafe extern "C" fn vmstate_post_load_cb< opaque: *mut c_void, version_id: c_int, ) -> c_int { - // SAFETY: assumes vmstate_struct! is used correctly + // SAFETY: the function is used in T's implementation of VMState let owner: &T = unsafe { &*(opaque.cast::()) }; let version: u8 = version_id.try_into().unwrap(); let result = F::call((owner, version)); @@ -670,14 +551,14 @@ unsafe extern "C" fn vmstate_post_load_cb< unsafe extern "C" fn vmstate_needed_cb FnCall<(&'a T,), bool>>( opaque: *mut c_void, ) -> bool { - // SAFETY: assumes vmstate_struct! is used correctly + // SAFETY: the function is used in T's implementation of VMState F::call((unsafe { &*(opaque.cast::()) },)) } unsafe extern "C" fn vmstate_dev_unplug_pending_cb FnCall<(&'a T,), bool>>( opaque: *mut c_void, ) -> bool { - // SAFETY: assumes vmstate_struct! is used correctly + // SAFETY: the function is used in T's implementation of VMState F::call((unsafe { &*(opaque.cast::()) },)) } diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs index 8ffc2779d4613..2c0670ba0eed3 100644 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -15,9 +15,9 @@ use qemu_api::{ vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags, }, cell::{BqlCell, Opaque}, - impl_vmstate_forward, + impl_vmstate_forward, impl_vmstate_struct, vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField}, - vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, vmstate_validate, + vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate, }; const FOO_ARRAY_MAX: usize = 3; @@ -52,6 +52,8 @@ static VMSTATE_FOOA: VMStateDescription = VMStateDescriptionBuilder:: bool { true } -static VMSTATE_FOOB: VMStateDescription = - VMStateDescriptionBuilder::::new() - .name(c"foo_b") - .version_id(2) - .minimum_version_id(1) - .fields(vmstate_fields! { - vmstate_of!(FooB, val).with_version_id(2), - vmstate_of!(FooB, wrap), - vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1), - vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2), - vmstate_of!(FooB, arr_i64), - vmstate_struct!(FooB, arr_a_wrap[0 .. num_a_wrap], &VMSTATE_FOOA, FooA, validate_foob), - }) - .build(); +static VMSTATE_FOOB: VMStateDescription = VMStateDescriptionBuilder::::new() + .name(c"foo_b") + .version_id(2) + .minimum_version_id(1) + .fields(vmstate_fields! { + vmstate_of!(FooB, val).with_version_id(2), + vmstate_of!(FooB, wrap), + vmstate_of!(FooB, arr_a[0 .. num_a]).with_version_id(1), + vmstate_of!(FooB, arr_a_mul[0 .. num_a_mul * 32]).with_version_id(2), + vmstate_of!(FooB, arr_i64), + vmstate_of!(FooB, arr_a_wrap[0 .. num_a_wrap], validate_foob), + }) + .build(); #[test] fn test_vmstate_bool_v() { @@ -351,9 +352,7 @@ static VMSTATE_FOOC: VMStateDescription = VMStateDescriptionBuilder::) - vmstate_unused!(size_of::>()), + vmstate_of!(FooC, ptr_a), vmstate_of!(FooC, arr_ptr), vmstate_of!(FooC, arr_ptr_wrap), }) @@ -385,6 +384,31 @@ fn test_vmstate_pointer() { assert!(foo_fields[0].field_exists.is_none()); } +#[test] +fn test_vmstate_struct_pointer() { + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) }; + + // 2st VMStateField ("ptr_a") in VMSTATE_FOOC (corresponding to + // VMSTATE_STRUCT_POINTER) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), + b"ptr_a\0" + ); + assert_eq!(foo_fields[1].offset, PTR_SIZE); + assert_eq!(foo_fields[1].num_offset, 0); + assert_eq!(foo_fields[1].vmsd, VMSTATE_FOOA.as_ref()); + assert_eq!(foo_fields[1].version_id, 0); + assert_eq!(foo_fields[1].size, size_of::()); + assert_eq!(foo_fields[1].num, 0); + assert_eq!( + foo_fields[1].flags.0, + VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0 + ); + assert!(foo_fields[1].info.is_null()); + assert!(foo_fields[1].field_exists.is_none()); +} + #[test] fn test_vmstate_macro_array_of_pointer() { let foo_fields: &[VMStateField] = @@ -444,8 +468,7 @@ fn test_vmstate_macro_array_of_pointer_wrapped() { // * VMSTATE_FOOD: // - VMSTATE_VALIDATE -// Add more member fields when vmstate_of/vmstate_struct support "test" -// parameter. +// Add more member fields when vmstate_of support "test" parameter. struct FooD; impl FooD { From 2a8a7bb8aaf54c3c878a616884a2e4bb239b6095 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Sep 2025 12:49:41 +0200 Subject: [PATCH 0733/1794] rust: qdev: const_refs_to_static Now that const_refs_static can be assumed, convert the members of the DeviceImpl trait from functions to constants. This lets the compiler know that they have a 'static lifetime, and removes the need for the weird "Box::leak()". Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250908105005.2119297-10-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 4 +-- rust/hw/timer/hpet/src/device.rs | 9 ++----- rust/qemu-api-macros/src/lib.rs | 8 +++--- rust/qemu-api-macros/src/tests.rs | 44 ++++++++++++++----------------- rust/qemu-api/src/qdev.rs | 19 +++++-------- rust/qemu-api/tests/tests.rs | 4 +-- 6 files changed, 33 insertions(+), 55 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 87a17716fed4d..8411db8d00ca0 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -172,9 +172,7 @@ impl ObjectImpl for PL011State { } impl DeviceImpl for PL011State { - fn vmsd() -> Option> { - Some(VMSTATE_PL011) - } + const VMSTATE: Option> = Some(VMSTATE_PL011); const REALIZE: Option qemu_api::Result<()>> = Some(Self::realize); } diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index eb5bd042b1cbc..dd5326a40d487 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -1008,16 +1008,11 @@ const VMSTATE_HPET: VMStateDescription = // SAFETY: HPET_PROPERTIES is a valid Property array constructed with the // qemu_api::declare_properties macro. unsafe impl qemu_api::qdev::DevicePropertiesImpl for HPETState { - fn properties() -> &'static [Property] { - &HPET_PROPERTIES - } + const PROPERTIES: &'static [Property] = &HPET_PROPERTIES; } impl DeviceImpl for HPETState { - fn vmsd() -> Option> { - Some(VMSTATE_HPET) - } - + const VMSTATE: Option> = Some(VMSTATE_HPET); const REALIZE: Option qemu_api::Result<()>> = Some(Self::realize); } diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 97b2c214b62eb..a65a7ce2fe9a4 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -289,11 +289,9 @@ fn derive_device_or_error(input: DeriveInput) -> Result unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for #name { - fn properties() -> &'static [::qemu_api::bindings::Property] { - static PROPERTIES: &[::qemu_api::bindings::Property] = &[#(#properties_expanded),*]; - - PROPERTIES - } + const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[ + #(#properties_expanded),* + ]; } }) } diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs index aafffcdce912e..0e5a5728908a3 100644 --- a/rust/qemu-api-macros/src/tests.rs +++ b/rust/qemu-api-macros/src/tests.rs @@ -101,18 +101,16 @@ fn test_derive_device() { }, quote! { unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState { - fn properties() -> &'static [::qemu_api::bindings::Property] { - static PROPERTIES: &[::qemu_api::bindings::Property] = - &[::qemu_api::bindings::Property { - name: ::std::ffi::CStr::as_ptr(c"migrate_clock"), - info: ::VALUE, - offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, - set_default: true, - defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 }, - ..::qemu_api::zeroable::Zeroable::ZERO - }]; - PROPERTIES - } + const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[ + ::qemu_api::bindings::Property { + name: ::std::ffi::CStr::as_ptr(c"migrate_clock"), + info: ::VALUE, + offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, + set_default: true, + defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 }, + ..::qemu_api::zeroable::Zeroable::ZERO + } + ]; } } ); @@ -130,18 +128,16 @@ fn test_derive_device() { }, quote! { unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState { - fn properties() -> &'static [::qemu_api::bindings::Property] { - static PROPERTIES: &[::qemu_api::bindings::Property] = - &[::qemu_api::bindings::Property { - name: ::std::ffi::CStr::as_ptr(c"migrate-clk"), - info: ::VALUE, - offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, - set_default: true, - defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 }, - ..::qemu_api::zeroable::Zeroable::ZERO - }]; - PROPERTIES - } + const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[ + ::qemu_api::bindings::Property { + name: ::std::ffi::CStr::as_ptr(c"migrate-clk"), + info: ::VALUE, + offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, + set_default: true, + defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 }, + ..::qemu_api::zeroable::Zeroable::ZERO + } + ]; } } ); diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 4dda8c8113143..436142d8aefc1 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -151,11 +151,8 @@ unsafe impl QDevProp for crate::chardev::CharBackend { /// Caller is responsible for the validity of properties array. pub unsafe trait DevicePropertiesImpl { /// An array providing the properties that the user can set on the - /// device. Not a `const` because referencing statics in constants - /// is unstable until Rust 1.83.0. - fn properties() -> &'static [Property] { - &[] - } + /// device. + const PROPERTIES: &'static [Property] = &[]; } /// Trait providing the contents of [`DeviceClass`]. @@ -173,9 +170,7 @@ pub trait DeviceImpl: /// A `VMStateDescription` providing the migration format for the device /// Not a `const` because referencing statics in constants is unstable /// until Rust 1.83.0. - fn vmsd() -> Option> { - None - } + const VMSTATE: Option> = None; } /// # Safety @@ -224,12 +219,10 @@ impl DeviceClass { if ::REALIZE.is_some() { self.realize = Some(rust_realize_fn::); } - if let Some(vmsd) = ::vmsd() { - // Give a 'static lifetime to the return value of vmsd(). - // Temporary until vmsd() can be changed into a const. - self.vmsd = Box::leak(Box::new(vmsd.get())); + if let Some(ref vmsd) = ::VMSTATE { + self.vmsd = vmsd.as_ref(); } - let prop = ::properties(); + let prop = ::PROPERTIES; if !prop.is_empty() { unsafe { bindings::device_class_set_props_n(self, prop.as_ptr(), prop.len()); diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 4d4e4653f38e7..1349568741914 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -56,9 +56,7 @@ impl ObjectImpl for DummyState { impl ResettablePhasesImpl for DummyState {} impl DeviceImpl for DummyState { - fn vmsd() -> Option> { - Some(VMSTATE) - } + const VMSTATE: Option> = Some(VMSTATE); } #[repr(C)] From 945bf29aa82a2dfca3f69a19b7d048cddcc095bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:42 +0200 Subject: [PATCH 0734/1794] docs/rust: update msrv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-2-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 2 +- rust/Cargo.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 10b0590b56cbb..20d15347defb4 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -1,4 +1,4 @@ -.. |msrv| replace:: 1.63.0 +.. |msrv| replace:: 1.83.0 Rust in QEMU ============ diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 99c275f2d9f52..9b26aa07cec94 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -13,6 +13,7 @@ edition = "2021" homepage = "https://www.qemu.org" license = "GPL-2.0-or-later" repository = "https://gitlab.com/qemu-project/qemu/" +# don't forget to update docs/devel/rust.rst msrv rust-version = "1.83.0" [workspace.dependencies] From f665219b03dda114539c637c50c748a2ebd73c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:43 +0200 Subject: [PATCH 0735/1794] rust: remove unused global qemu "allocator" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The global allocator has always been disabled. There is no clear reason Rust and C should use the same allocator. Allocations made from Rust must be freed by Rust, and same for C, otherwise we head into troubles. Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-3-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- meson.build | 4 -- rust/Cargo.toml | 2 +- rust/qemu-api/Cargo.toml | 1 - rust/qemu-api/meson.build | 1 - rust/qemu-api/src/lib.rs | 135 -------------------------------------- 5 files changed, 1 insertion(+), 142 deletions(-) diff --git a/meson.build b/meson.build index a238df3f5e3c1..6ade30f36adb2 100644 --- a/meson.build +++ b/meson.build @@ -1086,9 +1086,6 @@ glib = declare_dependency(dependencies: [glib_pc, gmodule], # TODO: remove this check and the corresponding workaround (qtree) when # the minimum supported glib is >= 2.75.3 glib_has_gslice = glib.version().version_compare('<2.75.3') -# Check whether glib has the aligned_alloc family of functions. -# -glib_has_aligned_alloc = glib.version().version_compare('>=2.72.0') # override glib dep to include the above refinements meson.override_dependency('glib-2.0', glib) @@ -2702,7 +2699,6 @@ config_host_data.set('CONFIG_GETLOADAVG', cc.has_function('getloadavg')) config_host_data.set('HAVE_COPY_FILE_RANGE', cc.has_function('copy_file_range')) config_host_data.set('HAVE_GETIFADDRS', cc.has_function('getifaddrs')) config_host_data.set('HAVE_GLIB_WITH_SLICE_ALLOCATOR', glib_has_gslice) -config_host_data.set('HAVE_GLIB_WITH_ALIGNED_ALLOC', glib_has_aligned_alloc) config_host_data.set('HAVE_OPENPTY', cc.has_function('openpty', dependencies: util)) config_host_data.set('HAVE_STRCHRNUL', cc.has_function('strchrnul', prefix: osdep_prefix)) config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include ')) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 9b26aa07cec94..d98d2b77026d9 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -23,7 +23,7 @@ libc = "0.2.162" [workspace.lints.rust] unexpected_cfgs = { level = "deny", check-cfg = [ - 'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)', + 'cfg(MESON)', ] } # Occasionally, we may need to silence warnings and clippy lints that diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index c07a17a28b014..c5ed78035b6e3 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -21,7 +21,6 @@ libc = { workspace = true } [features] default = ["debug_cell"] -allocator = [] debug_cell = [] [lints] diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index a090297c458b1..062009f1618de 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -2,7 +2,6 @@ _qemu_api_cfg = run_command(rustc_args, '--config-headers', config_host_h, '--features', files('Cargo.toml'), capture: true, check: true).stdout().strip().splitlines() -# _qemu_api_cfg += ['--cfg', 'feature="allocator"'] if get_option('debug_mutex') _qemu_api_cfg += ['--cfg', 'feature="debug_cell"'] endif diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index bcb51c7986a2c..daa2493bb69d9 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -36,139 +36,4 @@ pub mod zeroable; // crate). extern crate self as qemu_api; -use std::{ - alloc::{GlobalAlloc, Layout}, - ffi::c_void, -}; - pub use error::{Error, Result}; - -#[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)] -extern "C" { - fn g_aligned_alloc0( - n_blocks: bindings::gsize, - n_block_bytes: bindings::gsize, - alignment: bindings::gsize, - ) -> bindings::gpointer; - fn g_aligned_free(mem: bindings::gpointer); -} - -#[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))] -extern "C" { - fn qemu_memalign(alignment: usize, size: usize) -> *mut c_void; - fn qemu_vfree(ptr: *mut c_void); -} - -extern "C" { - fn g_malloc0(n_bytes: bindings::gsize) -> bindings::gpointer; - fn g_free(mem: bindings::gpointer); -} - -/// An allocator that uses the same allocator as QEMU in C. -/// -/// It is enabled by default with the `allocator` feature. -/// -/// To set it up manually as a global allocator in your crate: -/// -/// ```ignore -/// use qemu_api::QemuAllocator; -/// -/// #[global_allocator] -/// static GLOBAL: QemuAllocator = QemuAllocator::new(); -/// ``` -#[derive(Clone, Copy, Debug)] -#[repr(C)] -pub struct QemuAllocator { - _unused: [u8; 0], -} - -#[cfg_attr(all(feature = "allocator", not(test)), global_allocator)] -pub static GLOBAL: QemuAllocator = QemuAllocator::new(); - -impl QemuAllocator { - // From the glibc documentation, on GNU systems, malloc guarantees 16-byte - // alignment on 64-bit systems and 8-byte alignment on 32-bit systems. See - // https://www.gnu.org/software/libc/manual/html_node/Malloc-Examples.html. - // This alignment guarantee also applies to Windows and Android. On Darwin - // and OpenBSD, the alignment is 16 bytes on both 64-bit and 32-bit systems. - #[cfg(all( - target_pointer_width = "32", - not(any(target_os = "macos", target_os = "openbsd")) - ))] - pub const DEFAULT_ALIGNMENT_BYTES: Option = Some(8); - #[cfg(all( - target_pointer_width = "64", - not(any(target_os = "macos", target_os = "openbsd")) - ))] - pub const DEFAULT_ALIGNMENT_BYTES: Option = Some(16); - #[cfg(all( - any(target_pointer_width = "32", target_pointer_width = "64"), - any(target_os = "macos", target_os = "openbsd") - ))] - pub const DEFAULT_ALIGNMENT_BYTES: Option = Some(16); - #[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] - pub const DEFAULT_ALIGNMENT_BYTES: Option = None; - - pub const fn new() -> Self { - Self { _unused: [] } - } -} - -impl Default for QemuAllocator { - fn default() -> Self { - Self::new() - } -} - -// Sanity check. -const _: [(); 8] = [(); ::core::mem::size_of::<*mut c_void>()]; - -unsafe impl GlobalAlloc for QemuAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - if matches!(Self::DEFAULT_ALIGNMENT_BYTES, Some(default) if default.checked_rem(layout.align()) == Some(0)) - { - // SAFETY: g_malloc0() is safe to call. - unsafe { g_malloc0(layout.size().try_into().unwrap()).cast::() } - } else { - #[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)] - { - // SAFETY: g_aligned_alloc0() is safe to call. - unsafe { - g_aligned_alloc0( - layout.size().try_into().unwrap(), - 1, - layout.align().try_into().unwrap(), - ) - .cast::() - } - } - #[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))] - { - // SAFETY: qemu_memalign() is safe to call. - unsafe { qemu_memalign(layout.align(), layout.size()).cast::() } - } - } - } - - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - if matches!(Self::DEFAULT_ALIGNMENT_BYTES, Some(default) if default.checked_rem(layout.align()) == Some(0)) - { - // SAFETY: `ptr` must have been allocated by Self::alloc thus a valid - // glib-allocated pointer, so `g_free`ing is safe. - unsafe { g_free(ptr.cast::<_>()) } - } else { - #[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)] - { - // SAFETY: `ptr` must have been allocated by Self::alloc thus a valid aligned - // glib-allocated pointer, so `g_aligned_free`ing is safe. - unsafe { g_aligned_free(ptr.cast::<_>()) } - } - #[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))] - { - // SAFETY: `ptr` must have been allocated by Self::alloc thus a valid aligned - // glib-allocated pointer, so `qemu_vfree`ing is safe. - unsafe { qemu_vfree(ptr.cast::<_>()) } - } - } - } -} From d85df9a8e2436d1243a28f580e1bf155519dfdfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:44 +0200 Subject: [PATCH 0736/1794] rust: add workspace authors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-4-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index d98d2b77026d9..cd4bf8ef8e1bb 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -15,6 +15,7 @@ license = "GPL-2.0-or-later" repository = "https://gitlab.com/qemu-project/qemu/" # don't forget to update docs/devel/rust.rst msrv rust-version = "1.83.0" +authors = ["The QEMU Project Developers "] [workspace.dependencies] anyhow = "~1.0" From 88cf78e9172850c786d24d298adfadd09ec43a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:45 +0200 Subject: [PATCH 0737/1794] rust: move vmstate_clock!() to qdev module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will allow to split vmstate to a standalone crate next. Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-8-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/qdev.rs | 3 +++ rust/qemu-api/src/vmstate.rs | 14 ++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 436142d8aefc1..e2b4121cac5ac 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -17,6 +17,7 @@ use crate::{ cell::{bql_locked, Opaque}, chardev::Chardev, error::{Error, Result}, + impl_vmstate_c_struct, irq::InterruptSource, prelude::*, qom::{ObjectClass, ObjectImpl, Owned, ParentInit}, @@ -455,3 +456,5 @@ unsafe impl ObjectType for Clock { unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) }; } qom_isa!(Clock: Object); + +impl_vmstate_c_struct!(Clock, bindings::vmstate_clock); diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index b5c6b764fbba8..59c7950a5aa7a 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -41,7 +41,6 @@ use crate::{ callbacks::FnCall, errno::{into_neg_errno, Errno}, prelude::*, - qdev, qom::Owned, zeroable::Zeroable, }; @@ -318,21 +317,20 @@ impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32); impl_vmstate_scalar!(vmstate_info_uint64, u64); impl_vmstate_scalar!(vmstate_info_timer, crate::timer::Timer); +#[macro_export] macro_rules! impl_vmstate_c_struct { ($type:ty, $vmsd:expr) => { unsafe impl VMState for $type { - const BASE: VMStateField = $crate::bindings::VMStateField { - vmsd: addr_of!($vmsd), - size: mem::size_of::<$type>(), - flags: VMStateFlags::VMS_STRUCT, - ..Zeroable::ZERO + const BASE: $crate::bindings::VMStateField = $crate::bindings::VMStateField { + vmsd: ::std::ptr::addr_of!($vmsd), + size: ::std::mem::size_of::<$type>(), + flags: $crate::bindings::VMStateFlags::VMS_STRUCT, + ..$crate::zeroable::Zeroable::ZERO }; } }; } -impl_vmstate_c_struct!(qdev::Clock, bindings::vmstate_clock); - // Pointer types using the underlying type's VMState plus VMS_POINTER // Note that references are not supported, though references to cells // could be allowed. From 0611dd418abbf5d7c011bd465779cb955cea0f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:46 +0200 Subject: [PATCH 0738/1794] rust: move VMState handling to QOM module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will allow to split vmstate to a standalone crate next. Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-9-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/qom.rs | 3 +++ rust/qemu-api/src/vmstate.rs | 11 +++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 014ffb1fd88fe..12c6fc6752cad 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -109,6 +109,7 @@ use crate::{ object_get_typename, object_new, object_ref, object_unref, TypeInfo, }, cell::{bql_locked, Opaque}, + impl_vmstate_pointer, }; /// A safe wrapper around [`bindings::Object`]. @@ -948,3 +949,5 @@ where impl ObjectClassMethods for T where T: IsA {} impl ObjectMethods for R where R::Target: IsA {} + +impl_vmstate_pointer!(Owned where T: VMState + ObjectType); diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 59c7950a5aa7a..4e1086779ac5a 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -40,8 +40,6 @@ use crate::{ bindings::{self, VMStateFlags}, callbacks::FnCall, errno::{into_neg_errno, Errno}, - prelude::*, - qom::Owned, zeroable::Zeroable, }; @@ -128,7 +126,8 @@ pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags /// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`, /// [`BqlCell`], [`BqlRefCell`] /// * a raw pointer to any of the above -/// * a `NonNull` pointer, a `Box` or an [`Owned`] for any of the above +/// * a `NonNull` pointer, a `Box` or an [`Owned`](crate::qom::Owned) for any of +/// the above /// * an array of any of the above /// /// In order to support other types, the trait `VMState` must be implemented @@ -335,10 +334,11 @@ macro_rules! impl_vmstate_c_struct { // Note that references are not supported, though references to cells // could be allowed. +#[macro_export] macro_rules! impl_vmstate_pointer { ($type:ty where $base:tt: VMState $($where:tt)*) => { - unsafe impl<$base> VMState for $type where $base: VMState $($where)* { - const BASE: VMStateField = <$base as VMState>::BASE.with_pointer_flag(); + unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* { + const BASE: $crate::vmstate::VMStateField = <$base as $crate::vmstate::VMState>::BASE.with_pointer_flag(); } }; } @@ -350,7 +350,6 @@ impl_vmstate_pointer!(NonNull where T: VMState); // Unlike C pointers, Box is always non-null therefore there is no need // to specify VMS_ALLOC. impl_vmstate_pointer!(Box where T: VMState); -impl_vmstate_pointer!(Owned where T: VMState + ObjectType); // Arrays using the underlying type's VMState plus // VMS_ARRAY/VMS_ARRAY_OF_POINTER From a6765c04beac0a3e20032b619df6afbc70b6ef74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:47 +0200 Subject: [PATCH 0739/1794] rust: move Cell vmstate impl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will allow to split vmstate to a standalone crate next. Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-10-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/cell.rs | 6 +++++- rust/qemu-api/src/vmstate.rs | 14 +++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index 148a13e3f5661..9943d7286b6df 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -221,7 +221,7 @@ use std::{ ptr::NonNull, }; -use crate::bindings; +use crate::{bindings, impl_vmstate_transparent}; /// An internal function that is used by doctests. pub fn bql_start_test() { @@ -456,6 +456,8 @@ impl BqlCell { } } +impl_vmstate_transparent!(crate::cell::BqlCell where T: VMState); + /// A mutable memory location with dynamically checked borrow rules, /// protected by the Big QEMU Lock. /// @@ -764,6 +766,8 @@ impl From for BqlRefCell { } } +impl_vmstate_transparent!(crate::cell::BqlRefCell where T: VMState); + struct BorrowRef<'b> { borrow: &'b Cell, } diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 4e1086779ac5a..ce42b031bc150 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -124,7 +124,8 @@ pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags /// * scalar types (integer and `bool`) /// * the C struct `QEMUTimer` /// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`, -/// [`BqlCell`], [`BqlRefCell`] +/// [`BqlCell`](crate::cell::BqlCell), +/// [`BqlRefCell`](crate::cell::BqlRefCell)), /// * a raw pointer to any of the above /// * a `NonNull` pointer, a `Box` or an [`Owned`](crate::qom::Owned) for any of /// the above @@ -254,14 +255,15 @@ macro_rules! impl_vmstate_forward { // Transparent wrappers: just use the internal type +#[macro_export] macro_rules! impl_vmstate_transparent { ($type:ty where $base:tt: VMState $($where:tt)*) => { - unsafe impl<$base> VMState for $type where $base: VMState $($where)* { - const BASE: VMStateField = VMStateField { + unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* { + const BASE: $crate::vmstate::VMStateField = $crate::vmstate::VMStateField { size: mem::size_of::<$type>(), - ..<$base as VMState>::BASE + ..<$base as $crate::vmstate::VMState>::BASE }; - const VARRAY_FLAG: VMStateFlags = <$base as VMState>::VARRAY_FLAG; + const VARRAY_FLAG: $crate::bindings::VMStateFlags = <$base as $crate::vmstate::VMState>::VARRAY_FLAG; } }; } @@ -269,8 +271,6 @@ macro_rules! impl_vmstate_transparent { impl_vmstate_transparent!(std::cell::Cell where T: VMState); impl_vmstate_transparent!(std::cell::UnsafeCell where T: VMState); impl_vmstate_transparent!(std::pin::Pin where T: VMState); -impl_vmstate_transparent!(crate::cell::BqlCell where T: VMState); -impl_vmstate_transparent!(crate::cell::BqlRefCell where T: VMState); impl_vmstate_transparent!(crate::cell::Opaque where T: VMState); #[macro_export] From 593c408a6a8cd8b0af9bf60c7c3625da7910a737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:48 +0200 Subject: [PATCH 0740/1794] rust: split Rust-only "common" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-6-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 10 + rust/Cargo.toml | 1 + rust/common/Cargo.toml | 19 ++ rust/common/meson.build | 34 +++ rust/{qemu-api => common}/src/assertions.rs | 16 +- rust/{qemu-api => common}/src/bitops.rs | 1 - rust/{qemu-api => common}/src/callbacks.rs | 10 +- rust/{qemu-api => common}/src/errno.rs | 8 +- rust/common/src/lib.rs | 20 ++ rust/common/src/opaque.rs | 238 ++++++++++++++++++++ rust/{qemu-api => common}/src/uninit.rs | 2 +- rust/common/src/zeroable.rs | 18 ++ rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 3 +- rust/hw/char/pl011/src/device.rs | 3 +- rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 3 +- rust/hw/timer/hpet/src/device.rs | 2 +- rust/hw/timer/hpet/src/fw_cfg.rs | 3 +- rust/meson.build | 1 + rust/qemu-api-macros/src/lib.rs | 16 +- rust/qemu-api-macros/src/tests.rs | 6 +- rust/qemu-api/Cargo.toml | 1 + rust/qemu-api/meson.build | 14 +- rust/qemu-api/src/bindings.rs | 21 ++ rust/qemu-api/src/cell.rs | 235 +------------------ rust/qemu-api/src/chardev.rs | 5 +- rust/qemu-api/src/error.rs | 3 +- rust/qemu-api/src/irq.rs | 3 +- rust/qemu-api/src/lib.rs | 6 - rust/qemu-api/src/log.rs | 4 +- rust/qemu-api/src/memory.rs | 5 +- rust/qemu-api/src/prelude.rs | 6 +- rust/qemu-api/src/qdev.rs | 10 +- rust/qemu-api/src/qom.rs | 3 +- rust/qemu-api/src/sysbus.rs | 3 +- rust/qemu-api/src/timer.rs | 8 +- rust/qemu-api/src/vmstate.rs | 23 +- rust/qemu-api/src/zeroable.rs | 37 --- rust/qemu-api/tests/vmstate_tests.rs | 3 +- 41 files changed, 448 insertions(+), 359 deletions(-) create mode 100644 rust/common/Cargo.toml create mode 100644 rust/common/meson.build rename rust/{qemu-api => common}/src/assertions.rs (92%) rename rust/{qemu-api => common}/src/bitops.rs (98%) rename rust/{qemu-api => common}/src/callbacks.rs (97%) rename rust/{qemu-api => common}/src/errno.rs (98%) create mode 100644 rust/common/src/lib.rs create mode 100644 rust/common/src/opaque.rs rename rust/{qemu-api => common}/src/uninit.rs (98%) create mode 100644 rust/common/src/zeroable.rs delete mode 100644 rust/qemu-api/src/zeroable.rs diff --git a/MAINTAINERS b/MAINTAINERS index fb045388b92c2..cee5a3420628c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3515,6 +3515,7 @@ F: include/hw/registerfields.h Rust M: Manos Pitsidianakis S: Maintained +F: rust/common/ F: rust/qemu-api F: rust/qemu-api-macros F: rust/rustfmt.toml diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 4baf6ba663cd5..71e8c7ed62ad5 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -44,6 +44,13 @@ dependencies = [ "qemu_api_macros", ] +[[package]] +name = "common" +version = "0.1.0" +dependencies = [ + "libc", +] + [[package]] name = "either" version = "1.12.0" @@ -63,6 +70,7 @@ dependencies = [ name = "hpet" version = "0.1.0" dependencies = [ + "common", "qemu_api", "qemu_api_macros", ] @@ -89,6 +97,7 @@ dependencies = [ "bilge", "bilge-impl", "bits", + "common", "qemu_api", "qemu_api_macros", ] @@ -130,6 +139,7 @@ name = "qemu_api" version = "0.1.0" dependencies = [ "anyhow", + "common", "foreign", "libc", "qemu_api_macros", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index cd4bf8ef8e1bb..c0426d42430d1 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -2,6 +2,7 @@ resolver = "2" members = [ "bits", + "common", "qemu-api-macros", "qemu-api", "hw/char/pl011", diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml new file mode 100644 index 0000000000000..5e106427e80a2 --- /dev/null +++ b/rust/common/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "common" +version = "0.1.0" +description = "Rust common code for QEMU" +resolver = "2" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +libc.workspace = true + +[lints] +workspace = true diff --git a/rust/common/meson.build b/rust/common/meson.build new file mode 100644 index 0000000000000..230a967760dce --- /dev/null +++ b/rust/common/meson.build @@ -0,0 +1,34 @@ +_common_cfg = run_command(rustc_args, + '--config-headers', config_host_h, '--features', files('Cargo.toml'), + capture: true, check: true).stdout().strip().splitlines() + +_common_rs = static_library( + 'common', + structured_sources( + [ + 'src/lib.rs', + 'src/assertions.rs', + 'src/bitops.rs', + 'src/callbacks.rs', + 'src/errno.rs', + 'src/opaque.rs', + 'src/uninit.rs', + 'src/zeroable.rs', + ], + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + rust_args: _common_cfg, + dependencies: [libc_rs], +) + +common_rs = declare_dependency(link_with: [_common_rs]) + +# Doctests are essentially integration tests, so they need the same dependencies. +# Note that running them requires the object files for C code, so place them +# in a separate suite that is run by the "build" CI jobs rather than "check". +rust.doctest('rust-common-doctests', + _common_rs, + protocol: 'rust', + dependencies: common_rs, + suite: ['doc', 'rust']) diff --git a/rust/qemu-api/src/assertions.rs b/rust/common/src/assertions.rs similarity index 92% rename from rust/qemu-api/src/assertions.rs rename to rust/common/src/assertions.rs index e4fe23b674e26..91f83a5d3d43d 100644 --- a/rust/qemu-api/src/assertions.rs +++ b/rust/common/src/assertions.rs @@ -8,7 +8,7 @@ //! types match the expectations of C code. //! //! Documentation is hidden because it only exposes macros, which -//! are exported directly from `qemu_api`. +//! are exported directly from `common`. // Based on https://stackoverflow.com/questions/64251852/x/70978292#70978292 // (stackoverflow answers are released under MIT license). @@ -27,7 +27,7 @@ impl EqType for T { /// # Examples /// /// ``` -/// # use qemu_api::assert_same_type; +/// # use common::assert_same_type; /// # use std::ops::Deref; /// assert_same_type!(u32, u32); /// assert_same_type!( as Deref>::Target, u32); @@ -36,7 +36,7 @@ impl EqType for T { /// Different types will cause a compile failure /// /// ```compile_fail -/// # use qemu_api::assert_same_type; +/// # use common::assert_same_type; /// assert_same_type!(&Box, &u32); /// ``` #[macro_export] @@ -61,7 +61,7 @@ macro_rules! assert_same_type { /// # Examples /// /// ``` -/// # use qemu_api::assert_field_type; +/// # use common::assert_field_type; /// pub struct A { /// field1: u32, /// } @@ -72,7 +72,7 @@ macro_rules! assert_same_type { /// Different types will cause a compile failure /// /// ```compile_fail -/// # use qemu_api::assert_field_type; +/// # use common::assert_field_type; /// # pub struct A { field1: u32 } /// assert_field_type!(A, field1, i32); /// ``` @@ -103,7 +103,7 @@ macro_rules! assert_field_type { /// # Examples /// /// ``` -/// # use qemu_api::assert_match; +/// # use common::assert_match; /// // JoinHandle does not implement `Eq`, therefore the result /// // does not either. /// let result: Result, u32> = Err(42); @@ -132,12 +132,12 @@ macro_rules! assert_match { /// # Examples /// /// ``` -/// # use qemu_api::static_assert; +/// # use common::static_assert; /// static_assert!("abc".len() == 3); /// ``` /// /// ```compile_fail -/// # use qemu_api::static_assert; +/// # use common::static_assert; /// static_assert!("abc".len() == 2); // does not compile /// ``` #[macro_export] diff --git a/rust/qemu-api/src/bitops.rs b/rust/common/src/bitops.rs similarity index 98% rename from rust/qemu-api/src/bitops.rs rename to rust/common/src/bitops.rs index b1e3a530ab542..06c78c3b8a7f4 100644 --- a/rust/qemu-api/src/bitops.rs +++ b/rust/common/src/bitops.rs @@ -3,7 +3,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later //! This module provides bit operation extensions to integer types. -//! It is usually included via the `qemu_api` prelude. use std::ops::{ Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, diff --git a/rust/qemu-api/src/callbacks.rs b/rust/common/src/callbacks.rs similarity index 97% rename from rust/qemu-api/src/callbacks.rs rename to rust/common/src/callbacks.rs index dbe2305f50939..b8898fe96f7c7 100644 --- a/rust/qemu-api/src/callbacks.rs +++ b/rust/common/src/callbacks.rs @@ -55,7 +55,7 @@ use std::{mem, ptr::NonNull}; /// # Examples /// /// ``` -/// # use qemu_api::callbacks::FnCall; +/// # use common::callbacks::FnCall; /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { /// F::call((s,)) /// } @@ -71,7 +71,7 @@ use std::{mem, ptr::NonNull}; /// Attempting to pass a non-zero-sized closure causes a compile-time failure: /// /// ```compile_fail -/// # use qemu_api::callbacks::FnCall; +/// # use common::callbacks::FnCall; /// # fn call_it<'a, F: FnCall<(&'a str,), String>>(_f: &F, s: &'a str) -> String { /// # F::call((s,)) /// # } @@ -82,7 +82,7 @@ use std::{mem, ptr::NonNull}; /// `()` can be used to indicate "no function": /// /// ``` -/// # use qemu_api::callbacks::FnCall; +/// # use common::callbacks::FnCall; /// fn optional FnCall<(&'a str,), String>>(_f: &F, s: &str) -> Option { /// if F::IS_SOME { /// Some(F::call((s,))) @@ -97,7 +97,7 @@ use std::{mem, ptr::NonNull}; /// Invoking `F::call` will then be a run-time error. /// /// ```should_panic -/// # use qemu_api::callbacks::FnCall; +/// # use common::callbacks::FnCall; /// # fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { /// # F::call((s,)) /// # } @@ -120,7 +120,7 @@ pub unsafe trait FnCall: 'static + Sync + Sized { /// You can use `IS_SOME` to catch this at compile time: /// /// ```compile_fail - /// # use qemu_api::callbacks::FnCall; + /// # use common::callbacks::FnCall; /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { /// const { assert!(F::IS_SOME) } /// F::call((s,)) diff --git a/rust/qemu-api/src/errno.rs b/rust/common/src/errno.rs similarity index 98% rename from rust/qemu-api/src/errno.rs rename to rust/common/src/errno.rs index 507850fe33cb6..64b2933b07877 100644 --- a/rust/qemu-api/src/errno.rs +++ b/rust/common/src/errno.rs @@ -185,7 +185,7 @@ use traits::{GetErrno, MergeErrno}; /// are interpreted as negated `errno` and turned into an `Err`. /// /// ``` -/// # use qemu_api::errno::into_io_result; +/// # use common::errno::into_io_result; /// # use std::io::ErrorKind; /// let ok = into_io_result(1i32).unwrap(); /// assert_eq!(ok, 1u32); @@ -201,7 +201,7 @@ use traits::{GetErrno, MergeErrno}; /// likely overflows and will panic: /// /// ```should_panic -/// # use qemu_api::errno::into_io_result; +/// # use common::errno::into_io_result; /// # #[allow(dead_code)] /// let err = into_io_result(-0x1234_5678i32); // panic /// ``` @@ -213,7 +213,7 @@ pub fn into_io_result(value: T) -> io::Result { /// values to report errors. /// /// ``` -/// # use qemu_api::errno::into_neg_errno; +/// # use common::errno::into_neg_errno; /// # use std::io::{self, ErrorKind}; /// let ok: io::Result<()> = Ok(()); /// assert_eq!(into_neg_errno(ok), 0); @@ -232,7 +232,7 @@ pub fn into_io_result(value: T) -> io::Result { /// positive: /// /// ```should_panic -/// # use qemu_api::errno::into_neg_errno; +/// # use common::errno::into_neg_errno; /// # use std::io; /// let err: io::Result = Ok(0x8899_AABB); /// into_neg_errno(err) // panic diff --git a/rust/common/src/lib.rs b/rust/common/src/lib.rs new file mode 100644 index 0000000000000..25216503aab28 --- /dev/null +++ b/rust/common/src/lib.rs @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pub mod assertions; + +pub mod bitops; + +pub mod callbacks; +pub use callbacks::FnCall; + +pub mod errno; +pub use errno::Errno; + +pub mod opaque; +pub use opaque::{Opaque, Wrapper}; + +pub mod uninit; +pub use uninit::MaybeUninitField; + +pub mod zeroable; +pub use zeroable::Zeroable; diff --git a/rust/common/src/opaque.rs b/rust/common/src/opaque.rs new file mode 100644 index 0000000000000..d25a5f3ae1a11 --- /dev/null +++ b/rust/common/src/opaque.rs @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: MIT + +//! ## Opaque wrappers +//! +//! The cell types from the previous section are useful at the boundaries +//! of code that requires interior mutability. When writing glue code that +//! interacts directly with C structs, however, it is useful to operate +//! at a lower level. +//! +//! C functions often violate Rust's fundamental assumptions about memory +//! safety by modifying memory even if it is shared. Furthermore, C structs +//! often start their life uninitialized and may be populated lazily. +//! +//! For this reason, this module provides the [`Opaque`] type to opt out +//! of Rust's usual guarantees about the wrapped type. Access to the wrapped +//! value is always through raw pointers, obtained via methods like +//! [`as_mut_ptr()`](Opaque::as_mut_ptr) and [`as_ptr()`](Opaque::as_ptr). These +//! pointers can then be passed to C functions or dereferenced; both actions +//! require `unsafe` blocks, making it clear where safety guarantees must be +//! manually verified. For example +//! +//! ```ignore +//! unsafe { +//! let state = Opaque::::uninit(); +//! qemu_struct_init(state.as_mut_ptr()); +//! } +//! ``` +//! +//! [`Opaque`] will usually be wrapped one level further, so that +//! bridge methods can be added to the wrapper: +//! +//! ```ignore +//! pub struct MyStruct(Opaque); +//! +//! impl MyStruct { +//! fn new() -> Pin> { +//! let result = Box::pin(unsafe { Opaque::uninit() }); +//! unsafe { qemu_struct_init(result.as_mut_ptr()) }; +//! result +//! } +//! } +//! ``` +//! +//! This pattern of wrapping bindgen-generated types in [`Opaque`] provides +//! several advantages: +//! +//! * The choice of traits to be implemented is not limited by the +//! bindgen-generated code. For example, [`Drop`] can be added without +//! disabling [`Copy`] on the underlying bindgen type +//! +//! * [`Send`] and [`Sync`] implementations can be controlled by the wrapper +//! type rather than being automatically derived from the C struct's layout +//! +//! * Methods can be implemented in a separate crate from the bindgen-generated +//! bindings +//! +//! * [`Debug`](std::fmt::Debug) and [`Display`](std::fmt::Display) +//! implementations can be customized to be more readable than the raw C +//! struct representation +//! +//! The [`Opaque`] type does not include BQL validation; it is possible to +//! assert in the code that the right lock is taken, to use it together +//! with a custom lock guard type, or to let C code take the lock, as +//! appropriate. It is also possible to use it with non-thread-safe +//! types, since by default (unlike [`BqlCell`] and [`BqlRefCell`] +//! it is neither `Sync` nor `Send`. +//! +//! While [`Opaque`] is necessary for C interop, it should be used sparingly +//! and only at FFI boundaries. For QEMU-specific types that need interior +//! mutability, prefer [`BqlCell`] or [`BqlRefCell`]. +//! +//! [`BqlCell`]: ../../qemu_api/cell/struct.BqlCell.html +//! [`BqlRefCell`]: ../../qemu_api/cell/struct.BqlRefCell.html +use std::{cell::UnsafeCell, fmt, marker::PhantomPinned, mem::MaybeUninit, ptr::NonNull}; + +/// Stores an opaque value that is shared with C code. +/// +/// Often, C structs can changed when calling a C function even if they are +/// behind a shared Rust reference, or they can be initialized lazily and have +/// invalid bit patterns (e.g. `3` for a [`bool`]). This goes against Rust's +/// strict aliasing rules, which normally prevent mutation through shared +/// references. +/// +/// Wrapping the struct with `Opaque` ensures that the Rust compiler does not +/// assume the usual constraints that Rust structs require, and allows using +/// shared references on the Rust side. +/// +/// `Opaque` is `#[repr(transparent)]`, so that it matches the memory layout +/// of `T`. +#[repr(transparent)] +pub struct Opaque { + value: UnsafeCell>, + // PhantomPinned also allows multiple references to the `Opaque`, i.e. + // one `&mut Opaque` can coexist with a `&mut T` or any number of `&T`; + // see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/. + _pin: PhantomPinned, +} + +impl Opaque { + /// Creates a new shared reference from a C pointer + /// + /// # Safety + /// + /// The pointer must be valid, though it need not point to a valid value. + pub unsafe fn from_raw<'a>(ptr: *mut T) -> &'a Self { + let ptr = NonNull::new(ptr).unwrap().cast::(); + // SAFETY: Self is a transparent wrapper over T + unsafe { ptr.as_ref() } + } + + /// Creates a new opaque object with uninitialized contents. + /// + /// # Safety + /// + /// Ultimately the pointer to the returned value will be dereferenced + /// in another `unsafe` block, for example when passing it to a C function, + /// but the functions containing the dereference are usually safe. The + /// value returned from `uninit()` must be initialized and pinned before + /// calling them. + pub const unsafe fn uninit() -> Self { + Self { + value: UnsafeCell::new(MaybeUninit::uninit()), + _pin: PhantomPinned, + } + } + + /// Creates a new opaque object with zeroed contents. + /// + /// # Safety + /// + /// Ultimately the pointer to the returned value will be dereferenced + /// in another `unsafe` block, for example when passing it to a C function, + /// but the functions containing the dereference are usually safe. The + /// value returned from `uninit()` must be pinned (and possibly initialized) + /// before calling them. + pub const unsafe fn zeroed() -> Self { + Self { + value: UnsafeCell::new(MaybeUninit::zeroed()), + _pin: PhantomPinned, + } + } + + /// Returns a raw mutable pointer to the opaque data. + pub const fn as_mut_ptr(&self) -> *mut T { + UnsafeCell::get(&self.value).cast() + } + + /// Returns a raw pointer to the opaque data. + pub const fn as_ptr(&self) -> *const T { + self.as_mut_ptr().cast_const() + } + + /// Returns a raw pointer to the opaque data that can be passed to a + /// C function as `void *`. + pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void { + UnsafeCell::get(&self.value).cast() + } + + /// Converts a raw pointer to the wrapped type. + pub const fn raw_get(slot: *mut Self) -> *mut T { + // Compare with Linux's raw_get method, which goes through an UnsafeCell + // because it takes a *const Self instead. + slot.cast() + } +} + +impl fmt::Debug for Opaque { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut name: String = "Opaque<".to_string(); + name += std::any::type_name::(); + name += ">"; + f.debug_tuple(&name).field(&self.as_ptr()).finish() + } +} + +impl Opaque { + /// Creates a new opaque object with default contents. + /// + /// # Safety + /// + /// Ultimately the pointer to the returned value will be dereferenced + /// in another `unsafe` block, for example when passing it to a C function, + /// but the functions containing the dereference are usually safe. The + /// value returned from `uninit()` must be pinned before calling them. + pub unsafe fn new() -> Self { + Self { + value: UnsafeCell::new(MaybeUninit::new(T::default())), + _pin: PhantomPinned, + } + } +} + +/// Annotates [`Self`] as a transparent wrapper for another type. +/// +/// Usually defined via the [`qemu_api_macros::Wrapper`] derive macro. +/// +/// # Examples +/// +/// ``` +/// # use std::mem::ManuallyDrop; +/// # use common::opaque::Wrapper; +/// #[repr(transparent)] +/// pub struct Example { +/// inner: ManuallyDrop, +/// } +/// +/// unsafe impl Wrapper for Example { +/// type Wrapped = String; +/// } +/// ``` +/// +/// # Safety +/// +/// `Self` must be a `#[repr(transparent)]` wrapper for the `Wrapped` type, +/// whether directly or indirectly. +/// +/// # Methods +/// +/// By convention, types that implement Wrapper also implement the following +/// methods: +/// +/// ```ignore +/// pub const unsafe fn from_raw<'a>(value: *mut Self::Wrapped) -> &'a Self; +/// pub const unsafe fn as_mut_ptr(&self) -> *mut Self::Wrapped; +/// pub const unsafe fn as_ptr(&self) -> *const Self::Wrapped; +/// pub const unsafe fn raw_get(slot: *mut Self) -> *const Self::Wrapped; +/// ``` +/// +/// They are not defined here to allow them to be `const`. +/// +/// [`qemu_api_macros::Wrapper`]: ../../qemu_api_macros/derive.Wrapper.html +pub unsafe trait Wrapper { + type Wrapped; +} + +unsafe impl Wrapper for Opaque { + type Wrapped = T; +} diff --git a/rust/qemu-api/src/uninit.rs b/rust/common/src/uninit.rs similarity index 98% rename from rust/qemu-api/src/uninit.rs rename to rust/common/src/uninit.rs index b0a829729dd74..e7f9fcd2e3fb3 100644 --- a/rust/qemu-api/src/uninit.rs +++ b/rust/common/src/uninit.rs @@ -63,7 +63,7 @@ impl<'a, T, U> DerefMut for MaybeUninitField<'a, T, U> { /// } /// /// # use std::mem::MaybeUninit; -/// # use qemu_api::{assert_match, uninit_field_mut}; +/// # use common::{assert_match, uninit_field_mut}; /// /// let mut s: MaybeUninit = MaybeUninit::zeroed(); /// uninit_field_mut!(s, x).write(5); diff --git a/rust/common/src/zeroable.rs b/rust/common/src/zeroable.rs new file mode 100644 index 0000000000000..fd056deb1f641 --- /dev/null +++ b/rust/common/src/zeroable.rs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Defines a trait for structs that can be safely initialized with zero bytes. + +/// Encapsulates the requirement that +/// `MaybeUninit::::zeroed().assume_init()` does not cause undefined +/// behavior. +/// +/// # Safety +/// +/// Do not add this trait to a type unless all-zeroes is a valid value for the +/// type. In particular, raw pointers can be zero, but references and +/// `NonNull` cannot. +pub unsafe trait Zeroable: Default { + /// Return a value of Self whose memory representation consists of all + /// zeroes, with the possible exclusion of padding bytes. + const ZERO: Self = unsafe { ::core::mem::MaybeUninit::::zeroed().assume_init() }; +} diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 88ef110507de7..6d15f107dfae2 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -16,6 +16,7 @@ rust-version.workspace = true bilge = { version = "0.2.0" } bilge-impl = { version = "0.2.0" } bits = { path = "../../../bits" } +common = { path = "../../../common" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index 2a1be329abc16..c4a9f531f7d1f 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -7,7 +7,8 @@ _libpl011_rs = static_library( bilge_rs, bilge_impl_rs, bits_rs, - qemu_api, + common_rs, + qemu_api_rs, qemu_api_macros, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 8411db8d00ca0..b4aa6c45f8743 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -4,6 +4,7 @@ use std::{ffi::CStr, mem::size_of}; +use common::{static_assert, uninit_field_mut}; use qemu_api::{ chardev::{CharBackend, Chardev, Event}, impl_vmstate_forward, impl_vmstate_struct, @@ -14,9 +15,7 @@ use qemu_api::{ prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, qom::{ObjectImpl, Owned, ParentField, ParentInit}, - static_assert, sysbus::{SysBusDevice, SysBusDeviceImpl}, - uninit_field_mut, vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused, }; diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index ac5df23c1d00b..ba7354f07e06b 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -11,6 +11,7 @@ repository.workspace = true rust-version.workspace = true [dependencies] +common = { path = "../../../common" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index c2d7c0532ca4e..c91d330439aeb 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -4,7 +4,8 @@ _libhpet_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', dependencies: [ - qemu_api, + common_rs, + qemu_api_rs, qemu_api_macros, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index dd5326a40d487..72375d315506f 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -10,6 +10,7 @@ use std::{ slice::from_ref, }; +use common::{bitops::IntegerExt, uninit_field_mut}; use qemu_api::{ bindings::{ address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool, @@ -27,7 +28,6 @@ use qemu_api::{ qom_isa, sysbus::{SysBusDevice, SysBusDeviceImpl}, timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}, - uninit_field_mut, vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, }; diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs index 619d662ee1e75..0605225fbb182 100644 --- a/rust/hw/timer/hpet/src/fw_cfg.rs +++ b/rust/hw/timer/hpet/src/fw_cfg.rs @@ -4,7 +4,8 @@ use std::ptr::addr_of_mut; -use qemu_api::{cell::bql_locked, zeroable::Zeroable}; +use common::Zeroable; +use qemu_api::cell::bql_locked; /// Each `HPETState` represents a Event Timer Block. The v1 spec supports /// up to 8 blocks. QEMU only uses 1 block (in PC machine). diff --git a/rust/meson.build b/rust/meson.build index 331f11b7e72a4..402f8d66007dc 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -22,6 +22,7 @@ qemuutil_rs = qemuutil.partial_dependency(link_args: true, links: true) genrs = [] +subdir('common') subdir('qemu-api-macros') subdir('bits') subdir('qemu-api') diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index a65a7ce2fe9a4..49003a47801db 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -97,7 +97,7 @@ fn derive_object_or_error(input: DeriveInput) -> Result::ParentType>); ::qemu_api::module_init! { @@ -125,20 +125,20 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result::Wrapped; + unsafe impl ::common::opaque::Wrapper for #name { + type Wrapped = <#typ as ::common::opaque::Wrapper>::Wrapped; } impl #name { - pub unsafe fn from_raw<'a>(ptr: *mut ::Wrapped) -> &'a Self { + pub unsafe fn from_raw<'a>(ptr: *mut ::Wrapped) -> &'a Self { let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::(); unsafe { ptr.as_ref() } } - pub const fn as_mut_ptr(&self) -> *mut ::Wrapped { + pub const fn as_mut_ptr(&self) -> *mut ::Wrapped { self.0.as_mut_ptr() } - pub const fn as_ptr(&self) -> *const ::Wrapped { + pub const fn as_ptr(&self) -> *const ::Wrapped { self.0.as_ptr() } @@ -146,7 +146,7 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result *mut ::Wrapped { + pub const fn raw_get(slot: *mut Self) -> *mut ::Wrapped { slot.cast() } } @@ -282,7 +282,7 @@ fn derive_device_or_error(input: DeriveInput) -> Result::ParentType> diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index c5ed78035b6e3..2e0e204491214 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -14,6 +14,7 @@ repository.workspace = true rust-version.workspace = true [dependencies] +common = { path = "../common" } qemu_api_macros = { path = "../qemu-api-macros" } anyhow = { workspace = true } foreign = { workspace = true } diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 062009f1618de..64af3caef5828 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -52,13 +52,9 @@ _qemu_api_rs = static_library( structured_sources( [ 'src/lib.rs', - 'src/assertions.rs', 'src/bindings.rs', - 'src/bitops.rs', - 'src/callbacks.rs', 'src/cell.rs', 'src/chardev.rs', - 'src/errno.rs', 'src/error.rs', 'src/irq.rs', 'src/log.rs', @@ -69,23 +65,21 @@ _qemu_api_rs = static_library( 'src/qom.rs', 'src/sysbus.rs', 'src/timer.rs', - 'src/uninit.rs', 'src/vmstate.rs', - 'src/zeroable.rs', ], {'.' : _qemu_api_bindings_inc_rs}, ), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, + dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, qom, hwcore, chardev, migration], ) rust.test('rust-qemu-api-tests', _qemu_api_rs, suite: ['unit', 'rust']) -qemu_api = declare_dependency(link_with: [_qemu_api_rs], +qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], dependencies: [qemu_api_macros, qom, hwcore, chardev, migration]) # Doctests are essentially integration tests, so they need the same dependencies. @@ -94,7 +88,7 @@ qemu_api = declare_dependency(link_with: [_qemu_api_rs], rust.doctest('rust-qemu-api-doctests', _qemu_api_rs, protocol: 'rust', - dependencies: qemu_api, + dependencies: [qemu_api_rs], suite: ['doc', 'rust']) test('rust-qemu-api-integration', @@ -104,7 +98,7 @@ test('rust-qemu-api-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [qemu_api]), + dependencies: [common_rs, qemu_api_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index b8104dea8bea4..3acdd903b5212 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -20,6 +20,8 @@ //! `bindgen`-generated declarations. +use common::Zeroable; + #[cfg(MESON)] include!("bindings.inc.rs"); @@ -56,3 +58,22 @@ unsafe impl Sync for VMStateField {} unsafe impl Send for VMStateInfo {} unsafe impl Sync for VMStateInfo {} + +// bindgen does not derive Default here +#[allow(clippy::derivable_impls)] +impl Default for crate::bindings::VMStateFlags { + fn default() -> Self { + Self(0) + } +} + +unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {} +unsafe impl Zeroable for crate::bindings::Property {} +unsafe impl Zeroable for crate::bindings::VMStateFlags {} +unsafe impl Zeroable for crate::bindings::VMStateField {} +unsafe impl Zeroable for crate::bindings::VMStateDescription {} +unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {} +unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {} +unsafe impl Zeroable for crate::bindings::MemoryRegionOps {} +unsafe impl Zeroable for crate::bindings::MemTxAttrs {} +unsafe impl Zeroable for crate::bindings::CharBackend {} diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index 9943d7286b6df..d13848df20015 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -141,82 +141,13 @@ //! Multiple immutable borrows are allowed via [`borrow`](BqlRefCell::borrow), //! or a single mutable borrow via [`borrow_mut`](BqlRefCell::borrow_mut). The //! thread will panic if these rules are violated or if the BQL is not held. -//! -//! ## Opaque wrappers -//! -//! The cell types from the previous section are useful at the boundaries -//! of code that requires interior mutability. When writing glue code that -//! interacts directly with C structs, however, it is useful to operate -//! at a lower level. -//! -//! C functions often violate Rust's fundamental assumptions about memory -//! safety by modifying memory even if it is shared. Furthermore, C structs -//! often start their life uninitialized and may be populated lazily. -//! -//! For this reason, this module provides the [`Opaque`] type to opt out -//! of Rust's usual guarantees about the wrapped type. Access to the wrapped -//! value is always through raw pointers, obtained via methods like -//! [`as_mut_ptr()`](Opaque::as_mut_ptr) and [`as_ptr()`](Opaque::as_ptr). These -//! pointers can then be passed to C functions or dereferenced; both actions -//! require `unsafe` blocks, making it clear where safety guarantees must be -//! manually verified. For example -//! -//! ```ignore -//! unsafe { -//! let state = Opaque::::uninit(); -//! qemu_struct_init(state.as_mut_ptr()); -//! } -//! ``` -//! -//! [`Opaque`] will usually be wrapped one level further, so that -//! bridge methods can be added to the wrapper: -//! -//! ```ignore -//! pub struct MyStruct(Opaque); -//! -//! impl MyStruct { -//! fn new() -> Pin> { -//! let result = Box::pin(unsafe { Opaque::uninit() }); -//! unsafe { qemu_struct_init(result.as_mut_ptr()) }; -//! result -//! } -//! } -//! ``` -//! -//! This pattern of wrapping bindgen-generated types in [`Opaque`] provides -//! several advantages: -//! -//! * The choice of traits to be implemented is not limited by the -//! bindgen-generated code. For example, [`Drop`] can be added without -//! disabling [`Copy`] on the underlying bindgen type -//! -//! * [`Send`] and [`Sync`] implementations can be controlled by the wrapper -//! type rather than being automatically derived from the C struct's layout -//! -//! * Methods can be implemented in a separate crate from the bindgen-generated -//! bindings -//! -//! * [`Debug`](std::fmt::Debug) and [`Display`](std::fmt::Display) -//! implementations can be customized to be more readable than the raw C -//! struct representation -//! -//! The [`Opaque`] type does not include BQL validation; it is possible to -//! assert in the code that the right lock is taken, to use it together -//! with a custom lock guard type, or to let C code take the lock, as -//! appropriate. It is also possible to use it with non-thread-safe -//! types, since by default (unlike [`BqlCell`] and [`BqlRefCell`] -//! it is neither `Sync` nor `Send`. -//! -//! While [`Opaque`] is necessary for C interop, it should be used sparingly -//! and only at FFI boundaries. For QEMU-specific types that need interior -//! mutability, prefer [`BqlCell`] or [`BqlRefCell`]. use std::{ cell::{Cell, UnsafeCell}, cmp::Ordering, fmt, - marker::{PhantomData, PhantomPinned}, - mem::{self, MaybeUninit}, + marker::PhantomData, + mem, ops::{Deref, DerefMut}, ptr::NonNull, }; @@ -939,165 +870,3 @@ impl fmt::Display for BqlRefMut<'_, T> { (**self).fmt(f) } } - -/// Stores an opaque value that is shared with C code. -/// -/// Often, C structs can changed when calling a C function even if they are -/// behind a shared Rust reference, or they can be initialized lazily and have -/// invalid bit patterns (e.g. `3` for a [`bool`]). This goes against Rust's -/// strict aliasing rules, which normally prevent mutation through shared -/// references. -/// -/// Wrapping the struct with `Opaque` ensures that the Rust compiler does not -/// assume the usual constraints that Rust structs require, and allows using -/// shared references on the Rust side. -/// -/// `Opaque` is `#[repr(transparent)]`, so that it matches the memory layout -/// of `T`. -#[repr(transparent)] -pub struct Opaque { - value: UnsafeCell>, - // PhantomPinned also allows multiple references to the `Opaque`, i.e. - // one `&mut Opaque` can coexist with a `&mut T` or any number of `&T`; - // see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/. - _pin: PhantomPinned, -} - -impl Opaque { - /// Creates a new shared reference from a C pointer - /// - /// # Safety - /// - /// The pointer must be valid, though it need not point to a valid value. - pub unsafe fn from_raw<'a>(ptr: *mut T) -> &'a Self { - let ptr = NonNull::new(ptr).unwrap().cast::(); - // SAFETY: Self is a transparent wrapper over T - unsafe { ptr.as_ref() } - } - - /// Creates a new opaque object with uninitialized contents. - /// - /// # Safety - /// - /// Ultimately the pointer to the returned value will be dereferenced - /// in another `unsafe` block, for example when passing it to a C function, - /// but the functions containing the dereference are usually safe. The - /// value returned from `uninit()` must be initialized and pinned before - /// calling them. - pub const unsafe fn uninit() -> Self { - Self { - value: UnsafeCell::new(MaybeUninit::uninit()), - _pin: PhantomPinned, - } - } - - /// Creates a new opaque object with zeroed contents. - /// - /// # Safety - /// - /// Ultimately the pointer to the returned value will be dereferenced - /// in another `unsafe` block, for example when passing it to a C function, - /// but the functions containing the dereference are usually safe. The - /// value returned from `uninit()` must be pinned (and possibly initialized) - /// before calling them. - pub const unsafe fn zeroed() -> Self { - Self { - value: UnsafeCell::new(MaybeUninit::zeroed()), - _pin: PhantomPinned, - } - } - - /// Returns a raw mutable pointer to the opaque data. - pub const fn as_mut_ptr(&self) -> *mut T { - UnsafeCell::get(&self.value).cast() - } - - /// Returns a raw pointer to the opaque data. - pub const fn as_ptr(&self) -> *const T { - self.as_mut_ptr().cast_const() - } - - /// Returns a raw pointer to the opaque data that can be passed to a - /// C function as `void *`. - pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void { - UnsafeCell::get(&self.value).cast() - } - - /// Converts a raw pointer to the wrapped type. - pub const fn raw_get(slot: *mut Self) -> *mut T { - // Compare with Linux's raw_get method, which goes through an UnsafeCell - // because it takes a *const Self instead. - slot.cast() - } -} - -impl fmt::Debug for Opaque { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut name: String = "Opaque<".to_string(); - name += std::any::type_name::(); - name += ">"; - f.debug_tuple(&name).field(&self.as_ptr()).finish() - } -} - -impl Opaque { - /// Creates a new opaque object with default contents. - /// - /// # Safety - /// - /// Ultimately the pointer to the returned value will be dereferenced - /// in another `unsafe` block, for example when passing it to a C function, - /// but the functions containing the dereference are usually safe. The - /// value returned from `uninit()` must be pinned before calling them. - pub unsafe fn new() -> Self { - Self { - value: UnsafeCell::new(MaybeUninit::new(T::default())), - _pin: PhantomPinned, - } - } -} - -/// Annotates [`Self`] as a transparent wrapper for another type. -/// -/// Usually defined via the [`qemu_api_macros::Wrapper`] derive macro. -/// -/// # Examples -/// -/// ``` -/// # use std::mem::ManuallyDrop; -/// # use qemu_api::cell::Wrapper; -/// #[repr(transparent)] -/// pub struct Example { -/// inner: ManuallyDrop, -/// } -/// -/// unsafe impl Wrapper for Example { -/// type Wrapped = String; -/// } -/// ``` -/// -/// # Safety -/// -/// `Self` must be a `#[repr(transparent)]` wrapper for the `Wrapped` type, -/// whether directly or indirectly. -/// -/// # Methods -/// -/// By convention, types that implement Wrapper also implement the following -/// methods: -/// -/// ```ignore -/// pub const unsafe fn from_raw<'a>(value: *mut Self::Wrapped) -> &'a Self; -/// pub const unsafe fn as_mut_ptr(&self) -> *mut Self::Wrapped; -/// pub const unsafe fn as_ptr(&self) -> *const Self::Wrapped; -/// pub const unsafe fn raw_get(slot: *mut Self) -> *const Self::Wrapped; -/// ``` -/// -/// They are not defined here to allow them to be `const`. -pub unsafe trait Wrapper { - type Wrapped; -} - -unsafe impl Wrapper for Opaque { - type Wrapped = T; -} diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs index cb27be52569cb..5a351dcecb0f0 100644 --- a/rust/qemu-api/src/chardev.rs +++ b/rust/qemu-api/src/chardev.rs @@ -18,10 +18,11 @@ use std::{ slice, }; +use common::{callbacks::FnCall, errno, Opaque}; + use crate::{ bindings, - callbacks::FnCall, - cell::{BqlRefMut, Opaque}, + cell::{BqlRefCell, BqlRefMut}, prelude::*, }; diff --git a/rust/qemu-api/src/error.rs b/rust/qemu-api/src/error.rs index e114fc4178bf8..8bac3cbec8107 100644 --- a/rust/qemu-api/src/error.rs +++ b/rust/qemu-api/src/error.rs @@ -316,10 +316,11 @@ mod tests { use std::ffi::CStr; use anyhow::anyhow; + use common::assert_match; use foreign::OwnedPointer; use super::*; - use crate::{assert_match, bindings}; + use crate::bindings; #[track_caller] fn error_for_test(msg: &CStr) -> OwnedPointer { diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index 1526e6f63a178..ea6b32848c50b 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -10,9 +10,10 @@ use std::{ ptr, }; +use common::Opaque; + use crate::{ bindings::{self, qemu_set_irq}, - cell::Opaque, prelude::*, qom::ObjectClass, }; diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index daa2493bb69d9..6232e39e71116 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -13,12 +13,8 @@ pub mod bindings; #[rustfmt::skip] pub mod prelude; -pub mod assertions; -pub mod bitops; -pub mod callbacks; pub mod cell; pub mod chardev; -pub mod errno; pub mod error; pub mod irq; pub mod log; @@ -28,9 +24,7 @@ pub mod qdev; pub mod qom; pub mod sysbus; pub mod timer; -pub mod uninit; pub mod vmstate; -pub mod zeroable; // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this // crate). diff --git a/rust/qemu-api/src/log.rs b/rust/qemu-api/src/log.rs index a441b8c1f2e61..d07f6272dc144 100644 --- a/rust/qemu-api/src/log.rs +++ b/rust/qemu-api/src/log.rs @@ -8,7 +8,9 @@ use std::{ ptr::NonNull, }; -use crate::{bindings, errno}; +use common::errno; + +use crate::bindings; #[repr(u32)] /// Represents specific error categories within QEMU's logging system. diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs index e40fad6cf19ea..f790cb5fd2d94 100644 --- a/rust/qemu-api/src/memory.rs +++ b/rust/qemu-api/src/memory.rs @@ -10,14 +10,11 @@ use std::{ }; pub use bindings::{hwaddr, MemTxAttrs}; +use common::{callbacks::FnCall, uninit::MaybeUninitField, zeroable::Zeroable, Opaque}; use crate::{ bindings::{self, device_endian, memory_region_init_io}, - callbacks::FnCall, - cell::Opaque, prelude::*, - uninit::MaybeUninitField, - zeroable::Zeroable, }; pub struct MemoryRegionOps( diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 8f9e23ee2c5f1..dcfe71497f027 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -4,13 +4,9 @@ //! Commonly used traits and types for QEMU. -pub use crate::bitops::IntegerExt; - pub use crate::cell::BqlCell; pub use crate::cell::BqlRefCell; -pub use crate::errno; - pub use crate::log_mask_ln; pub use crate::qdev::DeviceMethods; @@ -19,8 +15,8 @@ pub use crate::qom::InterfaceType; pub use crate::qom::IsA; pub use crate::qom::Object; pub use crate::qom::ObjectCast; -pub use crate::qom::ObjectDeref; pub use crate::qom::ObjectClassMethods; +pub use crate::qom::ObjectDeref; pub use crate::qom::ObjectMethods; pub use crate::qom::ObjectType; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index e2b4121cac5ac..8be125fae47ff 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -10,11 +10,11 @@ use std::{ }; pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; +use common::{callbacks::FnCall, Opaque}; use crate::{ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, - callbacks::FnCall, - cell::{bql_locked, Opaque}, + cell::bql_locked, chardev::Chardev, error::{Error, Result}, impl_vmstate_c_struct, @@ -246,7 +246,7 @@ macro_rules! define_property { bitnr: $bitnr, set_default: true, defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, - ..$crate::zeroable::Zeroable::ZERO + ..::common::zeroable::Zeroable::ZERO } }; ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { @@ -257,7 +257,7 @@ macro_rules! define_property { offset: ::std::mem::offset_of!($state, $field) as isize, set_default: true, defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, - ..$crate::zeroable::Zeroable::ZERO + ..::common::zeroable::Zeroable::ZERO } }; ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => { @@ -267,7 +267,7 @@ macro_rules! define_property { info: $prop, offset: ::std::mem::offset_of!($state, $field) as isize, set_default: false, - ..$crate::zeroable::Zeroable::ZERO + ..::common::zeroable::Zeroable::ZERO } }; } diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 12c6fc6752cad..49b4f03ccfcf3 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -102,13 +102,14 @@ use std::{ }; pub use bindings::ObjectClass; +use common::Opaque; use crate::{ bindings::{ self, object_class_dynamic_cast, object_dynamic_cast, object_get_class, object_get_typename, object_new, object_ref, object_unref, TypeInfo, }, - cell::{bql_locked, Opaque}, + cell::bql_locked, impl_vmstate_pointer, }; diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index e92502a8fe65d..4a5b4cbbf673a 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -7,10 +7,11 @@ use std::{ffi::CStr, ptr::addr_of_mut}; pub use bindings::SysBusDeviceClass; +use common::Opaque; use crate::{ bindings, - cell::{bql_locked, Opaque}, + cell::bql_locked, irq::{IRQState, InterruptSource}, memory::MemoryRegion, prelude::*, diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs index 1e639eaf221f7..383e1a6e774e5 100644 --- a/rust/qemu-api/src/timer.rs +++ b/rust/qemu-api/src/timer.rs @@ -7,10 +7,10 @@ use std::{ pin::Pin, }; -use crate::{ - bindings::{self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType}, - callbacks::FnCall, - cell::Opaque, +use common::{callbacks::FnCall, Opaque}; + +use crate::bindings::{ + self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType, }; /// A safe wrapper around [`bindings::QEMUTimer`]. diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index ce42b031bc150..06aac3a73f4de 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -35,14 +35,15 @@ use std::{ ptr::{addr_of, NonNull}, }; -pub use crate::bindings::{MigrationPriority, VMStateField}; -use crate::{ - bindings::{self, VMStateFlags}, +use common::{ callbacks::FnCall, errno::{into_neg_errno, Errno}, - zeroable::Zeroable, + Zeroable, }; +use crate::bindings::{self, VMStateFlags}; +pub use crate::bindings::{MigrationPriority, VMStateField}; + /// This macro is used to call a function with a generic argument bound /// to the type of a field. The function must take a /// [`PhantomData`]`` argument; `T` is the type of @@ -271,7 +272,7 @@ macro_rules! impl_vmstate_transparent { impl_vmstate_transparent!(std::cell::Cell where T: VMState); impl_vmstate_transparent!(std::cell::UnsafeCell where T: VMState); impl_vmstate_transparent!(std::pin::Pin where T: VMState); -impl_vmstate_transparent!(crate::cell::Opaque where T: VMState); +impl_vmstate_transparent!(::common::Opaque where T: VMState); #[macro_export] macro_rules! impl_vmstate_bitsized { @@ -324,7 +325,7 @@ macro_rules! impl_vmstate_c_struct { vmsd: ::std::ptr::addr_of!($vmsd), size: ::std::mem::size_of::<$type>(), flags: $crate::bindings::VMStateFlags::VMS_STRUCT, - ..$crate::zeroable::Zeroable::ZERO + ..common::zeroable::Zeroable::ZERO }; } }; @@ -367,7 +368,7 @@ macro_rules! vmstate_unused { size: $size, info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) }, flags: $crate::bindings::VMStateFlags::VMS_BUFFER, - ..$crate::zeroable::Zeroable::ZERO + ..::common::Zeroable::ZERO } }}; } @@ -390,7 +391,7 @@ pub type VMSFieldExistCb = unsafe extern "C" fn( #[macro_export] macro_rules! vmstate_exist_fn { ($struct_name:ty, $test_fn:expr) => {{ - const fn test_cb_builder__ $crate::callbacks::FnCall<(&'a T, u8), bool>>( + const fn test_cb_builder__ ::common::callbacks::FnCall<(&'a T, u8), bool>>( _phantom: ::core::marker::PhantomData, ) -> $crate::vmstate::VMSFieldExistCb { const { assert!(F::IS_SOME) }; @@ -414,7 +415,7 @@ macro_rules! vmstate_fields { $($field),*, $crate::bindings::VMStateField { flags: $crate::bindings::VMStateFlags::VMS_END, - ..$crate::zeroable::Zeroable::ZERO + ..::common::zeroable::Zeroable::ZERO } ]; _FIELDS.as_ptr() @@ -433,7 +434,7 @@ macro_rules! vmstate_validate { | $crate::bindings::VMStateFlags::VMS_ARRAY.0, ), num: 0, // 0 elements: no data, only run test_fn callback - ..$crate::zeroable::Zeroable::ZERO + ..::common::zeroable::Zeroable::ZERO } }; } @@ -455,7 +456,7 @@ macro_rules! impl_vmstate_struct { vmsd: ::core::ptr::addr_of!(*VMSD), size: ::core::mem::size_of::<$type>(), flags: $crate::bindings::VMStateFlags::VMS_STRUCT, - ..$crate::zeroable::Zeroable::ZERO + ..common::Zeroable::ZERO } }; } diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs deleted file mode 100644 index d8239d08563d1..0000000000000 --- a/rust/qemu-api/src/zeroable.rs +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Defines a trait for structs that can be safely initialized with zero bytes. - -/// Encapsulates the requirement that -/// `MaybeUninit::::zeroed().assume_init()` does not cause undefined -/// behavior. -/// -/// # Safety -/// -/// Do not add this trait to a type unless all-zeroes is a valid value for the -/// type. In particular, raw pointers can be zero, but references and -/// `NonNull` cannot. -pub unsafe trait Zeroable: Default { - /// Return a value of Self whose memory representation consists of all - /// zeroes, with the possible exclusion of padding bytes. - const ZERO: Self = unsafe { ::core::mem::MaybeUninit::::zeroed().assume_init() }; -} - -// bindgen does not derive Default here -#[allow(clippy::derivable_impls)] -impl Default for crate::bindings::VMStateFlags { - fn default() -> Self { - Self(0) - } -} - -unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {} -unsafe impl Zeroable for crate::bindings::Property {} -unsafe impl Zeroable for crate::bindings::VMStateFlags {} -unsafe impl Zeroable for crate::bindings::VMStateField {} -unsafe impl Zeroable for crate::bindings::VMStateDescription {} -unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {} -unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {} -unsafe impl Zeroable for crate::bindings::MemoryRegionOps {} -unsafe impl Zeroable for crate::bindings::MemTxAttrs {} -unsafe impl Zeroable for crate::bindings::CharBackend {} diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs index 2c0670ba0eed3..d9e5bcc49878e 100644 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -9,12 +9,13 @@ use std::{ slice, }; +use common::Opaque; use qemu_api::{ bindings::{ vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8, vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags, }, - cell::{BqlCell, Opaque}, + cell::BqlCell, impl_vmstate_forward, impl_vmstate_struct, vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField}, vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate, From 1e70d83de4d502759d6e68fbac301c1243088385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:49 +0200 Subject: [PATCH 0741/1794] rust: make build.rs generic over various ./rust/projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Guess the name of the subdir from the manifest directory, instead of hard-coding it. In the following commits, other crates can then link to this file, instead of maintaining their own copy. Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-5-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/build.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs index 29d09456257cb..5654d1d5624d9 100644 --- a/rust/qemu-api/build.rs +++ b/rust/qemu-api/build.rs @@ -9,12 +9,14 @@ use std::os::windows::fs::symlink_file; use std::{env, fs::remove_file, io::Result, path::Path}; fn main() -> Result<()> { + let manifest_dir = env!("CARGO_MANIFEST_DIR"); let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") { - format!("{root}/rust/qemu-api/bindings.inc.rs") + let sub = get_rust_subdir(manifest_dir).unwrap(); + format!("{root}/{sub}/bindings.inc.rs") } else { // Placing bindings.inc.rs in the source directory is supported // but not documented or encouraged. - format!("{}/src/bindings.inc.rs", env!("CARGO_MANIFEST_DIR")) + format!("{manifest_dir}/src/bindings.inc.rs") }; let file = Path::new(&file); @@ -41,3 +43,7 @@ fn main() -> Result<()> { println!("cargo:rerun-if-changed=build.rs"); Ok(()) } + +fn get_rust_subdir(path: &str) -> Option<&str> { + path.find("/rust").map(|index| &path[index + 1..]) +} From 59869b4d58854190f09a79c5392d60fdc0b55d45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:50 +0200 Subject: [PATCH 0742/1794] rust: split "util" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-7-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 17 +++++++-- rust/Cargo.toml | 5 +-- rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 7 ++-- rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 1 + rust/hw/timer/hpet/src/device.rs | 6 +-- rust/meson.build | 1 + rust/qemu-api-macros/src/lib.rs | 2 +- rust/qemu-api-macros/src/tests.rs | 2 +- rust/qemu-api/Cargo.toml | 4 +- rust/qemu-api/build.rs | 50 +----------------------- rust/qemu-api/meson.build | 14 ++----- rust/qemu-api/src/bindings.rs | 1 + rust/qemu-api/src/lib.rs | 6 --- rust/qemu-api/src/prelude.rs | 2 - rust/qemu-api/src/qdev.rs | 4 +- rust/qemu-api/src/sysbus.rs | 2 +- rust/qemu-api/src/vmstate.rs | 2 +- rust/qemu-api/tests/tests.rs | 2 +- rust/qemu-api/wrapper.h | 6 --- rust/util/Cargo.toml | 23 +++++++++++ rust/util/build.rs | 49 ++++++++++++++++++++++++ rust/util/meson.build | 55 +++++++++++++++++++++++++++ rust/util/src/bindings.rs | 25 ++++++++++++ rust/{qemu-api => util}/src/error.rs | 5 +-- rust/util/src/lib.rs | 9 +++++ rust/{qemu-api => util}/src/log.rs | 12 +++--- rust/{qemu-api => util}/src/module.rs | 2 +- rust/{qemu-api => util}/src/timer.rs | 0 rust/util/wrapper.h | 32 ++++++++++++++++ 33 files changed, 243 insertions(+), 107 deletions(-) mode change 100644 => 120000 rust/qemu-api/build.rs create mode 100644 rust/util/Cargo.toml create mode 100644 rust/util/build.rs create mode 100644 rust/util/meson.build create mode 100644 rust/util/src/bindings.rs rename rust/{qemu-api => util}/src/error.rs (98%) create mode 100644 rust/util/src/lib.rs rename rust/{qemu-api => util}/src/log.rs (93%) rename rust/{qemu-api => util}/src/module.rs (97%) rename rust/{qemu-api => util}/src/timer.rs (100%) create mode 100644 rust/util/wrapper.h diff --git a/MAINTAINERS b/MAINTAINERS index cee5a3420628c..3d7b47873f85c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3519,6 +3519,7 @@ F: rust/common/ F: rust/qemu-api F: rust/qemu-api-macros F: rust/rustfmt.toml +F: rust/util/ F: scripts/get-wraps-from-cargo-registry.py Rust-related patches CC here diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 71e8c7ed62ad5..757c03cbdecf9 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -73,6 +73,7 @@ dependencies = [ "common", "qemu_api", "qemu_api_macros", + "util", ] [[package]] @@ -100,6 +101,7 @@ dependencies = [ "common", "qemu_api", "qemu_api_macros", + "util", ] [[package]] @@ -138,11 +140,9 @@ dependencies = [ name = "qemu_api" version = "0.1.0" dependencies = [ - "anyhow", "common", - "foreign", - "libc", "qemu_api_macros", + "util", ] [[package]] @@ -180,6 +180,17 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "util" +version = "0.1.0" +dependencies = [ + "anyhow", + "common", + "foreign", + "libc", + "qemu_api_macros", +] + [[package]] name = "version_check" version = "0.9.4" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index c0426d42430d1..cfdd535e3bd16 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -7,6 +7,7 @@ members = [ "qemu-api", "hw/char/pl011", "hw/timer/hpet", + "util", ] [workspace.package] @@ -24,9 +25,7 @@ foreign = "~0.3.1" libc = "0.2.162" [workspace.lints.rust] -unexpected_cfgs = { level = "deny", check-cfg = [ - 'cfg(MESON)', -] } +unexpected_cfgs = { level = "deny", check-cfg = ['cfg(MESON)'] } # Occasionally, we may need to silence warnings and clippy lints that # were only introduced in newer Rust compiler versions. Do not croak diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 6d15f107dfae2..0cf9943fe8679 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -17,6 +17,7 @@ bilge = { version = "0.2.0" } bilge-impl = { version = "0.2.0" } bits = { path = "../../../bits" } common = { path = "../../../common" } +util = { path = "../../../util" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index c4a9f531f7d1f..8a931a4d03d4b 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -9,6 +9,7 @@ _libpl011_rs = static_library( bits_rs, common_rs, qemu_api_rs, + util_rs, qemu_api_macros, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index b4aa6c45f8743..ab38d57fc4152 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -9,8 +9,6 @@ use qemu_api::{ chardev::{CharBackend, Chardev, Event}, impl_vmstate_forward, impl_vmstate_struct, irq::{IRQState, InterruptSource}, - log::Log, - log_mask_ln, memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, @@ -19,6 +17,7 @@ use qemu_api::{ vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused, }; +use util::{log::Log, log_mask_ln}; use crate::registers::{self, Interrupt, RegisterOffset}; @@ -172,7 +171,7 @@ impl ObjectImpl for PL011State { impl DeviceImpl for PL011State { const VMSTATE: Option> = Some(VMSTATE_PL011); - const REALIZE: Option qemu_api::Result<()>> = Some(Self::realize); + const REALIZE: Option util::Result<()>> = Some(Self::realize); } impl ResettablePhasesImpl for PL011State { @@ -623,7 +622,7 @@ impl PL011State { } } - fn realize(&self) -> qemu_api::Result<()> { + fn realize(&self) -> util::Result<()> { self.char_backend .enable_handlers(self, Self::can_receive, Self::receive, Self::event); Ok(()) diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index ba7354f07e06b..dd9a5ed3d4303 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -12,6 +12,7 @@ rust-version.workspace = true [dependencies] common = { path = "../../../common" } +util = { path = "../../../util" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index c91d330439aeb..ca09660bf47b5 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -6,6 +6,7 @@ _libhpet_rs = static_library( dependencies: [ common_rs, qemu_api_rs, + util_rs, qemu_api_macros, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 72375d315506f..2be180fdedda2 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -27,10 +27,10 @@ use qemu_api::{ qom::{ObjectImpl, ObjectType, ParentField, ParentInit}, qom_isa, sysbus::{SysBusDevice, SysBusDeviceImpl}, - timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}, vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, }; +use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}; use crate::fw_cfg::HPETFwConfig; @@ -728,7 +728,7 @@ impl HPETState { } } - fn realize(&self) -> qemu_api::Result<()> { + fn realize(&self) -> util::Result<()> { if self.num_timers < HPET_MIN_TIMERS || self.num_timers > HPET_MAX_TIMERS { Err(format!( "hpet.num_timers must be between {HPET_MIN_TIMERS} and {HPET_MAX_TIMERS}" @@ -1013,7 +1013,7 @@ unsafe impl qemu_api::qdev::DevicePropertiesImpl for HPETState { impl DeviceImpl for HPETState { const VMSTATE: Option> = Some(VMSTATE_HPET); - const REALIZE: Option qemu_api::Result<()>> = Some(Self::realize); + const REALIZE: Option util::Result<()>> = Some(Self::realize); } impl ResettablePhasesImpl for HPETState { diff --git a/rust/meson.build b/rust/meson.build index 402f8d66007dc..a9d715e6e946e 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -25,6 +25,7 @@ genrs = [] subdir('common') subdir('qemu-api-macros') subdir('bits') +subdir('util') subdir('qemu-api') subdir('hw') diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 49003a47801db..67650a9a26938 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -100,7 +100,7 @@ fn derive_object_or_error(input: DeriveInput) -> Result::ParentType>); - ::qemu_api::module_init! { + ::util::module_init! { MODULE_INIT_QOM => unsafe { ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::qom::ObjectImpl>::TYPE_INFO); } diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs index ef2806368d205..8e71ac6e67783 100644 --- a/rust/qemu-api-macros/src/tests.rs +++ b/rust/qemu-api-macros/src/tests.rs @@ -170,7 +170,7 @@ fn test_derive_object() { _unused, ::qemu_api::qom::ParentField<::ParentType> ); - ::qemu_api::module_init! { + ::util::module_init! { MODULE_INIT_QOM => unsafe { ::qemu_api::bindings::type_register_static(&::TYPE_INFO); } diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 2e0e204491214..fbfb894421d64 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -15,10 +15,8 @@ rust-version.workspace = true [dependencies] common = { path = "../common" } +util = { path = "../util" } qemu_api_macros = { path = "../qemu-api-macros" } -anyhow = { workspace = true } -foreign = { workspace = true } -libc = { workspace = true } [features] default = ["debug_cell"] diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs deleted file mode 100644 index 5654d1d5624d9..0000000000000 --- a/rust/qemu-api/build.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -#[cfg(unix)] -use std::os::unix::fs::symlink as symlink_file; -#[cfg(windows)] -use std::os::windows::fs::symlink_file; -use std::{env, fs::remove_file, io::Result, path::Path}; - -fn main() -> Result<()> { - let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") { - let sub = get_rust_subdir(manifest_dir).unwrap(); - format!("{root}/{sub}/bindings.inc.rs") - } else { - // Placing bindings.inc.rs in the source directory is supported - // but not documented or encouraged. - format!("{manifest_dir}/src/bindings.inc.rs") - }; - - let file = Path::new(&file); - if !Path::new(&file).exists() { - panic!(concat!( - "\n", - " No generated C bindings found! Maybe you wanted one of\n", - " `make clippy`, `make rustfmt`, `make rustdoc`?\n", - "\n", - " For other uses of `cargo`, start a subshell with\n", - " `pyvenv/bin/meson devenv`, or point MESON_BUILD_ROOT to\n", - " the top of the build tree." - )); - } - - let out_dir = env::var("OUT_DIR").unwrap(); - let dest_path = format!("{out_dir}/bindings.inc.rs"); - let dest_path = Path::new(&dest_path); - if dest_path.symlink_metadata().is_ok() { - remove_file(dest_path)?; - } - symlink_file(file, dest_path)?; - - println!("cargo:rerun-if-changed=build.rs"); - Ok(()) -} - -fn get_rust_subdir(path: &str) -> Option<&str> { - path.find("/rust").map(|index| &path[index + 1..]) -} diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs new file mode 120000 index 0000000000000..71a3167885c23 --- /dev/null +++ b/rust/qemu-api/build.rs @@ -0,0 +1 @@ +../util/build.rs \ No newline at end of file diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 64af3caef5828..7734f656a2663 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -14,10 +14,8 @@ c_enums = [ 'MigrationPolicy', 'MigrationPriority', 'QEMUChrEvent', - 'QEMUClockType', 'ResetType', 'device_endian', - 'module_init_type', ] _qemu_api_bindgen_args = [] foreach enum : c_enums @@ -31,6 +29,7 @@ foreach enum : c_bitfields _qemu_api_bindgen_args += ['--bitfield-enum', enum] endforeach +_qemu_api_bindgen_args += ['--blocklist-type', 'Error'] # TODO: Remove this comment when the clang/libclang mismatch issue is solved. # # Rust bindings generation with `bindgen` might fail in some cases where the @@ -55,16 +54,12 @@ _qemu_api_rs = static_library( 'src/bindings.rs', 'src/cell.rs', 'src/chardev.rs', - 'src/error.rs', 'src/irq.rs', - 'src/log.rs', 'src/memory.rs', - 'src/module.rs', 'src/prelude.rs', 'src/qdev.rs', 'src/qom.rs', 'src/sysbus.rs', - 'src/timer.rs', 'src/vmstate.rs', ], {'.' : _qemu_api_bindings_inc_rs}, @@ -72,13 +67,10 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, + dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, util_rs, qom, hwcore, chardev, migration], ) -rust.test('rust-qemu-api-tests', _qemu_api_rs, - suite: ['unit', 'rust']) - qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], dependencies: [qemu_api_macros, qom, hwcore, chardev, migration]) @@ -98,7 +90,7 @@ test('rust-qemu-api-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [common_rs, qemu_api_rs]), + dependencies: [common_rs, util_rs, qemu_api_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 3acdd903b5212..aedf42b652c64 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -21,6 +21,7 @@ //! `bindgen`-generated declarations. use common::Zeroable; +use util::bindings::Error; #[cfg(MESON)] include!("bindings.inc.rs"); diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 6232e39e71116..54b252fb2c598 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -15,19 +15,13 @@ pub mod prelude; pub mod cell; pub mod chardev; -pub mod error; pub mod irq; -pub mod log; pub mod memory; -pub mod module; pub mod qdev; pub mod qom; pub mod sysbus; -pub mod timer; pub mod vmstate; // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this // crate). extern crate self as qemu_api; - -pub use error::{Error, Result}; diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index dcfe71497f027..3d771481e407f 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -7,8 +7,6 @@ pub use crate::cell::BqlCell; pub use crate::cell::BqlRefCell; -pub use crate::log_mask_ln; - pub use crate::qdev::DeviceMethods; pub use crate::qom::InterfaceType; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 8be125fae47ff..d87479ce13b94 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -11,12 +11,12 @@ use std::{ pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; use common::{callbacks::FnCall, Opaque}; +use util::{Error, Result}; use crate::{ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, cell::bql_locked, chardev::Chardev, - error::{Error, Result}, impl_vmstate_c_struct, irq::InterruptSource, prelude::*, @@ -183,7 +183,7 @@ pub trait DeviceImpl: /// readable/writeable from one thread at any time. unsafe extern "C" fn rust_realize_fn( dev: *mut bindings::DeviceState, - errp: *mut *mut bindings::Error, + errp: *mut *mut util::bindings::Error, ) { let state = NonNull::new(dev).unwrap().cast::(); let result = T::REALIZE.unwrap()(unsafe { state.as_ref() }); diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index 4a5b4cbbf673a..2dbfc31dbda13 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -114,7 +114,7 @@ where unsafe { bindings::sysbus_realize( self.upcast().as_mut_ptr(), - addr_of_mut!(bindings::error_fatal), + addr_of_mut!(util::bindings::error_fatal), ); } } diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 06aac3a73f4de..37e47cc4c6f7c 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -315,7 +315,7 @@ impl_vmstate_scalar!(vmstate_info_uint8, u8, VMS_VARRAY_UINT8); impl_vmstate_scalar!(vmstate_info_uint16, u16, VMS_VARRAY_UINT16); impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32); impl_vmstate_scalar!(vmstate_info_uint64, u64); -impl_vmstate_scalar!(vmstate_info_timer, crate::timer::Timer); +impl_vmstate_scalar!(vmstate_info_timer, util::timer::Timer); #[macro_export] macro_rules! impl_vmstate_c_struct { diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 1349568741914..70ef4a80d5735 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -5,7 +5,6 @@ use std::{ffi::CStr, ptr::addr_of}; use qemu_api::{ - bindings::{module_call_init, module_init_type}, cell::{self, BqlCell}, prelude::*, qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl}, @@ -13,6 +12,7 @@ use qemu_api::{ sysbus::SysBusDevice, vmstate::{VMStateDescription, VMStateDescriptionBuilder}, }; +use util::bindings::{module_call_init, module_init_type}; mod vmstate_tests; diff --git a/rust/qemu-api/wrapper.h b/rust/qemu-api/wrapper.h index 15a1b19847f2f..cc7112406b28a 100644 --- a/rust/qemu-api/wrapper.h +++ b/rust/qemu-api/wrapper.h @@ -48,9 +48,6 @@ typedef enum memory_order { #endif /* __CLANG_STDATOMIC_H */ #include "qemu/osdep.h" -#include "qemu/log.h" -#include "qemu/log-for-trace.h" -#include "qemu/module.h" #include "qemu-io.h" #include "system/system.h" #include "hw/sysbus.h" @@ -61,11 +58,8 @@ typedef enum memory_order { #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "hw/irq.h" -#include "qapi/error.h" -#include "qapi/error-internal.h" #include "migration/vmstate.h" #include "chardev/char-serial.h" #include "exec/memattrs.h" -#include "qemu/timer.h" #include "system/address-spaces.h" #include "hw/char/pl011.h" diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml new file mode 100644 index 0000000000000..637df61060ac4 --- /dev/null +++ b/rust/util/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "util" +version = "0.1.0" +description = "Rust bindings for QEMU/util" +resolver = "2" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +anyhow = { workspace = true } +foreign = { workspace = true } +libc = { workspace = true } +common = { path = "../common" } +qemu_api_macros = { path = "../qemu-api-macros" } + +[lints] +workspace = true diff --git a/rust/util/build.rs b/rust/util/build.rs new file mode 100644 index 0000000000000..5654d1d5624d9 --- /dev/null +++ b/rust/util/build.rs @@ -0,0 +1,49 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +#[cfg(unix)] +use std::os::unix::fs::symlink as symlink_file; +#[cfg(windows)] +use std::os::windows::fs::symlink_file; +use std::{env, fs::remove_file, io::Result, path::Path}; + +fn main() -> Result<()> { + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") { + let sub = get_rust_subdir(manifest_dir).unwrap(); + format!("{root}/{sub}/bindings.inc.rs") + } else { + // Placing bindings.inc.rs in the source directory is supported + // but not documented or encouraged. + format!("{manifest_dir}/src/bindings.inc.rs") + }; + + let file = Path::new(&file); + if !Path::new(&file).exists() { + panic!(concat!( + "\n", + " No generated C bindings found! Maybe you wanted one of\n", + " `make clippy`, `make rustfmt`, `make rustdoc`?\n", + "\n", + " For other uses of `cargo`, start a subshell with\n", + " `pyvenv/bin/meson devenv`, or point MESON_BUILD_ROOT to\n", + " the top of the build tree." + )); + } + + let out_dir = env::var("OUT_DIR").unwrap(); + let dest_path = format!("{out_dir}/bindings.inc.rs"); + let dest_path = Path::new(&dest_path); + if dest_path.symlink_metadata().is_ok() { + remove_file(dest_path)?; + } + symlink_file(file, dest_path)?; + + println!("cargo:rerun-if-changed=build.rs"); + Ok(()) +} + +fn get_rust_subdir(path: &str) -> Option<&str> { + path.find("/rust").map(|index| &path[index + 1..]) +} diff --git a/rust/util/meson.build b/rust/util/meson.build new file mode 100644 index 0000000000000..56e929349b82d --- /dev/null +++ b/rust/util/meson.build @@ -0,0 +1,55 @@ +_util_bindgen_args = [] +c_enums = [ + 'module_init_type', + 'QEMUClockType', +] +foreach enum : c_enums + _util_bindgen_args += ['--rustified-enum', enum] +endforeach + +# +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_util_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common + _util_bindgen_args, +) + +_util_rs = static_library( + 'util', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/error.rs', + 'src/log.rs', + 'src/module.rs', + 'src/timer.rs', + ], + {'.': _util_bindings_inc_rs} + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qemu_api_macros, qom, qemuutil], +) + +util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom]) + +# Doctests are essentially integration tests, so they need the same dependencies. +# Note that running them requires the object files for C code, so place them +# in a separate suite that is run by the "build" CI jobs rather than "check". +rust.doctest('rust-util-rs-doctests', + _util_rs, + protocol: 'rust', + dependencies: util_rs, + suite: ['doc', 'rust'] +) diff --git a/rust/util/src/bindings.rs b/rust/util/src/bindings.rs new file mode 100644 index 0000000000000..9ffff12cded80 --- /dev/null +++ b/rust/util/src/bindings.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![allow( + dead_code, + improper_ctypes_definitions, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unnecessary_transmutes, + unsafe_op_in_unsafe_fn, + clippy::pedantic, + clippy::restriction, + clippy::style, + clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, + clippy::useless_transmute, + clippy::missing_safety_doc, + clippy::too_many_arguments +)] + +#[cfg(MESON)] +include!("bindings.inc.rs"); + +#[cfg(not(MESON))] +include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); diff --git a/rust/qemu-api/src/error.rs b/rust/util/src/error.rs similarity index 98% rename from rust/qemu-api/src/error.rs rename to rust/util/src/error.rs index 8bac3cbec8107..bfa5a8685bc4e 100644 --- a/rust/qemu-api/src/error.rs +++ b/rust/util/src/error.rs @@ -19,7 +19,7 @@ //! //! This module is most commonly used at the boundary between C and Rust code; //! other code will usually access it through the -//! [`qemu_api::Result`](crate::Result) type alias, and will use the +//! [`utils::Result`](crate::Result) type alias, and will use the //! [`std::error::Error`] interface to let C errors participate in Rust's error //! handling functionality. //! @@ -30,7 +30,7 @@ //! type up to C code, or from a combination of the two. //! //! The third case, corresponding to [`Error::with_error`], is the only one that -//! requires mentioning [`qemu_api::Error`](crate::Error) explicitly. Similar +//! requires mentioning [`utils::Error`](crate::Error) explicitly. Similar //! to how QEMU's C code handles errno values, the string and the //! `anyhow::Error` object will be concatenated with `:` as the separator. @@ -320,7 +320,6 @@ mod tests { use foreign::OwnedPointer; use super::*; - use crate::bindings; #[track_caller] fn error_for_test(msg: &CStr) -> OwnedPointer { diff --git a/rust/util/src/lib.rs b/rust/util/src/lib.rs new file mode 100644 index 0000000000000..16c89b95174d0 --- /dev/null +++ b/rust/util/src/lib.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pub mod bindings; +pub mod error; +pub mod log; +pub mod module; +pub mod timer; + +pub use error::{Error, Result}; diff --git a/rust/qemu-api/src/log.rs b/rust/util/src/log.rs similarity index 93% rename from rust/qemu-api/src/log.rs rename to rust/util/src/log.rs index d07f6272dc144..af9a3e912348b 100644 --- a/rust/qemu-api/src/log.rs +++ b/rust/util/src/log.rs @@ -49,7 +49,7 @@ impl LogGuard { /// # Examples /// /// ``` - /// # use qemu_api::log::LogGuard; + /// # use util::log::LogGuard; /// # use std::io::Write; /// if let Some(mut log) = LogGuard::new() { /// writeln!(log, "test"); @@ -116,7 +116,7 @@ impl Drop for LogGuard { /// # Example /// /// ``` -/// use qemu_api::{log::Log, log_mask_ln}; +/// use util::{log::Log, log_mask_ln}; /// /// let error_address = 0xbad; /// log_mask_ln!(Log::GuestError, "Address 0x{error_address:x} out of range"); @@ -126,7 +126,7 @@ impl Drop for LogGuard { /// trailing `,`: /// /// ``` -/// use qemu_api::{log::Log, log_mask_ln}; +/// use util::{log::Log, log_mask_ln}; /// /// let error_address = 0xbad; /// log_mask_ln!( @@ -139,12 +139,12 @@ impl Drop for LogGuard { macro_rules! log_mask_ln { ($mask:expr, $fmt:tt $($args:tt)*) => {{ // Type assertion to enforce type `Log` for $mask - let _: Log = $mask; + let _: $crate::log::Log = $mask; if unsafe { - (::qemu_api::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0 + ($crate::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0 } { - _ = ::qemu_api::log::LogGuard::log_fmt( + _ = $crate::log::LogGuard::log_fmt( format_args!("{}\n", format_args!($fmt $($args)*))); } }}; diff --git a/rust/qemu-api/src/module.rs b/rust/util/src/module.rs similarity index 97% rename from rust/qemu-api/src/module.rs rename to rust/util/src/module.rs index fa5cea3598f96..06c45fc142b4b 100644 --- a/rust/qemu-api/src/module.rs +++ b/rust/util/src/module.rs @@ -36,7 +36,7 @@ macro_rules! module_init { // shortcut because it's quite common that $body needs unsafe {} ($type:ident => unsafe $body:block) => { - $crate::module_init! { + ::util::module_init! { $type => { unsafe { $body } } } }; diff --git a/rust/qemu-api/src/timer.rs b/rust/util/src/timer.rs similarity index 100% rename from rust/qemu-api/src/timer.rs rename to rust/util/src/timer.rs diff --git a/rust/util/wrapper.h b/rust/util/wrapper.h new file mode 100644 index 0000000000000..b9ed68a01d824 --- /dev/null +++ b/rust/util/wrapper.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * This header file is meant to be used as input to the `bindgen` application + * in order to generate C FFI compatible Rust bindings. + */ + +#ifndef __CLANG_STDATOMIC_H +#define __CLANG_STDATOMIC_H +/* + * Fix potential missing stdatomic.h error in case bindgen does not insert the + * correct libclang header paths on its own. We do not use stdatomic.h symbols + * in QEMU code, so it's fine to declare dummy types instead. + */ +typedef enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst, +} memory_order; +#endif /* __CLANG_STDATOMIC_H */ + +#include "qemu/osdep.h" + +#include "qapi/error.h" +#include "qapi/error-internal.h" +#include "qemu/log-for-trace.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/timer.h" From 4dff343d2312bfec25f95ab99ed1068511ddbebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:51 +0200 Subject: [PATCH 0743/1794] rust: split "migration" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-11-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 12 +++++ rust/Cargo.toml | 1 + rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 13 ++--- rust/hw/char/pl011/src/registers.rs | 2 +- rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 1 + rust/hw/timer/hpet/src/device.rs | 11 +++-- rust/meson.build | 1 + rust/migration/Cargo.toml | 21 ++++++++ rust/migration/build.rs | 1 + rust/migration/meson.build | 53 +++++++++++++++++++++ rust/migration/src/bindings.rs | 48 +++++++++++++++++++ rust/migration/src/lib.rs | 6 +++ rust/{qemu-api => migration}/src/vmstate.rs | 42 +++++++++------- rust/migration/wrapper.h | 51 ++++++++++++++++++++ rust/qemu-api/Cargo.toml | 1 + rust/qemu-api/meson.build | 12 ++--- rust/qemu-api/src/bindings.rs | 21 +------- rust/qemu-api/src/cell.rs | 4 +- rust/qemu-api/src/lib.rs | 1 - rust/qemu-api/src/prelude.rs | 2 - rust/qemu-api/src/qdev.rs | 3 +- rust/qemu-api/src/qom.rs | 2 +- rust/qemu-api/tests/tests.rs | 2 +- rust/qemu-api/tests/vmstate_tests.rs | 4 +- rust/qemu-api/wrapper.h | 1 - 29 files changed, 252 insertions(+), 68 deletions(-) create mode 100644 rust/migration/Cargo.toml create mode 120000 rust/migration/build.rs create mode 100644 rust/migration/meson.build create mode 100644 rust/migration/src/bindings.rs create mode 100644 rust/migration/src/lib.rs rename rust/{qemu-api => migration}/src/vmstate.rs (95%) create mode 100644 rust/migration/wrapper.h diff --git a/MAINTAINERS b/MAINTAINERS index 3d7b47873f85c..76dcf6ceb2375 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3516,6 +3516,7 @@ Rust M: Manos Pitsidianakis S: Maintained F: rust/common/ +F: rust/migration/ F: rust/qemu-api F: rust/qemu-api-macros F: rust/rustfmt.toml diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 757c03cbdecf9..048dd74757f09 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -71,6 +71,7 @@ name = "hpet" version = "0.1.0" dependencies = [ "common", + "migration", "qemu_api", "qemu_api_macros", "util", @@ -91,6 +92,15 @@ version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +[[package]] +name = "migration" +version = "0.1.0" +dependencies = [ + "common", + "qemu_api_macros", + "util", +] + [[package]] name = "pl011" version = "0.1.0" @@ -99,6 +109,7 @@ dependencies = [ "bilge-impl", "bits", "common", + "migration", "qemu_api", "qemu_api_macros", "util", @@ -141,6 +152,7 @@ name = "qemu_api" version = "0.1.0" dependencies = [ "common", + "migration", "qemu_api_macros", "util", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index cfdd535e3bd16..e0958ef28a861 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = [ "bits", "common", + "migration", "qemu-api-macros", "qemu-api", "hw/char/pl011", diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 0cf9943fe8679..7fd7531823d75 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -18,6 +18,7 @@ bilge-impl = { version = "0.2.0" } bits = { path = "../../../bits" } common = { path = "../../../common" } util = { path = "../../../util" } +migration = { path = "../../../migration" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index 8a931a4d03d4b..2198fcee9bc1c 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -10,6 +10,7 @@ _libpl011_rs = static_library( common_rs, qemu_api_rs, util_rs, + migration_rs, qemu_api_macros, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index ab38d57fc4152..225be34e08bbe 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -5,17 +5,18 @@ use std::{ffi::CStr, mem::size_of}; use common::{static_assert, uninit_field_mut}; +use migration::{ + self, impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of, + vmstate_subsections, vmstate_unused, VMStateDescription, VMStateDescriptionBuilder, +}; use qemu_api::{ chardev::{CharBackend, Chardev, Event}, - impl_vmstate_forward, impl_vmstate_struct, irq::{IRQState, InterruptSource}, memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, qom::{ObjectImpl, Owned, ParentField, ParentInit}, sysbus::{SysBusDevice, SysBusDeviceImpl}, - vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, - vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused, }; use util::{log::Log, log_mask_ln}; @@ -458,10 +459,10 @@ impl PL011Registers { false } - pub fn post_load(&mut self) -> Result<(), vmstate::InvalidError> { + pub fn post_load(&mut self) -> Result<(), migration::InvalidError> { /* Sanity-check input state */ if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() { - return Err(vmstate::InvalidError); + return Err(migration::InvalidError); } if !self.fifo_enabled() && self.read_count > 0 && self.read_pos > 0 { @@ -640,7 +641,7 @@ impl PL011State { } } - pub fn post_load(&self, _version_id: u8) -> Result<(), vmstate::InvalidError> { + pub fn post_load(&self, _version_id: u8) -> Result<(), migration::InvalidError> { self.regs.borrow_mut().post_load() } } diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs index 7ececd39f8616..2bfbd81095e64 100644 --- a/rust/hw/char/pl011/src/registers.rs +++ b/rust/hw/char/pl011/src/registers.rs @@ -10,7 +10,7 @@ use bilge::prelude::*; use bits::bits; -use qemu_api::{impl_vmstate_bitsized, impl_vmstate_forward}; +use migration::{impl_vmstate_bitsized, impl_vmstate_forward}; /// Offset of each register from the base memory address of the device. #[doc(alias = "offset")] diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index dd9a5ed3d4303..70acdf03d6e5d 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -13,6 +13,7 @@ rust-version.workspace = true [dependencies] common = { path = "../../../common" } util = { path = "../../../util" } +migration = { path = "../../../migration" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index ca09660bf47b5..8cd70091e631c 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -7,6 +7,7 @@ _libhpet_rs = static_library( common_rs, qemu_api_rs, util_rs, + migration_rs, qemu_api_macros, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 2be180fdedda2..1c2253466d95b 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -11,13 +11,16 @@ use std::{ }; use common::{bitops::IntegerExt, uninit_field_mut}; +use migration::{ + self, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, + VMStateDescription, VMStateDescriptionBuilder, +}; use qemu_api::{ bindings::{ address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize, }, cell::{BqlCell, BqlRefCell}, - impl_vmstate_struct, irq::InterruptSource, memory::{ hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED, @@ -27,8 +30,6 @@ use qemu_api::{ qom::{ObjectImpl, ObjectType, ParentField, ParentInit}, qom_isa, sysbus::{SysBusDevice, SysBusDeviceImpl}, - vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, - vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, }; use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}; @@ -845,7 +846,7 @@ impl HPETState { } } - fn pre_save(&self) -> Result<(), vmstate::Infallible> { + fn pre_save(&self) -> Result<(), migration::Infallible> { if self.is_hpet_enabled() { self.counter.set(self.get_ticks()); } @@ -859,7 +860,7 @@ impl HPETState { Ok(()) } - fn post_load(&self, _version_id: u8) -> Result<(), vmstate::Infallible> { + fn post_load(&self, _version_id: u8) -> Result<(), migration::Infallible> { for timer in self.timers.iter().take(self.num_timers) { let mut t = timer.borrow_mut(); diff --git a/rust/meson.build b/rust/meson.build index a9d715e6e946e..826949b2e60f4 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -26,6 +26,7 @@ subdir('common') subdir('qemu-api-macros') subdir('bits') subdir('util') +subdir('migration') subdir('qemu-api') subdir('hw') diff --git a/rust/migration/Cargo.toml b/rust/migration/Cargo.toml new file mode 100644 index 0000000000000..98e6df2109d33 --- /dev/null +++ b/rust/migration/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "migration" +version = "0.1.0" +description = "Rust bindings for QEMU/migration" +resolver = "2" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +common = { path = "../common" } +util = { path = "../util" } +qemu_api_macros = { path = "../qemu-api-macros" } + +[lints] +workspace = true diff --git a/rust/migration/build.rs b/rust/migration/build.rs new file mode 120000 index 0000000000000..71a3167885c23 --- /dev/null +++ b/rust/migration/build.rs @@ -0,0 +1 @@ +../util/build.rs \ No newline at end of file diff --git a/rust/migration/meson.build b/rust/migration/meson.build new file mode 100644 index 0000000000000..5e820d43f5017 --- /dev/null +++ b/rust/migration/meson.build @@ -0,0 +1,53 @@ +_migration_bindgen_args = [] +c_bitfields = [ + 'MigrationPolicy', + 'MigrationPriority', + 'VMStateFlags', +] +foreach enum : c_bitfields + _migration_bindgen_args += ['--bitfield-enum', enum] +endforeach +# +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_migration_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common + _migration_bindgen_args, + ) + +_migration_rs = static_library( + 'migration', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/vmstate.rs', + ], + {'.' : _migration_bindings_inc_rs}, + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + link_with: [_util_rs], + dependencies: [common_rs], +) + +migration_rs = declare_dependency(link_with: [_migration_rs], + dependencies: [migration, qemuutil]) + +# Doctests are essentially integration tests, so they need the same dependencies. +# Note that running them requires the object files for C code, so place them +# in a separate suite that is run by the "build" CI jobs rather than "check". +rust.doctest('rust-migration-rs-doctests', + _migration_rs, + protocol: 'rust', + dependencies: migration_rs, + suite: ['doc', 'rust']) diff --git a/rust/migration/src/bindings.rs b/rust/migration/src/bindings.rs new file mode 100644 index 0000000000000..8ce13a9000ef2 --- /dev/null +++ b/rust/migration/src/bindings.rs @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![allow( + dead_code, + improper_ctypes_definitions, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unnecessary_transmutes, + unsafe_op_in_unsafe_fn, + clippy::pedantic, + clippy::restriction, + clippy::style, + clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, + clippy::useless_transmute, + clippy::missing_safety_doc, + clippy::too_many_arguments +)] + +use common::Zeroable; + +#[cfg(MESON)] +include!("bindings.inc.rs"); + +#[cfg(not(MESON))] +include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); + +unsafe impl Send for VMStateDescription {} +unsafe impl Sync for VMStateDescription {} + +unsafe impl Send for VMStateField {} +unsafe impl Sync for VMStateField {} + +unsafe impl Send for VMStateInfo {} +unsafe impl Sync for VMStateInfo {} + +// bindgen does not derive Default here +#[allow(clippy::derivable_impls)] +impl Default for VMStateFlags { + fn default() -> Self { + Self(0) + } +} + +unsafe impl Zeroable for VMStateFlags {} +unsafe impl Zeroable for VMStateField {} +unsafe impl Zeroable for VMStateDescription {} diff --git a/rust/migration/src/lib.rs b/rust/migration/src/lib.rs new file mode 100644 index 0000000000000..5f51dde440617 --- /dev/null +++ b/rust/migration/src/lib.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pub mod bindings; + +pub mod vmstate; +pub use vmstate::*; diff --git a/rust/qemu-api/src/vmstate.rs b/rust/migration/src/vmstate.rs similarity index 95% rename from rust/qemu-api/src/vmstate.rs rename to rust/migration/src/vmstate.rs index 37e47cc4c6f7c..537d54e43689e 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -52,7 +52,7 @@ pub use crate::bindings::{MigrationPriority, VMStateField}; /// # Examples /// /// ``` -/// # use qemu_api::call_func_with_field; +/// # use migration::call_func_with_field; /// # use core::marker::PhantomData; /// const fn size_of_field(_: PhantomData) -> usize { /// std::mem::size_of::() @@ -125,17 +125,19 @@ pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags /// * scalar types (integer and `bool`) /// * the C struct `QEMUTimer` /// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`, -/// [`BqlCell`](crate::cell::BqlCell), -/// [`BqlRefCell`](crate::cell::BqlRefCell)), +/// [`BqlCell`], [`BqlRefCell`]) /// * a raw pointer to any of the above -/// * a `NonNull` pointer, a `Box` or an [`Owned`](crate::qom::Owned) for any of -/// the above +/// * a `NonNull` pointer, a `Box` or an [`Owned`] for any of the above /// * an array of any of the above /// /// In order to support other types, the trait `VMState` must be implemented /// for them. The macros [`impl_vmstate_forward`](crate::impl_vmstate_forward), /// [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and /// [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this. +/// +/// [`BqlCell`]: ../../qemu_api/cell/struct.BqlCell.html +/// [`BqlRefCell`]: ../../qemu_api/cell/struct.BqlRefCell.html +/// [`Owned`]: ../../qemu_api/qom/struct.Owned.html #[macro_export] macro_rules! vmstate_of { ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => { @@ -161,7 +163,11 @@ macro_rules! vmstate_of { }; } -impl VMStateFlags { +pub trait VMStateFlagsExt { + const VMS_VARRAY_FLAGS: VMStateFlags; +} + +impl VMStateFlagsExt for VMStateFlags { const VMS_VARRAY_FLAGS: VMStateFlags = VMStateFlags( VMStateFlags::VMS_VARRAY_INT32.0 | VMStateFlags::VMS_VARRAY_UINT8.0 @@ -207,7 +213,7 @@ impl VMStateField { } #[must_use] - pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> VMStateField { + pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> Self { self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0); self.flags = VMStateFlags(self.flags.0 | flag.0); self.num = 0; // varray uses num_offset instead of num. @@ -216,13 +222,13 @@ impl VMStateField { #[must_use] #[allow(unused_mut)] - pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> VMStateField { + pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> Self { assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0); self.with_varray_flag_unchecked(flag) } #[must_use] - pub const fn with_varray_multiply(mut self, num: u32) -> VMStateField { + pub const fn with_varray_multiply(mut self, num: u32) -> Self { assert!(num <= 0x7FFF_FFFFu32); self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0); self.num = num as i32; @@ -237,7 +243,7 @@ impl VMStateField { /// # Examples /// /// ``` -/// # use qemu_api::impl_vmstate_forward; +/// # use migration::impl_vmstate_forward; /// pub struct Fifo([u8; 16]); /// impl_vmstate_forward!(Fifo); /// ``` @@ -272,7 +278,7 @@ macro_rules! impl_vmstate_transparent { impl_vmstate_transparent!(std::cell::Cell where T: VMState); impl_vmstate_transparent!(std::cell::UnsafeCell where T: VMState); impl_vmstate_transparent!(std::pin::Pin where T: VMState); -impl_vmstate_transparent!(::common::Opaque where T: VMState); +impl_vmstate_transparent!(common::Opaque where T: VMState); #[macro_export] macro_rules! impl_vmstate_bitsized { @@ -294,12 +300,12 @@ macro_rules! impl_vmstate_bitsized { macro_rules! impl_vmstate_scalar { ($info:ident, $type:ty$(, $varray_flag:ident)?) => { - unsafe impl VMState for $type { - const BASE: VMStateField = VMStateField { + unsafe impl $crate::vmstate::VMState for $type { + const BASE: $crate::vmstate::VMStateField = $crate::vmstate::VMStateField { info: addr_of!(bindings::$info), size: mem::size_of::<$type>(), - flags: VMStateFlags::VMS_SINGLE, - ..Zeroable::ZERO + flags: $crate::vmstate::VMStateFlags::VMS_SINGLE, + ..::common::zeroable::Zeroable::ZERO }; $(const VARRAY_FLAG: VMStateFlags = VMStateFlags::$varray_flag;)? } @@ -320,12 +326,12 @@ impl_vmstate_scalar!(vmstate_info_timer, util::timer::Timer); #[macro_export] macro_rules! impl_vmstate_c_struct { ($type:ty, $vmsd:expr) => { - unsafe impl VMState for $type { + unsafe impl $crate::vmstate::VMState for $type { const BASE: $crate::bindings::VMStateField = $crate::bindings::VMStateField { vmsd: ::std::ptr::addr_of!($vmsd), size: ::std::mem::size_of::<$type>(), flags: $crate::bindings::VMStateFlags::VMS_STRUCT, - ..common::zeroable::Zeroable::ZERO + ..::common::zeroable::Zeroable::ZERO }; } }; @@ -391,7 +397,7 @@ pub type VMSFieldExistCb = unsafe extern "C" fn( #[macro_export] macro_rules! vmstate_exist_fn { ($struct_name:ty, $test_fn:expr) => {{ - const fn test_cb_builder__ ::common::callbacks::FnCall<(&'a T, u8), bool>>( + const fn test_cb_builder__ ::common::FnCall<(&'a T, u8), bool>>( _phantom: ::core::marker::PhantomData, ) -> $crate::vmstate::VMSFieldExistCb { const { assert!(F::IS_SOME) }; diff --git a/rust/migration/wrapper.h b/rust/migration/wrapper.h new file mode 100644 index 0000000000000..daf316aed411b --- /dev/null +++ b/rust/migration/wrapper.h @@ -0,0 +1,51 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2024 Linaro Ltd. + * + * Authors: Manos Pitsidianakis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +/* + * This header file is meant to be used as input to the `bindgen` application + * in order to generate C FFI compatible Rust bindings. + */ + +#ifndef __CLANG_STDATOMIC_H +#define __CLANG_STDATOMIC_H +/* + * Fix potential missing stdatomic.h error in case bindgen does not insert the + * correct libclang header paths on its own. We do not use stdatomic.h symbols + * in QEMU code, so it's fine to declare dummy types instead. + */ +typedef enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst, +} memory_order; +#endif /* __CLANG_STDATOMIC_H */ + +#include "qemu/osdep.h" +#include "migration/vmstate.h" diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index fbfb894421d64..7276e67aa974c 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -15,6 +15,7 @@ rust-version.workspace = true [dependencies] common = { path = "../common" } +migration = { path = "../migration" } util = { path = "../util" } qemu_api_macros = { path = "../qemu-api-macros" } diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 7734f656a2663..a6b5772d19428 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -11,8 +11,6 @@ c_enums = [ 'GpioPolarity', 'MachineInitPhase', 'MemoryDeviceInfoKind', - 'MigrationPolicy', - 'MigrationPriority', 'QEMUChrEvent', 'ResetType', 'device_endian', @@ -23,12 +21,13 @@ foreach enum : c_enums endforeach c_bitfields = [ 'ClockEvent', - 'VMStateFlags', ] foreach enum : c_bitfields _qemu_api_bindgen_args += ['--bitfield-enum', enum] endforeach +_qemu_api_bindgen_args += ['--blocklist-type', 'VMStateDescription'] + _qemu_api_bindgen_args += ['--blocklist-type', 'Error'] # TODO: Remove this comment when the clang/libclang mismatch issue is solved. # @@ -60,15 +59,14 @@ _qemu_api_rs = static_library( 'src/qdev.rs', 'src/qom.rs', 'src/sysbus.rs', - 'src/vmstate.rs', ], {'.' : _qemu_api_bindings_inc_rs}, ), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, util_rs, - qom, hwcore, chardev, migration], + dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, + util_rs, qom, hwcore, chardev], ) qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], @@ -90,7 +88,7 @@ test('rust-qemu-api-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [common_rs, util_rs, qemu_api_rs]), + dependencies: [common_rs, util_rs, migration_rs, qemu_api_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index aedf42b652c64..ce00a6e0e4cf3 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -21,6 +21,7 @@ //! `bindgen`-generated declarations. use common::Zeroable; +use migration::bindings::VMStateDescription; use util::bindings::Error; #[cfg(MESON)] @@ -51,28 +52,8 @@ unsafe impl Sync for Property {} unsafe impl Send for TypeInfo {} unsafe impl Sync for TypeInfo {} -unsafe impl Send for VMStateDescription {} -unsafe impl Sync for VMStateDescription {} - -unsafe impl Send for VMStateField {} -unsafe impl Sync for VMStateField {} - -unsafe impl Send for VMStateInfo {} -unsafe impl Sync for VMStateInfo {} - -// bindgen does not derive Default here -#[allow(clippy::derivable_impls)] -impl Default for crate::bindings::VMStateFlags { - fn default() -> Self { - Self(0) - } -} - unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {} unsafe impl Zeroable for crate::bindings::Property {} -unsafe impl Zeroable for crate::bindings::VMStateFlags {} -unsafe impl Zeroable for crate::bindings::VMStateField {} -unsafe impl Zeroable for crate::bindings::VMStateDescription {} unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {} unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {} unsafe impl Zeroable for crate::bindings::MemoryRegionOps {} diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index d13848df20015..b80a0fd80b7b0 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -152,7 +152,9 @@ use std::{ ptr::NonNull, }; -use crate::{bindings, impl_vmstate_transparent}; +use migration::impl_vmstate_transparent; + +use crate::bindings; /// An internal function that is used by doctests. pub fn bql_start_test() { diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 54b252fb2c598..55386f66978ab 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -20,7 +20,6 @@ pub mod memory; pub mod qdev; pub mod qom; pub mod sysbus; -pub mod vmstate; // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this // crate). diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 3d771481e407f..c10c171158107 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -21,5 +21,3 @@ pub use crate::qom::ObjectType; pub use crate::qom_isa; pub use crate::sysbus::SysBusDeviceMethods; - -pub use crate::vmstate::VMState; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index d87479ce13b94..c81ae7cf45c53 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -11,17 +11,16 @@ use std::{ pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; use common::{callbacks::FnCall, Opaque}; +use migration::{impl_vmstate_c_struct, VMStateDescription}; use util::{Error, Result}; use crate::{ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, cell::bql_locked, chardev::Chardev, - impl_vmstate_c_struct, irq::InterruptSource, prelude::*, qom::{ObjectClass, ObjectImpl, Owned, ParentInit}, - vmstate::VMStateDescription, }; /// A safe wrapper around [`bindings::Clock`]. diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 49b4f03ccfcf3..7f2f7797e44d6 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -103,6 +103,7 @@ use std::{ pub use bindings::ObjectClass; use common::Opaque; +use migration::impl_vmstate_pointer; use crate::{ bindings::{ @@ -110,7 +111,6 @@ use crate::{ object_get_typename, object_new, object_ref, object_unref, TypeInfo, }, cell::bql_locked, - impl_vmstate_pointer, }; /// A safe wrapper around [`bindings::Object`]. diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 70ef4a80d5735..92e3534d3c111 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -4,13 +4,13 @@ use std::{ffi::CStr, ptr::addr_of}; +use migration::{VMStateDescription, VMStateDescriptionBuilder}; use qemu_api::{ cell::{self, BqlCell}, prelude::*, qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl}, qom::{ObjectImpl, ParentField}, sysbus::SysBusDevice, - vmstate::{VMStateDescription, VMStateDescriptionBuilder}, }; use util::bindings::{module_call_init, module_init_type}; diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs index d9e5bcc49878e..47fc15149b4d2 100644 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -10,16 +10,16 @@ use std::{ }; use common::Opaque; -use qemu_api::{ +use migration::{ bindings::{ vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8, vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags, }, - cell::BqlCell, impl_vmstate_forward, impl_vmstate_struct, vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField}, vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate, }; +use qemu_api::cell::BqlCell; const FOO_ARRAY_MAX: usize = 3; diff --git a/rust/qemu-api/wrapper.h b/rust/qemu-api/wrapper.h index cc7112406b28a..b99df9f568a30 100644 --- a/rust/qemu-api/wrapper.h +++ b/rust/qemu-api/wrapper.h @@ -58,7 +58,6 @@ typedef enum memory_order { #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "hw/irq.h" -#include "migration/vmstate.h" #include "chardev/char-serial.h" #include "exec/memattrs.h" #include "system/address-spaces.h" From f6b4f0dd9c57079b72ca9ff7569ce4d0dbdcc4d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:52 +0200 Subject: [PATCH 0744/1794] rust: split "bql" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unfortunately, an example had to be compile-time disabled, since it relies on higher level crates (qdev, irq etc). The alternative is probably to move that code to an example in qemu-api or elsewere and make a link to it, or include_str. Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-12-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 10 +++ rust/Cargo.toml | 1 + rust/bql/Cargo.toml | 23 +++++++ rust/bql/build.rs | 1 + rust/bql/meson.build | 52 ++++++++++++++++ rust/bql/src/bindings.rs | 25 ++++++++ rust/{qemu-api => bql}/src/cell.rs | 92 ++++++++++------------------ rust/bql/src/lib.rs | 29 +++++++++ rust/bql/wrapper.h | 27 ++++++++ rust/common/src/opaque.rs | 4 +- rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 1 + rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 1 + rust/hw/timer/hpet/src/device.rs | 2 +- rust/hw/timer/hpet/src/fw_cfg.rs | 5 +- rust/meson.build | 1 + rust/migration/src/vmstate.rs | 4 +- rust/qemu-api/Cargo.toml | 5 +- rust/qemu-api/meson.build | 18 +----- rust/qemu-api/src/chardev.rs | 17 +++-- rust/qemu-api/src/irq.rs | 1 + rust/qemu-api/src/lib.rs | 1 - rust/qemu-api/src/prelude.rs | 3 - rust/qemu-api/src/qdev.rs | 5 +- rust/qemu-api/src/qom.rs | 13 ++-- rust/qemu-api/src/sysbus.rs | 13 ++-- rust/qemu-api/tests/tests.rs | 4 +- rust/qemu-api/tests/vmstate_tests.rs | 2 +- 31 files changed, 244 insertions(+), 120 deletions(-) create mode 100644 rust/bql/Cargo.toml create mode 120000 rust/bql/build.rs create mode 100644 rust/bql/meson.build create mode 100644 rust/bql/src/bindings.rs rename rust/{qemu-api => bql}/src/cell.rs (92%) create mode 100644 rust/bql/src/lib.rs create mode 100644 rust/bql/wrapper.h diff --git a/MAINTAINERS b/MAINTAINERS index 76dcf6ceb2375..a55d5c95d7334 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3515,6 +3515,7 @@ F: include/hw/registerfields.h Rust M: Manos Pitsidianakis S: Maintained +F: rust/bql/ F: rust/common/ F: rust/migration/ F: rust/qemu-api diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 048dd74757f09..73ca9582a5672 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -44,6 +44,13 @@ dependencies = [ "qemu_api_macros", ] +[[package]] +name = "bql" +version = "0.1.0" +dependencies = [ + "migration", +] + [[package]] name = "common" version = "0.1.0" @@ -70,6 +77,7 @@ dependencies = [ name = "hpet" version = "0.1.0" dependencies = [ + "bql", "common", "migration", "qemu_api", @@ -108,6 +116,7 @@ dependencies = [ "bilge", "bilge-impl", "bits", + "bql", "common", "migration", "qemu_api", @@ -151,6 +160,7 @@ dependencies = [ name = "qemu_api" version = "0.1.0" dependencies = [ + "bql", "common", "migration", "qemu_api_macros", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index e0958ef28a861..8be90da8ff76d 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -2,6 +2,7 @@ resolver = "2" members = [ "bits", + "bql", "common", "migration", "qemu-api-macros", diff --git a/rust/bql/Cargo.toml b/rust/bql/Cargo.toml new file mode 100644 index 0000000000000..1041bd4ea93d8 --- /dev/null +++ b/rust/bql/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "bql" +version = "0.1.0" +description = "Rust bindings for QEMU/BQL" +resolver = "2" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +migration = { path = "../migration" } + +[features] +default = ["debug_cell"] +debug_cell = [] + +[lints] +workspace = true diff --git a/rust/bql/build.rs b/rust/bql/build.rs new file mode 120000 index 0000000000000..71a3167885c23 --- /dev/null +++ b/rust/bql/build.rs @@ -0,0 +1 @@ +../util/build.rs \ No newline at end of file diff --git a/rust/bql/meson.build b/rust/bql/meson.build new file mode 100644 index 0000000000000..f369209dfddce --- /dev/null +++ b/rust/bql/meson.build @@ -0,0 +1,52 @@ +_bql_cfg = run_command(rustc_args, + '--config-headers', config_host_h, '--features', files('Cargo.toml'), + capture: true, check: true).stdout().strip().splitlines() + +if get_option('debug_mutex') + _bql_cfg += ['--cfg', 'feature="debug_cell"'] +endif + +# +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_bql_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common, +) + +_bql_rs = static_library( + 'bql', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/cell.rs', + ], + {'.': _bql_bindings_inc_rs} + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + rust_args: _bql_cfg, + link_with: [_migration_rs], +) + +bql_rs = declare_dependency(link_with: [_bql_rs], + dependencies: [qemuutil]) + +# Doctests are essentially integration tests, so they need the same dependencies. +# Note that running them requires the object files for C code, so place them +# in a separate suite that is run by the "build" CI jobs rather than "check". +rust.doctest('rust-bql-rs-doctests', + _bql_rs, + protocol: 'rust', + dependencies: bql_rs, + suite: ['doc', 'rust']) diff --git a/rust/bql/src/bindings.rs b/rust/bql/src/bindings.rs new file mode 100644 index 0000000000000..9ffff12cded80 --- /dev/null +++ b/rust/bql/src/bindings.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![allow( + dead_code, + improper_ctypes_definitions, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unnecessary_transmutes, + unsafe_op_in_unsafe_fn, + clippy::pedantic, + clippy::restriction, + clippy::style, + clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, + clippy::useless_transmute, + clippy::missing_safety_doc, + clippy::too_many_arguments +)] + +#[cfg(MESON)] +include!("bindings.inc.rs"); + +#[cfg(not(MESON))] +include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); diff --git a/rust/qemu-api/src/cell.rs b/rust/bql/src/cell.rs similarity index 92% rename from rust/qemu-api/src/cell.rs rename to rust/bql/src/cell.rs index b80a0fd80b7b0..25007427edcf9 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/bql/src/cell.rs @@ -75,9 +75,10 @@ //! //! ### Example //! -//! ``` +//! ```ignore +//! # use bql::BqlRefCell; //! # use qemu_api::prelude::*; -//! # use qemu_api::{cell::BqlRefCell, irq::InterruptSource, irq::IRQState}; +//! # use qemu_api::{irq::InterruptSource, irq::IRQState}; //! # use qemu_api::{sysbus::SysBusDevice, qom::Owned, qom::ParentField}; //! # const N_GPIOS: usize = 8; //! # struct PL061Registers { /* ... */ } @@ -141,7 +142,6 @@ //! Multiple immutable borrows are allowed via [`borrow`](BqlRefCell::borrow), //! or a single mutable borrow via [`borrow_mut`](BqlRefCell::borrow_mut). The //! thread will panic if these rules are violated or if the BQL is not held. - use std::{ cell::{Cell, UnsafeCell}, cmp::Ordering, @@ -154,30 +154,6 @@ use std::{ use migration::impl_vmstate_transparent; -use crate::bindings; - -/// An internal function that is used by doctests. -pub fn bql_start_test() { - // SAFETY: integration tests are run with --test-threads=1, while - // unit tests and doctests are not multithreaded and do not have - // any BQL-protected data. Just set bql_locked to true. - unsafe { - bindings::rust_bql_mock_lock(); - } -} - -pub fn bql_locked() -> bool { - // SAFETY: the function does nothing but return a thread-local bool - unsafe { bindings::bql_locked() } -} - -fn bql_block_unlock(increase: bool) { - // SAFETY: this only adjusts a counter - unsafe { - bindings::bql_block_unlock(increase); - } -} - /// A mutable memory location that is protected by the Big QEMU Lock. /// /// # Memory layout @@ -256,8 +232,8 @@ impl BqlCell { /// # Examples /// /// ``` - /// use qemu_api::cell::BqlCell; - /// # qemu_api::cell::bql_start_test(); + /// use bql::BqlCell; + /// # bql::start_test(); /// /// let c = BqlCell::new(5); /// ``` @@ -273,8 +249,8 @@ impl BqlCell { /// # Examples /// /// ``` - /// use qemu_api::cell::BqlCell; - /// # qemu_api::cell::bql_start_test(); + /// use bql::BqlCell; + /// # bql::start_test(); /// /// let c = BqlCell::new(5); /// @@ -291,8 +267,8 @@ impl BqlCell { /// # Examples /// /// ``` - /// use qemu_api::cell::BqlCell; - /// # qemu_api::cell::bql_start_test(); + /// use bql::BqlCell; + /// # bql::start_test(); /// /// let cell = BqlCell::new(5); /// assert_eq!(cell.get(), 5); @@ -301,7 +277,7 @@ impl BqlCell { /// ``` #[inline] pub fn replace(&self, val: T) -> T { - assert!(bql_locked()); + assert!(crate::is_locked()); // SAFETY: This can cause data races if called from multiple threads, // but it won't happen as long as C code accesses the value // under BQL protection only. @@ -313,8 +289,8 @@ impl BqlCell { /// # Examples /// /// ``` - /// use qemu_api::cell::BqlCell; - /// # qemu_api::cell::bql_start_test(); + /// use bql::BqlCell; + /// # bql::start_test(); /// /// let c = BqlCell::new(5); /// let five = c.into_inner(); @@ -322,7 +298,7 @@ impl BqlCell { /// assert_eq!(five, 5); /// ``` pub fn into_inner(self) -> T { - assert!(bql_locked()); + assert!(crate::is_locked()); self.value.into_inner() } } @@ -333,8 +309,8 @@ impl BqlCell { /// # Examples /// /// ``` - /// use qemu_api::cell::BqlCell; - /// # qemu_api::cell::bql_start_test(); + /// use bql::BqlCell; + /// # bql::start_test(); /// /// let c = BqlCell::new(5); /// @@ -342,7 +318,7 @@ impl BqlCell { /// ``` #[inline] pub fn get(&self) -> T { - assert!(bql_locked()); + assert!(crate::is_locked()); // SAFETY: This can cause data races if called from multiple threads, // but it won't happen as long as C code accesses the value // under BQL protection only. @@ -356,8 +332,8 @@ impl BqlCell { /// # Examples /// /// ``` - /// use qemu_api::cell::BqlCell; - /// # qemu_api::cell::bql_start_test(); + /// use bql::BqlCell; + /// # bql::start_test(); /// /// let c = BqlCell::new(5); /// @@ -375,8 +351,8 @@ impl BqlCell { /// # Examples /// /// ``` - /// use qemu_api::cell::BqlCell; - /// # qemu_api::cell::bql_start_test(); + /// use bql::BqlCell; + /// # bql::start_test(); /// /// let c = BqlCell::new(5); /// let five = c.take(); @@ -447,7 +423,7 @@ impl BqlRefCell { /// # Examples /// /// ``` - /// use qemu_api::cell::BqlRefCell; + /// use bql::BqlRefCell; /// /// let c = BqlRefCell::new(5); /// ``` @@ -506,8 +482,8 @@ impl BqlRefCell { /// # Examples /// /// ``` - /// use qemu_api::cell::BqlRefCell; - /// # qemu_api::cell::bql_start_test(); + /// use bql::BqlRefCell; + /// # bql::start_test(); /// /// let c = BqlRefCell::new(5); /// @@ -518,8 +494,8 @@ impl BqlRefCell { /// An example of panic: /// /// ```should_panic - /// use qemu_api::cell::BqlRefCell; - /// # qemu_api::cell::bql_start_test(); + /// use bql::BqlRefCell; + /// # bql::start_test(); /// /// let c = BqlRefCell::new(5); /// @@ -536,7 +512,7 @@ impl BqlRefCell { self.borrowed_at.set(Some(std::panic::Location::caller())); } - bql_block_unlock(true); + crate::block_unlock(true); // SAFETY: `BorrowRef` ensures that there is only immutable access // to the value while borrowed. @@ -560,8 +536,8 @@ impl BqlRefCell { /// # Examples /// /// ``` - /// use qemu_api::cell::BqlRefCell; - /// # qemu_api::cell::bql_start_test(); + /// use bql::BqlRefCell; + /// # bql::start_test(); /// /// let c = BqlRefCell::new("hello".to_owned()); /// @@ -573,8 +549,8 @@ impl BqlRefCell { /// An example of panic: /// /// ```should_panic - /// use qemu_api::cell::BqlRefCell; - /// # qemu_api::cell::bql_start_test(); + /// use bql::BqlRefCell; + /// # bql::start_test(); /// /// let c = BqlRefCell::new(5); /// let m = c.borrow(); @@ -591,7 +567,7 @@ impl BqlRefCell { } // SAFETY: this only adjusts a counter - bql_block_unlock(true); + crate::block_unlock(true); // SAFETY: `BorrowRefMut` guarantees unique access. let value = unsafe { NonNull::new_unchecked(self.value.get()) }; @@ -610,7 +586,7 @@ impl BqlRefCell { /// # Examples /// /// ``` - /// use qemu_api::cell::BqlRefCell; + /// use bql::BqlRefCell; /// /// let c = BqlRefCell::new(5); /// @@ -737,7 +713,7 @@ impl Drop for BorrowRef<'_> { let borrow = self.borrow.get(); debug_assert!(is_reading(borrow)); self.borrow.set(borrow - 1); - bql_block_unlock(false) + crate::block_unlock(false) } } @@ -827,7 +803,7 @@ impl Drop for BorrowRefMut<'_> { let borrow = self.borrow.get(); debug_assert!(is_writing(borrow)); self.borrow.set(borrow + 1); - bql_block_unlock(false) + crate::block_unlock(false) } } diff --git a/rust/bql/src/lib.rs b/rust/bql/src/lib.rs new file mode 100644 index 0000000000000..ef08221e9c1a0 --- /dev/null +++ b/rust/bql/src/lib.rs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +mod bindings; +use bindings::{bql_block_unlock, bql_locked, rust_bql_mock_lock}; + +mod cell; +pub use cell::*; + +/// An internal function that is used by doctests. +pub fn start_test() { + // SAFETY: integration tests are run with --test-threads=1, while + // unit tests and doctests are not multithreaded and do not have + // any BQL-protected data. Just set bql_locked to true. + unsafe { + rust_bql_mock_lock(); + } +} + +pub fn is_locked() -> bool { + // SAFETY: the function does nothing but return a thread-local bool + unsafe { bql_locked() } +} + +pub fn block_unlock(increase: bool) { + // SAFETY: this only adjusts a counter + unsafe { + bql_block_unlock(increase); + } +} diff --git a/rust/bql/wrapper.h b/rust/bql/wrapper.h new file mode 100644 index 0000000000000..2ef9a96e1d3c0 --- /dev/null +++ b/rust/bql/wrapper.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * This header file is meant to be used as input to the `bindgen` application + * in order to generate C FFI compatible Rust bindings. + */ + +#ifndef __CLANG_STDATOMIC_H +#define __CLANG_STDATOMIC_H +/* + * Fix potential missing stdatomic.h error in case bindgen does not insert the + * correct libclang header paths on its own. We do not use stdatomic.h symbols + * in QEMU code, so it's fine to declare dummy types instead. + */ +typedef enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst, +} memory_order; +#endif /* __CLANG_STDATOMIC_H */ + +#include "qemu/osdep.h" + +#include "qemu/main-loop.h" diff --git a/rust/common/src/opaque.rs b/rust/common/src/opaque.rs index d25a5f3ae1a11..97ed3e845224b 100644 --- a/rust/common/src/opaque.rs +++ b/rust/common/src/opaque.rs @@ -69,8 +69,8 @@ //! and only at FFI boundaries. For QEMU-specific types that need interior //! mutability, prefer [`BqlCell`] or [`BqlRefCell`]. //! -//! [`BqlCell`]: ../../qemu_api/cell/struct.BqlCell.html -//! [`BqlRefCell`]: ../../qemu_api/cell/struct.BqlRefCell.html +//! [`BqlCell`]: ../../bql/cell/struct.BqlCell.html +//! [`BqlRefCell`]: ../../bql/cell/struct.BqlRefCell.html use std::{cell::UnsafeCell, fmt, marker::PhantomPinned, mem::MaybeUninit, ptr::NonNull}; /// Stores an opaque value that is shared with C code. diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 7fd7531823d75..1a1d4ba7152a7 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -18,6 +18,7 @@ bilge-impl = { version = "0.2.0" } bits = { path = "../../../bits" } common = { path = "../../../common" } util = { path = "../../../util" } +bql = { path = "../../../bql" } migration = { path = "../../../migration" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index 2198fcee9bc1c..8561c4c14ae25 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -11,6 +11,7 @@ _libpl011_rs = static_library( qemu_api_rs, util_rs, migration_rs, + bql_rs, qemu_api_macros, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 225be34e08bbe..00ae432825052 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -4,6 +4,7 @@ use std::{ffi::CStr, mem::size_of}; +use bql::BqlRefCell; use common::{static_assert, uninit_field_mut}; use migration::{ self, impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of, diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index 70acdf03d6e5d..9fcec38bfa688 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -14,6 +14,7 @@ rust-version.workspace = true common = { path = "../../../common" } util = { path = "../../../util" } migration = { path = "../../../migration" } +bql = { path = "../../../bql" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index 8cd70091e631c..43a62db0d011f 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -8,6 +8,7 @@ _libhpet_rs = static_library( qemu_api_rs, util_rs, migration_rs, + bql_rs, qemu_api_macros, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 1c2253466d95b..9658e071c2697 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -10,6 +10,7 @@ use std::{ slice::from_ref, }; +use bql::{BqlCell, BqlRefCell}; use common::{bitops::IntegerExt, uninit_field_mut}; use migration::{ self, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, @@ -20,7 +21,6 @@ use qemu_api::{ address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize, }, - cell::{BqlCell, BqlRefCell}, irq::InterruptSource, memory::{ hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED, diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs index 0605225fbb182..e569b57b93b53 100644 --- a/rust/hw/timer/hpet/src/fw_cfg.rs +++ b/rust/hw/timer/hpet/src/fw_cfg.rs @@ -5,7 +5,6 @@ use std::ptr::addr_of_mut; use common::Zeroable; -use qemu_api::cell::bql_locked; /// Each `HPETState` represents a Event Timer Block. The v1 spec supports /// up to 8 blocks. QEMU only uses 1 block (in PC machine). @@ -38,7 +37,7 @@ pub static mut hpet_fw_cfg: HPETFwConfig = HPETFwConfig { impl HPETFwConfig { pub(crate) fn assign_hpet_id() -> Result { - assert!(bql_locked()); + assert!(bql::is_locked()); // SAFETY: all accesses go through these methods, which guarantee // that the accesses are protected by the BQL. let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) }; @@ -58,7 +57,7 @@ impl HPETFwConfig { } pub(crate) fn update_hpet_cfg(hpet_id: usize, timer_block_id: u32, address: u64) { - assert!(bql_locked()); + assert!(bql::is_locked()); // SAFETY: all accesses go through these methods, which guarantee // that the accesses are protected by the BQL. let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) }; diff --git a/rust/meson.build b/rust/meson.build index 826949b2e60f4..2ba1ea2280dd9 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -27,6 +27,7 @@ subdir('qemu-api-macros') subdir('bits') subdir('util') subdir('migration') +subdir('bql') subdir('qemu-api') subdir('hw') diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs index 537d54e43689e..d714aacb7e3a0 100644 --- a/rust/migration/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -135,8 +135,8 @@ pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags /// [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and /// [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this. /// -/// [`BqlCell`]: ../../qemu_api/cell/struct.BqlCell.html -/// [`BqlRefCell`]: ../../qemu_api/cell/struct.BqlRefCell.html +/// [`BqlCell`]: ../../bql/cell/struct.BqlCell.html +/// [`BqlRefCell`]: ../../bql/cell/struct.BqlRefCell.html /// [`Owned`]: ../../qemu_api/qom/struct.Owned.html #[macro_export] macro_rules! vmstate_of { diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 7276e67aa974c..6e9427f80c8f9 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -17,11 +17,8 @@ rust-version.workspace = true common = { path = "../common" } migration = { path = "../migration" } util = { path = "../util" } +bql = { path = "../bql" } qemu_api_macros = { path = "../qemu-api-macros" } -[features] -default = ["debug_cell"] -debug_cell = [] - [lints] workspace = true diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index a6b5772d19428..a47ee6c1a3d7f 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -2,10 +2,6 @@ _qemu_api_cfg = run_command(rustc_args, '--config-headers', config_host_h, '--features', files('Cargo.toml'), capture: true, check: true).stdout().strip().splitlines() -if get_option('debug_mutex') - _qemu_api_cfg += ['--cfg', 'feature="debug_cell"'] -endif - c_enums = [ 'DeviceCategory', 'GpioPolarity', @@ -51,7 +47,6 @@ _qemu_api_rs = static_library( [ 'src/lib.rs', 'src/bindings.rs', - 'src/cell.rs', 'src/chardev.rs', 'src/irq.rs', 'src/memory.rs', @@ -65,22 +60,13 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, + dependencies: [anyhow_rs, bql_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, util_rs, qom, hwcore, chardev], ) qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], dependencies: [qemu_api_macros, qom, hwcore, chardev, migration]) -# Doctests are essentially integration tests, so they need the same dependencies. -# Note that running them requires the object files for C code, so place them -# in a separate suite that is run by the "build" CI jobs rather than "check". -rust.doctest('rust-qemu-api-doctests', - _qemu_api_rs, - protocol: 'rust', - dependencies: [qemu_api_rs], - suite: ['doc', 'rust']) - test('rust-qemu-api-integration', executable( 'rust-qemu-api-integration', @@ -88,7 +74,7 @@ test('rust-qemu-api-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [common_rs, util_rs, migration_rs, qemu_api_rs]), + dependencies: [bql_rs, common_rs, util_rs, migration_rs, qemu_api_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs index 5a351dcecb0f0..2ec90cc0b2eb7 100644 --- a/rust/qemu-api/src/chardev.rs +++ b/rust/qemu-api/src/chardev.rs @@ -18,13 +18,10 @@ use std::{ slice, }; +use bql::{BqlRefCell, BqlRefMut}; use common::{callbacks::FnCall, errno, Opaque}; -use crate::{ - bindings, - cell::{BqlRefCell, BqlRefMut}, - prelude::*, -}; +use crate::{bindings, prelude::*}; /// A safe wrapper around [`bindings::Chardev`]. #[repr(transparent)] @@ -44,13 +41,15 @@ pub struct CharBackend { _pin: PhantomPinned, } -impl Write for BqlRefMut<'_, bindings::CharBackend> { +pub struct CharBackendMut<'a>(BqlRefMut<'a, bindings::CharBackend>); + +impl Write for CharBackendMut<'_> { fn flush(&mut self) -> io::Result<()> { Ok(()) } fn write(&mut self, buf: &[u8]) -> io::Result { - let chr: &mut bindings::CharBackend = self; + let chr: &mut bindings::CharBackend = &mut self.0; let len = buf.len().try_into().unwrap(); let r = unsafe { bindings::qemu_chr_fe_write(addr_of_mut!(*chr), buf.as_ptr(), len) }; @@ -58,7 +57,7 @@ impl Write for BqlRefMut<'_, bindings::CharBackend> { } fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let chr: &mut bindings::CharBackend = self; + let chr: &mut bindings::CharBackend = &mut self.0; let len = buf.len().try_into().unwrap(); let r = unsafe { bindings::qemu_chr_fe_write_all(addr_of_mut!(*chr), buf.as_ptr(), len) }; @@ -198,7 +197,7 @@ impl CharBackend { /// the big QEMU lock while the character device is borrowed, as /// that might cause C code to write to the character device. pub fn borrow_mut(&self) -> impl Write + '_ { - self.inner.borrow_mut() + CharBackendMut(self.inner.borrow_mut()) } /// Send a continuous stream of zero bits on the line if `enabled` is diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index ea6b32848c50b..3063fbe97aa50 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -10,6 +10,7 @@ use std::{ ptr, }; +use bql::BqlCell; use common::Opaque; use crate::{ diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 55386f66978ab..6cd9e5b99056e 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -13,7 +13,6 @@ pub mod bindings; #[rustfmt::skip] pub mod prelude; -pub mod cell; pub mod chardev; pub mod irq; pub mod memory; diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index c10c171158107..9da7313016d93 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -4,9 +4,6 @@ //! Commonly used traits and types for QEMU. -pub use crate::cell::BqlCell; -pub use crate::cell::BqlRefCell; - pub use crate::qdev::DeviceMethods; pub use crate::qom::InterfaceType; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index c81ae7cf45c53..74a82b8710508 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -16,7 +16,6 @@ use util::{Error, Result}; use crate::{ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, - cell::bql_locked, chardev::Chardev, irq::InterruptSource, prelude::*, @@ -322,7 +321,7 @@ impl DeviceState { cb: Option, events: ClockEvent, ) -> Owned { - assert!(bql_locked()); + assert!(bql::is_locked()); // SAFETY: the clock is heap allocated, but qdev_init_clock_in() // does not gift the reference to its caller; so use Owned::from to @@ -393,7 +392,7 @@ where Self::Target: IsA, { fn prop_set_chr(&self, propname: &str, chr: &Owned) { - assert!(bql_locked()); + assert!(bql::is_locked()); let c_propname = CString::new(propname).unwrap(); let chr: &Chardev = chr; unsafe { diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 7f2f7797e44d6..032701af65241 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -105,12 +105,9 @@ pub use bindings::ObjectClass; use common::Opaque; use migration::impl_vmstate_pointer; -use crate::{ - bindings::{ - self, object_class_dynamic_cast, object_dynamic_cast, object_get_class, - object_get_typename, object_new, object_ref, object_unref, TypeInfo, - }, - cell::bql_locked, +use crate::bindings::{ + self, object_class_dynamic_cast, object_dynamic_cast, object_get_class, object_get_typename, + object_new, object_ref, object_unref, TypeInfo, }; /// A safe wrapper around [`bindings::Object`]. @@ -873,7 +870,7 @@ impl ObjectDeref for Owned {} impl Drop for Owned { fn drop(&mut self) { - assert!(bql_locked()); + assert!(bql::is_locked()); // SAFETY: creation method is unsafe, and whoever calls it has // responsibility that the pointer is valid, and remains valid // throughout the lifetime of the `Owned` and its clones. @@ -897,7 +894,7 @@ impl> fmt::Debug for Owned { pub trait ObjectClassMethods: IsA { /// Return a new reference counted instance of this class fn new() -> Owned { - assert!(bql_locked()); + assert!(bql::is_locked()); // SAFETY: the object created by object_new is allocated on // the heap and has a reference count of 1 unsafe { diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index 2dbfc31dbda13..b21883246e930 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -11,7 +11,6 @@ use common::Opaque; use crate::{ bindings, - cell::bql_locked, irq::{IRQState, InterruptSource}, memory::MemoryRegion, prelude::*, @@ -56,7 +55,7 @@ where /// region with a number that corresponds to the order of calls to /// `init_mmio`. fn init_mmio(&self, iomem: &MemoryRegion) { - assert!(bql_locked()); + assert!(bql::is_locked()); unsafe { bindings::sysbus_init_mmio(self.upcast().as_mut_ptr(), iomem.as_mut_ptr()); } @@ -67,7 +66,7 @@ where /// whoever creates the sysbus device will refer to the interrupts with /// a number that corresponds to the order of calls to `init_irq`. fn init_irq(&self, irq: &InterruptSource) { - assert!(bql_locked()); + assert!(bql::is_locked()); unsafe { bindings::sysbus_init_irq(self.upcast().as_mut_ptr(), irq.as_ptr()); } @@ -75,7 +74,7 @@ where // TODO: do we want a type like GuestAddress here? fn mmio_addr(&self, id: u32) -> Option { - assert!(bql_locked()); + assert!(bql::is_locked()); // SAFETY: the BQL ensures that no one else writes to sbd.mmio[], and // the SysBusDevice must be initialized to get an IsA. let sbd = unsafe { *self.upcast().as_ptr() }; @@ -89,7 +88,7 @@ where // TODO: do we want a type like GuestAddress here? fn mmio_map(&self, id: u32, addr: u64) { - assert!(bql_locked()); + assert!(bql::is_locked()); let id: i32 = id.try_into().unwrap(); unsafe { bindings::sysbus_mmio_map(self.upcast().as_mut_ptr(), id, addr); @@ -100,7 +99,7 @@ where // object_property_set_link) adds a reference to the IRQState, // which can prolong its life fn connect_irq(&self, id: u32, irq: &Owned) { - assert!(bql_locked()); + assert!(bql::is_locked()); let id: i32 = id.try_into().unwrap(); let irq: &IRQState = irq; unsafe { @@ -110,7 +109,7 @@ where fn sysbus_realize(&self) { // TODO: return an Error - assert!(bql_locked()); + assert!(bql::is_locked()); unsafe { bindings::sysbus_realize( self.upcast().as_mut_ptr(), diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 92e3534d3c111..e72ba08aefed0 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -4,9 +4,9 @@ use std::{ffi::CStr, ptr::addr_of}; +use bql::BqlCell; use migration::{VMStateDescription, VMStateDescriptionBuilder}; use qemu_api::{ - cell::{self, BqlCell}, prelude::*, qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl}, qom::{ObjectImpl, ParentField}, @@ -94,7 +94,7 @@ impl DummyChildClass { fn init_qom() { static ONCE: BqlCell = BqlCell::new(false); - cell::bql_start_test(); + bql::start_test(); if !ONCE.get() { unsafe { module_call_init(module_init_type::MODULE_INIT_QOM); diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs index 47fc15149b4d2..fa9bbd6a12227 100644 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -9,6 +9,7 @@ use std::{ slice, }; +use bql::BqlCell; use common::Opaque; use migration::{ bindings::{ @@ -19,7 +20,6 @@ use migration::{ vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField}, vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate, }; -use qemu_api::cell::BqlCell; const FOO_ARRAY_MAX: usize = 3; From fcf4c00b4d73185db9239b1a6f03289f6211e142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:53 +0200 Subject: [PATCH 0745/1794] rust: split "qom" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250827104147.717203-13-marcandre.lureau@redhat.com Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 14 ++++++++++ rust/Cargo.toml | 1 + rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 2 +- rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 1 + rust/hw/timer/hpet/src/device.rs | 3 +-- rust/meson.build | 1 + rust/migration/src/vmstate.rs | 2 +- rust/qemu-api-macros/src/lib.rs | 4 +-- rust/qemu-api-macros/src/tests.rs | 4 +-- rust/qemu-api/Cargo.toml | 1 + rust/qemu-api/meson.build | 15 +++++++---- rust/qemu-api/src/bindings.rs | 1 + rust/qemu-api/src/chardev.rs | 3 ++- rust/qemu-api/src/irq.rs | 10 +++---- rust/qemu-api/src/lib.rs | 1 - rust/qemu-api/src/memory.rs | 7 +++-- rust/qemu-api/src/prelude.rs | 11 -------- rust/qemu-api/src/qdev.rs | 5 ++-- rust/qemu-api/src/sysbus.rs | 4 +-- rust/qemu-api/tests/tests.rs | 3 +-- rust/qom/Cargo.toml | 23 +++++++++++++++++ rust/qom/build.rs | 1 + rust/qom/meson.build | 43 +++++++++++++++++++++++++++++++ rust/qom/src/bindings.rs | 25 ++++++++++++++++++ rust/qom/src/lib.rs | 11 ++++++++ rust/qom/src/prelude.rs | 12 +++++++++ rust/{qemu-api => qom}/src/qom.rs | 4 +-- rust/qom/wrapper.h | 27 +++++++++++++++++++ 32 files changed, 199 insertions(+), 44 deletions(-) create mode 100644 rust/qom/Cargo.toml create mode 120000 rust/qom/build.rs create mode 100644 rust/qom/meson.build create mode 100644 rust/qom/src/bindings.rs create mode 100644 rust/qom/src/lib.rs create mode 100644 rust/qom/src/prelude.rs rename rust/{qemu-api => qom}/src/qom.rs (99%) create mode 100644 rust/qom/wrapper.h diff --git a/MAINTAINERS b/MAINTAINERS index a55d5c95d7334..c7bd02aef166e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3520,6 +3520,7 @@ F: rust/common/ F: rust/migration/ F: rust/qemu-api F: rust/qemu-api-macros +F: rust/qom/ F: rust/rustfmt.toml F: rust/util/ F: scripts/get-wraps-from-cargo-registry.py diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 73ca9582a5672..442eadf08faea 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -82,6 +82,7 @@ dependencies = [ "migration", "qemu_api", "qemu_api_macros", + "qom", "util", ] @@ -121,6 +122,7 @@ dependencies = [ "migration", "qemu_api", "qemu_api_macros", + "qom", "util", ] @@ -164,6 +166,7 @@ dependencies = [ "common", "migration", "qemu_api_macros", + "qom", "util", ] @@ -176,6 +179,17 @@ dependencies = [ "syn", ] +[[package]] +name = "qom" +version = "0.1.0" +dependencies = [ + "bql", + "common", + "migration", + "qemu_api_macros", + "util", +] + [[package]] name = "quote" version = "1.0.36" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 8be90da8ff76d..0516c16591cd3 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -7,6 +7,7 @@ members = [ "migration", "qemu-api-macros", "qemu-api", + "qom", "hw/char/pl011", "hw/timer/hpet", "util", diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 1a1d4ba7152a7..da89f78727fec 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -20,6 +20,7 @@ common = { path = "../../../common" } util = { path = "../../../util" } bql = { path = "../../../bql" } migration = { path = "../../../migration" } +qom = { path = "../../../qom" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index 8561c4c14ae25..af9393c9da9d3 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -13,6 +13,7 @@ _libpl011_rs = static_library( migration_rs, bql_rs, qemu_api_macros, + qom_rs, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 00ae432825052..63651b9dcdf8e 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -16,9 +16,9 @@ use qemu_api::{ memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, - qom::{ObjectImpl, Owned, ParentField, ParentInit}, sysbus::{SysBusDevice, SysBusDeviceImpl}, }; +use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit}; use util::{log::Log, log_mask_ln}; use crate::registers::{self, Interrupt, RegisterOffset}; diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index 9fcec38bfa688..19456ec72b575 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -15,6 +15,7 @@ common = { path = "../../../common" } util = { path = "../../../util" } migration = { path = "../../../migration" } bql = { path = "../../../bql" } +qom = { path = "../../../qom" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index 43a62db0d011f..50ccdee4a9352 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -10,6 +10,7 @@ _libhpet_rs = static_library( migration_rs, bql_rs, qemu_api_macros, + qom_rs, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 9658e071c2697..404569aa2dea3 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -27,10 +27,9 @@ use qemu_api::{ }, prelude::*, qdev::{DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, - qom::{ObjectImpl, ObjectType, ParentField, ParentInit}, - qom_isa, sysbus::{SysBusDevice, SysBusDeviceImpl}, }; +use qom::{prelude::*, ObjectImpl, ParentField, ParentInit}; use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}; use crate::fw_cfg::HPETFwConfig; diff --git a/rust/meson.build b/rust/meson.build index 2ba1ea2280dd9..043603d416986 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -28,6 +28,7 @@ subdir('bits') subdir('util') subdir('migration') subdir('bql') +subdir('qom') subdir('qemu-api') subdir('hw') diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs index d714aacb7e3a0..c05c4a1fd6658 100644 --- a/rust/migration/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -137,7 +137,7 @@ pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags /// /// [`BqlCell`]: ../../bql/cell/struct.BqlCell.html /// [`BqlRefCell`]: ../../bql/cell/struct.BqlRefCell.html -/// [`Owned`]: ../../qemu_api/qom/struct.Owned.html +/// [`Owned`]: ../../qom/qom/struct.Owned.html #[macro_export] macro_rules! vmstate_of { ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => { diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 67650a9a26938..e643e57ebdfd9 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -98,11 +98,11 @@ fn derive_object_or_error(input: DeriveInput) -> Result::ParentType>); + ::qom::ParentField<<#name as ::qom::ObjectImpl>::ParentType>); ::util::module_init! { MODULE_INIT_QOM => unsafe { - ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::qom::ObjectImpl>::TYPE_INFO); + ::qom::type_register_static(&<#name as ::qom::ObjectImpl>::TYPE_INFO); } } }) diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs index 8e71ac6e67783..76e6c57479ec1 100644 --- a/rust/qemu-api-macros/src/tests.rs +++ b/rust/qemu-api-macros/src/tests.rs @@ -168,11 +168,11 @@ fn test_derive_object() { ::common::assert_field_type!( Foo, _unused, - ::qemu_api::qom::ParentField<::ParentType> + ::qom::ParentField<::ParentType> ); ::util::module_init! { MODULE_INIT_QOM => unsafe { - ::qemu_api::bindings::type_register_static(&::TYPE_INFO); + ::qom::type_register_static(&::TYPE_INFO); } } } diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 6e9427f80c8f9..9d11becb284d0 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -18,6 +18,7 @@ common = { path = "../common" } migration = { path = "../migration" } util = { path = "../util" } bql = { path = "../bql" } +qom = { path = "../qom" } qemu_api_macros = { path = "../qemu-api-macros" } [lints] diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index a47ee6c1a3d7f..11e43bb646811 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -22,9 +22,15 @@ foreach enum : c_bitfields _qemu_api_bindgen_args += ['--bitfield-enum', enum] endforeach -_qemu_api_bindgen_args += ['--blocklist-type', 'VMStateDescription'] +blocked_type = [ + 'ObjectClass', + 'VMStateDescription', + 'Error', +] +foreach type: blocked_type + _qemu_api_bindgen_args += ['--blocklist-type', type] +endforeach -_qemu_api_bindgen_args += ['--blocklist-type', 'Error'] # TODO: Remove this comment when the clang/libclang mismatch issue is solved. # # Rust bindings generation with `bindgen` might fail in some cases where the @@ -52,7 +58,6 @@ _qemu_api_rs = static_library( 'src/memory.rs', 'src/prelude.rs', 'src/qdev.rs', - 'src/qom.rs', 'src/sysbus.rs', ], {'.' : _qemu_api_bindings_inc_rs}, @@ -61,7 +66,7 @@ _qemu_api_rs = static_library( rust_abi: 'rust', rust_args: _qemu_api_cfg, dependencies: [anyhow_rs, bql_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, - util_rs, qom, hwcore, chardev], + qom_rs, util_rs, hwcore, chardev], ) qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], @@ -74,7 +79,7 @@ test('rust-qemu-api-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [bql_rs, common_rs, util_rs, migration_rs, qemu_api_rs]), + dependencies: [bql_rs, common_rs, util_rs, migration_rs, qom_rs, qemu_api_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index ce00a6e0e4cf3..525f136ae2bb6 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -22,6 +22,7 @@ use common::Zeroable; use migration::bindings::VMStateDescription; +use qom::bindings::ObjectClass; use util::bindings::Error; #[cfg(MESON)] diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs index 2ec90cc0b2eb7..072d806e4a34a 100644 --- a/rust/qemu-api/src/chardev.rs +++ b/rust/qemu-api/src/chardev.rs @@ -20,8 +20,9 @@ use std::{ use bql::{BqlRefCell, BqlRefMut}; use common::{callbacks::FnCall, errno, Opaque}; +use qom::prelude::*; -use crate::{bindings, prelude::*}; +use crate::bindings; /// A safe wrapper around [`bindings::Chardev`]. #[repr(transparent)] diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index 3063fbe97aa50..fead2bbe8e0a5 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -12,12 +12,9 @@ use std::{ use bql::BqlCell; use common::Opaque; +use qom::{prelude::*, ObjectClass}; -use crate::{ - bindings::{self, qemu_set_irq}, - prelude::*, - qom::ObjectClass, -}; +use crate::bindings::{self, qemu_set_irq}; /// An opaque wrapper around [`bindings::IRQState`]. #[repr(transparent)] @@ -36,7 +33,7 @@ pub struct IRQState(Opaque); /// /// Interrupts are implemented as a pointer to the interrupt "sink", which has /// type [`IRQState`]. A device exposes its source as a QOM link property using -/// a function such as [`SysBusDeviceMethods::init_irq`], and +/// a function such as [`crate::sysbus::SysBusDeviceMethods::init_irq`], and /// initially leaves the pointer to a NULL value, representing an unconnected /// interrupt. To connect it, whoever creates the device fills the pointer with /// the sink's `IRQState *`, for example using `sysbus_connect_irq`. Because @@ -114,4 +111,5 @@ unsafe impl ObjectType for IRQState { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_IRQ) }; } + qom_isa!(IRQState: Object); diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 6cd9e5b99056e..0541050e66745 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -17,7 +17,6 @@ pub mod chardev; pub mod irq; pub mod memory; pub mod qdev; -pub mod qom; pub mod sysbus; // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs index f790cb5fd2d94..ecbbd9b60462c 100644 --- a/rust/qemu-api/src/memory.rs +++ b/rust/qemu-api/src/memory.rs @@ -11,11 +11,9 @@ use std::{ pub use bindings::{hwaddr, MemTxAttrs}; use common::{callbacks::FnCall, uninit::MaybeUninitField, zeroable::Zeroable, Opaque}; +use qom::prelude::*; -use crate::{ - bindings::{self, device_endian, memory_region_init_io}, - prelude::*, -}; +use crate::bindings::{self, device_endian, memory_region_init_io}; pub struct MemoryRegionOps( bindings::MemoryRegionOps, @@ -186,6 +184,7 @@ unsafe impl ObjectType for MemoryRegion { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_MEMORY_REGION) }; } + qom_isa!(MemoryRegion: Object); /// A special `MemTxAttrs` constant, used to indicate that no memory diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 9da7313016d93..9e9d1c824747a 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -6,15 +6,4 @@ pub use crate::qdev::DeviceMethods; -pub use crate::qom::InterfaceType; -pub use crate::qom::IsA; -pub use crate::qom::Object; -pub use crate::qom::ObjectCast; -pub use crate::qom::ObjectClassMethods; -pub use crate::qom::ObjectDeref; -pub use crate::qom::ObjectMethods; -pub use crate::qom::ObjectType; - -pub use crate::qom_isa; - pub use crate::sysbus::SysBusDeviceMethods; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 74a82b8710508..3daf9dda2b54d 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -12,14 +12,13 @@ use std::{ pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; use common::{callbacks::FnCall, Opaque}; use migration::{impl_vmstate_c_struct, VMStateDescription}; +use qom::{prelude::*, ObjectClass, ObjectImpl, Owned, ParentInit}; use util::{Error, Result}; use crate::{ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, chardev::Chardev, irq::InterruptSource, - prelude::*, - qom::{ObjectClass, ObjectImpl, Owned, ParentInit}, }; /// A safe wrapper around [`bindings::Clock`]. @@ -291,6 +290,7 @@ unsafe impl ObjectType for DeviceState { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; } + qom_isa!(DeviceState: Object); /// Initialization methods take a [`ParentInit`] and can be called as @@ -453,6 +453,7 @@ unsafe impl ObjectType for Clock { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) }; } + qom_isa!(Clock: Object); impl_vmstate_c_struct!(Clock, bindings::vmstate_clock); diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index b21883246e930..b883d7eaf18cb 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -8,14 +8,13 @@ use std::{ffi::CStr, ptr::addr_of_mut}; pub use bindings::SysBusDeviceClass; use common::Opaque; +use qom::{prelude::*, Owned}; use crate::{ bindings, irq::{IRQState, InterruptSource}, memory::MemoryRegion, - prelude::*, qdev::{DeviceImpl, DeviceState}, - qom::Owned, }; /// A safe wrapper around [`bindings::SysBusDevice`]. @@ -31,6 +30,7 @@ unsafe impl ObjectType for SysBusDevice { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) }; } + qom_isa!(SysBusDevice: DeviceState, Object); // TODO: add virtual methods diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index e72ba08aefed0..f2e5eb9f4f427 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -7,11 +7,10 @@ use std::{ffi::CStr, ptr::addr_of}; use bql::BqlCell; use migration::{VMStateDescription, VMStateDescriptionBuilder}; use qemu_api::{ - prelude::*, qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl}, - qom::{ObjectImpl, ParentField}, sysbus::SysBusDevice, }; +use qom::{prelude::*, ObjectImpl, ParentField}; use util::bindings::{module_call_init, module_init_type}; mod vmstate_tests; diff --git a/rust/qom/Cargo.toml b/rust/qom/Cargo.toml new file mode 100644 index 0000000000000..46bbf7c7fe42b --- /dev/null +++ b/rust/qom/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "qom" +version = "0.1.0" +description = "Rust bindings for QEMU/QOM" +resolver = "2" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +common = { path = "../common" } +bql = { path = "../bql" } +migration = { path = "../migration" } +qemu_api_macros = { path = "../qemu-api-macros" } +util = { path = "../util" } + +[lints] +workspace = true diff --git a/rust/qom/build.rs b/rust/qom/build.rs new file mode 120000 index 0000000000000..71a3167885c23 --- /dev/null +++ b/rust/qom/build.rs @@ -0,0 +1 @@ +../util/build.rs \ No newline at end of file diff --git a/rust/qom/meson.build b/rust/qom/meson.build new file mode 100644 index 0000000000000..84a65cb737ecb --- /dev/null +++ b/rust/qom/meson.build @@ -0,0 +1,43 @@ +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_qom_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common, +) + +_qom_rs = static_library( + 'qom', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/prelude.rs', + 'src/qom.rs', + ], + {'.': _qom_bindings_inc_rs} + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + link_with: [_bql_rs, _migration_rs], + dependencies: [common_rs, qemu_api_macros], +) + +qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_api_macros, qom]) + +# Doctests are essentially integration tests, so they need the same dependencies. +# Note that running them requires the object files for C code, so place them +# in a separate suite that is run by the "build" CI jobs rather than "check". +rust.doctest('rust-qom-rs-doctests', + _qom_rs, + protocol: 'rust', + dependencies: qom_rs, + suite: ['doc', 'rust']) diff --git a/rust/qom/src/bindings.rs b/rust/qom/src/bindings.rs new file mode 100644 index 0000000000000..9ffff12cded80 --- /dev/null +++ b/rust/qom/src/bindings.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![allow( + dead_code, + improper_ctypes_definitions, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unnecessary_transmutes, + unsafe_op_in_unsafe_fn, + clippy::pedantic, + clippy::restriction, + clippy::style, + clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, + clippy::useless_transmute, + clippy::missing_safety_doc, + clippy::too_many_arguments +)] + +#[cfg(MESON)] +include!("bindings.inc.rs"); + +#[cfg(not(MESON))] +include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); diff --git a/rust/qom/src/lib.rs b/rust/qom/src/lib.rs new file mode 100644 index 0000000000000..204c6fea2ffea --- /dev/null +++ b/rust/qom/src/lib.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pub mod bindings; + +// preserve one-item-per-"use" syntax, it is clearer +// for prelude-like modules +#[rustfmt::skip] +pub mod prelude; + +mod qom; +pub use qom::*; diff --git a/rust/qom/src/prelude.rs b/rust/qom/src/prelude.rs new file mode 100644 index 0000000000000..00a6095977110 --- /dev/null +++ b/rust/qom/src/prelude.rs @@ -0,0 +1,12 @@ +//! Traits and essential types intended for blanket imports. + +pub use crate::qom::InterfaceType; +pub use crate::qom::IsA; +pub use crate::qom::Object; +pub use crate::qom::ObjectCast; +pub use crate::qom::ObjectClassMethods; +pub use crate::qom::ObjectDeref; +pub use crate::qom::ObjectMethods; +pub use crate::qom::ObjectType; + +pub use crate::qom_isa; diff --git a/rust/qemu-api/src/qom.rs b/rust/qom/src/qom.rs similarity index 99% rename from rust/qemu-api/src/qom.rs rename to rust/qom/src/qom.rs index 032701af65241..3ea1ad9c5bebd 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qom/src/qom.rs @@ -101,7 +101,6 @@ use std::{ ptr::NonNull, }; -pub use bindings::ObjectClass; use common::Opaque; use migration::impl_vmstate_pointer; @@ -109,6 +108,7 @@ use crate::bindings::{ self, object_class_dynamic_cast, object_dynamic_cast, object_get_class, object_get_typename, object_new, object_ref, object_unref, TypeInfo, }; +pub use crate::bindings::{type_register_static, ObjectClass}; /// A safe wrapper around [`bindings::Object`]. #[repr(transparent)] @@ -146,7 +146,7 @@ macro_rules! qom_isa { $( // SAFETY: it is the caller responsibility to have $parent as the // first field - unsafe impl $crate::qom::IsA<$parent> for $struct {} + unsafe impl $crate::IsA<$parent> for $struct {} impl AsRef<$parent> for $struct { fn as_ref(&self) -> &$parent { diff --git a/rust/qom/wrapper.h b/rust/qom/wrapper.h new file mode 100644 index 0000000000000..3b71bcd3f5bfa --- /dev/null +++ b/rust/qom/wrapper.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * This header file is meant to be used as input to the `bindgen` application + * in order to generate C FFI compatible Rust bindings. + */ + +#ifndef __CLANG_STDATOMIC_H +#define __CLANG_STDATOMIC_H +/* + * Fix potential missing stdatomic.h error in case bindgen does not insert the + * correct libclang header paths on its own. We do not use stdatomic.h symbols + * in QEMU code, so it's fine to declare dummy types instead. + */ +typedef enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst, +} memory_order; +#endif /* __CLANG_STDATOMIC_H */ + +#include "qemu/osdep.h" + +#include "qom/object.h" From fef932ef09c82c3831ff3336d1b2d566cd6ccae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:54 +0200 Subject: [PATCH 0746/1794] rust: split "chardev" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-14-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 14 ++++++++ rust/chardev/Cargo.toml | 24 +++++++++++++ rust/chardev/build.rs | 1 + rust/chardev/meson.build | 41 +++++++++++++++++++++++ rust/chardev/src/bindings.rs | 36 ++++++++++++++++++++ rust/{qemu-api => chardev}/src/chardev.rs | 0 rust/chardev/src/lib.rs | 6 ++++ rust/chardev/wrapper.h | 28 ++++++++++++++++ rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 2 +- rust/meson.build | 1 + rust/qemu-api/Cargo.toml | 1 + rust/qemu-api/meson.build | 9 +++-- rust/qemu-api/src/bindings.rs | 9 +---- rust/qemu-api/src/lib.rs | 1 - rust/qemu-api/src/qdev.rs | 6 ++-- rust/qemu-api/wrapper.h | 2 -- 19 files changed, 164 insertions(+), 20 deletions(-) create mode 100644 rust/chardev/Cargo.toml create mode 120000 rust/chardev/build.rs create mode 100644 rust/chardev/meson.build create mode 100644 rust/chardev/src/bindings.rs rename rust/{qemu-api => chardev}/src/chardev.rs (100%) create mode 100644 rust/chardev/src/lib.rs create mode 100644 rust/chardev/wrapper.h diff --git a/MAINTAINERS b/MAINTAINERS index c7bd02aef166e..cac6dcdc658da 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3516,6 +3516,7 @@ Rust M: Manos Pitsidianakis S: Maintained F: rust/bql/ +F: rust/chardev/ F: rust/common/ F: rust/migration/ F: rust/qemu-api diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 442eadf08faea..ae852c55508b2 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -51,6 +51,18 @@ dependencies = [ "migration", ] +[[package]] +name = "chardev" +version = "0.1.0" +dependencies = [ + "bql", + "common", + "migration", + "qemu_api_macros", + "qom", + "util", +] + [[package]] name = "common" version = "0.1.0" @@ -118,6 +130,7 @@ dependencies = [ "bilge-impl", "bits", "bql", + "chardev", "common", "migration", "qemu_api", @@ -163,6 +176,7 @@ name = "qemu_api" version = "0.1.0" dependencies = [ "bql", + "chardev", "common", "migration", "qemu_api_macros", diff --git a/rust/chardev/Cargo.toml b/rust/chardev/Cargo.toml new file mode 100644 index 0000000000000..7df9c677fc7b6 --- /dev/null +++ b/rust/chardev/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "chardev" +version = "0.1.0" +description = "Rust bindings for QEMU/chardev" +resolver = "2" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +common = { path = "../common" } +bql = { path = "../bql" } +migration = { path = "../migration" } +qom = { path = "../qom" } +util = { path = "../util" } +qemu_api_macros = { path = "../qemu-api-macros" } + +[lints] +workspace = true diff --git a/rust/chardev/build.rs b/rust/chardev/build.rs new file mode 120000 index 0000000000000..71a3167885c23 --- /dev/null +++ b/rust/chardev/build.rs @@ -0,0 +1 @@ +../util/build.rs \ No newline at end of file diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build new file mode 100644 index 0000000000000..5d333e232bdc1 --- /dev/null +++ b/rust/chardev/meson.build @@ -0,0 +1,41 @@ +c_enums = [ + 'QEMUChrEvent', +] +_chardev_bindgen_args = [] +foreach enum : c_enums + _chardev_bindgen_args += ['--rustified-enum', enum] +endforeach + +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_chardev_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common + _chardev_bindgen_args, +) + +_chardev_rs = static_library( + 'chardev', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/chardev.rs', + ], + {'.': _chardev_bindings_inc_rs} + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], + dependencies: [common_rs, qemu_api_macros], +) + +chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_api_macros, chardev, qemuutil]) diff --git a/rust/chardev/src/bindings.rs b/rust/chardev/src/bindings.rs new file mode 100644 index 0000000000000..2d98026d62752 --- /dev/null +++ b/rust/chardev/src/bindings.rs @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![allow( + dead_code, + improper_ctypes_definitions, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unnecessary_transmutes, + unsafe_op_in_unsafe_fn, + clippy::pedantic, + clippy::restriction, + clippy::style, + clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, + clippy::useless_transmute, + clippy::missing_safety_doc, + clippy::too_many_arguments +)] + +use common::Zeroable; + +#[cfg(MESON)] +include!("bindings.inc.rs"); + +#[cfg(not(MESON))] +include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); + +// SAFETY: these are implemented in C; the bindings need to assert that the +// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`. +// When bindings for character devices are introduced, this can be +// moved to the Opaque<> wrapper in src/chardev.rs. +unsafe impl Send for CharBackend {} +unsafe impl Sync for CharBackend {} + +unsafe impl Zeroable for CharBackend {} diff --git a/rust/qemu-api/src/chardev.rs b/rust/chardev/src/chardev.rs similarity index 100% rename from rust/qemu-api/src/chardev.rs rename to rust/chardev/src/chardev.rs diff --git a/rust/chardev/src/lib.rs b/rust/chardev/src/lib.rs new file mode 100644 index 0000000000000..2e549f99d9139 --- /dev/null +++ b/rust/chardev/src/lib.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pub mod bindings; + +mod chardev; +pub use chardev::*; diff --git a/rust/chardev/wrapper.h b/rust/chardev/wrapper.h new file mode 100644 index 0000000000000..65ede6ea6d7d1 --- /dev/null +++ b/rust/chardev/wrapper.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * This header file is meant to be used as input to the `bindgen` application + * in order to generate C FFI compatible Rust bindings. + */ + +#ifndef __CLANG_STDATOMIC_H +#define __CLANG_STDATOMIC_H +/* + * Fix potential missing stdatomic.h error in case bindgen does not insert the + * correct libclang header paths on its own. We do not use stdatomic.h symbols + * in QEMU code, so it's fine to declare dummy types instead. + */ +typedef enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst, +} memory_order; +#endif /* __CLANG_STDATOMIC_H */ + +#include "qemu/osdep.h" + +#include "chardev/char-fe.h" +#include "chardev/char-serial.h" diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index da89f78727fec..f7ad5f8e08f26 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -21,6 +21,7 @@ util = { path = "../../../util" } bql = { path = "../../../bql" } migration = { path = "../../../migration" } qom = { path = "../../../qom" } +chardev = { path = "../../../chardev" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index af9393c9da9d3..aaf911c5f428d 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -14,6 +14,7 @@ _libpl011_rs = static_library( bql_rs, qemu_api_macros, qom_rs, + chardev_rs, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 63651b9dcdf8e..bc64061fb397b 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -5,13 +5,13 @@ use std::{ffi::CStr, mem::size_of}; use bql::BqlRefCell; +use chardev::{CharBackend, Chardev, Event}; use common::{static_assert, uninit_field_mut}; use migration::{ self, impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused, VMStateDescription, VMStateDescriptionBuilder, }; use qemu_api::{ - chardev::{CharBackend, Chardev, Event}, irq::{IRQState, InterruptSource}, memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, diff --git a/rust/meson.build b/rust/meson.build index 043603d416986..4d9e2912232e8 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -29,6 +29,7 @@ subdir('util') subdir('migration') subdir('bql') subdir('qom') +subdir('chardev') subdir('qemu-api') subdir('hw') diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 9d11becb284d0..3bf2dafa6df3f 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -15,6 +15,7 @@ rust-version.workspace = true [dependencies] common = { path = "../common" } +chardev = { path = "../chardev" } migration = { path = "../migration" } util = { path = "../util" } bql = { path = "../bql" } diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 11e43bb646811..a47f178b69835 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -7,7 +7,6 @@ c_enums = [ 'GpioPolarity', 'MachineInitPhase', 'MemoryDeviceInfoKind', - 'QEMUChrEvent', 'ResetType', 'device_endian', ] @@ -23,9 +22,10 @@ foreach enum : c_bitfields endforeach blocked_type = [ + 'Chardev', + 'Error', 'ObjectClass', 'VMStateDescription', - 'Error', ] foreach type: blocked_type _qemu_api_bindgen_args += ['--blocklist-type', type] @@ -53,7 +53,6 @@ _qemu_api_rs = static_library( [ 'src/lib.rs', 'src/bindings.rs', - 'src/chardev.rs', 'src/irq.rs', 'src/memory.rs', 'src/prelude.rs', @@ -65,8 +64,8 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, bql_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, - qom_rs, util_rs, hwcore, chardev], + dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, + qom_rs, util_rs, hwcore], ) qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 525f136ae2bb6..526bcf8e31b5f 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -20,6 +20,7 @@ //! `bindgen`-generated declarations. +use chardev::bindings::Chardev; use common::Zeroable; use migration::bindings::VMStateDescription; use qom::bindings::ObjectClass; @@ -31,13 +32,6 @@ include!("bindings.inc.rs"); #[cfg(not(MESON))] include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); -// SAFETY: these are implemented in C; the bindings need to assert that the -// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`. -// When bindings for character devices are introduced, this can be -// moved to the Opaque<> wrapper in src/chardev.rs. -unsafe impl Send for CharBackend {} -unsafe impl Sync for CharBackend {} - // SAFETY: this is a pure data struct unsafe impl Send for CoalescedMemoryRange {} unsafe impl Sync for CoalescedMemoryRange {} @@ -59,4 +53,3 @@ unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {} unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {} unsafe impl Zeroable for crate::bindings::MemoryRegionOps {} unsafe impl Zeroable for crate::bindings::MemTxAttrs {} -unsafe impl Zeroable for crate::bindings::CharBackend {} diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 0541050e66745..d96096899d01b 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -13,7 +13,6 @@ pub mod bindings; #[rustfmt::skip] pub mod prelude; -pub mod chardev; pub mod irq; pub mod memory; pub mod qdev; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 3daf9dda2b54d..7efc796f50222 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -10,6 +10,7 @@ use std::{ }; pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; +use chardev::Chardev; use common::{callbacks::FnCall, Opaque}; use migration::{impl_vmstate_c_struct, VMStateDescription}; use qom::{prelude::*, ObjectClass, ObjectImpl, Owned, ParentInit}; @@ -17,7 +18,6 @@ use util::{Error, Result}; use crate::{ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, - chardev::Chardev, irq::InterruptSource, }; @@ -137,8 +137,8 @@ unsafe impl QDevProp for u64 { const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_uint64 }; } -/// Use [`bindings::qdev_prop_chr`] for [`crate::chardev::CharBackend`]. -unsafe impl QDevProp for crate::chardev::CharBackend { +/// Use [`bindings::qdev_prop_chr`] for [`chardev::CharBackend`]. +unsafe impl QDevProp for chardev::CharBackend { const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_chr }; } diff --git a/rust/qemu-api/wrapper.h b/rust/qemu-api/wrapper.h index b99df9f568a30..07dbc9987a847 100644 --- a/rust/qemu-api/wrapper.h +++ b/rust/qemu-api/wrapper.h @@ -52,13 +52,11 @@ typedef enum memory_order { #include "system/system.h" #include "hw/sysbus.h" #include "system/memory.h" -#include "chardev/char-fe.h" #include "hw/clock.h" #include "hw/qdev-clock.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "hw/irq.h" -#include "chardev/char-serial.h" #include "exec/memattrs.h" #include "system/address-spaces.h" #include "hw/char/pl011.h" From ee4ffbf239cbd9de8c6b6cc33283b7a64a95a956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:55 +0200 Subject: [PATCH 0747/1794] rust: split "system" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-15-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 13 ++++++++ rust/Cargo.toml | 1 + rust/bql/src/cell.rs | 5 ++- rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 2 +- rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 1 + rust/hw/timer/hpet/src/device.rs | 12 +++---- rust/meson.build | 1 + rust/qemu-api/Cargo.toml | 1 + rust/qemu-api/meson.build | 7 +++-- rust/qemu-api/src/bindings.rs | 14 +-------- rust/qemu-api/src/lib.rs | 1 - rust/qemu-api/src/sysbus.rs | 2 +- rust/qemu-api/wrapper.h | 3 -- rust/system/Cargo.toml | 22 +++++++++++++ rust/system/build.rs | 1 + rust/system/meson.build | 42 +++++++++++++++++++++++++ rust/system/src/bindings.rs | 41 ++++++++++++++++++++++++ rust/system/src/lib.rs | 6 ++++ rust/{qemu-api => system}/src/memory.rs | 2 +- rust/system/wrapper.h | 29 +++++++++++++++++ 24 files changed, 177 insertions(+), 33 deletions(-) create mode 100644 rust/system/Cargo.toml create mode 120000 rust/system/build.rs create mode 100644 rust/system/meson.build create mode 100644 rust/system/src/bindings.rs create mode 100644 rust/system/src/lib.rs rename rust/{qemu-api => system}/src/memory.rs (99%) create mode 100644 rust/system/wrapper.h diff --git a/MAINTAINERS b/MAINTAINERS index cac6dcdc658da..432ed5135450c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3523,6 +3523,7 @@ F: rust/qemu-api F: rust/qemu-api-macros F: rust/qom/ F: rust/rustfmt.toml +F: rust/system/ F: rust/util/ F: scripts/get-wraps-from-cargo-registry.py diff --git a/rust/Cargo.lock b/rust/Cargo.lock index ae852c55508b2..e6b75f30bef48 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -95,6 +95,7 @@ dependencies = [ "qemu_api", "qemu_api_macros", "qom", + "system", "util", ] @@ -136,6 +137,7 @@ dependencies = [ "qemu_api", "qemu_api_macros", "qom", + "system", "util", ] @@ -181,6 +183,7 @@ dependencies = [ "migration", "qemu_api_macros", "qom", + "system", "util", ] @@ -224,6 +227,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "system" +version = "0.1.0" +dependencies = [ + "common", + "qemu_api_macros", + "qom", + "util", +] + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 0516c16591cd3..8e210d277a2be 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -8,6 +8,7 @@ members = [ "qemu-api-macros", "qemu-api", "qom", + "system", "hw/char/pl011", "hw/timer/hpet", "util", diff --git a/rust/bql/src/cell.rs b/rust/bql/src/cell.rs index 25007427edcf9..24ab294b60d08 100644 --- a/rust/bql/src/cell.rs +++ b/rust/bql/src/cell.rs @@ -77,9 +77,8 @@ //! //! ```ignore //! # use bql::BqlRefCell; -//! # use qemu_api::prelude::*; -//! # use qemu_api::{irq::InterruptSource, irq::IRQState}; -//! # use qemu_api::{sysbus::SysBusDevice, qom::Owned, qom::ParentField}; +//! # use qom::{Owned, ParentField}; +//! # use system::{InterruptSource, IRQState, SysBusDevice}; //! # const N_GPIOS: usize = 8; //! # struct PL061Registers { /* ... */ } //! # unsafe impl ObjectType for PL061State { diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index f7ad5f8e08f26..e4b1c3f1eb8e9 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -22,6 +22,7 @@ bql = { path = "../../../bql" } migration = { path = "../../../migration" } qom = { path = "../../../qom" } chardev = { path = "../../../chardev" } +system = { path = "../../../system" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index aaf911c5f428d..fae6e1b9c9d50 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -15,6 +15,7 @@ _libpl011_rs = static_library( qemu_api_macros, qom_rs, chardev_rs, + system_rs, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index bc64061fb397b..c65db5a5174c3 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -13,12 +13,12 @@ use migration::{ }; use qemu_api::{ irq::{IRQState, InterruptSource}, - memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, sysbus::{SysBusDevice, SysBusDeviceImpl}, }; use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit}; +use system::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}; use util::{log::Log, log_mask_ln}; use crate::registers::{self, Interrupt, RegisterOffset}; diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index 19456ec72b575..a95b1271c643f 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -16,6 +16,7 @@ util = { path = "../../../util" } migration = { path = "../../../migration" } bql = { path = "../../../bql" } qom = { path = "../../../qom" } +system = { path = "../../../system" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index 50ccdee4a9352..c4ffe020f6bed 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -11,6 +11,7 @@ _libhpet_rs = static_library( bql_rs, qemu_api_macros, qom_rs, + system_rs, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 404569aa2dea3..841c2ba337531 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -17,19 +17,17 @@ use migration::{ VMStateDescription, VMStateDescriptionBuilder, }; use qemu_api::{ - bindings::{ - address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool, - qdev_prop_uint32, qdev_prop_usize, - }, + bindings::{qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize}, irq::InterruptSource, - memory::{ - hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED, - }, prelude::*, qdev::{DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, sysbus::{SysBusDevice, SysBusDeviceImpl}, }; use qom::{prelude::*, ObjectImpl, ParentField, ParentInit}; +use system::{ + bindings::{address_space_memory, address_space_stl_le, hwaddr}, + MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED, +}; use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}; use crate::fw_cfg::HPETFwConfig; diff --git a/rust/meson.build b/rust/meson.build index 4d9e2912232e8..d8b71f550614a 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -29,6 +29,7 @@ subdir('util') subdir('migration') subdir('bql') subdir('qom') +subdir('system') subdir('chardev') subdir('qemu-api') diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 3bf2dafa6df3f..2884c1d460b08 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -20,6 +20,7 @@ migration = { path = "../migration" } util = { path = "../util" } bql = { path = "../bql" } qom = { path = "../qom" } +system = { path = "../system" } qemu_api_macros = { path = "../qemu-api-macros" } [lints] diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index a47f178b69835..92e2581a64ea9 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -8,7 +8,6 @@ c_enums = [ 'MachineInitPhase', 'MemoryDeviceInfoKind', 'ResetType', - 'device_endian', ] _qemu_api_bindgen_args = [] foreach enum : c_enums @@ -24,8 +23,11 @@ endforeach blocked_type = [ 'Chardev', 'Error', + 'MemTxAttrs', + 'MemoryRegion', 'ObjectClass', 'VMStateDescription', + 'device_endian', ] foreach type: blocked_type _qemu_api_bindgen_args += ['--blocklist-type', type] @@ -54,7 +56,6 @@ _qemu_api_rs = static_library( 'src/lib.rs', 'src/bindings.rs', 'src/irq.rs', - 'src/memory.rs', 'src/prelude.rs', 'src/qdev.rs', 'src/sysbus.rs', @@ -65,7 +66,7 @@ _qemu_api_rs = static_library( rust_abi: 'rust', rust_args: _qemu_api_cfg, dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, - qom_rs, util_rs, hwcore], + qom_rs, system_rs, util_rs, hwcore], ) qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 526bcf8e31b5f..63b805c76e4d1 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -24,6 +24,7 @@ use chardev::bindings::Chardev; use common::Zeroable; use migration::bindings::VMStateDescription; use qom::bindings::ObjectClass; +use system::bindings::{device_endian, MemTxAttrs, MemoryRegion}; use util::bindings::Error; #[cfg(MESON)] @@ -32,15 +33,6 @@ include!("bindings.inc.rs"); #[cfg(not(MESON))] include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); -// SAFETY: this is a pure data struct -unsafe impl Send for CoalescedMemoryRange {} -unsafe impl Sync for CoalescedMemoryRange {} - -// SAFETY: these are constants and vtables; the Send and Sync requirements -// are deferred to the unsafe callbacks that they contain -unsafe impl Send for MemoryRegionOps {} -unsafe impl Sync for MemoryRegionOps {} - unsafe impl Send for Property {} unsafe impl Sync for Property {} @@ -49,7 +41,3 @@ unsafe impl Sync for TypeInfo {} unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {} unsafe impl Zeroable for crate::bindings::Property {} -unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {} -unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {} -unsafe impl Zeroable for crate::bindings::MemoryRegionOps {} -unsafe impl Zeroable for crate::bindings::MemTxAttrs {} diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index d96096899d01b..8d574404789a8 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -14,7 +14,6 @@ pub mod bindings; pub mod prelude; pub mod irq; -pub mod memory; pub mod qdev; pub mod sysbus; diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index b883d7eaf18cb..dda71ebda7089 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -9,11 +9,11 @@ use std::{ffi::CStr, ptr::addr_of_mut}; pub use bindings::SysBusDeviceClass; use common::Opaque; use qom::{prelude::*, Owned}; +use system::MemoryRegion; use crate::{ bindings, irq::{IRQState, InterruptSource}, - memory::MemoryRegion, qdev::{DeviceImpl, DeviceState}, }; diff --git a/rust/qemu-api/wrapper.h b/rust/qemu-api/wrapper.h index 07dbc9987a847..564733b90352f 100644 --- a/rust/qemu-api/wrapper.h +++ b/rust/qemu-api/wrapper.h @@ -49,14 +49,11 @@ typedef enum memory_order { #include "qemu/osdep.h" #include "qemu-io.h" -#include "system/system.h" #include "hw/sysbus.h" -#include "system/memory.h" #include "hw/clock.h" #include "hw/qdev-clock.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "hw/irq.h" #include "exec/memattrs.h" -#include "system/address-spaces.h" #include "hw/char/pl011.h" diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml new file mode 100644 index 0000000000000..6803895e080cd --- /dev/null +++ b/rust/system/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "system" +version = "0.1.0" +description = "Rust bindings for QEMU/system" +resolver = "2" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +common = { path = "../common" } +qom = { path = "../qom" } +util = { path = "../util" } +qemu_api_macros = { path = "../qemu-api-macros" } + +[lints] +workspace = true diff --git a/rust/system/build.rs b/rust/system/build.rs new file mode 120000 index 0000000000000..71a3167885c23 --- /dev/null +++ b/rust/system/build.rs @@ -0,0 +1 @@ +../util/build.rs \ No newline at end of file diff --git a/rust/system/meson.build b/rust/system/meson.build new file mode 100644 index 0000000000000..ae9b932d29da7 --- /dev/null +++ b/rust/system/meson.build @@ -0,0 +1,42 @@ +c_enums = [ + 'device_endian', +] +_system_bindgen_args = [] +foreach enum : c_enums + _system_bindgen_args += ['--rustified-enum', enum] +endforeach + +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_system_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common + _system_bindgen_args, +) + +_system_rs = static_library( + 'system', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/memory.rs', + ], + {'.': _system_bindings_inc_rs} + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], + dependencies: [common_rs, qemu_api_macros], +) + +system_rs = declare_dependency(link_with: [_system_rs], + dependencies: [qemu_api_macros, hwcore]) diff --git a/rust/system/src/bindings.rs b/rust/system/src/bindings.rs new file mode 100644 index 0000000000000..43edd98807adb --- /dev/null +++ b/rust/system/src/bindings.rs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![allow( + dead_code, + improper_ctypes_definitions, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unnecessary_transmutes, + unsafe_op_in_unsafe_fn, + clippy::pedantic, + clippy::restriction, + clippy::style, + clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, + clippy::useless_transmute, + clippy::missing_safety_doc, + clippy::too_many_arguments +)] + +use common::Zeroable; + +#[cfg(MESON)] +include!("bindings.inc.rs"); + +#[cfg(not(MESON))] +include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); + +// SAFETY: these are constants and vtables; the Send and Sync requirements +// are deferred to the unsafe callbacks that they contain +unsafe impl Send for MemoryRegionOps {} +unsafe impl Sync for MemoryRegionOps {} + +// SAFETY: this is a pure data struct +unsafe impl Send for CoalescedMemoryRange {} +unsafe impl Sync for CoalescedMemoryRange {} + +unsafe impl Zeroable for MemoryRegionOps__bindgen_ty_1 {} +unsafe impl Zeroable for MemoryRegionOps__bindgen_ty_2 {} +unsafe impl Zeroable for MemoryRegionOps {} +unsafe impl Zeroable for MemTxAttrs {} diff --git a/rust/system/src/lib.rs b/rust/system/src/lib.rs new file mode 100644 index 0000000000000..aafe9a866c925 --- /dev/null +++ b/rust/system/src/lib.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pub mod bindings; + +mod memory; +pub use memory::*; diff --git a/rust/qemu-api/src/memory.rs b/rust/system/src/memory.rs similarity index 99% rename from rust/qemu-api/src/memory.rs rename to rust/system/src/memory.rs index ecbbd9b60462c..29568ed7676fe 100644 --- a/rust/qemu-api/src/memory.rs +++ b/rust/system/src/memory.rs @@ -9,11 +9,11 @@ use std::{ marker::PhantomData, }; -pub use bindings::{hwaddr, MemTxAttrs}; use common::{callbacks::FnCall, uninit::MaybeUninitField, zeroable::Zeroable, Opaque}; use qom::prelude::*; use crate::bindings::{self, device_endian, memory_region_init_io}; +pub use crate::bindings::{hwaddr, MemTxAttrs}; pub struct MemoryRegionOps( bindings::MemoryRegionOps, diff --git a/rust/system/wrapper.h b/rust/system/wrapper.h new file mode 100644 index 0000000000000..48abde8505268 --- /dev/null +++ b/rust/system/wrapper.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * This header file is meant to be used as input to the `bindgen` application + * in order to generate C FFI compatible Rust bindings. + */ + +#ifndef __CLANG_STDATOMIC_H +#define __CLANG_STDATOMIC_H +/* + * Fix potential missing stdatomic.h error in case bindgen does not insert the + * correct libclang header paths on its own. We do not use stdatomic.h symbols + * in QEMU code, so it's fine to declare dummy types instead. + */ +typedef enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst, +} memory_order; +#endif /* __CLANG_STDATOMIC_H */ + +#include "qemu/osdep.h" + +#include "system/system.h" +#include "system/memory.h" +#include "system/address-spaces.h" From 5e588c9d08b0da64fab7f370e65744cb7a4174ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:56 +0200 Subject: [PATCH 0748/1794] rust: split "hwcore" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-16-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 17 +++++ rust/Cargo.toml | 1 + rust/bindings/src/lib.rs | 64 ++++++++++++++++++ rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 10 ++- rust/hw/core/Cargo.toml | 26 ++++++++ rust/hw/core/build.rs | 1 + rust/hw/core/meson.build | 80 +++++++++++++++++++++++ rust/hw/core/src/bindings.rs | 41 ++++++++++++ rust/{qemu-api => hw/core}/src/irq.rs | 0 rust/hw/core/src/lib.rs | 14 ++++ rust/{qemu-api => hw/core}/src/qdev.rs | 2 +- rust/{qemu-api => hw/core}/src/sysbus.rs | 0 rust/{qemu-api => hw/core}/tests/tests.rs | 7 +- rust/hw/core/wrapper.h | 32 +++++++++ rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 1 + rust/hw/timer/hpet/src/device.rs | 26 ++++---- rust/meson.build | 1 + rust/qemu-api-macros/src/lib.rs | 10 +-- rust/qemu-api-macros/src/tests.rs | 20 +++--- rust/qemu-api/Cargo.toml | 1 + rust/qemu-api/meson.build | 17 +---- rust/qemu-api/src/bindings.rs | 10 --- rust/qemu-api/src/lib.rs | 4 -- rust/qemu-api/src/prelude.rs | 4 -- rust/qemu-api/wrapper.h | 6 -- 29 files changed, 318 insertions(+), 81 deletions(-) create mode 100644 rust/bindings/src/lib.rs create mode 100644 rust/hw/core/Cargo.toml create mode 120000 rust/hw/core/build.rs create mode 100644 rust/hw/core/meson.build create mode 100644 rust/hw/core/src/bindings.rs rename rust/{qemu-api => hw/core}/src/irq.rs (100%) create mode 100644 rust/hw/core/src/lib.rs rename rust/{qemu-api => hw/core}/src/qdev.rs (99%) rename rust/{qemu-api => hw/core}/src/sysbus.rs (100%) rename rust/{qemu-api => hw/core}/tests/tests.rs (97%) create mode 100644 rust/hw/core/wrapper.h diff --git a/MAINTAINERS b/MAINTAINERS index 432ed5135450c..92d575b535ad8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3518,6 +3518,7 @@ S: Maintained F: rust/bql/ F: rust/chardev/ F: rust/common/ +F: rust/hw/core/ F: rust/migration/ F: rust/qemu-api F: rust/qemu-api-macros diff --git a/rust/Cargo.lock b/rust/Cargo.lock index e6b75f30bef48..77118e882b5f7 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -91,6 +91,7 @@ version = "0.1.0" dependencies = [ "bql", "common", + "hwcore", "migration", "qemu_api", "qemu_api_macros", @@ -99,6 +100,20 @@ dependencies = [ "util", ] +[[package]] +name = "hwcore" +version = "0.1.0" +dependencies = [ + "bql", + "chardev", + "common", + "migration", + "qemu_api_macros", + "qom", + "system", + "util", +] + [[package]] name = "itertools" version = "0.11.0" @@ -133,6 +148,7 @@ dependencies = [ "bql", "chardev", "common", + "hwcore", "migration", "qemu_api", "qemu_api_macros", @@ -180,6 +196,7 @@ dependencies = [ "bql", "chardev", "common", + "hwcore", "migration", "qemu_api_macros", "qom", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 8e210d277a2be..8ec07d206518b 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -9,6 +9,7 @@ members = [ "qemu-api", "qom", "system", + "hw/core", "hw/char/pl011", "hw/timer/hpet", "util", diff --git a/rust/bindings/src/lib.rs b/rust/bindings/src/lib.rs new file mode 100644 index 0000000000000..5bf03b13706e3 --- /dev/null +++ b/rust/bindings/src/lib.rs @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![allow( + dead_code, + improper_ctypes_definitions, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unsafe_op_in_unsafe_fn, + clippy::pedantic, + clippy::restriction, + clippy::style, + clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, + clippy::useless_transmute, + clippy::missing_safety_doc +)] + +//! `bindgen`-generated declarations. + +#[cfg(MESON)] +include!("bindings.inc.rs"); + +#[cfg(not(MESON))] +include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); + +// SAFETY: these are implemented in C; the bindings need to assert that the +// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`. +// When bindings for character devices are introduced, this can be +// moved to the Opaque<> wrapper in src/chardev.rs. +unsafe impl Send for CharBackend {} +unsafe impl Sync for CharBackend {} + +// SAFETY: this is a pure data struct +unsafe impl Send for CoalescedMemoryRange {} +unsafe impl Sync for CoalescedMemoryRange {} + +// SAFETY: these are constants and vtables; the Send and Sync requirements +// are deferred to the unsafe callbacks that they contain +unsafe impl Send for MemoryRegionOps {} +unsafe impl Sync for MemoryRegionOps {} + +unsafe impl Send for Property {} +unsafe impl Sync for Property {} + +unsafe impl Send for TypeInfo {} +unsafe impl Sync for TypeInfo {} + +unsafe impl Send for VMStateDescription {} +unsafe impl Sync for VMStateDescription {} + +unsafe impl Send for VMStateField {} +unsafe impl Sync for VMStateField {} + +unsafe impl Send for VMStateInfo {} +unsafe impl Sync for VMStateInfo {} + +// bindgen does not derive Default here +#[allow(clippy::derivable_impls)] +impl Default for VMStateFlags { + fn default() -> Self { + Self(0) + } +} diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index e4b1c3f1eb8e9..830d88586bb47 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -23,6 +23,7 @@ migration = { path = "../../../migration" } qom = { path = "../../../qom" } chardev = { path = "../../../chardev" } system = { path = "../../../system" } +hwcore = { path = "../../../hw/core" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index fae6e1b9c9d50..fac0432113317 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -16,6 +16,7 @@ _libpl011_rs = static_library( qom_rs, chardev_rs, system_rs, + hwcore_rs, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index c65db5a5174c3..a6a17d9f2dcc4 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -7,16 +7,14 @@ use std::{ffi::CStr, mem::size_of}; use bql::BqlRefCell; use chardev::{CharBackend, Chardev, Event}; use common::{static_assert, uninit_field_mut}; +use hwcore::{ + Clock, ClockEvent, DeviceImpl, DeviceMethods, DeviceState, IRQState, InterruptSource, + ResetType, ResettablePhasesImpl, SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods, +}; use migration::{ self, impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused, VMStateDescription, VMStateDescriptionBuilder, }; -use qemu_api::{ - irq::{IRQState, InterruptSource}, - prelude::*, - qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, - sysbus::{SysBusDevice, SysBusDeviceImpl}, -}; use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit}; use system::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}; use util::{log::Log, log_mask_ln}; diff --git a/rust/hw/core/Cargo.toml b/rust/hw/core/Cargo.toml new file mode 100644 index 0000000000000..0b3538026447b --- /dev/null +++ b/rust/hw/core/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "hwcore" +version = "0.1.0" +description = "Rust bindings for QEMU/hwcore" +resolver = "2" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +common = { path = "../../common" } +bql = { path = "../../bql" } +qom = { path = "../../qom" } +chardev = { path = "../../chardev" } +migration = { path = "../../migration" } +system = { path = "../../system" } +util = { path = "../../util" } +qemu_api_macros = { path = "../../qemu-api-macros" } + +[lints] +workspace = true diff --git a/rust/hw/core/build.rs b/rust/hw/core/build.rs new file mode 120000 index 0000000000000..2a79ee31b8c8f --- /dev/null +++ b/rust/hw/core/build.rs @@ -0,0 +1 @@ +../../util/build.rs \ No newline at end of file diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build new file mode 100644 index 0000000000000..7dd1ade6f0fd1 --- /dev/null +++ b/rust/hw/core/meson.build @@ -0,0 +1,80 @@ +_hwcore_bindgen_args = [] +c_enums = [ + 'DeviceCategory', + 'GpioPolarity', + 'MachineInitPhase', + 'ResetType', +] +foreach enum : c_enums + _hwcore_bindgen_args += ['--rustified-enum', enum] +endforeach + +blocked_type = [ + 'Chardev', + 'Error', + 'ObjectClass', + 'MemoryRegion', + 'VMStateDescription', +] +foreach type: blocked_type + _hwcore_bindgen_args += ['--blocklist-type', type] +endforeach + +c_bitfields = [ + 'ClockEvent', +] +foreach enum : c_bitfields + _hwcore_bindgen_args += ['--bitfield-enum', enum] +endforeach + +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_hwcore_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common + _hwcore_bindgen_args, +) + +_hwcore_rs = static_library( + 'hwcore', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/irq.rs', + 'src/qdev.rs', + 'src/sysbus.rs', + ], + {'.': _hwcore_bindings_inc_rs} + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + link_with: [_bql_rs, _chardev_rs, _migration_rs, _qom_rs, _system_rs, _util_rs], + dependencies: [qemu_api_macros, common_rs], +) + +hwcore_rs = declare_dependency(link_with: [_hwcore_rs], + dependencies: [qom_rs, hwcore]) + +test('rust-hwcore-rs-integration', + executable( + 'rust-hwcore-rs-integration', + files('tests/tests.rs'), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_args: ['--test'], + install: false, + dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, qemu_api_macros, util_rs]), + args: [ + '--test', '--test-threads', '1', + '--format', 'pretty', + ], + protocol: 'rust', + suite: ['unit', 'rust']) diff --git a/rust/hw/core/src/bindings.rs b/rust/hw/core/src/bindings.rs new file mode 100644 index 0000000000000..919c02b56aea6 --- /dev/null +++ b/rust/hw/core/src/bindings.rs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![allow( + dead_code, + improper_ctypes_definitions, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unnecessary_transmutes, + unsafe_op_in_unsafe_fn, + clippy::pedantic, + clippy::restriction, + clippy::style, + clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, + clippy::useless_transmute, + clippy::missing_safety_doc, + clippy::too_many_arguments +)] + +use chardev::bindings::Chardev; +use common::Zeroable; +use migration::bindings::VMStateDescription; +use qom::bindings::ObjectClass; +use system::bindings::MemoryRegion; +use util::bindings::Error; + +#[cfg(MESON)] +include!("bindings.inc.rs"); + +#[cfg(not(MESON))] +include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); + +unsafe impl Send for Property {} +unsafe impl Sync for Property {} + +unsafe impl Send for TypeInfo {} +unsafe impl Sync for TypeInfo {} + +unsafe impl Zeroable for Property__bindgen_ty_1 {} +unsafe impl Zeroable for Property {} diff --git a/rust/qemu-api/src/irq.rs b/rust/hw/core/src/irq.rs similarity index 100% rename from rust/qemu-api/src/irq.rs rename to rust/hw/core/src/irq.rs diff --git a/rust/hw/core/src/lib.rs b/rust/hw/core/src/lib.rs new file mode 100644 index 0000000000000..c5588d9bc279c --- /dev/null +++ b/rust/hw/core/src/lib.rs @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pub use qom; + +pub mod bindings; + +mod irq; +pub use irq::*; + +mod qdev; +pub use qdev::*; + +mod sysbus; +pub use sysbus::*; diff --git a/rust/qemu-api/src/qdev.rs b/rust/hw/core/src/qdev.rs similarity index 99% rename from rust/qemu-api/src/qdev.rs rename to rust/hw/core/src/qdev.rs index 7efc796f50222..8e9702ce0bb77 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/hw/core/src/qdev.rs @@ -9,13 +9,13 @@ use std::{ ptr::NonNull, }; -pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; use chardev::Chardev; use common::{callbacks::FnCall, Opaque}; use migration::{impl_vmstate_c_struct, VMStateDescription}; use qom::{prelude::*, ObjectClass, ObjectImpl, Owned, ParentInit}; use util::{Error, Result}; +pub use crate::bindings::{ClockEvent, DeviceClass, Property, ResetType}; use crate::{ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, irq::InterruptSource, diff --git a/rust/qemu-api/src/sysbus.rs b/rust/hw/core/src/sysbus.rs similarity index 100% rename from rust/qemu-api/src/sysbus.rs rename to rust/hw/core/src/sysbus.rs diff --git a/rust/qemu-api/tests/tests.rs b/rust/hw/core/tests/tests.rs similarity index 97% rename from rust/qemu-api/tests/tests.rs rename to rust/hw/core/tests/tests.rs index f2e5eb9f4f427..21ee301fa68e4 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/hw/core/tests/tests.rs @@ -5,16 +5,11 @@ use std::{ffi::CStr, ptr::addr_of}; use bql::BqlCell; +use hwcore::{DeviceImpl, DeviceState, ResettablePhasesImpl, SysBusDevice}; use migration::{VMStateDescription, VMStateDescriptionBuilder}; -use qemu_api::{ - qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl}, - sysbus::SysBusDevice, -}; use qom::{prelude::*, ObjectImpl, ParentField}; use util::bindings::{module_call_init, module_init_type}; -mod vmstate_tests; - // Test that macros can compile. pub const VMSTATE: VMStateDescription = VMStateDescriptionBuilder::::new() .name(c"name") diff --git a/rust/hw/core/wrapper.h b/rust/hw/core/wrapper.h new file mode 100644 index 0000000000000..3bdbd1249e41d --- /dev/null +++ b/rust/hw/core/wrapper.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * This header file is meant to be used as input to the `bindgen` application + * in order to generate C FFI compatible Rust bindings. + */ + +#ifndef __CLANG_STDATOMIC_H +#define __CLANG_STDATOMIC_H +/* + * Fix potential missing stdatomic.h error in case bindgen does not insert the + * correct libclang header paths on its own. We do not use stdatomic.h symbols + * in QEMU code, so it's fine to declare dummy types instead. + */ +typedef enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst, +} memory_order; +#endif /* __CLANG_STDATOMIC_H */ + +#include "qemu/osdep.h" + +#include "hw/sysbus.h" +#include "hw/clock.h" +#include "hw/qdev-clock.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "hw/irq.h" diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index a95b1271c643f..e28d66f6457f1 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -19,6 +19,7 @@ qom = { path = "../../../qom" } system = { path = "../../../system" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } +hwcore = { path = "../../../hw/core" } [lints] workspace = true diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index c4ffe020f6bed..e6f99b67785f3 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -12,6 +12,7 @@ _libhpet_rs = static_library( qemu_api_macros, qom_rs, system_rs, + hwcore_rs, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 841c2ba337531..3031539744f74 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -12,17 +12,15 @@ use std::{ use bql::{BqlCell, BqlRefCell}; use common::{bitops::IntegerExt, uninit_field_mut}; +use hwcore::{ + bindings::{qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize}, + declare_properties, define_property, DeviceImpl, DeviceMethods, DeviceState, InterruptSource, + Property, ResetType, ResettablePhasesImpl, SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods, +}; use migration::{ self, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, VMStateDescription, VMStateDescriptionBuilder, }; -use qemu_api::{ - bindings::{qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize}, - irq::InterruptSource, - prelude::*, - qdev::{DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, - sysbus::{SysBusDevice, SysBusDeviceImpl}, -}; use qom::{prelude::*, ObjectImpl, ParentField, ParentInit}; use system::{ bindings::{address_space_memory, address_space_stl_le, hwaddr}, @@ -904,9 +902,9 @@ impl ObjectImpl for HPETState { } // TODO: Make these properties user-configurable! -qemu_api::declare_properties! { +declare_properties! { HPET_PROPERTIES, - qemu_api::define_property!( + define_property!( c"timers", HPETState, num_timers, @@ -914,7 +912,7 @@ qemu_api::declare_properties! { u8, default = HPET_MIN_TIMERS ), - qemu_api::define_property!( + define_property!( c"msi", HPETState, flags, @@ -923,7 +921,7 @@ qemu_api::declare_properties! { bit = HPET_FLAG_MSI_SUPPORT_SHIFT as u8, default = false, ), - qemu_api::define_property!( + define_property!( c"hpet-intcap", HPETState, int_route_cap, @@ -931,7 +929,7 @@ qemu_api::declare_properties! { u32, default = 0 ), - qemu_api::define_property!( + define_property!( c"hpet-offset-saved", HPETState, hpet_offset_saved, @@ -1004,8 +1002,8 @@ const VMSTATE_HPET: VMStateDescription = .build(); // SAFETY: HPET_PROPERTIES is a valid Property array constructed with the -// qemu_api::declare_properties macro. -unsafe impl qemu_api::qdev::DevicePropertiesImpl for HPETState { +// hwcore::declare_properties macro. +unsafe impl hwcore::DevicePropertiesImpl for HPETState { const PROPERTIES: &'static [Property] = &HPET_PROPERTIES; } diff --git a/rust/meson.build b/rust/meson.build index d8b71f550614a..041b0a473e5c3 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -31,6 +31,7 @@ subdir('bql') subdir('qom') subdir('system') subdir('chardev') +subdir('hw/core') subdir('qemu-api') subdir('hw') diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index e643e57ebdfd9..830b4326985bf 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -272,24 +272,24 @@ fn derive_device_or_error(input: DeriveInput) -> Result::VALUE }; + let qdev_prop = quote! { <#field_ty as ::hwcore::QDevProp>::VALUE }; let set_default = defval.is_some(); let defval = defval.unwrap_or(syn::Expr::Verbatim(quote! { 0 })); properties_expanded.push(quote! { - ::qemu_api::bindings::Property { + ::hwcore::bindings::Property { name: ::std::ffi::CStr::as_ptr(#prop_name), info: #qdev_prop , offset: ::core::mem::offset_of!(#name, #field_name) as isize, set_default: #set_default, - defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: #defval as u64 }, + defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: #defval as u64 }, ..::common::Zeroable::ZERO } }); } Ok(quote_spanned! {input.span() => - unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for #name { - const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[ + unsafe impl ::hwcore::DevicePropertiesImpl for #name { + const PROPERTIES: &'static [::hwcore::bindings::Property] = &[ #(#properties_expanded),* ]; } diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs index 76e6c57479ec1..9ab7eab7f37a1 100644 --- a/rust/qemu-api-macros/src/tests.rs +++ b/rust/qemu-api-macros/src/tests.rs @@ -100,14 +100,14 @@ fn test_derive_device() { } }, quote! { - unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState { - const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[ - ::qemu_api::bindings::Property { + unsafe impl ::hwcore::DevicePropertiesImpl for DummyState { + const PROPERTIES: &'static [::hwcore::bindings::Property] = &[ + ::hwcore::bindings::Property { name: ::std::ffi::CStr::as_ptr(c"migrate_clock"), - info: ::VALUE, + info: ::VALUE, offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, set_default: true, - defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 }, + defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 }, ..::common::Zeroable::ZERO } ]; @@ -127,14 +127,14 @@ fn test_derive_device() { } }, quote! { - unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState { - const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[ - ::qemu_api::bindings::Property { + unsafe impl ::hwcore::DevicePropertiesImpl for DummyState { + const PROPERTIES: &'static [::hwcore::bindings::Property] = &[ + ::hwcore::bindings::Property { name: ::std::ffi::CStr::as_ptr(c"migrate-clk"), - info: ::VALUE, + info: ::VALUE, offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, set_default: true, - defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 }, + defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 }, ..::common::Zeroable::ZERO } ]; diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 2884c1d460b08..9e7afc7e3ad3c 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -16,6 +16,7 @@ rust-version.workspace = true [dependencies] common = { path = "../common" } chardev = { path = "../chardev" } +hwcore = { path = "../hw/core" } migration = { path = "../migration" } util = { path = "../util" } bql = { path = "../bql" } diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 92e2581a64ea9..2dc638782ca00 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -3,22 +3,12 @@ _qemu_api_cfg = run_command(rustc_args, capture: true, check: true).stdout().strip().splitlines() c_enums = [ - 'DeviceCategory', - 'GpioPolarity', - 'MachineInitPhase', 'MemoryDeviceInfoKind', - 'ResetType', ] _qemu_api_bindgen_args = [] foreach enum : c_enums _qemu_api_bindgen_args += ['--rustified-enum', enum] endforeach -c_bitfields = [ - 'ClockEvent', -] -foreach enum : c_bitfields - _qemu_api_bindgen_args += ['--bitfield-enum', enum] -endforeach blocked_type = [ 'Chardev', @@ -55,17 +45,14 @@ _qemu_api_rs = static_library( [ 'src/lib.rs', 'src/bindings.rs', - 'src/irq.rs', 'src/prelude.rs', - 'src/qdev.rs', - 'src/sysbus.rs', ], {'.' : _qemu_api_bindings_inc_rs}, ), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, + dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, hwcore_rs, libc_rs, migration_rs, qemu_api_macros, qom_rs, system_rs, util_rs, hwcore], ) @@ -75,7 +62,7 @@ qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], test('rust-qemu-api-integration', executable( 'rust-qemu-api-integration', - files('tests/tests.rs', 'tests/vmstate_tests.rs'), + files('tests/vmstate_tests.rs'), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 63b805c76e4d1..9c863e9b5b40f 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -21,7 +21,6 @@ //! `bindgen`-generated declarations. use chardev::bindings::Chardev; -use common::Zeroable; use migration::bindings::VMStateDescription; use qom::bindings::ObjectClass; use system::bindings::{device_endian, MemTxAttrs, MemoryRegion}; @@ -32,12 +31,3 @@ include!("bindings.inc.rs"); #[cfg(not(MESON))] include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); - -unsafe impl Send for Property {} -unsafe impl Sync for Property {} - -unsafe impl Send for TypeInfo {} -unsafe impl Sync for TypeInfo {} - -unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {} -unsafe impl Zeroable for crate::bindings::Property {} diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 8d574404789a8..21b886035f33c 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -13,10 +13,6 @@ pub mod bindings; #[rustfmt::skip] pub mod prelude; -pub mod irq; -pub mod qdev; -pub mod sysbus; - // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this // crate). extern crate self as qemu_api; diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 9e9d1c824747a..8db56f9f81789 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -3,7 +3,3 @@ // SPDX-License-Identifier: GPL-2.0-or-later //! Commonly used traits and types for QEMU. - -pub use crate::qdev::DeviceMethods; - -pub use crate::sysbus::SysBusDeviceMethods; diff --git a/rust/qemu-api/wrapper.h b/rust/qemu-api/wrapper.h index 564733b90352f..7c9c20b14fe71 100644 --- a/rust/qemu-api/wrapper.h +++ b/rust/qemu-api/wrapper.h @@ -49,11 +49,5 @@ typedef enum memory_order { #include "qemu/osdep.h" #include "qemu-io.h" -#include "hw/sysbus.h" -#include "hw/clock.h" -#include "hw/qdev-clock.h" -#include "hw/qdev-properties.h" -#include "hw/qdev-properties-system.h" -#include "hw/irq.h" #include "exec/memattrs.h" #include "hw/char/pl011.h" From 0d93f8177310515ae2d8aea8e1320e53818d70bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:57 +0200 Subject: [PATCH 0749/1794] rust: rename qemu_api_macros -> qemu_macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since "qemu_api" is no longer the unique crate to provide APIs. Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-17-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 2 +- rust/Cargo.lock | 22 +++++++++---------- rust/Cargo.toml | 2 +- rust/bits/Cargo.toml | 2 +- rust/bits/meson.build | 2 +- rust/bits/src/lib.rs | 4 ++-- rust/chardev/Cargo.toml | 2 +- rust/chardev/meson.build | 4 ++-- rust/chardev/src/chardev.rs | 2 +- rust/common/src/opaque.rs | 4 ++-- rust/hw/char/pl011/Cargo.toml | 2 +- rust/hw/char/pl011/meson.build | 4 ++-- rust/hw/char/pl011/src/device.rs | 4 ++-- rust/hw/char/pl011/src/registers.rs | 2 +- rust/hw/core/Cargo.toml | 2 +- rust/hw/core/meson.build | 4 ++-- rust/hw/core/src/irq.rs | 2 +- rust/hw/core/src/qdev.rs | 6 ++--- rust/hw/core/src/sysbus.rs | 2 +- rust/hw/core/tests/tests.rs | 4 ++-- rust/hw/timer/hpet/Cargo.toml | 2 +- rust/hw/timer/hpet/meson.build | 4 ++-- rust/hw/timer/hpet/src/device.rs | 6 ++--- rust/meson.build | 2 +- rust/migration/Cargo.toml | 2 +- rust/qemu-api/Cargo.toml | 2 +- rust/qemu-api/meson.build | 4 ++-- .../Cargo.toml | 2 +- .../meson.build | 10 ++++----- .../src/bits.rs | 0 .../src/lib.rs | 0 .../src/tests.rs | 0 rust/qom/Cargo.toml | 2 +- rust/qom/meson.build | 4 ++-- rust/qom/src/qom.rs | 4 ++-- rust/system/Cargo.toml | 2 +- rust/system/meson.build | 4 ++-- rust/system/src/memory.rs | 2 +- rust/util/Cargo.toml | 2 +- rust/util/meson.build | 2 +- rust/util/src/timer.rs | 4 ++-- 41 files changed, 69 insertions(+), 69 deletions(-) rename rust/{qemu-api-macros => qemu-macros}/Cargo.toml (94%) rename rust/{qemu-api-macros => qemu-macros}/meson.build (63%) rename rust/{qemu-api-macros => qemu-macros}/src/bits.rs (100%) rename rust/{qemu-api-macros => qemu-macros}/src/lib.rs (100%) rename rust/{qemu-api-macros => qemu-macros}/src/tests.rs (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 92d575b535ad8..23bda7d332570 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3521,7 +3521,7 @@ F: rust/common/ F: rust/hw/core/ F: rust/migration/ F: rust/qemu-api -F: rust/qemu-api-macros +F: rust/qemu-macros/ F: rust/qom/ F: rust/rustfmt.toml F: rust/system/ diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 77118e882b5f7..021ce6dd48c25 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -41,7 +41,7 @@ dependencies = [ name = "bits" version = "0.1.0" dependencies = [ - "qemu_api_macros", + "qemu_macros", ] [[package]] @@ -58,7 +58,7 @@ dependencies = [ "bql", "common", "migration", - "qemu_api_macros", + "qemu_macros", "qom", "util", ] @@ -94,7 +94,7 @@ dependencies = [ "hwcore", "migration", "qemu_api", - "qemu_api_macros", + "qemu_macros", "qom", "system", "util", @@ -108,7 +108,7 @@ dependencies = [ "chardev", "common", "migration", - "qemu_api_macros", + "qemu_macros", "qom", "system", "util", @@ -134,7 +134,7 @@ name = "migration" version = "0.1.0" dependencies = [ "common", - "qemu_api_macros", + "qemu_macros", "util", ] @@ -151,7 +151,7 @@ dependencies = [ "hwcore", "migration", "qemu_api", - "qemu_api_macros", + "qemu_macros", "qom", "system", "util", @@ -198,14 +198,14 @@ dependencies = [ "common", "hwcore", "migration", - "qemu_api_macros", + "qemu_macros", "qom", "system", "util", ] [[package]] -name = "qemu_api_macros" +name = "qemu_macros" version = "0.1.0" dependencies = [ "proc-macro2", @@ -220,7 +220,7 @@ dependencies = [ "bql", "common", "migration", - "qemu_api_macros", + "qemu_macros", "util", ] @@ -249,7 +249,7 @@ name = "system" version = "0.1.0" dependencies = [ "common", - "qemu_api_macros", + "qemu_macros", "qom", "util", ] @@ -268,7 +268,7 @@ dependencies = [ "common", "foreign", "libc", - "qemu_api_macros", + "qemu_macros", ] [[package]] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 8ec07d206518b..b2a5c230fa263 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -5,7 +5,7 @@ members = [ "bql", "common", "migration", - "qemu-api-macros", + "qemu-macros", "qemu-api", "qom", "system", diff --git a/rust/bits/Cargo.toml b/rust/bits/Cargo.toml index 1ff38a411756c..7fce972b270b6 100644 --- a/rust/bits/Cargo.toml +++ b/rust/bits/Cargo.toml @@ -13,7 +13,7 @@ repository.workspace = true rust-version.workspace = true [dependencies] -qemu_api_macros = { path = "../qemu-api-macros" } +qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/bits/meson.build b/rust/bits/meson.build index 2a41e138c54bf..359ca86f1556c 100644 --- a/rust/bits/meson.build +++ b/rust/bits/meson.build @@ -3,7 +3,7 @@ _bits_rs = static_library( 'src/lib.rs', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', - dependencies: [qemu_api_macros], + dependencies: [qemu_macros], ) bits_rs = declare_dependency(link_with: _bits_rs) diff --git a/rust/bits/src/lib.rs b/rust/bits/src/lib.rs index e9d15ad0cb578..1bc882fde1865 100644 --- a/rust/bits/src/lib.rs +++ b/rust/bits/src/lib.rs @@ -380,11 +380,11 @@ macro_rules! bits { }; { $type:ty: $expr:expr } => { - ::qemu_api_macros::bits_const_internal! { $type @ ($expr) } + ::qemu_macros::bits_const_internal! { $type @ ($expr) } }; { $type:ty as $int_type:ty: $expr:expr } => { - (::qemu_api_macros::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type + (::qemu_macros::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type }; } diff --git a/rust/chardev/Cargo.toml b/rust/chardev/Cargo.toml index 7df9c677fc7b6..c13917730786b 100644 --- a/rust/chardev/Cargo.toml +++ b/rust/chardev/Cargo.toml @@ -18,7 +18,7 @@ bql = { path = "../bql" } migration = { path = "../migration" } qom = { path = "../qom" } util = { path = "../util" } -qemu_api_macros = { path = "../qemu-api-macros" } +qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build index 5d333e232bdc1..a2fa3268d21cf 100644 --- a/rust/chardev/meson.build +++ b/rust/chardev/meson.build @@ -35,7 +35,7 @@ _chardev_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], - dependencies: [common_rs, qemu_api_macros], + dependencies: [common_rs, qemu_macros], ) -chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_api_macros, chardev, qemuutil]) +chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_macros, chardev, qemuutil]) diff --git a/rust/chardev/src/chardev.rs b/rust/chardev/src/chardev.rs index 072d806e4a34a..cb6f99398eb30 100644 --- a/rust/chardev/src/chardev.rs +++ b/rust/chardev/src/chardev.rs @@ -26,7 +26,7 @@ use crate::bindings; /// A safe wrapper around [`bindings::Chardev`]. #[repr(transparent)] -#[derive(qemu_api_macros::Wrapper)] +#[derive(qemu_macros::Wrapper)] pub struct Chardev(Opaque); pub type ChardevClass = bindings::ChardevClass; diff --git a/rust/common/src/opaque.rs b/rust/common/src/opaque.rs index 97ed3e845224b..3b3263acaa340 100644 --- a/rust/common/src/opaque.rs +++ b/rust/common/src/opaque.rs @@ -192,7 +192,7 @@ impl Opaque { /// Annotates [`Self`] as a transparent wrapper for another type. /// -/// Usually defined via the [`qemu_api_macros::Wrapper`] derive macro. +/// Usually defined via the [`qemu_macros::Wrapper`] derive macro. /// /// # Examples /// @@ -228,7 +228,7 @@ impl Opaque { /// /// They are not defined here to allow them to be `const`. /// -/// [`qemu_api_macros::Wrapper`]: ../../qemu_api_macros/derive.Wrapper.html +/// [`qemu_macros::Wrapper`]: ../../qemu_macros/derive.Wrapper.html pub unsafe trait Wrapper { type Wrapped; } diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 830d88586bb47..9e451fc0aa859 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -25,7 +25,7 @@ chardev = { path = "../../../chardev" } system = { path = "../../../system" } hwcore = { path = "../../../hw/core" } qemu_api = { path = "../../../qemu-api" } -qemu_api_macros = { path = "../../../qemu-api-macros" } +qemu_macros = { path = "../../../qemu-macros" } [lints] workspace = true diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index fac0432113317..bad6a839c3987 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -12,7 +12,7 @@ _libpl011_rs = static_library( util_rs, migration_rs, bql_rs, - qemu_api_macros, + qemu_macros, qom_rs, chardev_rs, system_rs, @@ -24,6 +24,6 @@ rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency( link_whole: [_libpl011_rs], # Putting proc macro crates in `dependencies` is necessary for Meson to find # them when compiling the root per-target static rust lib. - dependencies: [bilge_impl_rs, qemu_api_macros], + dependencies: [bilge_impl_rs, qemu_macros], variables: {'crate': 'pl011'}, )]) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index a6a17d9f2dcc4..3010b6d983938 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -97,7 +97,7 @@ pub struct PL011Registers { } #[repr(C)] -#[derive(qemu_api_macros::Object, qemu_api_macros::Device)] +#[derive(qemu_macros::Object, qemu_macros::Device)] /// PL011 Device Model in QEMU pub struct PL011State { pub parent_obj: ParentField, @@ -683,7 +683,7 @@ pub unsafe extern "C" fn pl011_create( } #[repr(C)] -#[derive(qemu_api_macros::Object, qemu_api_macros::Device)] +#[derive(qemu_macros::Object, qemu_macros::Device)] /// PL011 Luminary device model. pub struct PL011Luminary { parent_obj: ParentField, diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs index 2bfbd81095e64..a1c41347ed5bf 100644 --- a/rust/hw/char/pl011/src/registers.rs +++ b/rust/hw/char/pl011/src/registers.rs @@ -16,7 +16,7 @@ use migration::{impl_vmstate_bitsized, impl_vmstate_forward}; #[doc(alias = "offset")] #[allow(non_camel_case_types)] #[repr(u64)] -#[derive(Debug, Eq, PartialEq, qemu_api_macros::TryInto)] +#[derive(Debug, Eq, PartialEq, qemu_macros::TryInto)] pub enum RegisterOffset { /// Data Register /// diff --git a/rust/hw/core/Cargo.toml b/rust/hw/core/Cargo.toml index 0b3538026447b..0eb9ffee2638c 100644 --- a/rust/hw/core/Cargo.toml +++ b/rust/hw/core/Cargo.toml @@ -20,7 +20,7 @@ chardev = { path = "../../chardev" } migration = { path = "../../migration" } system = { path = "../../system" } util = { path = "../../util" } -qemu_api_macros = { path = "../../qemu-api-macros" } +qemu_macros = { path = "../../qemu-macros" } [lints] workspace = true diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build index 7dd1ade6f0fd1..67eacf854fe7f 100644 --- a/rust/hw/core/meson.build +++ b/rust/hw/core/meson.build @@ -58,7 +58,7 @@ _hwcore_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', link_with: [_bql_rs, _chardev_rs, _migration_rs, _qom_rs, _system_rs, _util_rs], - dependencies: [qemu_api_macros, common_rs], + dependencies: [qemu_macros, common_rs], ) hwcore_rs = declare_dependency(link_with: [_hwcore_rs], @@ -71,7 +71,7 @@ test('rust-hwcore-rs-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, qemu_api_macros, util_rs]), + dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, qemu_macros, util_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/hw/core/src/irq.rs b/rust/hw/core/src/irq.rs index fead2bbe8e0a5..d8d964cad27a2 100644 --- a/rust/hw/core/src/irq.rs +++ b/rust/hw/core/src/irq.rs @@ -18,7 +18,7 @@ use crate::bindings::{self, qemu_set_irq}; /// An opaque wrapper around [`bindings::IRQState`]. #[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] +#[derive(Debug, qemu_macros::Wrapper)] pub struct IRQState(Opaque); /// Interrupt sources are used by devices to pass changes to a value (typically diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs index 8e9702ce0bb77..c9faf44a71d36 100644 --- a/rust/hw/core/src/qdev.rs +++ b/rust/hw/core/src/qdev.rs @@ -23,7 +23,7 @@ use crate::{ /// A safe wrapper around [`bindings::Clock`]. #[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] +#[derive(Debug, qemu_macros::Wrapper)] pub struct Clock(Opaque); unsafe impl Send for Clock {} @@ -31,7 +31,7 @@ unsafe impl Sync for Clock {} /// A safe wrapper around [`bindings::DeviceState`]. #[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] +#[derive(Debug, qemu_macros::Wrapper)] pub struct DeviceState(Opaque); unsafe impl Send for DeviceState {} @@ -101,7 +101,7 @@ unsafe extern "C" fn rust_resettable_exit_fn( /// Helper trait to return pointer to a [`bindings::PropertyInfo`] for a type. /// -/// This trait is used by [`qemu_api_macros::Device`] derive macro. +/// This trait is used by [`qemu_macros::Device`] derive macro. /// /// Base types that already have `qdev_prop_*` globals in the QEMU API should /// use those values as exported by the [`bindings`] module, instead of diff --git a/rust/hw/core/src/sysbus.rs b/rust/hw/core/src/sysbus.rs index dda71ebda7089..92c7449b802d4 100644 --- a/rust/hw/core/src/sysbus.rs +++ b/rust/hw/core/src/sysbus.rs @@ -19,7 +19,7 @@ use crate::{ /// A safe wrapper around [`bindings::SysBusDevice`]. #[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] +#[derive(Debug, qemu_macros::Wrapper)] pub struct SysBusDevice(Opaque); unsafe impl Send for SysBusDevice {} diff --git a/rust/hw/core/tests/tests.rs b/rust/hw/core/tests/tests.rs index 21ee301fa68e4..2f08b8f3bfebf 100644 --- a/rust/hw/core/tests/tests.rs +++ b/rust/hw/core/tests/tests.rs @@ -17,7 +17,7 @@ pub const VMSTATE: VMStateDescription = VMStateDescriptionBuilder::< .build(); #[repr(C)] -#[derive(qemu_api_macros::Object, qemu_api_macros::Device)] +#[derive(qemu_macros::Object, qemu_macros::Device)] pub struct DummyState { parent: ParentField, #[property(rename = "migrate-clk", default = true)] @@ -54,7 +54,7 @@ impl DeviceImpl for DummyState { } #[repr(C)] -#[derive(qemu_api_macros::Object, qemu_api_macros::Device)] +#[derive(qemu_macros::Object, qemu_macros::Device)] pub struct DummyChildState { parent: ParentField, } diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index e28d66f6457f1..68e8187bb820b 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -18,7 +18,7 @@ bql = { path = "../../../bql" } qom = { path = "../../../qom" } system = { path = "../../../system" } qemu_api = { path = "../../../qemu-api" } -qemu_api_macros = { path = "../../../qemu-api-macros" } +qemu_macros = { path = "../../../qemu-macros" } hwcore = { path = "../../../hw/core" } [lints] diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index e6f99b67785f3..3b94d5ec0a7c4 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -9,7 +9,7 @@ _libhpet_rs = static_library( util_rs, migration_rs, bql_rs, - qemu_api_macros, + qemu_macros, qom_rs, system_rs, hwcore_rs, @@ -20,6 +20,6 @@ rust_devices_ss.add(when: 'CONFIG_X_HPET_RUST', if_true: [declare_dependency( link_whole: [_libhpet_rs], # Putting proc macro crates in `dependencies` is necessary for Meson to find # them when compiling the root per-target static rust lib. - dependencies: [qemu_api_macros], + dependencies: [qemu_macros], variables: {'crate': 'hpet'}, )]) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 3031539744f74..07e0f639fc4cb 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -97,7 +97,7 @@ const HPET_TN_CFG_FSB_CAP_SHIFT: usize = 15; /// Timer N Interrupt Routing Capability (bits 32:63) const HPET_TN_CFG_INT_ROUTE_CAP_SHIFT: usize = 32; -#[derive(qemu_api_macros::TryInto)] +#[derive(qemu_macros::TryInto)] #[repr(u64)] #[allow(non_camel_case_types)] /// Timer registers, masked by 0x18 @@ -110,7 +110,7 @@ enum TimerRegister { ROUTE = 16, } -#[derive(qemu_api_macros::TryInto)] +#[derive(qemu_macros::TryInto)] #[repr(u64)] #[allow(non_camel_case_types)] /// Global registers @@ -520,7 +520,7 @@ impl HPETTimer { /// HPET Event Timer Block Abstraction #[repr(C)] -#[derive(qemu_api_macros::Object)] +#[derive(qemu_macros::Object)] pub struct HPETState { parent_obj: ParentField, iomem: MemoryRegion, diff --git a/rust/meson.build b/rust/meson.build index 041b0a473e5c3..9f6a0b161d2ec 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -23,7 +23,7 @@ qemuutil_rs = qemuutil.partial_dependency(link_args: true, links: true) genrs = [] subdir('common') -subdir('qemu-api-macros') +subdir('qemu-macros') subdir('bits') subdir('util') subdir('migration') diff --git a/rust/migration/Cargo.toml b/rust/migration/Cargo.toml index 98e6df2109d33..66af81e0a35b5 100644 --- a/rust/migration/Cargo.toml +++ b/rust/migration/Cargo.toml @@ -15,7 +15,7 @@ rust-version.workspace = true [dependencies] common = { path = "../common" } util = { path = "../util" } -qemu_api_macros = { path = "../qemu-api-macros" } +qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 9e7afc7e3ad3c..9abb88aa1f8a0 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -20,9 +20,9 @@ hwcore = { path = "../hw/core" } migration = { path = "../migration" } util = { path = "../util" } bql = { path = "../bql" } +qemu_macros = { path = "../qemu-macros" } qom = { path = "../qom" } system = { path = "../system" } -qemu_api_macros = { path = "../qemu-api-macros" } [lints] workspace = true diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 2dc638782ca00..fe81f16d9900a 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -52,12 +52,12 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, hwcore_rs, libc_rs, migration_rs, qemu_api_macros, + dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, hwcore_rs, libc_rs, migration_rs, qemu_macros, qom_rs, system_rs, util_rs, hwcore], ) qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], - dependencies: [qemu_api_macros, qom, hwcore, chardev, migration]) + dependencies: [qemu_macros, qom, hwcore, chardev, migration]) test('rust-qemu-api-integration', executable( diff --git a/rust/qemu-api-macros/Cargo.toml b/rust/qemu-macros/Cargo.toml similarity index 94% rename from rust/qemu-api-macros/Cargo.toml rename to rust/qemu-macros/Cargo.toml index 0cd40c8e168a3..3b6f1d337f8ed 100644 --- a/rust/qemu-api-macros/Cargo.toml +++ b/rust/qemu-macros/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "qemu_api_macros" +name = "qemu_macros" version = "0.1.0" authors = ["Manos Pitsidianakis "] description = "Rust bindings for QEMU - Utility macros" diff --git a/rust/qemu-api-macros/meson.build b/rust/qemu-macros/meson.build similarity index 63% rename from rust/qemu-api-macros/meson.build rename to rust/qemu-macros/meson.build index 2152bcb99b30e..d0b2992e204f1 100644 --- a/rust/qemu-api-macros/meson.build +++ b/rust/qemu-macros/meson.build @@ -1,5 +1,5 @@ -_qemu_api_macros_rs = rust.proc_macro( - 'qemu_api_macros', +_qemu_macros_rs = rust.proc_macro( + 'qemu_macros', files('src/lib.rs'), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: [ @@ -14,9 +14,9 @@ _qemu_api_macros_rs = rust.proc_macro( ], ) -qemu_api_macros = declare_dependency( - link_with: _qemu_api_macros_rs, +qemu_macros = declare_dependency( + link_with: _qemu_macros_rs, ) -rust.test('rust-qemu-api-macros-tests', _qemu_api_macros_rs, +rust.test('rust-qemu-macros-tests', _qemu_macros_rs, suite: ['unit', 'rust']) diff --git a/rust/qemu-api-macros/src/bits.rs b/rust/qemu-macros/src/bits.rs similarity index 100% rename from rust/qemu-api-macros/src/bits.rs rename to rust/qemu-macros/src/bits.rs diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-macros/src/lib.rs similarity index 100% rename from rust/qemu-api-macros/src/lib.rs rename to rust/qemu-macros/src/lib.rs diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-macros/src/tests.rs similarity index 100% rename from rust/qemu-api-macros/src/tests.rs rename to rust/qemu-macros/src/tests.rs diff --git a/rust/qom/Cargo.toml b/rust/qom/Cargo.toml index 46bbf7c7fe42b..060ad2ec349da 100644 --- a/rust/qom/Cargo.toml +++ b/rust/qom/Cargo.toml @@ -16,7 +16,7 @@ rust-version.workspace = true common = { path = "../common" } bql = { path = "../bql" } migration = { path = "../migration" } -qemu_api_macros = { path = "../qemu-api-macros" } +qemu_macros = { path = "../qemu-macros" } util = { path = "../util" } [lints] diff --git a/rust/qom/meson.build b/rust/qom/meson.build index 84a65cb737ecb..40c51b71b23a9 100644 --- a/rust/qom/meson.build +++ b/rust/qom/meson.build @@ -28,10 +28,10 @@ _qom_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', link_with: [_bql_rs, _migration_rs], - dependencies: [common_rs, qemu_api_macros], + dependencies: [common_rs, qemu_macros], ) -qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_api_macros, qom]) +qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_macros, qom]) # Doctests are essentially integration tests, so they need the same dependencies. # Note that running them requires the object files for C code, so place them diff --git a/rust/qom/src/qom.rs b/rust/qom/src/qom.rs index 3ea1ad9c5bebd..2cd1d85011201 100644 --- a/rust/qom/src/qom.rs +++ b/rust/qom/src/qom.rs @@ -112,7 +112,7 @@ pub use crate::bindings::{type_register_static, ObjectClass}; /// A safe wrapper around [`bindings::Object`]. #[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] +#[derive(Debug, qemu_macros::Wrapper)] pub struct Object(Opaque); unsafe impl Send for Object {} @@ -173,7 +173,7 @@ macro_rules! qom_isa { /// /// ```ignore /// #[repr(C)] -/// #[derive(qemu_api_macros::Object)] +/// #[derive(qemu_macros::Object)] /// pub struct MyDevice { /// parent: ParentField, /// ... diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml index 6803895e080cd..d8338c8348d59 100644 --- a/rust/system/Cargo.toml +++ b/rust/system/Cargo.toml @@ -16,7 +16,7 @@ rust-version.workspace = true common = { path = "../common" } qom = { path = "../qom" } util = { path = "../util" } -qemu_api_macros = { path = "../qemu-api-macros" } +qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/system/meson.build b/rust/system/meson.build index ae9b932d29da7..9f88166f3d394 100644 --- a/rust/system/meson.build +++ b/rust/system/meson.build @@ -35,8 +35,8 @@ _system_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], - dependencies: [common_rs, qemu_api_macros], + dependencies: [common_rs, qemu_macros], ) system_rs = declare_dependency(link_with: [_system_rs], - dependencies: [qemu_api_macros, hwcore]) + dependencies: [qemu_macros, hwcore]) diff --git a/rust/system/src/memory.rs b/rust/system/src/memory.rs index 29568ed7676fe..7312f809f51b3 100644 --- a/rust/system/src/memory.rs +++ b/rust/system/src/memory.rs @@ -129,7 +129,7 @@ impl Default for MemoryRegionOpsBuilder { /// A safe wrapper around [`bindings::MemoryRegion`]. #[repr(transparent)] -#[derive(qemu_api_macros::Wrapper)] +#[derive(qemu_macros::Wrapper)] pub struct MemoryRegion(Opaque); unsafe impl Send for MemoryRegion {} diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml index 637df61060ac4..18e6619ca0662 100644 --- a/rust/util/Cargo.toml +++ b/rust/util/Cargo.toml @@ -17,7 +17,7 @@ anyhow = { workspace = true } foreign = { workspace = true } libc = { workspace = true } common = { path = "../common" } -qemu_api_macros = { path = "../qemu-api-macros" } +qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/util/meson.build b/rust/util/meson.build index 56e929349b82d..197872c9b23ac 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -39,7 +39,7 @@ _util_rs = static_library( ), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', - dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qemu_api_macros, qom, qemuutil], + dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qemu_macros, qom, qemuutil], ) util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom]) diff --git a/rust/util/src/timer.rs b/rust/util/src/timer.rs index 383e1a6e774e5..622b6ee30976d 100644 --- a/rust/util/src/timer.rs +++ b/rust/util/src/timer.rs @@ -15,14 +15,14 @@ use crate::bindings::{ /// A safe wrapper around [`bindings::QEMUTimer`]. #[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] +#[derive(Debug, qemu_macros::Wrapper)] pub struct Timer(Opaque); unsafe impl Send for Timer {} unsafe impl Sync for Timer {} #[repr(transparent)] -#[derive(qemu_api_macros::Wrapper)] +#[derive(qemu_macros::Wrapper)] pub struct TimerListGroup(Opaque); unsafe impl Send for TimerListGroup {} From 960aaeb0238612cf7d7a8f17937e860da3d23cb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:58 +0200 Subject: [PATCH 0750/1794] rust/hpet: drop now unneeded qemu_api dep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-18-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/Cargo.lock | 1 - rust/hw/timer/hpet/Cargo.toml | 1 - rust/hw/timer/hpet/meson.build | 1 - 3 files changed, 3 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 021ce6dd48c25..c407029afed6b 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -93,7 +93,6 @@ dependencies = [ "common", "hwcore", "migration", - "qemu_api", "qemu_macros", "qom", "system", diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index 68e8187bb820b..08bf97af55324 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -17,7 +17,6 @@ migration = { path = "../../../migration" } bql = { path = "../../../bql" } qom = { path = "../../../qom" } system = { path = "../../../system" } -qemu_api = { path = "../../../qemu-api" } qemu_macros = { path = "../../../qemu-macros" } hwcore = { path = "../../../hw/core" } diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index 3b94d5ec0a7c4..8ab26630d959c 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -5,7 +5,6 @@ _libhpet_rs = static_library( rust_abi: 'rust', dependencies: [ common_rs, - qemu_api_rs, util_rs, migration_rs, bql_rs, From 966b1c302e1bab3b20a356f1a6678dd1bee5575c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:49:59 +0200 Subject: [PATCH 0751/1794] rust/pl011: drop dependency on qemu_api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-19-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/Cargo.lock | 1 - rust/hw/char/pl011/Cargo.toml | 1 - rust/hw/char/pl011/build.rs | 1 + rust/hw/char/pl011/meson.build | 27 ++++++++++++++-- rust/hw/char/pl011/src/bindings.rs | 27 ++++++++++++++++ rust/hw/char/pl011/src/device.rs | 2 +- rust/hw/char/pl011/src/lib.rs | 1 + rust/hw/char/pl011/wrapper.h | 51 ++++++++++++++++++++++++++++++ 8 files changed, 106 insertions(+), 5 deletions(-) create mode 120000 rust/hw/char/pl011/build.rs create mode 100644 rust/hw/char/pl011/src/bindings.rs create mode 100644 rust/hw/char/pl011/wrapper.h diff --git a/rust/Cargo.lock b/rust/Cargo.lock index c407029afed6b..2018d13fbf53d 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -149,7 +149,6 @@ dependencies = [ "common", "hwcore", "migration", - "qemu_api", "qemu_macros", "qom", "system", diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 9e451fc0aa859..285d25c21788a 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -24,7 +24,6 @@ qom = { path = "../../../qom" } chardev = { path = "../../../chardev" } system = { path = "../../../system" } hwcore = { path = "../../../hw/core" } -qemu_api = { path = "../../../qemu-api" } qemu_macros = { path = "../../../qemu-macros" } [lints] diff --git a/rust/hw/char/pl011/build.rs b/rust/hw/char/pl011/build.rs new file mode 120000 index 0000000000000..5f5060db35668 --- /dev/null +++ b/rust/hw/char/pl011/build.rs @@ -0,0 +1 @@ +../../../util/build.rs \ No newline at end of file diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index bad6a839c3987..a14993f6921c5 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -1,6 +1,30 @@ +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_libpl011_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common, +) + _libpl011_rs = static_library( 'pl011', - files('src/lib.rs'), + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/device.rs', + 'src/registers.rs', + ], + {'.' : _libpl011_bindings_inc_rs}, + ), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', dependencies: [ @@ -8,7 +32,6 @@ _libpl011_rs = static_library( bilge_impl_rs, bits_rs, common_rs, - qemu_api_rs, util_rs, migration_rs, bql_rs, diff --git a/rust/hw/char/pl011/src/bindings.rs b/rust/hw/char/pl011/src/bindings.rs new file mode 100644 index 0000000000000..bd5ea840cb2b1 --- /dev/null +++ b/rust/hw/char/pl011/src/bindings.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![allow( + dead_code, + improper_ctypes_definitions, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unnecessary_transmutes, + unsafe_op_in_unsafe_fn, + clippy::pedantic, + clippy::restriction, + clippy::style, + clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, + clippy::useless_transmute, + clippy::missing_safety_doc, + clippy::too_many_arguments +)] + +//! `bindgen`-generated declarations. + +#[cfg(MESON)] +include!("bindings.inc.rs"); + +#[cfg(not(MESON))] +include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 3010b6d983938..85626a969d41f 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -130,7 +130,7 @@ pub struct PL011State { // structs, so the size of the Rust version must not be any larger // than the size of the C one. If this assert triggers you need to // expand the padding_for_rust[] array in the C PL011State struct. -static_assert!(size_of::() <= size_of::()); +static_assert!(size_of::() <= size_of::()); qom_isa!(PL011State : SysBusDevice, DeviceState, Object); diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 2b70d2ff56041..0c19b708c0ade 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -12,6 +12,7 @@ //! See [`PL011State`](crate::device::PL011State) for the device model type and //! the [`registers`] module for register types. +mod bindings; mod device; mod registers; diff --git a/rust/hw/char/pl011/wrapper.h b/rust/hw/char/pl011/wrapper.h new file mode 100644 index 0000000000000..87a5a589c8eac --- /dev/null +++ b/rust/hw/char/pl011/wrapper.h @@ -0,0 +1,51 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2024 Linaro Ltd. + * + * Authors: Manos Pitsidianakis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +/* + * This header file is meant to be used as input to the `bindgen` application + * in order to generate C FFI compatible Rust bindings. + */ + +#ifndef __CLANG_STDATOMIC_H +#define __CLANG_STDATOMIC_H +/* + * Fix potential missing stdatomic.h error in case bindgen does not insert the + * correct libclang header paths on its own. We do not use stdatomic.h symbols + * in QEMU code, so it's fine to declare dummy types instead. + */ +typedef enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst, +} memory_order; +#endif /* __CLANG_STDATOMIC_H */ + +#include "qemu/osdep.h" +#include "hw/char/pl011.h" From d58fcd05ffc682fbad02cd8d0bee840cb7997e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:50:00 +0200 Subject: [PATCH 0752/1794] rust: repurpose qemu_api -> tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The crate purpose is only to provide integration tests at this point, that can't easily be moved to a specific crate. It's also often a good practice to have a single integration test crate (see for ex https://github.com/rust-lang/cargo/issues/4867) Drop README.md, use docs/devel/rust.rst instead. Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-20-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 2 +- rust/Cargo.lock | 30 ++++---- rust/Cargo.toml | 2 +- rust/meson.build | 4 +- rust/qemu-api/.gitignore | 2 - rust/qemu-api/README.md | 19 ----- rust/qemu-api/build.rs | 1 - rust/qemu-api/meson.build | 75 ------------------- rust/qemu-api/src/bindings.rs | 33 -------- rust/qemu-api/src/lib.rs | 18 ----- rust/qemu-api/src/prelude.rs | 5 -- rust/qemu-api/wrapper.h | 53 ------------- rust/{qemu-api => tests}/Cargo.toml | 7 +- rust/tests/meson.build | 14 ++++ .../tests/vmstate_tests.rs | 0 15 files changed, 35 insertions(+), 230 deletions(-) delete mode 100644 rust/qemu-api/.gitignore delete mode 100644 rust/qemu-api/README.md delete mode 120000 rust/qemu-api/build.rs delete mode 100644 rust/qemu-api/meson.build delete mode 100644 rust/qemu-api/src/bindings.rs delete mode 100644 rust/qemu-api/src/lib.rs delete mode 100644 rust/qemu-api/src/prelude.rs delete mode 100644 rust/qemu-api/wrapper.h rename rust/{qemu-api => tests}/Cargo.toml (78%) create mode 100644 rust/tests/meson.build rename rust/{qemu-api => tests}/tests/vmstate_tests.rs (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 23bda7d332570..05e0597d53284 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3520,11 +3520,11 @@ F: rust/chardev/ F: rust/common/ F: rust/hw/core/ F: rust/migration/ -F: rust/qemu-api F: rust/qemu-macros/ F: rust/qom/ F: rust/rustfmt.toml F: rust/system/ +F: rust/tests/ F: rust/util/ F: scripts/get-wraps-from-cargo-registry.py diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 2018d13fbf53d..ac79c6a34a967 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -187,21 +187,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "qemu_api" -version = "0.1.0" -dependencies = [ - "bql", - "chardev", - "common", - "hwcore", - "migration", - "qemu_macros", - "qom", - "system", - "util", -] - [[package]] name = "qemu_macros" version = "0.1.0" @@ -252,6 +237,21 @@ dependencies = [ "util", ] +[[package]] +name = "tests" +version = "0.1.0" +dependencies = [ + "bql", + "chardev", + "common", + "hwcore", + "migration", + "qemu_macros", + "qom", + "system", + "util", +] + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index b2a5c230fa263..d8183c614d491 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -6,13 +6,13 @@ members = [ "common", "migration", "qemu-macros", - "qemu-api", "qom", "system", "hw/core", "hw/char/pl011", "hw/timer/hpet", "util", + "tests", ] [workspace.package] diff --git a/rust/meson.build b/rust/meson.build index 9f6a0b161d2ec..bd9b9cb83eaaa 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -18,8 +18,6 @@ quote_rs_native = dependency('quote-1-rs', native: true) syn_rs_native = dependency('syn-2-rs', native: true) proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true) -qemuutil_rs = qemuutil.partial_dependency(link_args: true, links: true) - genrs = [] subdir('common') @@ -32,7 +30,7 @@ subdir('qom') subdir('system') subdir('chardev') subdir('hw/core') -subdir('qemu-api') +subdir('tests') subdir('hw') diff --git a/rust/qemu-api/.gitignore b/rust/qemu-api/.gitignore deleted file mode 100644 index df6c2163e030a..0000000000000 --- a/rust/qemu-api/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Ignore generated bindings file overrides. -/src/bindings.inc.rs diff --git a/rust/qemu-api/README.md b/rust/qemu-api/README.md deleted file mode 100644 index ed1b7ab263d72..0000000000000 --- a/rust/qemu-api/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# QEMU bindings and API wrappers - -This library exports helper Rust types, Rust macros and C FFI bindings for internal QEMU APIs. - -The C bindings can be generated with `bindgen`, using this build target: - -```console -$ make bindings.inc.rs -``` - -## Generate Rust documentation - -Common Cargo tasks can be performed from the QEMU build directory - -```console -$ make clippy -$ make rustfmt -$ make rustdoc -``` diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs deleted file mode 120000 index 71a3167885c23..0000000000000 --- a/rust/qemu-api/build.rs +++ /dev/null @@ -1 +0,0 @@ -../util/build.rs \ No newline at end of file diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build deleted file mode 100644 index fe81f16d9900a..0000000000000 --- a/rust/qemu-api/meson.build +++ /dev/null @@ -1,75 +0,0 @@ -_qemu_api_cfg = run_command(rustc_args, - '--config-headers', config_host_h, '--features', files('Cargo.toml'), - capture: true, check: true).stdout().strip().splitlines() - -c_enums = [ - 'MemoryDeviceInfoKind', -] -_qemu_api_bindgen_args = [] -foreach enum : c_enums - _qemu_api_bindgen_args += ['--rustified-enum', enum] -endforeach - -blocked_type = [ - 'Chardev', - 'Error', - 'MemTxAttrs', - 'MemoryRegion', - 'ObjectClass', - 'VMStateDescription', - 'device_endian', -] -foreach type: blocked_type - _qemu_api_bindgen_args += ['--blocklist-type', type] -endforeach - -# TODO: Remove this comment when the clang/libclang mismatch issue is solved. -# -# Rust bindings generation with `bindgen` might fail in some cases where the -# detected `libclang` does not match the expected `clang` version/target. In -# this case you must pass the path to `clang` and `libclang` to your build -# command invocation using the environment variables CLANG_PATH and -# LIBCLANG_PATH -_qemu_api_bindings_inc_rs = rust.bindgen( - input: 'wrapper.h', - dependencies: common_ss.all_dependencies(), - output: 'bindings.inc.rs', - include_directories: bindings_incdir, - bindgen_version: ['>=0.60.0'], - args: bindgen_args_common + _qemu_api_bindgen_args, - ) - -_qemu_api_rs = static_library( - 'qemu_api', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/prelude.rs', - ], - {'.' : _qemu_api_bindings_inc_rs}, - ), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, hwcore_rs, libc_rs, migration_rs, qemu_macros, - qom_rs, system_rs, util_rs, hwcore], -) - -qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], - dependencies: [qemu_macros, qom, hwcore, chardev, migration]) - -test('rust-qemu-api-integration', - executable( - 'rust-qemu-api-integration', - files('tests/vmstate_tests.rs'), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_args: ['--test'], - install: false, - dependencies: [bql_rs, common_rs, util_rs, migration_rs, qom_rs, qemu_api_rs]), - args: [ - '--test', '--test-threads', '1', - '--format', 'pretty', - ], - protocol: 'rust', - suite: ['unit', 'rust']) diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs deleted file mode 100644 index 9c863e9b5b40f..0000000000000 --- a/rust/qemu-api/src/bindings.rs +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#![allow( - dead_code, - improper_ctypes_definitions, - improper_ctypes, - non_camel_case_types, - non_snake_case, - non_upper_case_globals, - unnecessary_transmutes, - unsafe_op_in_unsafe_fn, - clippy::pedantic, - clippy::restriction, - clippy::style, - clippy::missing_const_for_fn, - clippy::ptr_offset_with_cast, - clippy::useless_transmute, - clippy::missing_safety_doc, - clippy::too_many_arguments -)] - -//! `bindgen`-generated declarations. - -use chardev::bindings::Chardev; -use migration::bindings::VMStateDescription; -use qom::bindings::ObjectClass; -use system::bindings::{device_endian, MemTxAttrs, MemoryRegion}; -use util::bindings::Error; - -#[cfg(MESON)] -include!("bindings.inc.rs"); - -#[cfg(not(MESON))] -include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs deleted file mode 100644 index 21b886035f33c..0000000000000 --- a/rust/qemu-api/src/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -#![cfg_attr(not(MESON), doc = include_str!("../README.md"))] -#![deny(clippy::missing_const_for_fn)] - -#[rustfmt::skip] -pub mod bindings; - -// preserve one-item-per-"use" syntax, it is clearer -// for prelude-like modules -#[rustfmt::skip] -pub mod prelude; - -// Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this -// crate). -extern crate self as qemu_api; diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs deleted file mode 100644 index 8db56f9f81789..0000000000000 --- a/rust/qemu-api/src/prelude.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2024 Red Hat, Inc. -// Author(s): Paolo Bonzini -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Commonly used traits and types for QEMU. diff --git a/rust/qemu-api/wrapper.h b/rust/qemu-api/wrapper.h deleted file mode 100644 index 7c9c20b14fe71..0000000000000 --- a/rust/qemu-api/wrapper.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * QEMU System Emulator - * - * Copyright (c) 2024 Linaro Ltd. - * - * Authors: Manos Pitsidianakis - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - - -/* - * This header file is meant to be used as input to the `bindgen` application - * in order to generate C FFI compatible Rust bindings. - */ - -#ifndef __CLANG_STDATOMIC_H -#define __CLANG_STDATOMIC_H -/* - * Fix potential missing stdatomic.h error in case bindgen does not insert the - * correct libclang header paths on its own. We do not use stdatomic.h symbols - * in QEMU code, so it's fine to declare dummy types instead. - */ -typedef enum memory_order { - memory_order_relaxed, - memory_order_consume, - memory_order_acquire, - memory_order_release, - memory_order_acq_rel, - memory_order_seq_cst, -} memory_order; -#endif /* __CLANG_STDATOMIC_H */ - -#include "qemu/osdep.h" -#include "qemu-io.h" -#include "exec/memattrs.h" -#include "hw/char/pl011.h" diff --git a/rust/qemu-api/Cargo.toml b/rust/tests/Cargo.toml similarity index 78% rename from rust/qemu-api/Cargo.toml rename to rust/tests/Cargo.toml index 9abb88aa1f8a0..8d106d896d77a 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/tests/Cargo.toml @@ -1,12 +1,11 @@ [package] -name = "qemu_api" +name = "tests" version = "0.1.0" -authors = ["Manos Pitsidianakis "] -description = "Rust bindings for QEMU" -readme = "README.md" +description = "Rust integration tests for QEMU" resolver = "2" publish = false +authors.workspace = true edition.workspace = true homepage.workspace = true license.workspace = true diff --git a/rust/tests/meson.build b/rust/tests/meson.build new file mode 100644 index 0000000000000..00688c66fb134 --- /dev/null +++ b/rust/tests/meson.build @@ -0,0 +1,14 @@ +test('rust-integration', + executable( + 'rust-integration', + files('tests/vmstate_tests.rs'), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_args: ['--test'], + install: false, + dependencies: [bql_rs, common_rs, util_rs, migration_rs, qom_rs]), + args: [ + '--test', '--test-threads', '1', + '--format', 'pretty', + ], + protocol: 'rust', + suite: ['unit', 'rust']) diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/tests/tests/vmstate_tests.rs similarity index 100% rename from rust/qemu-api/tests/vmstate_tests.rs rename to rust/tests/tests/vmstate_tests.rs From b0f6bf8a5b3b3a9dd4a11f792084d39a76b4b007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:50:01 +0200 Subject: [PATCH 0753/1794] rust: re-export qemu_macros internal helper in "bits" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid the need to import "qemu_macros". Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-21-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/bits/src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rust/bits/src/lib.rs b/rust/bits/src/lib.rs index 1bc882fde1865..d1141f7c882a3 100644 --- a/rust/bits/src/lib.rs +++ b/rust/bits/src/lib.rs @@ -380,14 +380,17 @@ macro_rules! bits { }; { $type:ty: $expr:expr } => { - ::qemu_macros::bits_const_internal! { $type @ ($expr) } + $crate::bits_const_internal! { $type @ ($expr) } }; { $type:ty as $int_type:ty: $expr:expr } => { - (::qemu_macros::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type + ($crate::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type }; } +#[doc(hidden)] +pub use qemu_macros::bits_const_internal; + #[cfg(test)] mod test { bits! { From e4444d71e85b5f5ea8311eb59fea3e52f5fc5a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:50:02 +0200 Subject: [PATCH 0754/1794] rust: re-export qemu macros from common/qom/hwcore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is just a bit nicer. Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-22-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 2 +- rust/Cargo.lock | 8 +------- rust/chardev/Cargo.toml | 1 - rust/chardev/meson.build | 2 +- rust/chardev/src/chardev.rs | 2 +- rust/common/Cargo.toml | 1 + rust/common/meson.build | 2 +- rust/common/src/lib.rs | 2 ++ rust/common/src/opaque.rs | 4 +--- rust/hw/char/pl011/Cargo.toml | 1 - rust/hw/char/pl011/meson.build | 1 - rust/hw/char/pl011/src/device.rs | 4 ++-- rust/hw/char/pl011/src/registers.rs | 2 +- rust/hw/core/Cargo.toml | 2 +- rust/hw/core/meson.build | 2 +- rust/hw/core/src/irq.rs | 2 +- rust/hw/core/src/lib.rs | 1 + rust/hw/core/src/qdev.rs | 4 ++-- rust/hw/core/src/sysbus.rs | 2 +- rust/hw/core/tests/tests.rs | 4 ++-- rust/hw/timer/hpet/Cargo.toml | 1 - rust/hw/timer/hpet/meson.build | 1 - rust/hw/timer/hpet/src/device.rs | 6 +++--- rust/meson.build | 3 ++- rust/migration/Cargo.toml | 1 - rust/qom/src/lib.rs | 2 ++ rust/qom/src/qom.rs | 4 ++-- rust/system/Cargo.toml | 1 - rust/system/meson.build | 2 +- rust/system/src/memory.rs | 2 +- rust/tests/Cargo.toml | 1 - rust/util/Cargo.toml | 1 - rust/util/meson.build | 2 +- rust/util/src/timer.rs | 4 ++-- 34 files changed, 35 insertions(+), 45 deletions(-) diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 20d15347defb4..29eb48af35b51 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -278,7 +278,7 @@ a raw pointer, for use in calls to C functions. It can be used for example as follows:: #[repr(transparent)] - #[derive(Debug, qemu_api_macros::Wrapper)] + #[derive(Debug, common::Wrapper)] pub struct Object(Opaque); where the special ``derive`` macro provides useful methods such as diff --git a/rust/Cargo.lock b/rust/Cargo.lock index ac79c6a34a967..eea928621a7db 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -58,7 +58,6 @@ dependencies = [ "bql", "common", "migration", - "qemu_macros", "qom", "util", ] @@ -68,6 +67,7 @@ name = "common" version = "0.1.0" dependencies = [ "libc", + "qemu_macros", ] [[package]] @@ -93,7 +93,6 @@ dependencies = [ "common", "hwcore", "migration", - "qemu_macros", "qom", "system", "util", @@ -133,7 +132,6 @@ name = "migration" version = "0.1.0" dependencies = [ "common", - "qemu_macros", "util", ] @@ -149,7 +147,6 @@ dependencies = [ "common", "hwcore", "migration", - "qemu_macros", "qom", "system", "util", @@ -232,7 +229,6 @@ name = "system" version = "0.1.0" dependencies = [ "common", - "qemu_macros", "qom", "util", ] @@ -246,7 +242,6 @@ dependencies = [ "common", "hwcore", "migration", - "qemu_macros", "qom", "system", "util", @@ -266,7 +261,6 @@ dependencies = [ "common", "foreign", "libc", - "qemu_macros", ] [[package]] diff --git a/rust/chardev/Cargo.toml b/rust/chardev/Cargo.toml index c13917730786b..3e77972546eb6 100644 --- a/rust/chardev/Cargo.toml +++ b/rust/chardev/Cargo.toml @@ -18,7 +18,6 @@ bql = { path = "../bql" } migration = { path = "../migration" } qom = { path = "../qom" } util = { path = "../util" } -qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build index a2fa3268d21cf..370895c111fd0 100644 --- a/rust/chardev/meson.build +++ b/rust/chardev/meson.build @@ -38,4 +38,4 @@ _chardev_rs = static_library( dependencies: [common_rs, qemu_macros], ) -chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_macros, chardev, qemuutil]) +chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [chardev, qemuutil]) diff --git a/rust/chardev/src/chardev.rs b/rust/chardev/src/chardev.rs index cb6f99398eb30..2014479674f28 100644 --- a/rust/chardev/src/chardev.rs +++ b/rust/chardev/src/chardev.rs @@ -26,7 +26,7 @@ use crate::bindings; /// A safe wrapper around [`bindings::Chardev`]. #[repr(transparent)] -#[derive(qemu_macros::Wrapper)] +#[derive(common::Wrapper)] pub struct Chardev(Opaque); pub type ChardevClass = bindings::ChardevClass; diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml index 5e106427e80a2..0e1b4fc505027 100644 --- a/rust/common/Cargo.toml +++ b/rust/common/Cargo.toml @@ -14,6 +14,7 @@ rust-version.workspace = true [dependencies] libc.workspace = true +qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/common/meson.build b/rust/common/meson.build index 230a967760dce..b805e0faf573d 100644 --- a/rust/common/meson.build +++ b/rust/common/meson.build @@ -19,7 +19,7 @@ _common_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _common_cfg, - dependencies: [libc_rs], + dependencies: [libc_rs, qemu_macros], ) common_rs = declare_dependency(link_with: [_common_rs]) diff --git a/rust/common/src/lib.rs b/rust/common/src/lib.rs index 25216503aab28..8311bf945da84 100644 --- a/rust/common/src/lib.rs +++ b/rust/common/src/lib.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later +pub use qemu_macros::{TryInto, Wrapper}; + pub mod assertions; pub mod bitops; diff --git a/rust/common/src/opaque.rs b/rust/common/src/opaque.rs index 3b3263acaa340..c941fb45462d8 100644 --- a/rust/common/src/opaque.rs +++ b/rust/common/src/opaque.rs @@ -192,7 +192,7 @@ impl Opaque { /// Annotates [`Self`] as a transparent wrapper for another type. /// -/// Usually defined via the [`qemu_macros::Wrapper`] derive macro. +/// Usually defined via the [`crate::Wrapper`] derive macro. /// /// # Examples /// @@ -227,8 +227,6 @@ impl Opaque { /// ``` /// /// They are not defined here to allow them to be `const`. -/// -/// [`qemu_macros::Wrapper`]: ../../qemu_macros/derive.Wrapper.html pub unsafe trait Wrapper { type Wrapped; } diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 285d25c21788a..b2418abc4bfe9 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -24,7 +24,6 @@ qom = { path = "../../../qom" } chardev = { path = "../../../chardev" } system = { path = "../../../system" } hwcore = { path = "../../../hw/core" } -qemu_macros = { path = "../../../qemu-macros" } [lints] workspace = true diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index a14993f6921c5..628a523870221 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -35,7 +35,6 @@ _libpl011_rs = static_library( util_rs, migration_rs, bql_rs, - qemu_macros, qom_rs, chardev_rs, system_rs, diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 85626a969d41f..1b4587d5f6071 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -97,7 +97,7 @@ pub struct PL011Registers { } #[repr(C)] -#[derive(qemu_macros::Object, qemu_macros::Device)] +#[derive(qom::Object, hwcore::Device)] /// PL011 Device Model in QEMU pub struct PL011State { pub parent_obj: ParentField, @@ -683,7 +683,7 @@ pub unsafe extern "C" fn pl011_create( } #[repr(C)] -#[derive(qemu_macros::Object, qemu_macros::Device)] +#[derive(qom::Object, hwcore::Device)] /// PL011 Luminary device model. pub struct PL011Luminary { parent_obj: ParentField, diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs index a1c41347ed5bf..0c3a4d7d214e5 100644 --- a/rust/hw/char/pl011/src/registers.rs +++ b/rust/hw/char/pl011/src/registers.rs @@ -16,7 +16,7 @@ use migration::{impl_vmstate_bitsized, impl_vmstate_forward}; #[doc(alias = "offset")] #[allow(non_camel_case_types)] #[repr(u64)] -#[derive(Debug, Eq, PartialEq, qemu_macros::TryInto)] +#[derive(Debug, Eq, PartialEq, common::TryInto)] pub enum RegisterOffset { /// Data Register /// diff --git a/rust/hw/core/Cargo.toml b/rust/hw/core/Cargo.toml index 0eb9ffee2638c..9a9aa517082ed 100644 --- a/rust/hw/core/Cargo.toml +++ b/rust/hw/core/Cargo.toml @@ -13,6 +13,7 @@ repository.workspace = true rust-version.workspace = true [dependencies] +qemu_macros = { path = "../../qemu-macros" } common = { path = "../../common" } bql = { path = "../../bql" } qom = { path = "../../qom" } @@ -20,7 +21,6 @@ chardev = { path = "../../chardev" } migration = { path = "../../migration" } system = { path = "../../system" } util = { path = "../../util" } -qemu_macros = { path = "../../qemu-macros" } [lints] workspace = true diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build index 67eacf854fe7f..81d8c77f9ad7a 100644 --- a/rust/hw/core/meson.build +++ b/rust/hw/core/meson.build @@ -71,7 +71,7 @@ test('rust-hwcore-rs-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, qemu_macros, util_rs]), + dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, util_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/hw/core/src/irq.rs b/rust/hw/core/src/irq.rs index d8d964cad27a2..e0d7784d97bcc 100644 --- a/rust/hw/core/src/irq.rs +++ b/rust/hw/core/src/irq.rs @@ -18,7 +18,7 @@ use crate::bindings::{self, qemu_set_irq}; /// An opaque wrapper around [`bindings::IRQState`]. #[repr(transparent)] -#[derive(Debug, qemu_macros::Wrapper)] +#[derive(Debug, common::Wrapper)] pub struct IRQState(Opaque); /// Interrupt sources are used by devices to pass changes to a value (typically diff --git a/rust/hw/core/src/lib.rs b/rust/hw/core/src/lib.rs index c5588d9bc279c..b40801eb843f7 100644 --- a/rust/hw/core/src/lib.rs +++ b/rust/hw/core/src/lib.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later +pub use qemu_macros::Device; pub use qom; pub mod bindings; diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs index c9faf44a71d36..71b9ef141c3a4 100644 --- a/rust/hw/core/src/qdev.rs +++ b/rust/hw/core/src/qdev.rs @@ -23,7 +23,7 @@ use crate::{ /// A safe wrapper around [`bindings::Clock`]. #[repr(transparent)] -#[derive(Debug, qemu_macros::Wrapper)] +#[derive(Debug, common::Wrapper)] pub struct Clock(Opaque); unsafe impl Send for Clock {} @@ -31,7 +31,7 @@ unsafe impl Sync for Clock {} /// A safe wrapper around [`bindings::DeviceState`]. #[repr(transparent)] -#[derive(Debug, qemu_macros::Wrapper)] +#[derive(Debug, common::Wrapper)] pub struct DeviceState(Opaque); unsafe impl Send for DeviceState {} diff --git a/rust/hw/core/src/sysbus.rs b/rust/hw/core/src/sysbus.rs index 92c7449b802d4..282315fce99ff 100644 --- a/rust/hw/core/src/sysbus.rs +++ b/rust/hw/core/src/sysbus.rs @@ -19,7 +19,7 @@ use crate::{ /// A safe wrapper around [`bindings::SysBusDevice`]. #[repr(transparent)] -#[derive(Debug, qemu_macros::Wrapper)] +#[derive(Debug, common::Wrapper)] pub struct SysBusDevice(Opaque); unsafe impl Send for SysBusDevice {} diff --git a/rust/hw/core/tests/tests.rs b/rust/hw/core/tests/tests.rs index 2f08b8f3bfebf..247d812866dff 100644 --- a/rust/hw/core/tests/tests.rs +++ b/rust/hw/core/tests/tests.rs @@ -17,7 +17,7 @@ pub const VMSTATE: VMStateDescription = VMStateDescriptionBuilder::< .build(); #[repr(C)] -#[derive(qemu_macros::Object, qemu_macros::Device)] +#[derive(qom::Object, hwcore::Device)] pub struct DummyState { parent: ParentField, #[property(rename = "migrate-clk", default = true)] @@ -54,7 +54,7 @@ impl DeviceImpl for DummyState { } #[repr(C)] -#[derive(qemu_macros::Object, qemu_macros::Device)] +#[derive(qom::Object, hwcore::Device)] pub struct DummyChildState { parent: ParentField, } diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index 08bf97af55324..f781b28d8b3ca 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -17,7 +17,6 @@ migration = { path = "../../../migration" } bql = { path = "../../../bql" } qom = { path = "../../../qom" } system = { path = "../../../system" } -qemu_macros = { path = "../../../qemu-macros" } hwcore = { path = "../../../hw/core" } [lints] diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index 8ab26630d959c..b6bb9477f0c7d 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -8,7 +8,6 @@ _libhpet_rs = static_library( util_rs, migration_rs, bql_rs, - qemu_macros, qom_rs, system_rs, hwcore_rs, diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 07e0f639fc4cb..3cfbe9c32bb18 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -97,7 +97,7 @@ const HPET_TN_CFG_FSB_CAP_SHIFT: usize = 15; /// Timer N Interrupt Routing Capability (bits 32:63) const HPET_TN_CFG_INT_ROUTE_CAP_SHIFT: usize = 32; -#[derive(qemu_macros::TryInto)] +#[derive(common::TryInto)] #[repr(u64)] #[allow(non_camel_case_types)] /// Timer registers, masked by 0x18 @@ -110,7 +110,7 @@ enum TimerRegister { ROUTE = 16, } -#[derive(qemu_macros::TryInto)] +#[derive(common::TryInto)] #[repr(u64)] #[allow(non_camel_case_types)] /// Global registers @@ -520,7 +520,7 @@ impl HPETTimer { /// HPET Event Timer Block Abstraction #[repr(C)] -#[derive(qemu_macros::Object)] +#[derive(qom::Object)] pub struct HPETState { parent_obj: ParentField, iomem: MemoryRegion, diff --git a/rust/meson.build b/rust/meson.build index bd9b9cb83eaaa..c7bd6aba45f45 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -20,8 +20,9 @@ proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true) genrs = [] -subdir('common') subdir('qemu-macros') + +subdir('common') subdir('bits') subdir('util') subdir('migration') diff --git a/rust/migration/Cargo.toml b/rust/migration/Cargo.toml index 66af81e0a35b5..708bfaaa68256 100644 --- a/rust/migration/Cargo.toml +++ b/rust/migration/Cargo.toml @@ -15,7 +15,6 @@ rust-version.workspace = true [dependencies] common = { path = "../common" } util = { path = "../util" } -qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/qom/src/lib.rs b/rust/qom/src/lib.rs index 204c6fea2ffea..24c44fc2afb50 100644 --- a/rust/qom/src/lib.rs +++ b/rust/qom/src/lib.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later +pub use qemu_macros::Object; + pub mod bindings; // preserve one-item-per-"use" syntax, it is clearer diff --git a/rust/qom/src/qom.rs b/rust/qom/src/qom.rs index 2cd1d85011201..5808051cd77ec 100644 --- a/rust/qom/src/qom.rs +++ b/rust/qom/src/qom.rs @@ -112,7 +112,7 @@ pub use crate::bindings::{type_register_static, ObjectClass}; /// A safe wrapper around [`bindings::Object`]. #[repr(transparent)] -#[derive(Debug, qemu_macros::Wrapper)] +#[derive(Debug, common::Wrapper)] pub struct Object(Opaque); unsafe impl Send for Object {} @@ -173,7 +173,7 @@ macro_rules! qom_isa { /// /// ```ignore /// #[repr(C)] -/// #[derive(qemu_macros::Object)] +/// #[derive(qom::Object)] /// pub struct MyDevice { /// parent: ParentField, /// ... diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml index d8338c8348d59..7fd369b9e3239 100644 --- a/rust/system/Cargo.toml +++ b/rust/system/Cargo.toml @@ -16,7 +16,6 @@ rust-version.workspace = true common = { path = "../common" } qom = { path = "../qom" } util = { path = "../util" } -qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/system/meson.build b/rust/system/meson.build index 9f88166f3d394..3ec140de01877 100644 --- a/rust/system/meson.build +++ b/rust/system/meson.build @@ -39,4 +39,4 @@ _system_rs = static_library( ) system_rs = declare_dependency(link_with: [_system_rs], - dependencies: [qemu_macros, hwcore]) + dependencies: [hwcore]) diff --git a/rust/system/src/memory.rs b/rust/system/src/memory.rs index 7312f809f51b3..02aa3af7b130c 100644 --- a/rust/system/src/memory.rs +++ b/rust/system/src/memory.rs @@ -129,7 +129,7 @@ impl Default for MemoryRegionOpsBuilder { /// A safe wrapper around [`bindings::MemoryRegion`]. #[repr(transparent)] -#[derive(qemu_macros::Wrapper)] +#[derive(common::Wrapper)] pub struct MemoryRegion(Opaque); unsafe impl Send for MemoryRegion {} diff --git a/rust/tests/Cargo.toml b/rust/tests/Cargo.toml index 8d106d896d77a..d47dc3314d81b 100644 --- a/rust/tests/Cargo.toml +++ b/rust/tests/Cargo.toml @@ -19,7 +19,6 @@ hwcore = { path = "../hw/core" } migration = { path = "../migration" } util = { path = "../util" } bql = { path = "../bql" } -qemu_macros = { path = "../qemu-macros" } qom = { path = "../qom" } system = { path = "../system" } diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml index 18e6619ca0662..1f6767ed9d102 100644 --- a/rust/util/Cargo.toml +++ b/rust/util/Cargo.toml @@ -17,7 +17,6 @@ anyhow = { workspace = true } foreign = { workspace = true } libc = { workspace = true } common = { path = "../common" } -qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/util/meson.build b/rust/util/meson.build index 197872c9b23ac..87a893673d23b 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -39,7 +39,7 @@ _util_rs = static_library( ), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', - dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qemu_macros, qom, qemuutil], + dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qom, qemuutil], ) util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom]) diff --git a/rust/util/src/timer.rs b/rust/util/src/timer.rs index 622b6ee30976d..c6b3e4088ecb1 100644 --- a/rust/util/src/timer.rs +++ b/rust/util/src/timer.rs @@ -15,14 +15,14 @@ use crate::bindings::{ /// A safe wrapper around [`bindings::QEMUTimer`]. #[repr(transparent)] -#[derive(Debug, qemu_macros::Wrapper)] +#[derive(Debug, common::Wrapper)] pub struct Timer(Opaque); unsafe impl Send for Timer {} unsafe impl Sync for Timer {} #[repr(transparent)] -#[derive(qemu_macros::Wrapper)] +#[derive(common::Wrapper)] pub struct TimerListGroup(Opaque); unsafe impl Send for TimerListGroup {} From 31b35e78be99ae0c9b7a50fa4f29c0041538ceea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 8 Sep 2025 12:50:03 +0200 Subject: [PATCH 0755/1794] docs: update rust.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-23-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 61 ++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 29eb48af35b51..13a20e86a1605 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -103,15 +103,18 @@ anymore. Writing Rust code in QEMU ------------------------- -QEMU includes four crates: +QEMU includes several crates: -* ``qemu_api`` for bindings to C code and useful functionality +* ``common`` provides Rust-only utilities -* ``qemu_api_macros`` defines several procedural macros that are useful when +* ``bql``, ``chardev``, ``hw/core``, ``migration``, ``qom``, ``system``, + ``util`` for bindings to respective QEMU C library APIs + +* ``qemu_macros`` defines several procedural macros that are useful when writing C code * ``pl011`` (under ``rust/hw/char/pl011``) and ``hpet`` (under ``rust/hw/timer/hpet``) - are sample devices that demonstrate ``qemu_api`` and ``qemu_api_macros``, and are + are sample devices that demonstrate Rust binding usage and ``qemu_macros``, and are used to further develop them. These two crates are functional\ [#issues]_ replacements for the ``hw/char/pl011.c`` and ``hw/timer/hpet.c`` files. @@ -124,7 +127,7 @@ This section explains how to work with them. Status '''''' -Modules of ``qemu_api`` can be defined as: +The stability of the modules can be defined as: - *complete*: ready for use in new devices; if applicable, the API supports the full functionality available in C @@ -140,26 +143,26 @@ Modules of ``qemu_api`` can be defined as: The status of the modules is as follows: -================ ====================== -module status -================ ====================== -``assertions`` stable -``bitops`` complete -``callbacks`` complete -``cell`` stable -``errno`` complete -``error`` stable -``irq`` complete -``log`` proof of concept -``memory`` stable -``module`` complete -``qdev`` stable -``qom`` stable -``sysbus`` stable -``timer`` stable -``vmstate`` stable -``zeroable`` stable -================ ====================== +========================== ====================== +module status +========================== ====================== +``bql::cell`` stable +``common::assertions`` stable +``common::bitops`` complete +``common::callbacks`` complete +``common::errno`` complete +``common::zeroable`` stable +``hwcore::irq`` complete +``hwcore::qdev`` stable +``hwcore::sysbus`` stable +``migration::vmstate`` stable +``qom`` stable +``system::memory`` stable +``util::error`` stable +``util::log`` proof of concept +``util::module`` complete +``util::timer`` stable +========================== ====================== .. note:: API stability is not a promise, if anything because the C APIs are not a stable @@ -260,7 +263,7 @@ to go from a shared reference to a ``&mut``. Whenever C code provides you with an opaque ``void *``, avoid converting it to a Rust mutable reference, and use a shared reference instead. The -``qemu_api::cell`` module provides wrappers that can be used to tell the +``bql::cell`` module provides wrappers that can be used to tell the Rust compiler about interior mutability, and optionally to enforce locking rules for the "Big QEMU Lock". In the future, similar cell types might also be provided for ``AioContext``-based locking as well. @@ -292,7 +295,7 @@ the wrapper to be declared thread-safe:: Writing bindings to C code '''''''''''''''''''''''''' -Here are some things to keep in mind when working on the ``qemu_api`` crate. +Here are some things to keep in mind when working on the QEMU Rust crate. **Look at existing code** Very often, similar idioms in C code correspond to similar tricks in @@ -355,7 +358,7 @@ from the type after ``as`` in the invocation of ``parse_macro_input!``:: .into() } -The ``qemu_api_macros`` crate has utility functions to examine a +The ``qemu_macros`` crate has utility functions to examine a ``DeriveInput`` and perform common checks (e.g. looking for a struct with named fields). These functions return ``Result<..., syn::Error>`` and can be used easily in the procedural macro function:: @@ -396,7 +399,7 @@ Right now, only the nightly version of ``rustfmt`` is supported. This might change in the future. While CI checks for correct formatting via ``cargo fmt --check``, maintainers can fix this for you when applying patches. -It is expected that ``qemu_api`` provides full ``rustdoc`` documentation for +It is expected that QEMU Rust crates provides full ``rustdoc`` documentation for bindings that are in their final shape or close. Adding dependencies From efe8d07383e22e39e4dafbc08855956a459b395f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Sep 2025 12:50:04 +0200 Subject: [PATCH 0756/1794] rust: meson: remove unnecessary complication in device crates It is not necessary anymore to explicitly list procedural macro crates when doing the final link using rustc. Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250908105005.2119297-33-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/meson.build | 3 --- rust/hw/timer/hpet/meson.build | 3 --- 2 files changed, 6 deletions(-) diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index 628a523870221..ffdc8af53f18d 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -44,8 +44,5 @@ _libpl011_rs = static_library( rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency( link_whole: [_libpl011_rs], - # Putting proc macro crates in `dependencies` is necessary for Meson to find - # them when compiling the root per-target static rust lib. - dependencies: [bilge_impl_rs, qemu_macros], variables: {'crate': 'pl011'}, )]) diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index b6bb9477f0c7d..bb64b96672e55 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -16,8 +16,5 @@ _libhpet_rs = static_library( rust_devices_ss.add(when: 'CONFIG_X_HPET_RUST', if_true: [declare_dependency( link_whole: [_libhpet_rs], - # Putting proc macro crates in `dependencies` is necessary for Meson to find - # them when compiling the root per-target static rust lib. - dependencies: [qemu_macros], variables: {'crate': 'hpet'}, )]) From 7c83e501565a57560aa1d87109819f46effeb7fc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Sep 2025 12:50:05 +0200 Subject: [PATCH 0757/1794] rust: do not inline do_init_io This is now possible since the hwcore integration tests do not link the system crate anymore. Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250908105005.2119297-34-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- rust/system/src/memory.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/rust/system/src/memory.rs b/rust/system/src/memory.rs index 02aa3af7b130c..4b3316bf767f1 100644 --- a/rust/system/src/memory.rs +++ b/rust/system/src/memory.rs @@ -136,11 +136,6 @@ unsafe impl Send for MemoryRegion {} unsafe impl Sync for MemoryRegion {} impl MemoryRegion { - // inline to ensure that it is not included in tests, which only - // link to hwcore and qom. FIXME: inlining is actually the opposite - // of what we want, since this is the type-erased version of the - // init_io function below. Look into splitting the qemu_api crate. - #[inline(always)] unsafe fn do_init_io( slot: *mut bindings::MemoryRegion, owner: *mut bindings::Object, From d99041a20328423bc6819ddd69b60cf98d8d84f1 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Wed, 10 Sep 2025 16:25:06 +0200 Subject: [PATCH 0758/1794] hpet: guard IRQ handling with BQL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit [1] made qemu fail with abort: xen_evtchn_set_gsi: Assertion `bql_locked()' failed. when running ./tests/functional/x86_64/test_kvm_xen.py tests. To fix it make sure that BQL is held when manipulating IRQs. Fixes: 7defb58baf (hpet: switch to fine-grained device locking) Reported-by: Daniel P. Berrangé Signed-off-by: Igor Mammedov Link: https://lore.kernel.org/r/20250910142506.86274-1-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- hw/timer/hpet.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 789a31d0a08a2..1acba4fa9db08 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -40,6 +40,7 @@ #include "qom/object.h" #include "qemu/lockable.h" #include "qemu/seqlock.h" +#include "qemu/main-loop.h" #include "trace.h" struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX}; @@ -222,12 +223,15 @@ static void update_irq(struct HPETTimer *timer, int set) timer->fsb & 0xffffffff, MEMTXATTRS_UNSPECIFIED, NULL); } else if (timer->config & HPET_TN_TYPE_LEVEL) { + BQL_LOCK_GUARD(); qemu_irq_raise(s->irqs[route]); } else { + BQL_LOCK_GUARD(); qemu_irq_pulse(s->irqs[route]); } } else { if (!timer_fsb_route(timer)) { + BQL_LOCK_GUARD(); qemu_irq_lower(s->irqs[route]); } } @@ -534,10 +538,12 @@ static void hpet_ram_write(void *opaque, hwaddr addr, /* i8254 and RTC output pins are disabled * when HPET is in legacy mode */ if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) { + BQL_LOCK_GUARD(); qemu_set_irq(s->pit_enabled, 0); qemu_irq_lower(s->irqs[0]); qemu_irq_lower(s->irqs[RTC_ISA_IRQ]); } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) { + BQL_LOCK_GUARD(); qemu_irq_lower(s->irqs[0]); qemu_set_irq(s->pit_enabled, 1); qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level); @@ -683,11 +689,13 @@ static void hpet_handle_legacy_irq(void *opaque, int n, int level) if (n == HPET_LEGACY_PIT_INT) { if (!hpet_in_legacy_mode(s)) { + BQL_LOCK_GUARD(); qemu_set_irq(s->irqs[0], level); } } else { s->rtc_irq_level = level; if (!hpet_in_legacy_mode(s)) { + BQL_LOCK_GUARD(); qemu_set_irq(s->irqs[RTC_ISA_IRQ], level); } } From 0516f4b70264b9710a25718d21bd35ef463c875e Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Wed, 30 Jul 2025 17:52:52 +0800 Subject: [PATCH 0759/1794] i386/cpu: Enable SMM cpu address space under KVM Kirill Martynov reported assertation in cpu_asidx_from_attrs() being hit when x86_cpu_dump_state() is called to dump the CPU state[*]. It happens when the CPU is in SMM and KVM emulation failure due to misbehaving guest. The root cause is that QEMU i386 never enables the SMM address space for cpu since KVM SMM support has been added. Enable the SMM cpu address space under KVM when the SMM is enabled for the x86machine. [*] https://lore.kernel.org/qemu-devel/20250523154431.506993-1-stdcalllevi@yandex-team.ru/ Reported-by: Kirill Martynov Reviewed-by: Zhao Liu Tested-by: Kirill Martynov Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250730095253.1833411-2-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- system/physmem.c | 5 ----- target/i386/kvm/kvm-cpu.c | 10 ++++++++++ target/i386/kvm/kvm.c | 5 +++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index ddd58e9eb8243..ae8ecd50ea1bd 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -793,9 +793,6 @@ void cpu_address_space_init(CPUState *cpu, int asidx, cpu->as = as; } - /* KVM cannot currently support multiple address spaces. */ - assert(asidx == 0 || !kvm_enabled()); - if (!cpu->cpu_ases) { cpu->cpu_ases = g_new0(CPUAddressSpace, cpu->num_ases); cpu->cpu_ases_count = cpu->num_ases; @@ -818,8 +815,6 @@ void cpu_address_space_destroy(CPUState *cpu, int asidx) assert(cpu->cpu_ases); assert(asidx >= 0 && asidx < cpu->num_ases); - /* KVM cannot currently support multiple address spaces. */ - assert(asidx == 0 || !kvm_enabled()); cpuas = &cpu->cpu_ases[asidx]; if (tcg_enabled()) { diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index 89a7953659452..1dc1ba9b4869d 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -13,6 +13,7 @@ #include "qapi/error.h" #include "system/system.h" #include "hw/boards.h" +#include "hw/i386/x86.h" #include "kvm_i386.h" #include "accel/accel-cpu-target.h" @@ -91,6 +92,15 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) kvm_set_guest_phys_bits(cs); } + /* + * When SMM is enabled, there is 2 address spaces. Otherwise only 1. + * + * Only initialize address space 0 here, the second one for SMM is + * initialized at register_smram_listener() after machine init done. + */ + cs->num_ases = x86_machine_is_smm_enabled(X86_MACHINE(current_machine)) ? 2 : 1; + cpu_address_space_init(cs, 0, "cpu-memory", cs->memory); + return true; } diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 34e74f2447096..d191d7177f18a 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -2704,6 +2704,7 @@ static MemoryRegion smram_as_mem; static void register_smram_listener(Notifier *n, void *unused) { + CPUState *cpu; MemoryRegion *smram = (MemoryRegion *) object_resolve_path("/machine/smram", NULL); @@ -2728,6 +2729,10 @@ static void register_smram_listener(Notifier *n, void *unused) address_space_init(&smram_address_space, &smram_as_root, "KVM-SMRAM"); kvm_memory_listener_register(kvm_state, &smram_listener, &smram_address_space, 1, "kvm-smram"); + + CPU_FOREACH(cpu) { + cpu_address_space_init(cpu, 1, "cpu-smm", &smram_as_root); + } } static void *kvm_msr_energy_thread(void *data) From 591f817d819f5511fd9001dc863a326d23088811 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Wed, 30 Jul 2025 17:52:53 +0800 Subject: [PATCH 0760/1794] target/i386: Define enum X86ASIdx for x86's address spaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define X86ASIdx as enum, like ARM's ARMASIdx, so that it's clear index 0 is for memory and index 1 is for SMM. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Tested-By: Kirill Martynov Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250730095253.1833411-3-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 5 +++++ target/i386/kvm/kvm-cpu.c | 2 +- target/i386/kvm/kvm.c | 4 ++-- target/i386/tcg/system/tcg-cpu.c | 4 ++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index f977fc49a774e..e0be7a7406850 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2574,6 +2574,11 @@ static inline bool x86_has_cpuid_0x1f(X86CPU *cpu) void x86_cpu_set_a20(X86CPU *cpu, int a20_state); void cpu_sync_avx_hflag(CPUX86State *env); +typedef enum X86ASIdx { + X86ASIdx_MEM = 0, + X86ASIdx_SMM = 1, +} X86ASIdx; + #ifndef CONFIG_USER_ONLY static inline int x86_asidx_from_attrs(CPUState *cs, MemTxAttrs attrs) { diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index 1dc1ba9b4869d..9c25b55839557 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -99,7 +99,7 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) * initialized at register_smram_listener() after machine init done. */ cs->num_ases = x86_machine_is_smm_enabled(X86_MACHINE(current_machine)) ? 2 : 1; - cpu_address_space_init(cs, 0, "cpu-memory", cs->memory); + cpu_address_space_init(cs, X86ASIdx_MEM, "cpu-memory", cs->memory); return true; } diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index d191d7177f18a..28012d2b578b6 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -2728,10 +2728,10 @@ static void register_smram_listener(Notifier *n, void *unused) address_space_init(&smram_address_space, &smram_as_root, "KVM-SMRAM"); kvm_memory_listener_register(kvm_state, &smram_listener, - &smram_address_space, 1, "kvm-smram"); + &smram_address_space, X86ASIdx_SMM, "kvm-smram"); CPU_FOREACH(cpu) { - cpu_address_space_init(cpu, 1, "cpu-smm", &smram_as_root); + cpu_address_space_init(cpu, X86ASIdx_SMM, "cpu-smm", &smram_as_root); } } diff --git a/target/i386/tcg/system/tcg-cpu.c b/target/i386/tcg/system/tcg-cpu.c index 0538a4fd51a35..7255862c2449f 100644 --- a/target/i386/tcg/system/tcg-cpu.c +++ b/target/i386/tcg/system/tcg-cpu.c @@ -74,8 +74,8 @@ bool tcg_cpu_realizefn(CPUState *cs, Error **errp) memory_region_set_enabled(cpu->cpu_as_mem, true); cs->num_ases = 2; - cpu_address_space_init(cs, 0, "cpu-memory", cs->memory); - cpu_address_space_init(cs, 1, "cpu-smm", cpu->cpu_as_root); + cpu_address_space_init(cs, X86ASIdx_MEM, "cpu-memory", cs->memory); + cpu_address_space_init(cs, X86ASIdx_SMM, "cpu-smm", cpu->cpu_as_root); /* ... SMRAM with higher priority, linked from /machine/smram. */ cpu->machine_done.notify = tcg_cpu_machine_done; From 4c8f69b94839f72314c69902312068d0b9b05a34 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Fri, 8 Aug 2025 11:50:27 +0800 Subject: [PATCH 0761/1794] multiboot: Fix the split lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While running the kvm-unit-tests on Intel platforms with "split lock disable" feature, every test triggers a kernel warning of x86/split lock detection: #AC: qemu-system-x86_64/373232 took a split_lock trap at address: 0x1e3 Hack KVM by exiting to QEMU on split lock #AC, we get KVM: exception 17 exit (error code 0x0) EAX=00000001 EBX=00000000 ECX=00000014 EDX=0001fb80 ESI=00000000 EDI=000000a8 EBP=00000000 ESP=00006f10 EIP=000001e3 EFL=00010002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0 ES =0900 00009000 0000ffff 00009300 DPL=0 DS16 [-WA] CS =c000 000c0000 0000ffff 00009b00 DPL=0 CS16 [-RA] SS =0000 00000000 0000ffff 00009300 DPL=0 DS16 [-WA] DS =c000 000c0000 0000ffff 00009300 DPL=0 DS16 [-WA] FS =0950 00009500 0000ffff 00009300 DPL=0 DS16 [-WA] GS =06f2 00006f20 0000ffff 00009300 DPL=0 DS16 [-WA] LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy GDT= 000c02b4 00000027 IDT= 00000000 000003ff CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000 DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 DR6=00000000ffff0ff0 DR7=0000000000000400 EFER=0000000000000000 Code=89 16 08 00 65 66 0f 01 16 06 00 66 b8 01 00 00 00 0f 22 c0 <65> 66 ff 2e 00 00 b8 10 00 00 00 8e d0 8e d8 8e c0 8e e0 8e e8 66 b8 08 00 66 ba 10 05 66 And it matches with what disassembled from multiboo_dma.bin: #objdump -b binary -m i386 -D pc-bios/multiboot_dma.bin 1d1: 08 00 or %al,(%eax) 1d3: 65 66 0f 01 16 lgdtw %gs:(%esi) 1d8: 06 push %es 1d9: 00 66 b8 add %ah,-0x48(%esi) 1dc: 01 00 add %eax,(%eax) 1de: 00 00 add %al,(%eax) 1e0: 0f 22 c0 mov %eax,%cr0 > 1e3: 65 66 ff 2e ljmpw *%gs:(%esi) 1e7: 00 00 add %al,(%eax) 1e9: b8 10 00 00 00 mov $0x10,%eax 1ee: 8e d0 mov %eax,%ss 1f0: 8e d8 mov %eax,%ds 1f2: 8e c0 mov %eax,%es 1f4: 8e e0 mov %eax,%fs 1f6: 8e e8 mov %eax,%gs 1f8: 66 b8 08 00 mov $0x8,%ax 1fc: 66 ba 10 05 mov $0x510,%dx We can see that the instruction at 0x1e3 is a far jmp through the GDT. However, the GDT is not 8 byte aligned, the base is 0xc02b4. Intel processors follow the LOCK semantics to set the accessed flag of the segment descriptor when loading a segment descriptor. If the the segment descriptor crosses two cache line, it causes split lock. Fix it by aligning the GDT on 8 bytes, so that segment descriptor cannot span two cache lines. Signed-off-by: Xiaoyao Li Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250808035027.2194673-1-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- pc-bios/multiboot_dma.bin | Bin 1024 -> 1024 bytes pc-bios/optionrom/multiboot.S | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/multiboot_dma.bin b/pc-bios/multiboot_dma.bin index c0e2c3102a3358207c61d3ae113524fb6007abc3..e6d0c97093b4fc11bc87fd58cbe6e577c2841cd0 100644 GIT binary patch delta 42 ycmZqRXyBNj#r%xv^+a8N?gvb%opNjpY29* Date: Tue, 29 Jul 2025 14:20:14 +0800 Subject: [PATCH 0762/1794] i386/kvm: Drop KVM_CAP_X86_SMM check in kvm_arch_init() x86_machine_is_smm_enabled() checks the KVM_CAP_X86_SMM for KVM case. No need to check KVM_CAP_X86_SMM in kvm_arch_init(). So just drop the check of KVM_CAP_X86_SMM to simplify the code. Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250729062014.1669578-3-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 28012d2b578b6..6a3a1c1ed8ea8 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -3315,8 +3315,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) return ret; } - if (kvm_check_extension(s, KVM_CAP_X86_SMM) && - object_dynamic_cast(OBJECT(ms), TYPE_X86_MACHINE) && + if (object_dynamic_cast(OBJECT(ms), TYPE_X86_MACHINE) && x86_machine_is_smm_enabled(X86_MACHINE(ms))) { smram_machine_done.notify = register_smram_listener; qemu_add_machine_init_done_notifier(&smram_machine_done); From 706cc708656c91552b355faa9b72dfa7ad64571e Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Mon, 28 Jul 2025 19:57:05 +0800 Subject: [PATCH 0763/1794] accel/kvm: Switch to check KVM_CAP_GUEST_MEMFD and KVM_CAP_USER_MEMORY2 on VM It returns more accruate result on checking KVM_CAP_GUEST_MEMFD and KVM_CAP_USER_MEMORY2 on VM instance instead of on KVM platform. Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250728115707.1374614-2-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index d13156bee8712..633b72bde2d6c 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2776,8 +2776,8 @@ static int kvm_init(AccelState *as, MachineState *ms) kvm_supported_memory_attributes = kvm_vm_check_extension(s, KVM_CAP_MEMORY_ATTRIBUTES); kvm_guest_memfd_supported = - kvm_check_extension(s, KVM_CAP_GUEST_MEMFD) && - kvm_check_extension(s, KVM_CAP_USER_MEMORY2) && + kvm_vm_check_extension(s, KVM_CAP_GUEST_MEMFD) && + kvm_vm_check_extension(s, KVM_CAP_USER_MEMORY2) && (kvm_supported_memory_attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE); kvm_pre_fault_memory_supported = kvm_vm_check_extension(s, KVM_CAP_PRE_FAULT_MEMORY); From 80030f66ad15c1534f5e3700b0acefd6d0d92e37 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Mon, 28 Jul 2025 19:57:06 +0800 Subject: [PATCH 0764/1794] accel/kvm: Zero out mem explicitly in kvm_set_user_memory_region() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zero out the entire mem explicitly before it's used, to ensure the unused feilds (pad1, pad2) are all zeros. Otherwise, it might cause problem when the pad fields are extended by future KVM. Fixes: ce5a983233b4 ("kvm: Enable KVM_SET_USER_MEMORY_REGION2 for memslot") Signed-off-by: Xiaoyao Li Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250728115707.1374614-3-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 633b72bde2d6c..72b571a69721a 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -358,7 +358,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram, static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new) { KVMState *s = kvm_state; - struct kvm_userspace_memory_region2 mem; + struct kvm_userspace_memory_region2 mem = {}; int ret; mem.slot = slot->slot | (kml->as_id << 16); From 00c0911c68e5bd664de1a261b74c390f4c0be83d Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Mon, 28 Jul 2025 19:57:07 +0800 Subject: [PATCH 0765/1794] accel/kvm: Set guest_memfd_offset to non-zero value only when guest_memfd is valid Current QEMU unconditionally sets the guest_memfd_offset of KVMSlot in kvm_set_phys_mem(), which leads to the trace of kvm_set_user_memory looks: kvm_set_user_memory AddrSpace#0 Slot#4 flags=0x2 gpa=0xe0000 size=0x20000 ua=0x7f5840de0000 guest_memfd=-1 guest_memfd_offset=0x3e0000 ret=0 It's confusing that the guest_memfd_offset has a non-zero value while the guest_memfd is invalid (-1). Change to only set guest_memfd_offset when guest_memfd is valid and leave it as 0 when no valid guest_memfd. Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250728115707.1374614-4-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 72b571a69721a..9060599cd736a 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -1595,7 +1595,8 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, mem->ram = ram; mem->flags = kvm_mem_flags(mr); mem->guest_memfd = mr->ram_block->guest_memfd; - mem->guest_memfd_offset = (uint8_t*)ram - mr->ram_block->host; + mem->guest_memfd_offset = mem->guest_memfd >= 0 ? + (uint8_t*)ram - mr->ram_block->host : 0; kvm_slot_init_dirty_bitmap(mem); err = kvm_set_user_memory_region(kml, mem, true); From 0c042601a638c37a95690c6429af5a1b5a35d1dd Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Sat, 6 Sep 2025 15:01:58 +0800 Subject: [PATCH 0766/1794] hw/loongarch/virt: Add BSP support with aux boot code If system boots directly from Linux kernel, BSP core jumps to kernel entry of Linux kernel image and other APs jump to aux boot code. Instead BSP and APs can all jump to aux boot code like UEFI bios. With aux boot code, BSP core is judged from physical cpu id, whose cpu id is 0. With BSP core, load data to register A0-A2 and PC. Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-ID: <20250906070200.3749326-2-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/boot.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 14d6c52d4e4d5..4962668e5ac66 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -35,13 +35,19 @@ struct loongarch_linux_hdr { uint32_t pe_header_offset; } QEMU_PACKED; -static const unsigned int slave_boot_code[] = { +static const unsigned int aux_boot_code[] = { /* Configure reset ebase. */ 0x0400302c, /* csrwr $t0, LOONGARCH_CSR_EENTRY */ /* Disable interrupt. */ 0x0380100c, /* ori $t0, $zero,0x4 */ 0x04000180, /* csrxchg $zero, $t0, LOONGARCH_CSR_CRMD */ + 0x03400000, /* nop */ + + 0x0400800c, /* csrrd $t0, LOONGARCH_CSR_CPUNUM */ + 0x034ffd8c, /* andi $t0, $t0, 0x3ff */ + 0x0015000d, /* move $t1, $zero */ + 0x5800718d, /* beq $t0, $t1, 112 */ /* Clear mailbox. */ 0x1400002d, /* lu12i.w $t1, 1(0x1) */ @@ -81,6 +87,26 @@ static const unsigned int slave_boot_code[] = { 0x06480dac, /* iocsrrd.d $t0, $t1 */ 0x00150181, /* move $ra, $t0 */ 0x4c000020, /* jirl $zero, $ra,0 */ + /* BSP Core */ + 0x03400000, /* nop */ + 0x1800000d, /* pcaddi $t1, 0 */ + 0x28c0a1a4, /* ld.d $a0, $t1, 40 */ + 0x1800000d, /* pcaddi $t1, 0 */ + 0x28c0a1a5, /* ld.d $a1, $t1, 40 */ + 0x1800000d, /* pcaddi $t1, 0 */ + 0x28c0a1a6, /* ld.d $a2, $t1, 40 */ + 0x1800000d, /* pcaddi $t1, 0 */ + 0x28c0a1ac, /* ld.d $t0, $t1, 40 */ + 0x00150181, /* move $ra, $t0 */ + 0x4c000020, /* jirl $zero, $ra,0 */ + 0x00000000, /* .dword 0 A0 */ + 0x00000000, + 0x00000000, /* .dword 0 A1 */ + 0x00000000, + 0x00000000, /* .dword 0 A2 */ + 0x00000000, + 0x00000000, /* .dword 0 PC */ + 0x00000000, }; static inline void *guidcpy(void *dst, const void *src) @@ -391,6 +417,7 @@ static void loongarch_direct_kernel_boot(MachineState *ms, int64_t kernel_addr = VIRT_FLASH0_BASE; LoongArchCPU *lacpu; CPUState *cs; + uint64_t *data; if (info->kernel_filename) { kernel_addr = load_kernel_info(info); @@ -408,7 +435,12 @@ static void loongarch_direct_kernel_boot(MachineState *ms, /* Load slave boot code at pflash0 . */ void *boot_code = g_malloc0(VIRT_FLASH0_SIZE); - memcpy(boot_code, &slave_boot_code, sizeof(slave_boot_code)); + memcpy(boot_code, &aux_boot_code, sizeof(aux_boot_code)); + data = (uint64_t *)(boot_code + sizeof(aux_boot_code)); + *(data - 4) = cpu_to_le64(info->a0); + *(data - 3) = cpu_to_le64(info->a1); + *(data - 2) = cpu_to_le64(info->a2); + *(data - 1) = cpu_to_le64(kernel_addr); rom_add_blob_fixed("boot_code", boot_code, VIRT_FLASH0_SIZE, VIRT_FLASH0_BASE); CPU_FOREACH(cs) { From 6ca2df47c9b85f630faacaba8a042cb095075599 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Sat, 6 Sep 2025 15:01:59 +0800 Subject: [PATCH 0767/1794] hw/loongarch/virt: Remove unnecessay pre-boot setting with BSP With BSP core, it boots from aux boot code and loads data into register A0-A2 and PC. Pre-boot setting is not unnecessary and can be removed. Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-ID: <20250906070200.3749326-3-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/boot.c | 22 ---------------------- target/loongarch/cpu.h | 4 ---- 2 files changed, 26 deletions(-) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 4962668e5ac66..5799b4c75c372 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -353,17 +353,8 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info) static void reset_load_elf(void *opaque) { LoongArchCPU *cpu = opaque; - CPULoongArchState *env = &cpu->env; cpu_reset(CPU(cpu)); - if (env->load_elf) { - if (cpu == LOONGARCH_CPU(first_cpu)) { - env->gpr[4] = env->boot_info->a0; - env->gpr[5] = env->boot_info->a1; - env->gpr[6] = env->boot_info->a2; - } - cpu_set_pc(CPU(cpu), env->elf_address); - } } static void fw_cfg_add_kernel_info(struct loongarch_boot_info *info, @@ -415,8 +406,6 @@ static void loongarch_direct_kernel_boot(MachineState *ms, { void *p, *bp; int64_t kernel_addr = VIRT_FLASH0_BASE; - LoongArchCPU *lacpu; - CPUState *cs; uint64_t *data; if (info->kernel_filename) { @@ -443,17 +432,6 @@ static void loongarch_direct_kernel_boot(MachineState *ms, *(data - 1) = cpu_to_le64(kernel_addr); rom_add_blob_fixed("boot_code", boot_code, VIRT_FLASH0_SIZE, VIRT_FLASH0_BASE); - CPU_FOREACH(cs) { - lacpu = LOONGARCH_CPU(cs); - lacpu->env.load_elf = true; - if (cs == first_cpu) { - lacpu->env.elf_address = kernel_addr; - } else { - lacpu->env.elf_address = VIRT_FLASH0_BASE; - } - lacpu->env.boot_info = info; - } - g_free(boot_code); g_free(bp); } diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 7731f6acdc990..c8b96f74dc011 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -387,11 +387,7 @@ typedef struct CPUArchState { #endif AddressSpace *address_space_iocsr; - bool load_elf; - uint64_t elf_address; uint32_t mp_state; - - struct loongarch_boot_info *boot_info; #endif } CPULoongArchState; From cb5ee0017fc9909916383634a3f13eae05e6fe5c Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Sat, 6 Sep 2025 15:02:00 +0800 Subject: [PATCH 0768/1794] hw/loongarch/virt: Register reset interface with cpu plug callback With cpu hotplug is implemented on LoongArch virt machine, reset interface with hot-added CPU should be registered. Otherwise there will be problem if system reboots after cpu is hot-added. Now register reset interface with CPU plug callback, so that all cold/hot added CPUs let their reset interface registered. And remove reset interface with CPU unplug callback. Signed-off-by: Bibo Mao Reviewed-by: Igor Mammedov Tested-by: Song Gao Message-ID: <20250906070200.3749326-4-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/boot.c | 13 ------------- hw/loongarch/virt.c | 2 ++ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 5799b4c75c372..a516415822d9f 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -350,13 +350,6 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info) return kernel_entry; } -static void reset_load_elf(void *opaque) -{ - LoongArchCPU *cpu = opaque; - - cpu_reset(CPU(cpu)); -} - static void fw_cfg_add_kernel_info(struct loongarch_boot_info *info, FWCfgState *fw_cfg) { @@ -439,12 +432,6 @@ static void loongarch_direct_kernel_boot(MachineState *ms, void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info) { LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms); - int i; - - /* register reset function */ - for (i = 0; i < ms->smp.cpus; i++) { - qemu_register_reset(reset_load_elf, LOONGARCH_CPU(qemu_get_cpu(i))); - } info->kernel_filename = ms->kernel_filename; info->kernel_cmdline = ms->kernel_cmdline; diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 31215b778543c..bd5cff1f1e01d 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -1014,6 +1014,7 @@ static void virt_cpu_unplug(HotplugHandler *hotplug_dev, /* Notify acpi ged CPU removed */ hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &error_abort); + qemu_unregister_resettable(OBJECT(dev)); cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id); cpu_slot->cpu = NULL; } @@ -1038,6 +1039,7 @@ static void virt_cpu_plug(HotplugHandler *hotplug_dev, &error_abort); } + qemu_register_resettable(OBJECT(dev)); cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id); cpu_slot->cpu = CPU(dev); } From 6657f3bb55edba8f068cbc9ac40bb230ea1d7a09 Mon Sep 17 00:00:00 2001 From: Mark Johnston Date: Wed, 6 Aug 2025 13:53:08 -0400 Subject: [PATCH 0769/1794] 9pfs: Add FreeBSD support This is largely derived from existing Darwin support. FreeBSD apparently has better support for *at() system calls so doesn't require workarounds for a missing mknodat(). The implementation has a couple of warts however: - The extattr(2) system calls don't support anything akin to XATTR_CREATE or XATTR_REPLACE, so a racy workaround is implemented. - Attribute names cannot begin with "user." or "system." on ZFS. However FreeBSD's extattr(2) system calls support two dedicated namespaces for these two. So "user." or "system." prefixes are trimmed off from attribute names and instead EXTATTR_NAMESPACE_USER or EXTATTR_NAMESPACE_SYSTEM are picked and passed to extattr system calls accordingly. The 9pfs tests were verified to pass on the UFS, ZFS and tmpfs filesystems. Signed-off-by: Mark Johnston Link: https://lore.kernel.org/qemu-devel/aJOWhHB2p-fbueAm@nuc Signed-off-by: Christian Schoenebeck --- fsdev/file-op-9p.h | 6 +- fsdev/meson.build | 2 +- hw/9pfs/9p-synth.c | 2 +- hw/9pfs/9p-util-freebsd.c | 132 ++++++++++++++++++++++++++++++++++++++ hw/9pfs/9p-util.h | 20 ++++-- hw/9pfs/9p.c | 16 ++++- hw/9pfs/meson.build | 2 + include/qemu/xattr.h | 6 +- meson.build | 8 +-- 9 files changed, 179 insertions(+), 15 deletions(-) create mode 100644 hw/9pfs/9p-util-freebsd.c diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h index b9dae8c84c230..b85c9934deff1 100644 --- a/fsdev/file-op-9p.h +++ b/fsdev/file-op-9p.h @@ -21,9 +21,11 @@ #ifdef CONFIG_LINUX # include -#endif -#ifdef CONFIG_DARWIN +#elif defined(CONFIG_DARWIN) || defined(CONFIG_FREEBSD) # include +# ifdef CONFIG_FREEBSD +# undef MACHINE /* work around some unfortunate namespace pollution */ +# endif # include #endif diff --git a/fsdev/meson.build b/fsdev/meson.build index c751d8cb6222b..95fe816604ff7 100644 --- a/fsdev/meson.build +++ b/fsdev/meson.build @@ -5,6 +5,6 @@ fsdev_ss.add(when: ['CONFIG_FSDEV_9P'], if_true: files( '9p-marshal.c', 'qemu-fsdev.c', ), if_false: files('qemu-fsdev-dummy.c')) -if host_os in ['linux', 'darwin'] +if host_os in ['linux', 'darwin', 'freebsd'] system_ss.add_all(fsdev_ss) endif diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c index 9cd1884224218..b3743f61696d5 100644 --- a/hw/9pfs/9p-synth.c +++ b/hw/9pfs/9p-synth.c @@ -451,7 +451,7 @@ static int synth_statfs(FsContext *s, V9fsPath *fs_path, stbuf->f_bsize = 512; stbuf->f_blocks = 0; stbuf->f_files = synth_node_count; -#ifndef CONFIG_DARWIN +#if !defined(CONFIG_DARWIN) && !defined(CONFIG_FREEBSD) stbuf->f_namelen = NAME_MAX; #endif return 0; diff --git a/hw/9pfs/9p-util-freebsd.c b/hw/9pfs/9p-util-freebsd.c new file mode 100644 index 0000000000000..9dd1d069f67d0 --- /dev/null +++ b/hw/9pfs/9p-util-freebsd.c @@ -0,0 +1,132 @@ +/* + * 9p utilities (FreeBSD Implementation) + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * Not so fast! You might want to read the 9p developer docs first: + * https://wiki.qemu.org/Documentation/9p + */ + +#include "qemu/osdep.h" +#include "qemu/xattr.h" +#include "9p-util.h" + +static int mangle_xattr_name(const char **namep) +{ + const char *name = *namep; + + /* + * ZFS forbids attributes in starting with "user." or "system.". + */ + if (strncmp(name, "system.", 7) == 0) { + *namep = name + 7; + return EXTATTR_NAMESPACE_SYSTEM; + } + if (strncmp(name, "user.", 5) == 0) { + *namep = name + 5; + } + return EXTATTR_NAMESPACE_USER; +} + +ssize_t fgetxattr(int fd, const char *name, void *value, size_t size) +{ + int namespace; + + namespace = mangle_xattr_name(&name); + return extattr_get_fd(fd, namespace, name, value, size); +} + +ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name, + void *value, size_t size) +{ + ssize_t ret; + int fd, namespace; + + fd = openat_file(dirfd, filename, + O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0); + if (fd == -1) { + return -1; + } + namespace = mangle_xattr_name(&name); + ret = extattr_get_fd(fd, namespace, name, value, size); + close_preserve_errno(fd); + return ret; +} + +ssize_t flistxattrat_nofollow(int dirfd, const char *filename, + char *list, size_t size) +{ + ssize_t ret; + int fd; + + fd = openat_file(dirfd, filename, + O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0); + if (fd == -1) { + return -1; + } + ret = extattr_list_fd(fd, EXTATTR_NAMESPACE_USER, list, size); + close_preserve_errno(fd); + return ret; +} + +ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, + const char *name) +{ + int fd, namespace, ret; + + fd = openat_file(dirfd, filename, + O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0); + if (fd == -1) { + return -1; + } + namespace = mangle_xattr_name(&name); + ret = extattr_delete_fd(fd, namespace, name); + close_preserve_errno(fd); + return ret; +} + +int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name, + void *value, size_t size, int flags) +{ + ssize_t ret; + int fd, namespace; + + namespace = mangle_xattr_name(&name); + if (flags == (XATTR_CREATE | XATTR_REPLACE)) { + errno = EINVAL; + return -1; + } + fd = openat_file(dirfd, filename, + O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0); + if (fd == -1) { + return -1; + } + if (flags & (XATTR_CREATE | XATTR_REPLACE)) { + ret = extattr_get_fd(fd, namespace, name, NULL, 0); + if (ret == -1 && errno != ENOATTR) { + close_preserve_errno(fd); + return -1; + } + if (ret >= 0 && (flags & XATTR_CREATE)) { + errno = EEXIST; + close_preserve_errno(fd); + return -1; + } + if (ret == -1 && (flags & XATTR_REPLACE)) { + errno = ENOATTR; + close_preserve_errno(fd); + return -1; + } + } + ret = extattr_set_fd(fd, namespace, name, value, size); + close_preserve_errno(fd); + return ret; +} + +int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev) +{ + return mknodat(dirfd, filename, mode, dev); +} diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index a1924fe3f05a8..8dfa803dc2c04 100644 --- a/hw/9pfs/9p-util.h +++ b/hw/9pfs/9p-util.h @@ -21,6 +21,15 @@ #define O_PATH_9P_UTIL 0 #endif +#ifdef CONFIG_FREEBSD +/* + * FreeBSD does not have these flags, so we can only emulate their intended + * behaviour (racily). + */ +#define XATTR_CREATE 0x1 +#define XATTR_REPLACE 0x2 +#endif + #if !defined(CONFIG_LINUX) /* @@ -64,9 +73,9 @@ static inline uint64_t host_dev_to_dotl_dev(dev_t dev) static inline int errno_to_dotl(int err) { #if defined(CONFIG_LINUX) /* nothing to translate (Linux -> Linux) */ -#elif defined(CONFIG_DARWIN) +#elif defined(CONFIG_DARWIN) || defined(CONFIG_FREEBSD) /* - * translation mandatory for macOS hosts + * translation mandatory for non-Linux hosts * * FIXME: Only most important errnos translated here yet, this should be * extended to as many errnos being translated as possible in future. @@ -155,13 +164,13 @@ static inline int openat_file(int dirfd, const char *name, int flags, { int fd, serrno, ret; -#ifndef CONFIG_DARWIN +#if !defined(CONFIG_DARWIN) && !defined(CONFIG_FREEBSD) again: #endif fd = qemu_openat(dirfd, name, flags | O_NOFOLLOW | O_NOCTTY | O_NONBLOCK, mode); if (fd == -1) { -#ifndef CONFIG_DARWIN +#if !defined(CONFIG_DARWIN) && !defined(CONFIG_FREEBSD) if (errno == EPERM && (flags & O_NOATIME)) { /* * The client passed O_NOATIME but we lack permissions to honor it. @@ -202,6 +211,9 @@ static inline int openat_file(int dirfd, const char *name, int flags, return fd; } +#ifdef CONFIG_FREEBSD +ssize_t fgetxattr(int dirfd, const char *name, void *value, size_t size); +#endif ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name, void *value, size_t size); int fsetxattrat_nofollow(int dirfd, const char *path, const char *name, diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index acfa7db4e19cd..bc4a016ee33a4 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -136,8 +136,10 @@ static int dotl_to_open_flags(int flags) { P9_DOTL_NONBLOCK, O_NONBLOCK } , { P9_DOTL_DSYNC, O_DSYNC }, { P9_DOTL_FASYNC, FASYNC }, -#ifndef CONFIG_DARWIN +#if !defined(CONFIG_DARWIN) && !defined(CONFIG_FREEBSD) { P9_DOTL_NOATIME, O_NOATIME }, +#endif +#ifndef CONFIG_DARWIN /* * On Darwin, we could map to F_NOCACHE, which is * similar, but doesn't quite have the same @@ -3658,7 +3660,7 @@ static int v9fs_fill_statfs(V9fsState *s, V9fsPDU *pdu, struct statfs *stbuf) f_bavail = stbuf->f_bavail / bsize_factor; f_files = stbuf->f_files; f_ffree = stbuf->f_ffree; -#ifdef CONFIG_DARWIN +#if defined(CONFIG_DARWIN) || defined(CONFIG_FREEBSD) fsid_val = (unsigned int)stbuf->f_fsid.val[0] | (unsigned long long)stbuf->f_fsid.val[1] << 32; f_namelen = NAME_MAX; @@ -4050,6 +4052,16 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque) * Linux guests. */ #define P9_XATTR_SIZE_MAX 65536 +#elif defined(CONFIG_FREEBSD) +/* + * FreeBSD similarly doesn't define a maximum xattr size, the limit is + * filesystem dependent. On UFS filesystems it's 2 times the filesystem block + * size, typically 32KB. On ZFS it depends on the value of the xattr property; + * with the default value there is no limit, and with xattr=sa it is 64KB. + * + * So, a limit of 64k seems reasonable here too. + */ +#define P9_XATTR_SIZE_MAX 65536 #else #error Missing definition for P9_XATTR_SIZE_MAX for this host system #endif diff --git a/hw/9pfs/meson.build b/hw/9pfs/meson.build index d35d4f44ffa3b..7f4d6e3a45177 100644 --- a/hw/9pfs/meson.build +++ b/hw/9pfs/meson.build @@ -15,6 +15,8 @@ fs_ss.add(files( )) if host_os == 'darwin' fs_ss.add(files('9p-util-darwin.c')) +elif host_os == 'freebsd' + fs_ss.add(files('9p-util-freebsd.c')) elif host_os == 'linux' fs_ss.add(files('9p-util-linux.c')) endif diff --git a/include/qemu/xattr.h b/include/qemu/xattr.h index b08a934acc2db..224ba1276e685 100644 --- a/include/qemu/xattr.h +++ b/include/qemu/xattr.h @@ -26,7 +26,11 @@ # define ENOATTR ENODATA # endif # ifndef CONFIG_WIN32 -# include +# ifdef CONFIG_FREEBSD +# include +# else +# include +# endif # endif #endif diff --git a/meson.build b/meson.build index 6ade30f36adb2..72da97829abe4 100644 --- a/meson.build +++ b/meson.build @@ -2384,11 +2384,11 @@ dbus_display = get_option('dbus_display') \ .allowed() have_virtfs = get_option('virtfs') \ - .require(host_os == 'linux' or host_os == 'darwin', - error_message: 'virtio-9p (virtfs) requires Linux or macOS') \ - .require(host_os == 'linux' or cc.has_function('pthread_fchdir_np'), + .require(host_os == 'linux' or host_os == 'darwin' or host_os == 'freebsd', + error_message: 'virtio-9p (virtfs) requires Linux or macOS or FreeBSD') \ + .require(host_os != 'darwin' or cc.has_function('pthread_fchdir_np'), error_message: 'virtio-9p (virtfs) on macOS requires the presence of pthread_fchdir_np') \ - .require(host_os == 'darwin' or libattr.found(), + .require(host_os != 'linux' or libattr.found(), error_message: 'virtio-9p (virtfs) on Linux requires libattr-devel') \ .disable_auto_if(not have_tools and not have_system) \ .allowed() From c921e5496f23221335bea0c9104364409cd0b2b8 Mon Sep 17 00:00:00 2001 From: Peter Foley Date: Fri, 5 Sep 2025 11:17:11 -0400 Subject: [PATCH 0770/1794] 9pfs: Stop including gstrfuncs.h gstrfuncs.h is not intended to be included directly. In fact this only works because glib.h is already included by osdep.h. Just remove the include. Signed-off-by: Peter Foley Reviewed-by: Richard Henderson Link: https://lore.kernel.org/qemu-devel/20250905-9p-v2-1-2ad31999684d@google.com Signed-off-by: Christian Schoenebeck --- hw/9pfs/9p-util-generic.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/9pfs/9p-util-generic.c b/hw/9pfs/9p-util-generic.c index 4c1e9c887d00c..b71fa2cb37853 100644 --- a/hw/9pfs/9p-util-generic.c +++ b/hw/9pfs/9p-util-generic.c @@ -2,7 +2,6 @@ #include "qemu/osdep.h" #include "9p-util.h" -#include char *qemu_open_flags_tostr(int flags) { From e5eacba4a3e6efdae8cda323f9a2ab5cfb1d0492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 20 Aug 2025 13:40:48 +0100 Subject: [PATCH 0771/1794] MAINTAINERS: list qemu-security@nongnu.org as security contact MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The qemu-security@nongnu.org list is considered the authoritative contact for reporting QEMU security issues. Remove the Red Hat security team address in favour of QEMU's list, to ensure that upstream gets first contact. There is a representative of the Red Hat security team as a member of qemu-security@nongnu.org whom requests CVE assignments on behalf of QEMU when needed. Reviewed-by: Alex Bennée Reviewed-by: Mauro Matteo Cascella Signed-off-by: Daniel P. Berrangé --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index bd417e96f700c..70eb0241d36c9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -85,7 +85,7 @@ Responsible Disclosure, Reporting Security Issues ------------------------------------------------- W: https://wiki.qemu.org/SecurityProcess M: Michael S. Tsirkin -L: secalert@redhat.com +L: qemu-security@nongnu.org Trivial patches --------------- From 7bc2cbe3306899559c4da1b05e410235d994e22d Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 10 Sep 2025 22:31:11 +0300 Subject: [PATCH 0772/1794] migration/qemu-file: don't make incoming fds blocking again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In migration we want to pass fd "as is", not changing its blocking status. The only current user of these fds is CPR state (through VMSTATE_FD), which of-course doesn't want to modify fds on target when source is still running and use these fds. Suggested-by: Daniel P. Berrangé Reviewed-by: Peter Xu Reviewed-by: Daniel P. Berrangé Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Daniel P. Berrangé --- include/io/channel.h | 1 + io/channel-socket.c | 13 +++++++++---- migration/qemu-file.c | 3 ++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/io/channel.h b/include/io/channel.h index 234e5db70dd4a..12266256a8f51 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -36,6 +36,7 @@ OBJECT_DECLARE_TYPE(QIOChannel, QIOChannelClass, #define QIO_CHANNEL_READ_FLAG_MSG_PEEK 0x1 #define QIO_CHANNEL_READ_FLAG_RELAXED_EOF 0x2 +#define QIO_CHANNEL_READ_FLAG_FD_PRESERVE_BLOCKING 0x4 typedef enum QIOChannelFeature QIOChannelFeature; diff --git a/io/channel-socket.c b/io/channel-socket.c index 3b7ca924ff349..21f8f2e0c54a7 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -464,7 +464,8 @@ static void qio_channel_socket_finalize(Object *obj) #ifndef WIN32 static void qio_channel_socket_copy_fds(struct msghdr *msg, - int **fds, size_t *nfds) + int **fds, size_t *nfds, + bool preserve_blocking) { struct cmsghdr *cmsg; @@ -497,8 +498,10 @@ static void qio_channel_socket_copy_fds(struct msghdr *msg, continue; } - /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ - qemu_socket_set_block(fd); + if (!preserve_blocking) { + /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ + qemu_socket_set_block(fd); + } #ifndef MSG_CMSG_CLOEXEC qemu_set_cloexec(fd); @@ -556,7 +559,9 @@ static ssize_t qio_channel_socket_readv(QIOChannel *ioc, } if (fds && nfds) { - qio_channel_socket_copy_fds(&msg, fds, nfds); + qio_channel_socket_copy_fds( + &msg, fds, nfds, + flags & QIO_CHANNEL_READ_FLAG_FD_PRESERVE_BLOCKING); } return ret; diff --git a/migration/qemu-file.c b/migration/qemu-file.c index b6ac190034f77..d5c6e7ec61984 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -340,7 +340,8 @@ static ssize_t coroutine_mixed_fn qemu_fill_buffer(QEMUFile *f) do { struct iovec iov = { f->buf + pending, IO_BUF_SIZE - pending }; - len = qio_channel_readv_full(f->ioc, &iov, 1, pfds, pnfd, 0, + len = qio_channel_readv_full(f->ioc, &iov, 1, pfds, pnfd, + QIO_CHANNEL_READ_FLAG_FD_PRESERVE_BLOCKING, &local_error); if (len == QIO_CHANNEL_ERR_BLOCK) { if (qemu_in_coroutine()) { From d343f395e371aa1b859eb42be331918931a6637e Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 10 Sep 2025 22:31:12 +0300 Subject: [PATCH 0773/1794] io/channel: document how qio_channel_readv_full() handles fds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only realization, which may have incoming fds is qio_channel_socket_readv() (in io/channel-socket.c). qio_channel_socket_readv() do call (through qio_channel_socket_copy_fds()) qemu_socket_set_block() and qemu_set_cloexec() for each fd. Also, qio_channel_socket_copy_fds() is called at the end of qio_channel_socket_readv(), on success path. Acked-by: Peter Xu Reviewed-by: Daniel P. Berrangé Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Daniel P. Berrangé --- include/io/channel.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/io/channel.h b/include/io/channel.h index 12266256a8f51..c7f64506f70ce 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -118,6 +118,15 @@ struct QIOChannelClass { size_t nfds, int flags, Error **errp); + + /* + * The io_readv handler must guarantee that all + * incoming fds are set BLOCKING (unless + * QIO_CHANNEL_READ_FLAG_FD_PRESERVE_BLOCKING flag is set) and + * CLOEXEC (if available). + * @fds and @nfds are set only on success path, and untouched + * in case of errors. + */ ssize_t (*io_readv)(QIOChannel *ioc, const struct iovec *iov, size_t niov, @@ -125,6 +134,7 @@ struct QIOChannelClass { size_t *nfds, int flags, Error **errp); + int (*io_close)(QIOChannel *ioc, Error **errp); GSource * (*io_create_watch)(QIOChannel *ioc, @@ -235,6 +245,13 @@ void qio_channel_set_name(QIOChannel *ioc, * was allocated. It is the callers responsibility * to call close() on each file descriptor and to * call g_free() on the array pointer in @fds. + * @fds allocated and set (and @nfds is set too) + * _only_ on success path. These parameters are + * untouched in case of errors. + * qio_channel_readv_full() guarantees that all + * incoming fds are set BLOCKING (unless + * QIO_CHANNEL_READ_FLAG_FD_PRESERVE_BLOCKING flag + * is set) and CLOEXEC (if available). * * It is an error to pass a non-NULL @fds parameter * unless qio_channel_has_feature() returns a true From 6e9a6d57bf8de9e1edc207c24ed4e9e9508838c1 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 16 Sep 2025 16:13:50 +0300 Subject: [PATCH 0774/1794] char-socket: tcp_chr_recv(): drop extra _set_(block,cloexec) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qio_channel_readv_full() guarantees BLOCKING and CLOEXEC states for incoming descriptors, no reason to call extra ioctls. Reviewed-by: Daniel P. Berrangé Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Daniel P. Berrangé --- chardev/char-socket.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 1e8313915b540..b1ce5d01c7b7a 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -307,20 +307,6 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len) s->read_msgfds_num = msgfds_num; } - for (i = 0; i < s->read_msgfds_num; i++) { - int fd = s->read_msgfds[i]; - if (fd < 0) { - continue; - } - - /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ - qemu_socket_set_block(fd); - -#ifndef MSG_CMSG_CLOEXEC - qemu_set_cloexec(fd); -#endif - } - if (ret == QIO_CHANNEL_ERR_BLOCK) { errno = EAGAIN; ret = -1; From 9adc8d052d1c6b3015ca07a6a0f0833fb725e06f Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 16 Sep 2025 16:13:51 +0300 Subject: [PATCH 0775/1794] char-socket: tcp_chr_recv(): add comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comment, to stress that the order of operation (first drop old fds, second check read status) is intended. Reviewed-by: Daniel P. Berrangé Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Daniel P. Berrangé --- chardev/char-socket.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index b1ce5d01c7b7a..1be078dfc023f 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -294,7 +294,12 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len) } if (msgfds_num) { - /* close and clean read_msgfds */ + /* + * Close and clean previous read_msgfds, they are obsolete at + * this point, regardless result of new call to + * qio_channel_readv_full(). + */ + for (i = 0; i < s->read_msgfds_num; i++) { close(s->read_msgfds[i]); } From 4149afca711cc735a84ad61314bdcf41f69a7607 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 16 Sep 2025 16:13:52 +0300 Subject: [PATCH 0776/1794] util: add qemu_set_blocking() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In generic code we have qio_channel_set_blocking(), which takes bool parameter, and qemu_file_set_blocking(), which as well takes bool parameter. At lower fd-layer we have a mess of functions: - enough direct calls to Unix-specific g_unix_set_fd_nonblocking() (of course, all calls are out of Windows-compatible code), which is glib specific with GError, which we can't use, and have to handle error-reporting by hand after the call. and several platform-agnostic qemu_* helpers: - qemu_socket_set_nonblock(), which asserts success for posix (still, in most cases we can handle the error in better way) and ignores error for win32 realization - qemu_socket_try_set_nonblock(), providing and error, but not errp, so we have to handle it after the call - qemu_socket_set_block(), which simply ignores an error Note, that *_socket_* word in original API, which we are going to substitute was intended, because Windows support these operations only for sockets. What leads to solution of dropping it again? 1. Having a QEMU-native wrapper with errp parameter for g_unix_set_fd_nonblocking() for non-socket fds worth doing, at least to unify error handling. 2. So, if try to keep _socket_ vs _file_ words, we'll have two actually duplicated functions for Linux, which actually will be executed successfully on any (good enough) fds, and nothing prevent using them improperly except for the name. That doesn't look good. 3. Naming helped us in the world where we crash on errors or ignore them. Now, with errp parameter, callers are intended to proper error checking. And for places where we really OK with crash-on-error semantics (like tests), we have an explicit &error_abort. So, this commit starts a series, which will effectively revert commit ff5927baa7ffb9 "util: rename qemu_*block() socket functions" (which in turn was reverting f9e8cacc5557e43 "oslib-posix: rename socket_set_nonblock() to qemu_set_nonblock()", so that's a long story). Now we don't simply rename, instead we provide the new API and update all the callers. This commit only introduces a new fd-layer wrapper. Next commits will replace old API calls with it, and finally remove old API. Reviewed-by: Daniel P. Berrangé Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Daniel P. Berrangé --- include/qemu/osdep.h | 1 + util/oslib-posix.c | 15 +++++++++++++++ util/oslib-win32.c | 18 ++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index be3460b32f2d8..1b38cb7e452ac 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -687,6 +687,7 @@ ssize_t qemu_write_full(int fd, const void *buf, size_t count) G_GNUC_WARN_UNUSED_RESULT; void qemu_set_cloexec(int fd); +bool qemu_set_blocking(int fd, bool block, Error **errp); /* Return a dynamically allocated directory path that is appropriate for storing * local state. diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 4ff577e5de666..c7377010758b7 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -250,6 +250,21 @@ void qemu_anon_ram_free(void *ptr, size_t size) #endif } +bool qemu_set_blocking(int fd, bool block, Error **errp) +{ + g_autoptr(GError) err = NULL; + + if (!g_unix_set_fd_nonblocking(fd, !block, &err)) { + error_setg_errno(errp, errno, + "Can't set file descriptor %d %s: %s", fd, + block ? "blocking" : "non-blocking", + err->message); + return false; + } + + return true; +} + void qemu_socket_set_block(int fd) { g_unix_set_fd_nonblocking(fd, false, NULL); diff --git a/util/oslib-win32.c b/util/oslib-win32.c index b7351634ece12..03044f5b59a74 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -177,6 +177,24 @@ static int socket_error(void) } } +bool qemu_set_blocking(int fd, bool block, Error **errp) +{ + unsigned long opt = block ? 0 : 1; + + if (block) { + qemu_socket_unselect(fd, NULL); + } + + if (ioctlsocket(fd, FIONBIO, &opt) != NO_ERROR) { + error_setg_errno(errp, socket_error(), + "Can't set file descriptor %d %s", fd, + block ? "blocking" : "non-blocking"); + return false; + } + + return true; +} + void qemu_socket_set_block(int fd) { unsigned long opt = 0; From 1ed8903916394fca2347c700da974ca3856274b2 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 16 Sep 2025 16:13:53 +0300 Subject: [PATCH 0777/1794] treewide: handle result of qio_channel_set_blocking() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, we just always pass NULL as errp argument. That doesn't look good. Some realizations of interface may actually report errors. Channel-socket realization actually either ignore or crash on errors, but we are going to straighten it out to always reporting an errp in further commits. So, convert all callers to either handle the error (where environment allows) or explicitly use &error_abort. Take also a chance to change the return value to more convenient bool (keeping also in mind, that underlying realizations may return -1 on failure, not -errno). Suggested-by: Daniel P. Berrangé Reviewed-by: Daniel P. Berrangé Signed-off-by: Vladimir Sementsov-Ogievskiy [DB: fix return type mismatch in TLS/websocket channel impls for qio_channel_set_blocking] Signed-off-by: Daniel P. Berrangé --- block/nbd.c | 4 +++- chardev/char-socket.c | 20 ++++++++++++++++---- hw/remote/proxy.c | 6 +++++- hw/remote/remote-obj.c | 6 +++++- hw/vfio-user/proxy.c | 11 ++++++++--- include/io/channel.h | 6 +++--- io/channel-tls.c | 2 +- io/channel-websock.c | 3 +-- io/channel.c | 4 ++-- nbd/server.c | 4 +++- scsi/qemu-pr-helper.c | 9 ++++++--- tests/unit/io-channel-helpers.c | 5 +++-- tests/unit/test-io-channel-tls.c | 4 ++-- tools/i386/qemu-vmsr-helper.c | 6 ++++-- ui/vnc.c | 2 +- util/vhost-user-server.c | 7 ++++++- 16 files changed, 69 insertions(+), 30 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index d5a2b21c6d14d..5d231d5c4e45a 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -351,7 +351,9 @@ int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs, return ret; } - qio_channel_set_blocking(s->ioc, false, NULL); + if (!qio_channel_set_blocking(s->ioc, false, errp)) { + return -EINVAL; + } qio_channel_set_follow_coroutine_ctx(s->ioc, true); /* successfully connected */ diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 1be078dfc023f..cb4ec78ebe304 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -530,16 +530,24 @@ static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len) SocketChardev *s = SOCKET_CHARDEV(chr); int size; int saved_errno; + Error *local_err = NULL; if (s->state != TCP_CHARDEV_STATE_CONNECTED) { return 0; } - qio_channel_set_blocking(s->ioc, true, NULL); + if (!qio_channel_set_blocking(s->ioc, true, &local_err)) { + error_report_err(local_err); + return -1; + } size = tcp_chr_recv(chr, (void *) buf, len); saved_errno = errno; if (s->state != TCP_CHARDEV_STATE_DISCONNECTED) { - qio_channel_set_blocking(s->ioc, false, NULL); + if (!qio_channel_set_blocking(s->ioc, false, &local_err)) { + error_report_err(local_err); + /* failed to recover non-blocking state */ + tcp_chr_disconnect(chr); + } } if (size == 0) { /* connection closed */ @@ -884,18 +892,22 @@ static void tcp_chr_set_client_ioc_name(Chardev *chr, static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc) { SocketChardev *s = SOCKET_CHARDEV(chr); + Error *local_err = NULL; if (s->state != TCP_CHARDEV_STATE_CONNECTING) { return -1; } + if (!qio_channel_set_blocking(QIO_CHANNEL(sioc), false, &local_err)) { + error_report_err(local_err); + return -1; + } + s->ioc = QIO_CHANNEL(sioc); object_ref(OBJECT(sioc)); s->sioc = sioc; object_ref(OBJECT(sioc)); - qio_channel_set_blocking(s->ioc, false, NULL); - if (s->do_nodelay) { qio_channel_set_delay(s->ioc, false); } diff --git a/hw/remote/proxy.c b/hw/remote/proxy.c index b0165aa2a1d71..18e0f7a064f26 100644 --- a/hw/remote/proxy.c +++ b/hw/remote/proxy.c @@ -112,8 +112,12 @@ static void pci_proxy_dev_realize(PCIDevice *device, Error **errp) return; } + if (!qio_channel_set_blocking(dev->ioc, true, errp)) { + object_unref(dev->ioc); + return; + } + qemu_mutex_init(&dev->io_mutex); - qio_channel_set_blocking(dev->ioc, true, NULL); pci_conf[PCI_LATENCY_TIMER] = 0xff; pci_conf[PCI_INTERRUPT_PIN] = 0x01; diff --git a/hw/remote/remote-obj.c b/hw/remote/remote-obj.c index 85882902d7fae..3402068ab9e84 100644 --- a/hw/remote/remote-obj.c +++ b/hw/remote/remote-obj.c @@ -107,7 +107,11 @@ static void remote_object_machine_done(Notifier *notifier, void *data) error_report_err(err); return; } - qio_channel_set_blocking(ioc, false, NULL); + if (!qio_channel_set_blocking(ioc, false, &err)) { + error_report_err(err); + object_unref(OBJECT(ioc)); + return; + } o->dev = dev; diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c index 2c03d49f97641..bbd7ec243d653 100644 --- a/hw/vfio-user/proxy.c +++ b/hw/vfio-user/proxy.c @@ -886,10 +886,11 @@ VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp) sioc = qio_channel_socket_new(); ioc = QIO_CHANNEL(sioc); if (qio_channel_socket_connect_sync(sioc, addr, errp) < 0) { - object_unref(OBJECT(ioc)); - return NULL; + goto fail; + } + if (!qio_channel_set_blocking(ioc, false, errp)) { + goto fail; } - qio_channel_set_blocking(ioc, false, NULL); proxy = g_malloc0(sizeof(VFIOUserProxy)); proxy->sockname = g_strdup_printf("unix:%s", sockname); @@ -923,6 +924,10 @@ VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp) QLIST_INSERT_HEAD(&vfio_user_sockets, proxy, next); return proxy; + +fail: + object_unref(OBJECT(ioc)); + return NULL; } void vfio_user_set_handler(VFIODevice *vbasedev, diff --git a/include/io/channel.h b/include/io/channel.h index c7f64506f70ce..999a8f5f238d0 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -531,9 +531,9 @@ int coroutine_mixed_fn qio_channel_write_all(QIOChannel *ioc, * return QIO_CHANNEL_ERR_BLOCK if they would otherwise * block on I/O */ -int qio_channel_set_blocking(QIOChannel *ioc, - bool enabled, - Error **errp); +bool qio_channel_set_blocking(QIOChannel *ioc, + bool enabled, + Error **errp); /** * qio_channel_set_follow_coroutine_ctx: diff --git a/io/channel-tls.c b/io/channel-tls.c index a8248a9216b6a..7135896f791f0 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -425,7 +425,7 @@ static int qio_channel_tls_set_blocking(QIOChannel *ioc, { QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); - return qio_channel_set_blocking(tioc->master, enabled, errp); + return qio_channel_set_blocking(tioc->master, enabled, errp) ? 0 : -1; } static void qio_channel_tls_set_delay(QIOChannel *ioc, diff --git a/io/channel-websock.c b/io/channel-websock.c index 08ddb274f0c92..0a8c5c47123e6 100644 --- a/io/channel-websock.c +++ b/io/channel-websock.c @@ -1184,8 +1184,7 @@ static int qio_channel_websock_set_blocking(QIOChannel *ioc, { QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc); - qio_channel_set_blocking(wioc->master, enabled, errp); - return 0; + return qio_channel_set_blocking(wioc->master, enabled, errp) ? 0 : -1; } static void qio_channel_websock_set_delay(QIOChannel *ioc, diff --git a/io/channel.c b/io/channel.c index ebd93227651f1..852e684938c5f 100644 --- a/io/channel.c +++ b/io/channel.c @@ -359,12 +359,12 @@ int coroutine_mixed_fn qio_channel_write_all(QIOChannel *ioc, } -int qio_channel_set_blocking(QIOChannel *ioc, +bool qio_channel_set_blocking(QIOChannel *ioc, bool enabled, Error **errp) { QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); - return klass->io_set_blocking(ioc, enabled, errp); + return klass->io_set_blocking(ioc, enabled, errp) == 0; } diff --git a/nbd/server.c b/nbd/server.c index d242be9811520..acec0487a8b13 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1411,7 +1411,9 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) ....options sent, ending in NBD_OPT_EXPORT_NAME or NBD_OPT_GO.... */ - qio_channel_set_blocking(client->ioc, false, NULL); + if (!qio_channel_set_blocking(client->ioc, false, errp)) { + return -EINVAL; + } qio_channel_set_follow_coroutine_ctx(client->ioc, true); trace_nbd_negotiate_begin(); diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index b69dd982d6a0b..074b4db472076 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -733,8 +733,11 @@ static void coroutine_fn prh_co_entry(void *opaque) uint32_t flags; int r; - qio_channel_set_blocking(QIO_CHANNEL(client->ioc), - false, NULL); + if (!qio_channel_set_blocking(QIO_CHANNEL(client->ioc), + false, &local_err)) { + goto out; + } + qio_channel_set_follow_coroutine_ctx(QIO_CHANNEL(client->ioc), true); /* A very simple negotiation for future extensibility. No features @@ -786,6 +789,7 @@ static void coroutine_fn prh_co_entry(void *opaque) } } +out: if (local_err) { if (verbose == 0) { error_free(local_err); @@ -794,7 +798,6 @@ static void coroutine_fn prh_co_entry(void *opaque) } } -out: object_unref(OBJECT(client->ioc)); g_free(client); } diff --git a/tests/unit/io-channel-helpers.c b/tests/unit/io-channel-helpers.c index c0799c21c233c..22b42d14cdd7e 100644 --- a/tests/unit/io-channel-helpers.c +++ b/tests/unit/io-channel-helpers.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "io-channel-helpers.h" +#include "qapi/error.h" #include "qemu/iov.h" struct QIOChannelTest { @@ -109,8 +110,8 @@ void qio_channel_test_run_threads(QIOChannelTest *test, test->src = src; test->dst = dst; - qio_channel_set_blocking(test->dst, blocking, NULL); - qio_channel_set_blocking(test->src, blocking, NULL); + qio_channel_set_blocking(test->dst, blocking, &error_abort); + qio_channel_set_blocking(test->src, blocking, &error_abort); reader = g_thread_new("reader", test_io_thread_reader, diff --git a/tests/unit/test-io-channel-tls.c b/tests/unit/test-io-channel-tls.c index e036ac5df4c29..6f282ad45d0cb 100644 --- a/tests/unit/test-io-channel-tls.c +++ b/tests/unit/test-io-channel-tls.c @@ -184,8 +184,8 @@ static void test_io_channel_tls(const void *opaque) * thread, so we need these non-blocking to avoid deadlock * of ourselves */ - qio_channel_set_blocking(QIO_CHANNEL(clientChanSock), false, NULL); - qio_channel_set_blocking(QIO_CHANNEL(serverChanSock), false, NULL); + qio_channel_set_blocking(QIO_CHANNEL(clientChanSock), false, &error_abort); + qio_channel_set_blocking(QIO_CHANNEL(serverChanSock), false, &error_abort); /* Now the real part of the test, setup the sessions */ clientChanTLS = qio_channel_tls_new_client( diff --git a/tools/i386/qemu-vmsr-helper.c b/tools/i386/qemu-vmsr-helper.c index 5f19a48cbd275..6c0f4fe870e96 100644 --- a/tools/i386/qemu-vmsr-helper.c +++ b/tools/i386/qemu-vmsr-helper.c @@ -213,8 +213,10 @@ static void coroutine_fn vh_co_entry(void *opaque) uint64_t vmsr; int r; - qio_channel_set_blocking(QIO_CHANNEL(client->ioc), - false, NULL); + if (!qio_channel_set_blocking(QIO_CHANNEL(client->ioc), + false, &local_err)) { + goto out; + } qio_channel_set_follow_coroutine_ctx(QIO_CHANNEL(client->ioc), true); diff --git a/ui/vnc.c b/ui/vnc.c index 9054fc812535b..77c823bf2e85f 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -3337,7 +3337,7 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, VNC_DEBUG("New client on socket %p\n", vs->sioc); update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); - qio_channel_set_blocking(vs->ioc, false, NULL); + qio_channel_set_blocking(vs->ioc, false, &error_abort); if (vs->ioc_tag) { g_source_remove(vs->ioc_tag); } diff --git a/util/vhost-user-server.c b/util/vhost-user-server.c index b19229074ad98..d805a923940eb 100644 --- a/util/vhost-user-server.c +++ b/util/vhost-user-server.c @@ -336,6 +336,7 @@ static void vu_accept(QIONetListener *listener, QIOChannelSocket *sioc, gpointer opaque) { VuServer *server = opaque; + Error *local_err = NULL; if (server->sioc) { warn_report("Only one vhost-user client is allowed to " @@ -368,7 +369,11 @@ static void vu_accept(QIONetListener *listener, QIOChannelSocket *sioc, object_ref(OBJECT(server->ioc)); /* TODO vu_message_write() spins if non-blocking! */ - qio_channel_set_blocking(server->ioc, false, NULL); + if (!qio_channel_set_blocking(server->ioc, false, &local_err)) { + error_report_err(local_err); + vu_deinit(&server->vu_dev); + return; + } qio_channel_set_follow_coroutine_ctx(server->ioc, true); From fe6a74f365743ae3ce2cbbc4d44617d51a446a02 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 16 Sep 2025 16:13:54 +0300 Subject: [PATCH 0778/1794] migration: qemu_file_set_blocking(): add errp parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu_file_set_blocking() is a wrapper on qio_channel_set_blocking(), so let's passthrough the errp. Note the migration should not be using &error_abort in these calls, however, this is done to expedite the API conversion. The original code would have eventually ended up calling either qemu_socket_set_nonblock which would asset on Linux, or g_unix_set_fd_nonblocking which would propagate errors. We never saw asserts in practice, and conceptually they should not happen, but ideally this code will be later adapted to remove use of &error_abort. Acked-by: Peter Xu Reviewed-by: Daniel P. Berrangé Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Daniel P. Berrangé --- migration/colo.c | 5 ++++- migration/migration.c | 8 +++++--- migration/postcopy-ram.c | 2 +- migration/qemu-file.c | 4 ++-- migration/qemu-file.h | 2 +- migration/savevm.c | 4 ++-- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/migration/colo.c b/migration/colo.c index e0f713c837f5d..cf4d71d9edfa2 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -859,7 +859,10 @@ static void *colo_process_incoming_thread(void *opaque) * coroutine, and here we are in the COLO incoming thread, so it is ok to * set the fd back to blocked. */ - qemu_file_set_blocking(mis->from_src_file, true); + if (!qemu_file_set_blocking(mis->from_src_file, true, &local_err)) { + error_report_err(local_err); + goto out; + } colo_incoming_start_dirty_log(); diff --git a/migration/migration.c b/migration/migration.c index 10c216d25dec0..e1ac4d73c2bb0 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -951,7 +951,7 @@ static void migration_incoming_setup(QEMUFile *f) assert(!mis->from_src_file); mis->from_src_file = f; - qemu_file_set_blocking(f, false); + qemu_file_set_blocking(f, false, &error_abort); } void migration_incoming_process(void) @@ -971,7 +971,7 @@ static bool postcopy_try_recover(void) /* This should be set already in migration_incoming_setup() */ assert(mis->from_src_file); /* Postcopy has standalone thread to do vm load */ - qemu_file_set_blocking(mis->from_src_file, true); + qemu_file_set_blocking(mis->from_src_file, true, &error_abort); /* Re-configure the return path */ mis->to_src_file = qemu_file_get_return_path(mis->from_src_file); @@ -4002,7 +4002,9 @@ void migration_connect(MigrationState *s, Error *error_in) } migration_rate_set(rate_limit); - qemu_file_set_blocking(s->to_dst_file, true); + if (!qemu_file_set_blocking(s->to_dst_file, true, &local_err)) { + goto fail; + } /* * Open the return path. For postcopy, it is used exclusively. For diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 45af9a361e8ea..01721723434d5 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -1909,7 +1909,7 @@ void postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file) * The new loading channel has its own threads, so it needs to be * blocked too. It's by default true, just be explicit. */ - qemu_file_set_blocking(file, true); + qemu_file_set_blocking(file, true, &error_abort); mis->postcopy_qemufile_dst = file; qemu_sem_post(&mis->postcopy_qemufile_dst_done); trace_postcopy_preempt_new_channel(); diff --git a/migration/qemu-file.c b/migration/qemu-file.c index d5c6e7ec61984..0f4280df21f63 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -888,9 +888,9 @@ void qemu_put_counted_string(QEMUFile *f, const char *str) * both directions, and thus changing the blocking on the main * QEMUFile can also affect the return path. */ -void qemu_file_set_blocking(QEMUFile *f, bool block) +bool qemu_file_set_blocking(QEMUFile *f, bool block, Error **errp) { - qio_channel_set_blocking(f->ioc, block, NULL); + return qio_channel_set_blocking(f->ioc, block, errp); } /* diff --git a/migration/qemu-file.h b/migration/qemu-file.h index f5b9f430e04b1..c13c96716702c 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -71,7 +71,7 @@ void qemu_file_set_error(QEMUFile *f, int ret); int qemu_file_shutdown(QEMUFile *f); QEMUFile *qemu_file_get_return_path(QEMUFile *f); int qemu_fflush(QEMUFile *f); -void qemu_file_set_blocking(QEMUFile *f, bool block); +bool qemu_file_set_blocking(QEMUFile *f, bool block, Error **errp); int qemu_file_get_to_fd(QEMUFile *f, int fd, size_t size); void qemu_set_offset(QEMUFile *f, off_t off, int whence); off_t qemu_get_offset(QEMUFile *f); diff --git a/migration/savevm.c b/migration/savevm.c index fabbeb296ae98..abe0547f9b3f5 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2095,7 +2095,7 @@ static void *postcopy_ram_listen_thread(void *opaque) * Because we're a thread and not a coroutine we can't yield * in qemu_file, and thus we must be blocking now. */ - qemu_file_set_blocking(f, true); + qemu_file_set_blocking(f, true, &error_fatal); /* TODO: sanity check that only postcopiable data will be loaded here */ load_res = qemu_loadvm_state_main(f, mis); @@ -2108,7 +2108,7 @@ static void *postcopy_ram_listen_thread(void *opaque) f = mis->from_src_file; /* And non-blocking again so we don't block in any cleanup */ - qemu_file_set_blocking(f, false); + qemu_file_set_blocking(f, false, &error_fatal); trace_postcopy_ram_listen_thread_exit(); if (load_res < 0) { From 8cb17f9c36f3f92a4f653a0b369ec7ab82ccf5f0 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 16 Sep 2025 16:13:55 +0300 Subject: [PATCH 0779/1794] util: drop qemu_socket_set_nonblock() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use common qemu_set_blocking() instead. Note that pre-patch the behavior of Win32 and Linux realizations are inconsistent: we ignore failure for Win32, and assert success for Linux. How do we convert the callers? 1. Most of callers call qemu_socket_set_nonblock() on a freshly created socket fd, in conditions when we may simply report an error. Seems correct switching to error handling both for Windows (pre-patch error is ignored) and Linux (pre-patch we assert success). Anyway, we normally don't expect errors in these cases. Still in tests let's use &error_abort for simplicity. What are exclusions? 2. hw/virtio/vhost-user.c - we are inside #ifdef CONFIG_LINUX, so no damage in switching to error handling from assertion. 3. io/channel-socket.c: here we convert both old calls to qemu_socket_set_nonblock() and qemu_socket_set_block() to one new call. Pre-patch we assert success for Linux in qemu_socket_set_nonblock(), and ignore all other errors here. So, for Windows switch is a bit dangerous: we may get new errors or crashes(when error_abort is passed) in cases where we have silently ignored the error before (was it correct in all such cases, if they were?) Still, there is no other way to stricter API than take this risk. 4. util/vhost-user-server - compiled only for Linux (see util/meson.build), so we are safe, switching from assertion to &error_abort. Note: In qga/channel-posix.c we use g_warning(), where g_printerr() would actually be a better choice. Still let's for now follow common style of qga, where g_warning() is commonly used to print such messages, and no call to g_printerr(). Converting everything to use g_printerr() should better be another series. Reviewed-by: Daniel P. Berrangé Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Daniel P. Berrangé --- contrib/ivshmem-server/ivshmem-server.c | 9 ++++++++- hw/hyperv/syndbg.c | 4 +++- hw/virtio/vhost-user.c | 5 ++++- include/qemu/sockets.h | 1 - io/channel-socket.c | 7 +++---- net/dgram.c | 16 +++++++++++++--- net/l2tpv3.c | 5 +++-- net/socket.c | 20 ++++++++++++++++---- qga/channel-posix.c | 7 ++++++- tests/unit/socket-helpers.c | 4 +++- tests/unit/test-crypto-tlssession.c | 8 ++++---- util/oslib-posix.c | 7 ------- util/oslib-win32.c | 5 ----- util/vhost-user-server.c | 6 ++++-- 14 files changed, 67 insertions(+), 37 deletions(-) diff --git a/contrib/ivshmem-server/ivshmem-server.c b/contrib/ivshmem-server/ivshmem-server.c index 2f3c7320a6781..13cb828174e80 100644 --- a/contrib/ivshmem-server/ivshmem-server.c +++ b/contrib/ivshmem-server/ivshmem-server.c @@ -6,6 +6,7 @@ * top-level directory. */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/host-utils.h" #include "qemu/sockets.h" @@ -135,6 +136,7 @@ ivshmem_server_handle_new_conn(IvshmemServer *server) socklen_t unaddr_len; int newfd; unsigned i; + Error *local_err = NULL; /* accept the incoming connection */ unaddr_len = sizeof(unaddr); @@ -146,9 +148,14 @@ ivshmem_server_handle_new_conn(IvshmemServer *server) return -1; } - qemu_socket_set_nonblock(newfd); IVSHMEM_SERVER_DEBUG(server, "accept()=%d\n", newfd); + if (!qemu_set_blocking(newfd, false, &local_err)) { + error_report_err(local_err); + close(newfd); + return -1; + } + /* allocate new structure for this peer */ peer = g_malloc0(sizeof(*peer)); peer->sock_fd = newfd; diff --git a/hw/hyperv/syndbg.c b/hw/hyperv/syndbg.c index ac7e15f6f1d5e..bcdfdf6af75d3 100644 --- a/hw/hyperv/syndbg.c +++ b/hw/hyperv/syndbg.c @@ -338,7 +338,9 @@ static void hv_syndbg_realize(DeviceState *dev, Error **errp) return; } - qemu_socket_set_nonblock(syndbg->socket); + if (!qemu_set_blocking(syndbg->socket, false, errp)) { + return; + } syndbg->servaddr.sin_port = htons(syndbg->host_port); syndbg->servaddr.sin_family = AF_INET; diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 1e1d6b0d6e023..36c9c2e04d618 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -2039,7 +2039,10 @@ static int vhost_user_postcopy_advise(struct vhost_dev *dev, Error **errp) error_setg(errp, "%s: Failed to get ufd", __func__); return -EIO; } - qemu_socket_set_nonblock(ufd); + if (!qemu_set_blocking(ufd, false, errp)) { + close(ufd); + return -EINVAL; + } /* register ufd with userfault thread */ u->postcopy_fd.fd = ufd; diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index c562690d89371..6477f90b9e5dd 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -48,7 +48,6 @@ int socket_set_cork(int fd, int v); int socket_set_nodelay(int fd); void qemu_socket_set_block(int fd); int qemu_socket_try_set_nonblock(int fd); -void qemu_socket_set_nonblock(int fd); int socket_set_fast_reuse(int fd); #ifdef WIN32 diff --git a/io/channel-socket.c b/io/channel-socket.c index 21f8f2e0c54a7..f7e3cb9742221 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -825,11 +825,10 @@ qio_channel_socket_set_blocking(QIOChannel *ioc, { QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); - if (enabled) { - qemu_socket_set_block(sioc->fd); - } else { - qemu_socket_set_nonblock(sioc->fd); + if (!qemu_set_blocking(sioc->fd, enabled, errp)) { + return -1; } + return 0; } diff --git a/net/dgram.c b/net/dgram.c index 48f653bceb257..fb9ded30df820 100644 --- a/net/dgram.c +++ b/net/dgram.c @@ -226,7 +226,10 @@ static int net_dgram_mcast_create(struct sockaddr_in *mcastaddr, } } - qemu_socket_set_nonblock(fd); + if (!qemu_set_blocking(fd, false, errp)) { + goto fail; + } + return fd; fail: if (fd >= 0) { @@ -504,7 +507,11 @@ int net_init_dgram(const Netdev *netdev, const char *name, close(fd); return -1; } - qemu_socket_set_nonblock(fd); + + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return -1; + } dest_len = sizeof(raddr_in); dest_addr = g_malloc(dest_len); @@ -551,7 +558,10 @@ int net_init_dgram(const Netdev *netdev, const char *name, close(fd); return -1; } - qemu_socket_set_nonblock(fd); + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return -1; + } dest_len = sizeof(raddr_un); dest_addr = g_malloc(dest_len); diff --git a/net/l2tpv3.c b/net/l2tpv3.c index b5547cb917af9..cdfc641aa6fe9 100644 --- a/net/l2tpv3.c +++ b/net/l2tpv3.c @@ -648,6 +648,9 @@ int net_init_l2tpv3(const Netdev *netdev, error_setg(errp, "could not bind socket err=%i", errno); goto outerr; } + if (!qemu_set_blocking(fd, false, errp)) { + goto outerr; + } freeaddrinfo(result); @@ -709,8 +712,6 @@ int net_init_l2tpv3(const Netdev *netdev, s->vec = g_new(struct iovec, MAX_L2TPV3_IOVCNT); s->header_buf = g_malloc(s->header_size); - qemu_socket_set_nonblock(fd); - s->fd = fd; s->counter = 0; diff --git a/net/socket.c b/net/socket.c index 784dda686f520..db25e3d9aec8b 100644 --- a/net/socket.c +++ b/net/socket.c @@ -295,7 +295,10 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, } } - qemu_socket_set_nonblock(fd); + if (!qemu_set_blocking(fd, false, errp)) { + goto fail; + } + return fd; fail: if (fd >= 0) @@ -508,7 +511,10 @@ static int net_socket_listen_init(NetClientState *peer, error_setg_errno(errp, errno, "can't create stream socket"); return -1; } - qemu_socket_set_nonblock(fd); + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return -1; + } socket_set_fast_reuse(fd); @@ -556,7 +562,10 @@ static int net_socket_connect_init(NetClientState *peer, error_setg_errno(errp, errno, "can't create stream socket"); return -1; } - qemu_socket_set_nonblock(fd); + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return -1; + } connected = 0; for(;;) { @@ -671,7 +680,10 @@ static int net_socket_udp_init(NetClientState *peer, close(fd); return -1; } - qemu_socket_set_nonblock(fd); + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return -1; + } s = net_socket_fd_init_dgram(peer, model, name, fd, 0, NULL, errp); if (!s) { diff --git a/qga/channel-posix.c b/qga/channel-posix.c index 465d688ecb886..9ccc8b7bd1449 100644 --- a/qga/channel-posix.c +++ b/qga/channel-posix.c @@ -28,6 +28,7 @@ static gboolean ga_channel_listen_accept(GIOChannel *channel, GAChannel *c = data; int ret, client_fd; bool accepted = false; + Error *err = NULL; g_assert(channel != NULL); @@ -36,7 +37,11 @@ static gboolean ga_channel_listen_accept(GIOChannel *channel, g_warning("error converting fd to gsocket: %s", strerror(errno)); goto out; } - qemu_socket_set_nonblock(client_fd); + if (!qemu_set_blocking(client_fd, false, &err)) { + g_warning("%s", error_get_pretty(err)); + error_free(err); + goto out; + } ret = ga_channel_client_add(c, client_fd); if (ret) { g_warning("error setting up connection"); diff --git a/tests/unit/socket-helpers.c b/tests/unit/socket-helpers.c index 37db24f72a1e3..46d2ff101c9ae 100644 --- a/tests/unit/socket-helpers.c +++ b/tests/unit/socket-helpers.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/sockets.h" #include "socket-helpers.h" @@ -88,7 +89,8 @@ static int socket_can_bind_connect(const char *hostname, int family) goto cleanup; } - qemu_socket_set_nonblock(cfd); + qemu_set_blocking(cfd, false, &error_abort); + if (connect(cfd, (struct sockaddr *)&ss, sslen) < 0) { if (errno == EINPROGRESS) { check_soerr = true; diff --git a/tests/unit/test-crypto-tlssession.c b/tests/unit/test-crypto-tlssession.c index 554054e9344c8..61311cbe6efe1 100644 --- a/tests/unit/test-crypto-tlssession.c +++ b/tests/unit/test-crypto-tlssession.c @@ -112,8 +112,8 @@ static void test_crypto_tls_session_psk(void) * thread, so we need these non-blocking to avoid deadlock * of ourselves */ - qemu_socket_set_nonblock(channel[0]); - qemu_socket_set_nonblock(channel[1]); + qemu_set_blocking(channel[0], false, &error_abort); + qemu_set_blocking(channel[1], false, &error_abort); clientCreds = test_tls_creds_psk_create( QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, @@ -264,8 +264,8 @@ static void test_crypto_tls_session_x509(const void *opaque) * thread, so we need these non-blocking to avoid deadlock * of ourselves */ - qemu_socket_set_nonblock(channel[0]); - qemu_socket_set_nonblock(channel[1]); + qemu_set_blocking(channel[0], false, &error_abort); + qemu_set_blocking(channel[1], false, &error_abort); #define CLIENT_CERT_DIR "tests/test-crypto-tlssession-client/" #define SERVER_CERT_DIR "tests/test-crypto-tlssession-server/" diff --git a/util/oslib-posix.c b/util/oslib-posix.c index c7377010758b7..599993d40d074 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -275,13 +275,6 @@ int qemu_socket_try_set_nonblock(int fd) return g_unix_set_fd_nonblocking(fd, true, NULL) ? 0 : -errno; } -void qemu_socket_set_nonblock(int fd) -{ - int f; - f = qemu_socket_try_set_nonblock(fd); - assert(f == 0); -} - int socket_set_fast_reuse(int fd) { int val = 1, ret; diff --git a/util/oslib-win32.c b/util/oslib-win32.c index 03044f5b59a74..1566eb57e7413 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -211,11 +211,6 @@ int qemu_socket_try_set_nonblock(int fd) return 0; } -void qemu_socket_set_nonblock(int fd) -{ - (void)qemu_socket_try_set_nonblock(fd); -} - int socket_set_fast_reuse(int fd) { /* Enabling the reuse of an endpoint that was used by a socket still in diff --git a/util/vhost-user-server.c b/util/vhost-user-server.c index d805a923940eb..04c72a92aae27 100644 --- a/util/vhost-user-server.c +++ b/util/vhost-user-server.c @@ -78,7 +78,8 @@ static void vmsg_unblock_fds(VhostUserMsg *vmsg) } for (i = 0; i < vmsg->fd_num; i++) { - qemu_socket_set_nonblock(vmsg->fds[i]); + /* TODO: handle error more gracefully than aborting */ + qemu_set_blocking(vmsg->fds[i], false, &error_abort); } } @@ -303,7 +304,8 @@ set_watch(VuDev *vu_dev, int fd, int vu_evt, vu_fd_watch->fd = fd; vu_fd_watch->cb = cb; - qemu_socket_set_nonblock(fd); + /* TODO: handle error more gracefully than aborting */ + qemu_set_blocking(fd, false, &error_abort); aio_set_fd_handler(server->ctx, fd, kick_handler, NULL, NULL, NULL, vu_fd_watch); vu_fd_watch->vu_dev = vu_dev; From 09759245cf762d2fed4259494ec31198ac1ec0f4 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 16 Sep 2025 16:13:56 +0300 Subject: [PATCH 0780/1794] util: drop qemu_socket_try_set_nonblock() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we can use qemu_set_blocking() in these cases. Reviewed-by: Peter Xu Reviewed-by: Daniel P. Berrangé Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Daniel P. Berrangé --- include/qemu/sockets.h | 1 - net/dgram.c | 12 +++--------- net/socket.c | 7 ++----- net/stream.c | 9 +++------ net/stream_data.c | 10 ++++------ util/oslib-posix.c | 4 ---- util/oslib-win32.c | 9 --------- 7 files changed, 12 insertions(+), 40 deletions(-) diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index 6477f90b9e5dd..9512fec514733 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -47,7 +47,6 @@ ssize_t qemu_send_full(int s, const void *buf, size_t count) int socket_set_cork(int fd, int v); int socket_set_nodelay(int fd); void qemu_socket_set_block(int fd); -int qemu_socket_try_set_nonblock(int fd); int socket_set_fast_reuse(int fd); #ifdef WIN32 diff --git a/net/dgram.c b/net/dgram.c index fb9ded30df820..baa126d514b06 100644 --- a/net/dgram.c +++ b/net/dgram.c @@ -287,7 +287,7 @@ static int net_dgram_mcast_init(NetClientState *peer, Error **errp) { NetDgramState *s; - int fd, ret; + int fd; struct sockaddr_in *saddr; if (remote->type != SOCKET_ADDRESS_TYPE_INET) { @@ -335,11 +335,8 @@ static int net_dgram_mcast_init(NetClientState *peer, g_free(saddr); return -1; } - ret = qemu_socket_try_set_nonblock(fd); - if (ret < 0) { + if (!qemu_set_blocking(fd, false, errp)) { g_free(saddr); - error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d", - name, fd); return -1; } @@ -572,10 +569,7 @@ int net_init_dgram(const Netdev *netdev, const char *name, if (fd == -1) { return -1; } - ret = qemu_socket_try_set_nonblock(fd); - if (ret < 0) { - error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d", - name, fd); + if (!qemu_set_blocking(fd, false, errp)) { return -1; } dest_addr = NULL; diff --git a/net/socket.c b/net/socket.c index db25e3d9aec8b..1ad03fc9d4558 100644 --- a/net/socket.c +++ b/net/socket.c @@ -718,7 +718,7 @@ int net_init_socket(const Netdev *netdev, const char *name, } if (sock->fd) { - int fd, ret, so_type; + int fd, so_type; fd = monitor_fd_param(monitor_cur(), sock->fd, errp); if (fd == -1) { @@ -728,10 +728,7 @@ int net_init_socket(const Netdev *netdev, const char *name, if (so_type < 0) { return -1; } - ret = qemu_socket_try_set_nonblock(fd); - if (ret < 0) { - error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d", - name, fd); + if (!qemu_set_blocking(fd, false, errp)) { return -1; } switch (so_type) { diff --git a/net/stream.c b/net/stream.c index d893f02cabe3d..94f823a2a7c2b 100644 --- a/net/stream.c +++ b/net/stream.c @@ -138,7 +138,6 @@ static void net_stream_server_listening(QIOTask *task, gpointer opaque) NetStreamData *d = opaque; QIOChannelSocket *listen_sioc = QIO_CHANNEL_SOCKET(d->listen_ioc); SocketAddress *addr; - int ret; Error *err = NULL; if (qio_task_propagate_error(task, &err)) { @@ -149,13 +148,11 @@ static void net_stream_server_listening(QIOTask *task, gpointer opaque) addr = qio_channel_socket_get_local_address(listen_sioc, NULL); g_assert(addr != NULL); - ret = qemu_socket_try_set_nonblock(listen_sioc->fd); - if (addr->type == SOCKET_ADDRESS_TYPE_FD && ret < 0) { - qemu_set_info_str(&d->nc, "can't use file descriptor %s (errno %d)", - addr->u.fd.str, -ret); + if (!qemu_set_blocking(listen_sioc->fd, false, &err)) { + qemu_set_info_str(&d->nc, "error: %s", error_get_pretty(err)); + error_free(err); return; } - g_assert(ret == 0); qapi_free_SocketAddress(addr); d->nc.link_down = true; diff --git a/net/stream_data.c b/net/stream_data.c index 5af27e0d1d6ad..03740e9f73e72 100644 --- a/net/stream_data.c +++ b/net/stream_data.c @@ -12,6 +12,7 @@ #include "net/net.h" #include "io/channel.h" #include "io/net-listener.h" +#include "qemu/sockets.h" #include "stream_data.h" @@ -154,7 +155,6 @@ int net_stream_data_client_connected(QIOTask *task, NetStreamData *d) { QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(d->ioc); SocketAddress *addr; - int ret; Error *err = NULL; if (qio_task_propagate_error(task, &err)) { @@ -166,14 +166,12 @@ int net_stream_data_client_connected(QIOTask *task, NetStreamData *d) addr = qio_channel_socket_get_remote_address(sioc, NULL); g_assert(addr != NULL); - ret = qemu_socket_try_set_nonblock(sioc->fd); - if (addr->type == SOCKET_ADDRESS_TYPE_FD && ret < 0) { - qemu_set_info_str(&d->nc, "can't use file descriptor %s (errno %d)", - addr->u.fd.str, -ret); + if (!qemu_set_blocking(sioc->fd, false, &err)) { + qemu_set_info_str(&d->nc, "error: %s", error_get_pretty(err)); + error_free(err); qapi_free_SocketAddress(addr); goto error; } - g_assert(ret == 0); qapi_free_SocketAddress(addr); net_socket_rs_init(&d->rs, net_stream_data_rs_finalize, false); diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 599993d40d074..7654febfa5b1d 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -270,10 +270,6 @@ void qemu_socket_set_block(int fd) g_unix_set_fd_nonblocking(fd, false, NULL); } -int qemu_socket_try_set_nonblock(int fd) -{ - return g_unix_set_fd_nonblocking(fd, true, NULL) ? 0 : -errno; -} int socket_set_fast_reuse(int fd) { diff --git a/util/oslib-win32.c b/util/oslib-win32.c index 1566eb57e7413..bf5d478c5c8a2 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -202,15 +202,6 @@ void qemu_socket_set_block(int fd) ioctlsocket(fd, FIONBIO, &opt); } -int qemu_socket_try_set_nonblock(int fd) -{ - unsigned long opt = 1; - if (ioctlsocket(fd, FIONBIO, &opt) != NO_ERROR) { - return -socket_error(); - } - return 0; -} - int socket_set_fast_reuse(int fd) { /* Enabling the reuse of an endpoint that was used by a socket still in From d14c8cc69dcd64031019da857f93ee5f6d64bb75 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 16 Sep 2025 16:13:57 +0300 Subject: [PATCH 0781/1794] io/channel-socket: rework qio_channel_socket_copy_fds() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to switch from qemu_socket_set_block() to newer qemu_set_blocking(), which provides return status of operation, to handle errors. Still, we want to keep qio_channel_socket_readv() interface clean, as currently it allocate @fds only on success. So, in case of error, we should close all incoming fds and keep user's @fds untouched or zero. Let's make separate functions qio_channel_handle_fds() and qio_channel_cleanup_fds(), to achieve what we want. Reviewed-by: Daniel P. Berrangé Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Daniel P. Berrangé --- include/io/channel.h | 8 +++--- io/channel-socket.c | 67 +++++++++++++++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 20 deletions(-) diff --git a/include/io/channel.h b/include/io/channel.h index 999a8f5f238d0..0f25ae0069f9e 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -124,8 +124,8 @@ struct QIOChannelClass { * incoming fds are set BLOCKING (unless * QIO_CHANNEL_READ_FLAG_FD_PRESERVE_BLOCKING flag is set) and * CLOEXEC (if available). - * @fds and @nfds are set only on success path, and untouched - * in case of errors. + * @fds and @nfds are set only on success path. Still, setting + * @fds and @nfds to zero is acceptable on failure path. */ ssize_t (*io_readv)(QIOChannel *ioc, const struct iovec *iov, @@ -246,8 +246,8 @@ void qio_channel_set_name(QIOChannel *ioc, * to call close() on each file descriptor and to * call g_free() on the array pointer in @fds. * @fds allocated and set (and @nfds is set too) - * _only_ on success path. These parameters are - * untouched in case of errors. + * _only_ on success path. Still, @fds and @nfds + * may be set to zero on failure path. * qio_channel_readv_full() guarantees that all * incoming fds are set BLOCKING (unless * QIO_CHANNEL_READ_FLAG_FD_PRESERVE_BLOCKING flag diff --git a/io/channel-socket.c b/io/channel-socket.c index f7e3cb9742221..e53d9ac76fff5 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -464,8 +464,7 @@ static void qio_channel_socket_finalize(Object *obj) #ifndef WIN32 static void qio_channel_socket_copy_fds(struct msghdr *msg, - int **fds, size_t *nfds, - bool preserve_blocking) + int **fds, size_t *nfds) { struct cmsghdr *cmsg; @@ -473,7 +472,7 @@ static void qio_channel_socket_copy_fds(struct msghdr *msg, *fds = NULL; for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { - int fd_size, i; + int fd_size; int gotfds; if (cmsg->cmsg_len < CMSG_LEN(sizeof(int)) || @@ -491,24 +490,53 @@ static void qio_channel_socket_copy_fds(struct msghdr *msg, gotfds = fd_size / sizeof(int); *fds = g_renew(int, *fds, *nfds + gotfds); memcpy(*fds + *nfds, CMSG_DATA(cmsg), fd_size); + *nfds += gotfds; + } +} - for (i = 0; i < gotfds; i++) { - int fd = (*fds)[*nfds + i]; - if (fd < 0) { - continue; - } +static bool qio_channel_handle_fds(int *fds, size_t nfds, + bool preserve_blocking, Error **errp) +{ + int *end = fds + nfds, *fd; + +#ifdef MSG_CMSG_CLOEXEC + if (preserve_blocking) { + /* Nothing to do */ + return true; + } +#endif - if (!preserve_blocking) { - /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ - qemu_socket_set_block(fd); + for (fd = fds; fd != end; fd++) { + if (*fd < 0) { + continue; + } + + if (!preserve_blocking) { + /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ + if (!qemu_set_blocking(*fd, true, errp)) { + return false; } + } #ifndef MSG_CMSG_CLOEXEC - qemu_set_cloexec(fd); + qemu_set_cloexec(*fd); #endif + } + + return true; +} + +static void qio_channel_cleanup_fds(int **fds, size_t *nfds) +{ + for (size_t i = 0; i < *nfds; i++) { + if ((*fds)[i] < 0) { + continue; } - *nfds += gotfds; + close((*fds)[i]); } + + g_clear_pointer(fds, g_free); + *nfds = 0; } @@ -559,9 +587,16 @@ static ssize_t qio_channel_socket_readv(QIOChannel *ioc, } if (fds && nfds) { - qio_channel_socket_copy_fds( - &msg, fds, nfds, - flags & QIO_CHANNEL_READ_FLAG_FD_PRESERVE_BLOCKING); + bool preserve_blocking = + flags & QIO_CHANNEL_READ_FLAG_FD_PRESERVE_BLOCKING; + + qio_channel_socket_copy_fds(&msg, fds, nfds); + + if (!qio_channel_handle_fds(*fds, *nfds, + preserve_blocking, errp)) { + qio_channel_cleanup_fds(fds, nfds); + return -1; + } } return ret; From 5d1d32ce9d26a7cdc1c60dceb1b0c55ff9ad9b3e Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 16 Sep 2025 16:13:58 +0300 Subject: [PATCH 0782/1794] util: drop qemu_socket_set_block() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now it's unused. Reviewed-by: Daniel P. Berrangé Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Daniel P. Berrangé --- include/qemu/sockets.h | 1 - util/oslib-posix.c | 6 ------ util/oslib-win32.c | 7 ------- 3 files changed, 14 deletions(-) diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index 9512fec514733..be351d85f7be5 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -46,7 +46,6 @@ ssize_t qemu_send_full(int s, const void *buf, size_t count) G_GNUC_WARN_UNUSED_RESULT; int socket_set_cork(int fd, int v); int socket_set_nodelay(int fd); -void qemu_socket_set_block(int fd); int socket_set_fast_reuse(int fd); #ifdef WIN32 diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 7654febfa5b1d..14cf94ac03417 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -265,12 +265,6 @@ bool qemu_set_blocking(int fd, bool block, Error **errp) return true; } -void qemu_socket_set_block(int fd) -{ - g_unix_set_fd_nonblocking(fd, false, NULL); -} - - int socket_set_fast_reuse(int fd) { int val = 1, ret; diff --git a/util/oslib-win32.c b/util/oslib-win32.c index bf5d478c5c8a2..b9ce2f96eefa7 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -195,13 +195,6 @@ bool qemu_set_blocking(int fd, bool block, Error **errp) return true; } -void qemu_socket_set_block(int fd) -{ - unsigned long opt = 0; - qemu_socket_unselect(fd, NULL); - ioctlsocket(fd, FIONBIO, &opt); -} - int socket_set_fast_reuse(int fd) { /* Enabling the reuse of an endpoint that was used by a socket still in From 6f607941b1c01679d6d3dca036ddd23bbe95a44c Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 16 Sep 2025 16:13:59 +0300 Subject: [PATCH 0783/1794] treewide: use qemu_set_blocking instead of g_unix_set_fd_nonblocking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of open-coded g_unix_set_fd_nonblocking() calls, use QEMU wrapper qemu_set_blocking(). Reviewed-by: Daniel P. Berrangé Signed-off-by: Vladimir Sementsov-Ogievskiy [DB: fix missing closing ) in tap-bsd.c, remove now unused GError var] Signed-off-by: Daniel P. Berrangé --- chardev/char-fd.c | 4 ++-- chardev/char-pty.c | 3 +-- chardev/char-serial.c | 3 +-- chardev/char-stdio.c | 3 +-- hw/input/virtio-input-host.c | 3 +-- hw/misc/ivshmem-flat.c | 3 ++- hw/misc/ivshmem-pci.c | 7 ++++++- hw/virtio/vhost-vsock.c | 8 ++------ io/channel-command.c | 9 ++++++--- io/channel-file.c | 3 +-- net/tap-bsd.c | 12 ++++++++++-- net/tap-linux.c | 7 ++++++- net/tap-solaris.c | 7 ++++++- net/tap.c | 21 ++++++--------------- qga/commands-posix.c | 3 +-- tests/qtest/fuzz/virtio_net_fuzz.c | 2 +- tests/qtest/vhost-user-test.c | 4 +--- tests/unit/test-iov.c | 5 +++-- ui/input-linux.c | 3 +-- util/event_notifier-posix.c | 7 +++++-- util/main-loop.c | 5 ++++- 21 files changed, 67 insertions(+), 55 deletions(-) diff --git a/chardev/char-fd.c b/chardev/char-fd.c index 6f03adf872531..739dc68c36276 100644 --- a/chardev/char-fd.c +++ b/chardev/char-fd.c @@ -212,8 +212,8 @@ void qemu_chr_open_fd(Chardev *chr, FDChardev *s = FD_CHARDEV(chr); g_autofree char *name = NULL; - if (fd_out >= 0 && !g_unix_set_fd_nonblocking(fd_out, true, NULL)) { - assert(!"Failed to set FD nonblocking"); + if (fd_out >= 0) { + qemu_set_blocking(fd_out, false, &error_abort); } if (fd_out == fd_in && fd_in >= 0) { diff --git a/chardev/char-pty.c b/chardev/char-pty.c index 674e9b3f1448f..fe6bfb043d50e 100644 --- a/chardev/char-pty.c +++ b/chardev/char-pty.c @@ -349,8 +349,7 @@ static void char_pty_open(Chardev *chr, } close(slave_fd); - if (!g_unix_set_fd_nonblocking(master_fd, true, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(master_fd, false, errp)) { return; } diff --git a/chardev/char-serial.c b/chardev/char-serial.c index 0a68b4b4e0b14..1ff31dcde3726 100644 --- a/chardev/char-serial.c +++ b/chardev/char-serial.c @@ -271,8 +271,7 @@ static void qmp_chardev_open_serial(Chardev *chr, if (fd < 0) { return; } - if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(fd, false, errp)) { return; } tty_serial_init(fd, 115200, 'N', 8, 1); diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c index 48db8d2f30fcf..193727e80738d 100644 --- a/chardev/char-stdio.c +++ b/chardev/char-stdio.c @@ -107,8 +107,7 @@ static void qemu_chr_open_stdio(Chardev *chr, old_fd0_flags = fcntl(0, F_GETFL); old_fd1_flags = fcntl(1, F_GETFL); tcgetattr(0, &oldtty); - if (!g_unix_set_fd_nonblocking(0, true, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(0, false, errp)) { return; } atexit(term_exit); diff --git a/hw/input/virtio-input-host.c b/hw/input/virtio-input-host.c index bbfee9d3b9f23..9f62532559d6a 100644 --- a/hw/input/virtio-input-host.c +++ b/hw/input/virtio-input-host.c @@ -114,8 +114,7 @@ static void virtio_input_host_realize(DeviceState *dev, Error **errp) error_setg_file_open(errp, errno, vih->evdev); return; } - if (!g_unix_set_fd_nonblocking(vih->fd, true, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(vih->fd, false, errp)) { goto err_close; } diff --git a/hw/misc/ivshmem-flat.c b/hw/misc/ivshmem-flat.c index fe4be6be178cf..e83e6c6ee9a7c 100644 --- a/hw/misc/ivshmem-flat.c +++ b/hw/misc/ivshmem-flat.c @@ -154,7 +154,8 @@ static void ivshmem_flat_add_vector(IvshmemFTState *s, IvshmemPeer *peer, * peer. */ peer->vector[peer->vector_counter].id = peer->vector_counter; - g_unix_set_fd_nonblocking(vector_fd, true, NULL); + /* WARNING: qemu_socket_set_nonblock() return code ignored */ + qemu_set_blocking(vector_fd, false, &error_warn); event_notifier_init_fd(&peer->vector[peer->vector_counter].event_notifier, vector_fd); diff --git a/hw/misc/ivshmem-pci.c b/hw/misc/ivshmem-pci.c index d47ae739d61ad..2748db9286e52 100644 --- a/hw/misc/ivshmem-pci.c +++ b/hw/misc/ivshmem-pci.c @@ -540,7 +540,12 @@ static void process_msg_connect(IVShmemState *s, uint16_t posn, int fd, IVSHMEM_DPRINTF("eventfds[%d][%d] = %d\n", posn, vector, fd); event_notifier_init_fd(&peer->eventfds[vector], fd); - g_unix_set_fd_nonblocking(fd, true, NULL); /* msix/irqfd poll non block */ + + /* msix/irqfd poll non block */ + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return; + } if (posn == s->vm_id) { setup_interrupt(s, vector, errp); diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c index 6e4088831fc50..107d88babea83 100644 --- a/hw/virtio/vhost-vsock.c +++ b/hw/virtio/vhost-vsock.c @@ -147,9 +147,7 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp) return; } - if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { - error_setg_errno(errp, errno, - "vhost-vsock: unable to set non-blocking mode"); + if (!qemu_set_blocking(vhostfd, false, errp)) { return; } } else { @@ -160,9 +158,7 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp) return; } - if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { - error_setg_errno(errp, errno, - "Failed to set FD nonblocking"); + if (!qemu_set_blocking(vhostfd, false, errp)) { return; } } diff --git a/io/channel-command.c b/io/channel-command.c index 8966dd3a2babf..8ae9a026b34c0 100644 --- a/io/channel-command.c +++ b/io/channel-command.c @@ -277,9 +277,12 @@ static int qio_channel_command_set_blocking(QIOChannel *ioc, cioc->blocking = enabled; #else - if ((cioc->writefd >= 0 && !g_unix_set_fd_nonblocking(cioc->writefd, !enabled, NULL)) || - (cioc->readfd >= 0 && !g_unix_set_fd_nonblocking(cioc->readfd, !enabled, NULL))) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (cioc->writefd >= 0 && + !qemu_set_blocking(cioc->writefd, enabled, errp)) { + return -1; + } + if (cioc->readfd >= 0 && + !qemu_set_blocking(cioc->readfd, enabled, errp)) { return -1; } #endif diff --git a/io/channel-file.c b/io/channel-file.c index ca3f180cc2fd9..5cef75a67c627 100644 --- a/io/channel-file.c +++ b/io/channel-file.c @@ -223,8 +223,7 @@ static int qio_channel_file_set_blocking(QIOChannel *ioc, #else QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); - if (!g_unix_set_fd_nonblocking(fioc->fd, !enabled, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(fioc->fd, enabled, errp)) { return -1; } return 0; diff --git a/net/tap-bsd.c b/net/tap-bsd.c index b4c84441ba8b7..3f98d0ea82e68 100644 --- a/net/tap-bsd.c +++ b/net/tap-bsd.c @@ -98,7 +98,12 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, return -1; } } - g_unix_set_fd_nonblocking(fd, true, NULL); + + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return -1; + } + return fd; } @@ -189,7 +194,10 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, goto error; } - g_unix_set_fd_nonblocking(fd, true, NULL); + if (!qemu_set_blocking(fd, false, errp)) { + goto error; + } + return fd; error: diff --git a/net/tap-linux.c b/net/tap-linux.c index 22ec2f45d2b7a..e832810665640 100644 --- a/net/tap-linux.c +++ b/net/tap-linux.c @@ -124,7 +124,12 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, return -1; } pstrcpy(ifname, ifname_size, ifr.ifr_name); - g_unix_set_fd_nonblocking(fd, true, NULL); + + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return -1; + } + return fd; } diff --git a/net/tap-solaris.c b/net/tap-solaris.c index 51b7830bef1d4..af2ebb16f513c 100644 --- a/net/tap-solaris.c +++ b/net/tap-solaris.c @@ -198,7 +198,12 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, return -1; } } - g_unix_set_fd_nonblocking(fd, true, NULL); + + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return -1; + } + return fd; } diff --git a/net/tap.c b/net/tap.c index f7df702f978f5..f37133e301647 100644 --- a/net/tap.c +++ b/net/tap.c @@ -627,8 +627,7 @@ int net_init_bridge(const Netdev *netdev, const char *name, return -1; } - if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(fd, false, errp)) { return -1; } vnet_hdr = tap_probe_vnet_hdr(fd, errp); @@ -729,9 +728,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, error_propagate(errp, err); goto failed; } - if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { - error_setg_errno(errp, errno, "%s: Can't use file descriptor %d", - name, fd); + if (!qemu_set_blocking(vhostfd, false, errp)) { goto failed; } } else { @@ -741,8 +738,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, "tap: open vhost char device failed"); goto failed; } - if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(vhostfd, false, errp)) { goto failed; } } @@ -839,9 +835,7 @@ int net_init_tap(const Netdev *netdev, const char *name, return -1; } - if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { - error_setg_errno(errp, errno, "%s: Can't use file descriptor %d", - name, fd); + if (!qemu_set_blocking(fd, false, errp)) { close(fd); return -1; } @@ -895,10 +889,8 @@ int net_init_tap(const Netdev *netdev, const char *name, goto free_fail; } - if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { + if (!qemu_set_blocking(fd, false, errp)) { ret = -1; - error_setg_errno(errp, errno, "%s: Can't use file descriptor %d", - name, fd); goto free_fail; } @@ -951,8 +943,7 @@ int net_init_tap(const Netdev *netdev, const char *name, return -1; } - if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(fd, false, errp)) { return -1; } vnet_hdr = tap_probe_vnet_hdr(fd, errp); diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 12bc086d79eac..5070f27d7586f 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -503,9 +503,8 @@ int64_t qmp_guest_file_open(const char *path, const char *mode, /* set fd non-blocking to avoid common use cases (like reading from a * named pipe) from hanging the agent */ - if (!g_unix_set_fd_nonblocking(fileno(fh), true, NULL)) { + if (!qemu_set_blocking(fileno(fh), false, errp)) { fclose(fh); - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); return -1; } diff --git a/tests/qtest/fuzz/virtio_net_fuzz.c b/tests/qtest/fuzz/virtio_net_fuzz.c index e239875e3b429..e9b13d3e4fabf 100644 --- a/tests/qtest/fuzz/virtio_net_fuzz.c +++ b/tests/qtest/fuzz/virtio_net_fuzz.c @@ -132,7 +132,7 @@ static void *virtio_net_test_setup_socket(GString *cmd_line, void *arg) { int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sockfds); g_assert_cmpint(ret, !=, -1); - g_unix_set_fd_nonblocking(sockfds[0], true, NULL); + qemu_set_blocking(sockfds[0], false, &error_abort); sockfds_initialized = true; g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", sockfds[1]); diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index 56472ca709f1b..6ec4ec2d5adc6 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -330,7 +330,6 @@ static int chr_can_read(void *opaque) static void chr_read(void *opaque, const uint8_t *buf, int size) { - g_autoptr(GError) err = NULL; TestServer *s = opaque; CharBackend *chr = &s->chr; VhostUserMsg msg; @@ -471,8 +470,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * The receive function forces it to be blocking, * so revert it back to non-blocking. */ - g_unix_set_fd_nonblocking(fd, true, &err); - g_assert_no_error(err); + qemu_set_blocking(fd, false, &error_abort); break; case VHOST_USER_SET_LOG_BASE: diff --git a/tests/unit/test-iov.c b/tests/unit/test-iov.c index 75bc3be00578e..63e2b1583cb66 100644 --- a/tests/unit/test-iov.c +++ b/tests/unit/test-iov.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/iov.h" #include "qemu/sockets.h" @@ -186,7 +187,7 @@ static void test_io(void) close(sv[0]); FD_SET(sv[1], &fds); - g_unix_set_fd_nonblocking(sv[1], true, NULL); + qemu_set_blocking(sv[1], false, &error_abort); r = g_test_rand_int_range(sz / 2, sz); setsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &r, sizeof(r)); @@ -222,7 +223,7 @@ static void test_io(void) close(sv[1]); FD_SET(sv[0], &fds); - g_unix_set_fd_nonblocking(sv[0], true, NULL); + qemu_set_blocking(sv[0], false, &error_abort); r = g_test_rand_int_range(sz / 2, sz); setsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &r, sizeof(r)); usleep(500000); diff --git a/ui/input-linux.c b/ui/input-linux.c index 92e1a1aa64ad1..44d0c15a9b724 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -316,8 +316,7 @@ static void input_linux_complete(UserCreatable *uc, Error **errp) error_setg_file_open(errp, errno, il->evdev); return; } - if (!g_unix_set_fd_nonblocking(il->fd, true, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(il->fd, false, errp)) { return; } diff --git a/util/event_notifier-posix.c b/util/event_notifier-posix.c index 76420c5b560c1..83fdbb96bbcfc 100644 --- a/util/event_notifier-posix.c +++ b/util/event_notifier-posix.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/event_notifier.h" #include "qemu/main-loop.h" @@ -36,6 +37,7 @@ int event_notifier_init(EventNotifier *e, int active) { int fds[2]; int ret; + Error *local_err = NULL; #ifdef CONFIG_EVENTFD ret = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); @@ -52,11 +54,11 @@ int event_notifier_init(EventNotifier *e, int active) if (!g_unix_open_pipe(fds, FD_CLOEXEC, NULL)) { return -errno; } - if (!g_unix_set_fd_nonblocking(fds[0], true, NULL)) { + if (!qemu_set_blocking(fds[0], false, &local_err)) { ret = -errno; goto fail; } - if (!g_unix_set_fd_nonblocking(fds[1], true, NULL)) { + if (!qemu_set_blocking(fds[1], false, &local_err)) { ret = -errno; goto fail; } @@ -70,6 +72,7 @@ int event_notifier_init(EventNotifier *e, int active) return 0; fail: + error_report_err(local_err); close(fds[0]); close(fds[1]); return ret; diff --git a/util/main-loop.c b/util/main-loop.c index 51aeb2432e77e..b8ddda8f5eecf 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -114,7 +114,10 @@ static int qemu_signal_init(Error **errp) return -errno; } - g_unix_set_fd_nonblocking(sigfd, true, NULL); + if (!qemu_set_blocking(sigfd, false, errp)) { + close(sigfd); + return -EINVAL; + } qemu_set_fd_handler(sigfd, sigfd_handler, NULL, (void *)(intptr_t)sigfd); From 69620c091d62f3a7016469c83b9064258524891d Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 16 Sep 2025 16:14:00 +0300 Subject: [PATCH 0784/1794] chardev: qemu_chr_open_fd(): add errp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every caller already support errp, let's go further. Suggested-by: Daniel P. Berrangé Reviewed-by: Daniel P. Berrangé Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Daniel P. Berrangé --- chardev/char-fd.c | 12 ++++++++---- chardev/char-file.c | 6 +++++- chardev/char-pipe.c | 9 ++++++++- chardev/char-serial.c | 5 ++++- chardev/char-stdio.c | 7 +++++-- include/chardev/char-fd.h | 2 +- 6 files changed, 31 insertions(+), 10 deletions(-) diff --git a/chardev/char-fd.c b/chardev/char-fd.c index 739dc68c36276..4ee286f323328 100644 --- a/chardev/char-fd.c +++ b/chardev/char-fd.c @@ -206,14 +206,16 @@ int qmp_chardev_open_file_source(char *src, int flags, Error **errp) } /* open a character device to a unix fd */ -void qemu_chr_open_fd(Chardev *chr, - int fd_in, int fd_out) +bool qemu_chr_open_fd(Chardev *chr, + int fd_in, int fd_out, Error **errp) { FDChardev *s = FD_CHARDEV(chr); g_autofree char *name = NULL; if (fd_out >= 0) { - qemu_set_blocking(fd_out, false, &error_abort); + if (!qemu_set_blocking(fd_out, false, errp)) { + return false; + } } if (fd_out == fd_in && fd_in >= 0) { @@ -221,7 +223,7 @@ void qemu_chr_open_fd(Chardev *chr, name = g_strdup_printf("chardev-file-%s", chr->label); qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name); s->ioc_out = QIO_CHANNEL(object_ref(s->ioc_in)); - return; + return true; } if (fd_in >= 0) { @@ -236,6 +238,8 @@ void qemu_chr_open_fd(Chardev *chr, name = g_strdup_printf("chardev-file-out-%s", chr->label); qio_channel_set_name(QIO_CHANNEL(s->ioc_out), name); } + + return true; } static void char_fd_class_init(ObjectClass *oc, const void *data) diff --git a/chardev/char-file.c b/chardev/char-file.c index a9e8c5e0d7f8d..89e9cb849c840 100644 --- a/chardev/char-file.c +++ b/chardev/char-file.c @@ -92,7 +92,11 @@ static void qmp_chardev_open_file(Chardev *chr, } } - qemu_chr_open_fd(chr, in, out); + if (!qemu_chr_open_fd(chr, in, out, errp)) { + qemu_close(out); + qemu_close(in); + return; + } #endif } diff --git a/chardev/char-pipe.c b/chardev/char-pipe.c index 3d1b0ce2d2ed3..e9f3bb82904c0 100644 --- a/chardev/char-pipe.c +++ b/chardev/char-pipe.c @@ -150,7 +150,14 @@ static void qemu_chr_open_pipe(Chardev *chr, return; } } - qemu_chr_open_fd(chr, fd_in, fd_out); + + if (!qemu_chr_open_fd(chr, fd_in, fd_out, errp)) { + close(fd_in); + if (fd_out != fd_in) { + close(fd_out); + } + return; + } } #endif /* !_WIN32 */ diff --git a/chardev/char-serial.c b/chardev/char-serial.c index 1ff31dcde3726..c622d758dbc48 100644 --- a/chardev/char-serial.c +++ b/chardev/char-serial.c @@ -276,7 +276,10 @@ static void qmp_chardev_open_serial(Chardev *chr, } tty_serial_init(fd, 115200, 'N', 8, 1); - qemu_chr_open_fd(chr, fd, fd); + if (!qemu_chr_open_fd(chr, fd, fd, errp)) { + close(fd); + return; + } } #endif /* __linux__ || __sun__ */ diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c index 193727e80738d..2568164a108e7 100644 --- a/chardev/char-stdio.c +++ b/chardev/char-stdio.c @@ -110,14 +110,17 @@ static void qemu_chr_open_stdio(Chardev *chr, if (!qemu_set_blocking(0, false, errp)) { return; } + + if (!qemu_chr_open_fd(chr, 0, 1, errp)) { + return; + } + atexit(term_exit); memset(&act, 0, sizeof(act)); act.sa_handler = term_stdio_handler; sigaction(SIGCONT, &act, NULL); - qemu_chr_open_fd(chr, 0, 1); - stdio_allow_signal = !opts->has_signal || opts->signal; qemu_chr_set_echo_stdio(chr, false); } diff --git a/include/chardev/char-fd.h b/include/chardev/char-fd.h index 9de0e440de5b2..6fe43062ca954 100644 --- a/include/chardev/char-fd.h +++ b/include/chardev/char-fd.h @@ -41,7 +41,7 @@ typedef struct FDChardev FDChardev; DECLARE_INSTANCE_CHECKER(FDChardev, FD_CHARDEV, TYPE_CHARDEV_FD) -void qemu_chr_open_fd(Chardev *chr, int fd_in, int fd_out); +bool qemu_chr_open_fd(Chardev *chr, int fd_in, int fd_out, Error **errp); int qmp_chardev_open_file_source(char *src, int flags, Error **errp); #endif /* CHAR_FD_H */ From 30b123acbb89bff8ce7e46f9eef8ad01c2f173c6 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 16 Sep 2025 16:14:01 +0300 Subject: [PATCH 0785/1794] chardev: close an fd on failure path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are at least two failure paths, where we forget to close an fd. Reviewed-by: Daniel P. Berrangé Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Daniel P. Berrangé --- chardev/char-pty.c | 1 + chardev/char-serial.c | 1 + 2 files changed, 2 insertions(+) diff --git a/chardev/char-pty.c b/chardev/char-pty.c index fe6bfb043d50e..b066f01412697 100644 --- a/chardev/char-pty.c +++ b/chardev/char-pty.c @@ -350,6 +350,7 @@ static void char_pty_open(Chardev *chr, close(slave_fd); if (!qemu_set_blocking(master_fd, false, errp)) { + close(master_fd); return; } diff --git a/chardev/char-serial.c b/chardev/char-serial.c index c622d758dbc48..4c6ca713eb1ab 100644 --- a/chardev/char-serial.c +++ b/chardev/char-serial.c @@ -272,6 +272,7 @@ static void qmp_chardev_open_serial(Chardev *chr, return; } if (!qemu_set_blocking(fd, false, errp)) { + close(fd); return; } tty_serial_init(fd, 115200, 'N', 8, 1); From 34523df31962401871ff091a9d0993ac87a9848a Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 16 Sep 2025 16:14:02 +0300 Subject: [PATCH 0786/1794] util/vhost-user-server: vu_message_read(): improve error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Drop extra error_report_err(NULL), it will just crash, if we get here. 2. Get and report error of qemu_set_blocking(), instead of aborting. Reviewed-by: Daniel P. Berrangé Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Daniel P. Berrangé --- util/vhost-user-server.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/util/vhost-user-server.c b/util/vhost-user-server.c index 04c72a92aae27..1dbe409f82938 100644 --- a/util/vhost-user-server.c +++ b/util/vhost-user-server.c @@ -62,7 +62,7 @@ static void vmsg_close_fds(VhostUserMsg *vmsg) } } -static void vmsg_unblock_fds(VhostUserMsg *vmsg) +static bool vmsg_unblock_fds(VhostUserMsg *vmsg, Error **errp) { int i; @@ -74,13 +74,16 @@ static void vmsg_unblock_fds(VhostUserMsg *vmsg) */ if (vmsg->request == VHOST_USER_ADD_MEM_REG || vmsg->request == VHOST_USER_SET_MEM_TABLE) { - return; + return true; } for (i = 0; i < vmsg->fd_num; i++) { - /* TODO: handle error more gracefully than aborting */ - qemu_set_blocking(vmsg->fds[i], false, &error_abort); + if (!qemu_set_blocking(vmsg->fds[i], false, errp)) { + return false; + } } + + return true; } static void panic_cb(VuDev *vu_dev, const char *buf) @@ -123,7 +126,6 @@ vu_message_read(VuDev *vu_dev, int conn_fd, VhostUserMsg *vmsg) vmsg->fd_num = 0; if (!ioc) { - error_report_err(local_err); goto fail; } @@ -177,7 +179,10 @@ vu_message_read(VuDev *vu_dev, int conn_fd, VhostUserMsg *vmsg) } while (read_bytes != VHOST_USER_HDR_SIZE); /* qio_channel_readv_full will make socket fds blocking, unblock them */ - vmsg_unblock_fds(vmsg); + if (!vmsg_unblock_fds(vmsg, &local_err)) { + error_report_err(local_err); + goto fail; + } if (vmsg->size > sizeof(vmsg->payload)) { error_report("Error: too big message request: %d, " "size: vmsg->size: %u, " From fcbe1080a4cb16767cdda788504f9793cbbe0532 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 19 Sep 2025 09:35:44 +0200 Subject: [PATCH 0787/1794] docs: use the pyvenv version of Meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The version in the system might be too old for QEMU; this will be especially true if Rust is going to be enabled by default. Adjust the docs to suggest using pyvenv/bin/meson, which is in fact what the "make" wrappers will be running internally. Reviewed-by: Daniel P. Berrangé Signed-off-by: Paolo Bonzini --- docs/devel/build-system.rst | 2 +- docs/devel/rust.rst | 2 +- docs/system/devices/igb.rst | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst index 2c884197a20aa..6204aa6a72edf 100644 --- a/docs/devel/build-system.rst +++ b/docs/devel/build-system.rst @@ -450,7 +450,7 @@ are run with ``make bench``. Meson test suites such as ``unit`` can be ran with ``make check-unit``, and ``make check-tcg`` builds and runs "non-Meson" tests for all targets. -If desired, it is also possible to use ``ninja`` and ``meson test``, +If desired, it is also possible to use ``ninja`` and ``pyvenv/bin/meson test``, respectively to build emulators and run tests defined in meson.build. The main difference is that ``make`` needs the ``-jN`` flag in order to enable parallel builds or tests. diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 13a20e86a1605..2f0ab2e282105 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -66,7 +66,7 @@ __ https://mesonbuild.com/Commands.html#devenv As shown above, you can use the ``--tests`` option as usual to operate on test code. Note however that you cannot *build* or run tests via ``cargo``, because they need support C code from QEMU that Cargo does not know about. Tests can -be run via ``meson test`` or ``make``:: +be run via Meson (``pyvenv/bin/meson test``) or ``make``:: make check-rust diff --git a/docs/system/devices/igb.rst b/docs/system/devices/igb.rst index 71f31cb116069..50f625fd77e42 100644 --- a/docs/system/devices/igb.rst +++ b/docs/system/devices/igb.rst @@ -54,7 +54,7 @@ directory: .. code-block:: shell - meson test qtest-x86_64/qos-test + pyvenv/bin/meson test qtest-x86_64/qos-test ethtool can test register accesses, interrupts, etc. It is automated as an functional test and can be run from the build directory with the following From b9e8bb6637d5f2926121d68e4f227ba0e0a44186 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 19 Sep 2025 10:23:58 +0200 Subject: [PATCH 0788/1794] rust: build: add back common and util tests These were dropped by mistake when extracting the crates. Reviewed-by: Manos Pitsidianakis Signed-off-by: Paolo Bonzini --- rust/common/meson.build | 3 +++ rust/util/meson.build | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/rust/common/meson.build b/rust/common/meson.build index b805e0faf573d..07d073182ed10 100644 --- a/rust/common/meson.build +++ b/rust/common/meson.build @@ -24,6 +24,9 @@ _common_rs = static_library( common_rs = declare_dependency(link_with: [_common_rs]) +rust.test('rust-common-tests', _common_rs, + suite: ['unit', 'rust']) + # Doctests are essentially integration tests, so they need the same dependencies. # Note that running them requires the object files for C code, so place them # in a separate suite that is run by the "build" CI jobs rather than "check". diff --git a/rust/util/meson.build b/rust/util/meson.build index 87a893673d23b..9633050445942 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -44,6 +44,10 @@ _util_rs = static_library( util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom]) +rust.test('rust-util-tests', _util_rs, + dependencies: [qemuutil, qom], + suite: ['unit', 'rust']) + # Doctests are essentially integration tests, so they need the same dependencies. # Note that running them requires the object files for C code, so place them # in a separate suite that is run by the "build" CI jobs rather than "check". From 75dbe618ac3895b2889a590de3bc589bf32dc999 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 17 Sep 2025 12:40:29 +0200 Subject: [PATCH 0789/1794] rust: vmstate: use "cast()" instead of "as" Reported by clippy, fix it. Reviewed-by: Manos Pitsidianakis Signed-off-by: Paolo Bonzini --- rust/migration/src/vmstate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs index c05c4a1fd6658..e04b19b3c9f31 100644 --- a/rust/migration/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -144,7 +144,7 @@ macro_rules! vmstate_of { $crate::bindings::VMStateField { name: ::core::concat!(::core::stringify!($field_name), "\0") .as_bytes() - .as_ptr() as *const ::std::os::raw::c_char, + .as_ptr().cast::<::std::os::raw::c_char>(), offset: ::std::mem::offset_of!($struct_name, $field_name), $(num_offset: ::std::mem::offset_of!($struct_name, $num),)? $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)? From f87a2e5c596620a606aaa7df4257c45c4988e160 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 19 Sep 2025 12:16:25 +0200 Subject: [PATCH 0790/1794] rust: build: remove "protocol: rust: from doctests It is added already by rust.doctest. Reviewed-by: Manos Pitsidianakis Signed-off-by: Paolo Bonzini --- rust/bql/meson.build | 1 - rust/common/meson.build | 1 - rust/migration/meson.build | 1 - rust/qom/meson.build | 1 - rust/util/meson.build | 1 - 5 files changed, 5 deletions(-) diff --git a/rust/bql/meson.build b/rust/bql/meson.build index f369209dfddce..7214d9440893e 100644 --- a/rust/bql/meson.build +++ b/rust/bql/meson.build @@ -47,6 +47,5 @@ bql_rs = declare_dependency(link_with: [_bql_rs], # in a separate suite that is run by the "build" CI jobs rather than "check". rust.doctest('rust-bql-rs-doctests', _bql_rs, - protocol: 'rust', dependencies: bql_rs, suite: ['doc', 'rust']) diff --git a/rust/common/meson.build b/rust/common/meson.build index 07d073182ed10..aff601d1df27c 100644 --- a/rust/common/meson.build +++ b/rust/common/meson.build @@ -32,6 +32,5 @@ rust.test('rust-common-tests', _common_rs, # in a separate suite that is run by the "build" CI jobs rather than "check". rust.doctest('rust-common-doctests', _common_rs, - protocol: 'rust', dependencies: common_rs, suite: ['doc', 'rust']) diff --git a/rust/migration/meson.build b/rust/migration/meson.build index 5e820d43f5017..2a49bd1633e59 100644 --- a/rust/migration/meson.build +++ b/rust/migration/meson.build @@ -48,6 +48,5 @@ migration_rs = declare_dependency(link_with: [_migration_rs], # in a separate suite that is run by the "build" CI jobs rather than "check". rust.doctest('rust-migration-rs-doctests', _migration_rs, - protocol: 'rust', dependencies: migration_rs, suite: ['doc', 'rust']) diff --git a/rust/qom/meson.build b/rust/qom/meson.build index 40c51b71b23a9..21e12148da4f0 100644 --- a/rust/qom/meson.build +++ b/rust/qom/meson.build @@ -38,6 +38,5 @@ qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_macros, qo # in a separate suite that is run by the "build" CI jobs rather than "check". rust.doctest('rust-qom-rs-doctests', _qom_rs, - protocol: 'rust', dependencies: qom_rs, suite: ['doc', 'rust']) diff --git a/rust/util/meson.build b/rust/util/meson.build index 9633050445942..7ca69939ce5cc 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -53,7 +53,6 @@ rust.test('rust-util-tests', _util_rs, # in a separate suite that is run by the "build" CI jobs rather than "check". rust.doctest('rust-util-rs-doctests', _util_rs, - protocol: 'rust', dependencies: util_rs, suite: ['doc', 'rust'] ) From 193f2ab6e076f0f3d07081802bee2d36eb86086f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 17 Jul 2025 08:02:15 +0200 Subject: [PATCH 0791/1794] subprojects: add attrs crate The attrs crate is a simple combinator-based for Rust attributes. It will be used instead of a handwritten parser. Signed-off-by: Paolo Bonzini --- rust/meson.build | 2 ++ rust/qemu-macros/Cargo.toml | 1 + rust/qemu-macros/meson.build | 1 + scripts/archive-source.sh | 2 +- scripts/make-release | 2 +- subprojects/.gitignore | 1 + subprojects/attrs-0.2-rs.wrap | 7 ++++ .../packagefiles/attrs-0.2-rs/meson.build | 33 +++++++++++++++++++ 8 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 subprojects/attrs-0.2-rs.wrap create mode 100644 subprojects/packagefiles/attrs-0.2-rs/meson.build diff --git a/rust/meson.build b/rust/meson.build index c7bd6aba45f45..b3ac3a7197045 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -13,10 +13,12 @@ libc_rs = dependency('libc-0.2-rs') subproject('proc-macro2-1-rs', required: true) subproject('quote-1-rs', required: true) subproject('syn-2-rs', required: true) +subproject('attrs-0.2-rs', required: true) quote_rs_native = dependency('quote-1-rs', native: true) syn_rs_native = dependency('syn-2-rs', native: true) proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true) +attrs_rs_native = dependency('attrs-0.2-rs', native: true) genrs = [] diff --git a/rust/qemu-macros/Cargo.toml b/rust/qemu-macros/Cargo.toml index 3b6f1d337f8ed..c25b6c0b0da97 100644 --- a/rust/qemu-macros/Cargo.toml +++ b/rust/qemu-macros/Cargo.toml @@ -16,6 +16,7 @@ rust-version.workspace = true proc-macro = true [dependencies] +attrs = "0.2.9" proc-macro2 = "1" quote = "1" syn = { version = "2", features = ["extra-traits"] } diff --git a/rust/qemu-macros/meson.build b/rust/qemu-macros/meson.build index d0b2992e204f1..0f27e0df925e9 100644 --- a/rust/qemu-macros/meson.build +++ b/rust/qemu-macros/meson.build @@ -8,6 +8,7 @@ _qemu_macros_rs = rust.proc_macro( '--cfg', 'feature="proc-macro"', ], dependencies: [ + attrs_rs_native, proc_macro2_rs_native, quote_rs_native, syn_rs_native, diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index 035828c532e78..476a996a70d52 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -27,7 +27,7 @@ sub_file="${sub_tdir}/submodule.tar" # in their checkout, because the build environment is completely # different to the host OS. subprojects="keycodemapdb libvfio-user berkeley-softfloat-3 - berkeley-testfloat-3 anyhow-1-rs arbitrary-int-1-rs bilge-0.2-rs + berkeley-testfloat-3 anyhow-1-rs arbitrary-int-1-rs attrs-0.2-rs bilge-0.2-rs bilge-impl-0.2-rs either-1-rs foreign-0.3-rs itertools-0.11-rs libc-0.2-rs proc-macro2-1-rs proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs diff --git a/scripts/make-release b/scripts/make-release index 87f563ef5f773..bc1b43caa2548 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -40,7 +40,7 @@ fi # Only include wraps that are invoked with subproject() SUBPROJECTS="libvfio-user keycodemapdb berkeley-softfloat-3 - berkeley-testfloat-3 anyhow-1-rs arbitrary-int-1-rs bilge-0.2-rs + berkeley-testfloat-3 anyhow-1-rs arbitrary-int-1-rs attrs-0.2-rs bilge-0.2-rs bilge-impl-0.2-rs either-1-rs foreign-0.3-rs itertools-0.11-rs libc-0.2-rs proc-macro2-1-rs proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs diff --git a/subprojects/.gitignore b/subprojects/.gitignore index f4281934ce11b..983c4c1549eff 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -8,6 +8,7 @@ /slirp /anyhow-1.0.98 /arbitrary-int-1.2.7 +/attrs-0.2.9 /bilge-0.2.0 /bilge-impl-0.2.0 /either-1.12.0 diff --git a/subprojects/attrs-0.2-rs.wrap b/subprojects/attrs-0.2-rs.wrap new file mode 100644 index 0000000000000..cd43c91d63ec7 --- /dev/null +++ b/subprojects/attrs-0.2-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = attrs-0.2.9 +source_url = https://crates.io/api/v1/crates/attrs/0.2.9/download +source_filename = attrs-0.2.9.tar.gz +source_hash = 2a207d40f43de65285f3de0509bb6cb16bc46098864fce957122bbacce327e5f +#method = cargo +patch_directory = attrs-0.2-rs diff --git a/subprojects/packagefiles/attrs-0.2-rs/meson.build b/subprojects/packagefiles/attrs-0.2-rs/meson.build new file mode 100644 index 0000000000000..ee575476cb2cd --- /dev/null +++ b/subprojects/packagefiles/attrs-0.2-rs/meson.build @@ -0,0 +1,33 @@ +project('attrs-0.2-rs', 'rust', + meson_version: '>=1.5.0', + version: '0.2.9', + license: 'MIT OR Apache-2.0', + default_options: []) + +subproject('proc-macro2-1-rs', required: true) +subproject('syn-2-rs', required: true) + +proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) +syn_dep = dependency('syn-2-rs', native: true) + +_attrs_rs = static_library( + 'attrs', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + rust_args: [ + '--cap-lints', 'allow', + ], + dependencies: [ + proc_macro2_dep, + syn_dep, + ], + native: true, +) + +attrs_dep = declare_dependency( + link_with: _attrs_rs, +) + +meson.override_dependency('attrs-0.2-rs', attrs_dep, native: true) From 60c96a8775c163383043d4ece6065dcb8f940856 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 16 Jul 2025 10:50:58 +0200 Subject: [PATCH 0792/1794] rust: qemu-macros: switch #[property] parsing to use combinators Since we are going to add more attribute parsing for high-level migration state macros, use the attrs crate instead of a handwritten parser for device properties as well. Signed-off-by: Paolo Bonzini --- rust/Cargo.lock | 11 +++++ rust/qemu-macros/src/lib.rs | 86 ++++++++++++++--------------------- rust/qemu-macros/src/tests.rs | 8 ++-- 3 files changed, 49 insertions(+), 56 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index eea928621a7db..8315f98c46f6b 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -14,6 +14,16 @@ version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d" +[[package]] +name = "attrs" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a207d40f43de65285f3de0509bb6cb16bc46098864fce957122bbacce327e5f" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "bilge" version = "0.2.0" @@ -188,6 +198,7 @@ dependencies = [ name = "qemu_macros" version = "0.1.0" dependencies = [ + "attrs", "proc-macro2", "quote", "syn", diff --git a/rust/qemu-macros/src/lib.rs b/rust/qemu-macros/src/lib.rs index 830b4326985bf..7ab18061776f7 100644 --- a/rust/qemu-macros/src/lib.rs +++ b/rust/qemu-macros/src/lib.rs @@ -3,10 +3,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later use proc_macro::TokenStream; -use quote::{quote, quote_spanned, ToTokens}; +use quote::{quote, quote_spanned}; use syn::{ - parse::Parse, parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, - token::Comma, Data, DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, + parse::{Parse, ParseStream}, + parse_macro_input, parse_quote, + punctuated::Punctuated, + spanned::Spanned, + token::Comma, + Attribute, Data, DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, Variant, }; mod bits; @@ -159,61 +163,37 @@ enum DevicePropertyName { Str(syn::LitStr), } -#[derive(Debug)] +impl Parse for DevicePropertyName { + fn parse(input: ParseStream<'_>) -> syn::Result { + let lo = input.lookahead1(); + if lo.peek(syn::LitStr) { + Ok(Self::Str(input.parse()?)) + } else if lo.peek(syn::LitCStr) { + Ok(Self::CStr(input.parse()?)) + } else { + Err(lo.error()) + } + } +} + +#[derive(Default, Debug)] struct DeviceProperty { rename: Option, defval: Option, } -impl Parse for DeviceProperty { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let _: syn::Token![#] = input.parse()?; - let bracketed; - _ = syn::bracketed!(bracketed in input); - let attribute = bracketed.parse::()?; - debug_assert_eq!(&attribute.to_string(), "property"); - let mut retval = Self { - rename: None, - defval: None, - }; - let content; - _ = syn::parenthesized!(content in bracketed); - while !content.is_empty() { - let value: syn::Ident = content.parse()?; - if value == "rename" { - let _: syn::Token![=] = content.parse()?; - if retval.rename.is_some() { - return Err(syn::Error::new( - value.span(), - "`rename` can only be used at most once", - )); - } - if content.peek(syn::LitStr) { - retval.rename = Some(DevicePropertyName::Str(content.parse::()?)); - } else { - retval.rename = - Some(DevicePropertyName::CStr(content.parse::()?)); - } - } else if value == "default" { - let _: syn::Token![=] = content.parse()?; - if retval.defval.is_some() { - return Err(syn::Error::new( - value.span(), - "`default` can only be used at most once", - )); - } - retval.defval = Some(content.parse()?); - } else { - return Err(syn::Error::new( - value.span(), - format!("unrecognized field `{value}`"), - )); - } +impl DeviceProperty { + fn parse_from(&mut self, a: &Attribute) -> syn::Result<()> { + use attrs::{set, with, Attrs}; + let mut parser = Attrs::new(); + parser.once("rename", with::eq(set::parse(&mut self.rename))); + parser.once("default", with::eq(set::parse(&mut self.defval))); + a.parse_args_with(&mut parser) + } - if !content.is_empty() { - let _: syn::Token![,] = content.parse()?; - } - } + fn parse(a: &Attribute) -> syn::Result { + let mut retval = Self::default(); + retval.parse_from(a)?; Ok(retval) } } @@ -235,7 +215,7 @@ fn derive_device_or_error(input: DeriveInput) -> Result, Error>>()?; let name = &input.ident; diff --git a/rust/qemu-macros/src/tests.rs b/rust/qemu-macros/src/tests.rs index 9ab7eab7f37a1..00a106612fc7d 100644 --- a/rust/qemu-macros/src/tests.rs +++ b/rust/qemu-macros/src/tests.rs @@ -60,7 +60,7 @@ fn test_derive_device() { migrate_clock: bool, } }, - "unrecognized field `defalt`" + "Expected one of `default` or `rename`" ); // Check that repeated attributes are not allowed: derive_compile_fail!( @@ -73,7 +73,8 @@ fn test_derive_device() { migrate_clock: bool, } }, - "`rename` can only be used at most once" + "Duplicate argument", + "Already used here", ); derive_compile_fail!( derive_device_or_error, @@ -85,7 +86,8 @@ fn test_derive_device() { migrate_clock: bool, } }, - "`default` can only be used at most once" + "Duplicate argument", + "Already used here", ); // Check that the field name is preserved when `rename` isn't used: derive_compile!( From a530a8d4ac39d04dc0095b1cdb88867146445706 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Sun, 21 Sep 2025 00:05:09 +0800 Subject: [PATCH 0793/1794] subprojects: Update .gitignore for proc-macro2 and syn Reviewed-by: Manos Pitsidianakis Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20250920160520.3699591-2-zhao1.liu@intel.com --- subprojects/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subprojects/.gitignore b/subprojects/.gitignore index 983c4c1549eff..38e949640f685 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -17,7 +17,7 @@ /libc-0.2.162 /proc-macro-error-1.0.4 /proc-macro-error-attr-1.0.4 -/proc-macro2-1.0.84 +/proc-macro2-1.0.95 /quote-1.0.36 /syn-2.0.66 /unicode-ident-1.0.12 From d935f0b8e17153e1dcfe93e7660bfd07693b513e Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Sun, 21 Sep 2025 00:05:10 +0800 Subject: [PATCH 0794/1794] subprojects: Ignore .wraplock file generated by meson v1.9.0 The .wraplock file is automatically generated by meson v1.9.0 (the related issue: https://github.com/mesonbuild/meson/issues/14948). Ignore it for now. Reviewed-by: Manos Pitsidianakis Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20250920160520.3699591-3-zhao1.liu@intel.com --- subprojects/.gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/subprojects/.gitignore b/subprojects/.gitignore index 38e949640f685..58a29f0120441 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -21,3 +21,6 @@ /quote-1.0.36 /syn-2.0.66 /unicode-ident-1.0.12 + +# Workaround for Meson v1.9.0 https://github.com/mesonbuild/meson/issues/14948 +/.wraplock From 1bbac0ca88cdfd6ac019685a855c92831e3862e3 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Sun, 21 Sep 2025 00:05:11 +0800 Subject: [PATCH 0795/1794] rust/qemu-macros: Fix Clippy's complaints about lambda parameter naming error: `rename` shadows a previous, unrelated binding --> qemu-macros/src/lib.rs:265:14 | 265 | |rename| -> Result { | ^^^^^^ | note: previous binding is here --> qemu-macros/src/lib.rs:245:30 | 245 | let DeviceProperty { rename, defval } = prop; | ^^^^^^ = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated = note: requested on the command line with `-D clippy::shadow-unrelated` Rename the lambda parameter to "prop_rename" to fix the above clippy error. Reviewed-by: Manos Pitsidianakis Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20250920160520.3699591-4-zhao1.liu@intel.com --- rust/qemu-macros/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/qemu-macros/src/lib.rs b/rust/qemu-macros/src/lib.rs index 7ab18061776f7..37e1b723bd55f 100644 --- a/rust/qemu-macros/src/lib.rs +++ b/rust/qemu-macros/src/lib.rs @@ -242,8 +242,8 @@ fn derive_device_or_error(input: DeriveInput) -> Result Result { - match rename { + |prop_rename| -> Result { + match prop_rename { DevicePropertyName::CStr(cstr_lit) => Ok(quote! { #cstr_lit }), DevicePropertyName::Str(str_lit) => { str_to_c_str!(str_lit.value(), str_lit.span()) From 35d7735f765adc4cf759b8ad221e8858ca5be9c5 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Sun, 21 Sep 2025 00:05:12 +0800 Subject: [PATCH 0796/1794] rust/common/uninit: Fix Clippy's complaints about lifetime Clippy complains about the following cases and following its suggestion to fix these warnings. warning: the following explicit lifetimes could be elided: 'a --> common/src/uninit.rs:38:6 | 38 | impl<'a, T, U> Deref for MaybeUninitField<'a, T, U> { | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes = note: `#[warn(clippy::needless_lifetimes)]` on by default help: elide the lifetimes | 38 - impl<'a, T, U> Deref for MaybeUninitField<'a, T, U> { 38 + impl Deref for MaybeUninitField<'_, T, U> { | warning: the following explicit lifetimes could be elided: 'a --> common/src/uninit.rs:49:6 | 49 | impl<'a, T, U> DerefMut for MaybeUninitField<'a, T, U> { | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 49 - impl<'a, T, U> DerefMut for MaybeUninitField<'a, T, U> { 49 + impl DerefMut for MaybeUninitField<'_, T, U> { | warning: `common` (lib) generated 2 warnings (run `cargo clippy --fix --lib -p common` to apply 2 suggestions) Reviewed-by: Manos Pitsidianakis Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20250920160520.3699591-5-zhao1.liu@intel.com --- rust/common/src/uninit.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/common/src/uninit.rs b/rust/common/src/uninit.rs index e7f9fcd2e3fb3..8d021b1dfc6e9 100644 --- a/rust/common/src/uninit.rs +++ b/rust/common/src/uninit.rs @@ -35,7 +35,7 @@ impl<'a, T, U> MaybeUninitField<'a, T, U> { } } -impl<'a, T, U> Deref for MaybeUninitField<'a, T, U> { +impl Deref for MaybeUninitField<'_, T, U> { type Target = MaybeUninit; fn deref(&self) -> &MaybeUninit { @@ -46,7 +46,7 @@ impl<'a, T, U> Deref for MaybeUninitField<'a, T, U> { } } -impl<'a, T, U> DerefMut for MaybeUninitField<'a, T, U> { +impl DerefMut for MaybeUninitField<'_, T, U> { fn deref_mut(&mut self) -> &mut MaybeUninit { // SAFETY: self.child was obtained by dereferencing a valid mutable // reference; the content of the memory may be invalid or uninitialized From 51d736cd71a6515808b705010ec7e38695cb7a01 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sun, 21 Sep 2025 00:05:13 +0800 Subject: [PATCH 0797/1794] rust/qdev: use addr_of! in QDevProp We want a &raw pointer, so unsafe { &_ } is not needed. Suggested-by: Zhao Liu Signed-off-by: Manos Pitsidianakis Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20250920160520.3699591-6-zhao1.liu@intel.com --- rust/hw/core/src/qdev.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs index 71b9ef141c3a4..3ee5b242622d8 100644 --- a/rust/hw/core/src/qdev.rs +++ b/rust/hw/core/src/qdev.rs @@ -6,7 +6,7 @@ use std::{ ffi::{c_int, c_void, CStr, CString}, - ptr::NonNull, + ptr::{addr_of, NonNull}, }; use chardev::Chardev; @@ -129,17 +129,17 @@ pub unsafe trait QDevProp { /// Use [`bindings::qdev_prop_bool`] for `bool`. unsafe impl QDevProp for bool { - const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_bool }; + const VALUE: *const bindings::PropertyInfo = addr_of!(bindings::qdev_prop_bool); } /// Use [`bindings::qdev_prop_uint64`] for `u64`. unsafe impl QDevProp for u64 { - const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_uint64 }; + const VALUE: *const bindings::PropertyInfo = addr_of!(bindings::qdev_prop_uint64); } /// Use [`bindings::qdev_prop_chr`] for [`chardev::CharBackend`]. unsafe impl QDevProp for chardev::CharBackend { - const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_chr }; + const VALUE: *const bindings::PropertyInfo = addr_of!(bindings::qdev_prop_chr); } /// Trait to define device properties. From bed2a37b2096e1d002f3b8e881c0f6eff863ff14 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sun, 21 Sep 2025 00:05:14 +0800 Subject: [PATCH 0798/1794] rust/qdev: Refine the documentation for QDevProp trait Refine the documentation to clarify: * `unsfae` requires that `VALUE` must be valid. * using `*const` instead of `&` because the latter will cause compiler error. Signed-off-by: Manos Pitsidianakis Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20250920160520.3699591-7-zhao1.liu@intel.com --- rust/hw/core/src/qdev.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs index 3ee5b242622d8..2735e2b2c1093 100644 --- a/rust/hw/core/src/qdev.rs +++ b/rust/hw/core/src/qdev.rs @@ -109,9 +109,16 @@ unsafe extern "C" fn rust_resettable_exit_fn( /// /// # Safety /// -/// This trait is marked as `unsafe` because currently having a `const` refer to -/// an `extern static` as a reference instead of a raw pointer results in this -/// compiler error: +/// This trait is marked as `unsafe` because `VALUE` must be a valid raw +/// reference to a [`bindings::PropertyInfo`]. +/// +/// Note we could not use a regular reference: +/// +/// ```text +/// const VALUE: &bindings::PropertyInfo = ... +/// ``` +/// +/// because this results in the following compiler error: /// /// ```text /// constructing invalid value: encountered reference to `extern` static in `const` @@ -119,7 +126,7 @@ unsafe extern "C" fn rust_resettable_exit_fn( /// /// This is because the compiler generally might dereference a normal reference /// during const evaluation, but not in this case (if it did, it'd need to -/// dereference the raw pointer so this would fail to compile). +/// dereference the raw pointer so using a `*const` would also fail to compile). /// /// It is the implementer's responsibility to provide a valid /// [`bindings::PropertyInfo`] pointer for the trait implementation to be safe. From b4221e88a9a497c3d72c4dfad1bcc12468f3fd01 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Sun, 21 Sep 2025 00:05:15 +0800 Subject: [PATCH 0799/1794] rust/qdev: Support property info for more common types Add a helper macro to implement QDevProp trait for u8/u16/u32/usize/i32 /i64. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20250920160520.3699591-8-zhao1.liu@intel.com --- rust/hw/core/src/qdev.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs index 2735e2b2c1093..85422e0379e65 100644 --- a/rust/hw/core/src/qdev.rs +++ b/rust/hw/core/src/qdev.rs @@ -134,20 +134,24 @@ pub unsafe trait QDevProp { const VALUE: *const bindings::PropertyInfo; } -/// Use [`bindings::qdev_prop_bool`] for `bool`. -unsafe impl QDevProp for bool { - const VALUE: *const bindings::PropertyInfo = addr_of!(bindings::qdev_prop_bool); -} - -/// Use [`bindings::qdev_prop_uint64`] for `u64`. -unsafe impl QDevProp for u64 { - const VALUE: *const bindings::PropertyInfo = addr_of!(bindings::qdev_prop_uint64); +macro_rules! impl_qdev_prop { + ($type:ty,$info:ident) => { + unsafe impl $crate::qdev::QDevProp for $type { + const VALUE: *const $crate::bindings::PropertyInfo = + addr_of!($crate::bindings::$info); + } + }; } -/// Use [`bindings::qdev_prop_chr`] for [`chardev::CharBackend`]. -unsafe impl QDevProp for chardev::CharBackend { - const VALUE: *const bindings::PropertyInfo = addr_of!(bindings::qdev_prop_chr); -} +impl_qdev_prop!(bool, qdev_prop_bool); +impl_qdev_prop!(u8, qdev_prop_uint8); +impl_qdev_prop!(u16, qdev_prop_uint16); +impl_qdev_prop!(u32, qdev_prop_uint32); +impl_qdev_prop!(u64, qdev_prop_uint64); +impl_qdev_prop!(usize, qdev_prop_usize); +impl_qdev_prop!(i32, qdev_prop_int32); +impl_qdev_prop!(i64, qdev_prop_int64); +impl_qdev_prop!(chardev::CharBackend, qdev_prop_chr); /// Trait to define device properties. /// From 9686aa9a05d9ef25c13ddc0af0123e2fc569b0ac Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Sun, 21 Sep 2025 00:05:16 +0800 Subject: [PATCH 0800/1794] rust/qdev: Support bit property in #property macro Add BIT_INFO to QDevProp trait, so that bit related property info could be bound to u32 & u64. Then add "bit=*" field in #property attributes macro to allow device to configure bit property. In addtion, convert the #property field parsing from `if-else` pattern to `match` pattern, to help readability. And note, the `bitnr` member of `Property` struct is generated by manual TokenStream construction, instead of conditional repetition (like #(bitnr: #bitnr,)?) since `quote` doesn't support this. In addtion, rename VALUE member of QDevProp trait to BASE_INFO. And update the test cases about qdev property. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20250920160520.3699591-9-zhao1.liu@intel.com --- rust/hw/core/src/qdev.rs | 19 ++++++++++++------- rust/qemu-macros/src/lib.rs | 18 +++++++++++++++--- rust/qemu-macros/src/tests.rs | 8 +++++--- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs index 85422e0379e65..9c82e1716c503 100644 --- a/rust/hw/core/src/qdev.rs +++ b/rust/hw/core/src/qdev.rs @@ -109,8 +109,8 @@ unsafe extern "C" fn rust_resettable_exit_fn( /// /// # Safety /// -/// This trait is marked as `unsafe` because `VALUE` must be a valid raw -/// reference to a [`bindings::PropertyInfo`]. +/// This trait is marked as `unsafe` because `BASE_INFO` and `BIT_INFO` must be +/// valid raw references to [`bindings::PropertyInfo`]. /// /// Note we could not use a regular reference: /// @@ -131,14 +131,19 @@ unsafe extern "C" fn rust_resettable_exit_fn( /// It is the implementer's responsibility to provide a valid /// [`bindings::PropertyInfo`] pointer for the trait implementation to be safe. pub unsafe trait QDevProp { - const VALUE: *const bindings::PropertyInfo; + const BASE_INFO: *const bindings::PropertyInfo; + const BIT_INFO: *const bindings::PropertyInfo = { + panic!("invalid type for bit property"); + }; } macro_rules! impl_qdev_prop { - ($type:ty,$info:ident) => { + ($type:ty,$info:ident$(, $bit_info:ident)?) => { unsafe impl $crate::qdev::QDevProp for $type { - const VALUE: *const $crate::bindings::PropertyInfo = + const BASE_INFO: *const $crate::bindings::PropertyInfo = addr_of!($crate::bindings::$info); + $(const BIT_INFO: *const $crate::bindings::PropertyInfo = + addr_of!($crate::bindings::$bit_info);)? } }; } @@ -146,8 +151,8 @@ macro_rules! impl_qdev_prop { impl_qdev_prop!(bool, qdev_prop_bool); impl_qdev_prop!(u8, qdev_prop_uint8); impl_qdev_prop!(u16, qdev_prop_uint16); -impl_qdev_prop!(u32, qdev_prop_uint32); -impl_qdev_prop!(u64, qdev_prop_uint64); +impl_qdev_prop!(u32, qdev_prop_uint32, qdev_prop_bit); +impl_qdev_prop!(u64, qdev_prop_uint64, qdev_prop_bit64); impl_qdev_prop!(usize, qdev_prop_usize); impl_qdev_prop!(i32, qdev_prop_int32); impl_qdev_prop!(i64, qdev_prop_int64); diff --git a/rust/qemu-macros/src/lib.rs b/rust/qemu-macros/src/lib.rs index 37e1b723bd55f..3e21b67b47195 100644 --- a/rust/qemu-macros/src/lib.rs +++ b/rust/qemu-macros/src/lib.rs @@ -179,6 +179,7 @@ impl Parse for DevicePropertyName { #[derive(Default, Debug)] struct DeviceProperty { rename: Option, + bitnr: Option, defval: Option, } @@ -187,6 +188,7 @@ impl DeviceProperty { use attrs::{set, with, Attrs}; let mut parser = Attrs::new(); parser.once("rename", with::eq(set::parse(&mut self.rename))); + parser.once("bit", with::eq(set::parse(&mut self.bitnr))); parser.once("default", with::eq(set::parse(&mut self.defval))); a.parse_args_with(&mut parser) } @@ -222,7 +224,11 @@ fn derive_device_or_error(input: DeriveInput) -> Result {{ @@ -252,14 +258,20 @@ fn derive_device_or_error(input: DeriveInput) -> Result::VALUE }; + let qdev_prop = if bitnr.is_none() { + quote! { <#field_ty as ::hwcore::QDevProp>::BASE_INFO } + } else { + quote! { <#field_ty as ::hwcore::QDevProp>::BIT_INFO } + }; + let bitnr = bitnr.unwrap_or(syn::Expr::Verbatim(quote! { 0 })); let set_default = defval.is_some(); let defval = defval.unwrap_or(syn::Expr::Verbatim(quote! { 0 })); properties_expanded.push(quote! { ::hwcore::bindings::Property { name: ::std::ffi::CStr::as_ptr(#prop_name), - info: #qdev_prop , + info: #qdev_prop, offset: ::core::mem::offset_of!(#name, #field_name) as isize, + bitnr: #bitnr, set_default: #set_default, defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: #defval as u64 }, ..::common::Zeroable::ZERO diff --git a/rust/qemu-macros/src/tests.rs b/rust/qemu-macros/src/tests.rs index 00a106612fc7d..ec137132ae7d9 100644 --- a/rust/qemu-macros/src/tests.rs +++ b/rust/qemu-macros/src/tests.rs @@ -60,7 +60,7 @@ fn test_derive_device() { migrate_clock: bool, } }, - "Expected one of `default` or `rename`" + "Expected one of `bit`, `default` or `rename`" ); // Check that repeated attributes are not allowed: derive_compile_fail!( @@ -106,8 +106,9 @@ fn test_derive_device() { const PROPERTIES: &'static [::hwcore::bindings::Property] = &[ ::hwcore::bindings::Property { name: ::std::ffi::CStr::as_ptr(c"migrate_clock"), - info: ::VALUE, + info: ::BASE_INFO, offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, + bitnr: 0, set_default: true, defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 }, ..::common::Zeroable::ZERO @@ -133,8 +134,9 @@ fn test_derive_device() { const PROPERTIES: &'static [::hwcore::bindings::Property] = &[ ::hwcore::bindings::Property { name: ::std::ffi::CStr::as_ptr(c"migrate-clk"), - info: ::VALUE, + info: ::BASE_INFO, offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, + bitnr: 0, set_default: true, defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 }, ..::common::Zeroable::ZERO From a8f080215f79d211c4be0f896ad5f32dbc3b7c03 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Sun, 21 Sep 2025 00:05:17 +0800 Subject: [PATCH 0801/1794] rust/qdev: Test bit property for #property There's a diference between Rust and C: Though C macro (e.g., DEFINE_PROP_BIT or DEFINE_PROP_BIT64) always requires default value, Rust side allows to omit this "default" field in #property, and provides a default value ("0" - false) for this field. This minor difference does not break user habits and should be acceptable. Therefore, the test cases also cover this scenario. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20250920160520.3699591-10-zhao1.liu@intel.com --- rust/qemu-macros/src/tests.rs | 99 +++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/rust/qemu-macros/src/tests.rs b/rust/qemu-macros/src/tests.rs index ec137132ae7d9..ac998d20e3040 100644 --- a/rust/qemu-macros/src/tests.rs +++ b/rust/qemu-macros/src/tests.rs @@ -89,6 +89,19 @@ fn test_derive_device() { "Duplicate argument", "Already used here", ); + derive_compile_fail!( + derive_device_or_error, + quote! { + #[repr(C)] + #[derive(Device)] + struct DummyState { + #[property(bit = 0, bit = 1)] + flags: u32, + } + }, + "Duplicate argument", + "Already used here", + ); // Check that the field name is preserved when `rename` isn't used: derive_compile!( derive_device_or_error, @@ -145,6 +158,92 @@ fn test_derive_device() { } } ); + // Check that `bit` value is used for the bit property without default + // value (note: though C macro (e.g., DEFINE_PROP_BIT) always requires + // default value, Rust side allows to default this field to "0"): + derive_compile!( + derive_device_or_error, + quote! { + #[repr(C)] + #[derive(Device)] + pub struct DummyState { + parent: ParentField, + #[property(bit = 3)] + flags: u32, + } + }, + quote! { + unsafe impl ::hwcore::DevicePropertiesImpl for DummyState { + const PROPERTIES: &'static [::hwcore::bindings::Property] = &[ + ::hwcore::bindings::Property { + name: ::std::ffi::CStr::as_ptr(c"flags"), + info: ::BIT_INFO, + offset: ::core::mem::offset_of!(DummyState, flags) as isize, + bitnr: 3, + set_default: false, + defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: 0 as u64 }, + ..::common::Zeroable::ZERO + } + ]; + } + } + ); + // Check that `bit` value is used for the bit property when used: + derive_compile!( + derive_device_or_error, + quote! { + #[repr(C)] + #[derive(Device)] + pub struct DummyState { + parent: ParentField, + #[property(bit = 3, default = true)] + flags: u32, + } + }, + quote! { + unsafe impl ::hwcore::DevicePropertiesImpl for DummyState { + const PROPERTIES: &'static [::hwcore::bindings::Property] = &[ + ::hwcore::bindings::Property { + name: ::std::ffi::CStr::as_ptr(c"flags"), + info: ::BIT_INFO, + offset: ::core::mem::offset_of!(DummyState, flags) as isize, + bitnr: 3, + set_default: true, + defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 }, + ..::common::Zeroable::ZERO + } + ]; + } + } + ); + // Check that `bit` value is used for the bit property with rename when used: + derive_compile!( + derive_device_or_error, + quote! { + #[repr(C)] + #[derive(Device)] + pub struct DummyState { + parent: ParentField, + #[property(rename = "msi", bit = 3, default = false)] + flags: u64, + } + }, + quote! { + unsafe impl ::hwcore::DevicePropertiesImpl for DummyState { + const PROPERTIES: &'static [::hwcore::bindings::Property] = &[ + ::hwcore::bindings::Property { + name: ::std::ffi::CStr::as_ptr(c"msi"), + info: ::BIT_INFO, + offset: ::core::mem::offset_of!(DummyState, flags) as isize, + bitnr: 3, + set_default: true, + defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: false as u64 }, + ..::common::Zeroable::ZERO + } + ]; + } + } + ); } #[test] From 4f09cf54faad32d41dfb5ccdd4c0e7871b636274 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Sun, 21 Sep 2025 00:05:18 +0800 Subject: [PATCH 0802/1794] rust/hpet: Clean up type mismatch for num_timers property Now `num_timers` is `usize` other than `u8`. Although the type field in `declare_properties` macro hasn't been used, it's better to explicitly point this out and clean up this before doing other property work. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20250920160520.3699591-11-zhao1.liu@intel.com --- rust/hw/timer/hpet/src/device.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 3cfbe9c32bb18..fce75415579dd 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -909,7 +909,7 @@ declare_properties! { HPETState, num_timers, unsafe { &qdev_prop_usize }, - u8, + usize, default = HPET_MIN_TIMERS ), define_property!( From b160ebadc196091ca7abe00471f49fdc6bb8faf6 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Sun, 21 Sep 2025 00:05:19 +0800 Subject: [PATCH 0803/1794] rust/hpet: Convert qdev properties to #property macro Convert HPET's properties to #property macro: * num_timers: usize property. * flags: u32 bit property. * int_route_cap: u32 property. * hpet_offset_saved: bool property. Reviewed-by: Manos Pitsidianakis Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20250920160520.3699591-12-zhao1.liu@intel.com --- rust/hw/timer/hpet/src/device.rs | 55 ++++---------------------------- 1 file changed, 7 insertions(+), 48 deletions(-) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index fce75415579dd..86638c0766668 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -13,9 +13,8 @@ use std::{ use bql::{BqlCell, BqlRefCell}; use common::{bitops::IntegerExt, uninit_field_mut}; use hwcore::{ - bindings::{qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize}, - declare_properties, define_property, DeviceImpl, DeviceMethods, DeviceState, InterruptSource, - Property, ResetType, ResettablePhasesImpl, SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods, + DeviceImpl, DeviceMethods, DeviceState, InterruptSource, ResetType, ResettablePhasesImpl, + SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods, }; use migration::{ self, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, @@ -520,7 +519,7 @@ impl HPETTimer { /// HPET Event Timer Block Abstraction #[repr(C)] -#[derive(qom::Object)] +#[derive(qom::Object, hwcore::Device)] pub struct HPETState { parent_obj: ParentField, iomem: MemoryRegion, @@ -540,10 +539,12 @@ pub struct HPETState { // Internal state /// Capabilities that QEMU HPET supports. /// bit 0: MSI (or FSB) support. + #[property(rename = "msi", bit = HPET_FLAG_MSI_SUPPORT_SHIFT as u8, default = false)] flags: u32, /// Offset of main counter relative to qemu clock. hpet_offset: BqlCell, + #[property(rename = "hpet-offset-saved", default = true)] hpet_offset_saved: bool, irqs: [InterruptSource; HPET_NUM_IRQ_ROUTES], @@ -555,11 +556,13 @@ pub struct HPETState { /// the timers' interrupt can be routed, and is encoded in the /// bits 32:64 of timer N's config register: #[doc(alias = "intcap")] + #[property(rename = "hpet-intcap", default = 0)] int_route_cap: u32, /// HPET timer array managed by this timer block. #[doc(alias = "timer")] timers: [BqlRefCell; HPET_MAX_TIMERS], + #[property(rename = "timers", default = HPET_MIN_TIMERS)] num_timers: usize, num_timers_save: BqlCell, @@ -901,44 +904,6 @@ impl ObjectImpl for HPETState { const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::; } -// TODO: Make these properties user-configurable! -declare_properties! { - HPET_PROPERTIES, - define_property!( - c"timers", - HPETState, - num_timers, - unsafe { &qdev_prop_usize }, - usize, - default = HPET_MIN_TIMERS - ), - define_property!( - c"msi", - HPETState, - flags, - unsafe { &qdev_prop_bit }, - u32, - bit = HPET_FLAG_MSI_SUPPORT_SHIFT as u8, - default = false, - ), - define_property!( - c"hpet-intcap", - HPETState, - int_route_cap, - unsafe { &qdev_prop_uint32 }, - u32, - default = 0 - ), - define_property!( - c"hpet-offset-saved", - HPETState, - hpet_offset_saved, - unsafe { &qdev_prop_bool }, - bool, - default = true - ), -} - static VMSTATE_HPET_RTC_IRQ_LEVEL: VMStateDescription = VMStateDescriptionBuilder::::new() .name(c"hpet/rtc_irq_level") @@ -1001,12 +966,6 @@ const VMSTATE_HPET: VMStateDescription = )) .build(); -// SAFETY: HPET_PROPERTIES is a valid Property array constructed with the -// hwcore::declare_properties macro. -unsafe impl hwcore::DevicePropertiesImpl for HPETState { - const PROPERTIES: &'static [Property] = &HPET_PROPERTIES; -} - impl DeviceImpl for HPETState { const VMSTATE: Option> = Some(VMSTATE_HPET); const REALIZE: Option util::Result<()>> = Some(Self::realize); From 0a7fe8d407009840bee0e6d95005714ede51d0b8 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Sun, 21 Sep 2025 00:05:20 +0800 Subject: [PATCH 0804/1794] rust/qdev: Drop declare_properties & define_property macros After HPET's #property conversion, there's no use case for declare_properties & define_property. So get rid of them for now. In future, if there's something that #property really cannot resolve, they can be brought back. Reviewed-by: Manos Pitsidianakis Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20250920160520.3699591-13-zhao1.liu@intel.com --- rust/hw/core/src/qdev.rs | 53 ---------------------------------------- 1 file changed, 53 deletions(-) diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs index 9c82e1716c503..a4493dbf0119e 100644 --- a/rust/hw/core/src/qdev.rs +++ b/rust/hw/core/src/qdev.rs @@ -248,59 +248,6 @@ impl DeviceClass { } } -#[macro_export] -macro_rules! define_property { - ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, bit = $bitnr:expr, default = $defval:expr$(,)*) => { - $crate::bindings::Property { - // use associated function syntax for type checking - name: ::std::ffi::CStr::as_ptr($name), - info: $prop, - offset: ::std::mem::offset_of!($state, $field) as isize, - bitnr: $bitnr, - set_default: true, - defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, - ..::common::zeroable::Zeroable::ZERO - } - }; - ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { - $crate::bindings::Property { - // use associated function syntax for type checking - name: ::std::ffi::CStr::as_ptr($name), - info: $prop, - offset: ::std::mem::offset_of!($state, $field) as isize, - set_default: true, - defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, - ..::common::zeroable::Zeroable::ZERO - } - }; - ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => { - $crate::bindings::Property { - // use associated function syntax for type checking - name: ::std::ffi::CStr::as_ptr($name), - info: $prop, - offset: ::std::mem::offset_of!($state, $field) as isize, - set_default: false, - ..::common::zeroable::Zeroable::ZERO - } - }; -} - -#[macro_export] -macro_rules! declare_properties { - ($ident:ident, $($prop:expr),*$(,)*) => { - pub static $ident: [$crate::bindings::Property; { - let mut len = 0; - $({ - _ = stringify!($prop); - len += 1; - })* - len - }] = [ - $($prop),*, - ]; - }; -} - unsafe impl ObjectType for DeviceState { type Class = DeviceClass; const TYPE_NAME: &'static CStr = From 6096dfa6c5cedf1e4e9ae04b27fb238ef390047f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 9 Sep 2025 09:48:17 +0200 Subject: [PATCH 0805/1794] tests/functional/m68k: Use proper polling in the next-cube test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The next-cube tests currently sleep for 2 seconds to wait for the guest's display to come up with the expected results. That's bad since there is still a theoretical race left here, and since there are two subtests, the whole test takes more than 4 seconds this way. Looking at what the firmware does, there is a better way instead of blindly waiting for two seconds: The firmware is writing some values to the FPU registers during a test (and never touches them again afterwards, so we can be sure about the final values), so we can poll for the right values in those registers to know when we reached a state when the display is initialized for sure. We just have to also make sure to not look for text anymore that is only printed after the FPU test has been done by the guest firmware. This way the whole tests finishes in less than 1 second here, and there should be no race condition here anymore. Message-ID: <20250909074817.84661-1-thuth@redhat.com> Acked-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- tests/functional/m68k/test_nextcube.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/functional/m68k/test_nextcube.py b/tests/functional/m68k/test_nextcube.py index c1610e58456fa..e5e1c69dcbd7f 100755 --- a/tests/functional/m68k/test_nextcube.py +++ b/tests/functional/m68k/test_nextcube.py @@ -29,8 +29,15 @@ def check_bootrom_framebuffer(self, screenshot_path): self.vm.launch() self.log.info('VM launched, waiting for display') - # TODO: wait for the 'displaysurface_create 1120x832' trace-event. - time.sleep(2) + # Wait for the FPU test to finish, then the display is available, too: + while True: + res = self.vm.cmd('human-monitor-command', + command_line='info registers') + if ("F0 = 400e 8400000000000000" in res and + "F1 = 400e 83ff000000000000" in res and + "F2 = 400e 83ff000000000000" in res): + break + time.sleep(0.1) res = self.vm.cmd('human-monitor-command', command_line='screendump %s' % screenshot_path) @@ -56,10 +63,10 @@ def test_bootrom_framebuffer_ocr_with_tesseract(self): self.check_bootrom_framebuffer(screenshot_path) lines = tesseract_ocr(screenshot_path) text = '\n'.join(lines) + self.assertIn('Backplane slot', text) + self.assertIn('Ethernet address', text) self.assertIn('Testing the FPU', text) - self.assertIn('System test failed. Error code', text) - self.assertIn('Boot command', text) - self.assertIn('Next>', text) + if __name__ == '__main__': QemuSystemTest.main() From db05b0d21ec1e0532cf5f5103ae6520a838d96f9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 19 Sep 2025 10:20:54 +0200 Subject: [PATCH 0806/1794] linux-user: avoid -Werror=int-in-bool-context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit linux-user is failing to compile on Fedora 43: ../linux-user/strace.c:57:66: error: enum constant in boolean context [-Werror=int-in-bool-context] 57 | #define FLAG_BASIC(V, M, N) { V, M | QEMU_BUILD_BUG_ON_ZERO(!(M)), N } The warning does not seem to be too useful and we could even disable it, but the workaround is simple in this case. Cc: qemu-stable@nongnu.org Cc: Richard Henderson Reviewed-by: Daniel P. Berrangé Signed-off-by: Paolo Bonzini --- linux-user/strace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/strace.c b/linux-user/strace.c index 1233ebceb08b1..758c5d32b6c0f 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -54,7 +54,7 @@ struct flags { }; /* No 'struct flags' element should have a zero mask. */ -#define FLAG_BASIC(V, M, N) { V, M | QEMU_BUILD_BUG_ON_ZERO(!(M)), N } +#define FLAG_BASIC(V, M, N) { V, M | QEMU_BUILD_BUG_ON_ZERO((M) == 0), N } /* common flags for all architectures */ #define FLAG_GENERIC_MASK(V, M) FLAG_BASIC(V, M, #V) From ee3b39c92454b0360a614bd017eb5e545ea61f4e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 4 Sep 2025 11:30:53 +0200 Subject: [PATCH 0807/1794] linux-user: Create vdso_sigreturn_region_{start,end} These variables will be populated from the vdso, and used for detecting whether we are executing the sigreturn. Signed-off-by: Richard Henderson --- linux-user/elfload.c | 5 +++++ linux-user/gen-vdso-elfn.c.inc | 7 +++++-- linux-user/gen-vdso.c | 6 ++++++ linux-user/loader.h | 2 ++ linux-user/signal-common.h | 2 ++ linux-user/signal.c | 2 ++ 6 files changed, 22 insertions(+), 2 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 26c090c95d3e9..28f0909d1a41c 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1659,6 +1659,11 @@ static void load_elf_vdso(struct image_info *info, const VdsoImageInfo *vdso) if (vdso->rt_sigreturn_ofs) { default_rt_sigreturn = load_addr + vdso->rt_sigreturn_ofs; } + if (vdso->sigreturn_region_start_ofs) { + vdso_sigreturn_region_start = + load_addr + vdso->sigreturn_region_start_ofs; + vdso_sigreturn_region_end = load_addr + vdso->sigreturn_region_end_ofs; + } /* Remove write from VDSO segment. */ target_mprotect(info->start_data, info->end_data - info->start_data, diff --git a/linux-user/gen-vdso-elfn.c.inc b/linux-user/gen-vdso-elfn.c.inc index b47019e136c45..c2677a146c679 100644 --- a/linux-user/gen-vdso-elfn.c.inc +++ b/linux-user/gen-vdso-elfn.c.inc @@ -84,9 +84,12 @@ static void elfN(search_symtab)(ElfN(Shdr) *shdr, unsigned sym_idx, if (sigreturn_sym && strcmp(sigreturn_sym, name) == 0) { sigreturn_addr = sym.st_value; - } - if (rt_sigreturn_sym && strcmp(rt_sigreturn_sym, name) == 0) { + } else if (rt_sigreturn_sym && strcmp(rt_sigreturn_sym, name) == 0) { rt_sigreturn_addr = sym.st_value; + } else if (strcmp("sigreturn_region_start", name) == 0) { + sigreturn_region_start_addr = sym.st_value; + } else if (strcmp("sigreturn_region_end", name) == 0) { + sigreturn_region_end_addr = sym.st_value; } } } diff --git a/linux-user/gen-vdso.c b/linux-user/gen-vdso.c index aeaa927db8fc9..d6a2cdaa835cc 100644 --- a/linux-user/gen-vdso.c +++ b/linux-user/gen-vdso.c @@ -36,6 +36,8 @@ static const char *rt_sigreturn_sym; static unsigned sigreturn_addr; static unsigned rt_sigreturn_addr; +static unsigned sigreturn_region_start_addr; +static unsigned sigreturn_region_end_addr; #define N 32 #define elfN(x) elf32_##x @@ -215,6 +217,10 @@ int main(int argc, char **argv) fprintf(outf, " .reloc_count = ARRAY_SIZE(%s_relocs),\n", prefix); fprintf(outf, " .sigreturn_ofs = 0x%x,\n", sigreturn_addr); fprintf(outf, " .rt_sigreturn_ofs = 0x%x,\n", rt_sigreturn_addr); + fprintf(outf, " .sigreturn_region_start_ofs = 0x%x,\n", + sigreturn_region_start_addr); + fprintf(outf, " .sigreturn_region_end_ofs = 0x%x,\n", + sigreturn_region_end_addr); fprintf(outf, "};\n"); ret = EXIT_SUCCESS; diff --git a/linux-user/loader.h b/linux-user/loader.h index e42b8fa1e30f3..da9ad28db5de5 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -117,6 +117,8 @@ typedef struct { unsigned reloc_count; unsigned sigreturn_ofs; unsigned rt_sigreturn_ofs; + unsigned sigreturn_region_start_ofs; + unsigned sigreturn_region_end_ofs; } VdsoImageInfo; /* Note that both Elf32_Word and Elf64_Word are uint32_t. */ diff --git a/linux-user/signal-common.h b/linux-user/signal-common.h index 196d2406f86ee..0b04868727ed6 100644 --- a/linux-user/signal-common.h +++ b/linux-user/signal-common.h @@ -25,6 +25,8 @@ /* Fallback addresses into sigtramp page. */ extern abi_ulong default_sigreturn; extern abi_ulong default_rt_sigreturn; +extern abi_ulong vdso_sigreturn_region_start; +extern abi_ulong vdso_sigreturn_region_end; void setup_sigtramp(abi_ulong tramp_page); diff --git a/linux-user/signal.c b/linux-user/signal.c index cd0e7398aa46a..804096bd4470b 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -50,6 +50,8 @@ static void host_signal_handler(int host_signum, siginfo_t *info, /* Fallback addresses into sigtramp page. */ abi_ulong default_sigreturn; abi_ulong default_rt_sigreturn; +abi_ulong vdso_sigreturn_region_start; +abi_ulong vdso_sigreturn_region_end; /* * System includes define _NSIG as SIGRTMAX + 1, but qemu (like the kernel) From b9baf09512756943ced419aa5ec71bf435681e09 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 4 Sep 2025 11:49:04 +0200 Subject: [PATCH 0808/1794] linux-user: Populate sigreturn_region_{start,end} in all vdso.S Mark the regions which contain sigreturn syscalls within each vdso. Rebuild the shared objects. Signed-off-by: Richard Henderson --- linux-user/aarch64/vdso-be.so | Bin 3224 -> 3320 bytes linux-user/aarch64/vdso-le.so | Bin 3224 -> 3320 bytes linux-user/aarch64/vdso.S | 2 ++ linux-user/arm/vdso-be32.so | Bin 2648 -> 2724 bytes linux-user/arm/vdso-be8.so | Bin 2648 -> 2724 bytes linux-user/arm/vdso-le.so | Bin 2648 -> 2724 bytes linux-user/arm/vdso.S | 2 ++ linux-user/hppa/vdso.S | 2 ++ linux-user/hppa/vdso.so | Bin 2104 -> 2224 bytes linux-user/i386/vdso.S | 2 ++ linux-user/i386/vdso.so | Bin 2672 -> 2792 bytes linux-user/loongarch64/vdso.S | 2 ++ linux-user/loongarch64/vdso.so | Bin 3560 -> 3712 bytes linux-user/ppc/vdso-32.so | Bin 3020 -> 3140 bytes linux-user/ppc/vdso-64.so | Bin 3896 -> 4048 bytes linux-user/ppc/vdso-64le.so | Bin 3896 -> 4048 bytes linux-user/ppc/vdso.S | 2 ++ linux-user/riscv/vdso-32.so | Bin 2980 -> 3124 bytes linux-user/riscv/vdso-64.so | Bin 3944 -> 4104 bytes linux-user/riscv/vdso.S | 2 ++ linux-user/s390x/vdso.S | 2 ++ linux-user/s390x/vdso.so | Bin 3464 -> 3616 bytes 22 files changed, 16 insertions(+) diff --git a/linux-user/aarch64/vdso-be.so b/linux-user/aarch64/vdso-be.so index d43c3b19cdf6588757f2039f2308a8bce21aed9c..4089838b30535738e89252a146a039af54ed8be1 100755 GIT binary patch delta 372 zcmbOs`9pGoKI4y#1`&)RyJD|o>HWC-e5%H5Ca1P4-f+Z z%LfPzCN+U1O#I8_0yb-3J4S~8A0a9j7eHt*=?)~hAbgezB=IPOdS(wK@d81hITIU& z>uZ5_!>qUgRUiPQCnJf!fr@hg>6J+0JCM}xM-o2)7f(($@JUXqDAdbmP)T7>sbDD1 zOfO0;DJ{y2FG@|%%+HgKFD^+eD!~wsPt8l&T*;oqIB^2Y=Gz=9%$y5=hVlWi_+$;zG-qO?aDAW<0|O6K;|-_+0U(`@B>n~} z&H%?iU*Gy7?J< qAme6hP8DX(0-!EFAQqjR!K2Ta0X1XQSxE{t}Xmv61yy2m1;okeN+zms!1<2TP?yu+d>!~g<33?Ep) z3^<_)X7Mn5nViXHFKGwlF*1Dozkm_00L*Zoe2`6CG72i|!Hg{!IA<+TPzY$l zjmeJe;*)PM2{5jl%*ZUxxPNjWyOdRaibY7VlU_c9N(zHY1w(OWdQoagX;EH$QEGZ- zeqMZWNn%k6rf_Or%H~S;c*cnnST^6_P+;a=zyz_I4@ikmX64c5oWTwexiSxrfSl257}AgzHD2Uk@GeFM#i?fJDcY)-eH-%g3Uluh=GBD zk>TV21&lxj5|Ez!kxg7u4=4hX^dyyqIfNkk=K=YtKt30gzX`~XM({ltP67o2;R4CYRh3x*p%!}i3@RlI z#hK|vsU@XFdGST5>6!U?@x>*HMOcJW^HMgKu{APtUH}@z55y9axTIM-fQ);S6}Xf| O(}1igKnw$nAO-;P2tZ~4 delta 232 zcmZ1?dP8J_GUJAcDhEV9iF@2Qn5bfLZGwFE!TAAV%KxoCZv1hKW%4~%*?J)c28JI% z%)rR-|09r+W?*1x1kwsj3=BL#TAP7^2`mp}a{;j(P~{CE4O1TiVuOGnM1LGme?E}Q z0W=t-zX-@r1=3tlejAVdyyqIfNkk=K=YtKt30gzX`~XM({ltP67o2;R4CYsV*V8W`=tC3@RlI z#hK|vsU@XFdGST5>6!U?@x>*HMOcJW^HMgKu{APtUH}@z55y9axTIM-fQ);S6}Xf| O(}1igKnw$nAO-;BYCz-w delta 232 zcmZ1?dP8J_GUJAcDhEW2jvP6cAn`ta_SEXT7Yg+lW8zuPZ2WPIW%4~%*?J)c28JI% z%)rR-|09r+W?*1x1kwsj3=BL#TAP7^2`mp}a{;j(P~{CE4O1TiVuOGnM1LGme?E}Q z0W=t-zX-@r1=3tlejAV+%m4rY diff --git a/linux-user/arm/vdso-le.so b/linux-user/arm/vdso-le.so index 38d3d51047372391b3125c3f9f6ea5401f04bba1..d34e577b69e6cb22a90982570d5e66a1fa22e195 100755 GIT binary patch delta 309 zcmca1vP5)(0^^^Fiibto-btLS^tfAl`qJz2U8lY?Z_Q&mz46Bp7TE?C1}NZRPy@0S zAn~my?_`y)w*!j)fU03+`1s!)$ZiCxQUKCS3_J|sK>7z0kN^WNhAbd`gAu|5sc(QV z!K5IN-vwml1NCzNX(6Egc|bZ9$mfFcHv#Er1mBzCBv2p_A;7?poNQne6;a`(m(QS5 z!cd%iibs{A2M=DJTKY)!fEEqs*sO2HQxVB+W6xL%j7$(GW9|X3=BVj z7z7v@KK_>m(v2((3<^M+iGhbf8%Y0P0uo@r#b5`dZ!ki5AoU>-CYTfi^5cN4e4u^~ zAT0#cUj(F6fqX6~zYR!7Blz+RbAbYZ2muC$X+$tBI2!N$O_Y4Sua;mI{z0*q3Vd%45`Chsqh diff --git a/linux-user/arm/vdso.S b/linux-user/arm/vdso.S index b3bb6491dc2eb..d84d964730803 100644 --- a/linux-user/arm/vdso.S +++ b/linux-user/arm/vdso.S @@ -140,6 +140,7 @@ SYSCALL __vdso_gettimeofday, __NR_gettimeofday .balign 16 sigreturn_codes: +sigreturn_region_start: /* [EO]ABI sigreturn */ slot 0 raw_syscall __NR_sigreturn @@ -172,3 +173,4 @@ sigreturn_codes: .balign 16 endf sigreturn_codes +sigreturn_region_end: diff --git a/linux-user/hppa/vdso.S b/linux-user/hppa/vdso.S index 5be14d2f7009a..a6f8da2981ef9 100644 --- a/linux-user/hppa/vdso.S +++ b/linux-user/hppa/vdso.S @@ -156,8 +156,10 @@ __kernel_sigtramp_rt: ldi 0, %r25 ldi __NR_rt_sigreturn, %r20 +sigreturn_region_start: be,l 0x100(%sr2, %r0), %sr0, %r31 nop +sigreturn_region_end: .cfi_endproc .size __kernel_sigtramp_rt, . - __kernel_sigtramp_rt diff --git a/linux-user/hppa/vdso.so b/linux-user/hppa/vdso.so index e1ddd70c37e9eb6871c21d538dcce0a1e736a918..68baf80fd3943cc2d847744f13637810788fafc1 100755 GIT binary patch delta 287 zcmdlXut9Kw5)+%jM3n;~wM-GFSH2~uzvW>xy0IuFLc1|}=f)rNnd=!qfQ8}zMD-eV*Sd?APT7Vb^7(omG-ZjNUA!li diff --git a/linux-user/i386/vdso.S b/linux-user/i386/vdso.S index e7a1f333a1ee6..8df77b5a948a3 100644 --- a/linux-user/i386/vdso.S +++ b/linux-user/i386/vdso.S @@ -114,6 +114,7 @@ vdso_syscall3 __vdso_getcpu, __NR_gettimeofday */ nop +sigreturn_region_start: __kernel_sigreturn: popl %eax /* pop sig */ .cfi_adjust_cfa_offset -4 @@ -128,6 +129,7 @@ __kernel_rt_sigreturn: movl $__NR_rt_sigreturn, %eax int $0x80 endf __kernel_rt_sigreturn +sigreturn_region_end: .cfi_endproc diff --git a/linux-user/i386/vdso.so b/linux-user/i386/vdso.so index bdece5dfcf8da036d013262d4e37208350283cfa..e01c3818d0d2e4871cf39b2ddd21043d0695d07e 100755 GIT binary patch delta 444 zcmew$@=kzJ5{@jp{@LTJl;I7NjX(ad)H6T<3&Y3%JV5q2 zR+tDkgEEkL4~Y+x0ns4!7C`z3RFIK@iNO)b=3s#eFmN)2z?eW<5U4&5NQVH;V+PVf z4BQL_3=H)QlYs&vP=OX8y&cFGgYqWg9x-u{<2MP#41sIZ(!&1}2ybbm88HzL0i&9HUi}K=&Qqwc@ z^Wuw35{pVOg;VoVHXmSXVBBoaah6d_fsuiM7ib#DiJ+hWnal~qAV0$aP*RX_vKg0> RgdorOeY#m4Sg7NDDD=Ggt!Y$w0mclYp!s=SQWathS7noo`vD#e+D$b z1LpHE)UcsSC_}{?(8NK)*vzp2iZcSuZ(sy#gcFW1aW;m@x7m3mqroy<3?EqFO2CY~ z$&4K0j4hJ|nZ+4rOfCeH%O*Pl$z78ZIm9K;0o4dG@Gt~GT?2771HsGt-MwOG=CK;)_z#GxPJ}i%SxVN-%{}^HMgi;Amjntj?v#%-g{PaSR`j a5}h2%tIf&52@!cWxs%tN@!RBsyy^hfi%Eb0 delta 205 zcmZpWeIY$TgXsn5MlCx=5mA9%)#h=o$th|*U-GxV_f~Om__Vo((Sc2rk>TV221W)H zz%|*BLtIh?EXKv~fdxg9!F+Nehd86}WI<+e#>B~mKr(-_Bam#Kd=RK>I#96?0}n$0 z8$=fmRP&n2A2}p8J8;%BZa%=Jz|5<`1d-qaQi7A;@@jLg;DCs1pRCAd&Kb`M;k8Uo Hjenet|GUJhnDhEWA#S8BnzBO%iakv;8rJgEnt>YiE@dr0sJp%}^F#P`rq<9z@ z*d)LLU^as?kgtQphce;nEr0?)K!S`63`{`Ek%58v4Tui}TtFNG6xIN7fbuLrDhtF0 z0YQj;iaYlTCGNaE#l>;J~u3G}Hz1nkb!uCykg)t8^X6|Be-uQ!?t^NlC5HK<@Faaqp z1_tIgAU+TP*$lEkVGR%mD9-|j@-*l|TgoKt2mp!+s!N0?B}DKnp+)h8ge#$iD*PZ+7Nb o&A9nJ=NCrK1wb)AAQqVXhFgk_{S1)#domA?5);Sq$p$DOEI@iQk~qvq96<3?5b;=$QuZw%5(q#R-$2qZ z0W1m#Eh#O^i!Vw|&&jGKjdRG2w6fMR?=%s<(I YUyqCP2UN+9$rb!&Ok5i#Z{Sw|0MvRWxBvhE diff --git a/linux-user/ppc/vdso-64le.so b/linux-user/ppc/vdso-64le.so index 424abb4290b7d3100e9dede2f3059483608ba703..22499d2701530ee2aa003ae5fecce695441a5f77 100755 GIT binary patch delta 383 zcmdlXcR_xF2IGZ|n)Zw$d#8TmX85vf$%g!(v+1T^wTi0NT-{v5IDx&Mh2i6W1~k9} z=JPO|U`LfuhKgT669)-nGsglb&d9*T(7*`R2qzrDEG~u@EDV#iIe8^RfP6tL?ns>c zkx5*#5GX6az{4;HYAeKb3=Hj)6`92)=R#!um3a93!Z2rO7z__`bTalT! kg9&0bACMBByp&IyGlB~u@_zD7K66fYZV0bsvLvrO0GX9Yi2wiq delta 220 zcmca0ze8?<2BXDBO?yU>es=TCa!hWME=wU}Qi6 zT$39)#U*8dVuB1G|D#9&Ia-rHGKouC0a*eJJPdQ#A@V#>WdV~FnZ+g3pt1*`vMf;9 zn#qD(;*66gA7qlA9KtNRc>-5G<7NRK1!i6iCWsO~AjLn~mS3Cm2PZ^i$K*nObIuK1 L5MImVjlA*zt)(a1 diff --git a/linux-user/ppc/vdso.S b/linux-user/ppc/vdso.S index 2e79ea98086a1..e9256a2deafc9 100644 --- a/linux-user/ppc/vdso.S +++ b/linux-user/ppc/vdso.S @@ -220,6 +220,7 @@ endf __kernel_sync_dicache nop +sigreturn_region_start: __kernel_sigtramp_rt: raw_syscall __NR_rt_sigreturn endf __kernel_sigtramp_rt @@ -235,5 +236,6 @@ __kernel_sigtramp32: raw_syscall __NR_sigreturn endf __kernel_sigtramp32 #endif +sigreturn_region_end: .cfi_endproc diff --git a/linux-user/riscv/vdso-32.so b/linux-user/riscv/vdso-32.so index c2ce2a4757900a16b891bb98f7a027ac30c47a5f..4818a994f0394d83ca5e8213f6e2d2d3ee2f504c 100755 GIT binary patch delta 447 zcmZ1?zC~h!0#gs?L?tIipNXmSMas5*X}YtK&*i=K5rz{7_FXjfZHe71!C1(`Ah&Y5~ zVfgsJ6v&idgzy=G{5l|W0#rSSKLg00!3>dSVBlg{4P-hnLHHo~gD@!|Ey(b({{I;u zqXcLG2apzG;9+B(DYJ2O#kol9OHh zi$k)E^zs>0DvHXCjWUf24C8Z+3Jl^CjV7;TRWyZ%3PW*bdQoagX;EH$QEGZ-eqMZW zNn%k6rf_Or%H~<@U5uNRIKMG6`b?h4Ej(F=TY&cp(BcO`2k-+OD?ZtiTbgYPI|G9~ P;G_8jvml>gNE`LJT|%RY3X&3j;$0l-~!WGl23u5WYRb z93Z0sNy7#pKL(+I;Rui)fW&7=PEO1*3&{4=%V$uj*sQ_P#khG3=NCps=gEpZ!jt!K m3-F!+Dn9`96Uc9(lW%fMv-YqvFx&*o^Y92TrcRdS5eESNR6iB~ diff --git a/linux-user/riscv/vdso-64.so b/linux-user/riscv/vdso-64.so index ae49f5b043b5941b9d304a056c2b50c185f413b0..cc6f7e974be67bd08ed412eccb9af22138385e56 100755 GIT binary patch delta 453 zcmaDM*P$>$gYm>f&3s0mjWgL9MZUOZ%%7)STEdup@5PH(j}Av>WiW2u!l=l~mu)4hczqpe!T9$NxJRfea)dF`1E5T+#?E z!o{G#iX;VOx=uC(%0>fOfBdakV2*m2=QkG9)Kwn!34I1nK27s8noD;abGFnS)1x8RF~7HvB5066_FD m7cfC+kat8Um-1_K?%;%o+ysTtULZ3;kBLiRqk#pZh})fO-I~!HOHa>f*qfa!daD0cMEm9%#s{n{EDZlYPHYr5 z0V*y5r1uCyyqa;M|Y2gD+ifAHvYu7Dc+d9neoIp=IBuVr!pw*&xqqDfc) delta 305 zcmZ1=(;+=UkBPHmqk#pZ$eP<0^W+mAZmVidw^)#U>fWD6>!)n4VSK<^&&crqBLf&P zHb7`F$ps`q7zkKiKxi;2i%`$(fFy1##K6D<;j_VnWq=H4ggEOHs02tn5-P3>WV6XZ zC76J8K9YtFP;m($-6{xl=)^{0-AM=qY#@_B0Hkp#LY(ykSd@W5X7hcHX2#9QTq?|* d8bC2VAQqTBfmff?1FB-r Date: Thu, 4 Sep 2025 12:11:45 +0200 Subject: [PATCH 0809/1794] linux-user: Populate vdso_sigreturn_region_{start,end} from sigtramp page When a target does not support a vdso, we generate a sigtramp page. The only thing on this page is a (set of) signal return syscalls. We do not need to narrowly restrict the vdso_sigreturn_region; simply record the entire page for all such targets. Signed-off-by: Richard Henderson --- linux-user/elfload.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 28f0909d1a41c..1370ec59be4df 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1974,6 +1974,8 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) setup_sigtramp(tramp_page); target_mprotect(tramp_page, TARGET_PAGE_SIZE, PROT_READ | PROT_EXEC); + vdso_sigreturn_region_start = tramp_page; + vdso_sigreturn_region_end = tramp_page + TARGET_PAGE_SIZE; } bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &ehdr, info, From c8e5aed246914ff6438839350e414a3bea5a8041 Mon Sep 17 00:00:00 2001 From: Arusekk Date: Fri, 11 Jul 2025 18:48:29 +0200 Subject: [PATCH 0810/1794] linux-user: Add syscall dispatch support This commit adds support for the `prctl(PR_SET_SYSCALL_USER_DISPATCH)` function in the Linux userspace emulator. It is implemented as a fully host-independent function, by forcing a SIGSYS early during syscall handling, if the PC is outside the allowed range. Since disabled SUD is indistinguishable from enabled SUD with always-allowed region length == ~0, this encoding is used instead of introducing a new flag. Tested on [uglendix][1], will probably also apply to software like tiny-wine, rpcsx, limbo, lazypoline, vicar, sysfail and endokernel, to name a few. [1]: https://sr.ht/~arusekk/uglendix Signed-off-by: Arusekk Message-ID: <20250711225226.14652-1-floss@arusekk.pl> [rth: Split out is_vdso_sigreturn region matching and other minor tweaks.] Signed-off-by: Richard Henderson --- linux-user/main.c | 2 + linux-user/qemu.h | 5 +++ linux-user/signal-common.h | 5 +++ linux-user/syscall.c | 76 +++++++++++++++++++++++++++++++++++++- linux-user/syscall_defs.h | 6 +++ 5 files changed, 93 insertions(+), 1 deletion(-) diff --git a/linux-user/main.c b/linux-user/main.c index 4ddfc9a619cbb..db751c07576f2 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -228,6 +228,8 @@ void init_task_state(TaskState *ts) ts->start_boottime += bt.tv_nsec * (uint64_t) ticks_per_sec / NANOSECONDS_PER_SECOND; } + + ts->sys_dispatch_len = -1; } CPUArchState *cpu_copy(CPUArchState *env) diff --git a/linux-user/qemu.h b/linux-user/qemu.h index e4dca0c20f082..cabb7bd6a8bad 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -155,6 +155,11 @@ struct TaskState { /* This thread's sigaltstack, if it has one */ struct target_sigaltstack sigaltstack_used; + /* This thread's SYSCALL_USER_DISPATCH state, len=~0 means disabled */ + vaddr sys_dispatch; + vaddr sys_dispatch_selector; + abi_ulong sys_dispatch_len; + /* Start time of task after system boot in clock ticks */ uint64_t start_boottime; }; diff --git a/linux-user/signal-common.h b/linux-user/signal-common.h index 0b04868727ed6..8a44714251add 100644 --- a/linux-user/signal-common.h +++ b/linux-user/signal-common.h @@ -28,6 +28,11 @@ extern abi_ulong default_rt_sigreturn; extern abi_ulong vdso_sigreturn_region_start; extern abi_ulong vdso_sigreturn_region_end; +static inline bool is_vdso_sigreturn(abi_ulong pc) +{ + return pc >= vdso_sigreturn_region_start && pc < vdso_sigreturn_region_end; +} + void setup_sigtramp(abi_ulong tramp_page); int on_sig_stack(unsigned long sp); diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 91360a072c7f4..9098cdb9faad8 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6344,6 +6344,10 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) #endif #ifndef PR_SET_SYSCALL_USER_DISPATCH # define PR_SET_SYSCALL_USER_DISPATCH 59 +# define PR_SYS_DISPATCH_OFF 0 +# define PR_SYS_DISPATCH_ON 1 +# define SYSCALL_DISPATCH_FILTER_ALLOW 0 +# define SYSCALL_DISPATCH_FILTER_BLOCK 1 #endif #ifndef PR_SME_SET_VL # define PR_SME_SET_VL 63 @@ -6398,6 +6402,36 @@ static abi_long do_prctl_inval1(CPUArchState *env, abi_long arg2) #define do_prctl_sme_set_vl do_prctl_inval1 #endif +static abi_long do_prctl_syscall_user_dispatch(CPUArchState *env, + abi_ulong arg2, abi_ulong arg3, + abi_ulong arg4, abi_ulong arg5) +{ + CPUState *cpu = env_cpu(env); + TaskState *ts = get_task_state(cpu); + + switch (arg2) { + case PR_SYS_DISPATCH_OFF: + if (arg3 || arg4 || arg5) { + return -TARGET_EINVAL; + } + ts->sys_dispatch_len = -1; + return 0; + case PR_SYS_DISPATCH_ON: + if (arg3 && arg3 + arg4 <= arg3) { + return -TARGET_EINVAL; + } + if (arg5 && !access_ok(cpu, VERIFY_READ, arg5, 1)) { + return -TARGET_EFAULT; + } + ts->sys_dispatch = arg3; + ts->sys_dispatch_len = arg4; + ts->sys_dispatch_selector = arg5; + return 0; + default: + return -TARGET_EINVAL; + } +} + static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5) { @@ -6473,6 +6507,9 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2, case PR_SET_UNALIGN: return do_prctl_set_unalign(env, arg2); + case PR_SET_SYSCALL_USER_DISPATCH: + return do_prctl_syscall_user_dispatch(env, arg2, arg3, arg4, arg5); + case PR_CAP_AMBIENT: case PR_CAPBSET_READ: case PR_CAPBSET_DROP: @@ -6527,7 +6564,6 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2, case PR_SET_MM: case PR_GET_SECCOMP: case PR_SET_SECCOMP: - case PR_SET_SYSCALL_USER_DISPATCH: case PR_GET_THP_DISABLE: case PR_SET_THP_DISABLE: case PR_GET_TSC: @@ -13897,12 +13933,46 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return ret; } +static bool sys_dispatch(CPUState *cpu, TaskState *ts) +{ + abi_ptr pc; + + if (likely(ts->sys_dispatch_len == -1)) { + return false; + } + + pc = cpu->cc->get_pc(cpu); + if (likely(pc - ts->sys_dispatch < ts->sys_dispatch_len)) { + return false; + } + if (unlikely(is_vdso_sigreturn(pc))) { + return false; + } + if (likely(ts->sys_dispatch_selector)) { + uint8_t sb; + if (get_user_u8(sb, ts->sys_dispatch_selector)) { + force_sig(TARGET_SIGSEGV); + return true; + } + if (likely(sb == SYSCALL_DISPATCH_FILTER_ALLOW)) { + return false; + } + if (unlikely(sb != SYSCALL_DISPATCH_FILTER_BLOCK)) { + force_sig(TARGET_SIGSYS); + return true; + } + } + force_sig_fault(TARGET_SIGSYS, TARGET_SYS_USER_DISPATCH, pc); + return true; +} + abi_long do_syscall(CPUArchState *cpu_env, int num, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6, abi_long arg7, abi_long arg8) { CPUState *cpu = env_cpu(cpu_env); + TaskState *ts = get_task_state(cpu); abi_long ret; #ifdef DEBUG_ERESTARTSYS @@ -13919,6 +13989,10 @@ abi_long do_syscall(CPUArchState *cpu_env, int num, abi_long arg1, } #endif + if (sys_dispatch(cpu, ts)) { + return -QEMU_ESIGRETURN; + } + record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index df26a2d28f255..cd9ff709b876b 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -689,6 +689,12 @@ typedef struct target_siginfo { #define TARGET_TRAP_HWBKPT (4) /* hardware breakpoint/watchpoint */ #define TARGET_TRAP_UNK (5) /* undiagnosed trap */ +/* + * SIGSYS si_codes + */ +#define TARGET_SYS_SECCOMP (1) /* seccomp triggered */ +#define TARGET_SYS_USER_DISPATCH (2) /* syscall user dispatch triggered */ + /* * SIGEMT si_codes */ From b7cd0a1821adf9906c5edb248394bb2a95482656 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Sep 2025 11:30:19 +0200 Subject: [PATCH 0811/1794] target/sparc: Allow TRANS macro with no extra arguments Use ## to drop the preceding comma if __VA_ARGS__ is empty. Reviewed-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index b922e53bf1200..336583beaba96 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2487,7 +2487,7 @@ static int extract_qfpreg(DisasContext *dc, int x) #define TRANS(NAME, AVAIL, FUNC, ...) \ static bool trans_##NAME(DisasContext *dc, arg_##NAME *a) \ - { return avail_##AVAIL(dc) && FUNC(dc, __VA_ARGS__); } + { return avail_##AVAIL(dc) && FUNC(dc, ## __VA_ARGS__); } #define avail_ALL(C) true #ifdef TARGET_SPARC64 From b6cdd6c6050567c02a3b3cd428f85bb79d7455aa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Sep 2025 06:59:43 +0200 Subject: [PATCH 0812/1794] target/sparc: Loosen decode of STBAR for v8 Solaris 8 appears to have a bug whereby it executes v9 MEMBAR instructions when booting a freshly installed image. According to the SPARC v8 architecture manual, whilst bits 13 and bits 12-0 of the "Read State Register Instructions" are notionally zero, they are marked as unused (i.e. ignored). Fixes: af25071c1d ("target/sparc: Move RDASR, STBAR, MEMBAR to decodetree") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3097 Signed-off-by: Richard Henderson Reviewed-by: Mark Cave-Ayland Tested-by: Mark Cave-Ayland --- target/sparc/insns.decode | 13 ++++++++++++- target/sparc/translate.c | 5 ++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 9e39d232735e6..1b1b85e9c233e 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -88,7 +88,7 @@ CALL 01 i:s30 { [ - STBAR 10 00000 101000 01111 0 0000000000000 + STBAR_v9 10 00000 101000 01111 0 0000000000000 MEMBAR 10 00000 101000 01111 1 000000 cmask:3 mmask:4 RDCCR 10 rd:5 101000 00010 0 0000000000000 @@ -107,6 +107,17 @@ CALL 01 i:s30 RDSTICK_CMPR 10 rd:5 101000 11001 0 0000000000000 RDSTRAND_STATUS 10 rd:5 101000 11010 0 0000000000000 ] + + # The v8 manual, section B.30 STBAR instruction, says + # bits [12:0] are ignored, but bit 13 must be 0. + # However, section B.28 Read State Register Instruction has a + # comment that RDASR with rs1 = 15, rd = 0 is STBAR. Here, + # bit 13 is also ignored and rd != 0 is merely reserved. + # + # Solaris 8 executes v9 MEMBAR instruction 0x8143e008 during boot. + # This confirms that bit 13 is ignored, as 0x8143c000 is STBAR. + STBAR_v8 10 ----- 101000 01111 - ------------- + # Before v8, all rs1 accepted; otherwise rs1==0. RDY 10 rd:5 101000 rs1:5 0 0000000000000 } diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 336583beaba96..ece393fffc18f 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2823,12 +2823,15 @@ static bool trans_Tcc_i_v9(DisasContext *dc, arg_Tcc_i_v9 *a) return do_tcc(dc, a->cond, a->cc, a->rs1, true, a->i); } -static bool trans_STBAR(DisasContext *dc, arg_STBAR *a) +static bool do_stbar(DisasContext *dc) { tcg_gen_mb(TCG_MO_ST_ST | TCG_BAR_SC); return advance_pc(dc); } +TRANS(STBAR_v8, 32, do_stbar) +TRANS(STBAR_v9, 64, do_stbar) + static bool trans_MEMBAR(DisasContext *dc, arg_MEMBAR *a) { if (avail_32(dc)) { From 49d669ccf33a772e3baf3fe4ebb996dc015f46c1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Sep 2025 07:15:57 +0200 Subject: [PATCH 0813/1794] target/sparc: Loosen decode of RDY for v7 Bits [18:0] are not decoded with v7, and for v8 unused values of rs1 simply produce undefined results. Fixes: af25071c1d ("target/sparc: Move RDASR, STBAR, MEMBAR to decodetree") Signed-off-by: Richard Henderson Reviewed-by: Mark Cave-Ayland Tested-by: Mark Cave-Ayland --- target/sparc/insns.decode | 12 ++++++++++-- target/sparc/translate.c | 14 ++------------ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 1b1b85e9c233e..74848996aedc2 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -91,6 +91,7 @@ CALL 01 i:s30 STBAR_v9 10 00000 101000 01111 0 0000000000000 MEMBAR 10 00000 101000 01111 1 000000 cmask:3 mmask:4 + RDY_v9 10 rd:5 101000 00000 0 0000000000000 RDCCR 10 rd:5 101000 00010 0 0000000000000 RDASI 10 rd:5 101000 00011 0 0000000000000 RDTICK 10 rd:5 101000 00100 0 0000000000000 @@ -118,8 +119,15 @@ CALL 01 i:s30 # This confirms that bit 13 is ignored, as 0x8143c000 is STBAR. STBAR_v8 10 ----- 101000 01111 - ------------- - # Before v8, all rs1 accepted; otherwise rs1==0. - RDY 10 rd:5 101000 rs1:5 0 0000000000000 + # For v7, bits [18:0] are ignored. + # For v8, bits [18:14], aka rs1, are repurposed and rs1 = 0 is RDY, + # and other values are RDASR. However, the v8 manual explicitly + # says that rs1 in 1..14 yield undefined results and do not cause + # an illegal instruction trap, and rs1 in 16..31 are available for + # implementation specific usage. + # Implement not causing an illegal instruction trap for v8 by + # continuing to interpret unused values per v7, i.e. as RDY. + RDY_v7 10 rd:5 101000 ----- - ------------- } { diff --git a/target/sparc/translate.c b/target/sparc/translate.c index ece393fffc18f..cfdd9c1ce41e1 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2863,18 +2863,8 @@ static TCGv do_rdy(DisasContext *dc, TCGv dst) return cpu_y; } -static bool trans_RDY(DisasContext *dc, arg_RDY *a) -{ - /* - * TODO: Need a feature bit for sparcv8. In the meantime, treat all - * 32-bit cpus like sparcv7, which ignores the rs1 field. - * This matches after all other ASR, so Leon3 Asr17 is handled first. - */ - if (avail_64(dc) && a->rs1 != 0) { - return false; - } - return do_rd_special(dc, true, a->rd, do_rdy); -} +TRANS(RDY_v7, 32, do_rd_special, true, a->rd, do_rdy) +TRANS(RDY_v9, 64, do_rd_special, true, a->rd, do_rdy) static TCGv do_rd_leon3_config(DisasContext *dc, TCGv dst) { From a0345f628394fbd001276c80fd02c8ad0d1b7ee2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Sep 2025 11:34:52 +0200 Subject: [PATCH 0814/1794] target/sparc: Loosen decode of RDPSR for v7 For v7, bits [18:0] are ignored. For v8, bits [18:14] are reserved and bits [13:0] are ignored. Fixes: 668bb9b755e ("target/sparc: Move RDPSR, RDHPR to decodetree") Reviewed-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 74848996aedc2..1c6403ad8a647 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -158,14 +158,16 @@ CALL 01 i:s30 } { - RDPSR 10 rd:5 101001 00000 0 0000000000000 - RDHPR_hpstate 10 rd:5 101001 00000 0 0000000000000 + [ + RDHPR_hpstate 10 rd:5 101001 00000 0 0000000000000 + RDHPR_htstate 10 rd:5 101001 00001 0 0000000000000 + RDHPR_hintp 10 rd:5 101001 00011 0 0000000000000 + RDHPR_htba 10 rd:5 101001 00101 0 0000000000000 + RDHPR_hver 10 rd:5 101001 00110 0 0000000000000 + RDHPR_hstick_cmpr 10 rd:5 101001 11111 0 0000000000000 + ] + RDPSR 10 rd:5 101001 ----- - ------------- } -RDHPR_htstate 10 rd:5 101001 00001 0 0000000000000 -RDHPR_hintp 10 rd:5 101001 00011 0 0000000000000 -RDHPR_htba 10 rd:5 101001 00101 0 0000000000000 -RDHPR_hver 10 rd:5 101001 00110 0 0000000000000 -RDHPR_hstick_cmpr 10 rd:5 101001 11111 0 0000000000000 { WRPSR 10 00000 110001 ..... . ............. @n_r_ri From dc9678cc9725d6c3053c6f110f162d956eb9d48f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Sep 2025 11:39:41 +0200 Subject: [PATCH 0815/1794] target/sparc: Loosen decode of RDWIM for v7 For v7, bits [18:0] are ignored. For v8, bits [18:14] are reserved and bits [13:0] are ignored. Fixes: 5d617bfba07 ("target/sparc: Move RDWIM, RDPR to decodetree") Reviewed-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 40 ++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 1c6403ad8a647..77b2f54fdf92f 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -180,26 +180,28 @@ RESTORED 10 00001 110001 00000 0 0000000000000 # UA2005 INVALW { - RDWIM 10 rd:5 101010 00000 0 0000000000000 - RDPR_tpc 10 rd:5 101010 00000 0 0000000000000 + [ + RDPR_tpc 10 rd:5 101010 00000 0 0000000000000 + RDPR_tnpc 10 rd:5 101010 00001 0 0000000000000 + RDPR_tstate 10 rd:5 101010 00010 0 0000000000000 + RDPR_tt 10 rd:5 101010 00011 0 0000000000000 + RDPR_tick 10 rd:5 101010 00100 0 0000000000000 + RDPR_tba 10 rd:5 101010 00101 0 0000000000000 + RDPR_pstate 10 rd:5 101010 00110 0 0000000000000 + RDPR_tl 10 rd:5 101010 00111 0 0000000000000 + RDPR_pil 10 rd:5 101010 01000 0 0000000000000 + RDPR_cwp 10 rd:5 101010 01001 0 0000000000000 + RDPR_cansave 10 rd:5 101010 01010 0 0000000000000 + RDPR_canrestore 10 rd:5 101010 01011 0 0000000000000 + RDPR_cleanwin 10 rd:5 101010 01100 0 0000000000000 + RDPR_otherwin 10 rd:5 101010 01101 0 0000000000000 + RDPR_wstate 10 rd:5 101010 01110 0 0000000000000 + RDPR_gl 10 rd:5 101010 10000 0 0000000000000 + RDPR_strand_status 10 rd:5 101010 11010 0 0000000000000 + RDPR_ver 10 rd:5 101010 11111 0 0000000000000 + ] + RDWIM 10 rd:5 101010 ----- - ------------- } -RDPR_tnpc 10 rd:5 101010 00001 0 0000000000000 -RDPR_tstate 10 rd:5 101010 00010 0 0000000000000 -RDPR_tt 10 rd:5 101010 00011 0 0000000000000 -RDPR_tick 10 rd:5 101010 00100 0 0000000000000 -RDPR_tba 10 rd:5 101010 00101 0 0000000000000 -RDPR_pstate 10 rd:5 101010 00110 0 0000000000000 -RDPR_tl 10 rd:5 101010 00111 0 0000000000000 -RDPR_pil 10 rd:5 101010 01000 0 0000000000000 -RDPR_cwp 10 rd:5 101010 01001 0 0000000000000 -RDPR_cansave 10 rd:5 101010 01010 0 0000000000000 -RDPR_canrestore 10 rd:5 101010 01011 0 0000000000000 -RDPR_cleanwin 10 rd:5 101010 01100 0 0000000000000 -RDPR_otherwin 10 rd:5 101010 01101 0 0000000000000 -RDPR_wstate 10 rd:5 101010 01110 0 0000000000000 -RDPR_gl 10 rd:5 101010 10000 0 0000000000000 -RDPR_strand_status 10 rd:5 101010 11010 0 0000000000000 -RDPR_ver 10 rd:5 101010 11111 0 0000000000000 { WRWIM 10 00000 110010 ..... . ............. @n_r_ri From 6ff52f9dee064d3c2138426834320f5877863d9b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Sep 2025 11:41:46 +0200 Subject: [PATCH 0816/1794] target/sparc: Loosen decode of RDTBR for v7 For v7, bits [18:0] are ignored. For v8, bits [18:14] are reserved and bits [13:0] are ignored. Fixes: e8325dc02d0 ("target/sparc: Move RDTBR, FLUSHW to decodetree") Reviewed-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 77b2f54fdf92f..242ec420161fc 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -226,7 +226,7 @@ WRPR_strand_status 10 11010 110010 ..... . ............. @n_r_ri { FLUSHW 10 00000 101011 00000 0 0000000000000 - RDTBR 10 rd:5 101011 00000 0 0000000000000 + RDTBR 10 rd:5 101011 ----- - ------------- } { From df663ac0a4e5d916b6b3e77552a925fec02bced4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Sep 2025 13:46:27 +0200 Subject: [PATCH 0817/1794] target/sparc: Relax decode of rs2_or_imm for v7 For v7, bits [12:5] are ignored for !imm. For v8, those same bits are reserved, but are not trapped. Reviewed-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 56 ++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index cfdd9c1ce41e1..810e2491a61d7 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2526,6 +2526,32 @@ static int extract_qfpreg(DisasContext *dc, int x) # define avail_VIS4(C) false #endif +/* + * We decoded bit 13 as imm, and bits [12:0] as rs2_or_imm. + * For v9, if !imm, then the unused bits [12:5] must be zero. + * For v7 and v8, the unused bits are ignored; clear them here. + */ +static bool check_rs2(DisasContext *dc, int *rs2) +{ + if (unlikely(*rs2 & ~0x1f)) { + if (avail_64(dc)) { + return false; + } + *rs2 &= 0x1f; + } + return true; +} + +static bool check_r_r_ri(DisasContext *dc, arg_r_r_ri *a) +{ + return a->imm || check_rs2(dc, &a->rs2_or_imm); +} + +static bool check_r_r_ri_cc(DisasContext *dc, arg_r_r_ri_cc *a) +{ + return a->imm || check_rs2(dc, &a->rs2_or_imm); +} + /* Default case for non jump instructions. */ static bool advance_pc(DisasContext *dc) { @@ -3249,8 +3275,7 @@ static bool do_wr_special(DisasContext *dc, arg_r_r_ri *a, bool priv, { TCGv src; - /* For simplicity, we under-decoded the rs2 form. */ - if (!a->imm && (a->rs2_or_imm & ~0x1f)) { + if (!check_r_r_ri(dc, a)) { return false; } if (!priv) { @@ -3693,8 +3718,7 @@ static bool do_arith_int(DisasContext *dc, arg_r_r_ri_cc *a, { TCGv dst, src1; - /* For simplicity, we under-decoded the rs2 form. */ - if (!a->imm && a->rs2_or_imm & ~0x1f) { + if (!check_r_r_ri_cc(dc, a)) { return false; } @@ -3778,11 +3802,11 @@ static bool trans_OR(DisasContext *dc, arg_r_r_ri_cc *a) { /* OR with %g0 is the canonical alias for MOV. */ if (!a->cc && a->rs1 == 0) { + if (!check_r_r_ri_cc(dc, a)) { + return false; + } if (a->imm || a->rs2_or_imm == 0) { gen_store_gpr(dc, a->rd, tcg_constant_tl(a->rs2_or_imm)); - } else if (a->rs2_or_imm & ~0x1f) { - /* For simplicity, we under-decoded the rs2 form. */ - return false; } else { gen_store_gpr(dc, a->rd, cpu_regs[a->rs2_or_imm]); } @@ -3799,8 +3823,7 @@ static bool trans_UDIV(DisasContext *dc, arg_r_r_ri *a) if (!avail_DIV(dc)) { return false; } - /* For simplicity, we under-decoded the rs2 form. */ - if (!a->imm && a->rs2_or_imm & ~0x1f) { + if (!check_r_r_ri(dc, a)) { return false; } @@ -3851,8 +3874,7 @@ static bool trans_UDIVX(DisasContext *dc, arg_r_r_ri *a) if (!avail_64(dc)) { return false; } - /* For simplicity, we under-decoded the rs2 form. */ - if (!a->imm && a->rs2_or_imm & ~0x1f) { + if (!check_r_r_ri(dc, a)) { return false; } @@ -3889,8 +3911,7 @@ static bool trans_SDIVX(DisasContext *dc, arg_r_r_ri *a) if (!avail_64(dc)) { return false; } - /* For simplicity, we under-decoded the rs2 form. */ - if (!a->imm && a->rs2_or_imm & ~0x1f) { + if (!check_r_r_ri(dc, a)) { return false; } @@ -4186,8 +4207,7 @@ TRANS(SRA_i, ALL, do_shift_i, a, false, false) static TCGv gen_rs2_or_imm(DisasContext *dc, bool imm, int rs2_or_imm) { - /* For simplicity, we under-decoded the rs2 form. */ - if (!imm && rs2_or_imm & ~0x1f) { + if (!imm && !check_rs2(dc, &rs2_or_imm)) { return NULL; } if (imm || rs2_or_imm == 0) { @@ -4250,8 +4270,7 @@ static bool do_add_special(DisasContext *dc, arg_r_r_ri *a, { TCGv src1, sum; - /* For simplicity, we under-decoded the rs2 form. */ - if (!a->imm && a->rs2_or_imm & ~0x1f) { + if (!check_r_r_ri(dc, a)) { return false; } @@ -4369,8 +4388,7 @@ static TCGv gen_ldst_addr(DisasContext *dc, int rs1, bool imm, int rs2_or_imm) { TCGv addr, tmp = NULL; - /* For simplicity, we under-decoded the rs2 form. */ - if (!imm && rs2_or_imm & ~0x1f) { + if (!imm && !check_rs2(dc, &rs2_or_imm)) { return NULL; } From e4a1b308b27cd77338b8f05d3a31e6b38eb717c7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 18 Sep 2025 12:42:58 +0100 Subject: [PATCH 0818/1794] hw/pci-host/dino: Don't call pci_register_root_bus() in init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the dino PCI host bridge device, we call pci_register_root_bus() in the device's instance_init. This is a problem for two reasons * the PCI bridge is then available to the rest of the simulation (e.g. via pci_qdev_find_device()), even though it hasn't yet been realized * we do not attempt to unregister in an instance_deinit, which means that if you go through an instance_init -> deinit lifecycle the freed memory for the host-bridge device is left on the pci_host_bridges list ASAN reports the resulting use-after-free: ==1771223==ERROR: AddressSanitizer: heap-use-after-free on address 0x527000018f80 at pc 0x5b4b9d3369b5 bp 0x7ffd01929980 sp 0x7ffd01929978 WRITE of size 8 at 0x527000018f80 thread T0 #0 0x5b4b9d3369b4 in pci_host_bus_register /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../hw/pci/pci.c:608:5 #1 0x5b4b9d321566 in pci_root_bus_internal_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../hw/pci/pci.c:677:5 #2 0x5b4b9d3215e0 in pci_root_bus_new /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../hw/pci/pci.c:706:5 #3 0x5b4b9d321fe5 in pci_register_root_bus /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../hw/pci/pci.c:751:11 #4 0x5b4b9d390521 in dino_pcihost_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../hw/pci-host/dino.c:473:16 0x527000018f80 is located 1664 bytes inside of 12384-byte region [0x527000018900,0x52700001b960) freed by thread T0 here: #0 0x5b4b9cab185a in free (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/qemu-system-hppa+0x17ad85a) (BuildId: ca496bb2e4fc750ebd289b448bad8d99c0ecd140) #1 0x5b4b9e3ee723 in object_finalize /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../qom/object.c:734:9 #2 0x5b4b9e3e69db in object_unref /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../qom/object.c:1232:9 #3 0x5b4b9ea6173c in qmp_device_list_properties /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../qom/qom-qmp-cmds.c:237:5 #4 0x5b4b9ec4e0f3 in qmp_marshal_device_list_properties /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/qapi/qapi-commands-qdev.c:65:14 previously allocated by thread T0 here: #0 0x5b4b9cab1af3 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/qemu-system-hppa+0x17adaf3) (BuildId: ca496bb2e4fc750ebd289b448bad8d99c0ecd140) #1 0x799d8270eb09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x5b4b9e3e75fc in object_new_with_type /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../qom/object.c:767:15 #3 0x5b4b9e3e7409 in object_new_with_class /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../qom/object.c:782:12 #4 0x5b4b9ea609a5 in qmp_device_list_properties /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../qom/qom-qmp-cmds.c:206:11 where we allocated one instance of the dino device, put it on the list, freed it, and then trying to allocate a second instance touches the freed memory on the pci_host_bridges list. Fix this by deferring all the setup of memory regions and registering the PCI bridge to the device's realize method. This brings it into line with almost all other PCI host bridges, which call pci_register_root_bus() in realize. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3118 Fixes: 63901b6cc4d8b4 ("dino: move PCI bus initialisation to dino_pcihost_init()") Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Tested-by: Alex Bennée Signed-off-by: Richard Henderson Message-ID: <20250918114259.1802337-2-peter.maydell@linaro.org> --- hw/pci-host/dino.c | 74 +++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 41 deletions(-) diff --git a/hw/pci-host/dino.c b/hw/pci-host/dino.c index 11b353be2eac3..924053499c164 100644 --- a/hw/pci-host/dino.c +++ b/hw/pci-host/dino.c @@ -413,43 +413,7 @@ static void dino_pcihost_reset(DeviceState *dev) static void dino_pcihost_realize(DeviceState *dev, Error **errp) { DinoState *s = DINO_PCI_HOST_BRIDGE(dev); - - /* Set up PCI view of memory: Bus master address space. */ - memory_region_init(&s->bm, OBJECT(s), "bm-dino", 4 * GiB); - memory_region_init_alias(&s->bm_ram_alias, OBJECT(s), - "bm-system", s->memory_as, 0, - 0xf0000000 + DINO_MEM_CHUNK_SIZE); - memory_region_init_alias(&s->bm_pci_alias, OBJECT(s), - "bm-pci", &s->pci_mem, - 0xf0000000 + DINO_MEM_CHUNK_SIZE, - 30 * DINO_MEM_CHUNK_SIZE); - memory_region_init_alias(&s->bm_cpu_alias, OBJECT(s), - "bm-cpu", s->memory_as, 0xfff00000, - 0xfffff); - memory_region_add_subregion(&s->bm, 0, - &s->bm_ram_alias); - memory_region_add_subregion(&s->bm, - 0xf0000000 + DINO_MEM_CHUNK_SIZE, - &s->bm_pci_alias); - memory_region_add_subregion(&s->bm, 0xfff00000, - &s->bm_cpu_alias); - - address_space_init(&s->bm_as, &s->bm, "pci-bm"); -} - -static void dino_pcihost_unrealize(DeviceState *dev) -{ - DinoState *s = DINO_PCI_HOST_BRIDGE(dev); - - address_space_destroy(&s->bm_as); -} - -static void dino_pcihost_init(Object *obj) -{ - DinoState *s = DINO_PCI_HOST_BRIDGE(obj); - PCIHostState *phb = PCI_HOST_BRIDGE(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - int i; + PCIHostState *phb = PCI_HOST_BRIDGE(dev); /* Dino PCI access from main memory. */ memory_region_init_io(&s->this_mem, OBJECT(s), &dino_chip_ops, @@ -476,7 +440,7 @@ static void dino_pcihost_init(Object *obj) PCI_DEVFN(0, 0), 32, TYPE_PCI_BUS); /* Set up windows into PCI bus memory. */ - for (i = 1; i < 31; i++) { + for (int i = 1; i < 31; i++) { uint32_t addr = 0xf0000000 + i * DINO_MEM_CHUNK_SIZE; char *name = g_strdup_printf("PCI Outbound Window %d", i); memory_region_init_alias(&s->pci_mem_alias[i], OBJECT(s), @@ -487,9 +451,38 @@ static void dino_pcihost_init(Object *obj) pci_setup_iommu(phb->bus, &dino_iommu_ops, s); - sysbus_init_mmio(sbd, &s->this_mem); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->this_mem); - qdev_init_gpio_in(DEVICE(obj), dino_set_irq, DINO_IRQS); + qdev_init_gpio_in(dev, dino_set_irq, DINO_IRQS); + + /* Set up PCI view of memory: Bus master address space. */ + memory_region_init(&s->bm, OBJECT(s), "bm-dino", 4 * GiB); + memory_region_init_alias(&s->bm_ram_alias, OBJECT(s), + "bm-system", s->memory_as, 0, + 0xf0000000 + DINO_MEM_CHUNK_SIZE); + memory_region_init_alias(&s->bm_pci_alias, OBJECT(s), + "bm-pci", &s->pci_mem, + 0xf0000000 + DINO_MEM_CHUNK_SIZE, + 30 * DINO_MEM_CHUNK_SIZE); + memory_region_init_alias(&s->bm_cpu_alias, OBJECT(s), + "bm-cpu", s->memory_as, 0xfff00000, + 0xfffff); + memory_region_add_subregion(&s->bm, 0, + &s->bm_ram_alias); + memory_region_add_subregion(&s->bm, + 0xf0000000 + DINO_MEM_CHUNK_SIZE, + &s->bm_pci_alias); + memory_region_add_subregion(&s->bm, 0xfff00000, + &s->bm_cpu_alias); + + address_space_init(&s->bm_as, &s->bm, "pci-bm"); +} + +static void dino_pcihost_unrealize(DeviceState *dev) +{ + DinoState *s = DINO_PCI_HOST_BRIDGE(dev); + + address_space_destroy(&s->bm_as); } static const Property dino_pcihost_properties[] = { @@ -511,7 +504,6 @@ static void dino_pcihost_class_init(ObjectClass *klass, const void *data) static const TypeInfo dino_pcihost_info = { .name = TYPE_DINO_PCI_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, - .instance_init = dino_pcihost_init, .instance_size = sizeof(DinoState), .class_init = dino_pcihost_class_init, }; From 76d2b8d42adb0db2d1ccd08a626f25ddd30208a8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 18 Sep 2025 12:42:59 +0100 Subject: [PATCH 0819/1794] hw/pci-host/astro: Don't call pci_regsiter_root_bus() in init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the astro PCI host bridge device, we call pci_register_root_bus() in the device's instance_init. This is a problem for two reasons * the PCI bridge is then available to the rest of the simulation (e.g. via pci_qdev_find_device()), even though it hasn't yet been realized * we do not attempt to unregister in an instance_deinit, which means that if you go through an instance_init -> deinit lifecycle the freed memory for the host-bridge device is left on the pci_host_bridges list ASAN reports the resulting use-after-free: ==1776584==ERROR: AddressSanitizer: heap-use-after-free on address 0x51f00000cb00 at pc 0x5b2d460a89b5 bp 0x7ffef7617f50 sp 0x7ffef7617f48 WRITE of size 8 at 0x51f00000cb00 thread T0 #0 0x5b2d460a89b4 in pci_host_bus_register /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../hw/pci/pci.c:608:5 #1 0x5b2d46093566 in pci_root_bus_internal_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../hw/pci/pci.c:677:5 #2 0x5b2d460935e0 in pci_root_bus_new /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../hw/pci/pci.c:706:5 #3 0x5b2d46093fe5 in pci_register_root_bus /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../hw/pci/pci.c:751:11 #4 0x5b2d46fe2335 in elroy_pcihost_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../hw/pci-host/astro.c:455:16 0x51f00000cb00 is located 1664 bytes inside of 3456-byte region [0x51f00000c480,0x51f00000d200) freed by thread T0 here: #0 0x5b2d4582385a in free (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/qemu-system-hppa+0x17ad85a) (BuildId: 692b49eedc6fb0ef618bbb6784a09311b3b7f1e8) #1 0x5b2d47160723 in object_finalize /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../qom/object.c:734:9 #2 0x5b2d471589db in object_unref /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../qom/object.c:1232:9 #3 0x5b2d477d373c in qmp_device_list_properties /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../qom/qom-qmp-cmds.c:237:5 previously allocated by thread T0 here: #0 0x5b2d45823af3 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/qemu-system-hppa+0x17adaf3) (BuildId: 692b49eedc6fb0ef618bbb6784a09311b3b7f1e8) #1 0x79728fa08b09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x5b2d471595fc in object_new_with_type /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../qom/object.c:767:15 #3 0x5b2d47159409 in object_new_with_class /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../qom/object.c:782:12 #4 0x5b2d477d29a5 in qmp_device_list_properties /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/hppa-asan/../../qom/qom-qmp-cmds.c:206:11 Cc: qemu-stable@nongnu.org Fixes: e029bb00a79be ("hw/pci-host: Add Astro system bus adapter found on PA-RISC machines") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3118 Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Tested-by: Alex Bennée Signed-off-by: Richard Henderson Message-ID: <20250918114259.1802337-3-peter.maydell@linaro.org> --- hw/pci-host/astro.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/hw/pci-host/astro.c b/hw/pci-host/astro.c index 859e308c577c5..1024ede7b68cc 100644 --- a/hw/pci-host/astro.c +++ b/hw/pci-host/astro.c @@ -424,22 +424,23 @@ static void elroy_reset(DeviceState *dev) } } -static void elroy_pcihost_init(Object *obj) +static void elroy_pcihost_realize(DeviceState *dev, Error **errp) { - ElroyState *s = ELROY_PCI_HOST_BRIDGE(obj); - PCIHostState *phb = PCI_HOST_BRIDGE(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + ElroyState *s = ELROY_PCI_HOST_BRIDGE(dev); + PCIHostState *phb = PCI_HOST_BRIDGE(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(s); /* Elroy config access from CPU. */ - memory_region_init_io(&s->this_mem, OBJECT(s), &elroy_chip_ops, + memory_region_init_io(&s->this_mem, obj, &elroy_chip_ops, s, "elroy", 0x2000); /* Elroy PCI config. */ - memory_region_init_io(&phb->conf_mem, OBJECT(phb), - &elroy_config_addr_ops, DEVICE(s), + memory_region_init_io(&phb->conf_mem, obj, + &elroy_config_addr_ops, dev, "pci-conf-idx", 8); - memory_region_init_io(&phb->data_mem, OBJECT(phb), - &elroy_config_data_ops, DEVICE(s), + memory_region_init_io(&phb->data_mem, obj, + &elroy_config_data_ops, dev, "pci-conf-data", 8); memory_region_add_subregion(&s->this_mem, 0x40, &phb->conf_mem); @@ -447,8 +448,8 @@ static void elroy_pcihost_init(Object *obj) &phb->data_mem); /* Elroy PCI bus memory. */ - memory_region_init(&s->pci_mmio, OBJECT(s), "pci-mmio", UINT64_MAX); - memory_region_init_io(&s->pci_io, OBJECT(s), &unassigned_io_ops, obj, + memory_region_init(&s->pci_mmio, obj, "pci-mmio", UINT64_MAX); + memory_region_init_io(&s->pci_io, obj, &unassigned_io_ops, obj, "pci-isa-mmio", ((uint32_t) IOS_DIST_BASE_SIZE) / ROPES_PER_IOC); @@ -459,7 +460,7 @@ static void elroy_pcihost_init(Object *obj) sysbus_init_mmio(sbd, &s->this_mem); - qdev_init_gpio_in(DEVICE(obj), elroy_set_irq, ELROY_IRQS); + qdev_init_gpio_in(dev, elroy_set_irq, ELROY_IRQS); } static const VMStateDescription vmstate_elroy = { @@ -487,6 +488,7 @@ static void elroy_pcihost_class_init(ObjectClass *klass, const void *data) DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_legacy_reset(dc, elroy_reset); + dc->realize = elroy_pcihost_realize; dc->vmsd = &vmstate_elroy; dc->user_creatable = false; } @@ -494,7 +496,6 @@ static void elroy_pcihost_class_init(ObjectClass *klass, const void *data) static const TypeInfo elroy_pcihost_info = { .name = TYPE_ELROY_PCI_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, - .instance_init = elroy_pcihost_init, .instance_size = sizeof(ElroyState), .class_init = elroy_pcihost_class_init, }; From a50347a4145faf6d409afd4b9b682c8b3e60854a Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Fri, 19 Sep 2025 20:49:01 +0800 Subject: [PATCH 0820/1794] tcg/optimize: Fix folding of vector bitsel It looks like a typo. When the false value (C) is the constant -1, the correct fold should be: R = B | ~A Reproducer (LoongArch64 assembly): .text .globl _start _start: vldi $vr1, 3073 vldi $vr2, 1023 vbitsel.v $vr0, $vr2, $vr1, $vr2 vpickve2gr.d $a1, $vr0, 1 xori $a0, $a1, 1 li.w $a7, 93 syscall 0 Fixes: e58b977238e3 ("tcg/optimize: Optimize bitsel_vec") Link: https://github.com/llvm/llvm-project/issues/159610 Signed-off-by: WANG Rui Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250919124901.2756538-1-wangrui@loongson.cn> --- tcg/optimize.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 3638ab9fea051..f69702b26e82f 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1568,9 +1568,10 @@ static bool fold_bitsel_vec(OptContext *ctx, TCGOp *op) return fold_and(ctx, op); } if (fv == -1 && TCG_TARGET_HAS_orc_vec) { + TCGArg ta = op->args[2]; op->opc = INDEX_op_orc_vec; op->args[2] = op->args[1]; - op->args[1] = op->args[3]; + op->args[1] = ta; return fold_orc(ctx, op); } } From 3014059e8faa7e4d25482710ca2c0f095441396b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 11 Jul 2025 16:56:22 -0600 Subject: [PATCH 0821/1794] include/hw/core/cpu: Introduce MMUIdxMap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a typedef instead of uint16_t directly when describing sets of mmu indexes. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 30 +++++++++++++++--------------- include/exec/cputlb.h | 32 ++++++++++++++++---------------- include/hw/core/cpu.h | 6 +++--- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 87e14bde4f225..d324f3333963e 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -370,8 +370,8 @@ static void flush_all_helper(CPUState *src, run_on_cpu_func fn, static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) { - uint16_t asked = data.host_int; - uint16_t all_dirty, work, to_clean; + MMUIdxMap asked = data.host_int; + MMUIdxMap all_dirty, work, to_clean; int64_t now = get_clock_realtime(); assert_cpu_is_self(cpu); @@ -408,7 +408,7 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) } } -void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) +void tlb_flush_by_mmuidx(CPUState *cpu, MMUIdxMap idxmap) { tlb_debug("mmu_idx: 0x%" PRIx16 "\n", idxmap); @@ -422,7 +422,7 @@ void tlb_flush(CPUState *cpu) tlb_flush_by_mmuidx(cpu, ALL_MMUIDX_BITS); } -void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, uint16_t idxmap) +void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, MMUIdxMap idxmap) { const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work; @@ -531,7 +531,7 @@ static void tlb_flush_page_locked(CPUState *cpu, int midx, vaddr page) */ static void tlb_flush_page_by_mmuidx_async_0(CPUState *cpu, vaddr addr, - uint16_t idxmap) + MMUIdxMap idxmap) { int mmu_idx; @@ -570,14 +570,14 @@ static void tlb_flush_page_by_mmuidx_async_1(CPUState *cpu, { vaddr addr_and_idxmap = data.target_ptr; vaddr addr = addr_and_idxmap & TARGET_PAGE_MASK; - uint16_t idxmap = addr_and_idxmap & ~TARGET_PAGE_MASK; + MMUIdxMap idxmap = addr_and_idxmap & ~TARGET_PAGE_MASK; tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap); } typedef struct { vaddr addr; - uint16_t idxmap; + MMUIdxMap idxmap; } TLBFlushPageByMMUIdxData; /** @@ -599,7 +599,7 @@ static void tlb_flush_page_by_mmuidx_async_2(CPUState *cpu, g_free(d); } -void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap) +void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, MMUIdxMap idxmap) { tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%" PRIx16 "\n", addr, idxmap); @@ -618,7 +618,7 @@ void tlb_flush_page(CPUState *cpu, vaddr addr) void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu, vaddr addr, - uint16_t idxmap) + MMUIdxMap idxmap) { tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%"PRIx16"\n", addr, idxmap); @@ -715,8 +715,8 @@ static void tlb_flush_range_locked(CPUState *cpu, int midx, typedef struct { vaddr addr; vaddr len; - uint16_t idxmap; - uint16_t bits; + MMUIdxMap idxmap; + unsigned bits; } TLBFlushRangeData; static void tlb_flush_range_by_mmuidx_async_0(CPUState *cpu, @@ -766,7 +766,7 @@ static void tlb_flush_range_by_mmuidx_async_1(CPUState *cpu, } void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, - vaddr len, uint16_t idxmap, + vaddr len, MMUIdxMap idxmap, unsigned bits) { TLBFlushRangeData d; @@ -797,7 +797,7 @@ void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, } void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr, - uint16_t idxmap, unsigned bits) + MMUIdxMap idxmap, unsigned bits) { tlb_flush_range_by_mmuidx(cpu, addr, TARGET_PAGE_SIZE, idxmap, bits); } @@ -805,7 +805,7 @@ void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr, void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu, vaddr addr, vaddr len, - uint16_t idxmap, + MMUIdxMap idxmap, unsigned bits) { TLBFlushRangeData d, *p; @@ -847,7 +847,7 @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu, void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu, vaddr addr, - uint16_t idxmap, + MMUIdxMap idxmap, unsigned bits) { tlb_flush_range_by_mmuidx_all_cpus_synced(src_cpu, addr, TARGET_PAGE_SIZE, diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h index 03ed7e216590a..9bec0e78909aa 100644 --- a/include/exec/cputlb.h +++ b/include/exec/cputlb.h @@ -150,7 +150,7 @@ void tlb_flush_all_cpus_synced(CPUState *src_cpu); * MMU indexes. */ void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, - uint16_t idxmap); + MMUIdxMap idxmap); /** * tlb_flush_page_by_mmuidx_all_cpus_synced: @@ -165,7 +165,7 @@ void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, * translations using the flushed TLBs. */ void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, - uint16_t idxmap); + MMUIdxMap idxmap); /** * tlb_flush_by_mmuidx: @@ -176,7 +176,7 @@ void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, * Flush all entries from the TLB of the specified CPU, for the specified * MMU indexes. */ -void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap); +void tlb_flush_by_mmuidx(CPUState *cpu, MMUIdxMap idxmap); /** * tlb_flush_by_mmuidx_all_cpus_synced: @@ -189,7 +189,7 @@ void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap); * When this function returns, no CPUs will subsequently perform * translations using the flushed TLBs. */ -void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap); +void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, MMUIdxMap idxmap); /** * tlb_flush_page_bits_by_mmuidx @@ -201,11 +201,11 @@ void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap); * Similar to tlb_flush_page_mask, but with a bitmap of indexes. */ void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr, - uint16_t idxmap, unsigned bits); + MMUIdxMap idxmap, unsigned bits); /* Similarly, with broadcast and syncing. */ void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, - uint16_t idxmap, + MMUIdxMap idxmap, unsigned bits); /** @@ -220,14 +220,14 @@ void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, * comparing only the low @bits worth of each virtual page. */ void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, - vaddr len, uint16_t idxmap, + vaddr len, MMUIdxMap idxmap, unsigned bits); /* Similarly, with broadcast and syncing. */ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, vaddr len, - uint16_t idxmap, + MMUIdxMap idxmap, unsigned bits); #else static inline void tlb_flush_page(CPUState *cpu, vaddr addr) @@ -243,42 +243,42 @@ static inline void tlb_flush_all_cpus_synced(CPUState *src_cpu) { } static inline void tlb_flush_page_by_mmuidx(CPUState *cpu, - vaddr addr, uint16_t idxmap) + vaddr addr, MMUIdxMap idxmap) { } -static inline void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) +static inline void tlb_flush_by_mmuidx(CPUState *cpu, MMUIdxMap idxmap) { } static inline void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, - uint16_t idxmap) + MMUIdxMap idxmap) { } static inline void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, - uint16_t idxmap) + MMUIdxMap idxmap) { } static inline void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr, - uint16_t idxmap, + MMUIdxMap idxmap, unsigned bits) { } static inline void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, - uint16_t idxmap, unsigned bits) + MMUIdxMap idxmap, unsigned bits) { } static inline void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, - vaddr len, uint16_t idxmap, + vaddr len, MMUIdxMap idxmap, unsigned bits) { } static inline void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, vaddr len, - uint16_t idxmap, + MMUIdxMap idxmap, unsigned bits) { } diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index fb788ca1107b1..8dd83f7457c7c 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -198,10 +198,10 @@ struct CPUClass { }; /* - * Fix the number of mmu modes to 16, which is also the maximum - * supported by the softmmu tlb api. + * Fix the number of mmu modes to 16. */ #define NB_MMU_MODES 16 +typedef uint16_t MMUIdxMap; /* Use a fully associative victim tlb of 8 entries. */ #define CPU_VTLB_SIZE 8 @@ -306,7 +306,7 @@ typedef struct CPUTLBCommon { * mmu_idx N since the last time that mmu_idx was flushed. * Protected by tlb_c.lock. */ - uint16_t dirty; + MMUIdxMap dirty; /* * Statistics. These are not lock protected, but are read and * written atomically. This allows the monitor to print a snapshot From 3c58ddc9d784cd65af3d5810d9d66e57900e280a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 11 Jul 2025 17:32:37 -0600 Subject: [PATCH 0822/1794] include/hw/core/cpu: Introduce cpu_tlb_fast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Encapsulate access to cpu->neg.tlb.f[] in a function. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 16 ++++++++-------- include/hw/core/cpu.h | 7 +++++++ tcg/aarch64/tcg-target.c.inc | 2 +- tcg/arm/tcg-target.c.inc | 2 +- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index d324f3333963e..2a6aa01c57c4c 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -129,7 +129,7 @@ static inline uint64_t tlb_addr_write(const CPUTLBEntry *entry) static inline uintptr_t tlb_index(CPUState *cpu, uintptr_t mmu_idx, vaddr addr) { - uintptr_t size_mask = cpu->neg.tlb.f[mmu_idx].mask >> CPU_TLB_ENTRY_BITS; + uintptr_t size_mask = cpu_tlb_fast(cpu, mmu_idx)->mask >> CPU_TLB_ENTRY_BITS; return (addr >> TARGET_PAGE_BITS) & size_mask; } @@ -138,7 +138,7 @@ static inline uintptr_t tlb_index(CPUState *cpu, uintptr_t mmu_idx, static inline CPUTLBEntry *tlb_entry(CPUState *cpu, uintptr_t mmu_idx, vaddr addr) { - return &cpu->neg.tlb.f[mmu_idx].table[tlb_index(cpu, mmu_idx, addr)]; + return &cpu_tlb_fast(cpu, mmu_idx)->table[tlb_index(cpu, mmu_idx, addr)]; } static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns, @@ -292,7 +292,7 @@ static void tlb_flush_one_mmuidx_locked(CPUState *cpu, int mmu_idx, int64_t now) { CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx]; - CPUTLBDescFast *fast = &cpu->neg.tlb.f[mmu_idx]; + CPUTLBDescFast *fast = cpu_tlb_fast(cpu, mmu_idx); tlb_mmu_resize_locked(desc, fast, now); tlb_mmu_flush_locked(desc, fast); @@ -331,7 +331,7 @@ void tlb_init(CPUState *cpu) cpu->neg.tlb.c.dirty = 0; for (i = 0; i < NB_MMU_MODES; i++) { - tlb_mmu_init(&cpu->neg.tlb.d[i], &cpu->neg.tlb.f[i], now); + tlb_mmu_init(&cpu->neg.tlb.d[i], cpu_tlb_fast(cpu, i), now); } } @@ -342,7 +342,7 @@ void tlb_destroy(CPUState *cpu) qemu_spin_destroy(&cpu->neg.tlb.c.lock); for (i = 0; i < NB_MMU_MODES; i++) { CPUTLBDesc *desc = &cpu->neg.tlb.d[i]; - CPUTLBDescFast *fast = &cpu->neg.tlb.f[i]; + CPUTLBDescFast *fast = cpu_tlb_fast(cpu, i); g_free(fast->table); g_free(desc->fulltlb); @@ -667,7 +667,7 @@ static void tlb_flush_range_locked(CPUState *cpu, int midx, unsigned bits) { CPUTLBDesc *d = &cpu->neg.tlb.d[midx]; - CPUTLBDescFast *f = &cpu->neg.tlb.f[midx]; + CPUTLBDescFast *f = cpu_tlb_fast(cpu, midx); vaddr mask = MAKE_64BIT_MASK(0, bits); /* @@ -923,7 +923,7 @@ void tlb_reset_dirty(CPUState *cpu, uintptr_t start, uintptr_t length) qemu_spin_lock(&cpu->neg.tlb.c.lock); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx]; - CPUTLBDescFast *fast = &cpu->neg.tlb.f[mmu_idx]; + CPUTLBDescFast *fast = cpu_tlb_fast(cpu, mmu_idx); unsigned int n = tlb_n_entries(fast); unsigned int i; @@ -1316,7 +1316,7 @@ static bool victim_tlb_hit(CPUState *cpu, size_t mmu_idx, size_t index, if (cmp == page) { /* Found entry in victim tlb, swap tlb and iotlb. */ - CPUTLBEntry tmptlb, *tlb = &cpu->neg.tlb.f[mmu_idx].table[index]; + CPUTLBEntry tmptlb, *tlb = &cpu_tlb_fast(cpu, mmu_idx)->table[index]; qemu_spin_lock(&cpu->neg.tlb.c.lock); copy_tlb_helper_locked(&tmptlb, tlb); diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 8dd83f7457c7c..22a78c9ee13b3 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -602,6 +602,13 @@ static inline CPUArchState *cpu_env(CPUState *cpu) return (CPUArchState *)(cpu + 1); } +#ifdef CONFIG_TCG +static inline CPUTLBDescFast *cpu_tlb_fast(CPUState *cpu, int mmu_idx) +{ + return &cpu->neg.tlb.f[mmu_idx]; +} +#endif + typedef QTAILQ_HEAD(CPUTailQ, CPUState) CPUTailQ; extern CPUTailQ cpus_queue; diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 3b088b7bd9727..caf79c742d910 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1668,7 +1668,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst->oi = oi; ldst->addr_reg = addr_reg; - /* Load cpu->neg.tlb.f[mmu_idx].{mask,table} into {tmp0,tmp1}. */ + /* Load CPUTLBDescFast.{mask,table} into {tmp0,tmp1}. */ QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, mask) != 0); QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, table) != 8); tcg_out_insn(s, 3314, LDP, TCG_REG_TMP0, TCG_REG_TMP1, TCG_AREG0, diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 338c57b061631..87ca66bb0287f 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1421,7 +1421,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst->oi = oi; ldst->addr_reg = addr; - /* Load cpu->neg.tlb.f[mmu_idx].{mask,table} into {r0,r1}. */ + /* Load CPUTLBDescFast.{mask,table} into {r0,r1}. */ QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, mask) != 0); QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, table) != 4); tcg_out_ldrd_8(s, COND_AL, TCG_REG_R0, TCG_AREG0, fast_off); From 33ea495cd3bfb21db920b6928af7eda39ced5c20 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 11 Jul 2025 18:20:26 -0600 Subject: [PATCH 0823/1794] include/hw/core/cpu: Invert the indexing into CPUTLBDescFast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This array is within CPUNegativeOffsetState, which means the last element of the array has an offset from env with the smallest magnitude. This can be encoded into fewer bits when generating TCG fast path memory references. When we changed the NB_MMU_MODES to be a global constant, rather than a per-target value, we pessimized the code generated for targets which use only a few mmu indexes. By inverting the array index, we counteract that. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/hw/core/cpu.h | 11 ++++++++++- tcg/tcg.c | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 22a78c9ee13b3..c9f40c25392e5 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -603,9 +603,18 @@ static inline CPUArchState *cpu_env(CPUState *cpu) } #ifdef CONFIG_TCG +/* + * Invert the index order of the CPUTLBDescFast array so that lower + * mmu_idx have offsets from env with smaller magnitude. + */ +static inline int mmuidx_to_fast_index(int mmu_idx) +{ + return NB_MMU_MODES - 1 - mmu_idx; +} + static inline CPUTLBDescFast *cpu_tlb_fast(CPUState *cpu, int mmu_idx) { - return &cpu->neg.tlb.f[mmu_idx]; + return &cpu->neg.tlb.f[mmuidx_to_fast_index(mmu_idx)]; } #endif diff --git a/tcg/tcg.c b/tcg/tcg.c index afac55a203ab0..294762c283b74 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -425,7 +425,8 @@ static uintptr_t G_GNUC_UNUSED get_jmp_target_addr(TCGContext *s, int which) static int __attribute__((unused)) tlb_mask_table_ofs(TCGContext *s, int which) { - return (offsetof(CPUNegativeOffsetState, tlb.f[which]) - + int fi = mmuidx_to_fast_index(which); + return (offsetof(CPUNegativeOffsetState, tlb.f[fi]) - sizeof(CPUNegativeOffsetState)); } From 9ea28805814bedf511baf1635bb2fd0ceefcd638 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 11 Jul 2025 18:35:59 -0600 Subject: [PATCH 0824/1794] target/hppa: Adjust mmu indexes to begin with 0 This is a logical reversion of 2ad04500543, though there have been additions to the set of mmu indexes since then. The impetus to that original patch, "9-15 will use shorter assembler instructions when run on a x86-64 host" is now handled generically. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 11d59d11ca8a9..672ab3750c886 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -29,21 +29,21 @@ #include "qemu/interval-tree.h" #include "hw/registerfields.h" -#define MMU_ABS_W_IDX 6 -#define MMU_ABS_IDX 7 -#define MMU_KERNEL_IDX 8 -#define MMU_KERNEL_P_IDX 9 -#define MMU_PL1_IDX 10 -#define MMU_PL1_P_IDX 11 -#define MMU_PL2_IDX 12 -#define MMU_PL2_P_IDX 13 -#define MMU_USER_IDX 14 -#define MMU_USER_P_IDX 15 - -#define MMU_IDX_MMU_DISABLED(MIDX) ((MIDX) < MMU_KERNEL_IDX) -#define MMU_IDX_TO_PRIV(MIDX) (((MIDX) - MMU_KERNEL_IDX) / 2) -#define MMU_IDX_TO_P(MIDX) (((MIDX) - MMU_KERNEL_IDX) & 1) -#define PRIV_P_TO_MMU_IDX(PRIV, P) ((PRIV) * 2 + !!(P) + MMU_KERNEL_IDX) +#define MMU_KERNEL_IDX 0 +#define MMU_KERNEL_P_IDX 1 +#define MMU_PL1_IDX 2 +#define MMU_PL1_P_IDX 3 +#define MMU_PL2_IDX 4 +#define MMU_PL2_P_IDX 5 +#define MMU_USER_IDX 6 +#define MMU_USER_P_IDX 7 +#define MMU_ABS_IDX 8 +#define MMU_ABS_W_IDX 9 + +#define MMU_IDX_MMU_DISABLED(MIDX) ((MIDX) >= MMU_ABS_IDX) +#define MMU_IDX_TO_PRIV(MIDX) ((MIDX) / 2) +#define MMU_IDX_TO_P(MIDX) ((MIDX) & 1) +#define PRIV_P_TO_MMU_IDX(PRIV, P) ((PRIV) * 2 + !!(P)) #define PRIV_KERNEL 0 #define PRIV_USER 3 From fa1bc5c8c3302b2fefd99e13227165bfac1f59dd Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 12 Sep 2025 10:52:51 +0200 Subject: [PATCH 0825/1794] tests/functional/s390x/test_pxelinux: Fix warnings from pylint pylint complains about wrong indentation in one of the lines and that the pxelinux_cfg_contents is a constant that should be written with capital letters. While we're at it, also add the missing doc strings. Reviewed-by: Jared Rossi Signed-off-by: Thomas Huth Message-ID: <20250912085251.274294-1-thuth@redhat.com> --- tests/functional/s390x/test_pxelinux.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/tests/functional/s390x/test_pxelinux.py b/tests/functional/s390x/test_pxelinux.py index 4fc33b8c46de4..c00cce6a5a437 100755 --- a/tests/functional/s390x/test_pxelinux.py +++ b/tests/functional/s390x/test_pxelinux.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 # # SPDX-License-Identifier: GPL-2.0-or-later -# -# Functional test that checks the pxelinux.cfg network booting of a s390x VM -# (TFTP booting without config file is already tested by the pxe qtest, so -# we don't repeat that here). +''' +Functional test that checks the pxelinux.cfg network booting of a s390x VM +(TFTP booting without config file is already tested by the pxe qtest, so +we don't repeat that here). +''' import os import shutil @@ -12,7 +13,7 @@ from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern -pxelinux_cfg_contents='''# pxelinux.cfg style config file +PXELINUX_CFG_CONTENTS='''# pxelinux.cfg style config file default Debian label Nonexisting kernel kernel.notavailable @@ -26,6 +27,10 @@ ''' class S390PxeLinux(QemuSystemTest): + ''' + Test various ways of booting via a pxelinux.cfg file, for details see: + https://wiki.syslinux.org/wiki/index.php?title=PXELINUX#Configuration + ''' ASSET_DEBIAN_KERNEL = Asset( ('https://snapshot.debian.org/archive/debian/' @@ -46,6 +51,7 @@ class S390PxeLinux(QemuSystemTest): '480859574f3f44caa6cd35c62d70e1ac0609134e22ce2a954bbed9b110c06e0b') def pxelinux_launch(self, pl_name='default', extra_opts=None): + '''Create a pxelinux.cfg file in the right location and launch QEMU''' self.require_netdev('user') self.set_machine('s390-ccw-virtio') @@ -66,11 +72,11 @@ def pxelinux_launch(self, pl_name='default', extra_opts=None): cfg_fname = self.scratch_file('tftp', 'pxelinux.cfg', pl_name) with open(cfg_fname, 'w', encoding='utf-8') as f: - f.write(pxelinux_cfg_contents) + f.write(PXELINUX_CFG_CONTENTS) virtio_net_dev = 'virtio-net-ccw,netdev=n1,bootindex=1' if extra_opts: - virtio_net_dev += ',' + extra_opts + virtio_net_dev += ',' + extra_opts self.vm.add_args('-m', '384', '-netdev', f'user,id=n1,tftp={tftpdir}', @@ -80,6 +86,7 @@ def pxelinux_launch(self, pl_name='default', extra_opts=None): def test_default(self): + '''Check whether the guest uses the "default" file name''' self.pxelinux_launch() # The kernel prints its arguments to the console, so we can use # this to check whether the kernel parameters are correctly handled: @@ -89,11 +96,13 @@ def test_default(self): wait_for_console_pattern(self, 'Run /init as init process') def test_mac(self): + '''Check whether the guest uses file name based on its MAC address''' self.pxelinux_launch(pl_name='01-02-ca-fe-ba-be-42', extra_opts='mac=02:ca:fe:ba:be:42,loadparm=3') wait_for_console_pattern(self, 'Linux version 5.3.7-301.fc31.s390x') def test_uuid(self): + '''Check whether the guest uses file name based on its UUID''' # Also add a non-bootable disk to check the fallback to network boot: self.vm.add_args('-blockdev', 'null-co,size=65536,node-name=d1', '-device', 'virtio-blk,drive=d1,bootindex=0,loadparm=1', @@ -102,11 +111,13 @@ def test_uuid(self): wait_for_console_pattern(self, 'Debian 4.19.146-1 (2020-09-17)') def test_ip(self): + '''Check whether the guest uses file name based on its IP address''' self.vm.add_args('-M', 'loadparm=3') self.pxelinux_launch(pl_name='0A00020F') wait_for_console_pattern(self, 'Linux version 5.3.7-301.fc31.s390x') def test_menu(self): + '''Check whether the boot menu works for pxelinux.cfg booting''' self.vm.add_args('-boot', 'menu=on,splash-time=10') self.pxelinux_launch(pl_name='0A00') wait_for_console_pattern(self, '[1] Nonexisting') From 2bfe71aa7e6bce5c0254e679f66a44cadcb3a367 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 12 Sep 2025 12:07:52 +0200 Subject: [PATCH 0826/1794] tests: Move the old vmstate-static-checker files to tests/data/ All other test data resides in tests/data/, so let's move the dump files here, too. Reviewed-by: Peter Xu Signed-off-by: Thomas Huth Message-ID: <20250912100755.316518-2-thuth@redhat.com> --- MAINTAINERS | 2 +- .../vmstate-static-checker}/dump1.json | 0 .../vmstate-static-checker}/dump2.json | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename tests/{vmstate-static-checker-data => data/vmstate-static-checker}/dump1.json (100%) rename tests/{vmstate-static-checker-data => data/vmstate-static-checker}/dump2.json (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 70eb0241d36c9..79abe5f6c9a58 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3613,7 +3613,7 @@ F: migration/ F: scripts/vmstate-static-checker.py F: tests/functional/migration.py F: tests/functional/*/*migration.py -F: tests/vmstate-static-checker-data/ +F: tests/data/vmstate-static-checker/ F: tests/qtest/migration/ F: tests/qtest/migration-* F: docs/devel/migration/ diff --git a/tests/vmstate-static-checker-data/dump1.json b/tests/data/vmstate-static-checker/dump1.json similarity index 100% rename from tests/vmstate-static-checker-data/dump1.json rename to tests/data/vmstate-static-checker/dump1.json diff --git a/tests/vmstate-static-checker-data/dump2.json b/tests/data/vmstate-static-checker/dump2.json similarity index 100% rename from tests/vmstate-static-checker-data/dump2.json rename to tests/data/vmstate-static-checker/dump2.json From 5fe2b6255fa2794e9d0fa29f94b85766af3a4286 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 12 Sep 2025 12:07:53 +0200 Subject: [PATCH 0827/1794] tests/functional: Test whether the vmstate-static-checker script works fine We've got two vmstate dump files in the repository which are meant for verifying whether the vmstate-static-checker.py works as expected. Since running this manually is a cumbersome job, let's add an automated test for this instead that runs the script with the two dump files and checks for the expected output. Signed-off-by: Thomas Huth Message-ID: <20250912100755.316518-3-thuth@redhat.com> --- MAINTAINERS | 1 + tests/functional/x86_64/meson.build | 1 + tests/functional/x86_64/test_bad_vmstate.py | 58 +++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100755 tests/functional/x86_64/test_bad_vmstate.py diff --git a/MAINTAINERS b/MAINTAINERS index 79abe5f6c9a58..24c061aff35ea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3613,6 +3613,7 @@ F: migration/ F: scripts/vmstate-static-checker.py F: tests/functional/migration.py F: tests/functional/*/*migration.py +F: tests/functional/x86_64/test_bad_vmstate.py F: tests/data/vmstate-static-checker/ F: tests/qtest/migration/ F: tests/qtest/migration-* diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build index d0b4667bb8abf..ef12ac43b370d 100644 --- a/tests/functional/x86_64/meson.build +++ b/tests/functional/x86_64/meson.build @@ -10,6 +10,7 @@ test_x86_64_timeouts = { } tests_x86_64_system_quick = [ + 'bad_vmstate', 'cpu_model_versions', 'cpu_queries', 'mem_addr_space', diff --git a/tests/functional/x86_64/test_bad_vmstate.py b/tests/functional/x86_64/test_bad_vmstate.py new file mode 100755 index 0000000000000..40098a8490b06 --- /dev/null +++ b/tests/functional/x86_64/test_bad_vmstate.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +'''Test whether the vmstate-static-checker script detects problems correctly''' + +import subprocess + +from qemu_test import QemuBaseTest + + +EXPECTED_OUTPUT='''Warning: checking incompatible machine types: "pc-i440fx-2.1", "pc-i440fx-2.2" +Section "fw_cfg" does not exist in dest +Section "fusbh200-ehci-usb" version error: 2 > 1 +Section "fusbh200-ehci-usb", Description "ehci-core": expected field "usbsts", got "usbsts_pending"; skipping rest +Section "pci-serial-4x" Description "pci-serial-multi": Entry "Fields" missing +Section "intel-hda-generic", Description "intel-hda", Field "pci": missing description +Section "cfi.pflash01": Entry "Description" missing +Section "megasas", Description "PCIDevice": expected field "irq_state", while dest has no further fields +Section "PIIX3-xen" Description "PIIX3": minimum version error: 1 < 2 +Section "PIIX3-xen" Description "PIIX3": Entry "Subsections" missing +Section "tpci200": Description "tpci200" missing, got "tpci2002" instead; skipping +Section "sun-fdtwo" Description "fdc": version error: 2 > 1 +Section "sun-fdtwo", Description "fdrive": Subsection "fdrive/media_rate" not found +Section "usb-kbd" Description "usb-kbd" Field "kbd.keycodes" size mismatch: 4 , 2 +''' + +class BadVmStateTest(QemuBaseTest): + '''Test class for testing vmstat-static-checker script with bad input''' + + def test_checker(self): + """ + Test whether the checker script correctly detects the changes + between dump1.json and dump2.json. + """ + src_json = self.data_file('..', 'data', 'vmstate-static-checker', + 'dump1.json') + dst_json = self.data_file('..', 'data', 'vmstate-static-checker', + 'dump2.json') + checkerscript = self.data_file('..', '..', 'scripts', + 'vmstate-static-checker.py') + + self.log.info('Comparing %s with %s', src_json, dst_json) + cp = subprocess.run([checkerscript, '-s', src_json, '-d', dst_json], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, check=False) + if cp.returncode != 13: + self.fail('Unexpected return code of vmstate-static-checker: ' + + cp.returncode) + if cp.stdout != EXPECTED_OUTPUT: + self.log.info('vmstate-static-checker output:\n%s', cp.stdout) + self.log.info('expected output:\n%s', EXPECTED_OUTPUT) + self.fail('Unexpected vmstate-static-checker output!') + + +if __name__ == '__main__': + QemuBaseTest.main() From d0fdd2a20b4596258ace2a93650390ac4db653ab Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 12 Sep 2025 12:07:54 +0200 Subject: [PATCH 0828/1794] tests/data/vmstate-static-checker: Add dump files from QEMU 7.2.17 For automatic tests, we need reference files from older QEMU versions. QEMU 7.2 is a long term stable release, so it's a good candidate for checking whether the migration could still work correctly. Let's add the files from that version that have been taken with the "-dump-vmstate" parameter of QEMU (compiled with single machines and the configure switch "--without-default-devices" to keep the json files reasonable small). Some devices also have been removed manually from the json files, e.g. the "pci-bridge" (which can be disabled in later QEMU versions via Kconfig), and some Linux-related devices like "scsi-block" and "scsi-generic" and KVM-related devices. Without removing them, we might get errors otherwise if these devices have not been compiled into the destination QEMU build. Acked-by: Peter Xu Signed-off-by: Thomas Huth Message-ID: <20250912100755.316518-4-thuth@redhat.com> --- .../aarch64/virt-7.2.json | 2571 +++++++++++++ .../vmstate-static-checker/m68k/virt-7.2.json | 2936 +++++++++++++++ .../ppc64/pseries-7.2.json | 1068 ++++++ .../s390x/s390-ccw-virtio-7.2.json | 475 +++ .../x86_64/pc-q35-7.2.json | 3297 +++++++++++++++++ 5 files changed, 10347 insertions(+) create mode 100644 tests/data/vmstate-static-checker/aarch64/virt-7.2.json create mode 100644 tests/data/vmstate-static-checker/m68k/virt-7.2.json create mode 100644 tests/data/vmstate-static-checker/ppc64/pseries-7.2.json create mode 100644 tests/data/vmstate-static-checker/s390x/s390-ccw-virtio-7.2.json create mode 100644 tests/data/vmstate-static-checker/x86_64/pc-q35-7.2.json diff --git a/tests/data/vmstate-static-checker/aarch64/virt-7.2.json b/tests/data/vmstate-static-checker/aarch64/virt-7.2.json new file mode 100644 index 0000000000000..d7491be99cb35 --- /dev/null +++ b/tests/data/vmstate-static-checker/aarch64/virt-7.2.json @@ -0,0 +1,2571 @@ +{ + "vmschkmachine": { + "Name": "virt-7.2" + }, + "gpex-pcihost": { + "Name": "gpex-pcihost", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "PCIHost", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "config_reg", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + "arm_gic": { + "Name": "arm_gic", + "version_id": 12, + "minimum_version_id": 12, + "Description": { + "name": "arm_gic", + "version_id": 12, + "minimum_version_id": 12, + "Fields": [ + { + "field": "ctlr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "cpu_ctlr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "irq_state", + "version_id": 1, + "field_exists": false, + "size": 7, + "Description": { + "name": "arm_gic_irq_state", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "enabled", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "pending", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "active", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "level", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "model", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "edge_trigger", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "group", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + { + "field": "irq_target", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "priority1", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "priority2", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "sgi_pending", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "priority_mask", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "running_priority", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "current_pending", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "bpr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "abpr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "apr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "nsapr", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "arm_gic_virt_state", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "h_hcr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "h_misr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "h_lr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "h_apr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "cpu_ctlr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "priority_mask", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "running_priority", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "current_pending", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "bpr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "abpr", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + ] + } + }, + "arm-gicv3-its-common": { + "Name": "arm-gicv3-its-common", + "version_id": 0, + "minimum_version_id": 0, + "Description": { + "name": "arm_gicv3_its", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "ctlr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "iidr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "cbaser", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "cwriter", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "creadr", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "baser", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + }, + "arm-gicv3-common": { + "Name": "arm-gicv3-common", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "arm_gicv3", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "gicd_ctlr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "gicd_statusr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "group", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "grpmod", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "enabled", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "pending", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "active", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "level", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "edge_trigger", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "gicd_ipriority", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "gicd_irouter", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "gicd_nsacr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "cpu", + "version_id": 0, + "field_exists": false, + "size": 664, + "Description": { + "name": "arm_gicv3_cpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "level", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "gicr_ctlr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "gicr_statusr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "gicr_waker", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "gicr_propbaser", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "gicr_pendbaser", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "gicr_igroupr0", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "gicr_ienabler0", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "gicr_ipendr0", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "gicr_iactiver0", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "edge_trigger", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "gicr_igrpmodr0", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "gicr_nsacr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "gicr_ipriorityr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "icc_ctlr_el1", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "icc_pmr_el1", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "icc_bpr", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "icc_apr", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "icc_igrpen", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "icc_ctlr_el3", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ], + "Subsections": [ + { + "name": "arm_gicv3_cpu/virt", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "ich_apr", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "ich_hcr_el2", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "ich_lr_el2", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "ich_vmcr_el2", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "arm_gicv3_cpu/sre_el1", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "icc_sre_el1", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "arm_gicv3_cpu/gicv4", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "gicr_vpropbaser", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "gicr_vpendbaser", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + } + ], + "Subsections": [ + { + "name": "arm_gicv3/gicd_no_migration_shift_bug", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "gicd_no_migration_shift_bug", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + ] + } + }, + "fw_cfg": { + "Name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Description": { + "name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "cur_entry", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "cur_offset", + "version_id": 0, + "field_exists": true, + "size": 4 + }, + { + "field": "cur_offset", + "version_id": 2, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "fw_cfg/dma", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "dma_addr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "fw_cfg/acpi_mr", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "table_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "linker_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "rsdp_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + }, + "cfi.pflash01": { + "Name": "cfi.pflash01", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "pflash_cfi01", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "wcycle", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "cmd", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "status", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "counter", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ], + "Subsections": [ + { + "name": "pflash_cfi01_blk_write", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "blk_bytes", + "version_id": 0, + "field_exists": false, + "size": 0 + }, + { + "field": "blk_offset", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + }, + "pl061_luminary": { + "Name": "pl061_luminary", + "version_id": 4, + "minimum_version_id": 4, + "Description": { + "name": "pl061", + "version_id": 4, + "minimum_version_id": 4, + "Fields": [ + { + "field": "locked", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "data", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "old_out_data", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "old_in_data", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "dir", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "isense", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "ibe", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "iev", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "im", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "istate", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "afsel", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "dr2r", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "dr4r", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "dr8r", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "odr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "pur", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "pdr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "slr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "den", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "cr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "amsel", + "version_id": 2, + "field_exists": false, + "size": 4 + } + ] + } + }, + "pl011_luminary": { + "Name": "pl011_luminary", + "version_id": 2, + "minimum_version_id": 2, + "Description": { + "name": "pl011", + "version_id": 2, + "minimum_version_id": 2, + "Fields": [ + { + "field": "readbuff", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "flags", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "lcr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "rsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "cr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "dmacr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "int_enabled", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "int_level", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "read_fifo", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "ilpr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "ibrd", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "fbrd", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "ifl", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "read_pos", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "read_count", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "read_trigger", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "pl011/clock", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "clk", + "version_id": 0, + "field_exists": false, + "size": 8, + "Description": { + "name": "clock", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "period", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ], + "Subsections": [ + { + "name": "clock/muldiv", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "multiplier", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "divider", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + } + ] + } + ] + } + }, + "pl061": { + "Name": "pl061", + "version_id": 4, + "minimum_version_id": 4, + "Description": { + "name": "pl061", + "version_id": 4, + "minimum_version_id": 4, + "Fields": [ + { + "field": "locked", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "data", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "old_out_data", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "old_in_data", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "dir", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "isense", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "ibe", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "iev", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "im", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "istate", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "afsel", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "dr2r", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "dr4r", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "dr8r", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "odr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "pur", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "pdr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "slr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "den", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "cr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "amsel", + "version_id": 2, + "field_exists": false, + "size": 4 + } + ] + } + }, + "vmcoreinfo": { + "Name": "vmcoreinfo", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "vmcoreinfo", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "has_vmcoreinfo", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "vmcoreinfo.host_format", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "vmcoreinfo.guest_format", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "vmcoreinfo.size", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "vmcoreinfo.paddr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + }, + "arm_gic_common": { + "Name": "arm_gic_common", + "version_id": 12, + "minimum_version_id": 12, + "Description": { + "name": "arm_gic", + "version_id": 12, + "minimum_version_id": 12, + "Fields": [ + { + "field": "ctlr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "cpu_ctlr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "irq_state", + "version_id": 1, + "field_exists": false, + "size": 7, + "Description": { + "name": "arm_gic_irq_state", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "enabled", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "pending", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "active", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "level", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "model", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "edge_trigger", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "group", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + { + "field": "irq_target", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "priority1", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "priority2", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "sgi_pending", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "priority_mask", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "running_priority", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "current_pending", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "bpr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "abpr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "apr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "nsapr", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "arm_gic_virt_state", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "h_hcr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "h_misr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "h_lr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "h_apr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "cpu_ctlr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "priority_mask", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "running_priority", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "current_pending", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "bpr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "abpr", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + ] + } + }, + "arm-smmuv3": { + "Name": "arm-smmuv3", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "smmuv3", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "features", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "sid_size", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "sid_split", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "cr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "cr0ack", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "statusr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "irq_ctrl", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "gerror", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "gerrorn", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "gerror_irq_cfg0", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "gerror_irq_cfg1", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "gerror_irq_cfg2", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "strtab_base", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "strtab_base_cfg", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "eventq_irq_cfg0", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "eventq_irq_cfg1", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "eventq_irq_cfg2", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "cmdq", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "smmuv3_queue", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "base", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "prod", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "cons", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "log2size", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + { + "field": "eventq", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "smmuv3_queue", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "base", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "prod", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "cons", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "log2size", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + } + ] + } + }, + "pl011": { + "Name": "pl011", + "version_id": 2, + "minimum_version_id": 2, + "Description": { + "name": "pl011", + "version_id": 2, + "minimum_version_id": 2, + "Fields": [ + { + "field": "readbuff", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "flags", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "lcr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "rsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "cr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "dmacr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "int_enabled", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "int_level", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "read_fifo", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "ilpr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "ibrd", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "fbrd", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "ifl", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "read_pos", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "read_count", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "read_trigger", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "pl011/clock", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "clk", + "version_id": 0, + "field_exists": false, + "size": 8, + "Description": { + "name": "clock", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "period", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ], + "Subsections": [ + { + "name": "clock/muldiv", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "multiplier", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "divider", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + } + ] + } + ] + } + }, + "armv7m": { + "Name": "armv7m", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "armv7m", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "refclk", + "version_id": 0, + "field_exists": false, + "size": 8, + "Description": { + "name": "clock", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "period", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ], + "Subsections": [ + { + "name": "clock/muldiv", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "multiplier", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "divider", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + }, + { + "field": "cpuclk", + "version_id": 0, + "field_exists": false, + "size": 8, + "Description": { + "name": "clock", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "period", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ], + "Subsections": [ + { + "name": "clock/muldiv", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "multiplier", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "divider", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + } + ] + } + }, + "fw_cfg_mem": { + "Name": "fw_cfg_mem", + "version_id": 2, + "minimum_version_id": 1, + "Description": { + "name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "cur_entry", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "cur_offset", + "version_id": 0, + "field_exists": true, + "size": 4 + }, + { + "field": "cur_offset", + "version_id": 2, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "fw_cfg/dma", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "dma_addr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "fw_cfg/acpi_mr", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "table_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "linker_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "rsdp_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + }, + "pl031": { + "Name": "pl031", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "pl031", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "tick_offset_vmstate", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "mr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "lr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "cr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "im", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "is", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "pl031/tick-offset", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "tick_offset", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + }, + "fw_cfg_io": { + "Name": "fw_cfg_io", + "version_id": 2, + "minimum_version_id": 1, + "Description": { + "name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "cur_entry", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "cur_offset", + "version_id": 0, + "field_exists": true, + "size": 4 + }, + { + "field": "cur_offset", + "version_id": 2, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "fw_cfg/dma", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "dma_addr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "fw_cfg/acpi_mr", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "table_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "linker_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "rsdp_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + }, + "virtio-serial-device": { + "Name": "virtio-serial-device", + "version_id": 3, + "minimum_version_id": 3, + "Description": { + "name": "virtio-console", + "version_id": 3, + "minimum_version_id": 3, + "Fields": [ + { + "field": "virtio", + "version_id": 0, + "field_exists": false, + "size": 0 + } + ] + } + }, + "acpi-ged": { + "Name": "acpi-ged", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "acpi-ged", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "ged_state", + "version_id": 1, + "field_exists": false, + "size": 560, + "Description": { + "name": "acpi-ged-state", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "sel", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + } + ], + "Subsections": [ + { + "name": "acpi-ged/memhp", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "memhp_state", + "version_id": 1, + "field_exists": false, + "size": 304, + "Description": { + "name": "memory hotplug state", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "selector", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "devs", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "memory hotplug device state", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "is_enabled", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "is_inserting", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "ost_event", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "ost_status", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + } + ] + } + } + ] + }, + { + "name": "acpi-ged/ghes", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "ghes_state", + "version_id": 1, + "field_exists": false, + "size": 16, + "Description": { + "name": "acpi-ghes", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "ghes_addr_le", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + } + ] + } + ] + } + }, + "pcie-host-bridge": { + "Name": "pcie-host-bridge", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "PCIHost", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "config_reg", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + "gpio-key": { + "Name": "gpio-key", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "gpio-key", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "timer", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + }, + "armv7m_nvic": { + "Name": "armv7m_nvic", + "version_id": 4, + "minimum_version_id": 4, + "Description": { + "name": "armv7m_nvic", + "version_id": 4, + "minimum_version_id": 4, + "Fields": [ + { + "field": "vectors", + "version_id": 1, + "field_exists": false, + "size": 6, + "Description": { + "name": "armv7m_nvic_info", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "prio", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "enabled", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "pending", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "active", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "level", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + { + "field": "prigroup[M_REG_NS]", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "armv7m_nvic/m-security", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "sec_vectors", + "version_id": 1, + "field_exists": false, + "size": 6, + "Description": { + "name": "armv7m_nvic_info", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "prio", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "enabled", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "pending", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "active", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "level", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + { + "field": "prigroup[M_REG_S]", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "itns", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + ] + } + }, + "pci-host-bridge": { + "Name": "pci-host-bridge", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "PCIHost", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "config_reg", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + "gpex-root": { + "Name": "gpex-root", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "gpex_root", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "parent_obj", + "version_id": 0, + "field_exists": false, + "size": 2608, + "Description": { + "name": "PCIDevice", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "version_id", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "config", + "version_id": 0, + "field_exists": true, + "size": 256 + }, + { + "field": "config", + "version_id": 0, + "field_exists": true, + "size": 4096 + }, + { + "field": "irq_state", + "version_id": 2, + "field_exists": false, + "size": 16 + } + ] + } + } + ] + } + }, + "armv7m_systick": { + "Name": "armv7m_systick", + "version_id": 3, + "minimum_version_id": 3, + "Description": { + "name": "armv7m_systick", + "version_id": 3, + "minimum_version_id": 3, + "Fields": [ + { + "field": "refclk", + "version_id": 0, + "field_exists": false, + "size": 8, + "Description": { + "name": "clock", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "period", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ], + "Subsections": [ + { + "name": "clock/muldiv", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "multiplier", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "divider", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + }, + { + "field": "cpuclk", + "version_id": 0, + "field_exists": false, + "size": 8, + "Description": { + "name": "clock", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "period", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ], + "Subsections": [ + { + "name": "clock/muldiv", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "multiplier", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "divider", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + }, + { + "field": "control", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "tick", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "ptimer", + "version_id": 1, + "field_exists": false, + "size": 8, + "Description": { + "name": "ptimer", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "enabled", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "limit", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "delta", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "period_frac", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "period", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "last_event", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "next_event", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "timer", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + } + ] + } + } +} diff --git a/tests/data/vmstate-static-checker/m68k/virt-7.2.json b/tests/data/vmstate-static-checker/m68k/virt-7.2.json new file mode 100644 index 0000000000000..16bee8cdad019 --- /dev/null +++ b/tests/data/vmstate-static-checker/m68k/virt-7.2.json @@ -0,0 +1,2936 @@ +{ + "vmschkmachine": { + "Name": "virt-7.2" + }, + "m68020-m68k-cpu": { + "Name": "m68020-m68k-cpu", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "cpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.dregs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.aregs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.current_sp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_op", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_x", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_n", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_v", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_c", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_z", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pending_vector", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pending_level", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "cpu/fpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.fpcr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.fpsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.fregs", + "version_id": 0, + "field_exists": false, + "size": 16, + "Description": { + "name": "freg", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "freg_tmp", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp_mant", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tmp_exp", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + } + ] + } + }, + { + "field": "env.fp_result", + "version_id": 0, + "field_exists": false, + "size": 16, + "Description": { + "name": "freg", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "freg_tmp", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp_mant", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tmp_exp", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + } + ] + } + } + ] + }, + { + "name": "cpu/cf_spregs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.macc", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "env.macsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mac_mask", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.rambar0", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mbar", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "cpu/68040_mmu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.mmu.ar", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.ssw", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.tcr", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "env.mmu.urp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.srp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.fault", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "env.mmu.ttr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.mmusr", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "cpu/68040_spregs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.vbr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cacr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sfc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.dfc", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + }, + "m68030-m68k-cpu": { + "Name": "m68030-m68k-cpu", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "cpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.dregs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.aregs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.current_sp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_op", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_x", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_n", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_v", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_c", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_z", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pending_vector", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pending_level", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "cpu/fpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.fpcr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.fpsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.fregs", + "version_id": 0, + "field_exists": false, + "size": 16, + "Description": { + "name": "freg", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "freg_tmp", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp_mant", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tmp_exp", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + } + ] + } + }, + { + "field": "env.fp_result", + "version_id": 0, + "field_exists": false, + "size": 16, + "Description": { + "name": "freg", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "freg_tmp", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp_mant", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tmp_exp", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + } + ] + } + } + ] + }, + { + "name": "cpu/cf_spregs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.macc", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "env.macsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mac_mask", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.rambar0", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mbar", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "cpu/68040_mmu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.mmu.ar", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.ssw", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.tcr", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "env.mmu.urp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.srp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.fault", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "env.mmu.ttr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.mmusr", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "cpu/68040_spregs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.vbr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cacr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sfc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.dfc", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + }, + "fw_cfg": { + "Name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Description": { + "name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "cur_entry", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "cur_offset", + "version_id": 0, + "field_exists": true, + "size": 4 + }, + { + "field": "cur_offset", + "version_id": 2, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "fw_cfg/dma", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "dma_addr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "fw_cfg/acpi_mr", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "table_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "linker_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "rsdp_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + }, + "m68k-irq-controller": { + "Name": "m68k-irq-controller", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "m68k-irqc", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "ipr", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + "virt-ctrl": { + "Name": "virt-ctrl", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "virt-ctrl", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "irq_enabled", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + "m68040-m68k-cpu": { + "Name": "m68040-m68k-cpu", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "cpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.dregs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.aregs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.current_sp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_op", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_x", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_n", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_v", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_c", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_z", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pending_vector", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pending_level", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "cpu/fpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.fpcr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.fpsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.fregs", + "version_id": 0, + "field_exists": false, + "size": 16, + "Description": { + "name": "freg", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "freg_tmp", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp_mant", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tmp_exp", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + } + ] + } + }, + { + "field": "env.fp_result", + "version_id": 0, + "field_exists": false, + "size": 16, + "Description": { + "name": "freg", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "freg_tmp", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp_mant", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tmp_exp", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + } + ] + } + } + ] + }, + { + "name": "cpu/cf_spregs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.macc", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "env.macsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mac_mask", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.rambar0", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mbar", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "cpu/68040_mmu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.mmu.ar", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.ssw", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.tcr", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "env.mmu.urp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.srp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.fault", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "env.mmu.ttr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.mmusr", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "cpu/68040_spregs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.vbr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cacr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sfc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.dfc", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + }, + "goldfish_pic": { + "Name": "goldfish_pic", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "goldfish_pic", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "pending", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "enabled", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + "goldfish_tty": { + "Name": "goldfish_tty", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "goldfish_tty", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "data_len", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "data_ptr", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "int_enabled", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "rx_fifo", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "Fifo8", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "data", + "version_id": 1, + "field_exists": false, + "size": 0 + }, + { + "field": "head", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "num", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + } + ] + } + }, + "m68000-m68k-cpu": { + "Name": "m68000-m68k-cpu", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "cpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.dregs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.aregs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.current_sp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_op", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_x", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_n", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_v", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_c", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_z", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pending_vector", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pending_level", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "cpu/fpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.fpcr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.fpsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.fregs", + "version_id": 0, + "field_exists": false, + "size": 16, + "Description": { + "name": "freg", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "freg_tmp", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp_mant", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tmp_exp", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + } + ] + } + }, + { + "field": "env.fp_result", + "version_id": 0, + "field_exists": false, + "size": 16, + "Description": { + "name": "freg", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "freg_tmp", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp_mant", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tmp_exp", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + } + ] + } + } + ] + }, + { + "name": "cpu/cf_spregs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.macc", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "env.macsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mac_mask", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.rambar0", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mbar", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "cpu/68040_mmu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.mmu.ar", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.ssw", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.tcr", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "env.mmu.urp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.srp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.fault", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "env.mmu.ttr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.mmusr", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "cpu/68040_spregs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.vbr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cacr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sfc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.dfc", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + }, + "m68010-m68k-cpu": { + "Name": "m68010-m68k-cpu", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "cpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.dregs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.aregs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.current_sp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_op", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_x", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_n", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_v", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_c", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_z", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pending_vector", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pending_level", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "cpu/fpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.fpcr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.fpsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.fregs", + "version_id": 0, + "field_exists": false, + "size": 16, + "Description": { + "name": "freg", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "freg_tmp", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp_mant", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tmp_exp", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + } + ] + } + }, + { + "field": "env.fp_result", + "version_id": 0, + "field_exists": false, + "size": 16, + "Description": { + "name": "freg", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "freg_tmp", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp_mant", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tmp_exp", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + } + ] + } + } + ] + }, + { + "name": "cpu/cf_spregs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.macc", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "env.macsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mac_mask", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.rambar0", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mbar", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "cpu/68040_mmu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.mmu.ar", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.ssw", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.tcr", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "env.mmu.urp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.srp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.fault", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "env.mmu.ttr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.mmusr", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "cpu/68040_spregs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.vbr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cacr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sfc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.dfc", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + }, + "m68060-m68k-cpu": { + "Name": "m68060-m68k-cpu", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "cpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.dregs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.aregs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.current_sp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_op", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_x", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_n", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_v", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_c", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_z", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pending_vector", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pending_level", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "cpu/fpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.fpcr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.fpsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.fregs", + "version_id": 0, + "field_exists": false, + "size": 16, + "Description": { + "name": "freg", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "freg_tmp", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp_mant", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tmp_exp", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + } + ] + } + }, + { + "field": "env.fp_result", + "version_id": 0, + "field_exists": false, + "size": 16, + "Description": { + "name": "freg", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "freg_tmp", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp_mant", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tmp_exp", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + } + ] + } + } + ] + }, + { + "name": "cpu/cf_spregs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.macc", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "env.macsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mac_mask", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.rambar0", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mbar", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "cpu/68040_mmu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.mmu.ar", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.ssw", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.tcr", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "env.mmu.urp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.srp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.fault", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "env.mmu.ttr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.mmusr", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "cpu/68040_spregs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.vbr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cacr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sfc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.dfc", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + }, + "fw_cfg_mem": { + "Name": "fw_cfg_mem", + "version_id": 2, + "minimum_version_id": 1, + "Description": { + "name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "cur_entry", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "cur_offset", + "version_id": 0, + "field_exists": true, + "size": 4 + }, + { + "field": "cur_offset", + "version_id": 2, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "fw_cfg/dma", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "dma_addr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "fw_cfg/acpi_mr", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "table_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "linker_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "rsdp_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + }, + "any-m68k-cpu": { + "Name": "any-m68k-cpu", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "cpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.dregs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.aregs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.current_sp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_op", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_x", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_n", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_v", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_c", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_z", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pending_vector", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pending_level", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "cpu/fpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.fpcr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.fpsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.fregs", + "version_id": 0, + "field_exists": false, + "size": 16, + "Description": { + "name": "freg", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "freg_tmp", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp_mant", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tmp_exp", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + } + ] + } + }, + { + "field": "env.fp_result", + "version_id": 0, + "field_exists": false, + "size": 16, + "Description": { + "name": "freg", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "freg_tmp", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp_mant", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tmp_exp", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + } + ] + } + } + ] + }, + { + "name": "cpu/cf_spregs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.macc", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "env.macsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mac_mask", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.rambar0", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mbar", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "cpu/68040_mmu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.mmu.ar", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.ssw", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.tcr", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "env.mmu.urp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.srp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.fault", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "env.mmu.ttr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.mmusr", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "cpu/68040_spregs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.vbr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cacr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sfc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.dfc", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + }, + "fw_cfg_io": { + "Name": "fw_cfg_io", + "version_id": 2, + "minimum_version_id": 1, + "Description": { + "name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "cur_entry", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "cur_offset", + "version_id": 0, + "field_exists": true, + "size": 4 + }, + { + "field": "cur_offset", + "version_id": 2, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "fw_cfg/dma", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "dma_addr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "fw_cfg/acpi_mr", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "table_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "linker_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "rsdp_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + }, + "virtio-serial-device": { + "Name": "virtio-serial-device", + "version_id": 3, + "minimum_version_id": 3, + "Description": { + "name": "virtio-console", + "version_id": 3, + "minimum_version_id": 3, + "Fields": [ + { + "field": "virtio", + "version_id": 0, + "field_exists": false, + "size": 0 + } + ] + } + }, + "goldfish_rtc": { + "Name": "goldfish_rtc", + "version_id": 3, + "minimum_version_id": 0, + "Description": { + "name": "goldfish_rtc", + "version_id": 3, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tick_offset_vmstate", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "alarm_next", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "alarm_running", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "irq_pending", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "irq_enabled", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "time_high", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "tick_offset", + "version_id": 3, + "field_exists": false, + "size": 8 + } + ] + } + }, + "m68k-cpu": { + "Name": "m68k-cpu", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "cpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.dregs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.aregs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.current_sp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_op", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_x", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_n", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_v", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_c", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cc_z", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pending_vector", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.pending_level", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "cpu/fpu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.fpcr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.fpsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.fregs", + "version_id": 0, + "field_exists": false, + "size": 16, + "Description": { + "name": "freg", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "freg_tmp", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp_mant", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tmp_exp", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + } + ] + } + }, + { + "field": "env.fp_result", + "version_id": 0, + "field_exists": false, + "size": 16, + "Description": { + "name": "freg", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "freg_tmp", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "tmp_mant", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tmp_exp", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + } + ] + } + } + ] + }, + { + "name": "cpu/cf_spregs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.macc", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "env.macsr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mac_mask", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.rambar0", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mbar", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "cpu/68040_mmu", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.mmu.ar", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.ssw", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.tcr", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "env.mmu.urp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.srp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.fault", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "env.mmu.ttr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.mmu.mmusr", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "cpu/68040_spregs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "env.vbr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.cacr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.sfc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "env.dfc", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + } +} diff --git a/tests/data/vmstate-static-checker/ppc64/pseries-7.2.json b/tests/data/vmstate-static-checker/ppc64/pseries-7.2.json new file mode 100644 index 0000000000000..330bbbefedfad --- /dev/null +++ b/tests/data/vmstate-static-checker/ppc64/pseries-7.2.json @@ -0,0 +1,1068 @@ +{ + "vmschkmachine": { + "Name": "pseries-7.2" + }, + "spapr-nvram": { + "Name": "spapr-nvram", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "spapr_nvram", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "size", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "buf", + "version_id": 1, + "field_exists": false, + "size": 0 + } + ] + } + }, + "xive-source": { + "Name": "xive-source", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "xive-source", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "nr_irqs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "status", + "version_id": 1, + "field_exists": false, + "size": 0 + } + ] + } + }, + "xive-tctx": { + "Name": "xive-tctx", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "xive-tctx", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "regs", + "version_id": 0, + "field_exists": false, + "size": 64 + } + ] + } + }, + "pci-host-bridge": { + "Name": "pci-host-bridge", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "PCIHost", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "config_reg", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + "ics-spapr": { + "Name": "ics-spapr", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "ics", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "nr_irqs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "irqs", + "version_id": 0, + "field_exists": false, + "size": 8, + "Description": { + "name": "ics/irq", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "server", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "priority", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "saved_priority", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "status", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "flags", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + } + ] + } + }, + "scsi-cd": { + "Name": "scsi-cd", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "scsi-disk", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "qdev", + "version_id": 0, + "field_exists": false, + "size": 608, + "Description": { + "name": "SCSIDevice", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "unit_attention.key", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "unit_attention.asc", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "unit_attention.ascq", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "sense_is_ua", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "sense", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "sense_len", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "requests", + "version_id": 0, + "field_exists": false, + "size": 0 + } + ], + "Subsections": [ + { + "name": "SCSIDevice/sense", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "sense", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + ] + } + }, + { + "field": "media_changed", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "media_event", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "eject_request", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "tray_open", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "tray_locked", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + "spapr-xive": { + "Name": "spapr-xive", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "spapr-xive", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "nr_irqs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "eat", + "version_id": 0, + "field_exists": false, + "size": 8, + "Description": { + "name": "spapr-xive/eas", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "w", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + }, + { + "field": "endt", + "version_id": 0, + "field_exists": false, + "size": 32, + "Description": { + "name": "spapr-xive/end", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "w0", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "w1", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "w2", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "w3", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "w4", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "w5", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "w6", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "w7", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + } + ] + } + }, + "scsi-hd": { + "Name": "scsi-hd", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "scsi-disk", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "qdev", + "version_id": 0, + "field_exists": false, + "size": 608, + "Description": { + "name": "SCSIDevice", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "unit_attention.key", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "unit_attention.asc", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "unit_attention.ascq", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "sense_is_ua", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "sense", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "sense_len", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "requests", + "version_id": 0, + "field_exists": false, + "size": 0 + } + ], + "Subsections": [ + { + "name": "SCSIDevice/sense", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "sense", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + ] + } + }, + { + "field": "media_changed", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "media_event", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "eject_request", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "tray_open", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "tray_locked", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + "fw_cfg_mem": { + "Name": "fw_cfg_mem", + "version_id": 2, + "minimum_version_id": 1, + "Description": { + "name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "cur_entry", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "cur_offset", + "version_id": 0, + "field_exists": true, + "size": 4 + }, + { + "field": "cur_offset", + "version_id": 2, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "fw_cfg/dma", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "dma_addr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "fw_cfg/acpi_mr", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "table_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "linker_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "rsdp_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + }, + "spapr-vty": { + "Name": "spapr-vty", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "spapr_vty", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "sdev", + "version_id": 0, + "field_exists": false, + "size": 848, + "Description": { + "name": "spapr_vio", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "reg", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "irq", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "signal_state", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "crq.qladdr", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "crq.qsize", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "crq.qnext", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + { + "field": "in", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "out", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "buf", + "version_id": 0, + "field_exists": false, + "size": 16 + } + ] + } + }, + "spapr-wdt": { + "Name": "spapr-wdt", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "spapr_watchdog", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "timer", + "version_id": 0, + "field_exists": false, + "size": 48 + }, + { + "field": "action", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "leave_others", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + "fw_cfg_io": { + "Name": "fw_cfg_io", + "version_id": 2, + "minimum_version_id": 1, + "Description": { + "name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "cur_entry", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "cur_offset", + "version_id": 0, + "field_exists": true, + "size": 4 + }, + { + "field": "cur_offset", + "version_id": 2, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "fw_cfg/dma", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "dma_addr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "fw_cfg/acpi_mr", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "table_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "linker_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "rsdp_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + }, + "spapr-vscsi": { + "Name": "spapr-vscsi", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "spapr_vscsi", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "vdev", + "version_id": 0, + "field_exists": false, + "size": 848, + "Description": { + "name": "spapr_vio", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "reg", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "irq", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "signal_state", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "crq.qladdr", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "crq.qsize", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "crq.qnext", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + } + ] + } + }, + "spapr-rtc": { + "Name": "spapr-rtc", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "spapr/rtc", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "ns_offset", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + }, + "spapr-vlan": { + "Name": "spapr-vlan", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "spapr_llan", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "sdev", + "version_id": 0, + "field_exists": false, + "size": 848, + "Description": { + "name": "spapr_vio", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "reg", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "irq", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "signal_state", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "crq.qladdr", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "crq.qsize", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "crq.qnext", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + { + "field": "isopen", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "buf_list", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "add_buf_ptr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "use_buf_ptr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "rx_bufs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "rxq_ptr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ], + "Subsections": [ + { + "name": "spapr_llan/rx_pools", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "rx_pool", + "version_id": 1, + "field_exists": false, + "size": 8, + "Description": { + "name": "spapr_llan/rx_buffer_pool", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "bufsize", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "count", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "bds", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + } + ] + } + ] + } + }, + "fw_cfg": { + "Name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Description": { + "name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "cur_entry", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "cur_offset", + "version_id": 0, + "field_exists": true, + "size": 4 + }, + { + "field": "cur_offset", + "version_id": 2, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "fw_cfg/dma", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "dma_addr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "fw_cfg/acpi_mr", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "table_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "linker_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "rsdp_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + }, + "ics": { + "Name": "ics", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "ics", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "nr_irqs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "irqs", + "version_id": 0, + "field_exists": false, + "size": 8, + "Description": { + "name": "ics/irq", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "server", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "priority", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "saved_priority", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "status", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "flags", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + } + ] + } + } +} diff --git a/tests/data/vmstate-static-checker/s390x/s390-ccw-virtio-7.2.json b/tests/data/vmstate-static-checker/s390x/s390-ccw-virtio-7.2.json new file mode 100644 index 0000000000000..9698852bd3870 --- /dev/null +++ b/tests/data/vmstate-static-checker/s390x/s390-ccw-virtio-7.2.json @@ -0,0 +1,475 @@ +{ + "vmschkmachine": { + "Name": "s390-ccw-virtio-7.2" + }, + "sclpquiesce": { + "Name": "sclpquiesce", + "version_id": 0, + "minimum_version_id": 0, + "Description": { + "name": "sclpquiesce", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "event_pending", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + "s390-sclp-event-facility": { + "Name": "s390-sclp-event-facility", + "version_id": 0, + "minimum_version_id": 0, + "Description": { + "name": "vmstate-event-facility", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "receive_mask_pieces[1]", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "vmstate-event-facility/mask64", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "receive_mask_pieces[0]", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "vmstate-event-facility/mask_length", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "mask_length", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + ] + } + }, + "zpci": { + "Name": "zpci", + "version_id": 0, + "minimum_version_id": 0, + "Description": { + "name": "zpci", + "version_id": 0, + "minimum_version_id": 0 + } + }, + "sclpconsole": { + "Name": "sclpconsole", + "version_id": 0, + "minimum_version_id": 0, + "Description": { + "name": "sclpconsole", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "event.event_pending", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "iov", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "iov_sclp", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "iov_bs", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "iov_data_len", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "iov_sclp_rest", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + "virtio-serial-device": { + "Name": "virtio-serial-device", + "version_id": 3, + "minimum_version_id": 3, + "Description": { + "name": "virtio-console", + "version_id": 3, + "minimum_version_id": 3, + "Fields": [ + { + "field": "virtio", + "version_id": 0, + "field_exists": false, + "size": 0 + } + ] + } + }, + "sclplmconsole": { + "Name": "sclplmconsole", + "version_id": 0, + "minimum_version_id": 0, + "Description": { + "name": "sclplmconsole", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "event.event_pending", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "write_errors", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "length", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "buf", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + "s390-pcihost": { + "Name": "s390-pcihost", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "PCIHost", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "config_reg", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + "s390-flic-qemu": { + "Name": "s390-flic-qemu", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "qemu-s390-flic", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "simm", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "nimm", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + "fw_cfg_io": { + "Name": "fw_cfg_io", + "version_id": 2, + "minimum_version_id": 1, + "Description": { + "name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "cur_entry", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "cur_offset", + "version_id": 0, + "field_exists": true, + "size": 4 + }, + { + "field": "cur_offset", + "version_id": 2, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "fw_cfg/dma", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "dma_addr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "fw_cfg/acpi_mr", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "table_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "linker_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "rsdp_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + }, + "s390-ipl": { + "Name": "s390-ipl", + "version_id": 0, + "minimum_version_id": 0, + "Description": { + "name": "ipl", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "compat_start_addr", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "compat_bios_start_addr", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "iplb", + "version_id": 0, + "field_exists": false, + "size": 4096, + "Description": { + "name": "ipl/iplb", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "reserved1", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "devno", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "reserved2", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ], + "Subsections": [ + { + "name": "ipl/iplb_extended", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "reserved_ext", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + ] + } + }, + { + "field": "iplb_valid", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "cssid", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "ssid", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "devno", + "version_id": 0, + "field_exists": false, + "size": 2 + } + ] + } + }, + "pci-host-bridge": { + "Name": "pci-host-bridge", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "PCIHost", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "config_reg", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + "fw_cfg_mem": { + "Name": "fw_cfg_mem", + "version_id": 2, + "minimum_version_id": 1, + "Description": { + "name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "cur_entry", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "cur_offset", + "version_id": 0, + "field_exists": true, + "size": 4 + }, + { + "field": "cur_offset", + "version_id": 2, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "fw_cfg/dma", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "dma_addr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "fw_cfg/acpi_mr", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "table_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "linker_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "rsdp_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + } +} diff --git a/tests/data/vmstate-static-checker/x86_64/pc-q35-7.2.json b/tests/data/vmstate-static-checker/x86_64/pc-q35-7.2.json new file mode 100644 index 0000000000000..e527dffaccace --- /dev/null +++ b/tests/data/vmstate-static-checker/x86_64/pc-q35-7.2.json @@ -0,0 +1,3297 @@ +{ + "vmschkmachine": { + "Name": "pc-q35-7.2" + }, + "fw_cfg": { + "Name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Description": { + "name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "cur_entry", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "cur_offset", + "version_id": 0, + "field_exists": true, + "size": 4 + }, + { + "field": "cur_offset", + "version_id": 2, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "fw_cfg/dma", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "dma_addr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "fw_cfg/acpi_mr", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "table_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "linker_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "rsdp_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + }, + "pcie-host-bridge": { + "Name": "pcie-host-bridge", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "PCIHost", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "config_reg", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + "isa-pit": { + "Name": "isa-pit", + "version_id": 3, + "minimum_version_id": 2, + "Description": { + "name": "i8254", + "version_id": 3, + "minimum_version_id": 2, + "Fields": [ + { + "field": "channels[0].irq_disabled", + "version_id": 3, + "field_exists": false, + "size": 4 + }, + { + "field": "channels", + "version_id": 2, + "field_exists": false, + "size": 56, + "Description": { + "name": "pit channel", + "version_id": 2, + "minimum_version_id": 2, + "Fields": [ + { + "field": "count", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "latched_count", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "count_latched", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "status_latched", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "status", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "read_state", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "write_state", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "write_latch", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "rw_mode", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "mode", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "bcd", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "gate", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "count_load_time", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "next_transition_time", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + }, + { + "field": "channels[0].next_transition_time", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + }, + "mch": { + "Name": "mch", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "mch", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "parent_obj", + "version_id": 0, + "field_exists": false, + "size": 2608, + "Description": { + "name": "PCIDevice", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "version_id", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "config", + "version_id": 0, + "field_exists": true, + "size": 256 + }, + { + "field": "config", + "version_id": 0, + "field_exists": true, + "size": 4096 + }, + { + "field": "irq_state", + "version_id": 2, + "field_exists": false, + "size": 16 + } + ] + } + }, + { + "field": "unused", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + "pic-common": { + "Name": "pic-common", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "i8259", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "last_irr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "irr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "imr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "isr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "priority_add", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "irq_base", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "read_reg_select", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "poll", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "special_mask", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "init_state", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "auto_eoi", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "rotate_on_auto_eoi", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "special_fully_nested_mode", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "init4", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "single_mode", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "elcr", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + "ICH9-LPC": { + "Name": "ICH9-LPC", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "ICH9LPC", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "d", + "version_id": 0, + "field_exists": false, + "size": 2608, + "Description": { + "name": "PCIDevice", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "version_id", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "config", + "version_id": 0, + "field_exists": true, + "size": 256 + }, + { + "field": "config", + "version_id": 0, + "field_exists": true, + "size": 4096 + }, + { + "field": "irq_state", + "version_id": 2, + "field_exists": false, + "size": 16 + } + ] + } + }, + { + "field": "apm", + "version_id": 0, + "field_exists": false, + "size": 304, + "Description": { + "name": "APM State", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "apmc", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "apms", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + { + "field": "pm", + "version_id": 0, + "field_exists": false, + "size": 6480, + "Description": { + "name": "ich9_pm", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "acpi_regs.pm1.evt.sts", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "acpi_regs.pm1.evt.en", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "acpi_regs.pm1.cnt.cnt", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "acpi_regs.tmr.timer", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "acpi_regs.tmr.overflow_time", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "acpi_regs.gpe.sts", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "acpi_regs.gpe.en", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "smi_en", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "smi_sts", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "ich9_pm/memhp", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "acpi_memory_hotplug", + "version_id": 1, + "field_exists": false, + "size": 304, + "Description": { + "name": "memory hotplug state", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "selector", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "devs", + "version_id": 0, + "field_exists": false, + "size": 24, + "Description": { + "name": "memory hotplug device state", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "is_enabled", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "is_inserting", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "ost_event", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "ost_status", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + } + ] + } + } + ] + }, + { + "name": "ich9_pm/tco", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "tco_regs", + "version_id": 1, + "field_exists": false, + "size": 320, + "Description": { + "name": "tco io device status", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "tco.rld", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "tco.din", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "tco.dout", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "tco.sts1", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "tco.sts2", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "tco.cnt1", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "tco.cnt2", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "tco.msg1", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "tco.msg2", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "tco.wdcnt", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "tco.tmr", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "sw_irq_gen", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "tco_timer", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "expire_time", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "timeouts_no", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + } + ] + }, + { + "name": "ich9_pm/cpuhp", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "cpuhp_state", + "version_id": 1, + "field_exists": false, + "size": 304, + "Description": { + "name": "CPU hotplug state", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "selector", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "command", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "devs", + "version_id": 0, + "field_exists": false, + "size": 32, + "Description": { + "name": "CPU hotplug device state", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "is_inserting", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "is_removing", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "ost_event", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "ost_status", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + } + ] + } + } + ] + }, + { + "name": "ich9_pm/pcihp", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "acpi_pci_hotplug.hotplug_select", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "acpi_pci_hotplug.acpi_pcihp_pci_status", + "version_id": 1, + "field_exists": false, + "size": 12, + "Description": { + "name": "acpi_pcihp_pci_status", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "up", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "down", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + { + "field": "acpi_pci_hotplug.acpi_index", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + }, + { + "field": "chip_config", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "sci_level", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "ICH9LPC/rst_cnt", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "rst_cnt", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + }, + { + "name": "ICH9LPC/smi_feat", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "smi_guest_features_le", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "smi_features_ok", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "smi_negotiated_features", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + }, + "pit-common": { + "Name": "pit-common", + "version_id": 3, + "minimum_version_id": 2, + "Description": { + "name": "i8254", + "version_id": 3, + "minimum_version_id": 2, + "Fields": [ + { + "field": "channels[0].irq_disabled", + "version_id": 3, + "field_exists": false, + "size": 4 + }, + { + "field": "channels", + "version_id": 2, + "field_exists": false, + "size": 56, + "Description": { + "name": "pit channel", + "version_id": 2, + "minimum_version_id": 2, + "Fields": [ + { + "field": "count", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "latched_count", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "count_latched", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "status_latched", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "status", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "read_state", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "write_state", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "write_latch", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "rw_mode", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "mode", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "bcd", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "gate", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "count_load_time", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "next_transition_time", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + }, + { + "field": "channels[0].next_transition_time", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + }, + "vmgenid": { + "Name": "vmgenid", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "vmgenid", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "vmgenid_addr_le", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + "ioapic-common": { + "Name": "ioapic-common", + "version_id": 3, + "minimum_version_id": 1, + "Description": { + "name": "ioapic", + "version_id": 3, + "minimum_version_id": 1, + "Fields": [ + { + "field": "id", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "ioregsel", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "unused", + "version_id": 2, + "field_exists": false, + "size": 8 + }, + { + "field": "irr", + "version_id": 2, + "field_exists": false, + "size": 4 + }, + { + "field": "ioredtbl", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + }, + "isa-i8259": { + "Name": "isa-i8259", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "i8259", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "last_irr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "irr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "imr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "isr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "priority_add", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "irq_base", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "read_reg_select", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "poll", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "special_mask", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "init_state", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "auto_eoi", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "rotate_on_auto_eoi", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "special_fully_nested_mode", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "init4", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "single_mode", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "elcr", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + "cfi.pflash01": { + "Name": "cfi.pflash01", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "pflash_cfi01", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "wcycle", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "cmd", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "status", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "counter", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ], + "Subsections": [ + { + "name": "pflash_cfi01_blk_write", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "blk_bytes", + "version_id": 0, + "field_exists": false, + "size": 0 + }, + { + "field": "blk_offset", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + }, + "i8042-mmio": { + "Name": "i8042-mmio", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "pckbd-mmio", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "kbd", + "version_id": 0, + "field_exists": false, + "size": 2328, + "Description": { + "name": "pckbd", + "version_id": 3, + "minimum_version_id": 3, + "Fields": [ + { + "field": "write_cmd", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "status", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "mode", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "pending_tmp", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ], + "Subsections": [ + { + "name": "pckbd_outport", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "outport", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + }, + { + "name": "pckbd/extended_state", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "migration_flags", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "obsrc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "obdata", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "cbdata", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + ] + } + } + ] + } + }, + "isa-serial": { + "Name": "isa-serial", + "version_id": 3, + "minimum_version_id": 2, + "Description": { + "name": "serial", + "version_id": 3, + "minimum_version_id": 2, + "Fields": [ + { + "field": "state", + "version_id": 0, + "field_exists": false, + "size": 656, + "Description": { + "name": "serial", + "version_id": 3, + "minimum_version_id": 2, + "Fields": [ + { + "field": "divider", + "version_id": 2, + "field_exists": false, + "size": 2 + }, + { + "field": "rbr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "ier", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "iir", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "lcr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "mcr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "lsr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "msr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "scr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "fcr_vmstate", + "version_id": 3, + "field_exists": false, + "size": 1 + } + ], + "Subsections": [ + { + "name": "serial/thr_ipending", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "thr_ipending", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "serial/tsr", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "tsr_retry", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "thr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "tsr", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + }, + { + "name": "serial/recv_fifo", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "recv_fifo", + "version_id": 1, + "field_exists": false, + "size": 24, + "Description": { + "name": "Fifo8", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "data", + "version_id": 1, + "field_exists": false, + "size": 0 + }, + { + "field": "head", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "num", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + } + ] + }, + { + "name": "serial/xmit_fifo", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "xmit_fifo", + "version_id": 1, + "field_exists": false, + "size": 24, + "Description": { + "name": "Fifo8", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "data", + "version_id": 1, + "field_exists": false, + "size": 0 + }, + { + "field": "head", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "num", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + } + ] + }, + { + "name": "serial/fifo_timeout_timer", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "fifo_timeout_timer", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "serial/timeout_ipending", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "timeout_ipending", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "serial/poll", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "poll_msl", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "modem_status_poll", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + } + ] + } + }, + "ps2-mouse": { + "Name": "ps2-mouse", + "version_id": 2, + "minimum_version_id": 2, + "Description": { + "name": "ps2mouse", + "version_id": 2, + "minimum_version_id": 2, + "Fields": [ + { + "field": "parent_obj", + "version_id": 0, + "field_exists": false, + "size": 1104, + "Description": { + "name": "PS2 Common State", + "version_id": 3, + "minimum_version_id": 2, + "Fields": [ + { + "field": "write_cmd", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "queue.rptr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "queue.wptr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "queue.count", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "queue.data", + "version_id": 0, + "field_exists": false, + "size": 256 + } + ] + } + }, + { + "field": "mouse_status", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "mouse_resolution", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "mouse_sample_rate", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "mouse_wrap", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "mouse_type", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "mouse_detect_state", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "mouse_dx", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "mouse_dy", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "mouse_dz", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "mouse_buttons", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + "kvmvapic": { + "Name": "kvmvapic", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "kvm-tpr-opt", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "rom_state", + "version_id": 0, + "field_exists": false, + "size": 124, + "Description": { + "name": "kvmvapic-guest-rom", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "unused", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "vaddr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "fixup_start", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "fixup_end", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "vapic_vaddr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "vapic_size", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "vcpu_shift", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "real_tpr_addr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "up", + "version_id": 0, + "field_exists": false, + "size": 44, + "Description": { + "name": "kvmvapic-handlers", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "set_tpr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "set_tpr_eax", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "get_tpr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "get_tpr_stack", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + { + "field": "mp", + "version_id": 0, + "field_exists": false, + "size": 44, + "Description": { + "name": "kvmvapic-handlers", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "set_tpr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "set_tpr_eax", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "get_tpr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "get_tpr_stack", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + } + ] + } + }, + { + "field": "state", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "real_tpr_addr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "rom_state_vaddr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "vapic_paddr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "rom_state_paddr", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + "ich9-ahci": { + "Name": "ich9-ahci", + "version_id": 1, + "minimum_version_id": 0, + "Description": { + "name": "ich9_ahci", + "version_id": 1, + "minimum_version_id": 0, + "Fields": [ + { + "field": "parent_obj", + "version_id": 0, + "field_exists": false, + "size": 2608, + "Description": { + "name": "PCIDevice", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "version_id", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "config", + "version_id": 0, + "field_exists": true, + "size": 256 + }, + { + "field": "config", + "version_id": 0, + "field_exists": true, + "size": 4096 + }, + { + "field": "irq_state", + "version_id": 2, + "field_exists": false, + "size": 16 + } + ] + } + }, + { + "field": "ahci", + "version_id": 0, + "field_exists": false, + "size": 624, + "Description": { + "name": "ahci", + "version_id": 1, + "minimum_version_id": 0, + "Fields": [ + { + "field": "dev", + "version_id": 0, + "field_exists": false, + "size": 6088, + "Description": { + "name": "ahci port", + "version_id": 1, + "minimum_version_id": 0, + "Fields": [ + { + "field": "port", + "version_id": 1, + "field_exists": false, + "size": 2304, + "Description": { + "name": "ide_bus", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "cmd", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "unit", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ], + "Subsections": [ + { + "name": "ide_bus/error", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "error_status", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "retry_sector_num", + "version_id": 2, + "field_exists": false, + "size": 8 + }, + { + "field": "retry_nsector", + "version_id": 2, + "field_exists": false, + "size": 4 + }, + { + "field": "retry_unit", + "version_id": 2, + "field_exists": false, + "size": 1 + } + ] + } + ] + } + }, + { + "field": "port.ifs[0]", + "version_id": 1, + "field_exists": false, + "size": 984, + "Description": { + "name": "ide_drive", + "version_id": 3, + "minimum_version_id": 0, + "Fields": [ + { + "field": "mult_sectors", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "identify_set", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "identify_data", + "version_id": 0, + "field_exists": true, + "size": 512 + }, + { + "field": "feature", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "error", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "nsector", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "sector", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "lcyl", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "hcyl", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "hob_feature", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "hob_sector", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "hob_nsector", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "hob_lcyl", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "hob_hcyl", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "select", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "status", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "lba48", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "sense_key", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "asc", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "cdrom_changed", + "version_id": 3, + "field_exists": false, + "size": 1 + } + ], + "Subsections": [ + { + "name": "ide_drive/pio_state", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "req_nb_sectors", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "io_buffer", + "version_id": 1, + "field_exists": false, + "size": 1 + }, + { + "field": "cur_io_buffer_offset", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "cur_io_buffer_len", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "end_transfer_fn_idx", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "elementary_transfer_size", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "packet_transfer_size", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "ide_drive/tray_state", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "tray_open", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "tray_locked", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + }, + { + "name": "ide_drive/atapi/gesn_state", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "events.new_media", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "events.eject_request", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + ] + } + }, + { + "field": "port_state", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "finished", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "port_regs.lst_addr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "port_regs.lst_addr_hi", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "port_regs.fis_addr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "port_regs.fis_addr_hi", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "port_regs.irq_stat", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "port_regs.irq_mask", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "port_regs.cmd", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "port_regs.tfdata", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "port_regs.sig", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "port_regs.scr_stat", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "port_regs.scr_ctl", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "port_regs.scr_err", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "port_regs.scr_act", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "port_regs.cmd_issue", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "done_first_drq", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "busy_slot", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "init_d2h_sent", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "ncq_tfs", + "version_id": 1, + "field_exists": false, + "size": 112, + "Description": { + "name": "ncq state", + "version_id": 1, + "minimum_version_id": 0, + "Fields": [ + { + "field": "sector_count", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "lba", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "tag", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "cmd", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "slot", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "used", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "halt", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + } + ] + } + }, + { + "field": "control_regs.cap", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "control_regs.ghc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "control_regs.irqstatus", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "control_regs.impl", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "control_regs.version", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "idp_index", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "ports", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + } + ] + } + }, + "i8042": { + "Name": "i8042", + "version_id": 3, + "minimum_version_id": 3, + "Description": { + "name": "pckbd", + "version_id": 3, + "minimum_version_id": 3, + "Fields": [ + { + "field": "kbd", + "version_id": 0, + "field_exists": false, + "size": 2328, + "Description": { + "name": "pckbd", + "version_id": 3, + "minimum_version_id": 3, + "Fields": [ + { + "field": "write_cmd", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "status", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "mode", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "pending_tmp", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ], + "Subsections": [ + { + "name": "pckbd_outport", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "outport", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + }, + { + "name": "pckbd/extended_state", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "migration_flags", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "obsrc", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "obdata", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "cbdata", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + ] + } + } + ] + } + }, + "fw_cfg_io": { + "Name": "fw_cfg_io", + "version_id": 2, + "minimum_version_id": 1, + "Description": { + "name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "cur_entry", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "cur_offset", + "version_id": 0, + "field_exists": true, + "size": 4 + }, + { + "field": "cur_offset", + "version_id": 2, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "fw_cfg/dma", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "dma_addr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "fw_cfg/acpi_mr", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "table_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "linker_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "rsdp_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + }, + "vmcoreinfo": { + "Name": "vmcoreinfo", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "vmcoreinfo", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "has_vmcoreinfo", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "vmcoreinfo.host_format", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "vmcoreinfo.guest_format", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "vmcoreinfo.size", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "vmcoreinfo.paddr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + }, + "ps2-kbd": { + "Name": "ps2-kbd", + "version_id": 3, + "minimum_version_id": 2, + "Description": { + "name": "ps2kbd", + "version_id": 3, + "minimum_version_id": 2, + "Fields": [ + { + "field": "parent_obj", + "version_id": 0, + "field_exists": false, + "size": 1104, + "Description": { + "name": "PS2 Common State", + "version_id": 3, + "minimum_version_id": 2, + "Fields": [ + { + "field": "write_cmd", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "queue.rptr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "queue.wptr", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "queue.count", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "queue.data", + "version_id": 0, + "field_exists": false, + "size": 256 + } + ] + } + }, + { + "field": "scan_enabled", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "translate", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "scancode_set", + "version_id": 3, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "ps2kbd/ledstate", + "version_id": 3, + "minimum_version_id": 2, + "Fields": [ + { + "field": "ledstate", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + }, + { + "name": "ps2kbd/need_high_bit", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "need_high_bit", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + }, + { + "name": "ps2kbd/command_reply_queue", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "parent_obj.queue.cwptr", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + ] + } + }, + "ICH9-SMB": { + "Name": "ICH9-SMB", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "ich9_smb", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "dev", + "version_id": 0, + "field_exists": false, + "size": 2608, + "Description": { + "name": "PCIDevice", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "version_id", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "config", + "version_id": 0, + "field_exists": true, + "size": 256 + }, + { + "field": "config", + "version_id": 0, + "field_exists": true, + "size": 4096 + }, + { + "field": "irq_state", + "version_id": 2, + "field_exists": false, + "size": 16 + } + ] + } + }, + { + "field": "irq_enabled", + "version_id": 0, + "field_exists": true, + "size": 1 + }, + { + "field": "smb", + "version_id": 1, + "field_exists": true, + "size": 384, + "Description": { + "name": "pmsmb", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "smb_stat", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "smb_ctl", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "smb_cmd", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "smb_addr", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "smb_data0", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "smb_data1", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "smb_index", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "smb_data", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "smb_auxctl", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "smb_blkdata", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "i2c_enable", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "op_done", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "in_i2c_block_read", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "start_transaction_on_status_read", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + } + ] + } + }, + "isa-pcspk": { + "Name": "isa-pcspk", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "pcspk", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "data_on", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "dummy_refresh_clock", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + "acpi-erst": { + "Name": "acpi-erst", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "acpi-erst", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "operation", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "busy_status", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "command_status", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "record_offset", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "reg_action", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "reg_value", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "record_identifier", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "next_record_index", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + "i8257": { + "Name": "i8257", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "dma", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "command", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "mask", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "flip_flop", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "dshift", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "regs", + "version_id": 1, + "field_exists": false, + "size": 40, + "Description": { + "name": "dma_regs", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "now", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "base", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "mode", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "page", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "pageh", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "dack", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "eop", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + } + ] + } + }, + "port92": { + "Name": "port92", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "port92", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "outport", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + "q35-pcihost": { + "Name": "q35-pcihost", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "PCIHost", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "config_reg", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + "ioapic": { + "Name": "ioapic", + "version_id": 3, + "minimum_version_id": 1, + "Description": { + "name": "ioapic", + "version_id": 3, + "minimum_version_id": 1, + "Fields": [ + { + "field": "id", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "ioregsel", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "unused", + "version_id": 2, + "field_exists": false, + "size": 8 + }, + { + "field": "irr", + "version_id": 2, + "field_exists": false, + "size": 4 + }, + { + "field": "ioredtbl", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + }, + "smbus-eeprom": { + "Name": "smbus-eeprom", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "smbus-eeprom", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "smbusdev", + "version_id": 0, + "field_exists": false, + "size": 216, + "Description": { + "name": "smbus-device", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "i2c", + "version_id": 0, + "field_exists": false, + "size": 168, + "Description": { + "name": "I2CSlave", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "address", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + { + "field": "mode", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "data_len", + "version_id": 0, + "field_exists": false, + "size": 4 + }, + { + "field": "data_buf", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + { + "field": "data", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "offset", + "version_id": 0, + "field_exists": false, + "size": 1 + }, + { + "field": "accessed", + "version_id": 0, + "field_exists": false, + "size": 1 + } + ] + } + }, + "pci-host-bridge": { + "Name": "pci-host-bridge", + "version_id": 1, + "minimum_version_id": 1, + "Description": { + "name": "PCIHost", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "config_reg", + "version_id": 0, + "field_exists": false, + "size": 4 + } + ] + } + }, + "fw_cfg_mem": { + "Name": "fw_cfg_mem", + "version_id": 2, + "minimum_version_id": 1, + "Description": { + "name": "fw_cfg", + "version_id": 2, + "minimum_version_id": 1, + "Fields": [ + { + "field": "cur_entry", + "version_id": 0, + "field_exists": false, + "size": 2 + }, + { + "field": "cur_offset", + "version_id": 0, + "field_exists": true, + "size": 4 + }, + { + "field": "cur_offset", + "version_id": 2, + "field_exists": false, + "size": 4 + } + ], + "Subsections": [ + { + "name": "fw_cfg/dma", + "version_id": 0, + "minimum_version_id": 0, + "Fields": [ + { + "field": "dma_addr", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + }, + { + "name": "fw_cfg/acpi_mr", + "version_id": 1, + "minimum_version_id": 1, + "Fields": [ + { + "field": "table_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "linker_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + }, + { + "field": "rsdp_mr_size", + "version_id": 0, + "field_exists": false, + "size": 8 + } + ] + } + ] + } + } +} From 5383ccf2512f4dc48e6321545f0f946d6bc78aa1 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 12 Sep 2025 12:07:55 +0200 Subject: [PATCH 0829/1794] tests/functional: Use vmstate-static-checker.py to test data from v7.2 We've got this nice vmstate-static-checker.py script that can help to detect screw-ups in the migration states. Unfortunately, it's currently only run manually, which can be cumbersome. Let's run it from a functional test automatically with the reference data from QEMU 7.2, so that we get at least a basic coverage here. Since the test can fail when the checker script detects a false positive, mark the test with a skipFlakyTest decorator for now, so that it is only run when the user also set the QEMU_TEST_FLAKY_TESTS environment variable. Acked-by: Fabiano Rosas Signed-off-by: Thomas Huth Message-ID: <20250912100755.316518-5-thuth@redhat.com> --- MAINTAINERS | 1 + tests/functional/aarch64/meson.build | 1 + tests/functional/generic/test_vmstate.py | 67 ++++++++++++++++++++++++ tests/functional/m68k/meson.build | 4 ++ tests/functional/ppc64/meson.build | 1 + tests/functional/s390x/meson.build | 4 ++ tests/functional/x86_64/meson.build | 3 +- 7 files changed, 80 insertions(+), 1 deletion(-) create mode 100755 tests/functional/generic/test_vmstate.py diff --git a/MAINTAINERS b/MAINTAINERS index 24c061aff35ea..3d1f88a4bbe12 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3613,6 +3613,7 @@ F: migration/ F: scripts/vmstate-static-checker.py F: tests/functional/migration.py F: tests/functional/*/*migration.py +F: tests/functional/generic/test_vmstate.py F: tests/functional/x86_64/test_bad_vmstate.py F: tests/data/vmstate-static-checker/ F: tests/qtest/migration/ diff --git a/tests/functional/aarch64/meson.build b/tests/functional/aarch64/meson.build index 04846c6eb1802..5ad52f93e1d74 100644 --- a/tests/functional/aarch64/meson.build +++ b/tests/functional/aarch64/meson.build @@ -19,6 +19,7 @@ test_aarch64_timeouts = { tests_aarch64_system_quick = [ 'migration', + 'vmstate', ] tests_aarch64_system_thorough = [ diff --git a/tests/functional/generic/test_vmstate.py b/tests/functional/generic/test_vmstate.py new file mode 100755 index 0000000000000..387ff5424265a --- /dev/null +++ b/tests/functional/generic/test_vmstate.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +'''This test runs the vmstate-static-checker script with the current QEMU''' + +import subprocess + +from qemu_test import QemuSystemTest, skipFlakyTest + + +@skipFlakyTest("vmstate-static-checker can produce false positives") +class VmStateTest(QemuSystemTest): + ''' + This test helps to check whether there are problems between old + reference data and the current QEMU + ''' + + def test_vmstate_7_2(self): + '''Check reference data from QEMU v7.2''' + + target_machine = { + 'aarch64': 'virt-7.2', + 'm68k': 'virt-7.2', + 'ppc64': 'pseries-7.2', + 's390x': 's390-ccw-virtio-7.2', + 'x86_64': 'pc-q35-7.2', + } + self.set_machine(target_machine[self.arch]) + + # Run QEMU to get the current vmstate json file: + dst_json = self.scratch_file('dest.json') + self.log.info('Dumping vmstate from %s', self.qemu_bin) + cp = subprocess.run([self.qemu_bin, '-nodefaults', + '-M', target_machine[self.arch], + '-dump-vmstate', dst_json], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, check=True) + if cp.stdout: + self.log.info('QEMU output: %s', cp.stdout) + + # Check whether the old vmstate json file is still compatible: + src_json = self.data_file('..', 'data', 'vmstate-static-checker', + self.arch, + target_machine[self.arch] + '.json') + self.log.info('Comparing vmstate with %s', src_json) + checkerscript = self.data_file('..', '..', 'scripts', + 'vmstate-static-checker.py') + cp = subprocess.run([checkerscript, '-s', src_json, '-d', dst_json], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, check=False) + if cp.returncode != 0: + self.fail('Running vmstate-static-checker failed:\n' + cp.stdout + + '\nThis either means that there is a migration bug ' + 'that needs to be fixed, or\nvmstate-static-checker.py ' + 'needs to be improved (e.g. extend the changed_names\n' + 'in case a field has been renamed), or drop the ' + 'problematic field from\n' + src_json + + '\nin case the script cannot be fixed easily.') + if cp.stdout: + self.log.warning('vmstate-static-checker output: %s', cp.stdout) + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/m68k/meson.build b/tests/functional/m68k/meson.build index e29044a6d7354..679faaf86d69e 100644 --- a/tests/functional/m68k/meson.build +++ b/tests/functional/m68k/meson.build @@ -1,5 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-or-later +tests_m68k_system_quick = [ + 'vmstate', +] + tests_m68k_system_thorough = [ 'mcf5208evb', 'nextcube', diff --git a/tests/functional/ppc64/meson.build b/tests/functional/ppc64/meson.build index 842fe0fc715f6..1fa0a70f7ed88 100644 --- a/tests/functional/ppc64/meson.build +++ b/tests/functional/ppc64/meson.build @@ -11,6 +11,7 @@ test_ppc64_timeouts = { tests_ppc64_system_quick = [ 'migration', + 'vmstate', ] tests_ppc64_system_thorough = [ diff --git a/tests/functional/s390x/meson.build b/tests/functional/s390x/meson.build index 030b116039cad..70cd36e2913d8 100644 --- a/tests/functional/s390x/meson.build +++ b/tests/functional/s390x/meson.build @@ -4,6 +4,10 @@ test_s390x_timeouts = { 'ccw_virtio' : 420, } +tests_s390x_system_quick = [ + 'vmstate', +] + tests_s390x_system_thorough = [ 'ccw_virtio', 'pxelinux', diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build index ef12ac43b370d..967426c30c3cf 100644 --- a/tests/functional/x86_64/meson.build +++ b/tests/functional/x86_64/meson.build @@ -14,10 +14,11 @@ tests_x86_64_system_quick = [ 'cpu_model_versions', 'cpu_queries', 'mem_addr_space', + 'memlock', 'migration', 'pc_cpu_hotplug_props', 'virtio_version', - 'memlock', + 'vmstate', ] tests_x86_64_system_thorough = [ From 8a44d8c2ac0921c8064fbfd00ef28e3a2588918e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 12 Sep 2025 19:22:00 +0100 Subject: [PATCH 0830/1794] tests/functional: use self.log for all logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some tests are creating their on logger category which means any output they log is not captured in base.log. Signed-off-by: Daniel P. Berrangé [thuth: drop changes to reverse_debugging.py (it's WIP in other patches)] Message-ID: <20250912182200.643909-7-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/aarch64/test_virt.py | 4 +-- tests/functional/arm/test_integratorcp.py | 3 +-- tests/functional/mips64el/test_malta.py | 3 +-- tests/functional/replay_kernel.py | 16 +++++------- tests/functional/x86_64/test_acpi_bits.py | 31 +++++++++++------------ 5 files changed, 25 insertions(+), 32 deletions(-) diff --git a/tests/functional/aarch64/test_virt.py b/tests/functional/aarch64/test_virt.py index 4d0ad90ff8928..63071f9b51752 100755 --- a/tests/functional/aarch64/test_virt.py +++ b/tests/functional/aarch64/test_virt.py @@ -72,8 +72,6 @@ def common_aarch64_virt(self, machine): self.set_machine('virt') self.require_accelerator("tcg") - logger = logging.getLogger('aarch64_virt') - kernel_path = self.ASSET_KERNEL.fetch() self.vm.set_console() @@ -91,7 +89,7 @@ def common_aarch64_virt(self, machine): 'rng-random,id=rng0,filename=/dev/urandom') # Also add a scratch block device - logger.info('creating scratch qcow2 image') + self.log.info('creating scratch qcow2 image') image_path = self.scratch_file('scratch.qcow2') qemu_img = get_qemu_img(self) check_call([qemu_img, 'create', '-f', 'qcow2', image_path, '8M'], diff --git a/tests/functional/arm/test_integratorcp.py b/tests/functional/arm/test_integratorcp.py index 4f00924aa0327..23ae919359d54 100755 --- a/tests/functional/arm/test_integratorcp.py +++ b/tests/functional/arm/test_integratorcp.py @@ -77,7 +77,6 @@ def test_framebuffer_tux_logo(self): command_line='screendump %s' % screendump_path) if 'unknown command' in res: self.skipTest('screendump not available') - logger = logging.getLogger('framebuffer') cpu_count = 1 match_threshold = 0.92 @@ -88,7 +87,7 @@ def test_framebuffer_tux_logo(self): loc = np.where(result >= match_threshold) tux_count = 0 for tux_count, pt in enumerate(zip(*loc[::-1]), start=1): - logger.debug('found Tux at position [x, y] = %s', pt) + self.log.debug('found Tux at position [x, y] = %s', pt) self.assertGreaterEqual(tux_count, cpu_count) if __name__ == '__main__': diff --git a/tests/functional/mips64el/test_malta.py b/tests/functional/mips64el/test_malta.py index 8fdc49b300562..170147bfcc206 100755 --- a/tests/functional/mips64el/test_malta.py +++ b/tests/functional/mips64el/test_malta.py @@ -159,7 +159,6 @@ def do_test_i6400_framebuffer_logo(self, cpu_cores_count): command_line='screendump %s' % screendump_path) if 'unknown command' in res: self.skipTest('screendump not available') - logger = logging.getLogger('framebuffer') match_threshold = 0.95 screendump_bgr = cv2.imread(screendump_path, cv2.IMREAD_COLOR) @@ -171,7 +170,7 @@ def do_test_i6400_framebuffer_logo(self, cpu_cores_count): h, w = tuxlogo_bgr.shape[:2] debug_png = os.getenv('QEMU_TEST_CV2_SCREENDUMP_PNG_PATH') for tuxlogo_count, pt in enumerate(zip(*loc[::-1]), start=1): - logger.debug('found Tux at position (x, y) = %s', pt) + self.log.debug('found Tux at position (x, y) = %s', pt) cv2.rectangle(screendump_bgr, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2) if debug_png: diff --git a/tests/functional/replay_kernel.py b/tests/functional/replay_kernel.py index 80795eb0520e9..acb1d29a1b525 100644 --- a/tests/functional/replay_kernel.py +++ b/tests/functional/replay_kernel.py @@ -32,15 +32,14 @@ def run_vm(self, kernel_path, kernel_command_line, console_pattern, # icount requires TCG to be available self.require_accelerator('tcg') - logger = logging.getLogger('replay') start_time = time.time() vm = self.get_vm(name='recording' if record else 'replay') vm.set_console() if record: - logger.info('recording the execution...') + self.log.info('recording the execution...') mode = 'record' else: - logger.info('replaying the execution...') + self.log.info('replaying the execution...') mode = 'replay' vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s' % (shift, mode, replay_path), @@ -54,15 +53,15 @@ def run_vm(self, kernel_path, kernel_command_line, console_pattern, self.wait_for_console_pattern(console_pattern, vm) if record: vm.shutdown() - logger.info('finished the recording with log size %s bytes' + self.log.info('finished the recording with log size %s bytes' % os.path.getsize(replay_path)) self.run_replay_dump(replay_path) - logger.info('successfully tested replay-dump.py') + self.log.info('successfully tested replay-dump.py') else: vm.wait() - logger.info('successfully finished the replay') + self.log.info('successfully finished the replay') elapsed = time.time() - start_time - logger.info('elapsed time %.2f sec' % elapsed) + self.log.info('elapsed time %.2f sec' % elapsed) return elapsed def run_replay_dump(self, replay_path): @@ -80,5 +79,4 @@ def run_rr(self, kernel_path, kernel_command_line, console_pattern, True, shift, args, replay_path) t2 = self.run_vm(kernel_path, kernel_command_line, console_pattern, False, shift, args, replay_path) - logger = logging.getLogger('replay') - logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1)) + self.log.info('replay overhead {:.2%}'.format(t2 / t1 - 1)) diff --git a/tests/functional/x86_64/test_acpi_bits.py b/tests/functional/x86_64/test_acpi_bits.py index 8e0563a97b18c..9a2816533d6a9 100755 --- a/tests/functional/x86_64/test_acpi_bits.py +++ b/tests/functional/x86_64/test_acpi_bits.py @@ -121,10 +121,10 @@ def __init__(self, *args, **kwargs): self._debugcon_log = 'debugcon-log.txt' def _print_log(self, log): - self.logger.info('\nlogs from biosbits follows:') - self.logger.info('==========================================\n') - self.logger.info(log) - self.logger.info('==========================================\n') + self.log.info('\nlogs from biosbits follows:') + self.log.info('==========================================\n') + self.log.info(log) + self.log.info('==========================================\n') def copy_bits_config(self): """ copies the bios bits config file into bits. @@ -138,8 +138,8 @@ def copy_bits_config(self): self.assertTrue(os.path.exists(bits_config_file)) self.assertTrue(os.path.exists(target_config_dir)) shutil.copy2(bits_config_file, target_config_dir) - self.logger.info('copied config file %s to %s', - bits_config_file, target_config_dir) + self.log.info('copied config file %s to %s', + bits_config_file, target_config_dir) def copy_test_scripts(self): """copies the python test scripts into bits. """ @@ -163,8 +163,8 @@ def copy_test_scripts(self): newfilename = os.path.splitext(filename)[0] + '.py' shutil.copy2(os.path.join(bits_test_dir, filename), os.path.join(target_test_dir, newfilename)) - self.logger.info('copied test file %s to %s', - filename, target_test_dir) + self.log.info('copied test file %s to %s', + filename, target_test_dir) # now remove the pyc test file if it exists, otherwise the # changes in the python test script won't be executed. @@ -172,9 +172,9 @@ def copy_test_scripts(self): if os.access(os.path.join(target_test_dir, testfile_pyc), os.F_OK): os.remove(os.path.join(target_test_dir, testfile_pyc)) - self.logger.info('removed compiled file %s', - os.path.join(target_test_dir, - testfile_pyc)) + self.log.info('removed compiled file %s', + os.path.join(target_test_dir, + testfile_pyc)) def fix_mkrescue(self, mkrescue): """ grub-mkrescue is a bash script with two variables, 'prefix' and @@ -216,7 +216,7 @@ def generate_bits_iso(self): self.fix_mkrescue(mkrescue_script) - self.logger.info('using grub-mkrescue for generating biosbits iso ...') + self.log.info('using grub-mkrescue for generating biosbits iso ...') try: if os.getenv('V') or os.getenv('BITS_DEBUG'): @@ -225,7 +225,7 @@ def generate_bits_iso(self): stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True) - self.logger.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], @@ -238,11 +238,10 @@ def generate_bits_iso(self): self.assertTrue(os.access(iso_file, os.R_OK)) - self.logger.info('iso file %s successfully generated.', iso_file) + self.log.info('iso file %s successfully generated.', iso_file) def setUp(self): # pylint: disable=arguments-differ super().setUp() - self.logger = self.log prebuiltDir = self.scratch_file('prebuilt') if not os.path.isdir(prebuiltDir): @@ -333,7 +332,7 @@ def test_acpi_smbios_bits(self): # in batch mode and then automatically initiate a vm shutdown. self._vm.event_wait('SHUTDOWN', timeout=BITS_TIMEOUT) self._vm.wait(timeout=None) - self.logger.debug("Checking console output ...") + self.log.debug("Checking console output ...") self.parse_log() if __name__ == '__main__': From fd34f56fe886250bdd64f9c222c1cb4c07a594ad Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 16 Sep 2025 17:30:30 +0100 Subject: [PATCH 0831/1794] .gitlab-ci.d/buildtest.yml: Unset CI_COMMIT_DESCRIPTION for htags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 52a21689cd829 we added a workaround for a bug in older versions of htags where they fail with a weird error message if the environment is too large. However, we missed one variable which gitlab CI can set to the body of the commit message: CI_COMMIT_DESCRIPTION. Add this to the variables we unset when running htags, so that the 'pages' job doesn't fail if the most recent commit happens to have a very large commit message. Cc: qemu-stable@nongnu.org Fixes: 52a21689cd8 (".gitlab-ci.d/buildtest.yml: Work around htags bug when environment is large") Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Daniel P. Berrangé Message-ID: <20250916163030.1467893-1-peter.maydell@linaro.org> Signed-off-by: Thomas Huth --- .gitlab-ci.d/buildtest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index f01978fb40cfe..e296fc3c14ae2 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -736,7 +736,7 @@ pages: - make gtags # We unset variables to work around a bug in some htags versions # which causes it to fail when the environment is large - - CI_COMMIT_MESSAGE= CI_COMMIT_TAG_MESSAGE= htags + - CI_COMMIT_MESSAGE= CI_COMMIT_TAG_MESSAGE= CI_COMMIT_DESCRIPTION= htags -anT --tree-view=filetree -m qemu_init -t "Welcome to the QEMU sourcecode" - mv HTML public/src From 464a0a584c8cf8183b0d61167a0234a3a7c9aa6f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 18 Sep 2025 14:24:47 +0200 Subject: [PATCH 0832/1794] tests/functional/hppa: Add a CD-ROM boot test for qemu-system-hppa MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test which boots a HP-UX firmware upgrade CD-ROM. It exercise the PCI LSI53C895A SCSI controller. The ISO image comes from: https://web.archive.org/web/20101204061612/http://ftp.parisc-linux.org/kernels/712/PF_C7120023 The test is very quick, less than 3s. Based on an old patch from Philippe that has been posted here: https://www.mail-archive.com/qemu-devel@nongnu.org/msg651012.html Suggested-by: Sven Schnelle Signed-off-by: Philippe Mathieu-Daudé [thuth: Adjusted the patch to the functional framework, and adjusted the commit message] Message-ID: <20250918122447.105861-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- MAINTAINERS | 2 +- tests/functional/hppa/meson.build | 4 +++ tests/functional/hppa/test_cdboot.py | 38 ++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100755 tests/functional/hppa/test_cdboot.py diff --git a/MAINTAINERS b/MAINTAINERS index 3d1f88a4bbe12..24b71a4fc54d8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1276,7 +1276,7 @@ F: include/hw/pci-host/astro.h F: include/hw/pci-host/dino.h F: pc-bios/hppa-firmware.img F: roms/seabios-hppa/ -F: tests/functional/hppa/test_seabios.py +F: tests/functional/hppa/ LoongArch Machines ------------------ diff --git a/tests/functional/hppa/meson.build b/tests/functional/hppa/meson.build index a334837088409..df2f7ccc9c3fd 100644 --- a/tests/functional/hppa/meson.build +++ b/tests/functional/hppa/meson.build @@ -3,3 +3,7 @@ tests_hppa_system_quick = [ 'seabios', ] + +tests_hppa_system_thorough = [ + 'cdboot', +] diff --git a/tests/functional/hppa/test_cdboot.py b/tests/functional/hppa/test_cdboot.py new file mode 100755 index 0000000000000..84421e8d63e17 --- /dev/null +++ b/tests/functional/hppa/test_cdboot.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# +# CD boot test for HPPA machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern + + +class HppaCdBoot(QemuSystemTest): + + ASSET_CD = Asset( + ('https://github.com/philmd/qemu-testing-blob/raw/ec1b741/' + 'hppa/hp9000/712/C7120023.frm'), + '32c612ad2074516986bdc27768903c561fa92af2ca48e5ac3f3359ade1c42f70') + + def test_cdboot(self): + self.set_machine('B160L') + cdrom_path = self.ASSET_CD.fetch() + + self.vm.set_console() + self.vm.add_args('-cdrom', cdrom_path, + '-boot', 'd', + '-no-reboot') + self.vm.launch() + wait_for_console_pattern(self, 'Unrecognized MODEL TYPE = 502') + wait_for_console_pattern(self, 'UPDATE PAUSED>') + + exec_command_and_wait_for_pattern(self, 'exit\r', 'UPDATE>') + exec_command_and_wait_for_pattern(self, 'ls\r', 'IMAGE1B') + wait_for_console_pattern(self, 'UPDATE>') + exec_command_and_wait_for_pattern(self, 'exit\r', + 'THIS UTILITY WILL NOW RESET THE SYSTEM.....') + + +if __name__ == '__main__': + QemuSystemTest.main() From 4f1ebc7712a7db61155080164f2169320d033559 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 18 Sep 2025 14:51:54 +0200 Subject: [PATCH 0833/1794] tests: Fix "make check-functional" for targets without thorough tests If QEMU gets configured for a single target that does not have any thorough functional tests, "make check-functional" currently fails with the error message "No rule to make target 'check-func'". This happens because "check-func" only gets defined for thorough tests (quick ones get added to "check-func-quick" instead). The same problem can happen with the quick tests for targets that do not have any functional test at all. To fix it, simply make sure that the targets are always available in the Makefile. Reported-by: Peter Maydell Closes: https://gitlab.com/qemu-project/qemu/-/issues/3119 Signed-off-by: Thomas Huth Message-ID: <20250918125154.126072-1-thuth@redhat.com> --- tests/Makefile.include | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Makefile.include b/tests/Makefile.include index 3538c0c7407b8..62a4fc8ed3126 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -113,6 +113,9 @@ check-functional: @$(NINJA) precache-functional @QEMU_TEST_NO_DOWNLOAD=1 $(MAKE) SPEED=thorough check-func check-func-quick +.PHONY: check-func check-func-quick +check-func check-func-quick: + # Consolidated targets .PHONY: check check-clean From bcc251800b9e60c541de860dc8a1ced70622ee43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 18 Sep 2025 13:57:45 +0100 Subject: [PATCH 0834/1794] tests/functional: retry when seeing ConnectionError exception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This base class is used for many different socket connection errors, corresponding to ECONNRESET, ECONNREFUSED, ECONNABORTED and more. Most of these are things you might expect to see every now and then as transient flaws. We should thus retry the asset download when seeing them. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20250918125746.1165658-2-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/asset.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index 2dd32bf28d957..f8b87d21538aa 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -179,6 +179,13 @@ def fetch(self): self.url, e.reason) raise AssetError(self, "Unable to download: URL error %s" % e.reason, transient=True) + except ConnectionError as e: + # A socket connection failure, such as dropped conn + # or refused conn + tmp_cache_file.unlink() + self.log.error("Unable to download %s: Connection error %s", + self.url, e) + continue except Exception as e: tmp_cache_file.unlink() raise AssetError(self, "Unable to download: %s" % e) From 097bbfc5e0ba889ce17106ef941a56111c3de270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 18 Sep 2025 13:57:46 +0100 Subject: [PATCH 0835/1794] tests/functional: treat unknown exceptions as transient faults MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To maximise the robustness of the functional tests we want to treat most asset download failures as non-fatal to the test suite. Instead it should just skip the tests which need that particular asset. The only time aim to make it fatal is for 404 errors which are highly likely to reflect genuine problems to be fixed. We catch certain exception classes and handle them as transient errors, but unfortunately it is proving difficult to predict what exception classes urlopen() is capable of raising, with new possibilities being discovered. To provide a fail-safe, treat the generic Exception class as being a transient error too. This may well mask certain genuine bugs, but it is preferrable to prioritize running the test suite to the greatest extent practical. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20250918125746.1165658-3-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/asset.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index f8b87d21538aa..2971a989d1ec8 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -188,7 +188,8 @@ def fetch(self): continue except Exception as e: tmp_cache_file.unlink() - raise AssetError(self, "Unable to download: %s" % e) + raise AssetError(self, "Unable to download: %s" % e, + transient=True) if not os.path.exists(tmp_cache_file): raise AssetError(self, "Download retries exceeded", transient=True) From 62e82053fc676e9493dc2c2072352634b8cd655d Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 24 Sep 2025 13:37:20 +0900 Subject: [PATCH 0836/1794] docs/devel: Do not unparent in instance_finalize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Children are automatically unparented so manually unparenting is unnecessary. Worse, automatic unparenting happens before the instance_finalize() callback of the parent gets called, so object_unparent() calls in the callback will refer to objects that are already unparented, which is semantically incorrect. Remove the instruction to call object_unparent(), and the exception of the "do not call object_unparent()" rule for instance_finalize(). Signed-off-by: Akihiko Odaki Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/r/20250924-use-v4-1-07c6c598f53d@rsg.ci.i.u-tokyo.ac.jp Signed-off-by: Paolo Bonzini --- docs/devel/memory.rst | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/docs/devel/memory.rst b/docs/devel/memory.rst index 42d3ca29c431e..f22146e56ce55 100644 --- a/docs/devel/memory.rst +++ b/docs/devel/memory.rst @@ -165,17 +165,14 @@ and finalized one by one. The order in which memory regions will be finalized is not guaranteed. If however the memory region is part of a dynamically allocated data -structure, you should call object_unparent() to destroy the memory region -before the data structure is freed. For an example see VFIOMSIXInfo -and VFIOQuirk in hw/vfio/pci.c. +structure, you should free the memory region in the instance_finalize +callback. For an example see VFIOMSIXInfo and VFIOQuirk in +hw/vfio/pci.c. You must not destroy a memory region as long as it may be in use by a device or CPU. In order to do this, as a general rule do not create or -destroy memory regions dynamically during a device's lifetime, and only -call object_unparent() in the memory region owner's instance_finalize -callback. The dynamically allocated data structure that contains the -memory region then should obviously be freed in the instance_finalize -callback as well. +destroy memory regions dynamically during a device's lifetime, and never +call object_unparent(). If you break this rule, the following situation can happen: @@ -201,9 +198,7 @@ this exception is rarely necessary, and therefore it is discouraged, but nevertheless it is used in a few places. For regions that "have no owner" (NULL is passed at creation time), the -machine object is actually used as the owner. Since instance_finalize is -never called for the machine object, you must never call object_unparent -on regions that have no owner, unless they are aliases or containers. +machine object is actually used as the owner. Overlapping regions and priority From e3ed862cabce6d8a12300b941243cb44e9cd40d1 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 24 Sep 2025 13:37:21 +0900 Subject: [PATCH 0837/1794] vfio/pci: Do not unparent in instance_finalize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Children are automatically unparented so manually unparenting is unnecessary. Worse, automatic unparenting happens before the insntance_finalize() callback of the parent gets called, so object_unparent() calls in the callback will refer to objects that are already unparented, which is semantically incorrect. Signed-off-by: Akihiko Odaki Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/r/20250924-use-v4-2-07c6c598f53d@rsg.ci.i.u-tokyo.ac.jp Signed-off-by: Paolo Bonzini --- hw/vfio/pci.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index d14e96b2f82d7..bc0b4c4d562bf 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2025,7 +2025,6 @@ static void vfio_bars_finalize(VFIOPCIDevice *vdev) vfio_region_finalize(&bar->region); if (bar->mr) { assert(bar->size); - object_unparent(OBJECT(bar->mr)); g_free(bar->mr); bar->mr = NULL; } @@ -2033,9 +2032,6 @@ static void vfio_bars_finalize(VFIOPCIDevice *vdev) if (vdev->vga) { vfio_vga_quirk_finalize(vdev); - for (i = 0; i < ARRAY_SIZE(vdev->vga->region); i++) { - object_unparent(OBJECT(&vdev->vga->region[i].mem)); - } g_free(vdev->vga); } } From f9ce08489424905ae4a70218fa35013fe88d54ed Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 24 Sep 2025 13:37:22 +0900 Subject: [PATCH 0838/1794] hw/core/register: Do not unparent in instance_finalize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Children are automatically unparented so manually unparenting is unnecessary. Worse, automatic unparenting happens before the instance_finalize() callback of the parent gets called, so object_unparent() calls in the callback will refer to objects that are already unparented, which is semantically incorrect. Signed-off-by: Akihiko Odaki Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/r/20250924-use-v4-3-07c6c598f53d@rsg.ci.i.u-tokyo.ac.jp Signed-off-by: Paolo Bonzini --- hw/core/register.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/core/register.c b/hw/core/register.c index 8f63d9f227c45..3340df70b06e7 100644 --- a/hw/core/register.c +++ b/hw/core/register.c @@ -314,7 +314,6 @@ RegisterInfoArray *register_init_block64(DeviceState *owner, void register_finalize_block(RegisterInfoArray *r_array) { - object_unparent(OBJECT(&r_array->mem)); g_free(r_array->r); g_free(r_array); } From 15d91385422eb42321f49144554439c52b18953a Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 24 Sep 2025 13:37:23 +0900 Subject: [PATCH 0839/1794] hv-balloon: hw/core/register: Do not unparent in instance_finalize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Children are automatically unparented so manually unparenting is unnecessary. Worse, automatic unparenting happens before the instance_finalize() callback of the parent gets called, so object_unparent() calls in the callback will refer to objects that are already unparented, which is semantically incorrect. Signed-off-by: Akihiko Odaki Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/r/20250924-use-v4-4-07c6c598f53d@rsg.ci.i.u-tokyo.ac.jp Signed-off-by: Paolo Bonzini --- hw/hyperv/hv-balloon.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/hw/hyperv/hv-balloon.c b/hw/hyperv/hv-balloon.c index 6dbcb2d9a29dc..2d6d7db4ee0e5 100644 --- a/hw/hyperv/hv-balloon.c +++ b/hw/hyperv/hv-balloon.c @@ -1475,16 +1475,6 @@ static void hv_balloon_ensure_mr(HvBalloon *balloon) balloon->mr->align = memory_region_get_alignment(hostmem_mr); } -static void hv_balloon_free_mr(HvBalloon *balloon) -{ - if (!balloon->mr) { - return; - } - - object_unparent(OBJECT(balloon->mr)); - g_clear_pointer(&balloon->mr, g_free); -} - static void hv_balloon_vmdev_realize(VMBusDevice *vdev, Error **errp) { ERRP_GUARD(); @@ -1580,7 +1570,7 @@ static void hv_balloon_vmdev_reset(VMBusDevice *vdev) */ static void hv_balloon_unrealize_finalize_common(HvBalloon *balloon) { - hv_balloon_free_mr(balloon); + g_clear_pointer(&balloon->mr, g_free); balloon->addr = 0; balloon->memslot_count = 0; From 0c6d897e38f5a6a864d6ae6dc42f521b84011107 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 24 Sep 2025 13:37:24 +0900 Subject: [PATCH 0840/1794] hw/sd/sdhci: Do not unparent in instance_finalize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Children are automatically unparented so manually unparenting is unnecessary. Worse, automatic unparenting happens before the instance_finalize() callback of the parent gets called, so object_unparent() calls in the callback will refer to objects that are already unparented, which is semantically incorrect. Signed-off-by: Akihiko Odaki Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/r/20250924-use-v4-5-07c6c598f53d@rsg.ci.i.u-tokyo.ac.jp Signed-off-by: Paolo Bonzini --- hw/sd/sdhci.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 3c897e54b7210..89b595ce4a5a7 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -1578,10 +1578,6 @@ static void sdhci_sysbus_finalize(Object *obj) { SDHCIState *s = SYSBUS_SDHCI(obj); - if (s->dma_mr) { - object_unparent(OBJECT(s->dma_mr)); - } - sdhci_uninitfn(s); } From 9b80f8a8e758b427501e6bcbcb114ae45ff68387 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 24 Sep 2025 13:37:25 +0900 Subject: [PATCH 0841/1794] vfio: Do not unparent in instance_finalize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Children are automatically unparented so manually unparenting is unnecessary. Worse, automatic unparenting happens before the instance_finalize() callback of the parent gets called, so object_unparent() calls in the callback will refer to objects that are already unparented, which is semantically incorrect. Signed-off-by: Akihiko Odaki Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/r/20250924-use-v4-6-07c6c598f53d@rsg.ci.i.u-tokyo.ac.jp Signed-off-by: Paolo Bonzini --- hw/vfio/pci-quirks.c | 9 +-------- hw/vfio/region.c | 3 --- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index c97606dbf194d..b5da6afbf5b00 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1159,15 +1159,12 @@ void vfio_vga_quirk_exit(VFIOPCIDevice *vdev) void vfio_vga_quirk_finalize(VFIOPCIDevice *vdev) { - int i, j; + int i; for (i = 0; i < ARRAY_SIZE(vdev->vga->region); i++) { while (!QLIST_EMPTY(&vdev->vga->region[i].quirks)) { VFIOQuirk *quirk = QLIST_FIRST(&vdev->vga->region[i].quirks); QLIST_REMOVE(quirk, next); - for (j = 0; j < quirk->nr_mem; j++) { - object_unparent(OBJECT(&quirk->mem[j])); - } g_free(quirk->mem); g_free(quirk->data); g_free(quirk); @@ -1207,14 +1204,10 @@ void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr) void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr) { VFIOBAR *bar = &vdev->bars[nr]; - int i; while (!QLIST_EMPTY(&bar->quirks)) { VFIOQuirk *quirk = QLIST_FIRST(&bar->quirks); QLIST_REMOVE(quirk, next); - for (i = 0; i < quirk->nr_mem; i++) { - object_unparent(OBJECT(&quirk->mem[i])); - } g_free(quirk->mem); g_free(quirk->data); g_free(quirk); diff --git a/hw/vfio/region.c b/hw/vfio/region.c index d04c57db630f3..b165ab0b9378c 100644 --- a/hw/vfio/region.c +++ b/hw/vfio/region.c @@ -365,12 +365,9 @@ void vfio_region_finalize(VFIORegion *region) for (i = 0; i < region->nr_mmaps; i++) { if (region->mmaps[i].mmap) { munmap(region->mmaps[i].mmap, region->mmaps[i].size); - object_unparent(OBJECT(®ion->mmaps[i].mem)); } } - object_unparent(OBJECT(region->mem)); - g_free(region->mem); g_free(region->mmaps); From 46a8624fa7d116989389867f534b07ee562ebb1e Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 24 Sep 2025 13:37:26 +0900 Subject: [PATCH 0842/1794] hw/xen: Do not unparent in instance_finalize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Children are automatically unparented so manually unparenting is unnecessary. Worse, automatic unparenting happens before the instance_finalize() callback of the parent gets called, so object_unparent() calls in the callback will refer to objects that are already unparented, which is semantically incorrect. Signed-off-by: Akihiko Odaki Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/r/20250924-use-v4-7-07c6c598f53d@rsg.ci.i.u-tokyo.ac.jp Signed-off-by: Paolo Bonzini --- hw/xen/xen_pt_msi.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c index 09cca4eecb1c0..e9ba17317abaf 100644 --- a/hw/xen/xen_pt_msi.c +++ b/hw/xen/xen_pt_msi.c @@ -637,14 +637,5 @@ void xen_pt_msix_unmap(XenPCIPassthroughState *s) void xen_pt_msix_delete(XenPCIPassthroughState *s) { - XenPTMSIX *msix = s->msix; - - if (!msix) { - return; - } - - object_unparent(OBJECT(&msix->mmio)); - - g_free(s->msix); - s->msix = NULL; + g_clear_pointer(&s->msix, g_free); } From 1f64df25477eff72779b000588cb821c654f26b7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 22 Sep 2025 12:45:21 +0200 Subject: [PATCH 0843/1794] docs/code-provenance: clarify scope very early MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AI policy in QEMU is not about content generators, it is about generated content. Other uses are explicitly not covered. Rename the policy and clarify its scope in the TL;DR section, as a matter of convenience to the reader. Reviewed-by: Daniel P. Berrangé Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini --- docs/devel/code-provenance.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/devel/code-provenance.rst b/docs/devel/code-provenance.rst index b5aae2e2532ee..dc3524ac177e3 100644 --- a/docs/devel/code-provenance.rst +++ b/docs/devel/code-provenance.rst @@ -285,8 +285,8 @@ Such tools are acceptable to use, provided there is clearly defined copyright and licensing for their output. Note in particular the caveats applying to AI content generators below. -Use of AI content generators -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Use of AI-generated content +~~~~~~~~~~~~~~~~~~~~~~~~~~~ TL;DR: @@ -294,6 +294,10 @@ TL;DR: believed to include or derive from AI generated content. This includes ChatGPT, Claude, Copilot, Llama and similar tools.** + **This policy does not apply to other uses of AI, such as researching APIs + or algorithms, static analysis, or debugging, provided their output is not + included in contributions.** + The increasing prevalence of AI-assisted software development results in a number of difficult legal questions and risks for software projects, including QEMU. Of particular concern is content generated by `Large Language Models @@ -322,10 +326,6 @@ The QEMU project thus requires that contributors refrain from using AI content generators on patches intended to be submitted to the project, and will decline any contribution if use of AI is either known or suspected. -This policy does not apply to other uses of AI, such as researching APIs or -algorithms, static analysis, or debugging, provided their output is not to be -included in contributions. - Examples of tools impacted by this policy includes GitHub's CoPilot, OpenAI's ChatGPT, Anthropic's Claude, and Meta's Code Llama, and code/content generation agents which are built on top of such tools. From 9ceb9b42c8f0f7ea6a4975e67405cfebee249ba8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 22 Sep 2025 12:51:37 +0200 Subject: [PATCH 0844/1794] docs/code-provenance: make the exception process more prominent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU's AI generated content policy does not flesh out the exception process yet. Do it, while at the same time keeping things informal: ask contributors to explain what they would like to use AI for, and let them reach a consensus with the project on why it is credible to claim DCO compliance in that specific scenario. In other words, exceptions do not "solve the AI copyright problem". They take a position that a reasonable contributor could have, and assert that we're comfortable with the argument. Suggested-by: Daniel P. Berrangé Signed-off-by: Paolo Bonzini --- docs/devel/code-provenance.rst | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/devel/code-provenance.rst b/docs/devel/code-provenance.rst index dc3524ac177e3..6968d54be8a5e 100644 --- a/docs/devel/code-provenance.rst +++ b/docs/devel/code-provenance.rst @@ -331,8 +331,13 @@ ChatGPT, Anthropic's Claude, and Meta's Code Llama, and code/content generation agents which are built on top of such tools. This policy may evolve as AI tools mature and the legal situation is -clarifed. In the meanwhile, requests for exceptions to this policy will be -evaluated by the QEMU project on a case by case basis. To be granted an -exception, a contributor will need to demonstrate clarity of the license and -copyright status for the tool's output in relation to its training model and -code, to the satisfaction of the project maintainers. +clarified. + +Exceptions +^^^^^^^^^^ + +The QEMU project welcomes discussion on any exceptions to this policy, +or more general revisions. This can be done by contacting the qemu-devel +mailing list with details of a proposed tool, model, usage scenario, etc. +that is beneficial to QEMU, while still mitigating issues around compliance +with the DCO. After discussion, any exception will be listed below. From cd64320e1e27168d3796a847bbfde66c8b1116f9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 22 Sep 2025 13:14:01 +0200 Subject: [PATCH 0845/1794] docs/code-provenance: AI exceptions are in addition to DCO Using phrasing from https://openinfra.org/legal/ai-policy (with just "commit" replaced by "submission", because we do not submit changes as commits but rather emails), clarify that the contributor remains responsible for its copyright or license status. [This is not my preferred phrasing. I would prefer something lighter like "the "Signed-off-by" label in the contribution gives the author responsibility". But for the sake of not reinventing the wheel I am keeping the exact words from the OpenInfra policy.] Reviewed-by: Peter Maydell Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini --- docs/devel/code-provenance.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/devel/code-provenance.rst b/docs/devel/code-provenance.rst index 6968d54be8a5e..8cdc56f6649a7 100644 --- a/docs/devel/code-provenance.rst +++ b/docs/devel/code-provenance.rst @@ -341,3 +341,9 @@ or more general revisions. This can be done by contacting the qemu-devel mailing list with details of a proposed tool, model, usage scenario, etc. that is beneficial to QEMU, while still mitigating issues around compliance with the DCO. After discussion, any exception will be listed below. + +Exceptions do not remove the need for authors to comply with all other +requirements for contribution. In particular, the "Signed-off-by" +label in a patch submission is a statement that the author takes +responsibility for the entire contents of the patch, including any parts +that were generated or assisted by AI tools or other tools. From 03fe6659803f83690b8587d01f8ee56bb4be4b90 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 23 Sep 2025 16:02:03 -0700 Subject: [PATCH 0846/1794] accel/tcg: Properly unlink a TB linked to itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we remove dest from orig's links, we lose the link that we rely on later to reset links. This can lead to failure to release from spinlock with self-modifying code. Cc: qemu-stable@nongnu.org Reported-by: 李威威 Signed-off-by: Richard Henderson Reviewed-by: Anton Johansson Tested-by: Anton Johansson --- accel/tcg/tb-maint.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index 0048316f99a77..e6d45c9c12702 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -836,6 +836,14 @@ static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig) * We first acquired the lock, and since the destination pointer matches, * we know for sure that @orig is in the jmp list. */ + if (dest == orig) { + /* + * In the case of a TB that links to itself, removing the entry + * from the list means that it won't be present later during + * tb_jmp_unlink -- unlink now. + */ + tb_reset_jump(orig, n_orig); + } pprev = &dest->jmp_list_head; TB_FOR_EACH_JMP(dest, tb, n) { if (tb == orig && n == n_orig) { From e13e1195db8af18e149065a59351ea85215645bb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 24 Sep 2025 08:42:37 -0700 Subject: [PATCH 0847/1794] tests/tcg/multiarch: Add tb-link test Signed-off-by: Richard Henderson --- tests/tcg/multiarch/Makefile.target | 2 + tests/tcg/multiarch/tb-link.c | 67 +++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 tests/tcg/multiarch/tb-link.c diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index 8dc65d7a064e9..f5b4d2b81381c 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -46,6 +46,8 @@ vma-pthread: LDFLAGS+=-pthread sigreturn-sigmask: CFLAGS+=-pthread sigreturn-sigmask: LDFLAGS+=-pthread +tb-link: LDFLAGS+=-lpthread + # GCC versions 12/13/14/15 at least incorrectly complain about # "'SHA1Transform' reading 64 bytes from a region of size 0"; see the gcc bug # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106709 diff --git a/tests/tcg/multiarch/tb-link.c b/tests/tcg/multiarch/tb-link.c new file mode 100644 index 0000000000000..4e40306fa18c3 --- /dev/null +++ b/tests/tcg/multiarch/tb-link.c @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Verify that a single TB spin-loop is properly invalidated, + * releasing the thread from the spin-loop. + */ + +#include +#include +#include +#include +#include +#include +#include + + +#ifdef __x86_64__ +#define READY 0x000047c6 /* movb $0,0(%rdi) */ +#define LOOP 0xfceb9090 /* 1: nop*2; jmp 1b */ +#define RETURN 0x909090c3 /* ret; nop*3 */ +#define NOP 0x90909090 /* nop*4 */ +#elif defined(__aarch64__) +#define READY 0x3900001f /* strb wzr,[x0] */ +#define LOOP 0x14000000 /* b . */ +#define RETURN 0xd65f03c0 /* ret */ +#define NOP 0xd503201f /* nop */ +#elif defined(__riscv) +#define READY 0x00050023 /* sb zero, (a0) */ +#define LOOP 0x0000006f /* jal zero, #0 */ +#define RETURN 0x00008067 /* jalr zero, ra, 0 */ +#define NOP 0x00000013 /* nop */ +#endif + + +int main() +{ +#ifdef READY + int tmp; + pthread_t thread_id; + bool hold = true; + uint32_t *buf; + + buf = mmap(NULL, 3 * sizeof(uint32_t), + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + assert(buf != MAP_FAILED); + + buf[0] = READY; + buf[1] = LOOP; + buf[2] = RETURN; + + alarm(2); + + tmp = pthread_create(&thread_id, NULL, (void *(*)(void *))buf, &hold); + assert(tmp == 0); + + while (hold) { + sched_yield(); + } + + buf[1] = NOP; + __builtin___clear_cache(&buf[1], &buf[2]); + + tmp = pthread_join(thread_id, NULL); + assert(tmp == 0); +#endif + return 0; +} From dae1e0df49d429e76efcc0505e2519c988cf14b4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Sep 2025 17:42:54 +0200 Subject: [PATCH 0848/1794] gdbstub: Remove tb_flush uses This hasn't been needed since d828b92b8a6 ("accel/tcg: Introduce CF_BP_PAGE"). Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- gdbstub/system.c | 5 ----- gdbstub/user.c | 3 --- 2 files changed, 8 deletions(-) diff --git a/gdbstub/system.c b/gdbstub/system.c index 5be0d3c58cec5..5221c579d90b2 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -18,13 +18,11 @@ #include "gdbstub/syscalls.h" #include "gdbstub/commands.h" #include "exec/hwaddr.h" -#include "exec/tb-flush.h" #include "accel/accel-ops.h" #include "accel/accel-cpu-ops.h" #include "system/cpus.h" #include "system/runstate.h" #include "system/replay.h" -#include "system/tcg.h" #include "hw/core/cpu.h" #include "hw/cpu/cluster.h" #include "hw/boards.h" @@ -174,9 +172,6 @@ static void gdb_vm_state_change(void *opaque, bool running, RunState state) } else { trace_gdbstub_hit_break(); } - if (tcg_enabled()) { - tb_flush(cpu); - } ret = GDB_SIGNAL_TRAP; break; case RUN_STATE_PAUSED: diff --git a/gdbstub/user.c b/gdbstub/user.c index 67403e5a252a6..2e14ded3f0105 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -15,7 +15,6 @@ #include "qemu/sockets.h" #include "qapi/error.h" #include "exec/hwaddr.h" -#include "exec/tb-flush.h" #include "exec/gdbstub.h" #include "gdbstub/commands.h" #include "gdbstub/syscalls.h" @@ -220,7 +219,6 @@ int gdb_handlesig(CPUState *cpu, int sig, const char *reason, void *siginfo, /* disable single step if it was enabled */ cpu_single_step(cpu, 0); - tb_flush(cpu); if (sig != 0) { gdb_set_stop_cpu(cpu); @@ -539,7 +537,6 @@ static void disable_gdbstub(CPUState *thread_cpu) /* no cpu_watchpoint_remove_all for user-mode */ cpu_single_step(cpu, 0); } - tb_flush(thread_cpu); } void gdbserver_fork_end(CPUState *cpu, pid_t pid) From 521c9e1b1adcbca90d1d84e1dc00ec63f17256ee Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Sep 2025 18:43:30 +0200 Subject: [PATCH 0849/1794] target/alpha: Simplify call_pal implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since 288a5fe980f, we don't link translation blocks directly to palcode entry points. If we load palbr from env instead of encoding the constant, we avoid all need for tb_flush(). Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/alpha/cpu_loop.c | 5 ----- target/alpha/helper.h | 1 - target/alpha/sys_helper.c | 6 ------ target/alpha/translate.c | 21 ++++++--------------- 4 files changed, 6 insertions(+), 27 deletions(-) diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c index bb8346b509456..f93597c400db1 100644 --- a/linux-user/alpha/cpu_loop.c +++ b/linux-user/alpha/cpu_loop.c @@ -94,11 +94,6 @@ void cpu_loop(CPUAlphaState *env) break; case 0x86: /* IMB */ - /* ??? We can probably elide the code using page_unprotect - that is checking for self-modifying code. Instead we - could simply call tb_flush here. Until we work out the - changes required to turn off the extra write protection, - this can be a no-op. */ break; case 0x9E: /* RDUNIQUE */ diff --git a/target/alpha/helper.h b/target/alpha/helper.h index d60f208703163..788d2fbf289b9 100644 --- a/target/alpha/helper.h +++ b/target/alpha/helper.h @@ -90,7 +90,6 @@ DEF_HELPER_FLAGS_2(ieee_input_s, TCG_CALL_NO_WG, void, env, i64) #if !defined (CONFIG_USER_ONLY) DEF_HELPER_FLAGS_1(tbia, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(tbis, TCG_CALL_NO_RWG, void, env, i64) -DEF_HELPER_FLAGS_1(tb_flush, TCG_CALL_NO_RWG, void, env) DEF_HELPER_1(halt, void, i64) diff --git a/target/alpha/sys_helper.c b/target/alpha/sys_helper.c index 51e32544287a8..87e37605c133f 100644 --- a/target/alpha/sys_helper.c +++ b/target/alpha/sys_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/cputlb.h" -#include "exec/tb-flush.h" #include "exec/helper-proto.h" #include "system/runstate.h" #include "system/system.h" @@ -38,11 +37,6 @@ void helper_tbis(CPUAlphaState *env, uint64_t p) tlb_flush_page(env_cpu(env), p); } -void helper_tb_flush(CPUAlphaState *env) -{ - tb_flush(env_cpu(env)); -} - void helper_halt(uint64_t restart) { if (restart) { diff --git a/target/alpha/translate.c b/target/alpha/translate.c index cebab0318cfff..f11b382438c2e 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -48,8 +48,6 @@ struct DisasContext { #ifdef CONFIG_USER_ONLY MemOp unalign; -#else - uint64_t palbr; #endif uint32_t tbflags; int mem_idx; @@ -1155,7 +1153,6 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode) #else { TCGv tmp = tcg_temp_new(); - uint64_t entry; gen_pc_disp(ctx, tmp, 0); if (ctx->tbflags & ENV_FLAG_PAL_MODE) { @@ -1165,12 +1162,11 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode) } tcg_gen_st_i64(tmp, tcg_env, offsetof(CPUAlphaState, exc_addr)); - entry = ctx->palbr; - entry += (palcode & 0x80 - ? 0x2000 + (palcode - 0x80) * 64 - : 0x1000 + palcode * 64); - - tcg_gen_movi_i64(cpu_pc, entry); + tcg_gen_ld_i64(cpu_pc, tcg_env, offsetof(CPUAlphaState, palbr)); + tcg_gen_addi_i64(cpu_pc, cpu_pc, + palcode & 0x80 + ? 0x2000 + (palcode - 0x80) * 64 + : 0x1000 + palcode * 64); return DISAS_PC_UPDATED; } #endif @@ -1292,11 +1288,7 @@ static DisasJumpType gen_mtpr(DisasContext *ctx, TCGv vb, int regno) case 7: /* PALBR */ tcg_gen_st_i64(vb, tcg_env, offsetof(CPUAlphaState, palbr)); - /* Changing the PAL base register implies un-chaining all of the TBs - that ended with a CALL_PAL. Since the base register usually only - changes during boot, flushing everything works well. */ - gen_helper_tb_flush(tcg_env); - return DISAS_PC_STALE; + break; case 32 ... 39: /* Accessing the "non-shadow" general registers. */ @@ -2874,7 +2866,6 @@ static void alpha_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) ctx->ir = cpu_std_ir; ctx->unalign = (ctx->tbflags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN); #else - ctx->palbr = env->palbr; ctx->ir = (ctx->tbflags & ENV_FLAG_PAL_MODE ? cpu_pal_ir : cpu_std_ir); #endif From 55b490b58fbfb4079186f05a1a63da1633239cbf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Sep 2025 18:53:50 +0200 Subject: [PATCH 0850/1794] target/riscv: Record misa_ext in TCGTBCPUState.cs_base The tb_flush within write_misa was incorrect. It assumed that we could adjust the ISA of the current processor and discard all TB and all would be well. But MISA is per vcpu, so globally flushing TB does not mean that the TB matches the MISA of any given vcpu. By recording misa in the tb state, we ensure that the code generated matches the vcpu. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/riscv/csr.c | 3 --- target/riscv/tcg/tcg-cpu.c | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 8842e07a735a5..3c8989f522e8f 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -25,7 +25,6 @@ #include "pmu.h" #include "time_helper.h" #include "exec/cputlb.h" -#include "exec/tb-flush.h" #include "exec/icount.h" #include "accel/tcg/getpc.h" #include "qemu/guest-random.h" @@ -2173,8 +2172,6 @@ static RISCVException write_misa(CPURISCVState *env, int csrno, env->mstatus &= ~MSTATUS_FS; } - /* flush translation cache */ - tb_flush(env_cpu(env)); env->xl = riscv_cpu_mxl(env); return RISCV_EXCP_NONE; } diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 78fb2791847e3..143ab079d49bf 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -191,7 +191,8 @@ static TCGTBCPUState riscv_get_tb_cpu_state(CPUState *cs) return (TCGTBCPUState){ .pc = env->xl == MXL_RV32 ? env->pc & UINT32_MAX : env->pc, - .flags = flags + .flags = flags, + .cs_base = env->misa_ext, }; } From 9d61f61b4fa4dc4cd65fca8b24a9cb70c0c61a88 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 6 Sep 2025 06:35:36 +0200 Subject: [PATCH 0851/1794] hw/ppc/spapr: Use tb_invalidate_phys_range in h_page_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only need invalidate tbs from a single page, not flush all translations. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Signed-off-by: Richard Henderson --- hw/ppc/spapr_hcall.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index c594d4b916eef..8c1e0a4817b17 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -8,7 +8,7 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/error-report.h" -#include "exec/tb-flush.h" +#include "exec/translation-block.h" #include "exec/target_page.h" #include "helper_regs.h" #include "hw/ppc/ppc.h" @@ -301,7 +301,7 @@ static target_ulong h_page_init(PowerPCCPU *cpu, SpaprMachineState *spapr, if (kvm_enabled()) { kvmppc_icbi_range(cpu, pdst, len); } else if (tcg_enabled()) { - tb_flush(CPU(cpu)); + tb_invalidate_phys_range(CPU(cpu), dst, dst + len - 1); } else { g_assert_not_reached(); } From b773c149a80b11449899b6a4ad3672250d4fcb0f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Sep 2025 17:57:14 +0200 Subject: [PATCH 0852/1794] accel/tcg: Split out tb_flush__exclusive_or_serial Expose a routine to be called when no cpus are running. Simplify the do_tb_flush run_on_cpu callback, because that is explicitly called with start_exclusive; there is no need for the mmap_lock as well. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/tb-maint.c | 39 +++++++++++++++++++++++++-------------- include/exec/tb-flush.h | 15 +++++++++++++++ 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index e6d45c9c12702..b4566a81d76d2 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -36,6 +36,9 @@ #include "internal-common.h" #ifdef CONFIG_USER_ONLY #include "user/page-protection.h" +#define runstate_is_running() true +#else +#include "system/runstate.h" #endif @@ -88,7 +91,10 @@ static IntervalTreeRoot tb_root; static void tb_remove_all(void) { - assert_memory_lock(); + /* + * Only called from tb_flush__exclusive_or_serial, where we have already + * asserted that we're in an exclusive state. + */ memset(&tb_root, 0, sizeof(tb_root)); } @@ -756,17 +762,19 @@ static void tb_remove(TranslationBlock *tb) } #endif /* CONFIG_USER_ONLY */ -/* flush all the translation blocks */ -static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) +/* + * Flush all the translation blocks. + * Must be called from a context in which no cpus are running, + * e.g. start_exclusive() or vm_stop(). + */ +void tb_flush__exclusive_or_serial(void) { - bool did_flush = false; + CPUState *cpu; - mmap_lock(); - /* If it is already been done on request of another CPU, just retry. */ - if (tb_ctx.tb_flush_count != tb_flush_count.host_int) { - goto done; - } - did_flush = true; + assert(tcg_enabled()); + /* Note that cpu_in_serial_context checks cpu_in_exclusive_context. */ + assert(!runstate_is_running() || + (current_cpu && cpu_in_serial_context(current_cpu))); CPU_FOREACH(cpu) { tcg_flush_jmp_cache(cpu); @@ -778,11 +786,14 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) tcg_region_reset_all(); /* XXX: flush processor icache at this point if cache flush is expensive */ qatomic_inc(&tb_ctx.tb_flush_count); + qemu_plugin_flush_cb(); +} -done: - mmap_unlock(); - if (did_flush) { - qemu_plugin_flush_cb(); +static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) +{ + /* If it is already been done on request of another CPU, just retry. */ + if (tb_ctx.tb_flush_count == tb_flush_count.host_int) { + tb_flush__exclusive_or_serial(); } } diff --git a/include/exec/tb-flush.h b/include/exec/tb-flush.h index 142c240d94c31..090ffc8818b0e 100644 --- a/include/exec/tb-flush.h +++ b/include/exec/tb-flush.h @@ -8,6 +8,21 @@ #ifndef _TB_FLUSH_H_ #define _TB_FLUSH_H_ +/** + * tb_flush__exclusive_or_serial() + * + * Used to flush all the translation blocks in the system. Mostly this is + * used to empty the code generation buffer after it is full. Sometimes it + * is used when it is simpler to flush everything than work out which + * individual translations are now invalid. + * + * Must be called from an exclusive or serial context, e.g. start_exclusive, + * vm_stop, or when there is only one vcpu. Note that start_exclusive cannot + * be called from within the cpu run loop, so this cannot be called from + * within target code. + */ +void tb_flush__exclusive_or_serial(void); + /** * tb_flush() - flush all translation blocks * @cs: CPUState (must be valid, but treated as anonymous pointer) From 84142184899322c2dca931e36abca7a35a2827cd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 6 Sep 2025 06:09:44 +0200 Subject: [PATCH 0853/1794] accel/tcg: Move post-load tb_flush to vm_change_state hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need not call tb_flush once per cpu, only once per vmload. By moving the call from cpu_common_post_load to a tcg-specific vm_change_state_handler, we do even better than that: we only flush when called from HMP triggered loadvm, when we had old state to flush. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/tcg-all.c | 21 +++++++++++++++++++++ hw/core/cpu-system.c | 9 --------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 5125e1a4e27d9..18ea0c58b0b74 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -38,6 +38,8 @@ #include "qemu/target-info.h" #ifndef CONFIG_USER_ONLY #include "hw/boards.h" +#include "exec/tb-flush.h" +#include "system/runstate.h" #endif #include "accel/accel-ops.h" #include "accel/accel-cpu-ops.h" @@ -82,6 +84,23 @@ static void tcg_accel_instance_init(Object *obj) bool one_insn_per_tb; +#ifndef CONFIG_USER_ONLY +static void tcg_vm_change_state(void *opaque, bool running, RunState state) +{ + if (state == RUN_STATE_RESTORE_VM) { + /* + * loadvm will update the content of RAM, bypassing the usual + * mechanisms that ensure we flush TBs for writes to memory + * we've translated code from, so we must flush all TBs. + * + * vm_stop() has just stopped all cpus, so we are exclusive. + */ + assert(!running); + tb_flush__exclusive_or_serial(); + } +} +#endif + static int tcg_init_machine(AccelState *as, MachineState *ms) { TCGState *s = TCG_STATE(as); @@ -124,6 +143,8 @@ static int tcg_init_machine(AccelState *as, MachineState *ms) default: g_assert_not_reached(); } + + qemu_add_vm_change_state_handler(tcg_vm_change_state, NULL); #endif tcg_allowed = true; diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index 09c928c1f9234..f601a083d143b 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -23,7 +23,6 @@ #include "system/address-spaces.h" #include "exec/cputlb.h" #include "system/memory.h" -#include "exec/tb-flush.h" #include "qemu/target-info.h" #include "hw/qdev-core.h" #include "hw/qdev-properties.h" @@ -207,14 +206,6 @@ static int cpu_common_post_load(void *opaque, int version_id) cpu_reset_interrupt(cpu, 0x01); tlb_flush(cpu); - - /* - * loadvm has just updated the content of RAM, bypassing the - * usual mechanisms that ensure we flush TBs for writes to - * memory we've translated code from. So we must flush all TBs, - * which will now be stale. - */ - tb_flush(cpu); } return 0; From 7330741b3e4bc4b1b32e85b9c1372f44c896c4da Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 6 Sep 2025 06:46:03 +0200 Subject: [PATCH 0854/1794] plugins: Use tb_flush__exclusive_or_serial MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all cases, we are already within start_exclusive. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 4 ++-- plugins/core.c | 6 ++---- plugins/loader.c | 3 +-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 9920381a84eec..1ffcb4b2d2d0f 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -102,8 +102,8 @@ static TCGv_i32 gen_cpu_index(void) /* * Optimize when we run with a single vcpu. All values using cpu_index, * including scoreboard index, will be optimized out. - * User-mode calls tb_flush when setting this flag. In system-mode, all - * vcpus are created before generating code. + * User-mode flushes all TBs when setting this flag. + * In system-mode, all vcpus are created before generating code. */ if (!tcg_cflags_has(current_cpu, CF_PARALLEL)) { return tcg_constant_i32(current_cpu->cpu_index); diff --git a/plugins/core.c b/plugins/core.c index c6e9ef14784ae..ead09fd2f1eec 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -248,7 +248,7 @@ static void plugin_grow_scoreboards__locked(CPUState *cpu) } plugin.scoreboard_alloc_size = scoreboard_size; /* force all tb to be flushed, as scoreboard pointers were changed. */ - tb_flush(cpu); + tb_flush__exclusive_or_serial(); } end_exclusive(); } @@ -684,8 +684,6 @@ void qemu_plugin_user_exit(void) * with the one in fork_start(). That is: * - start_exclusive(), which acquires qemu_cpu_list_lock, * must be called before acquiring plugin.lock. - * - tb_flush(), which acquires mmap_lock(), must be called - * while plugin.lock is not held. */ start_exclusive(); @@ -705,7 +703,7 @@ void qemu_plugin_user_exit(void) } qemu_rec_mutex_unlock(&plugin.lock); - tb_flush(current_cpu); + tb_flush__exclusive_or_serial(); end_exclusive(); /* now it's safe to handle the exit case */ diff --git a/plugins/loader.c b/plugins/loader.c index 8f0d75c9049fe..ba10ebac99357 100644 --- a/plugins/loader.c +++ b/plugins/loader.c @@ -377,8 +377,7 @@ static void plugin_flush_destroy(CPUState *cpu, run_on_cpu_data arg) { struct qemu_plugin_reset_data *data = arg.host_ptr; - g_assert(cpu_in_exclusive_context(cpu)); - tb_flush(cpu); + tb_flush__exclusive_or_serial(); plugin_reset_destroy(data); } From 430014bee7a6c26a70e9f3854428843d5918cdf5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 23 Sep 2025 13:14:09 -0700 Subject: [PATCH 0855/1794] linux-user: Split out begin_parallel_context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the test/flush/set block to a new function. Use tb_flush__exclusive_or_serial while we're at it. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/mmap.c | 13 ++----------- linux-user/syscall.c | 7 +------ linux-user/user-internals.h | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 002e1e668e631..847092a28a6e1 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -22,8 +22,6 @@ #include "exec/log.h" #include "exec/page-protection.h" #include "exec/mmap-lock.h" -#include "exec/tb-flush.h" -#include "exec/translation-block.h" #include "qemu.h" #include "user/page-protection.h" #include "user-internals.h" @@ -1007,11 +1005,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot, * be atomic with respect to an external process. */ if (ret != -1 && (flags & MAP_TYPE) != MAP_PRIVATE) { - CPUState *cpu = thread_cpu; - if (!tcg_cflags_has(cpu, CF_PARALLEL)) { - tcg_cflags_set(cpu, CF_PARALLEL); - tb_flush(cpu); - } + begin_parallel_context(thread_cpu); } return ret; @@ -1448,10 +1442,7 @@ abi_ulong target_shmat(CPUArchState *cpu_env, int shmid, * supported by the host -- anything that requires EXCP_ATOMIC will not * be atomic with respect to an external process. */ - if (!tcg_cflags_has(cpu, CF_PARALLEL)) { - tcg_cflags_set(cpu, CF_PARALLEL); - tb_flush(cpu); - } + begin_parallel_context(cpu); if (qemu_loglevel_mask(CPU_LOG_PAGE)) { FILE *f = qemu_log_trylock(); diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 9098cdb9faad8..1a5f2a03f9f9d 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -27,8 +27,6 @@ #include "target_mman.h" #include "exec/page-protection.h" #include "exec/mmap-lock.h" -#include "exec/tb-flush.h" -#include "exec/translation-block.h" #include #include #include @@ -6667,10 +6665,7 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, * generate code for parallel execution and flush old translations. * Do this now so that the copy gets CF_PARALLEL too. */ - if (!tcg_cflags_has(cpu, CF_PARALLEL)) { - tcg_cflags_set(cpu, CF_PARALLEL); - tb_flush(cpu); - } + begin_parallel_context(cpu); /* we create a new CPU instance. */ new_env = cpu_copy(env); diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h index 691b9a1775f1e..7099349ec8190 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -20,6 +20,8 @@ #include "user/thunk.h" #include "qemu/log.h" +#include "exec/tb-flush.h" +#include "exec/translation-block.h" extern char *exec_path; void init_task_state(TaskState *ts); @@ -172,6 +174,20 @@ static inline int regpairs_aligned(CPUArchState *cpu_env, int num) { return 0; } */ void preexit_cleanup(CPUArchState *env, int code); +/** + * begin_parallel_context + * @cs: the CPU context + * + * Called when starting the second vcpu, or joining shared memory. + */ +static inline void begin_parallel_context(CPUState *cs) +{ + if (!tcg_cflags_has(cs, CF_PARALLEL)) { + tb_flush__exclusive_or_serial(); + tcg_cflags_set(cs, CF_PARALLEL); + } +} + /* * Include target-specific struct and function definitions; * they may need access to the target-independent structures From a9519a46154105713c3f2d3039b40b205ac35e56 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 23 Sep 2025 10:59:07 -0700 Subject: [PATCH 0856/1794] accel/tcg: Create queue_tb_flush from tb_flush MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the function and remove the path which performs the flush immediately. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/tb-maint.c | 11 +++-------- accel/tcg/translate-all.c | 2 +- include/exec/tb-flush.h | 17 ++++++----------- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index b4566a81d76d2..c45fd4236638a 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -797,17 +797,12 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) } } -void tb_flush(CPUState *cpu) +void queue_tb_flush(CPUState *cs) { if (tcg_enabled()) { unsigned tb_flush_count = qatomic_read(&tb_ctx.tb_flush_count); - - if (cpu_in_serial_context(cpu)) { - do_tb_flush(cpu, RUN_ON_CPU_HOST_INT(tb_flush_count)); - } else { - async_safe_run_on_cpu(cpu, do_tb_flush, - RUN_ON_CPU_HOST_INT(tb_flush_count)); - } + async_safe_run_on_cpu(cs, do_tb_flush, + RUN_ON_CPU_HOST_INT(tb_flush_count)); } } diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index d468667b0dd4c..31bc0f8bc586a 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -289,7 +289,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s) tb = tcg_tb_alloc(tcg_ctx); if (unlikely(!tb)) { /* flush must be done */ - tb_flush(cpu); + queue_tb_flush(cpu); mmap_unlock(); /* Make the execution loop process the flush as soon as possible. */ cpu->exception_index = EXCP_INTERRUPT; diff --git a/include/exec/tb-flush.h b/include/exec/tb-flush.h index 090ffc8818b0e..e971d4ba6d69b 100644 --- a/include/exec/tb-flush.h +++ b/include/exec/tb-flush.h @@ -24,19 +24,14 @@ void tb_flush__exclusive_or_serial(void); /** - * tb_flush() - flush all translation blocks - * @cs: CPUState (must be valid, but treated as anonymous pointer) + * queue_tb_flush() - add flush to the cpu work queue + * @cs: CPUState * - * Used to flush all the translation blocks in the system. Sometimes - * it is simpler to flush everything than work out which individual - * translations are now invalid and ensure they are not called - * anymore. - * - * tb_flush() takes care of running the flush in an exclusive context - * if it is not already running in one. This means no guest code will - * run until this complete. + * Flush all translation blocks the next time @cs processes the work queue. + * This should generally be followed by cpu_loop_exit(), so that the work + * queue is processed promptly. */ -void tb_flush(CPUState *cs); +void queue_tb_flush(CPUState *cs); void tcg_flush_jmp_cache(CPUState *cs); From ebf7a5d294a336c7701018884587646d89030073 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 23 Sep 2025 13:26:53 -0700 Subject: [PATCH 0857/1794] accel/tcg: Improve buffer overflow in tb_gen_code If we only have one vcpu, or within cpu_exec_step_atomic, we needn't jump all the way back out to the cpu loop to empty the code gen buffer. Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 31bc0f8bc586a..da9d7f1675276 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -289,6 +289,10 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s) tb = tcg_tb_alloc(tcg_ctx); if (unlikely(!tb)) { /* flush must be done */ + if (cpu_in_serial_context(cpu)) { + tb_flush__exclusive_or_serial(); + goto buffer_overflow; + } queue_tb_flush(cpu); mmap_unlock(); /* Make the execution loop process the flush as soon as possible. */ From b94cca31a7dbd204c7c3b6ede419be9a53b76bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 24 Sep 2025 18:48:24 +0200 Subject: [PATCH 0858/1794] accel/tcg: Remove dead mmap_unlock() call in invalidate_phys_page_range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit a97d5d2c8be ("accel/tcg: Use interval tree for TBs in user-only mode") this code is guarded with #ifdef'ry and only build for system emulation where mmap_unlock() is meaningless. Remove the call. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250924164824.51971-1-philmd@linaro.org> --- accel/tcg/tb-maint.c | 1 - 1 file changed, 1 deletion(-) diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index c45fd4236638a..5a8d0784e7ad3 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -1168,7 +1168,6 @@ tb_invalidate_phys_page_range__locked(CPUState *cpu, page_collection_unlock(pages); /* Force execution of one insn next time. */ cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); - mmap_unlock(); cpu_loop_exit_noexc(cpu); } } From f6f7fdd68e6fbfafae828e504de544b5659bc4bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 24 Sep 2025 18:50:39 +0200 Subject: [PATCH 0859/1794] accel/tcg: Remove cpu_loop_exit_restore() stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not needed since commit 63e7af20352 ("hw/mips: Restrict ITU to TCG"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250924165039.52250-1-philmd@linaro.org> --- accel/stubs/tcg-stub.c | 5 ----- include/exec/cpu-common.h | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c index 3b76b8b17c17d..77055e396443b 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -17,8 +17,3 @@ G_NORETURN void cpu_loop_exit(CPUState *cpu) { g_assert_not_reached(); } - -G_NORETURN void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) -{ - g_assert_not_reached(); -} diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 9b658a3f48f78..f373781ae071b 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -221,9 +221,9 @@ static inline bool cpu_loop_exit_requested(CPUState *cpu) G_NORETURN void cpu_loop_exit_noexc(CPUState *cpu); G_NORETURN void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc); +G_NORETURN void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc); #endif /* CONFIG_TCG */ G_NORETURN void cpu_loop_exit(CPUState *cpu); -G_NORETURN void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc); /* accel/tcg/cpu-exec.c */ int cpu_exec(CPUState *cpu); From d0af3cd0274e265435170a583c72b9f0a4100dff Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 15 Sep 2025 14:29:10 +0100 Subject: [PATCH 0860/1794] hw/usb/hcd-uhci: don't assert for SETUP to non-0 endpoint If the guest feeds invalid data to the UHCI controller, we can assert: qemu-system-x86_64: ../../hw/usb/core.c:744: usb_ep_get: Assertion `pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT' failed. (see issue 2548 for the repro case). This happens because the guest attempts USB_TOKEN_SETUP to an endpoint other than 0, which is not valid. The controller code doesn't catch this guest error, so instead we hit the assertion in the USB core code. Catch the case of SETUP to non-zero endpoint, and treat it as a fatal error in the TD, in the same way we do for an invalid PID value in the TD. This is the UHCI equivalent of the same bug in OHCI that we fixed in commit 3c3c233677 ("hw/usb/hcd-ohci: Fix #1510, #303: pid not IN or OUT"). This bug has been tracked as CVE-2024-8354. Cc: qemu-stable@nongnu.org Fixes: https://gitlab.com/qemu-project/qemu/-/issues/2548 Signed-off-by: Peter Maydell Reviewed-by: Michael Tokarev --- hw/usb/hcd-uhci.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 4822c704f6922..e207d0587a117 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -735,6 +735,7 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, bool spd; bool queuing = (q != NULL); uint8_t pid = td->token & 0xff; + uint8_t ep_id = (td->token >> 15) & 0xf; UHCIAsync *async; async = uhci_async_find_td(s, td_addr); @@ -778,9 +779,14 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, switch (pid) { case USB_TOKEN_OUT: - case USB_TOKEN_SETUP: case USB_TOKEN_IN: break; + case USB_TOKEN_SETUP: + /* SETUP is only valid to endpoint 0 */ + if (ep_id == 0) { + break; + } + /* fallthrough */ default: /* invalid pid : frame interrupted */ s->status |= UHCI_STS_HCPERR; @@ -829,7 +835,7 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, return uhci_handle_td_error(s, td, td_addr, USB_RET_NODEV, int_mask); } - ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf); + ep = usb_ep_get(dev, pid, ep_id); q = uhci_queue_new(s, qh_addr, td, ep); } async = uhci_async_alloc(q, td_addr); From 4ccca2cc05a2d904d1e25365accf3bcbe553c8b0 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 18 Jul 2025 15:31:10 +0200 Subject: [PATCH 0861/1794] net/passt: Fix build failure due to missing GIO dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The passt networking backend uses functions from the GIO library, such as g_subprocess_launcher_new(), to manage its daemon process. So, building with passt enabled requires GIO to be available. If we enable passt and disable gio the build fails during linkage with undefined reference errors: /usr/bin/ld: libsystem.a.p/net_passt.c.o: in function `net_passt_start_daemon': net/passt.c:250: undefined reference to `g_subprocess_launcher_new' /usr/bin/ld: net/passt.c:251: undefined reference to `g_subprocess_launcher_take_fd' /usr/bin/ld: net/passt.c:253: undefined reference to `g_subprocess_launcher_spawnv' /usr/bin/ld: net/passt.c:256: undefined reference to `g_object_unref' /usr/bin/ld: net/passt.c:263: undefined reference to `g_subprocess_wait' /usr/bin/ld: net/passt.c:268: undefined reference to `g_subprocess_get_if_exited' /usr/bin/ld: libsystem.a.p/net_passt.c.o: in function `glib_autoptr_clear_GSubprocess': /usr/include/glib-2.0/gio/gio-autocleanups.h:132: undefined reference to `g_object_unref' /usr/bin/ld: libsystem.a.p/net_passt.c.o: in function `net_passt_start_daemon': net/passt.c:269: undefined reference to `g_subprocess_get_exit_status' Fix this by adding an explicit weson dependency on GIO for the passt option. The existing dependency on linux is kept because passt is only available on this OS. Cc: qemu-stable@nongnu.org Fixes: 854ee02b222 ("net: Add passt network backend") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3121 Reported-by: Thomas Huth Signed-off-by: Laurent Vivier Reviewed-by: Thomas Huth Reviewed-by: Daniel P. Berrangé Signed-off-by: Peter Maydell --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 72da97829abe4..bdfb6214e6f20 100644 --- a/meson.build +++ b/meson.build @@ -1280,6 +1280,7 @@ endif enable_passt = get_option('passt') \ .require(host_os == 'linux', error_message: 'passt is supported only on Linux') \ + .require(gio.found(), error_message: 'passt requires gio') \ .allowed() vde = not_found From 363e612c2efc4543b9283f8a1ce6ab7d6620d9f5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:02 -0700 Subject: [PATCH 0862/1794] target/arm: Introduce KVMID_AA64_SYS_REG64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow us to create kvm ids directly, rather than going through ENCODE_AA64_CP_REG + cpreg_to_kvm_id. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/kvm-consts.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/target/arm/kvm-consts.h b/target/arm/kvm-consts.h index c44d23dbe79c0..fdb305eea1a4e 100644 --- a/target/arm/kvm-consts.h +++ b/target/arm/kvm-consts.h @@ -180,4 +180,15 @@ MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_ARM64_SYSREG_OP2_SHIFT); #undef MISMATCH_CHECK +#define KVMID_AA64_SYS_REG_(op0, op1, crn, crm, op2) \ + (CP_REG_AA64_MASK | CP_REG_ARM64_SYSREG | \ + ((op0) << CP_REG_ARM64_SYSREG_OP0_SHIFT) | \ + ((op1) << CP_REG_ARM64_SYSREG_OP1_SHIFT) | \ + ((crn) << CP_REG_ARM64_SYSREG_CRN_SHIFT) | \ + ((crm) << CP_REG_ARM64_SYSREG_CRM_SHIFT) | \ + ((op2) << CP_REG_ARM64_SYSREG_OP2_SHIFT)) + +#define KVMID_AA64_SYS_REG64(op0, op1, crn, crm, op2) \ + (KVMID_AA64_SYS_REG_(op0, op1, crn, crm, op2) | CP_REG_SIZE_U64) + #endif From 5a8af95cb31122d2fcd2e6d3427b8e8427cd8bdc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:03 -0700 Subject: [PATCH 0863/1794] target/arm: Move compare_u64 to helper.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We will use this function beyond kvm.c. Reviewed-by: Manos Pitsidianakis Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/helper.c | 11 +++++++++++ target/arm/internals.h | 3 +++ target/arm/kvm.c | 11 ----------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index c44294711f87a..009f8d6fa1cc7 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -40,6 +40,17 @@ static void switch_mode(CPUARMState *env, int mode); +int compare_u64(const void *a, const void *b) +{ + if (*(uint64_t *)a > *(uint64_t *)b) { + return 1; + } + if (*(uint64_t *)a < *(uint64_t *)b) { + return -1; + } + return 0; +} + uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri) { assert(ri->fieldoffset); diff --git a/target/arm/internals.h b/target/arm/internals.h index 0f7df97b993ca..1d958dbf685f8 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -2004,4 +2004,7 @@ void vfp_clear_float_status_exc_flags(CPUARMState *env); void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask); bool arm_pan_enabled(CPUARMState *env); +/* Compare uint64_t for qsort and bsearch. */ +int compare_u64(const void *a, const void *b); + #endif diff --git a/target/arm/kvm.c b/target/arm/kvm.c index c1ec6654ca677..5a75ff59271d8 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -718,17 +718,6 @@ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, memory_region_ref(kd->mr); } -static int compare_u64(const void *a, const void *b) -{ - if (*(uint64_t *)a > *(uint64_t *)b) { - return 1; - } - if (*(uint64_t *)a < *(uint64_t *)b) { - return -1; - } - return 0; -} - /* * cpreg_values are sorted in ascending order by KVM register ID * (see kvm_arm_init_cpreg_list). This allows us to cheaply find From a648af4885b1a344394a98cf298a9485f5232b4b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:04 -0700 Subject: [PATCH 0864/1794] target/arm/hvf: Split out sysreg.c.inc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the list of supported sysregs to a reuseable file. Reviewed-by: Manos Pitsidianakis Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 147 ++---------------------------------- target/arm/hvf/sysreg.c.inc | 146 +++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 141 deletions(-) create mode 100644 target/arm/hvf/sysreg.c.inc diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index b77db99079e53..9f8e3083b4fbf 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -403,150 +403,15 @@ struct hvf_sreg_match { uint32_t cp_idx; }; +#define DEF_SYSREG(HVF_ID, crn, crm, op0, op1, op2) \ + { HVF_ID, HVF_SYSREG(crn, crm, op0, op1, op2) }, + static struct hvf_sreg_match hvf_sreg_match[] = { - { HV_SYS_REG_DBGBVR0_EL1, HVF_SYSREG(0, 0, 2, 0, 4) }, - { HV_SYS_REG_DBGBCR0_EL1, HVF_SYSREG(0, 0, 2, 0, 5) }, - { HV_SYS_REG_DBGWVR0_EL1, HVF_SYSREG(0, 0, 2, 0, 6) }, - { HV_SYS_REG_DBGWCR0_EL1, HVF_SYSREG(0, 0, 2, 0, 7) }, - - { HV_SYS_REG_DBGBVR1_EL1, HVF_SYSREG(0, 1, 2, 0, 4) }, - { HV_SYS_REG_DBGBCR1_EL1, HVF_SYSREG(0, 1, 2, 0, 5) }, - { HV_SYS_REG_DBGWVR1_EL1, HVF_SYSREG(0, 1, 2, 0, 6) }, - { HV_SYS_REG_DBGWCR1_EL1, HVF_SYSREG(0, 1, 2, 0, 7) }, - - { HV_SYS_REG_DBGBVR2_EL1, HVF_SYSREG(0, 2, 2, 0, 4) }, - { HV_SYS_REG_DBGBCR2_EL1, HVF_SYSREG(0, 2, 2, 0, 5) }, - { HV_SYS_REG_DBGWVR2_EL1, HVF_SYSREG(0, 2, 2, 0, 6) }, - { HV_SYS_REG_DBGWCR2_EL1, HVF_SYSREG(0, 2, 2, 0, 7) }, - - { HV_SYS_REG_DBGBVR3_EL1, HVF_SYSREG(0, 3, 2, 0, 4) }, - { HV_SYS_REG_DBGBCR3_EL1, HVF_SYSREG(0, 3, 2, 0, 5) }, - { HV_SYS_REG_DBGWVR3_EL1, HVF_SYSREG(0, 3, 2, 0, 6) }, - { HV_SYS_REG_DBGWCR3_EL1, HVF_SYSREG(0, 3, 2, 0, 7) }, - - { HV_SYS_REG_DBGBVR4_EL1, HVF_SYSREG(0, 4, 2, 0, 4) }, - { HV_SYS_REG_DBGBCR4_EL1, HVF_SYSREG(0, 4, 2, 0, 5) }, - { HV_SYS_REG_DBGWVR4_EL1, HVF_SYSREG(0, 4, 2, 0, 6) }, - { HV_SYS_REG_DBGWCR4_EL1, HVF_SYSREG(0, 4, 2, 0, 7) }, - - { HV_SYS_REG_DBGBVR5_EL1, HVF_SYSREG(0, 5, 2, 0, 4) }, - { HV_SYS_REG_DBGBCR5_EL1, HVF_SYSREG(0, 5, 2, 0, 5) }, - { HV_SYS_REG_DBGWVR5_EL1, HVF_SYSREG(0, 5, 2, 0, 6) }, - { HV_SYS_REG_DBGWCR5_EL1, HVF_SYSREG(0, 5, 2, 0, 7) }, - - { HV_SYS_REG_DBGBVR6_EL1, HVF_SYSREG(0, 6, 2, 0, 4) }, - { HV_SYS_REG_DBGBCR6_EL1, HVF_SYSREG(0, 6, 2, 0, 5) }, - { HV_SYS_REG_DBGWVR6_EL1, HVF_SYSREG(0, 6, 2, 0, 6) }, - { HV_SYS_REG_DBGWCR6_EL1, HVF_SYSREG(0, 6, 2, 0, 7) }, - - { HV_SYS_REG_DBGBVR7_EL1, HVF_SYSREG(0, 7, 2, 0, 4) }, - { HV_SYS_REG_DBGBCR7_EL1, HVF_SYSREG(0, 7, 2, 0, 5) }, - { HV_SYS_REG_DBGWVR7_EL1, HVF_SYSREG(0, 7, 2, 0, 6) }, - { HV_SYS_REG_DBGWCR7_EL1, HVF_SYSREG(0, 7, 2, 0, 7) }, - - { HV_SYS_REG_DBGBVR8_EL1, HVF_SYSREG(0, 8, 2, 0, 4) }, - { HV_SYS_REG_DBGBCR8_EL1, HVF_SYSREG(0, 8, 2, 0, 5) }, - { HV_SYS_REG_DBGWVR8_EL1, HVF_SYSREG(0, 8, 2, 0, 6) }, - { HV_SYS_REG_DBGWCR8_EL1, HVF_SYSREG(0, 8, 2, 0, 7) }, - - { HV_SYS_REG_DBGBVR9_EL1, HVF_SYSREG(0, 9, 2, 0, 4) }, - { HV_SYS_REG_DBGBCR9_EL1, HVF_SYSREG(0, 9, 2, 0, 5) }, - { HV_SYS_REG_DBGWVR9_EL1, HVF_SYSREG(0, 9, 2, 0, 6) }, - { HV_SYS_REG_DBGWCR9_EL1, HVF_SYSREG(0, 9, 2, 0, 7) }, - - { HV_SYS_REG_DBGBVR10_EL1, HVF_SYSREG(0, 10, 2, 0, 4) }, - { HV_SYS_REG_DBGBCR10_EL1, HVF_SYSREG(0, 10, 2, 0, 5) }, - { HV_SYS_REG_DBGWVR10_EL1, HVF_SYSREG(0, 10, 2, 0, 6) }, - { HV_SYS_REG_DBGWCR10_EL1, HVF_SYSREG(0, 10, 2, 0, 7) }, - - { HV_SYS_REG_DBGBVR11_EL1, HVF_SYSREG(0, 11, 2, 0, 4) }, - { HV_SYS_REG_DBGBCR11_EL1, HVF_SYSREG(0, 11, 2, 0, 5) }, - { HV_SYS_REG_DBGWVR11_EL1, HVF_SYSREG(0, 11, 2, 0, 6) }, - { HV_SYS_REG_DBGWCR11_EL1, HVF_SYSREG(0, 11, 2, 0, 7) }, - - { HV_SYS_REG_DBGBVR12_EL1, HVF_SYSREG(0, 12, 2, 0, 4) }, - { HV_SYS_REG_DBGBCR12_EL1, HVF_SYSREG(0, 12, 2, 0, 5) }, - { HV_SYS_REG_DBGWVR12_EL1, HVF_SYSREG(0, 12, 2, 0, 6) }, - { HV_SYS_REG_DBGWCR12_EL1, HVF_SYSREG(0, 12, 2, 0, 7) }, - - { HV_SYS_REG_DBGBVR13_EL1, HVF_SYSREG(0, 13, 2, 0, 4) }, - { HV_SYS_REG_DBGBCR13_EL1, HVF_SYSREG(0, 13, 2, 0, 5) }, - { HV_SYS_REG_DBGWVR13_EL1, HVF_SYSREG(0, 13, 2, 0, 6) }, - { HV_SYS_REG_DBGWCR13_EL1, HVF_SYSREG(0, 13, 2, 0, 7) }, - - { HV_SYS_REG_DBGBVR14_EL1, HVF_SYSREG(0, 14, 2, 0, 4) }, - { HV_SYS_REG_DBGBCR14_EL1, HVF_SYSREG(0, 14, 2, 0, 5) }, - { HV_SYS_REG_DBGWVR14_EL1, HVF_SYSREG(0, 14, 2, 0, 6) }, - { HV_SYS_REG_DBGWCR14_EL1, HVF_SYSREG(0, 14, 2, 0, 7) }, - - { HV_SYS_REG_DBGBVR15_EL1, HVF_SYSREG(0, 15, 2, 0, 4) }, - { HV_SYS_REG_DBGBCR15_EL1, HVF_SYSREG(0, 15, 2, 0, 5) }, - { HV_SYS_REG_DBGWVR15_EL1, HVF_SYSREG(0, 15, 2, 0, 6) }, - { HV_SYS_REG_DBGWCR15_EL1, HVF_SYSREG(0, 15, 2, 0, 7) }, - -#ifdef SYNC_NO_RAW_REGS - /* - * The registers below are manually synced on init because they are - * marked as NO_RAW. We still list them to make number space sync easier. - */ - { HV_SYS_REG_MDCCINT_EL1, HVF_SYSREG(0, 2, 2, 0, 0) }, - { HV_SYS_REG_MIDR_EL1, HVF_SYSREG(0, 0, 3, 0, 0) }, - { HV_SYS_REG_MPIDR_EL1, HVF_SYSREG(0, 0, 3, 0, 5) }, - { HV_SYS_REG_ID_AA64PFR0_EL1, HVF_SYSREG(0, 4, 3, 0, 0) }, -#endif - { HV_SYS_REG_ID_AA64PFR1_EL1, HVF_SYSREG(0, 4, 3, 0, 1) }, - { HV_SYS_REG_ID_AA64DFR0_EL1, HVF_SYSREG(0, 5, 3, 0, 0) }, - { HV_SYS_REG_ID_AA64DFR1_EL1, HVF_SYSREG(0, 5, 3, 0, 1) }, - { HV_SYS_REG_ID_AA64ISAR0_EL1, HVF_SYSREG(0, 6, 3, 0, 0) }, - { HV_SYS_REG_ID_AA64ISAR1_EL1, HVF_SYSREG(0, 6, 3, 0, 1) }, -#ifdef SYNC_NO_MMFR0 - /* We keep the hardware MMFR0 around. HW limits are there anyway */ - { HV_SYS_REG_ID_AA64MMFR0_EL1, HVF_SYSREG(0, 7, 3, 0, 0) }, -#endif - { HV_SYS_REG_ID_AA64MMFR1_EL1, HVF_SYSREG(0, 7, 3, 0, 1) }, - { HV_SYS_REG_ID_AA64MMFR2_EL1, HVF_SYSREG(0, 7, 3, 0, 2) }, - /* Add ID_AA64MMFR3_EL1 here when HVF supports it */ - - { HV_SYS_REG_MDSCR_EL1, HVF_SYSREG(0, 2, 2, 0, 2) }, - { HV_SYS_REG_SCTLR_EL1, HVF_SYSREG(1, 0, 3, 0, 0) }, - { HV_SYS_REG_CPACR_EL1, HVF_SYSREG(1, 0, 3, 0, 2) }, - { HV_SYS_REG_TTBR0_EL1, HVF_SYSREG(2, 0, 3, 0, 0) }, - { HV_SYS_REG_TTBR1_EL1, HVF_SYSREG(2, 0, 3, 0, 1) }, - { HV_SYS_REG_TCR_EL1, HVF_SYSREG(2, 0, 3, 0, 2) }, - - { HV_SYS_REG_APIAKEYLO_EL1, HVF_SYSREG(2, 1, 3, 0, 0) }, - { HV_SYS_REG_APIAKEYHI_EL1, HVF_SYSREG(2, 1, 3, 0, 1) }, - { HV_SYS_REG_APIBKEYLO_EL1, HVF_SYSREG(2, 1, 3, 0, 2) }, - { HV_SYS_REG_APIBKEYHI_EL1, HVF_SYSREG(2, 1, 3, 0, 3) }, - { HV_SYS_REG_APDAKEYLO_EL1, HVF_SYSREG(2, 2, 3, 0, 0) }, - { HV_SYS_REG_APDAKEYHI_EL1, HVF_SYSREG(2, 2, 3, 0, 1) }, - { HV_SYS_REG_APDBKEYLO_EL1, HVF_SYSREG(2, 2, 3, 0, 2) }, - { HV_SYS_REG_APDBKEYHI_EL1, HVF_SYSREG(2, 2, 3, 0, 3) }, - { HV_SYS_REG_APGAKEYLO_EL1, HVF_SYSREG(2, 3, 3, 0, 0) }, - { HV_SYS_REG_APGAKEYHI_EL1, HVF_SYSREG(2, 3, 3, 0, 1) }, - - { HV_SYS_REG_SPSR_EL1, HVF_SYSREG(4, 0, 3, 0, 0) }, - { HV_SYS_REG_ELR_EL1, HVF_SYSREG(4, 0, 3, 0, 1) }, - { HV_SYS_REG_SP_EL0, HVF_SYSREG(4, 1, 3, 0, 0) }, - { HV_SYS_REG_AFSR0_EL1, HVF_SYSREG(5, 1, 3, 0, 0) }, - { HV_SYS_REG_AFSR1_EL1, HVF_SYSREG(5, 1, 3, 0, 1) }, - { HV_SYS_REG_ESR_EL1, HVF_SYSREG(5, 2, 3, 0, 0) }, - { HV_SYS_REG_FAR_EL1, HVF_SYSREG(6, 0, 3, 0, 0) }, - { HV_SYS_REG_PAR_EL1, HVF_SYSREG(7, 4, 3, 0, 0) }, - { HV_SYS_REG_MAIR_EL1, HVF_SYSREG(10, 2, 3, 0, 0) }, - { HV_SYS_REG_AMAIR_EL1, HVF_SYSREG(10, 3, 3, 0, 0) }, - { HV_SYS_REG_VBAR_EL1, HVF_SYSREG(12, 0, 3, 0, 0) }, - { HV_SYS_REG_CONTEXTIDR_EL1, HVF_SYSREG(13, 0, 3, 0, 1) }, - { HV_SYS_REG_TPIDR_EL1, HVF_SYSREG(13, 0, 3, 0, 4) }, - { HV_SYS_REG_CNTKCTL_EL1, HVF_SYSREG(14, 1, 3, 0, 0) }, - { HV_SYS_REG_CSSELR_EL1, HVF_SYSREG(0, 0, 3, 2, 0) }, - { HV_SYS_REG_TPIDR_EL0, HVF_SYSREG(13, 0, 3, 3, 2) }, - { HV_SYS_REG_TPIDRRO_EL0, HVF_SYSREG(13, 0, 3, 3, 3) }, - { HV_SYS_REG_CNTV_CTL_EL0, HVF_SYSREG(14, 3, 3, 3, 1) }, - { HV_SYS_REG_CNTV_CVAL_EL0, HVF_SYSREG(14, 3, 3, 3, 2) }, - { HV_SYS_REG_SP_EL1, HVF_SYSREG(4, 1, 3, 4, 0) }, +#include "sysreg.c.inc" }; +#undef DEF_SYSREG + int hvf_get_registers(CPUState *cpu) { ARMCPU *arm_cpu = ARM_CPU(cpu); diff --git a/target/arm/hvf/sysreg.c.inc b/target/arm/hvf/sysreg.c.inc new file mode 100644 index 0000000000000..222698f1d198c --- /dev/null +++ b/target/arm/hvf/sysreg.c.inc @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +DEF_SYSREG(HV_SYS_REG_DBGBVR0_EL1, 0, 0, 2, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR0_EL1, 0, 0, 2, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR0_EL1, 0, 0, 2, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR0_EL1, 0, 0, 2, 0, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR1_EL1, 0, 1, 2, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR1_EL1, 0, 1, 2, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR1_EL1, 0, 1, 2, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR1_EL1, 0, 1, 2, 0, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR2_EL1, 0, 2, 2, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR2_EL1, 0, 2, 2, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR2_EL1, 0, 2, 2, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR2_EL1, 0, 2, 2, 0, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR3_EL1, 0, 3, 2, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR3_EL1, 0, 3, 2, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR3_EL1, 0, 3, 2, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR3_EL1, 0, 3, 2, 0, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR4_EL1, 0, 4, 2, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR4_EL1, 0, 4, 2, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR4_EL1, 0, 4, 2, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR4_EL1, 0, 4, 2, 0, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR5_EL1, 0, 5, 2, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR5_EL1, 0, 5, 2, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR5_EL1, 0, 5, 2, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR5_EL1, 0, 5, 2, 0, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR6_EL1, 0, 6, 2, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR6_EL1, 0, 6, 2, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR6_EL1, 0, 6, 2, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR6_EL1, 0, 6, 2, 0, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR7_EL1, 0, 7, 2, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR7_EL1, 0, 7, 2, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR7_EL1, 0, 7, 2, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR7_EL1, 0, 7, 2, 0, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR8_EL1, 0, 8, 2, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR8_EL1, 0, 8, 2, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR8_EL1, 0, 8, 2, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR8_EL1, 0, 8, 2, 0, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR9_EL1, 0, 9, 2, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR9_EL1, 0, 9, 2, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR9_EL1, 0, 9, 2, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR9_EL1, 0, 9, 2, 0, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR10_EL1, 0, 10, 2, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR10_EL1, 0, 10, 2, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR10_EL1, 0, 10, 2, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR10_EL1, 0, 10, 2, 0, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR11_EL1, 0, 11, 2, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR11_EL1, 0, 11, 2, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR11_EL1, 0, 11, 2, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR11_EL1, 0, 11, 2, 0, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR12_EL1, 0, 12, 2, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR12_EL1, 0, 12, 2, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR12_EL1, 0, 12, 2, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR12_EL1, 0, 12, 2, 0, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR13_EL1, 0, 13, 2, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR13_EL1, 0, 13, 2, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR13_EL1, 0, 13, 2, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR13_EL1, 0, 13, 2, 0, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR14_EL1, 0, 14, 2, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR14_EL1, 0, 14, 2, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR14_EL1, 0, 14, 2, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR14_EL1, 0, 14, 2, 0, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR15_EL1, 0, 15, 2, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR15_EL1, 0, 15, 2, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR15_EL1, 0, 15, 2, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR15_EL1, 0, 15, 2, 0, 7) + +#ifdef SYNC_NO_RAW_REGS +/* + * The registers below are manually synced on init because they are + * marked as NO_RAW. We still list them to make number space sync easier. + */ +DEF_SYSREG(HV_SYS_REG_MDCCINT_EL1, 0, 2, 2, 0, 0) +DEF_SYSREG(HV_SYS_REG_MIDR_EL1, 0, 0, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_MPIDR_EL1, 0, 0, 3, 0, 5) +DEF_SYSREG(HV_SYS_REG_ID_AA64PFR0_EL1, 0, 4, 3, 0, 0) +#endif + +DEF_SYSREG(HV_SYS_REG_ID_AA64PFR1_EL1, 0, 4, 3, 0, 1) +DEF_SYSREG(HV_SYS_REG_ID_AA64DFR0_EL1, 0, 5, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_ID_AA64DFR1_EL1, 0, 5, 3, 0, 1) +DEF_SYSREG(HV_SYS_REG_ID_AA64ISAR0_EL1, 0, 6, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_ID_AA64ISAR1_EL1, 0, 6, 3, 0, 1) + +#ifdef SYNC_NO_MMFR0 +/* We keep the hardware MMFR0 around. HW limits are there anyway */ +DEF_SYSREG(HV_SYS_REG_ID_AA64MMFR0_EL1, 0, 7, 3, 0, 0) +#endif + +DEF_SYSREG(HV_SYS_REG_ID_AA64MMFR1_EL1, 0, 7, 3, 0, 1) +DEF_SYSREG(HV_SYS_REG_ID_AA64MMFR2_EL1, 0, 7, 3, 0, 2) +/* Add ID_AA64MMFR3_EL1 here when HVF supports it */ + +DEF_SYSREG(HV_SYS_REG_MDSCR_EL1, 0, 2, 2, 0, 2) +DEF_SYSREG(HV_SYS_REG_SCTLR_EL1, 1, 0, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_CPACR_EL1, 1, 0, 3, 0, 2) +DEF_SYSREG(HV_SYS_REG_TTBR0_EL1, 2, 0, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_TTBR1_EL1, 2, 0, 3, 0, 1) +DEF_SYSREG(HV_SYS_REG_TCR_EL1, 2, 0, 3, 0, 2) + +DEF_SYSREG(HV_SYS_REG_APIAKEYLO_EL1, 2, 1, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_APIAKEYHI_EL1, 2, 1, 3, 0, 1) +DEF_SYSREG(HV_SYS_REG_APIBKEYLO_EL1, 2, 1, 3, 0, 2) +DEF_SYSREG(HV_SYS_REG_APIBKEYHI_EL1, 2, 1, 3, 0, 3) +DEF_SYSREG(HV_SYS_REG_APDAKEYLO_EL1, 2, 2, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_APDAKEYHI_EL1, 2, 2, 3, 0, 1) +DEF_SYSREG(HV_SYS_REG_APDBKEYLO_EL1, 2, 2, 3, 0, 2) +DEF_SYSREG(HV_SYS_REG_APDBKEYHI_EL1, 2, 2, 3, 0, 3) +DEF_SYSREG(HV_SYS_REG_APGAKEYLO_EL1, 2, 3, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_APGAKEYHI_EL1, 2, 3, 3, 0, 1) + +DEF_SYSREG(HV_SYS_REG_SPSR_EL1, 4, 0, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_ELR_EL1, 4, 0, 3, 0, 1) +DEF_SYSREG(HV_SYS_REG_SP_EL0, 4, 1, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_AFSR0_EL1, 5, 1, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_AFSR1_EL1, 5, 1, 3, 0, 1) +DEF_SYSREG(HV_SYS_REG_ESR_EL1, 5, 2, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_FAR_EL1, 6, 0, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_PAR_EL1, 7, 4, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_MAIR_EL1, 10, 2, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_AMAIR_EL1, 10, 3, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_VBAR_EL1, 12, 0, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_CONTEXTIDR_EL1, 13, 0, 3, 0, 1) +DEF_SYSREG(HV_SYS_REG_TPIDR_EL1, 13, 0, 3, 0, 4) +DEF_SYSREG(HV_SYS_REG_CNTKCTL_EL1, 14, 1, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_CSSELR_EL1, 0, 0, 3, 2, 0) +DEF_SYSREG(HV_SYS_REG_TPIDR_EL0, 13, 0, 3, 3, 2) +DEF_SYSREG(HV_SYS_REG_TPIDRRO_EL0, 13, 0, 3, 3, 3) +DEF_SYSREG(HV_SYS_REG_CNTV_CTL_EL0, 14, 3, 3, 3, 1) +DEF_SYSREG(HV_SYS_REG_CNTV_CVAL_EL0, 14, 3, 3, 3, 2) +DEF_SYSREG(HV_SYS_REG_SP_EL1, 4, 1, 3, 4, 0) From 8da60618b6d2ee7cdd73d58e9f0b670c021a6bd0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:05 -0700 Subject: [PATCH 0865/1794] target/arm/hvf: Reorder DEF_SYSREG arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The order of the parameters in the Arm ARM is op0, op1, crn, crm, op2 Reorder the arguments of DEF_SYSREG to match. Mechanical change to sysreg.c.inc using sed 's/\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\)/\1,\4,\5,\2,\3/' Reviewed-by: Manos Pitsidianakis Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 2 +- target/arm/hvf/sysreg.c.inc | 258 ++++++++++++++++++------------------ 2 files changed, 130 insertions(+), 130 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 9f8e3083b4fbf..f68924ba1f343 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -403,7 +403,7 @@ struct hvf_sreg_match { uint32_t cp_idx; }; -#define DEF_SYSREG(HVF_ID, crn, crm, op0, op1, op2) \ +#define DEF_SYSREG(HVF_ID, op0, op1, crn, crm, op2) \ { HVF_ID, HVF_SYSREG(crn, crm, op0, op1, op2) }, static struct hvf_sreg_match hvf_sreg_match[] = { diff --git a/target/arm/hvf/sysreg.c.inc b/target/arm/hvf/sysreg.c.inc index 222698f1d198c..f2276d534e68b 100644 --- a/target/arm/hvf/sysreg.c.inc +++ b/target/arm/hvf/sysreg.c.inc @@ -1,146 +1,146 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -DEF_SYSREG(HV_SYS_REG_DBGBVR0_EL1, 0, 0, 2, 0, 4) -DEF_SYSREG(HV_SYS_REG_DBGBCR0_EL1, 0, 0, 2, 0, 5) -DEF_SYSREG(HV_SYS_REG_DBGWVR0_EL1, 0, 0, 2, 0, 6) -DEF_SYSREG(HV_SYS_REG_DBGWCR0_EL1, 0, 0, 2, 0, 7) - -DEF_SYSREG(HV_SYS_REG_DBGBVR1_EL1, 0, 1, 2, 0, 4) -DEF_SYSREG(HV_SYS_REG_DBGBCR1_EL1, 0, 1, 2, 0, 5) -DEF_SYSREG(HV_SYS_REG_DBGWVR1_EL1, 0, 1, 2, 0, 6) -DEF_SYSREG(HV_SYS_REG_DBGWCR1_EL1, 0, 1, 2, 0, 7) - -DEF_SYSREG(HV_SYS_REG_DBGBVR2_EL1, 0, 2, 2, 0, 4) -DEF_SYSREG(HV_SYS_REG_DBGBCR2_EL1, 0, 2, 2, 0, 5) -DEF_SYSREG(HV_SYS_REG_DBGWVR2_EL1, 0, 2, 2, 0, 6) -DEF_SYSREG(HV_SYS_REG_DBGWCR2_EL1, 0, 2, 2, 0, 7) - -DEF_SYSREG(HV_SYS_REG_DBGBVR3_EL1, 0, 3, 2, 0, 4) -DEF_SYSREG(HV_SYS_REG_DBGBCR3_EL1, 0, 3, 2, 0, 5) -DEF_SYSREG(HV_SYS_REG_DBGWVR3_EL1, 0, 3, 2, 0, 6) -DEF_SYSREG(HV_SYS_REG_DBGWCR3_EL1, 0, 3, 2, 0, 7) - -DEF_SYSREG(HV_SYS_REG_DBGBVR4_EL1, 0, 4, 2, 0, 4) -DEF_SYSREG(HV_SYS_REG_DBGBCR4_EL1, 0, 4, 2, 0, 5) -DEF_SYSREG(HV_SYS_REG_DBGWVR4_EL1, 0, 4, 2, 0, 6) -DEF_SYSREG(HV_SYS_REG_DBGWCR4_EL1, 0, 4, 2, 0, 7) - -DEF_SYSREG(HV_SYS_REG_DBGBVR5_EL1, 0, 5, 2, 0, 4) -DEF_SYSREG(HV_SYS_REG_DBGBCR5_EL1, 0, 5, 2, 0, 5) -DEF_SYSREG(HV_SYS_REG_DBGWVR5_EL1, 0, 5, 2, 0, 6) -DEF_SYSREG(HV_SYS_REG_DBGWCR5_EL1, 0, 5, 2, 0, 7) - -DEF_SYSREG(HV_SYS_REG_DBGBVR6_EL1, 0, 6, 2, 0, 4) -DEF_SYSREG(HV_SYS_REG_DBGBCR6_EL1, 0, 6, 2, 0, 5) -DEF_SYSREG(HV_SYS_REG_DBGWVR6_EL1, 0, 6, 2, 0, 6) -DEF_SYSREG(HV_SYS_REG_DBGWCR6_EL1, 0, 6, 2, 0, 7) - -DEF_SYSREG(HV_SYS_REG_DBGBVR7_EL1, 0, 7, 2, 0, 4) -DEF_SYSREG(HV_SYS_REG_DBGBCR7_EL1, 0, 7, 2, 0, 5) -DEF_SYSREG(HV_SYS_REG_DBGWVR7_EL1, 0, 7, 2, 0, 6) -DEF_SYSREG(HV_SYS_REG_DBGWCR7_EL1, 0, 7, 2, 0, 7) - -DEF_SYSREG(HV_SYS_REG_DBGBVR8_EL1, 0, 8, 2, 0, 4) -DEF_SYSREG(HV_SYS_REG_DBGBCR8_EL1, 0, 8, 2, 0, 5) -DEF_SYSREG(HV_SYS_REG_DBGWVR8_EL1, 0, 8, 2, 0, 6) -DEF_SYSREG(HV_SYS_REG_DBGWCR8_EL1, 0, 8, 2, 0, 7) - -DEF_SYSREG(HV_SYS_REG_DBGBVR9_EL1, 0, 9, 2, 0, 4) -DEF_SYSREG(HV_SYS_REG_DBGBCR9_EL1, 0, 9, 2, 0, 5) -DEF_SYSREG(HV_SYS_REG_DBGWVR9_EL1, 0, 9, 2, 0, 6) -DEF_SYSREG(HV_SYS_REG_DBGWCR9_EL1, 0, 9, 2, 0, 7) - -DEF_SYSREG(HV_SYS_REG_DBGBVR10_EL1, 0, 10, 2, 0, 4) -DEF_SYSREG(HV_SYS_REG_DBGBCR10_EL1, 0, 10, 2, 0, 5) -DEF_SYSREG(HV_SYS_REG_DBGWVR10_EL1, 0, 10, 2, 0, 6) -DEF_SYSREG(HV_SYS_REG_DBGWCR10_EL1, 0, 10, 2, 0, 7) - -DEF_SYSREG(HV_SYS_REG_DBGBVR11_EL1, 0, 11, 2, 0, 4) -DEF_SYSREG(HV_SYS_REG_DBGBCR11_EL1, 0, 11, 2, 0, 5) -DEF_SYSREG(HV_SYS_REG_DBGWVR11_EL1, 0, 11, 2, 0, 6) -DEF_SYSREG(HV_SYS_REG_DBGWCR11_EL1, 0, 11, 2, 0, 7) - -DEF_SYSREG(HV_SYS_REG_DBGBVR12_EL1, 0, 12, 2, 0, 4) -DEF_SYSREG(HV_SYS_REG_DBGBCR12_EL1, 0, 12, 2, 0, 5) -DEF_SYSREG(HV_SYS_REG_DBGWVR12_EL1, 0, 12, 2, 0, 6) -DEF_SYSREG(HV_SYS_REG_DBGWCR12_EL1, 0, 12, 2, 0, 7) - -DEF_SYSREG(HV_SYS_REG_DBGBVR13_EL1, 0, 13, 2, 0, 4) -DEF_SYSREG(HV_SYS_REG_DBGBCR13_EL1, 0, 13, 2, 0, 5) -DEF_SYSREG(HV_SYS_REG_DBGWVR13_EL1, 0, 13, 2, 0, 6) -DEF_SYSREG(HV_SYS_REG_DBGWCR13_EL1, 0, 13, 2, 0, 7) - -DEF_SYSREG(HV_SYS_REG_DBGBVR14_EL1, 0, 14, 2, 0, 4) -DEF_SYSREG(HV_SYS_REG_DBGBCR14_EL1, 0, 14, 2, 0, 5) -DEF_SYSREG(HV_SYS_REG_DBGWVR14_EL1, 0, 14, 2, 0, 6) -DEF_SYSREG(HV_SYS_REG_DBGWCR14_EL1, 0, 14, 2, 0, 7) - -DEF_SYSREG(HV_SYS_REG_DBGBVR15_EL1, 0, 15, 2, 0, 4) -DEF_SYSREG(HV_SYS_REG_DBGBCR15_EL1, 0, 15, 2, 0, 5) -DEF_SYSREG(HV_SYS_REG_DBGWVR15_EL1, 0, 15, 2, 0, 6) -DEF_SYSREG(HV_SYS_REG_DBGWCR15_EL1, 0, 15, 2, 0, 7) +DEF_SYSREG(HV_SYS_REG_DBGBVR0_EL1, 2, 0, 0, 0, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR0_EL1, 2, 0, 0, 0, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR0_EL1, 2, 0, 0, 0, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR0_EL1, 2, 0, 0, 0, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR1_EL1, 2, 0, 0, 1, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR1_EL1, 2, 0, 0, 1, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR1_EL1, 2, 0, 0, 1, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR1_EL1, 2, 0, 0, 1, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR2_EL1, 2, 0, 0, 2, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR2_EL1, 2, 0, 0, 2, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR2_EL1, 2, 0, 0, 2, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR2_EL1, 2, 0, 0, 2, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR3_EL1, 2, 0, 0, 3, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR3_EL1, 2, 0, 0, 3, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR3_EL1, 2, 0, 0, 3, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR3_EL1, 2, 0, 0, 3, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR4_EL1, 2, 0, 0, 4, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR4_EL1, 2, 0, 0, 4, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR4_EL1, 2, 0, 0, 4, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR4_EL1, 2, 0, 0, 4, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR5_EL1, 2, 0, 0, 5, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR5_EL1, 2, 0, 0, 5, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR5_EL1, 2, 0, 0, 5, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR5_EL1, 2, 0, 0, 5, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR6_EL1, 2, 0, 0, 6, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR6_EL1, 2, 0, 0, 6, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR6_EL1, 2, 0, 0, 6, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR6_EL1, 2, 0, 0, 6, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR7_EL1, 2, 0, 0, 7, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR7_EL1, 2, 0, 0, 7, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR7_EL1, 2, 0, 0, 7, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR7_EL1, 2, 0, 0, 7, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR8_EL1, 2, 0, 0, 8, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR8_EL1, 2, 0, 0, 8, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR8_EL1, 2, 0, 0, 8, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR8_EL1, 2, 0, 0, 8, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR9_EL1, 2, 0, 0, 9, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR9_EL1, 2, 0, 0, 9, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR9_EL1, 2, 0, 0, 9, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR9_EL1, 2, 0, 0, 9, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR10_EL1, 2, 0, 0, 10, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR10_EL1, 2, 0, 0, 10, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR10_EL1, 2, 0, 0, 10, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR10_EL1, 2, 0, 0, 10, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR11_EL1, 2, 0, 0, 11, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR11_EL1, 2, 0, 0, 11, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR11_EL1, 2, 0, 0, 11, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR11_EL1, 2, 0, 0, 11, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR12_EL1, 2, 0, 0, 12, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR12_EL1, 2, 0, 0, 12, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR12_EL1, 2, 0, 0, 12, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR12_EL1, 2, 0, 0, 12, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR13_EL1, 2, 0, 0, 13, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR13_EL1, 2, 0, 0, 13, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR13_EL1, 2, 0, 0, 13, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR13_EL1, 2, 0, 0, 13, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR14_EL1, 2, 0, 0, 14, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR14_EL1, 2, 0, 0, 14, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR14_EL1, 2, 0, 0, 14, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR14_EL1, 2, 0, 0, 14, 7) + +DEF_SYSREG(HV_SYS_REG_DBGBVR15_EL1, 2, 0, 0, 15, 4) +DEF_SYSREG(HV_SYS_REG_DBGBCR15_EL1, 2, 0, 0, 15, 5) +DEF_SYSREG(HV_SYS_REG_DBGWVR15_EL1, 2, 0, 0, 15, 6) +DEF_SYSREG(HV_SYS_REG_DBGWCR15_EL1, 2, 0, 0, 15, 7) #ifdef SYNC_NO_RAW_REGS /* * The registers below are manually synced on init because they are * marked as NO_RAW. We still list them to make number space sync easier. */ -DEF_SYSREG(HV_SYS_REG_MDCCINT_EL1, 0, 2, 2, 0, 0) -DEF_SYSREG(HV_SYS_REG_MIDR_EL1, 0, 0, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_MPIDR_EL1, 0, 0, 3, 0, 5) -DEF_SYSREG(HV_SYS_REG_ID_AA64PFR0_EL1, 0, 4, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_MDCCINT_EL1, 2, 0, 0, 2, 0) +DEF_SYSREG(HV_SYS_REG_MIDR_EL1, 3, 0, 0, 0, 0) +DEF_SYSREG(HV_SYS_REG_MPIDR_EL1, 3, 0, 0, 0, 5) +DEF_SYSREG(HV_SYS_REG_ID_AA64PFR0_EL1, 3, 0, 0, 4, 0) #endif -DEF_SYSREG(HV_SYS_REG_ID_AA64PFR1_EL1, 0, 4, 3, 0, 1) -DEF_SYSREG(HV_SYS_REG_ID_AA64DFR0_EL1, 0, 5, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_ID_AA64DFR1_EL1, 0, 5, 3, 0, 1) -DEF_SYSREG(HV_SYS_REG_ID_AA64ISAR0_EL1, 0, 6, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_ID_AA64ISAR1_EL1, 0, 6, 3, 0, 1) +DEF_SYSREG(HV_SYS_REG_ID_AA64PFR1_EL1, 3, 0, 0, 4, 1) +DEF_SYSREG(HV_SYS_REG_ID_AA64DFR0_EL1, 3, 0, 0, 5, 0) +DEF_SYSREG(HV_SYS_REG_ID_AA64DFR1_EL1, 3, 0, 0, 5, 1) +DEF_SYSREG(HV_SYS_REG_ID_AA64ISAR0_EL1, 3, 0, 0, 6, 0) +DEF_SYSREG(HV_SYS_REG_ID_AA64ISAR1_EL1, 3, 0, 0, 6, 1) #ifdef SYNC_NO_MMFR0 /* We keep the hardware MMFR0 around. HW limits are there anyway */ -DEF_SYSREG(HV_SYS_REG_ID_AA64MMFR0_EL1, 0, 7, 3, 0, 0) +DEF_SYSREG(HV_SYS_REG_ID_AA64MMFR0_EL1, 3, 0, 0, 7, 0) #endif -DEF_SYSREG(HV_SYS_REG_ID_AA64MMFR1_EL1, 0, 7, 3, 0, 1) -DEF_SYSREG(HV_SYS_REG_ID_AA64MMFR2_EL1, 0, 7, 3, 0, 2) +DEF_SYSREG(HV_SYS_REG_ID_AA64MMFR1_EL1, 3, 0, 0, 7, 1) +DEF_SYSREG(HV_SYS_REG_ID_AA64MMFR2_EL1, 3, 0, 0, 7, 2) /* Add ID_AA64MMFR3_EL1 here when HVF supports it */ -DEF_SYSREG(HV_SYS_REG_MDSCR_EL1, 0, 2, 2, 0, 2) -DEF_SYSREG(HV_SYS_REG_SCTLR_EL1, 1, 0, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_CPACR_EL1, 1, 0, 3, 0, 2) -DEF_SYSREG(HV_SYS_REG_TTBR0_EL1, 2, 0, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_TTBR1_EL1, 2, 0, 3, 0, 1) -DEF_SYSREG(HV_SYS_REG_TCR_EL1, 2, 0, 3, 0, 2) - -DEF_SYSREG(HV_SYS_REG_APIAKEYLO_EL1, 2, 1, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_APIAKEYHI_EL1, 2, 1, 3, 0, 1) -DEF_SYSREG(HV_SYS_REG_APIBKEYLO_EL1, 2, 1, 3, 0, 2) -DEF_SYSREG(HV_SYS_REG_APIBKEYHI_EL1, 2, 1, 3, 0, 3) -DEF_SYSREG(HV_SYS_REG_APDAKEYLO_EL1, 2, 2, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_APDAKEYHI_EL1, 2, 2, 3, 0, 1) -DEF_SYSREG(HV_SYS_REG_APDBKEYLO_EL1, 2, 2, 3, 0, 2) -DEF_SYSREG(HV_SYS_REG_APDBKEYHI_EL1, 2, 2, 3, 0, 3) -DEF_SYSREG(HV_SYS_REG_APGAKEYLO_EL1, 2, 3, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_APGAKEYHI_EL1, 2, 3, 3, 0, 1) - -DEF_SYSREG(HV_SYS_REG_SPSR_EL1, 4, 0, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_ELR_EL1, 4, 0, 3, 0, 1) -DEF_SYSREG(HV_SYS_REG_SP_EL0, 4, 1, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_AFSR0_EL1, 5, 1, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_AFSR1_EL1, 5, 1, 3, 0, 1) -DEF_SYSREG(HV_SYS_REG_ESR_EL1, 5, 2, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_FAR_EL1, 6, 0, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_PAR_EL1, 7, 4, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_MAIR_EL1, 10, 2, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_AMAIR_EL1, 10, 3, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_VBAR_EL1, 12, 0, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_CONTEXTIDR_EL1, 13, 0, 3, 0, 1) -DEF_SYSREG(HV_SYS_REG_TPIDR_EL1, 13, 0, 3, 0, 4) -DEF_SYSREG(HV_SYS_REG_CNTKCTL_EL1, 14, 1, 3, 0, 0) -DEF_SYSREG(HV_SYS_REG_CSSELR_EL1, 0, 0, 3, 2, 0) -DEF_SYSREG(HV_SYS_REG_TPIDR_EL0, 13, 0, 3, 3, 2) -DEF_SYSREG(HV_SYS_REG_TPIDRRO_EL0, 13, 0, 3, 3, 3) -DEF_SYSREG(HV_SYS_REG_CNTV_CTL_EL0, 14, 3, 3, 3, 1) -DEF_SYSREG(HV_SYS_REG_CNTV_CVAL_EL0, 14, 3, 3, 3, 2) -DEF_SYSREG(HV_SYS_REG_SP_EL1, 4, 1, 3, 4, 0) +DEF_SYSREG(HV_SYS_REG_MDSCR_EL1, 2, 0, 0, 2, 2) +DEF_SYSREG(HV_SYS_REG_SCTLR_EL1, 3, 0, 1, 0, 0) +DEF_SYSREG(HV_SYS_REG_CPACR_EL1, 3, 0, 1, 0, 2) +DEF_SYSREG(HV_SYS_REG_TTBR0_EL1, 3, 0, 2, 0, 0) +DEF_SYSREG(HV_SYS_REG_TTBR1_EL1, 3, 0, 2, 0, 1) +DEF_SYSREG(HV_SYS_REG_TCR_EL1, 3, 0, 2, 0, 2) + +DEF_SYSREG(HV_SYS_REG_APIAKEYLO_EL1, 3, 0, 2, 1, 0) +DEF_SYSREG(HV_SYS_REG_APIAKEYHI_EL1, 3, 0, 2, 1, 1) +DEF_SYSREG(HV_SYS_REG_APIBKEYLO_EL1, 3, 0, 2, 1, 2) +DEF_SYSREG(HV_SYS_REG_APIBKEYHI_EL1, 3, 0, 2, 1, 3) +DEF_SYSREG(HV_SYS_REG_APDAKEYLO_EL1, 3, 0, 2, 2, 0) +DEF_SYSREG(HV_SYS_REG_APDAKEYHI_EL1, 3, 0, 2, 2, 1) +DEF_SYSREG(HV_SYS_REG_APDBKEYLO_EL1, 3, 0, 2, 2, 2) +DEF_SYSREG(HV_SYS_REG_APDBKEYHI_EL1, 3, 0, 2, 2, 3) +DEF_SYSREG(HV_SYS_REG_APGAKEYLO_EL1, 3, 0, 2, 3, 0) +DEF_SYSREG(HV_SYS_REG_APGAKEYHI_EL1, 3, 0, 2, 3, 1) + +DEF_SYSREG(HV_SYS_REG_SPSR_EL1, 3, 0, 4, 0, 0) +DEF_SYSREG(HV_SYS_REG_ELR_EL1, 3, 0, 4, 0, 1) +DEF_SYSREG(HV_SYS_REG_SP_EL0, 3, 0, 4, 1, 0) +DEF_SYSREG(HV_SYS_REG_AFSR0_EL1, 3, 0, 5, 1, 0) +DEF_SYSREG(HV_SYS_REG_AFSR1_EL1, 3, 0, 5, 1, 1) +DEF_SYSREG(HV_SYS_REG_ESR_EL1, 3, 0, 5, 2, 0) +DEF_SYSREG(HV_SYS_REG_FAR_EL1, 3, 0, 6, 0, 0) +DEF_SYSREG(HV_SYS_REG_PAR_EL1, 3, 0, 7, 4, 0) +DEF_SYSREG(HV_SYS_REG_MAIR_EL1, 3, 0, 10, 2, 0) +DEF_SYSREG(HV_SYS_REG_AMAIR_EL1, 3, 0, 10, 3, 0) +DEF_SYSREG(HV_SYS_REG_VBAR_EL1, 3, 0, 12, 0, 0) +DEF_SYSREG(HV_SYS_REG_CONTEXTIDR_EL1, 3, 0, 13, 0, 1) +DEF_SYSREG(HV_SYS_REG_TPIDR_EL1, 3, 0, 13, 0, 4) +DEF_SYSREG(HV_SYS_REG_CNTKCTL_EL1, 3, 0, 14, 1, 0) +DEF_SYSREG(HV_SYS_REG_CSSELR_EL1, 3, 2, 0, 0, 0) +DEF_SYSREG(HV_SYS_REG_TPIDR_EL0, 3, 3, 13, 0, 2) +DEF_SYSREG(HV_SYS_REG_TPIDRRO_EL0, 3, 3, 13, 0, 3) +DEF_SYSREG(HV_SYS_REG_CNTV_CTL_EL0, 3, 3, 14, 3, 1) +DEF_SYSREG(HV_SYS_REG_CNTV_CVAL_EL0, 3, 3, 14, 3, 2) +DEF_SYSREG(HV_SYS_REG_SP_EL1, 3, 4, 4, 1, 0) From 7d4d89a437790952c43ccd1ed7970d4d5ac6e37c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:06 -0700 Subject: [PATCH 0866/1794] target/arm/hvf: Add KVMID_TO_HVF, HVF_TO_KVMID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Conversion between KVM system registers ids and the HVF system register ids is trivial. Reviewed-by: Manos Pitsidianakis Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index f68924ba1f343..7515e59c56de0 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -403,6 +403,26 @@ struct hvf_sreg_match { uint32_t cp_idx; }; +/* + * QEMU uses KVM system register ids in the migration format. + * Conveniently, HVF uses the same encoding of the op* and cr* parameters + * within the low 16 bits of the ids. Thus conversion between the + * formats is trivial. + */ + +#define KVMID_TO_HVF(KVM) ((KVM) & 0xffff) +#define HVF_TO_KVMID(HVF) \ + (CP_REG_ARM64 | CP_REG_SIZE_U64 | CP_REG_ARM64_SYSREG | (HVF)) + +/* Verify this at compile-time. */ + +#define DEF_SYSREG(HVF_ID, ...) \ + QEMU_BUILD_BUG_ON(HVF_ID != KVMID_TO_HVF(KVMID_AA64_SYS_REG64(__VA_ARGS__))); + +#include "sysreg.c.inc" + +#undef DEF_SYSREG + #define DEF_SYSREG(HVF_ID, op0, op1, crn, crm, op2) \ { HVF_ID, HVF_SYSREG(crn, crm, op0, op1, op2) }, From e6728fb3492fcfcd16d3a0f27e4bbf162c9a9291 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:07 -0700 Subject: [PATCH 0867/1794] target/arm/hvf: Remove hvf_sreg_match.key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use conversion functions instead of table lookup. Reviewed-by: Manos Pitsidianakis Signed-off-by: Richard Henderson Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 7515e59c56de0..98f49ce33a410 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -152,9 +152,6 @@ void hvf_arm_init_debug(void) g_array_sized_new(true, true, sizeof(HWWatchpoint), max_hw_wps); } -#define HVF_SYSREG(crn, crm, op0, op1, op2) \ - ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, crn, crm, op0, op1, op2) - #define SYSREG_OP0_SHIFT 20 #define SYSREG_OP0_MASK 0x3 #define SYSREG_OP0(sysreg) ((sysreg >> SYSREG_OP0_SHIFT) & SYSREG_OP0_MASK) @@ -399,7 +396,6 @@ static const struct hvf_reg_match hvf_fpreg_match[] = { struct hvf_sreg_match { int reg; - uint32_t key; uint32_t cp_idx; }; @@ -423,8 +419,7 @@ struct hvf_sreg_match { #undef DEF_SYSREG -#define DEF_SYSREG(HVF_ID, op0, op1, crn, crm, op2) \ - { HVF_ID, HVF_SYSREG(crn, crm, op0, op1, op2) }, +#define DEF_SYSREG(HVF_ID, op0, op1, crn, crm, op2) { HVF_ID }, static struct hvf_sreg_match hvf_sreg_match[] = { #include "sysreg.c.inc" @@ -469,13 +464,16 @@ int hvf_get_registers(CPUState *cpu) pstate_write(env, val); for (i = 0; i < ARRAY_SIZE(hvf_sreg_match); i++) { + int hvf_id = hvf_sreg_match[i].reg; + uint64_t kvm_id = HVF_TO_KVMID(hvf_id); + if (hvf_sreg_match[i].cp_idx == -1) { continue; } if (cpu->accel->guest_debug_enabled) { /* Handle debug registers */ - switch (hvf_sreg_match[i].reg) { + switch (hvf_id) { case HV_SYS_REG_DBGBVR0_EL1: case HV_SYS_REG_DBGBCR0_EL1: case HV_SYS_REG_DBGWVR0_EL1: @@ -549,8 +547,10 @@ int hvf_get_registers(CPUState *cpu) * vCPU but simply keep the values from the previous * environment. */ - const ARMCPRegInfo *ri; - ri = get_arm_cp_reginfo(arm_cpu->cp_regs, hvf_sreg_match[i].key); + uint32_t key = kvm_to_cpreg_id(kvm_id); + const ARMCPRegInfo *ri = + get_arm_cp_reginfo(arm_cpu->cp_regs, key); + val = read_raw_cp_reg(env, ri); arm_cpu->cpreg_values[hvf_sreg_match[i].cp_idx] = val; @@ -559,7 +559,7 @@ int hvf_get_registers(CPUState *cpu) } } - ret = hv_vcpu_get_sys_reg(cpu->accel->fd, hvf_sreg_match[i].reg, &val); + ret = hv_vcpu_get_sys_reg(cpu->accel->fd, hvf_id, &val); assert_hvf_ok(ret); arm_cpu->cpreg_values[hvf_sreg_match[i].cp_idx] = val; @@ -606,13 +606,15 @@ int hvf_put_registers(CPUState *cpu) assert(write_cpustate_to_list(arm_cpu, false)); for (i = 0; i < ARRAY_SIZE(hvf_sreg_match); i++) { + int hvf_id = hvf_sreg_match[i].reg; + if (hvf_sreg_match[i].cp_idx == -1) { continue; } if (cpu->accel->guest_debug_enabled) { /* Handle debug registers */ - switch (hvf_sreg_match[i].reg) { + switch (hvf_id) { case HV_SYS_REG_DBGBVR0_EL1: case HV_SYS_REG_DBGBCR0_EL1: case HV_SYS_REG_DBGWVR0_EL1: @@ -687,7 +689,7 @@ int hvf_put_registers(CPUState *cpu) } val = arm_cpu->cpreg_values[hvf_sreg_match[i].cp_idx]; - ret = hv_vcpu_set_sys_reg(cpu->accel->fd, hvf_sreg_match[i].reg, val); + ret = hv_vcpu_set_sys_reg(cpu->accel->fd, hvf_id, val); assert_hvf_ok(ret); } @@ -922,14 +924,15 @@ int hvf_arch_init_vcpu(CPUState *cpu) /* Populate cp list for all known sysregs */ for (i = 0; i < sregs_match_len; i++) { - const ARMCPRegInfo *ri; - uint32_t key = hvf_sreg_match[i].key; + int hvf_id = hvf_sreg_match[i].reg; + uint64_t kvm_id = HVF_TO_KVMID(hvf_id); + uint32_t key = kvm_to_cpreg_id(kvm_id); + const ARMCPRegInfo *ri = get_arm_cp_reginfo(arm_cpu->cp_regs, key); - ri = get_arm_cp_reginfo(arm_cpu->cp_regs, key); if (ri) { assert(!(ri->type & ARM_CP_NO_RAW)); hvf_sreg_match[i].cp_idx = sregs_cnt; - arm_cpu->cpreg_indexes[sregs_cnt++] = cpreg_to_kvm_id(key); + arm_cpu->cpreg_indexes[sregs_cnt++] = kvm_id; } else { hvf_sreg_match[i].cp_idx = -1; } From 98c2af435e653c34fb5f91eb1676ac458fd5cc78 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:08 -0700 Subject: [PATCH 0868/1794] target/arm/hvf: Replace hvf_sreg_match with hvf_sreg_list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change hvf_get_registers and hvf_put_registers to iterate over cpregs_indexes instead of hvf_sreg_match. This lets us drop the cp_idx member of hvf_sreg_match, which leaves only one member in the struct. Replace the struct with a const array. Instead of int, use the proper enum type: hv_sys_reg_t. Rename from hvf_sreg_match to hvf_sreg_list because there is no longer any matching going on. Signed-off-by: Richard Henderson Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 45 +++++++++++++++----------------------------- 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 98f49ce33a410..b043eac8c62a6 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -394,11 +394,6 @@ static const struct hvf_reg_match hvf_fpreg_match[] = { { HV_SIMD_FP_REG_Q31, offsetof(CPUARMState, vfp.zregs[31]) }, }; -struct hvf_sreg_match { - int reg; - uint32_t cp_idx; -}; - /* * QEMU uses KVM system register ids in the migration format. * Conveniently, HVF uses the same encoding of the op* and cr* parameters @@ -419,9 +414,9 @@ struct hvf_sreg_match { #undef DEF_SYSREG -#define DEF_SYSREG(HVF_ID, op0, op1, crn, crm, op2) { HVF_ID }, +#define DEF_SYSREG(HVF_ID, op0, op1, crn, crm, op2) HVF_ID, -static struct hvf_sreg_match hvf_sreg_match[] = { +static const hv_sys_reg_t hvf_sreg_list[] = { #include "sysreg.c.inc" }; @@ -434,7 +429,7 @@ int hvf_get_registers(CPUState *cpu) hv_return_t ret; uint64_t val; hv_simd_fp_uchar16_t fpval; - int i; + int i, n; for (i = 0; i < ARRAY_SIZE(hvf_reg_match); i++) { ret = hv_vcpu_get_reg(cpu->accel->fd, hvf_reg_match[i].reg, &val); @@ -463,13 +458,9 @@ int hvf_get_registers(CPUState *cpu) assert_hvf_ok(ret); pstate_write(env, val); - for (i = 0; i < ARRAY_SIZE(hvf_sreg_match); i++) { - int hvf_id = hvf_sreg_match[i].reg; - uint64_t kvm_id = HVF_TO_KVMID(hvf_id); - - if (hvf_sreg_match[i].cp_idx == -1) { - continue; - } + for (i = 0, n = arm_cpu->cpreg_array_len; i < n; i++) { + uint64_t kvm_id = arm_cpu->cpreg_indexes[i]; + int hvf_id = KVMID_TO_HVF(kvm_id); if (cpu->accel->guest_debug_enabled) { /* Handle debug registers */ @@ -553,7 +544,7 @@ int hvf_get_registers(CPUState *cpu) val = read_raw_cp_reg(env, ri); - arm_cpu->cpreg_values[hvf_sreg_match[i].cp_idx] = val; + arm_cpu->cpreg_values[i] = val; continue; } } @@ -562,7 +553,7 @@ int hvf_get_registers(CPUState *cpu) ret = hv_vcpu_get_sys_reg(cpu->accel->fd, hvf_id, &val); assert_hvf_ok(ret); - arm_cpu->cpreg_values[hvf_sreg_match[i].cp_idx] = val; + arm_cpu->cpreg_values[i] = val; } assert(write_list_to_cpustate(arm_cpu)); @@ -578,7 +569,7 @@ int hvf_put_registers(CPUState *cpu) hv_return_t ret; uint64_t val; hv_simd_fp_uchar16_t fpval; - int i; + int i, n; for (i = 0; i < ARRAY_SIZE(hvf_reg_match); i++) { val = *(uint64_t *)((void *)env + hvf_reg_match[i].offset); @@ -605,12 +596,9 @@ int hvf_put_registers(CPUState *cpu) aarch64_save_sp(env, arm_current_el(env)); assert(write_cpustate_to_list(arm_cpu, false)); - for (i = 0; i < ARRAY_SIZE(hvf_sreg_match); i++) { - int hvf_id = hvf_sreg_match[i].reg; - - if (hvf_sreg_match[i].cp_idx == -1) { - continue; - } + for (i = 0, n = arm_cpu->cpreg_array_len; i < n; i++) { + uint64_t kvm_id = arm_cpu->cpreg_indexes[i]; + int hvf_id = KVMID_TO_HVF(kvm_id); if (cpu->accel->guest_debug_enabled) { /* Handle debug registers */ @@ -688,7 +676,7 @@ int hvf_put_registers(CPUState *cpu) } } - val = arm_cpu->cpreg_values[hvf_sreg_match[i].cp_idx]; + val = arm_cpu->cpreg_values[i]; ret = hv_vcpu_set_sys_reg(cpu->accel->fd, hvf_id, val); assert_hvf_ok(ret); } @@ -899,7 +887,7 @@ int hvf_arch_init_vcpu(CPUState *cpu) { ARMCPU *arm_cpu = ARM_CPU(cpu); CPUARMState *env = &arm_cpu->env; - uint32_t sregs_match_len = ARRAY_SIZE(hvf_sreg_match); + uint32_t sregs_match_len = ARRAY_SIZE(hvf_sreg_list); uint32_t sregs_cnt = 0; uint64_t pfr; hv_return_t ret; @@ -924,17 +912,14 @@ int hvf_arch_init_vcpu(CPUState *cpu) /* Populate cp list for all known sysregs */ for (i = 0; i < sregs_match_len; i++) { - int hvf_id = hvf_sreg_match[i].reg; + hv_sys_reg_t hvf_id = hvf_sreg_list[i]; uint64_t kvm_id = HVF_TO_KVMID(hvf_id); uint32_t key = kvm_to_cpreg_id(kvm_id); const ARMCPRegInfo *ri = get_arm_cp_reginfo(arm_cpu->cp_regs, key); if (ri) { assert(!(ri->type & ARM_CP_NO_RAW)); - hvf_sreg_match[i].cp_idx = sregs_cnt; arm_cpu->cpreg_indexes[sregs_cnt++] = kvm_id; - } else { - hvf_sreg_match[i].cp_idx = -1; } } arm_cpu->cpreg_array_len = sregs_cnt; From bffe756ea10dfc5795637911f52f964d897b3283 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:09 -0700 Subject: [PATCH 0869/1794] target/arm/hvf: Sort the cpreg_indexes array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index b043eac8c62a6..99d8672b9bc44 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -925,6 +925,9 @@ int hvf_arch_init_vcpu(CPUState *cpu) arm_cpu->cpreg_array_len = sregs_cnt; arm_cpu->cpreg_vmstate_array_len = sregs_cnt; + /* cpreg tuples must be in strictly ascending order */ + qsort(arm_cpu->cpreg_indexes, sregs_cnt, sizeof(uint64_t), compare_u64); + assert(write_cpustate_to_list(arm_cpu, false)); /* Set CP_NO_RAW system registers on init */ From acf2b6e6b96890cf0becd9fd17288d8e8b37394c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:10 -0700 Subject: [PATCH 0870/1794] target/arm/hvf: Use raw_read, raw_write to access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce the places that know about field types by 2. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 99d8672b9bc44..694584cc1302e 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1153,7 +1153,7 @@ static bool hvf_sysreg_read_cp(CPUState *cpu, uint32_t reg, uint64_t *val) } else if (ri->readfn) { *val = ri->readfn(env, ri); } else { - *val = CPREG_FIELD64(env, ri); + *val = raw_read(env, ri); } trace_hvf_vgic_read(ri->name, *val); return true; @@ -1435,7 +1435,7 @@ static bool hvf_sysreg_write_cp(CPUState *cpu, uint32_t reg, uint64_t val) if (ri->writefn) { ri->writefn(env, ri, val); } else { - CPREG_FIELD64(env, ri) = val; + raw_write(env, ri, val); } trace_hvf_vgic_write(ri->name, val); From 45fc665f8be54c7defc16016eb02d0aff8ac832e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:11 -0700 Subject: [PATCH 0871/1794] target/arm: Use raw_write in cp_reg_reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce the places that know about field types by 1. Reviewed-by: Manos Pitsidianakis Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/cpu.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index c65af7e7614a6..91ae56dddb2f9 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -192,14 +192,8 @@ static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) * This is basically only used for fields in non-core coprocessors * (like the pxa2xx ones). */ - if (!ri->fieldoffset) { - return; - } - - if (cpreg_field_is_64bit(ri)) { - CPREG_FIELD64(&cpu->env, ri) = ri->resetvalue; - } else { - CPREG_FIELD32(&cpu->env, ri) = ri->resetvalue; + if (ri->fieldoffset) { + raw_write(&cpu->env, ri, ri->resetvalue); } } From 5470f91ad19d051202d4d6931185f6a56b627c81 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:12 -0700 Subject: [PATCH 0872/1794] target/arm: Rename all ARMCPRegInfo from opaque to ri MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These pointers are no opaque, they have a specific type. Reviewed-by: Manos Pitsidianakis Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/cpregs.h | 10 +++++----- target/arm/helper.c | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 2a4826f5c4ff1..8f3e728d8eda7 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -866,15 +866,15 @@ typedef struct ARMCPRegInfo ARMCPRegInfo; * Access functions for coprocessor registers. These cannot fail and * may not raise exceptions. */ -typedef uint64_t CPReadFn(CPUARMState *env, const ARMCPRegInfo *opaque); -typedef void CPWriteFn(CPUARMState *env, const ARMCPRegInfo *opaque, +typedef uint64_t CPReadFn(CPUARMState *env, const ARMCPRegInfo *ri); +typedef void CPWriteFn(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value); /* Access permission check functions for coprocessor registers. */ typedef CPAccessResult CPAccessFn(CPUARMState *env, - const ARMCPRegInfo *opaque, + const ARMCPRegInfo *ri, bool isread); /* Hook function for register reset */ -typedef void CPResetFn(CPUARMState *env, const ARMCPRegInfo *opaque); +typedef void CPResetFn(CPUARMState *env, const ARMCPRegInfo *ri); #define CP_ANY 0xff @@ -1100,7 +1100,7 @@ void raw_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value); * CPResetFn that does nothing, for use if no reset is required even * if fieldoffset is non zero. */ -void arm_cp_reset_ignore(CPUARMState *env, const ARMCPRegInfo *opaque); +void arm_cp_reset_ignore(CPUARMState *env, const ARMCPRegInfo *ri); /* * Return true if this reginfo struct's field in the cpu state struct diff --git a/target/arm/helper.c b/target/arm/helper.c index 009f8d6fa1cc7..7b23e7e58897e 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -1073,7 +1073,7 @@ static const ARMCPRegInfo v6k_cp_reginfo[] = { .resetvalue = 0 }, }; -static void arm_gt_cntfrq_reset(CPUARMState *env, const ARMCPRegInfo *opaque) +static void arm_gt_cntfrq_reset(CPUARMState *env, const ARMCPRegInfo *ri) { ARMCPU *cpu = env_archcpu(env); @@ -5382,7 +5382,7 @@ static const ARMCPRegInfo rndr_reginfo[] = { .access = PL0_R, .readfn = rndr_readfn }, }; -static void dccvap_writefn(CPUARMState *env, const ARMCPRegInfo *opaque, +static void dccvap_writefn(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { #ifdef CONFIG_TCG @@ -7829,7 +7829,7 @@ uint64_t arm_cp_read_zero(CPUARMState *env, const ARMCPRegInfo *ri) return 0; } -void arm_cp_reset_ignore(CPUARMState *env, const ARMCPRegInfo *opaque) +void arm_cp_reset_ignore(CPUARMState *env, const ARMCPRegInfo *ri) { /* Helper coprocessor reset function for do-nothing-on-reset registers */ } From 166e7990566bcfe49e2a68443ea314a1ef7066a9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:13 -0700 Subject: [PATCH 0873/1794] target/arm: Drop define_one_arm_cp_reg_with_opaque MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last use of this interface was removed in 603bc048a27f ("hw/arm: Remove pxa2xx_pic"). As the comment in gicv3 stated, keeping pointer references to cpregs has SMP issues, so avoid future temptation by removing the interface. Reviewed-by: Manos Pitsidianakis Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_cpuif.c | 10 +--------- target/arm/cpregs.h | 32 ++++++++------------------------ target/arm/helper.c | 29 +++++++++++------------------ 3 files changed, 20 insertions(+), 51 deletions(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 4b4cf09157053..72e91f971a46f 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -3037,15 +3037,7 @@ void gicv3_init_cpuif(GICv3State *s) * cpu->gic_pribits */ - /* Note that we can't just use the GICv3CPUState as an opaque pointer - * in define_arm_cp_regs_with_opaque(), because when we're called back - * it might be with code translated by CPU 0 but run by CPU 1, in - * which case we'd get the wrong value. - * So instead we define the regs with no ri->opaque info, and - * get back to the GICv3CPUState from the CPUARMState. - * - * These CP regs callbacks can be called from either TCG or HVF code. - */ + /* These CP regs callbacks can be called from either TCG or HVF. */ define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); /* diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 8f3e728d8eda7..d02d74f1f5d61 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -931,11 +931,7 @@ struct ARMCPRegInfo { */ uint32_t nv2_redirect_offset; - /* - * The opaque pointer passed to define_arm_cp_regs_with_opaque() when - * this register was defined: can be used to hand data through to the - * register read/write functions, since they are passed the ARMCPRegInfo*. - */ + /* This is used only by VHE. */ void *opaque; /* * Value of this register, if it is ARM_CP_CONST. Otherwise, if @@ -1029,27 +1025,15 @@ struct ARMCPRegInfo { #define CPREG_FIELD64(env, ri) \ (*(uint64_t *)((char *)(env) + (ri)->fieldoffset)) -void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, const ARMCPRegInfo *reg, - void *opaque); - -static inline void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *regs) -{ - define_one_arm_cp_reg_with_opaque(cpu, regs, NULL); -} - -void define_arm_cp_regs_with_opaque_len(ARMCPU *cpu, const ARMCPRegInfo *regs, - void *opaque, size_t len); +void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *regs); +void define_arm_cp_regs_len(ARMCPU *cpu, const ARMCPRegInfo *regs, size_t len); -#define define_arm_cp_regs_with_opaque(CPU, REGS, OPAQUE) \ - do { \ - QEMU_BUILD_BUG_ON(ARRAY_SIZE(REGS) == 0); \ - define_arm_cp_regs_with_opaque_len(CPU, REGS, OPAQUE, \ - ARRAY_SIZE(REGS)); \ +#define define_arm_cp_regs(CPU, REGS) \ + do { \ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(REGS) == 0); \ + define_arm_cp_regs_len(CPU, REGS, ARRAY_SIZE(REGS)); \ } while (0) -#define define_arm_cp_regs(CPU, REGS) \ - define_arm_cp_regs_with_opaque(CPU, REGS, NULL) - const ARMCPRegInfo *get_arm_cp_reginfo(GHashTable *cpregs, uint32_t encoded_cp); /* @@ -1168,7 +1152,7 @@ static inline bool arm_cpreg_traps_in_nv(const ARMCPRegInfo *ri) * means that the right set of registers is exactly those where * the opc1 field is 4 or 5. (You can see this also in the assert * we do that the opc1 field and the permissions mask line up in - * define_one_arm_cp_reg_with_opaque().) + * define_one_arm_cp_reg().) * Checking the opc1 field is easier for us and avoids the problem * that we do not consistently use the right architectural names * for all sysregs, since we treat the name field as largely for debug. diff --git a/target/arm/helper.c b/target/arm/helper.c index 7b23e7e58897e..b76a0edb0f4ae 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7355,12 +7355,11 @@ void register_cp_regs_for_features(ARMCPU *cpu) } /* - * Private utility function for define_one_arm_cp_reg_with_opaque(): + * Private utility function for define_one_arm_cp_reg(): * add a single reginfo struct to the hash table. */ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, - void *opaque, CPState state, - CPSecureState secstate, + CPState state, CPSecureState secstate, int crm, int opc1, int opc2, const char *name) { @@ -7448,9 +7447,6 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, r2->opc2 = opc2; r2->state = state; r2->secure = secstate; - if (opaque) { - r2->opaque = opaque; - } if (make_const) { /* This should not have been a very special register to begin. */ @@ -7555,8 +7551,7 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, } -void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, - const ARMCPRegInfo *r, void *opaque) +void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) { /* * Define implementations of coprocessor registers. @@ -7715,7 +7710,7 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, if (nxs_ri.fgt) { nxs_ri.fgt |= R_FGT_NXS_MASK; } - add_cpreg_to_hashtable(cpu, &nxs_ri, opaque, state, + add_cpreg_to_hashtable(cpu, &nxs_ri, state, ARM_CP_SECSTATE_NS, crm, opc1, opc2, name); } @@ -7729,17 +7724,17 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, switch (r->secure) { case ARM_CP_SECSTATE_S: case ARM_CP_SECSTATE_NS: - add_cpreg_to_hashtable(cpu, r, opaque, state, + add_cpreg_to_hashtable(cpu, r, state, r->secure, crm, opc1, opc2, r->name); break; case ARM_CP_SECSTATE_BOTH: name = g_strdup_printf("%s_S", r->name); - add_cpreg_to_hashtable(cpu, r, opaque, state, + add_cpreg_to_hashtable(cpu, r, state, ARM_CP_SECSTATE_S, crm, opc1, opc2, name); g_free(name); - add_cpreg_to_hashtable(cpu, r, opaque, state, + add_cpreg_to_hashtable(cpu, r, state, ARM_CP_SECSTATE_NS, crm, opc1, opc2, r->name); break; @@ -7751,7 +7746,7 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, * AArch64 registers get mapped to non-secure instance * of AArch32 */ - add_cpreg_to_hashtable(cpu, r, opaque, state, + add_cpreg_to_hashtable(cpu, r, state, ARM_CP_SECSTATE_NS, crm, opc1, opc2, r->name); } @@ -7762,12 +7757,10 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, } /* Define a whole list of registers */ -void define_arm_cp_regs_with_opaque_len(ARMCPU *cpu, const ARMCPRegInfo *regs, - void *opaque, size_t len) +void define_arm_cp_regs_len(ARMCPU *cpu, const ARMCPRegInfo *regs, size_t len) { - size_t i; - for (i = 0; i < len; ++i) { - define_one_arm_cp_reg_with_opaque(cpu, regs + i, opaque); + for (size_t i = 0; i < len; ++i) { + define_one_arm_cp_reg(cpu, regs + i); } } From f570b394d2cc4d1c8128dd6c2b0493b9d58bc296 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:14 -0700 Subject: [PATCH 0874/1794] target/arm: Restrict the scope of CPREG_FIELD32, CPREG_FIELD64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Manos Pitsidianakis Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/cpregs.h | 9 --------- target/arm/helper.c | 12 ++++++++++++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index d02d74f1f5d61..6fb1994afa03e 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -1016,15 +1016,6 @@ struct ARMCPRegInfo { CPAccessFn *orig_accessfn; }; -/* - * Macros which are lvalues for the field in CPUARMState for the - * ARMCPRegInfo *ri. - */ -#define CPREG_FIELD32(env, ri) \ - (*(uint32_t *)((char *)(env) + (ri)->fieldoffset)) -#define CPREG_FIELD64(env, ri) \ - (*(uint64_t *)((char *)(env) + (ri)->fieldoffset)) - void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *regs); void define_arm_cp_regs_len(ARMCPU *cpu, const ARMCPRegInfo *regs, size_t len); diff --git a/target/arm/helper.c b/target/arm/helper.c index b76a0edb0f4ae..fe298670f121f 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -51,6 +51,15 @@ int compare_u64(const void *a, const void *b) return 0; } +/* + * Macros which are lvalues for the field in CPUARMState for the + * ARMCPRegInfo *ri. + */ +#define CPREG_FIELD32(env, ri) \ + (*(uint32_t *)((char *)(env) + (ri)->fieldoffset)) +#define CPREG_FIELD64(env, ri) \ + (*(uint64_t *)((char *)(env) + (ri)->fieldoffset)) + uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri) { assert(ri->fieldoffset); @@ -71,6 +80,9 @@ void raw_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) } } +#undef CPREG_FIELD32 +#undef CPREG_FIELD64 + static void *raw_ptr(CPUARMState *env, const ARMCPRegInfo *ri) { return (char *)env + ri->fieldoffset; From 1e3c93b161d721da5528041c8388b6d5931c0f48 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:15 -0700 Subject: [PATCH 0875/1794] target/arm: Replace cpreg_field_is_64bit with cpreg_field_type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare for 128-bit fields by using a better query api. Reviewed-by: Manos Pitsidianakis Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/cpregs.h | 10 ++++++---- target/arm/gdbstub.c | 7 +++++-- target/arm/helper.c | 18 +++++++++++++----- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 6fb1994afa03e..74bae7309a3da 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -22,6 +22,7 @@ #define TARGET_ARM_CPREGS_H #include "hw/registerfields.h" +#include "exec/memop.h" #include "target/arm/kvm-consts.h" #include "cpu.h" @@ -1078,12 +1079,13 @@ void raw_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value); void arm_cp_reset_ignore(CPUARMState *env, const ARMCPRegInfo *ri); /* - * Return true if this reginfo struct's field in the cpu state struct - * is 64 bits wide. + * Return MO_32 if the field in CPUARMState is uint32_t or + * MO_64 if the field in CPUARMState is uint64_t. */ -static inline bool cpreg_field_is_64bit(const ARMCPRegInfo *ri) +static inline MemOp cpreg_field_type(const ARMCPRegInfo *ri) { - return (ri->state == ARM_CP_STATE_AA64) || (ri->type & ARM_CP_64BIT); + return (ri->state == ARM_CP_STATE_AA64 || (ri->type & ARM_CP_64BIT) + ? MO_64 : MO_32); } static inline bool cp_access_ok(int current_el, diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index 2d331fff445b5..4e2ac49b6a9d0 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -247,10 +247,13 @@ static int arm_gdb_get_sysreg(CPUState *cs, GByteArray *buf, int reg) key = cpu->dyn_sysreg_feature.data.cpregs.keys[reg]; ri = get_arm_cp_reginfo(cpu->cp_regs, key); if (ri) { - if (cpreg_field_is_64bit(ri)) { + switch (cpreg_field_type(ri)) { + case MO_64: return gdb_get_reg64(buf, (uint64_t)read_raw_cp_reg(env, ri)); - } else { + case MO_32: return gdb_get_reg32(buf, (uint32_t)read_raw_cp_reg(env, ri)); + default: + g_assert_not_reached(); } } return 0; diff --git a/target/arm/helper.c b/target/arm/helper.c index fe298670f121f..26941ecd4f832 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -63,20 +63,28 @@ int compare_u64(const void *a, const void *b) uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri) { assert(ri->fieldoffset); - if (cpreg_field_is_64bit(ri)) { + switch (cpreg_field_type(ri)) { + case MO_64: return CPREG_FIELD64(env, ri); - } else { + case MO_32: return CPREG_FIELD32(env, ri); + default: + g_assert_not_reached(); } } void raw_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { assert(ri->fieldoffset); - if (cpreg_field_is_64bit(ri)) { + switch (cpreg_field_type(ri)) { + case MO_64: CPREG_FIELD64(env, ri) = value; - } else { + break; + case MO_32: CPREG_FIELD32(env, ri) = value; + break; + default: + g_assert_not_reached(); } } @@ -2754,7 +2762,7 @@ static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { /* If the ASID changes (with a 64-bit write), we must flush the TLB. */ - if (cpreg_field_is_64bit(ri) && + if (cpreg_field_type(ri) == MO_64 && extract64(raw_read(env, ri) ^ value, 48, 16) != 0) { ARMCPU *cpu = env_archcpu(env); tlb_flush(CPU(cpu)); From 8434f54f3eec1e19eafac2d883a1e945b12f52f9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:16 -0700 Subject: [PATCH 0876/1794] target/arm: Add CP_REG_AA32_64BIT_{SHIFT,MASK} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Give a name to the bit we're already using. Reviewed-by: Manos Pitsidianakis Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/cpregs.h | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 74bae7309a3da..f7dd6d2f75842 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -178,9 +178,14 @@ enum { #define CP_REG_NS_SHIFT 29 #define CP_REG_NS_MASK (1 << CP_REG_NS_SHIFT) +/* Distinguish 32-bit and 64-bit views of AArch32 system registers. */ +#define CP_REG_AA32_64BIT_SHIFT 15 +#define CP_REG_AA32_64BIT_MASK (1 << CP_REG_AA32_64BIT_SHIFT) + #define ENCODE_CP_REG(cp, is64, ns, crn, crm, opc1, opc2) \ - ((ns) << CP_REG_NS_SHIFT | ((cp) << 16) | ((is64) << 15) | \ - ((crn) << 11) | ((crm) << 7) | ((opc1) << 3) | (opc2)) + (((ns) << CP_REG_NS_SHIFT) | \ + ((is64) << CP_REG_AA32_64BIT_SHIFT) | \ + ((cp) << 16) | ((crn) << 11) | ((crm) << 7) | ((opc1) << 3) | (opc2)) #define ENCODE_AA64_CP_REG(cp, crn, crm, op0, op1, op2) \ (CP_REG_AA64_MASK | \ @@ -202,7 +207,7 @@ static inline uint32_t kvm_to_cpreg_id(uint64_t kvmid) cpregid |= CP_REG_AA64_MASK; } else { if ((kvmid & CP_REG_SIZE_MASK) == CP_REG_SIZE_U64) { - cpregid |= (1 << 15); + cpregid |= CP_REG_AA32_64BIT_MASK; } /* @@ -226,8 +231,8 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) kvmid = cpregid & ~CP_REG_AA64_MASK; kvmid |= CP_REG_SIZE_U64 | CP_REG_ARM64; } else { - kvmid = cpregid & ~(1 << 15); - if (cpregid & (1 << 15)) { + kvmid = cpregid & ~CP_REG_AA32_64BIT_MASK; + if (cpregid & CP_REG_AA32_64BIT_MASK) { kvmid |= CP_REG_SIZE_U64 | CP_REG_ARM; } else { kvmid |= CP_REG_SIZE_U32 | CP_REG_ARM; From 7015fbabde447abe7947a7112dff6b592ae664b1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:17 -0700 Subject: [PATCH 0877/1794] target/arm: Rename CP_REG_AA32_NS_{SHIFT,MASK} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename from CP_REG_NS_* to emphasize this is specific to AArch32. Reviewed-by: Manos Pitsidianakis Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/cpregs.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index f7dd6d2f75842..417d79f7ba6cb 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -175,15 +175,15 @@ enum { * add a bit to distinguish between secure and non-secure cpregs in the * hashtable. */ -#define CP_REG_NS_SHIFT 29 -#define CP_REG_NS_MASK (1 << CP_REG_NS_SHIFT) +#define CP_REG_AA32_NS_SHIFT 29 +#define CP_REG_AA32_NS_MASK (1 << CP_REG_AA32_NS_SHIFT) /* Distinguish 32-bit and 64-bit views of AArch32 system registers. */ #define CP_REG_AA32_64BIT_SHIFT 15 #define CP_REG_AA32_64BIT_MASK (1 << CP_REG_AA32_64BIT_SHIFT) #define ENCODE_CP_REG(cp, is64, ns, crn, crm, opc1, opc2) \ - (((ns) << CP_REG_NS_SHIFT) | \ + (((ns) << CP_REG_AA32_NS_SHIFT) | \ ((is64) << CP_REG_AA32_64BIT_SHIFT) | \ ((cp) << 16) | ((crn) << 11) | ((crm) << 7) | ((opc1) << 3) | (opc2)) @@ -214,7 +214,7 @@ static inline uint32_t kvm_to_cpreg_id(uint64_t kvmid) * KVM is always non-secure so add the NS flag on AArch32 register * entries. */ - cpregid |= 1 << CP_REG_NS_SHIFT; + cpregid |= CP_REG_AA32_NS_MASK; } return cpregid; } From dee3c0c2cf9848cd849744474cdac108ce68a1ef Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:18 -0700 Subject: [PATCH 0878/1794] target/arm: Convert init_cpreg_list to g_hash_table_foreach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adjust count_cpreg and add_cpreg_to_list to be used with g_hash_table_foreach instead of g_list_foreach. In this way we have the ARMCPRegInfo pointer directly rather than having to look it up from the key. Delay the sorting of the cpreg_indexes until after add_cpreg_to_list. This allows us to sort the data that we actually care about, the kvm id, as computed within add_cpreg_to_list, instead of having to repeatedly compute the kvm id within cpreg_key_compare. Signed-off-by: Richard Henderson Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/helper.c | 54 ++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 26941ecd4f832..27d5ab82920bc 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -229,11 +229,11 @@ bool write_list_to_cpustate(ARMCPU *cpu) return ok; } -static void add_cpreg_to_list(gpointer key, gpointer opaque) +static void add_cpreg_to_list(gpointer key, gpointer value, gpointer opaque) { ARMCPU *cpu = opaque; uint32_t regidx = (uintptr_t)key; - const ARMCPRegInfo *ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); + const ARMCPRegInfo *ri = value; if (!(ri->type & (ARM_CP_NO_RAW | ARM_CP_ALIAS))) { cpu->cpreg_indexes[cpu->cpreg_array_len] = cpreg_to_kvm_id(regidx); @@ -242,61 +242,49 @@ static void add_cpreg_to_list(gpointer key, gpointer opaque) } } -static void count_cpreg(gpointer key, gpointer opaque) +static void count_cpreg(gpointer key, gpointer value, gpointer opaque) { ARMCPU *cpu = opaque; - const ARMCPRegInfo *ri; - - ri = g_hash_table_lookup(cpu->cp_regs, key); + const ARMCPRegInfo *ri = value; if (!(ri->type & (ARM_CP_NO_RAW | ARM_CP_ALIAS))) { cpu->cpreg_array_len++; } } -static gint cpreg_key_compare(gconstpointer a, gconstpointer b, gpointer d) -{ - uint64_t aidx = cpreg_to_kvm_id((uintptr_t)a); - uint64_t bidx = cpreg_to_kvm_id((uintptr_t)b); - - if (aidx > bidx) { - return 1; - } - if (aidx < bidx) { - return -1; - } - return 0; -} - void init_cpreg_list(ARMCPU *cpu) { /* * Initialise the cpreg_tuples[] array based on the cp_regs hash. * Note that we require cpreg_tuples[] to be sorted by key ID. */ - GList *keys; int arraylen; - keys = g_hash_table_get_keys(cpu->cp_regs); - keys = g_list_sort_with_data(keys, cpreg_key_compare, NULL); - cpu->cpreg_array_len = 0; - - g_list_foreach(keys, count_cpreg, cpu); + g_hash_table_foreach(cpu->cp_regs, count_cpreg, cpu); arraylen = cpu->cpreg_array_len; - cpu->cpreg_indexes = g_new(uint64_t, arraylen); - cpu->cpreg_values = g_new(uint64_t, arraylen); - cpu->cpreg_vmstate_indexes = g_new(uint64_t, arraylen); - cpu->cpreg_vmstate_values = g_new(uint64_t, arraylen); - cpu->cpreg_vmstate_array_len = cpu->cpreg_array_len; + if (arraylen) { + cpu->cpreg_indexes = g_new(uint64_t, arraylen); + cpu->cpreg_values = g_new(uint64_t, arraylen); + cpu->cpreg_vmstate_indexes = g_new(uint64_t, arraylen); + cpu->cpreg_vmstate_values = g_new(uint64_t, arraylen); + } else { + cpu->cpreg_indexes = NULL; + cpu->cpreg_values = NULL; + cpu->cpreg_vmstate_indexes = NULL; + cpu->cpreg_vmstate_values = NULL; + } + cpu->cpreg_vmstate_array_len = arraylen; cpu->cpreg_array_len = 0; - g_list_foreach(keys, add_cpreg_to_list, cpu); + g_hash_table_foreach(cpu->cp_regs, add_cpreg_to_list, cpu); assert(cpu->cpreg_array_len == arraylen); - g_list_free(keys); + if (arraylen) { + qsort(cpu->cpreg_indexes, arraylen, sizeof(uint64_t), compare_u64); + } } bool arm_pan_enabled(CPUARMState *env) From 304ca4b827d91ab2fb7c76bb3d6315a2dc5ee8f1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:19 -0700 Subject: [PATCH 0879/1794] target/arm: Remove cp argument to ENCODE_AA64_CP_REG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All invocations were required to pass the same value, CP_REG_ARM64_SYSREG_CP. Bake that in to the result directly. Remove CP_REG_ARM64_SYSREG_CP as unused. Reviewed-by: Manos Pitsidianakis Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/cpregs.h | 5 ++--- target/arm/helper.c | 11 +++++------ target/arm/hvf/hvf.c | 3 +-- target/arm/kvm-consts.h | 3 --- target/arm/tcg/translate-a64.c | 6 ++---- 5 files changed, 10 insertions(+), 18 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 417d79f7ba6cb..a10abadb93298 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -187,9 +187,8 @@ enum { ((is64) << CP_REG_AA32_64BIT_SHIFT) | \ ((cp) << 16) | ((crn) << 11) | ((crm) << 7) | ((opc1) << 3) | (opc2)) -#define ENCODE_AA64_CP_REG(cp, crn, crm, op0, op1, op2) \ - (CP_REG_AA64_MASK | \ - ((cp) << CP_REG_ARM_COPROC_SHIFT) | \ +#define ENCODE_AA64_CP_REG(crn, crm, op0, op1, op2) \ + (CP_REG_AA64_MASK | CP_REG_ARM64_SYSREG | \ ((op0) << CP_REG_ARM64_SYSREG_OP0_SHIFT) | \ ((op1) << CP_REG_ARM64_SYSREG_OP1_SHIFT) | \ ((crn) << CP_REG_ARM64_SYSREG_CRN_SHIFT) | \ diff --git a/target/arm/helper.c b/target/arm/helper.c index 27d5ab82920bc..2732112ff21eb 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4503,7 +4503,7 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) }; #define K(op0, op1, crn, crm, op2) \ - ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, crn, crm, op0, op1, op2) + ENCODE_AA64_CP_REG(crn, crm, op0, op1, op2) static const struct E2HAlias aliases[] = { { K(3, 0, 1, 0, 0), K(3, 4, 1, 0, 0), K(3, 5, 1, 0, 0), @@ -7396,10 +7396,9 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, * in their AArch64 view (the .cp value may be non-zero for the * benefit of the AArch32 view). */ - if (cp == 0 || r->state == ARM_CP_STATE_BOTH) { - cp = CP_REG_ARM64_SYSREG_CP; - } - key = ENCODE_AA64_CP_REG(cp, r->crn, crm, r->opc0, opc1, opc2); + assert(cp == 0 || r->state == ARM_CP_STATE_BOTH); + cp = 0; + key = ENCODE_AA64_CP_REG(r->crn, crm, r->opc0, opc1, opc2); break; default: g_assert_not_reached(); @@ -7624,7 +7623,7 @@ void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) } break; case ARM_CP_STATE_AA64: - assert(r->cp == 0 || r->cp == CP_REG_ARM64_SYSREG_CP); + assert(r->cp == 0); break; default: g_assert_not_reached(); diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 694584cc1302e..6e67d89163ffa 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1124,8 +1124,7 @@ static bool is_id_sysreg(uint32_t reg) static uint32_t hvf_reg2cp_reg(uint32_t reg) { - return ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, - (reg >> SYSREG_CRN_SHIFT) & SYSREG_CRN_MASK, + return ENCODE_AA64_CP_REG((reg >> SYSREG_CRN_SHIFT) & SYSREG_CRN_MASK, (reg >> SYSREG_CRM_SHIFT) & SYSREG_CRM_MASK, (reg >> SYSREG_OP0_SHIFT) & SYSREG_OP0_MASK, (reg >> SYSREG_OP1_SHIFT) & SYSREG_OP1_MASK, diff --git a/target/arm/kvm-consts.h b/target/arm/kvm-consts.h index fdb305eea1a4e..54ae5da7ce3ad 100644 --- a/target/arm/kvm-consts.h +++ b/target/arm/kvm-consts.h @@ -160,9 +160,6 @@ MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A53, KVM_ARM_TARGET_CORTEX_A53); #define CP_REG_ARM64_SYSREG_OP2_MASK 0x0000000000000007 #define CP_REG_ARM64_SYSREG_OP2_SHIFT 0 -/* No kernel define but it's useful to QEMU */ -#define CP_REG_ARM64_SYSREG_CP (CP_REG_ARM64_SYSREG >> CP_REG_ARM_COPROC_SHIFT) - MISMATCH_CHECK(CP_REG_ARM64, KVM_REG_ARM64); MISMATCH_CHECK(CP_REG_ARM_COPROC_MASK, KVM_REG_ARM_COPROC_MASK); MISMATCH_CHECK(CP_REG_ARM_COPROC_SHIFT, KVM_REG_ARM_COPROC_SHIFT); diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 37bedc3780b78..a560ef0f42ce5 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2466,8 +2466,7 @@ static void handle_sys(DisasContext *s, bool isread, unsigned int op0, unsigned int op1, unsigned int op2, unsigned int crn, unsigned int crm, unsigned int rt) { - uint32_t key = ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, - crn, crm, op0, op1, op2); + uint32_t key = ENCODE_AA64_CP_REG(crn, crm, op0, op1, op2); const ARMCPRegInfo *ri = get_arm_cp_reginfo(s->cp_regs, key); bool need_exit_tb = false; bool nv_trap_to_el2 = false; @@ -2603,8 +2602,7 @@ static void handle_sys(DisasContext *s, bool isread, * We don't use the EL1 register's access function, and * fine-grained-traps on EL1 also do not apply here. */ - key = ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, - crn, crm, op0, 0, op2); + key = ENCODE_AA64_CP_REG(crn, crm, op0, 0, op2); ri = get_arm_cp_reginfo(s->cp_regs, key); assert(ri); assert(cp_access_ok(s->current_el, ri, isread)); From 398a33aeda5e01eb385f026d54192cfb73160b50 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:20 -0700 Subject: [PATCH 0880/1794] target/arm: Reorder ENCODE_AA64_CP_REG arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The order of the parameters in the Arm ARM is op0, op1, crn, crm, op2 Reorder the arguments of ENCODE_AA64_CP_REG to match. Reviewed-by: Manos Pitsidianakis Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/cpregs.h | 2 +- target/arm/helper.c | 4 ++-- target/arm/hvf/hvf.c | 6 +++--- target/arm/tcg/translate-a64.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index a10abadb93298..08fc42ea57137 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -187,7 +187,7 @@ enum { ((is64) << CP_REG_AA32_64BIT_SHIFT) | \ ((cp) << 16) | ((crn) << 11) | ((crm) << 7) | ((opc1) << 3) | (opc2)) -#define ENCODE_AA64_CP_REG(crn, crm, op0, op1, op2) \ +#define ENCODE_AA64_CP_REG(op0, op1, crn, crm, op2) \ (CP_REG_AA64_MASK | CP_REG_ARM64_SYSREG | \ ((op0) << CP_REG_ARM64_SYSREG_OP0_SHIFT) | \ ((op1) << CP_REG_ARM64_SYSREG_OP1_SHIFT) | \ diff --git a/target/arm/helper.c b/target/arm/helper.c index 2732112ff21eb..965941f04e5d9 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4503,7 +4503,7 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) }; #define K(op0, op1, crn, crm, op2) \ - ENCODE_AA64_CP_REG(crn, crm, op0, op1, op2) + ENCODE_AA64_CP_REG(op0, op1, crn, crm, op2) static const struct E2HAlias aliases[] = { { K(3, 0, 1, 0, 0), K(3, 4, 1, 0, 0), K(3, 5, 1, 0, 0), @@ -7398,7 +7398,7 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, */ assert(cp == 0 || r->state == ARM_CP_STATE_BOTH); cp = 0; - key = ENCODE_AA64_CP_REG(r->crn, crm, r->opc0, opc1, opc2); + key = ENCODE_AA64_CP_REG(r->opc0, opc1, r->crn, crm, opc2); break; default: g_assert_not_reached(); diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 6e67d89163ffa..8b467b3663884 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1124,10 +1124,10 @@ static bool is_id_sysreg(uint32_t reg) static uint32_t hvf_reg2cp_reg(uint32_t reg) { - return ENCODE_AA64_CP_REG((reg >> SYSREG_CRN_SHIFT) & SYSREG_CRN_MASK, - (reg >> SYSREG_CRM_SHIFT) & SYSREG_CRM_MASK, - (reg >> SYSREG_OP0_SHIFT) & SYSREG_OP0_MASK, + return ENCODE_AA64_CP_REG((reg >> SYSREG_OP0_SHIFT) & SYSREG_OP0_MASK, (reg >> SYSREG_OP1_SHIFT) & SYSREG_OP1_MASK, + (reg >> SYSREG_CRN_SHIFT) & SYSREG_CRN_MASK, + (reg >> SYSREG_CRM_SHIFT) & SYSREG_CRM_MASK, (reg >> SYSREG_OP2_SHIFT) & SYSREG_OP2_MASK); } diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index a560ef0f42ce5..0ec309f1ea9ec 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2466,7 +2466,7 @@ static void handle_sys(DisasContext *s, bool isread, unsigned int op0, unsigned int op1, unsigned int op2, unsigned int crn, unsigned int crm, unsigned int rt) { - uint32_t key = ENCODE_AA64_CP_REG(crn, crm, op0, op1, op2); + uint32_t key = ENCODE_AA64_CP_REG(op0, op1, crn, crm, op2); const ARMCPRegInfo *ri = get_arm_cp_reginfo(s->cp_regs, key); bool need_exit_tb = false; bool nv_trap_to_el2 = false; @@ -2602,7 +2602,7 @@ static void handle_sys(DisasContext *s, bool isread, * We don't use the EL1 register's access function, and * fine-grained-traps on EL1 also do not apply here. */ - key = ENCODE_AA64_CP_REG(crn, crm, op0, 0, op2); + key = ENCODE_AA64_CP_REG(op0, 0, crn, crm, op2); ri = get_arm_cp_reginfo(s->cp_regs, key); assert(ri); assert(cp_access_ok(s->current_el, ri, isread)); From 3b230602e2103ae1eafb5f5355b74fd8160b6093 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:21 -0700 Subject: [PATCH 0881/1794] target/arm: Split out add_cpreg_to_hashtable_aa{32, 64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nesting level for the inner loop of define_one_arm_cp_reg was overly deep. Split out that code into two functions, for the AArch32 and AArch64 paths separately. Simplify the innermost loop to a switch statement over r->state. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/helper.c | 147 +++++++++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 71 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 965941f04e5d9..39f5297a1a751 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7557,6 +7557,66 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, g_hash_table_insert(cpu->cp_regs, (gpointer)(uintptr_t)key, r2); } +static void add_cpreg_to_hashtable_aa32(ARMCPU *cpu, const ARMCPRegInfo *r, + int crm, int opc1, int opc2) +{ + /* + * Under AArch32 CP registers can be common + * (same for secure and non-secure world) or banked. + */ + char *name; + + assert(!(r->type & ARM_CP_ADD_TLBI_NXS)); /* aa64 only */ + + switch (r->secure) { + case ARM_CP_SECSTATE_S: + case ARM_CP_SECSTATE_NS: + add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, + r->secure, crm, opc1, opc2, r->name); + break; + case ARM_CP_SECSTATE_BOTH: + name = g_strdup_printf("%s_S", r->name); + add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, + ARM_CP_SECSTATE_S, crm, opc1, opc2, name); + g_free(name); + add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, + ARM_CP_SECSTATE_NS, crm, opc1, opc2, r->name); + break; + default: + g_assert_not_reached(); + } +} + +static void add_cpreg_to_hashtable_aa64(ARMCPU *cpu, const ARMCPRegInfo *r, + int crm, int opc1, int opc2) +{ + if ((r->type & ARM_CP_ADD_TLBI_NXS) && + cpu_isar_feature(aa64_xs, cpu)) { + /* + * This is a TLBI insn which has an NXS variant. The + * NXS variant is at the same encoding except that + * crn is +1, and has the same behaviour except for + * fine-grained trapping. Add the NXS insn here and + * then fall through to add the normal register. + * add_cpreg_to_hashtable() copies the cpreg struct + * and name that it is passed, so it's OK to use + * a local struct here. + */ + ARMCPRegInfo nxs_ri = *r; + g_autofree char *name = g_strdup_printf("%sNXS", r->name); + + assert(nxs_ri.crn < 0xf); + nxs_ri.crn++; + if (nxs_ri.fgt) { + nxs_ri.fgt |= R_FGT_NXS_MASK; + } + add_cpreg_to_hashtable(cpu, &nxs_ri, ARM_CP_STATE_AA64, + ARM_CP_SECSTATE_NS, crm, opc1, opc2, name); + } + + add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA64, ARM_CP_SECSTATE_NS, + crm, opc1, opc2, r->name); +} void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) { @@ -7584,14 +7644,12 @@ void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) * bits; the ARM_CP_64BIT* flag applies only to the AArch32 view of * the register, if any. */ - int crm, opc1, opc2; int crmmin = (r->crm == CP_ANY) ? 0 : r->crm; int crmmax = (r->crm == CP_ANY) ? 15 : r->crm; int opc1min = (r->opc1 == CP_ANY) ? 0 : r->opc1; int opc1max = (r->opc1 == CP_ANY) ? 7 : r->opc1; int opc2min = (r->opc2 == CP_ANY) ? 0 : r->opc2; int opc2max = (r->opc2 == CP_ANY) ? 7 : r->opc2; - CPState state; /* 64 bit registers have only CRm and Opc1 fields */ assert(!((r->type & ARM_CP_64BIT) && (r->opc2 || r->crn))); @@ -7688,75 +7746,22 @@ void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) } } - for (crm = crmmin; crm <= crmmax; crm++) { - for (opc1 = opc1min; opc1 <= opc1max; opc1++) { - for (opc2 = opc2min; opc2 <= opc2max; opc2++) { - for (state = ARM_CP_STATE_AA32; - state <= ARM_CP_STATE_AA64; state++) { - if (r->state != state && r->state != ARM_CP_STATE_BOTH) { - continue; - } - if ((r->type & ARM_CP_ADD_TLBI_NXS) && - cpu_isar_feature(aa64_xs, cpu)) { - /* - * This is a TLBI insn which has an NXS variant. The - * NXS variant is at the same encoding except that - * crn is +1, and has the same behaviour except for - * fine-grained trapping. Add the NXS insn here and - * then fall through to add the normal register. - * add_cpreg_to_hashtable() copies the cpreg struct - * and name that it is passed, so it's OK to use - * a local struct here. - */ - ARMCPRegInfo nxs_ri = *r; - g_autofree char *name = g_strdup_printf("%sNXS", r->name); - - assert(state == ARM_CP_STATE_AA64); - assert(nxs_ri.crn < 0xf); - nxs_ri.crn++; - if (nxs_ri.fgt) { - nxs_ri.fgt |= R_FGT_NXS_MASK; - } - add_cpreg_to_hashtable(cpu, &nxs_ri, state, - ARM_CP_SECSTATE_NS, - crm, opc1, opc2, name); - } - if (state == ARM_CP_STATE_AA32) { - /* - * Under AArch32 CP registers can be common - * (same for secure and non-secure world) or banked. - */ - char *name; - - switch (r->secure) { - case ARM_CP_SECSTATE_S: - case ARM_CP_SECSTATE_NS: - add_cpreg_to_hashtable(cpu, r, state, - r->secure, crm, opc1, opc2, - r->name); - break; - case ARM_CP_SECSTATE_BOTH: - name = g_strdup_printf("%s_S", r->name); - add_cpreg_to_hashtable(cpu, r, state, - ARM_CP_SECSTATE_S, - crm, opc1, opc2, name); - g_free(name); - add_cpreg_to_hashtable(cpu, r, state, - ARM_CP_SECSTATE_NS, - crm, opc1, opc2, r->name); - break; - default: - g_assert_not_reached(); - } - } else { - /* - * AArch64 registers get mapped to non-secure instance - * of AArch32 - */ - add_cpreg_to_hashtable(cpu, r, state, - ARM_CP_SECSTATE_NS, - crm, opc1, opc2, r->name); - } + for (int crm = crmmin; crm <= crmmax; crm++) { + for (int opc1 = opc1min; opc1 <= opc1max; opc1++) { + for (int opc2 = opc2min; opc2 <= opc2max; opc2++) { + switch (r->state) { + case ARM_CP_STATE_AA32: + add_cpreg_to_hashtable_aa32(cpu, r, crm, opc1, opc2); + break; + case ARM_CP_STATE_AA64: + add_cpreg_to_hashtable_aa64(cpu, r, crm, opc1, opc2); + break; + case ARM_CP_STATE_BOTH: + add_cpreg_to_hashtable_aa32(cpu, r, crm, opc1, opc2); + add_cpreg_to_hashtable_aa64(cpu, r, crm, opc1, opc2); + break; + default: + g_assert_not_reached(); } } } From ca332905256af29c23132167dd8d2a88510a8761 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:22 -0700 Subject: [PATCH 0882/1794] target/arm: Improve asserts in define_one_arm_cp_reg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reject ARM_CP_64BIT with ARM_CP_STATE_BOTH, because encoding constrains prevent it from working. Remove some extra parens; distribute ! across && to simplify. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/helper.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 39f5297a1a751..8c2b7e037e8fd 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7651,12 +7651,17 @@ void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) int opc2min = (r->opc2 == CP_ANY) ? 0 : r->opc2; int opc2max = (r->opc2 == CP_ANY) ? 7 : r->opc2; - /* 64 bit registers have only CRm and Opc1 fields */ - assert(!((r->type & ARM_CP_64BIT) && (r->opc2 || r->crn))); + /* + * AArch64 regs are all 64 bit so ARM_CP_64BIT is meaningless. + * Moreover, the encoding test just following in general prevents + * shared encoding so ARM_CP_STATE_BOTH won't work either. + */ + assert(r->state == ARM_CP_STATE_AA32 || !(r->type & ARM_CP_64BIT)); + /* AArch32 64-bit registers have only CRm and Opc1 fields. */ + assert(!(r->type & ARM_CP_64BIT) || !(r->opc2 || r->crn)); /* op0 only exists in the AArch64 encodings */ - assert((r->state != ARM_CP_STATE_AA32) || (r->opc0 == 0)); - /* AArch64 regs are all 64 bit so ARM_CP_64BIT is meaningless */ - assert((r->state != ARM_CP_STATE_AA64) || !(r->type & ARM_CP_64BIT)); + assert(r->state != ARM_CP_STATE_AA32 || r->opc0 == 0); + /* * This API is only for Arm's system coprocessors (14 and 15) or * (M-profile or v7A-and-earlier only) for implementation defined From 5a3af95cb990662ad0431f918135ae905e2d0cf3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:23 -0700 Subject: [PATCH 0883/1794] target/arm: Move cp processing to define_one_arm_cp_reg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Processing of cp was split between add_cpreg_to_hashtable and define_one_arm_cp_reg. Unify it all to the top-level function. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/helper.c | 53 +++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 8c2b7e037e8fd..f0e423e623b2e 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7368,7 +7368,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) */ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, CPState state, CPSecureState secstate, - int crm, int opc1, int opc2, + int cp, int crm, int opc1, int opc2, const char *name) { CPUARMState *env = &cpu->env; @@ -7376,28 +7376,14 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, ARMCPRegInfo *r2; bool is64 = r->type & ARM_CP_64BIT; bool ns = secstate & ARM_CP_SECSTATE_NS; - int cp = r->cp; size_t name_len; bool make_const; switch (state) { case ARM_CP_STATE_AA32: - /* We assume it is a cp15 register if the .cp field is left unset. */ - if (cp == 0 && r->state == ARM_CP_STATE_BOTH) { - cp = 15; - } key = ENCODE_CP_REG(cp, is64, ns, r->crn, crm, opc1, opc2); break; case ARM_CP_STATE_AA64: - /* - * To allow abbreviation of ARMCPRegInfo definitions, we treat - * cp == 0 as equivalent to the value for "standard guest-visible - * sysreg". STATE_BOTH definitions are also always "standard sysreg" - * in their AArch64 view (the .cp value may be non-zero for the - * benefit of the AArch32 view). - */ - assert(cp == 0 || r->state == ARM_CP_STATE_BOTH); - cp = 0; key = ENCODE_AA64_CP_REG(r->opc0, opc1, r->crn, crm, opc2); break; default: @@ -7558,7 +7544,7 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, } static void add_cpreg_to_hashtable_aa32(ARMCPU *cpu, const ARMCPRegInfo *r, - int crm, int opc1, int opc2) + int cp, int crm, int opc1, int opc2) { /* * Under AArch32 CP registers can be common @@ -7571,16 +7557,16 @@ static void add_cpreg_to_hashtable_aa32(ARMCPU *cpu, const ARMCPRegInfo *r, switch (r->secure) { case ARM_CP_SECSTATE_S: case ARM_CP_SECSTATE_NS: - add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, - r->secure, crm, opc1, opc2, r->name); + add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, r->secure, + cp, crm, opc1, opc2, r->name); break; case ARM_CP_SECSTATE_BOTH: name = g_strdup_printf("%s_S", r->name); - add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, - ARM_CP_SECSTATE_S, crm, opc1, opc2, name); + add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, ARM_CP_SECSTATE_S, + cp, crm, opc1, opc2, name); g_free(name); - add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, - ARM_CP_SECSTATE_NS, crm, opc1, opc2, r->name); + add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, ARM_CP_SECSTATE_NS, + cp, crm, opc1, opc2, r->name); break; default: g_assert_not_reached(); @@ -7611,11 +7597,11 @@ static void add_cpreg_to_hashtable_aa64(ARMCPU *cpu, const ARMCPRegInfo *r, nxs_ri.fgt |= R_FGT_NXS_MASK; } add_cpreg_to_hashtable(cpu, &nxs_ri, ARM_CP_STATE_AA64, - ARM_CP_SECSTATE_NS, crm, opc1, opc2, name); + ARM_CP_SECSTATE_NS, 0, crm, opc1, opc2, name); } add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA64, ARM_CP_SECSTATE_NS, - crm, opc1, opc2, r->name); + 0, crm, opc1, opc2, r->name); } void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) @@ -7650,6 +7636,7 @@ void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) int opc1max = (r->opc1 == CP_ANY) ? 7 : r->opc1; int opc2min = (r->opc2 == CP_ANY) ? 0 : r->opc2; int opc2max = (r->opc2 == CP_ANY) ? 7 : r->opc2; + int cp = r->cp; /* * AArch64 regs are all 64 bit so ARM_CP_64BIT is meaningless. @@ -7672,21 +7659,25 @@ void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) */ switch (r->state) { case ARM_CP_STATE_BOTH: - /* 0 has a special meaning, but otherwise the same rules as AA32. */ - if (r->cp == 0) { + /* + * If the cp field is left unset, assume cp15. + * Otherwise apply the same rules as AA32. + */ + if (cp == 0) { + cp = 15; break; } /* fall through */ case ARM_CP_STATE_AA32: if (arm_feature(&cpu->env, ARM_FEATURE_V8) && !arm_feature(&cpu->env, ARM_FEATURE_M)) { - assert(r->cp >= 14 && r->cp <= 15); + assert(cp >= 14 && cp <= 15); } else { - assert(r->cp < 8 || (r->cp >= 14 && r->cp <= 15)); + assert(cp < 8 || (cp >= 14 && cp <= 15)); } break; case ARM_CP_STATE_AA64: - assert(r->cp == 0); + assert(cp == 0); break; default: g_assert_not_reached(); @@ -7756,13 +7747,13 @@ void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) for (int opc2 = opc2min; opc2 <= opc2max; opc2++) { switch (r->state) { case ARM_CP_STATE_AA32: - add_cpreg_to_hashtable_aa32(cpu, r, crm, opc1, opc2); + add_cpreg_to_hashtable_aa32(cpu, r, cp, crm, opc1, opc2); break; case ARM_CP_STATE_AA64: add_cpreg_to_hashtable_aa64(cpu, r, crm, opc1, opc2); break; case ARM_CP_STATE_BOTH: - add_cpreg_to_hashtable_aa32(cpu, r, crm, opc1, opc2); + add_cpreg_to_hashtable_aa32(cpu, r, cp, crm, opc1, opc2); add_cpreg_to_hashtable_aa64(cpu, r, crm, opc1, opc2); break; default: From 89f942f29dfa0da08928c8097da2433257a14813 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:24 -0700 Subject: [PATCH 0884/1794] target/arm: Move cpreg elimination to define_one_arm_cp_reg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Eliminate unused registers earlier, so that by the time we arrive in add_cpreg_to_hashtable we never skip. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/helper.c | 123 +++++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 59 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index f0e423e623b2e..ade16138e75bf 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7377,7 +7377,6 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, bool is64 = r->type & ARM_CP_64BIT; bool ns = secstate & ARM_CP_SECSTATE_NS; size_t name_len; - bool make_const; switch (state) { case ARM_CP_STATE_AA32: @@ -7398,32 +7397,6 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, } } - /* - * Eliminate registers that are not present because the EL is missing. - * Doing this here makes it easier to put all registers for a given - * feature into the same ARMCPRegInfo array and define them all at once. - */ - make_const = false; - if (arm_feature(env, ARM_FEATURE_EL3)) { - /* - * An EL2 register without EL2 but with EL3 is (usually) RES0. - * See rule RJFFP in section D1.1.3 of DDI0487H.a. - */ - int min_el = ctz32(r->access) / 2; - if (min_el == 2 && !arm_feature(env, ARM_FEATURE_EL2)) { - if (r->type & ARM_CP_EL3_NO_EL2_UNDEF) { - return; - } - make_const = !(r->type & ARM_CP_EL3_NO_EL2_KEEP); - } - } else { - CPAccessRights max_el = (arm_feature(env, ARM_FEATURE_EL2) - ? PL2_RW : PL1_RW); - if ((r->access & max_el) == 0) { - return; - } - } - /* Combine cpreg and name into one allocation. */ name_len = strlen(name) + 1; r2 = g_malloc(sizeof(*r2) + name_len); @@ -7441,38 +7414,7 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, r2->state = state; r2->secure = secstate; - if (make_const) { - /* This should not have been a very special register to begin. */ - int old_special = r2->type & ARM_CP_SPECIAL_MASK; - assert(old_special == 0 || old_special == ARM_CP_NOP); - /* - * Set the special function to CONST, retaining the other flags. - * This is important for e.g. ARM_CP_SVE so that we still - * take the SVE trap if CPTR_EL3.EZ == 0. - */ - r2->type = (r2->type & ~ARM_CP_SPECIAL_MASK) | ARM_CP_CONST; - /* - * Usually, these registers become RES0, but there are a few - * special cases like VPIDR_EL2 which have a constant non-zero - * value with writes ignored. - */ - if (!(r->type & ARM_CP_EL3_NO_EL2_C_NZ)) { - r2->resetvalue = 0; - } - /* - * ARM_CP_CONST has precedence, so removing the callbacks and - * offsets are not strictly necessary, but it is potentially - * less confusing to debug later. - */ - r2->readfn = NULL; - r2->writefn = NULL; - r2->raw_readfn = NULL; - r2->raw_writefn = NULL; - r2->resetfn = NULL; - r2->fieldoffset = 0; - r2->bank_fieldoffsets[0] = 0; - r2->bank_fieldoffsets[1] = 0; - } else { + { bool isbanked = r->bank_fieldoffsets[0] && r->bank_fieldoffsets[1]; if (isbanked) { @@ -7637,6 +7579,8 @@ void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) int opc2min = (r->opc2 == CP_ANY) ? 0 : r->opc2; int opc2max = (r->opc2 == CP_ANY) ? 7 : r->opc2; int cp = r->cp; + ARMCPRegInfo r_const; + CPUARMState *env = &cpu->env; /* * AArch64 regs are all 64 bit so ARM_CP_64BIT is meaningless. @@ -7742,6 +7686,67 @@ void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) } } + /* + * Eliminate registers that are not present because the EL is missing. + * Doing this here makes it easier to put all registers for a given + * feature into the same ARMCPRegInfo array and define them all at once. + */ + if (arm_feature(env, ARM_FEATURE_EL3)) { + /* + * An EL2 register without EL2 but with EL3 is (usually) RES0. + * See rule RJFFP in section D1.1.3 of DDI0487H.a. + */ + int min_el = ctz32(r->access) / 2; + if (min_el == 2 && !arm_feature(env, ARM_FEATURE_EL2)) { + if (r->type & ARM_CP_EL3_NO_EL2_UNDEF) { + return; + } + if (!(r->type & ARM_CP_EL3_NO_EL2_KEEP)) { + /* This should not have been a very special register. */ + int old_special = r->type & ARM_CP_SPECIAL_MASK; + assert(old_special == 0 || old_special == ARM_CP_NOP); + + r_const = *r; + + /* + * Set the special function to CONST, retaining the other flags. + * This is important for e.g. ARM_CP_SVE so that we still + * take the SVE trap if CPTR_EL3.EZ == 0. + */ + r_const.type = (r->type & ~ARM_CP_SPECIAL_MASK) | ARM_CP_CONST; + /* + * Usually, these registers become RES0, but there are a few + * special cases like VPIDR_EL2 which have a constant non-zero + * value with writes ignored. + */ + if (!(r->type & ARM_CP_EL3_NO_EL2_C_NZ)) { + r_const.resetvalue = 0; + } + /* + * ARM_CP_CONST has precedence, so removing the callbacks and + * offsets are not strictly necessary, but it is potentially + * less confusing to debug later. + */ + r_const.readfn = NULL; + r_const.writefn = NULL; + r_const.raw_readfn = NULL; + r_const.raw_writefn = NULL; + r_const.resetfn = NULL; + r_const.fieldoffset = 0; + r_const.bank_fieldoffsets[0] = 0; + r_const.bank_fieldoffsets[1] = 0; + + r = &r_const; + } + } + } else { + CPAccessRights max_el = (arm_feature(env, ARM_FEATURE_EL2) + ? PL2_RW : PL1_RW); + if ((r->access & max_el) == 0) { + return; + } + } + for (int crm = crmmin; crm <= crmmax; crm++) { for (int opc1 = opc1min; opc1 <= opc1max; opc1++) { for (int opc2 = opc2min; opc2 <= opc2max; opc2++) { From 2696aa34ec592eb06578f49ad70e70b878483070 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:25 -0700 Subject: [PATCH 0885/1794] target/arm: Add key parameter to add_cpreg_to_hashtable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hoist the computation of key into the caller, where state is a known constant. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé [PMM: added comment about CRN key field increment] Signed-off-by: Peter Maydell --- target/arm/helper.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index ade16138e75bf..8a8fa8c40e959 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7369,26 +7369,13 @@ void register_cp_regs_for_features(ARMCPU *cpu) static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, CPState state, CPSecureState secstate, int cp, int crm, int opc1, int opc2, - const char *name) + const char *name, uint32_t key) { CPUARMState *env = &cpu->env; - uint32_t key; ARMCPRegInfo *r2; - bool is64 = r->type & ARM_CP_64BIT; bool ns = secstate & ARM_CP_SECSTATE_NS; size_t name_len; - switch (state) { - case ARM_CP_STATE_AA32: - key = ENCODE_CP_REG(cp, is64, ns, r->crn, crm, opc1, opc2); - break; - case ARM_CP_STATE_AA64: - key = ENCODE_AA64_CP_REG(r->opc0, opc1, r->crn, crm, opc2); - break; - default: - g_assert_not_reached(); - } - /* Overriding of an existing definition must be explicitly requested. */ if (!(r->type & ARM_CP_OVERRIDE)) { const ARMCPRegInfo *oldreg = get_arm_cp_reginfo(cpu->cp_regs, key); @@ -7493,22 +7480,28 @@ static void add_cpreg_to_hashtable_aa32(ARMCPU *cpu, const ARMCPRegInfo *r, * (same for secure and non-secure world) or banked. */ char *name; + bool is64 = r->type & ARM_CP_64BIT; + uint32_t key = ENCODE_CP_REG(cp, is64, 0, r->crn, crm, opc1, opc2); assert(!(r->type & ARM_CP_ADD_TLBI_NXS)); /* aa64 only */ switch (r->secure) { - case ARM_CP_SECSTATE_S: case ARM_CP_SECSTATE_NS: + key |= CP_REG_AA32_NS_MASK; + /* fall through */ + case ARM_CP_SECSTATE_S: add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, r->secure, - cp, crm, opc1, opc2, r->name); + cp, crm, opc1, opc2, r->name, key); break; case ARM_CP_SECSTATE_BOTH: name = g_strdup_printf("%s_S", r->name); add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, ARM_CP_SECSTATE_S, - cp, crm, opc1, opc2, name); + cp, crm, opc1, opc2, name, key); g_free(name); + + key |= CP_REG_AA32_NS_MASK; add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, ARM_CP_SECSTATE_NS, - cp, crm, opc1, opc2, r->name); + cp, crm, opc1, opc2, r->name, key); break; default: g_assert_not_reached(); @@ -7518,6 +7511,8 @@ static void add_cpreg_to_hashtable_aa32(ARMCPU *cpu, const ARMCPRegInfo *r, static void add_cpreg_to_hashtable_aa64(ARMCPU *cpu, const ARMCPRegInfo *r, int crm, int opc1, int opc2) { + uint32_t key = ENCODE_AA64_CP_REG(r->opc0, opc1, r->crn, crm, opc2); + if ((r->type & ARM_CP_ADD_TLBI_NXS) && cpu_isar_feature(aa64_xs, cpu)) { /* @@ -7532,18 +7527,23 @@ static void add_cpreg_to_hashtable_aa64(ARMCPU *cpu, const ARMCPRegInfo *r, */ ARMCPRegInfo nxs_ri = *r; g_autofree char *name = g_strdup_printf("%sNXS", r->name); + uint32_t nxs_key; assert(nxs_ri.crn < 0xf); nxs_ri.crn++; + /* Also increment the CRN field inside the key value */ + nxs_key = key + (1 << CP_REG_ARM64_SYSREG_CRN_SHIFT); if (nxs_ri.fgt) { nxs_ri.fgt |= R_FGT_NXS_MASK; } + add_cpreg_to_hashtable(cpu, &nxs_ri, ARM_CP_STATE_AA64, - ARM_CP_SECSTATE_NS, 0, crm, opc1, opc2, name); + ARM_CP_SECSTATE_NS, 0, crm, opc1, opc2, + name, nxs_key); } add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA64, ARM_CP_SECSTATE_NS, - 0, crm, opc1, opc2, r->name); + 0, crm, opc1, opc2, r->name, key); } void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) From 9044ba55426437de89e05ef9c192a0a1b529b968 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:26 -0700 Subject: [PATCH 0886/1794] target/arm: Split out alloc_cpreg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Include provision for a name suffix. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/helper.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 8a8fa8c40e959..e36598e273bf1 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7362,6 +7362,28 @@ void register_cp_regs_for_features(ARMCPU *cpu) #endif } +/* + * Copy a ARMCPRegInfo structure, allocating it along with the name + * and an optional suffix to the name. + */ +static ARMCPRegInfo *alloc_cpreg(const ARMCPRegInfo *in, + const char *name, const char *suffix) +{ + size_t name_len = strlen(name); + size_t suff_len = suffix ? strlen(suffix) : 0; + ARMCPRegInfo *out = g_malloc(sizeof(*in) + name_len + suff_len + 1); + char *p = (char *)(out + 1); + + *out = *in; + out->name = p; + + memcpy(p, name, name_len + 1); + if (suffix) { + memcpy(p + name_len, suffix, suff_len + 1); + } + return out; +} + /* * Private utility function for define_one_arm_cp_reg(): * add a single reginfo struct to the hash table. @@ -7374,7 +7396,6 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, CPUARMState *env = &cpu->env; ARMCPRegInfo *r2; bool ns = secstate & ARM_CP_SECSTATE_NS; - size_t name_len; /* Overriding of an existing definition must be explicitly requested. */ if (!(r->type & ARM_CP_OVERRIDE)) { @@ -7384,11 +7405,7 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, } } - /* Combine cpreg and name into one allocation. */ - name_len = strlen(name) + 1; - r2 = g_malloc(sizeof(*r2) + name_len); - *r2 = *r; - r2->name = memcpy(r2 + 1, name, name_len); + r2 = alloc_cpreg(r, name, NULL); /* * Update fields to match the instantiation, overwiting wildcards From 349127a9be7f19becd6b01b7d9ccc50624d48d01 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:27 -0700 Subject: [PATCH 0887/1794] target/arm: Hoist the allocation of ARMCPRegInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass in a newly allocated structure, rather than having to dance around allocation of the name and the structure. Since we no longer have two copies of the structure handy within add_cpreg_to_hashtable, delay the writeback of concrete values over wildcards until we're done querying the wildcards. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/helper.c | 97 ++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index e36598e273bf1..88b5ec1a5a2d3 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7388,13 +7388,12 @@ static ARMCPRegInfo *alloc_cpreg(const ARMCPRegInfo *in, * Private utility function for define_one_arm_cp_reg(): * add a single reginfo struct to the hash table. */ -static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, +static void add_cpreg_to_hashtable(ARMCPU *cpu, ARMCPRegInfo *r, CPState state, CPSecureState secstate, int cp, int crm, int opc1, int opc2, - const char *name, uint32_t key) + uint32_t key) { CPUARMState *env = &cpu->env; - ARMCPRegInfo *r2; bool ns = secstate & ARM_CP_SECSTATE_NS; /* Overriding of an existing definition must be explicitly requested. */ @@ -7405,19 +7404,6 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, } } - r2 = alloc_cpreg(r, name, NULL); - - /* - * Update fields to match the instantiation, overwiting wildcards - * such as CP_ANY, ARM_CP_STATE_BOTH, or ARM_CP_SECSTATE_BOTH. - */ - r2->cp = cp; - r2->crm = crm; - r2->opc1 = opc1; - r2->opc2 = opc2; - r2->state = state; - r2->secure = secstate; - { bool isbanked = r->bank_fieldoffsets[0] && r->bank_fieldoffsets[1]; @@ -7427,7 +7413,7 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, * Overwriting fieldoffset as the array is only used to define * banked registers but later only fieldoffset is used. */ - r2->fieldoffset = r->bank_fieldoffsets[ns]; + r->fieldoffset = r->bank_fieldoffsets[ns]; } if (state == ARM_CP_STATE_AA32) { if (isbanked) { @@ -7444,19 +7430,19 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, */ if ((r->state == ARM_CP_STATE_BOTH && ns) || (arm_feature(env, ARM_FEATURE_V8) && !ns)) { - r2->type |= ARM_CP_ALIAS; + r->type |= ARM_CP_ALIAS; } } else if ((secstate != r->secure) && !ns) { /* * The register is not banked so we only want to allow * migration of the non-secure instance. */ - r2->type |= ARM_CP_ALIAS; + r->type |= ARM_CP_ALIAS; } if (HOST_BIG_ENDIAN && - r->state == ARM_CP_STATE_BOTH && r2->fieldoffset) { - r2->fieldoffset += sizeof(uint32_t); + r->state == ARM_CP_STATE_BOTH && r->fieldoffset) { + r->fieldoffset += sizeof(uint32_t); } } } @@ -7468,35 +7454,46 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, * multiple times. Special registers (ie NOP/WFI) are * never migratable and not even raw-accessible. */ - if (r2->type & ARM_CP_SPECIAL_MASK) { - r2->type |= ARM_CP_NO_RAW; + if (r->type & ARM_CP_SPECIAL_MASK) { + r->type |= ARM_CP_NO_RAW; } if (((r->crm == CP_ANY) && crm != 0) || ((r->opc1 == CP_ANY) && opc1 != 0) || ((r->opc2 == CP_ANY) && opc2 != 0)) { - r2->type |= ARM_CP_ALIAS | ARM_CP_NO_GDB; + r->type |= ARM_CP_ALIAS | ARM_CP_NO_GDB; } + /* + * Update fields to match the instantiation, overwiting wildcards + * such as CP_ANY, ARM_CP_STATE_BOTH, or ARM_CP_SECSTATE_BOTH. + */ + r->cp = cp; + r->crm = crm; + r->opc1 = opc1; + r->opc2 = opc2; + r->state = state; + r->secure = secstate; + /* * Check that raw accesses are either forbidden or handled. Note that * we can't assert this earlier because the setup of fieldoffset for * banked registers has to be done first. */ - if (!(r2->type & ARM_CP_NO_RAW)) { - assert(!raw_accessors_invalid(r2)); + if (!(r->type & ARM_CP_NO_RAW)) { + assert(!raw_accessors_invalid(r)); } - g_hash_table_insert(cpu->cp_regs, (gpointer)(uintptr_t)key, r2); + g_hash_table_insert(cpu->cp_regs, (gpointer)(uintptr_t)key, r); } -static void add_cpreg_to_hashtable_aa32(ARMCPU *cpu, const ARMCPRegInfo *r, +static void add_cpreg_to_hashtable_aa32(ARMCPU *cpu, ARMCPRegInfo *r, int cp, int crm, int opc1, int opc2) { /* * Under AArch32 CP registers can be common * (same for secure and non-secure world) or banked. */ - char *name; + ARMCPRegInfo *r_s; bool is64 = r->type & ARM_CP_64BIT; uint32_t key = ENCODE_CP_REG(cp, is64, 0, r->crn, crm, opc1, opc2); @@ -7508,24 +7505,23 @@ static void add_cpreg_to_hashtable_aa32(ARMCPU *cpu, const ARMCPRegInfo *r, /* fall through */ case ARM_CP_SECSTATE_S: add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, r->secure, - cp, crm, opc1, opc2, r->name, key); + cp, crm, opc1, opc2, key); break; case ARM_CP_SECSTATE_BOTH: - name = g_strdup_printf("%s_S", r->name); - add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, ARM_CP_SECSTATE_S, - cp, crm, opc1, opc2, name, key); - g_free(name); + r_s = alloc_cpreg(r, r->name, "_S"); + add_cpreg_to_hashtable(cpu, r_s, ARM_CP_STATE_AA32, ARM_CP_SECSTATE_S, + cp, crm, opc1, opc2, key); key |= CP_REG_AA32_NS_MASK; add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, ARM_CP_SECSTATE_NS, - cp, crm, opc1, opc2, r->name, key); + cp, crm, opc1, opc2, key); break; default: g_assert_not_reached(); } } -static void add_cpreg_to_hashtable_aa64(ARMCPU *cpu, const ARMCPRegInfo *r, +static void add_cpreg_to_hashtable_aa64(ARMCPU *cpu, ARMCPRegInfo *r, int crm, int opc1, int opc2) { uint32_t key = ENCODE_AA64_CP_REG(r->opc0, opc1, r->crn, crm, opc2); @@ -7542,25 +7538,24 @@ static void add_cpreg_to_hashtable_aa64(ARMCPU *cpu, const ARMCPRegInfo *r, * and name that it is passed, so it's OK to use * a local struct here. */ - ARMCPRegInfo nxs_ri = *r; - g_autofree char *name = g_strdup_printf("%sNXS", r->name); + ARMCPRegInfo *nxs_ri = alloc_cpreg(r, r->name, "NXS"); uint32_t nxs_key; - assert(nxs_ri.crn < 0xf); - nxs_ri.crn++; + assert(nxs_ri->crn < 0xf); + nxs_ri->crn++; /* Also increment the CRN field inside the key value */ nxs_key = key + (1 << CP_REG_ARM64_SYSREG_CRN_SHIFT); - if (nxs_ri.fgt) { - nxs_ri.fgt |= R_FGT_NXS_MASK; + if (nxs_ri->fgt) { + nxs_ri->fgt |= R_FGT_NXS_MASK; } - add_cpreg_to_hashtable(cpu, &nxs_ri, ARM_CP_STATE_AA64, + add_cpreg_to_hashtable(cpu, nxs_ri, ARM_CP_STATE_AA64, ARM_CP_SECSTATE_NS, 0, crm, opc1, opc2, - name, nxs_key); + nxs_key); } add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA64, ARM_CP_SECSTATE_NS, - 0, crm, opc1, opc2, r->name, key); + 0, crm, opc1, opc2, key); } void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) @@ -7767,16 +7762,20 @@ void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) for (int crm = crmmin; crm <= crmmax; crm++) { for (int opc1 = opc1min; opc1 <= opc1max; opc1++) { for (int opc2 = opc2min; opc2 <= opc2max; opc2++) { + ARMCPRegInfo *r2 = alloc_cpreg(r, r->name, NULL); + ARMCPRegInfo *r3; + switch (r->state) { case ARM_CP_STATE_AA32: - add_cpreg_to_hashtable_aa32(cpu, r, cp, crm, opc1, opc2); + add_cpreg_to_hashtable_aa32(cpu, r2, cp, crm, opc1, opc2); break; case ARM_CP_STATE_AA64: - add_cpreg_to_hashtable_aa64(cpu, r, crm, opc1, opc2); + add_cpreg_to_hashtable_aa64(cpu, r2, crm, opc1, opc2); break; case ARM_CP_STATE_BOTH: - add_cpreg_to_hashtable_aa32(cpu, r, cp, crm, opc1, opc2); - add_cpreg_to_hashtable_aa64(cpu, r, crm, opc1, opc2); + r3 = alloc_cpreg(r2, r2->name, NULL); + add_cpreg_to_hashtable_aa32(cpu, r2, cp, crm, opc1, opc2); + add_cpreg_to_hashtable_aa64(cpu, r3, crm, opc1, opc2); break; default: g_assert_not_reached(); From dde8637d283e71c6e9bef776e60df099013237e7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:28 -0700 Subject: [PATCH 0888/1794] target/arm: Remove name argument to alloc_cpreg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All callers now pass in->name, so take the value from there. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/helper.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 88b5ec1a5a2d3..a199320f140d1 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7366,9 +7366,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) * Copy a ARMCPRegInfo structure, allocating it along with the name * and an optional suffix to the name. */ -static ARMCPRegInfo *alloc_cpreg(const ARMCPRegInfo *in, - const char *name, const char *suffix) +static ARMCPRegInfo *alloc_cpreg(const ARMCPRegInfo *in, const char *suffix) { + const char *name = in->name; size_t name_len = strlen(name); size_t suff_len = suffix ? strlen(suffix) : 0; ARMCPRegInfo *out = g_malloc(sizeof(*in) + name_len + suff_len + 1); @@ -7508,7 +7508,7 @@ static void add_cpreg_to_hashtable_aa32(ARMCPU *cpu, ARMCPRegInfo *r, cp, crm, opc1, opc2, key); break; case ARM_CP_SECSTATE_BOTH: - r_s = alloc_cpreg(r, r->name, "_S"); + r_s = alloc_cpreg(r, "_S"); add_cpreg_to_hashtable(cpu, r_s, ARM_CP_STATE_AA32, ARM_CP_SECSTATE_S, cp, crm, opc1, opc2, key); @@ -7538,7 +7538,7 @@ static void add_cpreg_to_hashtable_aa64(ARMCPU *cpu, ARMCPRegInfo *r, * and name that it is passed, so it's OK to use * a local struct here. */ - ARMCPRegInfo *nxs_ri = alloc_cpreg(r, r->name, "NXS"); + ARMCPRegInfo *nxs_ri = alloc_cpreg(r, "NXS"); uint32_t nxs_key; assert(nxs_ri->crn < 0xf); @@ -7762,7 +7762,7 @@ void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) for (int crm = crmmin; crm <= crmmax; crm++) { for (int opc1 = opc1min; opc1 <= opc1max; opc1++) { for (int opc2 = opc2min; opc2 <= opc2max; opc2++) { - ARMCPRegInfo *r2 = alloc_cpreg(r, r->name, NULL); + ARMCPRegInfo *r2 = alloc_cpreg(r, NULL); ARMCPRegInfo *r3; switch (r->state) { @@ -7773,7 +7773,7 @@ void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) add_cpreg_to_hashtable_aa64(cpu, r2, crm, opc1, opc2); break; case ARM_CP_STATE_BOTH: - r3 = alloc_cpreg(r2, r2->name, NULL); + r3 = alloc_cpreg(r2, NULL); add_cpreg_to_hashtable_aa32(cpu, r2, cp, crm, opc1, opc2); add_cpreg_to_hashtable_aa64(cpu, r3, crm, opc1, opc2); break; From afe6ed3cfb4f19bf93632ad0c763610cef07637e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:29 -0700 Subject: [PATCH 0889/1794] target/arm: Move alias setting for wildcards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move this test from add_cpreg_to_hashtable to define_one_arm_cp_reg_with_opaque, where we can also simplify it based on the loop variables. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé [PMM: adjusted placement of comma in a comment] Signed-off-by: Peter Maydell --- target/arm/helper.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index a199320f140d1..274b7b5808e24 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7448,20 +7448,12 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, ARMCPRegInfo *r, } /* - * By convention, for wildcarded registers only the first - * entry is used for migration; the others are marked as - * ALIAS so we don't try to transfer the register - * multiple times. Special registers (ie NOP/WFI) are - * never migratable and not even raw-accessible. + * Special registers (ie NOP/WFI) are never migratable and + * are not even raw-accessible. */ if (r->type & ARM_CP_SPECIAL_MASK) { r->type |= ARM_CP_NO_RAW; } - if (((r->crm == CP_ANY) && crm != 0) || - ((r->opc1 == CP_ANY) && opc1 != 0) || - ((r->opc2 == CP_ANY) && opc2 != 0)) { - r->type |= ARM_CP_ALIAS | ARM_CP_NO_GDB; - } /* * Update fields to match the instantiation, overwiting wildcards @@ -7765,6 +7757,16 @@ void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) ARMCPRegInfo *r2 = alloc_cpreg(r, NULL); ARMCPRegInfo *r3; + /* + * By convention, for wildcarded registers only the first + * entry is used for migration; the others are marked as + * ALIAS so we don't try to transfer the register + * multiple times. + */ + if (crm != crmmin || opc1 != opc1min || opc2 != opc2min) { + r2->type |= ARM_CP_ALIAS | ARM_CP_NO_GDB; + } + switch (r->state) { case ARM_CP_STATE_AA32: add_cpreg_to_hashtable_aa32(cpu, r2, cp, crm, opc1, opc2); From 415e21ac85d5209768f77cda2eef24d3da3deaa9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:30 -0700 Subject: [PATCH 0890/1794] target/arm: Move writeback of CP_ANY fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the writeback of cp, crm, opc1, opc2 to define_one_arm_cp_reg, which means we don't have to pass all those parameters down to subroutines. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/helper.c | 52 ++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 274b7b5808e24..4063c8a0b6f60 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7390,7 +7390,6 @@ static ARMCPRegInfo *alloc_cpreg(const ARMCPRegInfo *in, const char *suffix) */ static void add_cpreg_to_hashtable(ARMCPU *cpu, ARMCPRegInfo *r, CPState state, CPSecureState secstate, - int cp, int crm, int opc1, int opc2, uint32_t key) { CPUARMState *env = &cpu->env; @@ -7457,12 +7456,8 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, ARMCPRegInfo *r, /* * Update fields to match the instantiation, overwiting wildcards - * such as CP_ANY, ARM_CP_STATE_BOTH, or ARM_CP_SECSTATE_BOTH. + * such as ARM_CP_STATE_BOTH or ARM_CP_SECSTATE_BOTH. */ - r->cp = cp; - r->crm = crm; - r->opc1 = opc1; - r->opc2 = opc2; r->state = state; r->secure = secstate; @@ -7478,8 +7473,7 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, ARMCPRegInfo *r, g_hash_table_insert(cpu->cp_regs, (gpointer)(uintptr_t)key, r); } -static void add_cpreg_to_hashtable_aa32(ARMCPU *cpu, ARMCPRegInfo *r, - int cp, int crm, int opc1, int opc2) +static void add_cpreg_to_hashtable_aa32(ARMCPU *cpu, ARMCPRegInfo *r) { /* * Under AArch32 CP registers can be common @@ -7487,7 +7481,8 @@ static void add_cpreg_to_hashtable_aa32(ARMCPU *cpu, ARMCPRegInfo *r, */ ARMCPRegInfo *r_s; bool is64 = r->type & ARM_CP_64BIT; - uint32_t key = ENCODE_CP_REG(cp, is64, 0, r->crn, crm, opc1, opc2); + uint32_t key = ENCODE_CP_REG(r->cp, is64, 0, r->crn, + r->crm, r->opc1, r->opc2); assert(!(r->type & ARM_CP_ADD_TLBI_NXS)); /* aa64 only */ @@ -7496,27 +7491,26 @@ static void add_cpreg_to_hashtable_aa32(ARMCPU *cpu, ARMCPRegInfo *r, key |= CP_REG_AA32_NS_MASK; /* fall through */ case ARM_CP_SECSTATE_S: - add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, r->secure, - cp, crm, opc1, opc2, key); + add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, r->secure, key); break; case ARM_CP_SECSTATE_BOTH: r_s = alloc_cpreg(r, "_S"); - add_cpreg_to_hashtable(cpu, r_s, ARM_CP_STATE_AA32, ARM_CP_SECSTATE_S, - cp, crm, opc1, opc2, key); + add_cpreg_to_hashtable(cpu, r_s, ARM_CP_STATE_AA32, + ARM_CP_SECSTATE_S, key); key |= CP_REG_AA32_NS_MASK; - add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, ARM_CP_SECSTATE_NS, - cp, crm, opc1, opc2, key); + add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32, + ARM_CP_SECSTATE_NS, key); break; default: g_assert_not_reached(); } } -static void add_cpreg_to_hashtable_aa64(ARMCPU *cpu, ARMCPRegInfo *r, - int crm, int opc1, int opc2) +static void add_cpreg_to_hashtable_aa64(ARMCPU *cpu, ARMCPRegInfo *r) { - uint32_t key = ENCODE_AA64_CP_REG(r->opc0, opc1, r->crn, crm, opc2); + uint32_t key = ENCODE_AA64_CP_REG(r->opc0, r->opc1, + r->crn, r->crm, r->opc2); if ((r->type & ARM_CP_ADD_TLBI_NXS) && cpu_isar_feature(aa64_xs, cpu)) { @@ -7542,12 +7536,11 @@ static void add_cpreg_to_hashtable_aa64(ARMCPU *cpu, ARMCPRegInfo *r, } add_cpreg_to_hashtable(cpu, nxs_ri, ARM_CP_STATE_AA64, - ARM_CP_SECSTATE_NS, 0, crm, opc1, opc2, - nxs_key); + ARM_CP_SECSTATE_NS, nxs_key); } - add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA64, ARM_CP_SECSTATE_NS, - 0, crm, opc1, opc2, key); + add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA64, + ARM_CP_SECSTATE_NS, key); } void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) @@ -7767,17 +7760,24 @@ void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r) r2->type |= ARM_CP_ALIAS | ARM_CP_NO_GDB; } + /* Overwrite CP_ANY with the instantiation. */ + r2->crm = crm; + r2->opc1 = opc1; + r2->opc2 = opc2; + switch (r->state) { case ARM_CP_STATE_AA32: - add_cpreg_to_hashtable_aa32(cpu, r2, cp, crm, opc1, opc2); + add_cpreg_to_hashtable_aa32(cpu, r2); break; case ARM_CP_STATE_AA64: - add_cpreg_to_hashtable_aa64(cpu, r2, crm, opc1, opc2); + add_cpreg_to_hashtable_aa64(cpu, r2); break; case ARM_CP_STATE_BOTH: r3 = alloc_cpreg(r2, NULL); - add_cpreg_to_hashtable_aa32(cpu, r2, cp, crm, opc1, opc2); - add_cpreg_to_hashtable_aa64(cpu, r3, crm, opc1, opc2); + r2->cp = cp; + add_cpreg_to_hashtable_aa32(cpu, r2); + r3->cp = 0; + add_cpreg_to_hashtable_aa64(cpu, r3); break; default: g_assert_not_reached(); From 5bd746e212b0fac12cfe7332a0bb95e4b7276f53 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:31 -0700 Subject: [PATCH 0891/1794] target/arm: Move endianness fixup for 32-bit registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the test outside of the banked register block, and repeat the AA32 test. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/helper.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 4063c8a0b6f60..18066b0c5dc4c 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7438,14 +7438,21 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, ARMCPRegInfo *r, */ r->type |= ARM_CP_ALIAS; } - - if (HOST_BIG_ENDIAN && - r->state == ARM_CP_STATE_BOTH && r->fieldoffset) { - r->fieldoffset += sizeof(uint32_t); - } } } + /* + * For 32-bit AArch32 regs shared with 64-bit AArch64 regs, + * adjust the field offset for endianness. This had to be + * delayed until banked registers were resolved. + */ + if (HOST_BIG_ENDIAN && + state == ARM_CP_STATE_AA32 && + r->state == ARM_CP_STATE_BOTH && + r->fieldoffset) { + r->fieldoffset += sizeof(uint32_t); + } + /* * Special registers (ie NOP/WFI) are never migratable and * are not even raw-accessible. From 10bca9650c46633f484099dd3ab8167a9820084f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:32 -0700 Subject: [PATCH 0892/1794] target/arm: Rename TBFLAG_A64_NV2_MEM_E20 with *_E2H MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Install e2h in tbflags and compute nv2_mem_e20 from that in aarch64_tr_init_disas_context. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/cpu.h | 3 +-- target/arm/tcg/hflags.c | 8 +++++--- target/arm/tcg/translate-a64.c | 3 ++- target/arm/tcg/translate.h | 2 ++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 1c0deb723d705..d5534e358045e 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3065,8 +3065,7 @@ FIELD(TBFLAG_A64, ATA0, 31, 1) FIELD(TBFLAG_A64, NV, 32, 1) FIELD(TBFLAG_A64, NV1, 33, 1) FIELD(TBFLAG_A64, NV2, 34, 1) -/* Set if FEAT_NV2 RAM accesses use the EL2&0 translation regime */ -FIELD(TBFLAG_A64, NV2_MEM_E20, 35, 1) +FIELD(TBFLAG_A64, E2H, 35, 1) /* Set if FEAT_NV2 RAM accesses are big-endian */ FIELD(TBFLAG_A64, NV2_MEM_BE, 36, 1) FIELD(TBFLAG_A64, AH, 37, 1) /* FPCR.AH */ diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index 01894226cc9b6..17f83f13a4002 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -258,6 +258,11 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, DP_TBFLAG_A64(flags, TBII, tbii); DP_TBFLAG_A64(flags, TBID, tbid); + /* E2H is used by both VHE and NV2. */ + if (hcr & HCR_E2H) { + DP_TBFLAG_A64(flags, E2H, 1); + } + if (cpu_isar_feature(aa64_sve, env_archcpu(env))) { int sve_el = sve_exception_el(env, el); @@ -390,9 +395,6 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, } if (hcr & HCR_NV2) { DP_TBFLAG_A64(flags, NV2, 1); - if (hcr & HCR_E2H) { - DP_TBFLAG_A64(flags, NV2_MEM_E20, 1); - } if (env->cp15.sctlr_el[2] & SCTLR_EE) { DP_TBFLAG_A64(flags, NV2_MEM_BE, 1); } diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 0ec309f1ea9ec..599e7a36ee3ce 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -10304,10 +10304,11 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->pstate_za = EX_TBFLAG_A64(tb_flags, PSTATE_ZA); dc->sme_trap_nonstreaming = EX_TBFLAG_A64(tb_flags, SME_TRAP_NONSTREAMING); dc->naa = EX_TBFLAG_A64(tb_flags, NAA); + dc->e2h = EX_TBFLAG_A64(tb_flags, E2H); dc->nv = EX_TBFLAG_A64(tb_flags, NV); dc->nv1 = EX_TBFLAG_A64(tb_flags, NV1); dc->nv2 = EX_TBFLAG_A64(tb_flags, NV2); - dc->nv2_mem_e20 = EX_TBFLAG_A64(tb_flags, NV2_MEM_E20); + dc->nv2_mem_e20 = dc->nv2 && dc->e2h; dc->nv2_mem_be = EX_TBFLAG_A64(tb_flags, NV2_MEM_BE); dc->fpcr_ah = EX_TBFLAG_A64(tb_flags, AH); dc->fpcr_nep = EX_TBFLAG_A64(tb_flags, NEP); diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index ec4755ae3fdc8..f1a6e5e2b61fc 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -150,6 +150,8 @@ typedef struct DisasContext { bool trap_eret; /* True if FEAT_LSE2 SCTLR_ELx.nAA is set */ bool naa; + /* True if HCR_EL2.E2H is set */ + bool e2h; /* True if FEAT_NV HCR_EL2.NV is enabled */ bool nv; /* True if NV enabled and HCR_EL2.NV1 is set */ From e87878d4963714e9ff4b9e0ae71567dbef0af09c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:33 -0700 Subject: [PATCH 0893/1794] target/arm: Split out redirect_cpreg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 599e7a36ee3ce..c0fa2137b6350 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2455,6 +2455,19 @@ static void gen_sysreg_undef(DisasContext *s, bool isread, gen_exception_insn(s, 0, EXCP_UDEF, syndrome); } +/* + * Look up @key, returning the cpreg, which must exist. + * Additionally, the new cpreg must also be accessible. + */ +static const ARMCPRegInfo * +redirect_cpreg(DisasContext *s, uint32_t key, bool isread) +{ + const ARMCPRegInfo *ri = get_arm_cp_reginfo(s->cp_regs, key); + assert(ri); + assert(cp_access_ok(s->current_el, ri, isread)); + return ri; +} + /* MRS - move from system register * MSR (register) - move to system register * SYS @@ -2603,9 +2616,7 @@ static void handle_sys(DisasContext *s, bool isread, * fine-grained-traps on EL1 also do not apply here. */ key = ENCODE_AA64_CP_REG(op0, 0, crn, crm, op2); - ri = get_arm_cp_reginfo(s->cp_regs, key); - assert(ri); - assert(cp_access_ok(s->current_el, ri, isread)); + ri = redirect_cpreg(s, key, isread); /* * We might not have done an update_pc earlier, so check we don't * need it. We could support this in future if necessary. From 34577fc2f9c8048e96f66b3425df62d0662e9a1e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:34 -0700 Subject: [PATCH 0894/1794] target/arm: Redirect VHE FOO_EL1 -> FOO_EL2 during translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Manos Pitsidianakis Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/cpregs.h | 6 ++++ target/arm/gdbstub.c | 5 ++++ target/arm/helper.c | 53 +--------------------------------- target/arm/tcg/translate-a64.c | 9 ++++++ 4 files changed, 21 insertions(+), 52 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 08fc42ea57137..eac0cb9ebf1fb 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -936,6 +936,12 @@ struct ARMCPRegInfo { */ uint32_t nv2_redirect_offset; + /* + * With VHE, with E2H, at EL2, access to this EL0/EL1 reg redirects + * to the EL2 reg with the specified key. + */ + uint32_t vhe_redir_to_el2; + /* This is used only by VHE. */ void *opaque; /* diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index 4e2ac49b6a9d0..87d40d4366b69 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -249,6 +249,11 @@ static int arm_gdb_get_sysreg(CPUState *cs, GByteArray *buf, int reg) if (ri) { switch (cpreg_field_type(ri)) { case MO_64: + if (ri->vhe_redir_to_el2 && + (arm_hcr_el2_eff(env) & HCR_E2H) && + arm_current_el(env) == 2) { + ri = get_arm_cp_reginfo(cpu->cp_regs, ri->vhe_redir_to_el2); + } return gdb_get_reg64(buf, (uint64_t)read_raw_cp_reg(env, ri)); case MO_32: return gdb_get_reg32(buf, (uint32_t)read_raw_cp_reg(env, ri)); diff --git a/target/arm/helper.c b/target/arm/helper.c index 18066b0c5dc4c..87a32e363e99a 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4417,47 +4417,6 @@ static CPAccessResult access_el1nvvct(CPUARMState *env, const ARMCPRegInfo *ri, return e2h_access(env, ri, isread); } -/* Test if system register redirection is to occur in the current state. */ -static bool redirect_for_e2h(CPUARMState *env) -{ - return arm_current_el(env) == 2 && (arm_hcr_el2_eff(env) & HCR_E2H); -} - -static uint64_t el2_e2h_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - CPReadFn *readfn; - - if (redirect_for_e2h(env)) { - /* Switch to the saved EL2 version of the register. */ - ri = ri->opaque; - readfn = ri->readfn; - } else { - readfn = ri->orig_readfn; - } - if (readfn == NULL) { - readfn = raw_read; - } - return readfn(env, ri); -} - -static void el2_e2h_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPWriteFn *writefn; - - if (redirect_for_e2h(env)) { - /* Switch to the saved EL2 version of the register. */ - ri = ri->opaque; - writefn = ri->writefn; - } else { - writefn = ri->orig_writefn; - } - if (writefn == NULL) { - writefn = raw_write; - } - writefn(env, ri, value); -} - static uint64_t el2_e2h_e12_read(CPUARMState *env, const ARMCPRegInfo *ri) { /* Pass the EL1 register accessor its ri, not the EL12 alias ri */ @@ -4632,17 +4591,7 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) (gpointer)(uintptr_t)a->new_key, new_reg); g_assert(ok); - src_reg->opaque = dst_reg; - src_reg->orig_readfn = src_reg->readfn ?: raw_read; - src_reg->orig_writefn = src_reg->writefn ?: raw_write; - if (!src_reg->raw_readfn) { - src_reg->raw_readfn = raw_read; - } - if (!src_reg->raw_writefn) { - src_reg->raw_writefn = raw_write; - } - src_reg->readfn = el2_e2h_read; - src_reg->writefn = el2_e2h_write; + src_reg->vhe_redir_to_el2 = a->dst_key; } } #endif diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index c0fa2137b6350..3ef24fb0c3d32 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2573,6 +2573,15 @@ static void handle_sys(DisasContext *s, bool isread, } } + if (ri->vhe_redir_to_el2 && s->current_el == 2 && s->e2h) { + /* + * This one of the FOO_EL1 registers which redirect to FOO_EL2 + * from EL2 when HCR_EL2.E2H is set. + */ + key = ri->vhe_redir_to_el2; + ri = redirect_cpreg(s, key, isread); + } + if (ri->accessfn || (ri->fgt && s->fgt_active)) { /* Emit code to perform further access permissions checks at * runtime; this may result in an exception. From 5850e03554331dea7dde57c3f73de9b9fc482033 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:35 -0700 Subject: [PATCH 0895/1794] target/arm: Redirect VHE FOO_EL12 to FOO_EL1 during translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Tested-by: Philippe Mathieu-Daudé [PMM: expanded a comment slightly] Signed-off-by: Peter Maydell --- target/arm/cpregs.h | 22 ++++--------- target/arm/gdbstub.c | 2 ++ target/arm/helper.c | 57 +++------------------------------- target/arm/tcg/translate-a64.c | 12 +++++++ 4 files changed, 25 insertions(+), 68 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index eac0cb9ebf1fb..8ab5892accea1 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -942,8 +942,12 @@ struct ARMCPRegInfo { */ uint32_t vhe_redir_to_el2; - /* This is used only by VHE. */ - void *opaque; + /* + * With VHE, with E2H, at EL2+, access to this EL02/EL12 reg + * redirects to the EL0/EL1 reg with the specified key. + */ + uint32_t vhe_redir_to_el01; + /* * Value of this register, if it is ARM_CP_CONST. Otherwise, if * fieldoffset is non-zero, the reset value of the register. @@ -1011,20 +1015,6 @@ struct ARMCPRegInfo { * fieldoffset is 0 then no reset will be done. */ CPResetFn *resetfn; - - /* - * "Original" readfn, writefn, accessfn. - * For ARMv8.1-VHE register aliases, we overwrite the read/write - * accessor functions of various EL1/EL0 to perform the runtime - * check for which sysreg should actually be modified, and then - * forwards the operation. Before overwriting the accessors, - * the original function is copied here, so that accesses that - * really do go to the EL1/EL0 version proceed normally. - * (The corresponding EL2 register is linked via opaque.) - */ - CPReadFn *orig_readfn; - CPWriteFn *orig_writefn; - CPAccessFn *orig_accessfn; }; void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *regs); diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index 87d40d4366b69..8d2229f5192db 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -253,6 +253,8 @@ static int arm_gdb_get_sysreg(CPUState *cs, GByteArray *buf, int reg) (arm_hcr_el2_eff(env) & HCR_E2H) && arm_current_el(env) == 2) { ri = get_arm_cp_reginfo(cpu->cp_regs, ri->vhe_redir_to_el2); + } else if (ri->vhe_redir_to_el01) { + ri = get_arm_cp_reginfo(cpu->cp_regs, ri->vhe_redir_to_el01); } return gdb_get_reg64(buf, (uint64_t)read_raw_cp_reg(env, ri)); case MO_32: diff --git a/target/arm/helper.c b/target/arm/helper.c index 87a32e363e99a..3840ca62a696f 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4417,42 +4417,6 @@ static CPAccessResult access_el1nvvct(CPUARMState *env, const ARMCPRegInfo *ri, return e2h_access(env, ri, isread); } -static uint64_t el2_e2h_e12_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - /* Pass the EL1 register accessor its ri, not the EL12 alias ri */ - return ri->orig_readfn(env, ri->opaque); -} - -static void el2_e2h_e12_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* Pass the EL1 register accessor its ri, not the EL12 alias ri */ - return ri->orig_writefn(env, ri->opaque, value); -} - -static CPAccessResult el2_e2h_e12_access(CPUARMState *env, - const ARMCPRegInfo *ri, - bool isread) -{ - if (arm_current_el(env) == 1) { - /* - * This must be a FEAT_NV access (will either trap or redirect - * to memory). None of the registers with _EL12 aliases want to - * apply their trap controls for this kind of access, so don't - * call the orig_accessfn or do the "UNDEF when E2H is 0" check. - */ - return CP_ACCESS_OK; - } - /* FOO_EL12 aliases only exist when E2H is 1; otherwise they UNDEF */ - if (!(arm_hcr_el2_eff(env) & HCR_E2H)) { - return CP_ACCESS_UNDEFINED; - } - if (ri->orig_accessfn) { - return ri->orig_accessfn(env, ri->opaque, isread); - } - return CP_ACCESS_OK; -} - static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) { struct E2HAlias { @@ -4541,9 +4505,6 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) g_assert(strcmp(src_reg->name, a->src_name) == 0); g_assert(strcmp(dst_reg->name, a->dst_name) == 0); - /* None of the core system registers use opaque; we will. */ - g_assert(src_reg->opaque == NULL); - /* Create alias before redirection so we dup the right data. */ new_reg = g_memdup(src_reg, sizeof(ARMCPRegInfo)); @@ -4562,19 +4523,11 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) >> CP_REG_ARM64_SYSREG_OP1_SHIFT; new_reg->opc2 = (a->new_key & CP_REG_ARM64_SYSREG_OP2_MASK) >> CP_REG_ARM64_SYSREG_OP2_SHIFT; - new_reg->opaque = src_reg; - new_reg->orig_readfn = src_reg->readfn ?: raw_read; - new_reg->orig_writefn = src_reg->writefn ?: raw_write; - new_reg->orig_accessfn = src_reg->accessfn; - if (!new_reg->raw_readfn) { - new_reg->raw_readfn = raw_read; - } - if (!new_reg->raw_writefn) { - new_reg->raw_writefn = raw_write; - } - new_reg->readfn = el2_e2h_e12_read; - new_reg->writefn = el2_e2h_e12_write; - new_reg->accessfn = el2_e2h_e12_access; + new_reg->vhe_redir_to_el01 = a->src_key; + new_reg->readfn = NULL; + new_reg->writefn = NULL; + new_reg->accessfn = NULL; + new_reg->fieldoffset = 0; /* * If the _EL1 register is redirected to memory by FEAT_NV2, diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 3ef24fb0c3d32..a0e3300231faa 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2580,6 +2580,18 @@ static void handle_sys(DisasContext *s, bool isread, */ key = ri->vhe_redir_to_el2; ri = redirect_cpreg(s, key, isread); + } else if (ri->vhe_redir_to_el01 && s->current_el >= 2) { + /* + * This is one of the FOO_EL12 or FOO_EL02 registers. + * With !E2H, they all UNDEF. + * With E2H, from EL2 or EL3, they redirect to FOO_EL1/FOO_EL0. + */ + if (!s->e2h) { + gen_sysreg_undef(s, isread, op0, op1, op2, crn, crm, rt); + return; + } + key = ri->vhe_redir_to_el01; + ri = redirect_cpreg(s, key, isread); } if (ri->accessfn || (ri->fgt && s->fgt_active)) { From 2afdc4ce6098fc4316299d8af37f89ce75b65f1d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:36 -0700 Subject: [PATCH 0896/1794] target/arm: Rename some cpreg to their aarch64 names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename those registers which will have FOO_EL12 aliases. Reviewed-by: Manos Pitsidianakis Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/helper.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 3840ca62a696f..12835977bd8f3 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -671,7 +671,7 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { */ { .name = "WFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, }, - { .name = "CPACR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, + { .name = "CPACR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2, .accessfn = cpacr_access, .fgt = FGT_CPACR_EL1, .nv2_redirect_offset = 0x100 | NV2_REDIR_NV1, @@ -2018,7 +2018,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .resetfn = arm_gt_cntfrq_reset, }, /* overall control: mostly access permissions */ - { .name = "CNTKCTL", .state = ARM_CP_STATE_BOTH, + { .name = "CNTKCTL_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 14, .crm = 1, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c14_cntkctl), @@ -3048,8 +3048,8 @@ static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri) } static const ARMCPRegInfo lpae_cp_reginfo[] = { - /* NOP AMAIR0/1 */ - { .name = "AMAIR0", .state = ARM_CP_STATE_BOTH, + /* AMAIR0 is mapped to AMAIR_EL1[31:0] */ + { .name = "AMAIR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_AMAIR_EL1, @@ -4430,11 +4430,11 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) static const struct E2HAlias aliases[] = { { K(3, 0, 1, 0, 0), K(3, 4, 1, 0, 0), K(3, 5, 1, 0, 0), - "SCTLR", "SCTLR_EL2", "SCTLR_EL12" }, + "SCTLR_EL1", "SCTLR_EL2", "SCTLR_EL12" }, { K(3, 0, 1, 0, 3), K(3, 4, 1, 0, 3), K(3, 5, 1, 0, 3), "SCTLR2_EL1", "SCTLR2_EL2", "SCTLR2_EL12", isar_feature_aa64_sctlr2 }, { K(3, 0, 1, 0, 2), K(3, 4, 1, 1, 2), K(3, 5, 1, 0, 2), - "CPACR", "CPTR_EL2", "CPACR_EL12" }, + "CPACR_EL1", "CPTR_EL2", "CPACR_EL12" }, { K(3, 0, 2, 0, 0), K(3, 4, 2, 0, 0), K(3, 5, 2, 0, 0), "TTBR0_EL1", "TTBR0_EL2", "TTBR0_EL12" }, { K(3, 0, 2, 0, 1), K(3, 4, 2, 0, 1), K(3, 5, 2, 0, 1), @@ -4458,13 +4458,13 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) { K(3, 0, 10, 2, 0), K(3, 4, 10, 2, 0), K(3, 5, 10, 2, 0), "MAIR_EL1", "MAIR_EL2", "MAIR_EL12" }, { K(3, 0, 10, 3, 0), K(3, 4, 10, 3, 0), K(3, 5, 10, 3, 0), - "AMAIR0", "AMAIR_EL2", "AMAIR_EL12" }, + "AMAIR_EL1", "AMAIR_EL2", "AMAIR_EL12" }, { K(3, 0, 12, 0, 0), K(3, 4, 12, 0, 0), K(3, 5, 12, 0, 0), - "VBAR", "VBAR_EL2", "VBAR_EL12" }, + "VBAR_EL1", "VBAR_EL2", "VBAR_EL12" }, { K(3, 0, 13, 0, 1), K(3, 4, 13, 0, 1), K(3, 5, 13, 0, 1), "CONTEXTIDR_EL1", "CONTEXTIDR_EL2", "CONTEXTIDR_EL12" }, { K(3, 0, 14, 1, 0), K(3, 4, 14, 1, 0), K(3, 5, 14, 1, 0), - "CNTKCTL", "CNTHCTL_EL2", "CNTKCTL_EL12" }, + "CNTKCTL_EL1", "CNTHCTL_EL2", "CNTKCTL_EL12" }, { K(3, 0, 1, 2, 0), K(3, 4, 1, 2, 0), K(3, 5, 1, 2, 0), "ZCR_EL1", "ZCR_EL2", "ZCR_EL12", isar_feature_aa64_sve }, @@ -7098,7 +7098,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_VBAR)) { static const ARMCPRegInfo vbar_cp_reginfo[] = { - { .name = "VBAR", .state = ARM_CP_STATE_BOTH, + { .name = "VBAR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .writefn = vbar_write, .accessfn = access_nv1, @@ -7114,7 +7114,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) /* Generic registers whose values depend on the implementation */ { ARMCPRegInfo sctlr = { - .name = "SCTLR", .state = ARM_CP_STATE_BOTH, + .name = "SCTLR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_SCTLR_EL1, From d3a2ee5fe2540c8193561ce8bc1f979c2cf0877f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Sep 2025 07:22:37 -0700 Subject: [PATCH 0897/1794] target/arm: Remove define_arm_vh_e2h_redirects_aliases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Populate vhe_redir_to_{el2,el01} on each ARMCPRegInfo. Clear the fields within add_cpreg_to_hashtable_aa32. Create the FOO_EL12 cpreg within add_cpreg_to_hashtable_aa64; add ARM_CP_NO_RAW. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Tested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/cpregs.h | 6 +- target/arm/helper.c | 243 +++++++++++++++++++------------------------- 2 files changed, 107 insertions(+), 142 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 8ab5892accea1..57fde5f57ae30 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -943,8 +943,10 @@ struct ARMCPRegInfo { uint32_t vhe_redir_to_el2; /* - * With VHE, with E2H, at EL2+, access to this EL02/EL12 reg - * redirects to the EL0/EL1 reg with the specified key. + * For VHE. Before registration, this field holds the key for an + * EL02/EL12 reg to be created to point back to this EL0/EL1 reg. + * After registration, this field is set only on the EL02/EL12 reg + * and points back to the EL02/EL12 reg for redirection with E2H. */ uint32_t vhe_redir_to_el01; diff --git a/target/arm/helper.c b/target/arm/helper.c index 12835977bd8f3..c5a8ef50493a1 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -454,6 +454,8 @@ static const ARMCPRegInfo cp_reginfo[] = { .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_CONTEXTIDR_EL1, .nv2_redirect_offset = 0x108 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 13, 0, 1), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 13, 0, 1), .secure = ARM_CP_SECSTATE_NS, .fieldoffset = offsetof(CPUARMState, cp15.contextidr_el[1]), .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, @@ -674,6 +676,8 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { { .name = "CPACR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2, .accessfn = cpacr_access, .fgt = FGT_CPACR_EL1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 1, 1, 2), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 1, 0, 2), .nv2_redirect_offset = 0x100 | NV2_REDIR_NV1, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.cpacr_el1), .resetfn = cpacr_reset, .writefn = cpacr_write, .readfn = cpacr_read }, @@ -956,12 +960,16 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_AFSR0_EL1, .nv2_redirect_offset = 0x128 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 5, 1, 0), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 5, 1, 0), .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "AFSR1_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 1, .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_AFSR1_EL1, .nv2_redirect_offset = 0x130 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 5, 1, 1), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 5, 1, 1), .type = ARM_CP_CONST, .resetvalue = 0 }, /* * MAIR can just read-as-written because we don't implement caches @@ -972,6 +980,8 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_MAIR_EL1, .nv2_redirect_offset = 0x140 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 10, 2, 0), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 10, 2, 0), .fieldoffset = offsetof(CPUARMState, cp15.mair_el[1]), .resetvalue = 0 }, { .name = "MAIR_EL3", .state = ARM_CP_STATE_AA64, @@ -2021,6 +2031,8 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { { .name = "CNTKCTL_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 14, .crm = 1, .opc2 = 0, .access = PL1_RW, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 14, 1, 0), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 14, 1, 0), .fieldoffset = offsetof(CPUARMState, cp15.c14_cntkctl), .resetvalue = 0, }, @@ -2811,6 +2823,8 @@ static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = { .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_FAR_EL1, .nv2_redirect_offset = 0x220 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 6, 0, 0), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 6, 0, 0), .fieldoffset = offsetof(CPUARMState, cp15.far_el[1]), .resetvalue = 0, }, }; @@ -2821,12 +2835,16 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_ESR_EL1, .nv2_redirect_offset = 0x138 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 5, 2, 0), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 5, 2, 0), .fieldoffset = offsetof(CPUARMState, cp15.esr_el[1]), .resetvalue = 0, }, { .name = "TTBR0_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_TTBR0_EL1, .nv2_redirect_offset = 0x200 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 2, 0, 0), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 2, 0, 0), .writefn = vmsa_ttbr_write, .resetvalue = 0, .raw_writefn = raw_write, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr0_s), offsetof(CPUARMState, cp15.ttbr0_ns) } }, @@ -2835,6 +2853,8 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_TTBR1_EL1, .nv2_redirect_offset = 0x210 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 2, 0, 1), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 2, 0, 1), .writefn = vmsa_ttbr_write, .resetvalue = 0, .raw_writefn = raw_write, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr1_s), offsetof(CPUARMState, cp15.ttbr1_ns) } }, @@ -2843,6 +2863,8 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_TCR_EL1, .nv2_redirect_offset = 0x120 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 2, 0, 2), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 2, 0, 2), .writefn = vmsa_tcr_el12_write, .raw_writefn = raw_write, .resetvalue = 0, @@ -3054,6 +3076,8 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_AMAIR_EL1, .nv2_redirect_offset = 0x148 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 10, 3, 0), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 10, 3, 0), .type = ARM_CP_CONST, .resetvalue = 0 }, /* AMAIR1 is mapped to AMAIR_EL1[63:32] */ { .name = "AMAIR1", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 1, @@ -3569,12 +3593,16 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 1, .access = PL1_RW, .accessfn = access_nv1, .nv2_redirect_offset = 0x230 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 4, 0, 1), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 4, 0, 1), .fieldoffset = offsetof(CPUARMState, elr_el[1]) }, { .name = "SPSR_EL1", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_nv1, .nv2_redirect_offset = 0x160 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 4, 0, 0), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 4, 0, 0), .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_SVC]) }, /* * We rely on the access checks not allowing the guest to write to the @@ -4417,136 +4445,6 @@ static CPAccessResult access_el1nvvct(CPUARMState *env, const ARMCPRegInfo *ri, return e2h_access(env, ri, isread); } -static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) -{ - struct E2HAlias { - uint32_t src_key, dst_key, new_key; - const char *src_name, *dst_name, *new_name; - bool (*feature)(const ARMISARegisters *id); - }; - -#define K(op0, op1, crn, crm, op2) \ - ENCODE_AA64_CP_REG(op0, op1, crn, crm, op2) - - static const struct E2HAlias aliases[] = { - { K(3, 0, 1, 0, 0), K(3, 4, 1, 0, 0), K(3, 5, 1, 0, 0), - "SCTLR_EL1", "SCTLR_EL2", "SCTLR_EL12" }, - { K(3, 0, 1, 0, 3), K(3, 4, 1, 0, 3), K(3, 5, 1, 0, 3), - "SCTLR2_EL1", "SCTLR2_EL2", "SCTLR2_EL12", isar_feature_aa64_sctlr2 }, - { K(3, 0, 1, 0, 2), K(3, 4, 1, 1, 2), K(3, 5, 1, 0, 2), - "CPACR_EL1", "CPTR_EL2", "CPACR_EL12" }, - { K(3, 0, 2, 0, 0), K(3, 4, 2, 0, 0), K(3, 5, 2, 0, 0), - "TTBR0_EL1", "TTBR0_EL2", "TTBR0_EL12" }, - { K(3, 0, 2, 0, 1), K(3, 4, 2, 0, 1), K(3, 5, 2, 0, 1), - "TTBR1_EL1", "TTBR1_EL2", "TTBR1_EL12" }, - { K(3, 0, 2, 0, 2), K(3, 4, 2, 0, 2), K(3, 5, 2, 0, 2), - "TCR_EL1", "TCR_EL2", "TCR_EL12" }, - { K(3, 0, 2, 0, 3), K(3, 4, 2, 0, 3), K(3, 5, 2, 0, 3), - "TCR2_EL1", "TCR2_EL2", "TCR2_EL12", isar_feature_aa64_tcr2 }, - { K(3, 0, 4, 0, 0), K(3, 4, 4, 0, 0), K(3, 5, 4, 0, 0), - "SPSR_EL1", "SPSR_EL2", "SPSR_EL12" }, - { K(3, 0, 4, 0, 1), K(3, 4, 4, 0, 1), K(3, 5, 4, 0, 1), - "ELR_EL1", "ELR_EL2", "ELR_EL12" }, - { K(3, 0, 5, 1, 0), K(3, 4, 5, 1, 0), K(3, 5, 5, 1, 0), - "AFSR0_EL1", "AFSR0_EL2", "AFSR0_EL12" }, - { K(3, 0, 5, 1, 1), K(3, 4, 5, 1, 1), K(3, 5, 5, 1, 1), - "AFSR1_EL1", "AFSR1_EL2", "AFSR1_EL12" }, - { K(3, 0, 5, 2, 0), K(3, 4, 5, 2, 0), K(3, 5, 5, 2, 0), - "ESR_EL1", "ESR_EL2", "ESR_EL12" }, - { K(3, 0, 6, 0, 0), K(3, 4, 6, 0, 0), K(3, 5, 6, 0, 0), - "FAR_EL1", "FAR_EL2", "FAR_EL12" }, - { K(3, 0, 10, 2, 0), K(3, 4, 10, 2, 0), K(3, 5, 10, 2, 0), - "MAIR_EL1", "MAIR_EL2", "MAIR_EL12" }, - { K(3, 0, 10, 3, 0), K(3, 4, 10, 3, 0), K(3, 5, 10, 3, 0), - "AMAIR_EL1", "AMAIR_EL2", "AMAIR_EL12" }, - { K(3, 0, 12, 0, 0), K(3, 4, 12, 0, 0), K(3, 5, 12, 0, 0), - "VBAR_EL1", "VBAR_EL2", "VBAR_EL12" }, - { K(3, 0, 13, 0, 1), K(3, 4, 13, 0, 1), K(3, 5, 13, 0, 1), - "CONTEXTIDR_EL1", "CONTEXTIDR_EL2", "CONTEXTIDR_EL12" }, - { K(3, 0, 14, 1, 0), K(3, 4, 14, 1, 0), K(3, 5, 14, 1, 0), - "CNTKCTL_EL1", "CNTHCTL_EL2", "CNTKCTL_EL12" }, - - { K(3, 0, 1, 2, 0), K(3, 4, 1, 2, 0), K(3, 5, 1, 2, 0), - "ZCR_EL1", "ZCR_EL2", "ZCR_EL12", isar_feature_aa64_sve }, - { K(3, 0, 1, 2, 6), K(3, 4, 1, 2, 6), K(3, 5, 1, 2, 6), - "SMCR_EL1", "SMCR_EL2", "SMCR_EL12", isar_feature_aa64_sme }, - - { K(3, 0, 5, 6, 0), K(3, 4, 5, 6, 0), K(3, 5, 5, 6, 0), - "TFSR_EL1", "TFSR_EL2", "TFSR_EL12", isar_feature_aa64_mte }, - - { K(3, 0, 13, 0, 7), K(3, 4, 13, 0, 7), K(3, 5, 13, 0, 7), - "SCXTNUM_EL1", "SCXTNUM_EL2", "SCXTNUM_EL12", - isar_feature_aa64_scxtnum }, - - /* TODO: ARMv8.2-SPE -- PMSCR_EL2 */ - /* TODO: ARMv8.4-Trace -- TRFCR_EL2 */ - }; -#undef K - - size_t i; - - for (i = 0; i < ARRAY_SIZE(aliases); i++) { - const struct E2HAlias *a = &aliases[i]; - ARMCPRegInfo *src_reg, *dst_reg, *new_reg; - bool ok; - - if (a->feature && !a->feature(&cpu->isar)) { - continue; - } - - src_reg = g_hash_table_lookup(cpu->cp_regs, - (gpointer)(uintptr_t)a->src_key); - dst_reg = g_hash_table_lookup(cpu->cp_regs, - (gpointer)(uintptr_t)a->dst_key); - g_assert(src_reg != NULL); - g_assert(dst_reg != NULL); - - /* Cross-compare names to detect typos in the keys. */ - g_assert(strcmp(src_reg->name, a->src_name) == 0); - g_assert(strcmp(dst_reg->name, a->dst_name) == 0); - - /* Create alias before redirection so we dup the right data. */ - new_reg = g_memdup(src_reg, sizeof(ARMCPRegInfo)); - - new_reg->name = a->new_name; - new_reg->type |= ARM_CP_ALIAS; - /* Remove PL1/PL0 access, leaving PL2/PL3 R/W in place. */ - new_reg->access &= PL2_RW | PL3_RW; - /* The new_reg op fields are as per new_key, not the target reg */ - new_reg->crn = (a->new_key & CP_REG_ARM64_SYSREG_CRN_MASK) - >> CP_REG_ARM64_SYSREG_CRN_SHIFT; - new_reg->crm = (a->new_key & CP_REG_ARM64_SYSREG_CRM_MASK) - >> CP_REG_ARM64_SYSREG_CRM_SHIFT; - new_reg->opc0 = (a->new_key & CP_REG_ARM64_SYSREG_OP0_MASK) - >> CP_REG_ARM64_SYSREG_OP0_SHIFT; - new_reg->opc1 = (a->new_key & CP_REG_ARM64_SYSREG_OP1_MASK) - >> CP_REG_ARM64_SYSREG_OP1_SHIFT; - new_reg->opc2 = (a->new_key & CP_REG_ARM64_SYSREG_OP2_MASK) - >> CP_REG_ARM64_SYSREG_OP2_SHIFT; - new_reg->vhe_redir_to_el01 = a->src_key; - new_reg->readfn = NULL; - new_reg->writefn = NULL; - new_reg->accessfn = NULL; - new_reg->fieldoffset = 0; - - /* - * If the _EL1 register is redirected to memory by FEAT_NV2, - * then it shares the offset with the _EL12 register, - * and which one is redirected depends on HCR_EL2.NV1. - */ - if (new_reg->nv2_redirect_offset) { - assert(new_reg->nv2_redirect_offset & NV2_REDIR_NV1); - new_reg->nv2_redirect_offset &= ~NV2_REDIR_NV1; - new_reg->nv2_redirect_offset |= NV2_REDIR_NO_NV1; - } - - ok = g_hash_table_insert(cpu->cp_regs, - (gpointer)(uintptr_t)a->new_key, new_reg); - g_assert(ok); - - src_reg->vhe_redir_to_el2 = a->dst_key; - } -} #endif static CPAccessResult ctr_el0_access(CPUARMState *env, const ARMCPRegInfo *ri, @@ -4839,6 +4737,8 @@ static const ARMCPRegInfo zcr_reginfo[] = { { .name = "ZCR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 0, .nv2_redirect_offset = 0x1e0 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 1, 2, 0), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 1, 2, 0), .access = PL1_RW, .type = ARM_CP_SVE, .fieldoffset = offsetof(CPUARMState, vfp.zcr_el[1]), .writefn = zcr_write, .raw_writefn = raw_write }, @@ -4984,6 +4884,8 @@ static const ARMCPRegInfo sme_reginfo[] = { { .name = "SMCR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 6, .nv2_redirect_offset = 0x1f0 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 1, 2, 6), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 1, 2, 6), .access = PL1_RW, .type = ARM_CP_SME, .fieldoffset = offsetof(CPUARMState, vfp.smcr_el[1]), .writefn = smcr_write, .raw_writefn = raw_write }, @@ -5429,6 +5331,8 @@ static const ARMCPRegInfo mte_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 6, .opc2 = 0, .access = PL1_RW, .accessfn = access_tfsr_el1, .nv2_redirect_offset = 0x190 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 5, 6, 0), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 5, 6, 0), .fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[1]) }, { .name = "TFSR_EL2", .state = ARM_CP_STATE_AA64, .type = ARM_CP_NV2_REDIRECT, @@ -5604,6 +5508,8 @@ static const ARMCPRegInfo scxtnum_reginfo[] = { .access = PL1_RW, .accessfn = access_scxtnum_el1, .fgt = FGT_SCXTNUM_EL1, .nv2_redirect_offset = 0x188 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 13, 0, 7), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 13, 0, 7), .fieldoffset = offsetof(CPUARMState, scxtnum_el[1]) }, { .name = "SCXTNUM_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 7, @@ -5948,6 +5854,8 @@ static const ARMCPRegInfo sctlr2_reginfo[] = { .opc0 = 3, .opc1 = 0, .opc2 = 3, .crn = 1, .crm = 0, .access = PL1_RW, .accessfn = sctlr2_el1_access, .writefn = sctlr2_el1_write, .fgt = FGT_SCTLR_EL1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 1, 0, 3), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 1, 0, 3), .nv2_redirect_offset = 0x278 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[1]) }, { .name = "SCTLR2_EL2", .state = ARM_CP_STATE_AA64, @@ -6008,6 +5916,8 @@ static const ARMCPRegInfo tcr2_reginfo[] = { .opc0 = 3, .opc1 = 0, .opc2 = 3, .crn = 2, .crm = 0, .access = PL1_RW, .accessfn = tcr2_el1_access, .writefn = tcr2_el1_write, .fgt = FGT_TCR_EL1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 2, 0, 3), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 2, 0, 3), .nv2_redirect_offset = 0x270 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.tcr2_el[1]) }, { .name = "TCR2_EL2", .state = ARM_CP_STATE_AA64, @@ -7104,6 +7014,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) .accessfn = access_nv1, .fgt = FGT_VBAR_EL1, .nv2_redirect_offset = 0x250 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 12, 0, 0), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 12, 0, 0), .bank_fieldoffsets = { offsetof(CPUARMState, cp15.vbar_s), offsetof(CPUARMState, cp15.vbar_ns) }, .resetvalue = 0 }, @@ -7118,6 +7030,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_SCTLR_EL1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 1, 0, 0), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 1, 0, 0), .nv2_redirect_offset = 0x110 | NV2_REDIR_NV1, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.sctlr_s), offsetof(CPUARMState, cp15.sctlr_ns) }, @@ -7252,16 +7166,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) } define_pm_cpregs(cpu); - -#ifndef CONFIG_USER_ONLY - /* - * Register redirections and aliases must be done last, - * after the registers from the other extensions have been defined. - */ - if (arm_feature(env, ARM_FEATURE_EL2) && cpu_isar_feature(aa64_vh, cpu)) { - define_arm_vh_e2h_redirects_aliases(cpu); - } -#endif } /* @@ -7394,6 +7298,8 @@ static void add_cpreg_to_hashtable_aa32(ARMCPU *cpu, ARMCPRegInfo *r) r->crm, r->opc1, r->opc2); assert(!(r->type & ARM_CP_ADD_TLBI_NXS)); /* aa64 only */ + r->vhe_redir_to_el2 = 0; + r->vhe_redir_to_el01 = 0; switch (r->secure) { case ARM_CP_SECSTATE_NS: @@ -7448,6 +7354,63 @@ static void add_cpreg_to_hashtable_aa64(ARMCPU *cpu, ARMCPRegInfo *r) ARM_CP_SECSTATE_NS, nxs_key); } + if (!r->vhe_redir_to_el01) { + assert(!r->vhe_redir_to_el2); + } else if (!arm_feature(&cpu->env, ARM_FEATURE_EL2) || + !cpu_isar_feature(aa64_vh, cpu)) { + r->vhe_redir_to_el2 = 0; + r->vhe_redir_to_el01 = 0; + } else { + /* Create the FOO_EL12 alias. */ + ARMCPRegInfo *r2 = alloc_cpreg(r, "2"); + uint32_t key2 = r->vhe_redir_to_el01; + + /* + * Clear EL1 redirection on the FOO_EL1 reg; + * Clear EL2 redirection on the FOO_EL12 reg; + * Install redirection from FOO_EL12 back to FOO_EL1. + */ + r->vhe_redir_to_el01 = 0; + r2->vhe_redir_to_el2 = 0; + r2->vhe_redir_to_el01 = key; + + r2->type |= ARM_CP_ALIAS | ARM_CP_NO_RAW; + /* Remove PL1/PL0 access, leaving PL2/PL3 R/W in place. */ + r2->access &= PL2_RW | PL3_RW; + /* The new_reg op fields are as per new_key, not the target reg */ + r2->crn = (key2 & CP_REG_ARM64_SYSREG_CRN_MASK) + >> CP_REG_ARM64_SYSREG_CRN_SHIFT; + r2->crm = (key2 & CP_REG_ARM64_SYSREG_CRM_MASK) + >> CP_REG_ARM64_SYSREG_CRM_SHIFT; + r2->opc0 = (key2 & CP_REG_ARM64_SYSREG_OP0_MASK) + >> CP_REG_ARM64_SYSREG_OP0_SHIFT; + r2->opc1 = (key2 & CP_REG_ARM64_SYSREG_OP1_MASK) + >> CP_REG_ARM64_SYSREG_OP1_SHIFT; + r2->opc2 = (key2 & CP_REG_ARM64_SYSREG_OP2_MASK) + >> CP_REG_ARM64_SYSREG_OP2_SHIFT; + + /* Non-redirected access to this register will abort. */ + r2->readfn = NULL; + r2->writefn = NULL; + r2->raw_readfn = NULL; + r2->raw_writefn = NULL; + r2->accessfn = NULL; + r2->fieldoffset = 0; + + /* + * If the _EL1 register is redirected to memory by FEAT_NV2, + * then it shares the offset with the _EL12 register, + * and which one is redirected depends on HCR_EL2.NV1. + */ + if (r2->nv2_redirect_offset) { + assert(r2->nv2_redirect_offset & NV2_REDIR_NV1); + r2->nv2_redirect_offset &= ~NV2_REDIR_NV1; + r2->nv2_redirect_offset |= NV2_REDIR_NO_NV1; + } + add_cpreg_to_hashtable(cpu, r2, ARM_CP_STATE_AA64, + ARM_CP_SECSTATE_NS, key2); + } + add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA64, ARM_CP_SECSTATE_NS, key); } From ad9c861945f9e82073d86a7542acf6f928b839d8 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 11 Sep 2025 23:09:05 +0200 Subject: [PATCH 0898/1794] tests/functional: add a vfio-user smoke test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a basic test of the vfio-user PCI client implementation. Co-authored-by: John Levon Signed-off-by: Mark Cave-Ayland Signed-off-by: John Levon Reviewed-by: Thomas Huth Link: https://lore.kernel.org/qemu-devel/20250911210905.2070474-1-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + tests/functional/x86_64/meson.build | 1 + .../x86_64/test_vfio_user_client.py | 201 ++++++++++++++++++ 3 files changed, 203 insertions(+) create mode 100755 tests/functional/x86_64/test_vfio_user_client.py diff --git a/MAINTAINERS b/MAINTAINERS index 24b71a4fc54d8..7d134a85e6661 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4318,6 +4318,7 @@ F: docs/system/devices/vfio-user.rst F: hw/vfio-user/* F: include/hw/vfio-user/* F: subprojects/libvfio-user +F: tests/functional/x86_64/test_vfio_user_client.py EBPF: M: Jason Wang diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build index 967426c30c3cf..f78eec5e6cf43 100644 --- a/tests/functional/x86_64/meson.build +++ b/tests/functional/x86_64/meson.build @@ -33,6 +33,7 @@ tests_x86_64_system_thorough = [ 'replay', 'reverse_debug', 'tuxrun', + 'vfio_user_client', 'virtio_balloon', 'virtio_gpu', ] diff --git a/tests/functional/x86_64/test_vfio_user_client.py b/tests/functional/x86_64/test_vfio_user_client.py new file mode 100755 index 0000000000000..8bc16e5e31490 --- /dev/null +++ b/tests/functional/x86_64/test_vfio_user_client.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2025 Nutanix, Inc. +# +# Author: +# Mark Cave-Ayland +# John Levon +# +# SPDX-License-Identifier: GPL-2.0-or-later +""" +Check basic vfio-user-pci client functionality. The test starts two VMs: + + - the server VM runs the libvfio-user "gpio" example server inside it, + piping vfio-user traffic between a local UNIX socket and a virtio-serial + port. On the host, the virtio-serial port is backed by a local socket. + + - the client VM loads the gpio-pci-idio-16 kernel module, with the + vfio-user client connecting to the above local UNIX socket. + +This way, we don't depend on trying to run a vfio-user server on the host +itself. + +Once both VMs are running, we run some basic configuration on the gpio device +and verify that the server is logging the expected out. As this is consistent +given the same VM images, we just do a simple direct comparison. +""" + +import os + +from qemu_test import Asset +from qemu_test import QemuSystemTest +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern + +# Exact output can vary, so we just sample for some expected lines. +EXPECTED_SERVER_LINES = [ + "gpio: adding DMA region [0, 0xc0000) offset=0 flags=0x3", + "gpio: devinfo flags 0x3, num_regions 9, num_irqs 5", + "gpio: region_info[0] offset 0 flags 0 size 0 argsz 32", + "gpio: region_info[1] offset 0 flags 0 size 0 argsz 32", + "gpio: region_info[2] offset 0 flags 0x3 size 256 argsz 32", + "gpio: region_info[3] offset 0 flags 0 size 0 argsz 32", + "gpio: region_info[4] offset 0 flags 0 size 0 argsz 32", + "gpio: region_info[5] offset 0 flags 0 size 0 argsz 32", + "gpio: region_info[7] offset 0 flags 0x3 size 256 argsz 32", + "gpio: region7: read 256 bytes at 0", + "gpio: region7: read 0 from (0x30:4)", + "gpio: cleared EROM", + "gpio: I/O space enabled", + "gpio: memory space enabled", + "gpio: SERR# enabled", + "gpio: region7: wrote 0x103 to (0x4:2)", + "gpio: I/O space enabled", + "gpio: memory space enabled", +] + +class VfioUserClient(QemuSystemTest): + """vfio-user testing class.""" + + ASSET_REPO = 'https://github.com/mcayland-ntx/libvfio-user-test' + + ASSET_KERNEL = Asset( + f'{ASSET_REPO}/raw/refs/heads/main/images/bzImage', + '40292fa6ce95d516e26bccf5974e138d0db65a6de0bc540cabae060fe9dea605' + ) + + ASSET_ROOTFS = Asset( + f'{ASSET_REPO}/raw/refs/heads/main/images/rootfs.ext2', + 'e1e3abae8aebb8e6e77f08b1c531caeacf46250c94c815655c6bbea59fc3d1c1' + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.kernel_path = None + self.rootfs_path = None + + def configure_server_vm_args(self, server_vm, sock_path): + """ + Configuration for the server VM. Set up virtio-serial device backed by + the given socket path. + """ + server_vm.add_args('-kernel', self.kernel_path) + server_vm.add_args('-append', 'console=ttyS0 root=/dev/sda') + server_vm.add_args('-drive', + f"file={self.rootfs_path},if=ide,format=raw,id=drv0") + server_vm.add_args('-snapshot') + server_vm.add_args('-chardev', + f"socket,id=sock0,path={sock_path},telnet=off,server=on,wait=off") + server_vm.add_args('-device', 'virtio-serial') + server_vm.add_args('-device', + 'virtserialport,chardev=sock0,name=org.fedoraproject.port.0') + + def configure_client_vm_args(self, client_vm, sock_path): + """ + Configuration for the client VM. Point the vfio-user-pci device to the + socket path configured above. + """ + + client_vm.add_args('-kernel', self.kernel_path) + client_vm.add_args('-append', 'console=ttyS0 root=/dev/sda') + client_vm.add_args('-drive', + f'file={self.rootfs_path},if=ide,format=raw,id=drv0') + client_vm.add_args('-snapshot') + client_vm.add_args('-device', + '{"driver":"vfio-user-pci",' + + '"socket":{"path": "%s", "type": "unix"}}' % sock_path) + + def setup_vfio_user_pci_server(self, server_vm): + """ + Start the libvfio-user server within the server VM, and arrange + for data to shuttle between its socket and the virtio serial port. + """ + wait_for_console_pattern(self, 'login:', None, server_vm) + exec_command_and_wait_for_pattern(self, 'root', '#', None, server_vm) + + exec_command_and_wait_for_pattern(self, + 'gpio-pci-idio-16 -v /tmp/vfio-user.sock >/var/tmp/gpio.out 2>&1 &', + '#', None, server_vm) + + # wait for libvfio-user socket to appear + while True: + out = exec_command_and_wait_for_pattern(self, + 'ls --color=no /tmp/vfio-user.sock', '#', None, server_vm) + ls_out = out.decode().splitlines()[1].strip() + if ls_out == "/tmp/vfio-user.sock": + break + + exec_command_and_wait_for_pattern(self, + 'socat UNIX-CONNECT:/tmp/vfio-user.sock /dev/vport0p1,ignoreeof ' + + ' &', '#', None, server_vm) + + def test_vfio_user_pci(self): + """Run basic sanity test.""" + + self.set_machine('pc') + self.require_device('virtio-serial') + self.require_device('vfio-user-pci') + + self.kernel_path = self.ASSET_KERNEL.fetch() + self.rootfs_path = self.ASSET_ROOTFS.fetch() + + sock_dir = self.socket_dir() + socket_path = os.path.join(sock_dir.name, 'vfio-user.sock') + + server_vm = self.get_vm(name='server') + server_vm.set_console() + self.configure_server_vm_args(server_vm, socket_path) + + server_vm.launch() + + self.log.debug('starting libvfio-user server') + + self.setup_vfio_user_pci_server(server_vm) + + client_vm = self.get_vm(name="client") + client_vm.set_console() + self.configure_client_vm_args(client_vm, socket_path) + + try: + client_vm.launch() + except: + self.log.error('client VM failed to start, dumping server logs') + exec_command_and_wait_for_pattern(self, 'cat /var/tmp/gpio.out', + '#', None, server_vm) + raise + + self.log.debug('waiting for client VM boot') + + wait_for_console_pattern(self, 'login:', None, client_vm) + exec_command_and_wait_for_pattern(self, 'root', '#', None, client_vm) + + # + # Here, we'd like to actually interact with the gpio device a little + # more as described at: + # + # https://github.com/nutanix/libvfio-user/blob/master/docs/qemu.md + # + # Unfortunately, the buildroot Linux kernel has some undiagnosed issue + # so we don't get /sys/class/gpio. Nonetheless just the basic + # initialization and setup is enough for basic testing of vfio-user. + # + + self.log.debug('collecting libvfio-user server output') + + out = exec_command_and_wait_for_pattern(self, + 'cat /var/tmp/gpio.out', + 'gpio: region2: wrote 0 to (0x1:1)', + None, server_vm) + + gpio_server_out = [s for s in out.decode().splitlines() + if s.startswith("gpio:")] + + for line in EXPECTED_SERVER_LINES: + if line not in gpio_server_out: + self.log.error(f'Missing server debug line: {line}') + self.fail(False) + + +if __name__ == '__main__': + QemuSystemTest.main() From da9211f28e280bc9809b116fc186727a01b0f267 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:09 +0100 Subject: [PATCH 0899/1794] include/hw/vfio/vfio-container.h: rename VFIOContainer to VFIOLegacyContainer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VFIOContainer struct represents the legacy VFIO container even though the name suggests it may be the common superclass of all VFIO containers. Rename it to VFIOLegacyContainer to make this clearer, which is also a better match for its VFIO_IOMMU_LEGACY QOM type name. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-2-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/ppc/spapr_pci_vfio.c | 10 ++++---- hw/vfio/container.c | 42 +++++++++++++++++--------------- hw/vfio/cpr-legacy.c | 27 +++++++++++--------- hw/vfio/spapr.c | 18 +++++++------- include/hw/vfio/vfio-container.h | 8 +++--- include/hw/vfio/vfio-cpr.h | 9 ++++--- 6 files changed, 60 insertions(+), 54 deletions(-) diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c index 7e1c71ef59d5c..faa3ab3fe1ba8 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -32,7 +32,7 @@ * Interfaces for IBM EEH (Enhanced Error Handling) */ #ifdef CONFIG_VFIO_PCI -static bool vfio_eeh_container_ok(VFIOContainer *container) +static bool vfio_eeh_container_ok(VFIOLegacyContainer *container) { /* * As of 2016-03-04 (linux-4.5) the host kernel EEH/VFIO @@ -60,7 +60,7 @@ static bool vfio_eeh_container_ok(VFIOContainer *container) return true; } -static int vfio_eeh_container_op(VFIOContainer *container, uint32_t op) +static int vfio_eeh_container_op(VFIOLegacyContainer *container, uint32_t op) { struct vfio_eeh_pe_op pe_op = { .argsz = sizeof(pe_op), @@ -83,7 +83,7 @@ static int vfio_eeh_container_op(VFIOContainer *container, uint32_t op) return ret; } -static VFIOContainer *vfio_eeh_as_container(AddressSpace *as) +static VFIOLegacyContainer *vfio_eeh_as_container(AddressSpace *as) { VFIOAddressSpace *space = vfio_address_space_get(as); VFIOContainerBase *bcontainer = NULL; @@ -111,14 +111,14 @@ static VFIOContainer *vfio_eeh_as_container(AddressSpace *as) static bool vfio_eeh_as_ok(AddressSpace *as) { - VFIOContainer *container = vfio_eeh_as_container(as); + VFIOLegacyContainer *container = vfio_eeh_as_container(as); return (container != NULL) && vfio_eeh_container_ok(container); } static int vfio_eeh_as_op(AddressSpace *as, uint32_t op) { - VFIOContainer *container = vfio_eeh_as_container(as); + VFIOLegacyContainer *container = vfio_eeh_as_container(as); if (!container) { return -ENODEV; diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 030c6d3f89cf5..bdf415b3d639d 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -44,7 +44,8 @@ typedef QLIST_HEAD(VFIOGroupList, VFIOGroup) VFIOGroupList; static VFIOGroupList vfio_group_list = QLIST_HEAD_INITIALIZER(vfio_group_list); -static int vfio_ram_block_discard_disable(VFIOContainer *container, bool state) +static int vfio_ram_block_discard_disable(VFIOLegacyContainer *container, + bool state) { switch (container->iommu_type) { case VFIO_TYPE1v2_IOMMU: @@ -67,7 +68,7 @@ static int vfio_ram_block_discard_disable(VFIOContainer *container, bool state) } } -static int vfio_dma_unmap_bitmap(const VFIOContainer *container, +static int vfio_dma_unmap_bitmap(const VFIOLegacyContainer *container, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb) { @@ -124,7 +125,7 @@ static int vfio_legacy_dma_unmap_one(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb) { - const VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); + const VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); struct vfio_iommu_type1_dma_unmap unmap = { .argsz = sizeof(unmap), .flags = 0, @@ -212,7 +213,7 @@ static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mr) { - const VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); + const VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); struct vfio_iommu_type1_dma_map map = { .argsz = sizeof(map), .flags = VFIO_DMA_MAP_FLAG_READ, @@ -244,7 +245,7 @@ static int vfio_legacy_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, bool start, Error **errp) { - const VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); + const VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); int ret; struct vfio_iommu_type1_dirty_bitmap dirty = { .argsz = sizeof(dirty), @@ -269,7 +270,7 @@ vfio_legacy_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, static int vfio_legacy_query_dirty_bitmap(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) { - const VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); + const VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); struct vfio_iommu_type1_dirty_bitmap *dbitmap; struct vfio_iommu_type1_dirty_bitmap_get *range; int ret; @@ -413,12 +414,12 @@ static bool vfio_set_iommu(int container_fd, int group_fd, return true; } -static VFIOContainer *vfio_create_container(int fd, VFIOGroup *group, +static VFIOLegacyContainer *vfio_create_container(int fd, VFIOGroup *group, Error **errp) { int iommu_type; const char *vioc_name; - VFIOContainer *container; + VFIOLegacyContainer *container; iommu_type = vfio_get_iommu_type(fd, errp); if (iommu_type < 0) { @@ -442,7 +443,7 @@ static VFIOContainer *vfio_create_container(int fd, VFIOGroup *group, return container; } -static int vfio_get_iommu_info(VFIOContainer *container, +static int vfio_get_iommu_info(VFIOLegacyContainer *container, struct vfio_iommu_type1_info **info) { @@ -486,7 +487,7 @@ vfio_get_iommu_info_cap(struct vfio_iommu_type1_info *info, uint16_t id) return NULL; } -static void vfio_get_iommu_info_migration(VFIOContainer *container, +static void vfio_get_iommu_info_migration(VFIOLegacyContainer *container, struct vfio_iommu_type1_info *info) { struct vfio_info_cap_header *hdr; @@ -514,7 +515,7 @@ static void vfio_get_iommu_info_migration(VFIOContainer *container, static bool vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp) { - VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); + VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); g_autofree struct vfio_iommu_type1_info *info = NULL; int ret; @@ -540,8 +541,8 @@ static bool vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp) return true; } -static bool vfio_container_attach_discard_disable(VFIOContainer *container, - VFIOGroup *group, Error **errp) +static bool vfio_container_attach_discard_disable( + VFIOLegacyContainer *container, VFIOGroup *group, Error **errp) { int ret; @@ -587,8 +588,8 @@ static bool vfio_container_attach_discard_disable(VFIOContainer *container, return !ret; } -static bool vfio_container_group_add(VFIOContainer *container, VFIOGroup *group, - Error **errp) +static bool vfio_container_group_add(VFIOLegacyContainer *container, + VFIOGroup *group, Error **errp) { if (!vfio_container_attach_discard_disable(container, group, errp)) { return false; @@ -604,7 +605,8 @@ static bool vfio_container_group_add(VFIOContainer *container, VFIOGroup *group, return true; } -static void vfio_container_group_del(VFIOContainer *container, VFIOGroup *group) +static void vfio_container_group_del(VFIOLegacyContainer *container, + VFIOGroup *group) { QLIST_REMOVE(group, container_next); group->container = NULL; @@ -616,7 +618,7 @@ static void vfio_container_group_del(VFIOContainer *container, VFIOGroup *group) static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, Error **errp) { - VFIOContainer *container; + VFIOLegacyContainer *container; VFIOContainerBase *bcontainer; int ret, fd = -1; VFIOAddressSpace *space; @@ -729,7 +731,7 @@ static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, static void vfio_container_disconnect(VFIOGroup *group) { - VFIOContainer *container = group->container; + VFIOLegacyContainer *container = group->container; VFIOContainerBase *bcontainer = VFIO_IOMMU(container); VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); @@ -1243,7 +1245,7 @@ hiod_legacy_vfio_get_page_size_mask(HostIOMMUDevice *hiod) static void vfio_iommu_legacy_instance_init(Object *obj) { - VFIOContainer *container = VFIO_IOMMU_LEGACY(obj); + VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(obj); QLIST_INIT(&container->group_list); } @@ -1263,7 +1265,7 @@ static const TypeInfo types[] = { .name = TYPE_VFIO_IOMMU_LEGACY, .parent = TYPE_VFIO_IOMMU, .instance_init = vfio_iommu_legacy_instance_init, - .instance_size = sizeof(VFIOContainer), + .instance_size = sizeof(VFIOLegacyContainer), .class_init = vfio_iommu_legacy_class_init, }, { .name = TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO, diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index 8f437194fae03..12bf920a7de73 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -17,7 +17,8 @@ #include "qapi/error.h" #include "qemu/error-report.h" -static bool vfio_dma_unmap_vaddr_all(VFIOContainer *container, Error **errp) +static bool vfio_dma_unmap_vaddr_all(VFIOLegacyContainer *container, + Error **errp) { struct vfio_iommu_type1_dma_unmap unmap = { .argsz = sizeof(unmap), @@ -41,7 +42,7 @@ static int vfio_legacy_cpr_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mr) { - const VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); + const VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); struct vfio_iommu_type1_dma_map map = { .argsz = sizeof(map), @@ -63,12 +64,13 @@ static int vfio_legacy_cpr_dma_map(const VFIOContainerBase *bcontainer, static void vfio_region_remap(MemoryListener *listener, MemoryRegionSection *section) { - VFIOContainer *container = container_of(listener, VFIOContainer, - cpr.remap_listener); + VFIOLegacyContainer *container = container_of(listener, + VFIOLegacyContainer, + cpr.remap_listener); vfio_container_region_add(VFIO_IOMMU(container), section, true); } -static bool vfio_cpr_supported(VFIOContainer *container, Error **errp) +static bool vfio_cpr_supported(VFIOLegacyContainer *container, Error **errp) { if (!ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UPDATE_VADDR)) { error_setg(errp, "VFIO container does not support VFIO_UPDATE_VADDR"); @@ -85,7 +87,7 @@ static bool vfio_cpr_supported(VFIOContainer *container, Error **errp) static int vfio_container_pre_save(void *opaque) { - VFIOContainer *container = opaque; + VFIOLegacyContainer *container = opaque; Error *local_err = NULL; if (!vfio_dma_unmap_vaddr_all(container, &local_err)) { @@ -97,7 +99,7 @@ static int vfio_container_pre_save(void *opaque) static int vfio_container_post_load(void *opaque, int version_id) { - VFIOContainer *container = opaque; + VFIOLegacyContainer *container = opaque; VFIOContainerBase *bcontainer = VFIO_IOMMU(container); VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); dma_map_fn saved_dma_map = vioc->dma_map; @@ -133,8 +135,8 @@ static const VMStateDescription vfio_container_vmstate = { static int vfio_cpr_fail_notifier(NotifierWithReturn *notifier, MigrationEvent *e, Error **errp) { - VFIOContainer *container = - container_of(notifier, VFIOContainer, cpr.transfer_notifier); + VFIOLegacyContainer *container = + container_of(notifier, VFIOLegacyContainer, cpr.transfer_notifier); VFIOContainerBase *bcontainer = VFIO_IOMMU(container); if (e->type != MIG_EVENT_PRECOPY_FAILED) { @@ -165,7 +167,8 @@ static int vfio_cpr_fail_notifier(NotifierWithReturn *notifier, return 0; } -bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp) +bool vfio_legacy_cpr_register_container(VFIOLegacyContainer *container, + Error **errp) { VFIOContainerBase *bcontainer = VFIO_IOMMU(container); Error **cpr_blocker = &container->cpr.blocker; @@ -189,7 +192,7 @@ bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp) return true; } -void vfio_legacy_cpr_unregister_container(VFIOContainer *container) +void vfio_legacy_cpr_unregister_container(VFIOLegacyContainer *container) { VFIOContainerBase *bcontainer = VFIO_IOMMU(container); @@ -263,7 +266,7 @@ static bool same_device(int fd1, int fd2) return !fstat(fd1, &st1) && !fstat(fd2, &st2) && st1.st_dev == st2.st_dev; } -bool vfio_cpr_container_match(VFIOContainer *container, VFIOGroup *group, +bool vfio_cpr_container_match(VFIOLegacyContainer *container, VFIOGroup *group, int fd) { if (container->fd == fd) { diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index c41e4588d6f2f..b8bade90d7a0f 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -31,7 +31,7 @@ typedef struct VFIOHostDMAWindow { } VFIOHostDMAWindow; typedef struct VFIOSpaprContainer { - VFIOContainer container; + VFIOLegacyContainer container; MemoryListener prereg_listener; QLIST_HEAD(, VFIOHostDMAWindow) hostwin_list; unsigned int levels; @@ -61,7 +61,7 @@ static void vfio_prereg_listener_region_add(MemoryListener *listener, { VFIOSpaprContainer *scontainer = container_of(listener, VFIOSpaprContainer, prereg_listener); - VFIOContainer *container = &scontainer->container; + VFIOLegacyContainer *container = &scontainer->container; VFIOContainerBase *bcontainer = VFIO_IOMMU(container); const hwaddr gpa = section->offset_within_address_space; hwaddr end; @@ -121,7 +121,7 @@ static void vfio_prereg_listener_region_del(MemoryListener *listener, { VFIOSpaprContainer *scontainer = container_of(listener, VFIOSpaprContainer, prereg_listener); - VFIOContainer *container = &scontainer->container; + VFIOLegacyContainer *container = &scontainer->container; const hwaddr gpa = section->offset_within_address_space; hwaddr end; int ret; @@ -218,7 +218,7 @@ static VFIOHostDMAWindow *vfio_find_hostwin(VFIOSpaprContainer *container, return hostwin_found ? hostwin : NULL; } -static int vfio_spapr_remove_window(VFIOContainer *container, +static int vfio_spapr_remove_window(VFIOLegacyContainer *container, hwaddr offset_within_address_space) { struct vfio_iommu_spapr_tce_remove remove = { @@ -239,7 +239,7 @@ static int vfio_spapr_remove_window(VFIOContainer *container, return 0; } -static bool vfio_spapr_create_window(VFIOContainer *container, +static bool vfio_spapr_create_window(VFIOLegacyContainer *container, MemoryRegionSection *section, hwaddr *pgsize, Error **errp) { @@ -352,7 +352,7 @@ vfio_spapr_container_add_section_window(VFIOContainerBase *bcontainer, MemoryRegionSection *section, Error **errp) { - VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); + VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, container); VFIOHostDMAWindow *hostwin; @@ -442,7 +442,7 @@ static void vfio_spapr_container_del_section_window(VFIOContainerBase *bcontainer, MemoryRegionSection *section) { - VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); + VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, container); @@ -463,7 +463,7 @@ vfio_spapr_container_del_section_window(VFIOContainerBase *bcontainer, static void vfio_spapr_container_release(VFIOContainerBase *bcontainer) { - VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); + VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, container); VFIOHostDMAWindow *hostwin, *next; @@ -481,7 +481,7 @@ static void vfio_spapr_container_release(VFIOContainerBase *bcontainer) static bool vfio_spapr_container_setup(VFIOContainerBase *bcontainer, Error **errp) { - VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); + VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, container); struct vfio_iommu_spapr_tce_info info; diff --git a/include/hw/vfio/vfio-container.h b/include/hw/vfio/vfio-container.h index 240f56699336f..712a6914004ae 100644 --- a/include/hw/vfio/vfio-container.h +++ b/include/hw/vfio/vfio-container.h @@ -12,20 +12,20 @@ #include "hw/vfio/vfio-container-base.h" #include "hw/vfio/vfio-cpr.h" -typedef struct VFIOContainer VFIOContainer; +typedef struct VFIOLegacyContainer VFIOLegacyContainer; typedef struct VFIODevice VFIODevice; typedef struct VFIOGroup { int fd; int groupid; - VFIOContainer *container; + VFIOLegacyContainer *container; QLIST_HEAD(, VFIODevice) device_list; QLIST_ENTRY(VFIOGroup) next; QLIST_ENTRY(VFIOGroup) container_next; bool ram_block_discard_allowed; } VFIOGroup; -struct VFIOContainer { +struct VFIOLegacyContainer { VFIOContainerBase parent_obj; int fd; /* /dev/vfio/vfio, empowered by the attached groups */ @@ -34,6 +34,6 @@ struct VFIOContainer { VFIOContainerCPR cpr; }; -OBJECT_DECLARE_SIMPLE_TYPE(VFIOContainer, VFIO_IOMMU_LEGACY); +OBJECT_DECLARE_SIMPLE_TYPE(VFIOLegacyContainer, VFIO_IOMMU_LEGACY); #endif /* HW_VFIO_CONTAINER_H */ diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index d37daffbc5b3d..04e987258719e 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -12,7 +12,7 @@ #include "migration/misc.h" #include "system/memory.h" -struct VFIOContainer; +struct VFIOLegacyContainer; struct VFIOContainerBase; struct VFIOGroup; struct VFIODevice; @@ -42,9 +42,10 @@ typedef struct VFIOPCICPR { NotifierWithReturn transfer_notifier; } VFIOPCICPR; -bool vfio_legacy_cpr_register_container(struct VFIOContainer *container, +bool vfio_legacy_cpr_register_container(struct VFIOLegacyContainer *container, Error **errp); -void vfio_legacy_cpr_unregister_container(struct VFIOContainer *container); +void vfio_legacy_cpr_unregister_container( + struct VFIOLegacyContainer *container); int vfio_cpr_reboot_notifier(NotifierWithReturn *notifier, MigrationEvent *e, Error **errp); @@ -61,7 +62,7 @@ void vfio_cpr_load_device(struct VFIODevice *vbasedev); int vfio_cpr_group_get_device_fd(int d, const char *name); -bool vfio_cpr_container_match(struct VFIOContainer *container, +bool vfio_cpr_container_match(struct VFIOLegacyContainer *container, struct VFIOGroup *group, int fd); void vfio_cpr_giommu_remap(struct VFIOContainerBase *bcontainer, From e2e269d580947fe9b1b5735c8cb659277ac67996 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:10 +0100 Subject: [PATCH 0900/1794] include/hw/vfio/vfio-container-base.h: rename VFIOContainerBase to VFIOContainer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the VFIOContainer struct name is available, rename VFIOContainerBase to VFIOContainer to better indicate that it is the superclass of other VFIOFooContainer structs. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-3-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/ppc/spapr_pci_vfio.c | 2 +- hw/vfio-user/container.c | 18 +++---- hw/vfio-user/container.h | 2 +- hw/vfio/container-base.c | 41 ++++++++------- hw/vfio/container.c | 22 ++++---- hw/vfio/cpr-iommufd.c | 4 +- hw/vfio/cpr-legacy.c | 14 ++--- hw/vfio/device.c | 2 +- hw/vfio/iommufd.c | 18 +++---- hw/vfio/listener.c | 74 +++++++++++++------------- hw/vfio/spapr.c | 12 ++--- hw/vfio/vfio-iommufd.h | 2 +- hw/vfio/vfio-listener.h | 4 +- include/hw/vfio/vfio-container-base.h | 76 +++++++++++++-------------- include/hw/vfio/vfio-container.h | 2 +- include/hw/vfio/vfio-cpr.h | 8 +-- include/hw/vfio/vfio-device.h | 4 +- 17 files changed, 154 insertions(+), 151 deletions(-) diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c index faa3ab3fe1ba8..c8175dd8a89b1 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -86,7 +86,7 @@ static int vfio_eeh_container_op(VFIOLegacyContainer *container, uint32_t op) static VFIOLegacyContainer *vfio_eeh_as_container(AddressSpace *as) { VFIOAddressSpace *space = vfio_address_space_get(as); - VFIOContainerBase *bcontainer = NULL; + VFIOContainer *bcontainer = NULL; if (QLIST_EMPTY(&space->containers)) { /* No containers to act on */ diff --git a/hw/vfio-user/container.c b/hw/vfio-user/container.c index 3cdbd44c1aac2..411eb7b28b72a 100644 --- a/hw/vfio-user/container.c +++ b/hw/vfio-user/container.c @@ -22,14 +22,14 @@ * will fire during memory update transactions. These depend on BQL being held, * so do any resulting map/demap ops async while keeping BQL. */ -static void vfio_user_listener_begin(VFIOContainerBase *bcontainer) +static void vfio_user_listener_begin(VFIOContainer *bcontainer) { VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); container->proxy->async_ops = true; } -static void vfio_user_listener_commit(VFIOContainerBase *bcontainer) +static void vfio_user_listener_commit(VFIOContainer *bcontainer) { VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); @@ -38,7 +38,7 @@ static void vfio_user_listener_commit(VFIOContainerBase *bcontainer) vfio_user_wait_reqs(container->proxy); } -static int vfio_user_dma_unmap(const VFIOContainerBase *bcontainer, +static int vfio_user_dma_unmap(const VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb, bool unmap_all) { @@ -80,7 +80,7 @@ static int vfio_user_dma_unmap(const VFIOContainerBase *bcontainer, return ret; } -static int vfio_user_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, +static int vfio_user_dma_map(const VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mrp) { @@ -154,14 +154,14 @@ static int vfio_user_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, } static int -vfio_user_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, +vfio_user_set_dirty_page_tracking(const VFIOContainer *bcontainer, bool start, Error **errp) { error_setg_errno(errp, ENOTSUP, "Not supported"); return -ENOTSUP; } -static int vfio_user_query_dirty_bitmap(const VFIOContainerBase *bcontainer, +static int vfio_user_query_dirty_bitmap(const VFIOContainer *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) { @@ -169,7 +169,7 @@ static int vfio_user_query_dirty_bitmap(const VFIOContainerBase *bcontainer, return -ENOTSUP; } -static bool vfio_user_setup(VFIOContainerBase *bcontainer, Error **errp) +static bool vfio_user_setup(VFIOContainer *bcontainer, Error **errp) { VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); @@ -202,7 +202,7 @@ static VFIOUserContainer * vfio_user_container_connect(AddressSpace *as, VFIODevice *vbasedev, Error **errp) { - VFIOContainerBase *bcontainer; + VFIOContainer *bcontainer; VFIOUserContainer *container; VFIOAddressSpace *space; VFIOIOMMUClass *vioc; @@ -260,7 +260,7 @@ vfio_user_container_connect(AddressSpace *as, VFIODevice *vbasedev, static void vfio_user_container_disconnect(VFIOUserContainer *container) { - VFIOContainerBase *bcontainer = VFIO_IOMMU(container); + VFIOContainer *bcontainer = VFIO_IOMMU(container); VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); VFIOAddressSpace *space = bcontainer->space; diff --git a/hw/vfio-user/container.h b/hw/vfio-user/container.h index 96aa6785d9b39..241863ef976f5 100644 --- a/hw/vfio-user/container.h +++ b/hw/vfio-user/container.h @@ -14,7 +14,7 @@ /* MMU container sub-class for vfio-user. */ struct VFIOUserContainer { - VFIOContainerBase parent_obj; + VFIOContainer parent_obj; VFIOUserProxy *proxy; }; diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 56304978e1e8e..98c5198e5043b 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -67,13 +67,13 @@ void vfio_address_space_put(VFIOAddressSpace *space) } void vfio_address_space_insert(VFIOAddressSpace *space, - VFIOContainerBase *bcontainer) + VFIOContainer *bcontainer) { QLIST_INSERT_HEAD(&space->containers, bcontainer, next); bcontainer->space = space; } -int vfio_container_dma_map(VFIOContainerBase *bcontainer, +int vfio_container_dma_map(VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mr) { @@ -92,7 +92,7 @@ int vfio_container_dma_map(VFIOContainerBase *bcontainer, return vioc->dma_map(bcontainer, iova, size, vaddr, readonly, mr); } -int vfio_container_dma_unmap(VFIOContainerBase *bcontainer, +int vfio_container_dma_unmap(VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb, bool unmap_all) { @@ -102,7 +102,7 @@ int vfio_container_dma_unmap(VFIOContainerBase *bcontainer, return vioc->dma_unmap(bcontainer, iova, size, iotlb, unmap_all); } -bool vfio_container_add_section_window(VFIOContainerBase *bcontainer, +bool vfio_container_add_section_window(VFIOContainer *bcontainer, MemoryRegionSection *section, Error **errp) { @@ -115,7 +115,7 @@ bool vfio_container_add_section_window(VFIOContainerBase *bcontainer, return vioc->add_window(bcontainer, section, errp); } -void vfio_container_del_section_window(VFIOContainerBase *bcontainer, +void vfio_container_del_section_window(VFIOContainer *bcontainer, MemoryRegionSection *section) { VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); @@ -127,7 +127,7 @@ void vfio_container_del_section_window(VFIOContainerBase *bcontainer, return vioc->del_window(bcontainer, section); } -int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, +int vfio_container_set_dirty_page_tracking(VFIOContainer *bcontainer, bool start, Error **errp) { VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); @@ -151,7 +151,7 @@ int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, } static bool vfio_container_devices_dirty_tracking_is_started( - const VFIOContainerBase *bcontainer) + const VFIOContainer *bcontainer) { VFIODevice *vbasedev; @@ -165,14 +165,14 @@ static bool vfio_container_devices_dirty_tracking_is_started( } bool vfio_container_dirty_tracking_is_started( - const VFIOContainerBase *bcontainer) + const VFIOContainer *bcontainer) { return vfio_container_devices_dirty_tracking_is_started(bcontainer) || bcontainer->dirty_pages_started; } bool vfio_container_devices_dirty_tracking_is_supported( - const VFIOContainerBase *bcontainer) + const VFIOContainer *bcontainer) { VFIODevice *vbasedev; @@ -210,8 +210,9 @@ static int vfio_device_dma_logging_report(VFIODevice *vbasedev, hwaddr iova, return vbasedev->io_ops->device_feature(vbasedev, feature); } -static int vfio_container_iommu_query_dirty_bitmap(const VFIOContainerBase *bcontainer, - VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) +static int vfio_container_iommu_query_dirty_bitmap( + const VFIOContainer *bcontainer, VFIOBitmap *vbmap, hwaddr iova, + hwaddr size, Error **errp) { VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); @@ -220,8 +221,9 @@ static int vfio_container_iommu_query_dirty_bitmap(const VFIOContainerBase *bcon errp); } -static int vfio_container_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, - VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) +static int vfio_container_devices_query_dirty_bitmap( + const VFIOContainer *bcontainer, VFIOBitmap *vbmap, hwaddr iova, + hwaddr size, Error **errp) { VFIODevice *vbasedev; int ret; @@ -242,8 +244,9 @@ static int vfio_container_devices_query_dirty_bitmap(const VFIOContainerBase *bc return 0; } -int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, - uint64_t size, ram_addr_t ram_addr, Error **errp) +int vfio_container_query_dirty_bitmap(const VFIOContainer *bcontainer, + uint64_t iova, uint64_t size, + ram_addr_t ram_addr, Error **errp) { bool all_device_dirty_tracking = vfio_container_devices_dirty_tracking_is_supported(bcontainer); @@ -297,7 +300,7 @@ static gpointer copy_iova_range(gconstpointer src, gpointer data) return dest; } -GList *vfio_container_get_iova_ranges(const VFIOContainerBase *bcontainer) +GList *vfio_container_get_iova_ranges(const VFIOContainer *bcontainer) { assert(bcontainer); return g_list_copy_deep(bcontainer->iova_ranges, copy_iova_range, NULL); @@ -305,7 +308,7 @@ GList *vfio_container_get_iova_ranges(const VFIOContainerBase *bcontainer) static void vfio_container_instance_finalize(Object *obj) { - VFIOContainerBase *bcontainer = VFIO_IOMMU(obj); + VFIOContainer *bcontainer = VFIO_IOMMU(obj); VFIOGuestIOMMU *giommu, *tmp; QLIST_SAFE_REMOVE(bcontainer, next); @@ -322,7 +325,7 @@ static void vfio_container_instance_finalize(Object *obj) static void vfio_container_instance_init(Object *obj) { - VFIOContainerBase *bcontainer = VFIO_IOMMU(obj); + VFIOContainer *bcontainer = VFIO_IOMMU(obj); bcontainer->error = NULL; bcontainer->dirty_pages_supported = false; @@ -338,7 +341,7 @@ static const TypeInfo types[] = { .parent = TYPE_OBJECT, .instance_init = vfio_container_instance_init, .instance_finalize = vfio_container_instance_finalize, - .instance_size = sizeof(VFIOContainerBase), + .instance_size = sizeof(VFIOContainer), .class_size = sizeof(VFIOIOMMUClass), .abstract = true, }, diff --git a/hw/vfio/container.c b/hw/vfio/container.c index bdf415b3d639d..dc8425efb1f68 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -72,7 +72,7 @@ static int vfio_dma_unmap_bitmap(const VFIOLegacyContainer *container, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb) { - const VFIOContainerBase *bcontainer = VFIO_IOMMU(container); + const VFIOContainer *bcontainer = VFIO_IOMMU(container); struct vfio_iommu_type1_dma_unmap *unmap; struct vfio_bitmap *bitmap; VFIOBitmap vbmap; @@ -121,7 +121,7 @@ static int vfio_dma_unmap_bitmap(const VFIOLegacyContainer *container, return ret; } -static int vfio_legacy_dma_unmap_one(const VFIOContainerBase *bcontainer, +static int vfio_legacy_dma_unmap_one(const VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb) { @@ -184,7 +184,7 @@ static int vfio_legacy_dma_unmap_one(const VFIOContainerBase *bcontainer, /* * DMA - Mapping and unmapping for the "type1" IOMMU interface used on x86 */ -static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer, +static int vfio_legacy_dma_unmap(const VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb, bool unmap_all) { @@ -209,7 +209,7 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer, return ret; } -static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, +static int vfio_legacy_dma_map(const VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mr) { @@ -242,7 +242,7 @@ static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, } static int -vfio_legacy_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, +vfio_legacy_set_dirty_page_tracking(const VFIOContainer *bcontainer, bool start, Error **errp) { const VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); @@ -267,7 +267,7 @@ vfio_legacy_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, return ret; } -static int vfio_legacy_query_dirty_bitmap(const VFIOContainerBase *bcontainer, +static int vfio_legacy_query_dirty_bitmap(const VFIOContainer *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) { const VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); @@ -307,7 +307,7 @@ static int vfio_legacy_query_dirty_bitmap(const VFIOContainerBase *bcontainer, } static bool vfio_get_info_iova_range(struct vfio_iommu_type1_info *info, - VFIOContainerBase *bcontainer) + VFIOContainer *bcontainer) { struct vfio_info_cap_header *hdr; struct vfio_iommu_type1_info_cap_iova_range *cap; @@ -492,7 +492,7 @@ static void vfio_get_iommu_info_migration(VFIOLegacyContainer *container, { struct vfio_info_cap_header *hdr; struct vfio_iommu_type1_info_cap_migration *cap_mig; - VFIOContainerBase *bcontainer = VFIO_IOMMU(container); + VFIOContainer *bcontainer = VFIO_IOMMU(container); hdr = vfio_get_iommu_info_cap(info, VFIO_IOMMU_TYPE1_INFO_CAP_MIGRATION); if (!hdr) { @@ -513,7 +513,7 @@ static void vfio_get_iommu_info_migration(VFIOLegacyContainer *container, } } -static bool vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp) +static bool vfio_legacy_setup(VFIOContainer *bcontainer, Error **errp) { VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); g_autofree struct vfio_iommu_type1_info *info = NULL; @@ -619,7 +619,7 @@ static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, Error **errp) { VFIOLegacyContainer *container; - VFIOContainerBase *bcontainer; + VFIOContainer *bcontainer; int ret, fd = -1; VFIOAddressSpace *space; VFIOIOMMUClass *vioc = NULL; @@ -732,7 +732,7 @@ static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, static void vfio_container_disconnect(VFIOGroup *group) { VFIOLegacyContainer *container = group->container; - VFIOContainerBase *bcontainer = VFIO_IOMMU(container); + VFIOContainer *bcontainer = VFIO_IOMMU(container); VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); QLIST_REMOVE(group, container_next); diff --git a/hw/vfio/cpr-iommufd.c b/hw/vfio/cpr-iommufd.c index 148a06d552ff3..6aaf6f77a25a3 100644 --- a/hw/vfio/cpr-iommufd.c +++ b/hw/vfio/cpr-iommufd.c @@ -176,7 +176,7 @@ void vfio_iommufd_cpr_unregister_iommufd(IOMMUFDBackend *be) bool vfio_iommufd_cpr_register_container(VFIOIOMMUFDContainer *container, Error **errp) { - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainer *bcontainer = &container->bcontainer; migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier, vfio_cpr_reboot_notifier, @@ -189,7 +189,7 @@ bool vfio_iommufd_cpr_register_container(VFIOIOMMUFDContainer *container, void vfio_iommufd_cpr_unregister_container(VFIOIOMMUFDContainer *container) { - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainer *bcontainer = &container->bcontainer; migration_remove_notifier(&bcontainer->cpr_reboot_notifier); } diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index 12bf920a7de73..bd3f6fc3d3936 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -38,7 +38,7 @@ static bool vfio_dma_unmap_vaddr_all(VFIOLegacyContainer *container, * Set the new @vaddr for any mappings registered during cpr load. * The incoming state is cleared thereafter. */ -static int vfio_legacy_cpr_dma_map(const VFIOContainerBase *bcontainer, +static int vfio_legacy_cpr_dma_map(const VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mr) { @@ -100,7 +100,7 @@ static int vfio_container_pre_save(void *opaque) static int vfio_container_post_load(void *opaque, int version_id) { VFIOLegacyContainer *container = opaque; - VFIOContainerBase *bcontainer = VFIO_IOMMU(container); + VFIOContainer *bcontainer = VFIO_IOMMU(container); VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); dma_map_fn saved_dma_map = vioc->dma_map; Error *local_err = NULL; @@ -137,7 +137,7 @@ static int vfio_cpr_fail_notifier(NotifierWithReturn *notifier, { VFIOLegacyContainer *container = container_of(notifier, VFIOLegacyContainer, cpr.transfer_notifier); - VFIOContainerBase *bcontainer = VFIO_IOMMU(container); + VFIOContainer *bcontainer = VFIO_IOMMU(container); if (e->type != MIG_EVENT_PRECOPY_FAILED) { return 0; @@ -170,7 +170,7 @@ static int vfio_cpr_fail_notifier(NotifierWithReturn *notifier, bool vfio_legacy_cpr_register_container(VFIOLegacyContainer *container, Error **errp) { - VFIOContainerBase *bcontainer = VFIO_IOMMU(container); + VFIOContainer *bcontainer = VFIO_IOMMU(container); Error **cpr_blocker = &container->cpr.blocker; migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier, @@ -194,7 +194,7 @@ bool vfio_legacy_cpr_register_container(VFIOLegacyContainer *container, void vfio_legacy_cpr_unregister_container(VFIOLegacyContainer *container) { - VFIOContainerBase *bcontainer = VFIO_IOMMU(container); + VFIOContainer *bcontainer = VFIO_IOMMU(container); migration_remove_notifier(&bcontainer->cpr_reboot_notifier); migrate_del_blocker(&container->cpr.blocker); @@ -210,7 +210,7 @@ void vfio_legacy_cpr_unregister_container(VFIOLegacyContainer *container) * The giommu already exists. Find it and replay it, which calls * vfio_legacy_cpr_dma_map further down the stack. */ -void vfio_cpr_giommu_remap(VFIOContainerBase *bcontainer, +void vfio_cpr_giommu_remap(VFIOContainer *bcontainer, MemoryRegionSection *section) { VFIOGuestIOMMU *giommu = NULL; @@ -235,7 +235,7 @@ void vfio_cpr_giommu_remap(VFIOContainerBase *bcontainer, * The ram discard listener already exists. Call its populate function * directly, which calls vfio_legacy_cpr_dma_map. */ -bool vfio_cpr_ram_discard_register_listener(VFIOContainerBase *bcontainer, +bool vfio_cpr_ram_discard_register_listener(VFIOContainer *bcontainer, MemoryRegionSection *section) { VFIORamDiscardListener *vrdl = diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 08f12ac31f44f..963cefc053d09 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -460,7 +460,7 @@ void vfio_device_detach(VFIODevice *vbasedev) VFIO_IOMMU_GET_CLASS(vbasedev->bcontainer)->detach_device(vbasedev); } -void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainerBase *bcontainer, +void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainer *bcontainer, struct vfio_device_info *info) { int i; diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 8c27222f754a2..7f2243d9d197d 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -34,7 +34,7 @@ #define TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO \ TYPE_HOST_IOMMU_DEVICE_IOMMUFD "-vfio" -static int iommufd_cdev_map(const VFIOContainerBase *bcontainer, hwaddr iova, +static int iommufd_cdev_map(const VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mr) { @@ -46,7 +46,7 @@ static int iommufd_cdev_map(const VFIOContainerBase *bcontainer, hwaddr iova, iova, size, vaddr, readonly); } -static int iommufd_cdev_map_file(const VFIOContainerBase *bcontainer, +static int iommufd_cdev_map_file(const VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, int fd, unsigned long start, bool readonly) { @@ -58,7 +58,7 @@ static int iommufd_cdev_map_file(const VFIOContainerBase *bcontainer, iova, size, fd, start, readonly); } -static int iommufd_cdev_unmap(const VFIOContainerBase *bcontainer, +static int iommufd_cdev_unmap(const VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb, bool unmap_all) { @@ -159,7 +159,7 @@ static bool iommufd_hwpt_dirty_tracking(VFIOIOASHwpt *hwpt) return hwpt && hwpt->hwpt_flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING; } -static int iommufd_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, +static int iommufd_set_dirty_page_tracking(const VFIOContainer *bcontainer, bool start, Error **errp) { const VFIOIOMMUFDContainer *container = @@ -190,7 +190,7 @@ static int iommufd_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, return -EINVAL; } -static int iommufd_query_dirty_bitmap(const VFIOContainerBase *bcontainer, +static int iommufd_query_dirty_bitmap(const VFIOContainer *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) { @@ -464,7 +464,7 @@ static void iommufd_cdev_detach_container(VFIODevice *vbasedev, static void iommufd_cdev_container_destroy(VFIOIOMMUFDContainer *container) { - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainer *bcontainer = &container->bcontainer; if (!QLIST_EMPTY(&bcontainer->device_list)) { return; @@ -486,7 +486,7 @@ static int iommufd_cdev_ram_block_discard_disable(bool state) static bool iommufd_cdev_get_info_iova_range(VFIOIOMMUFDContainer *container, uint32_t ioas_id, Error **errp) { - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainer *bcontainer = &container->bcontainer; g_autofree struct iommu_ioas_iova_ranges *info = NULL; struct iommu_iova_range *iova_ranges; int sz, fd = container->be->fd; @@ -528,7 +528,7 @@ static bool iommufd_cdev_get_info_iova_range(VFIOIOMMUFDContainer *container, static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, AddressSpace *as, Error **errp) { - VFIOContainerBase *bcontainer; + VFIOContainer *bcontainer; VFIOIOMMUFDContainer *container; VFIOAddressSpace *space; struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) }; @@ -687,7 +687,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, static void iommufd_cdev_detach(VFIODevice *vbasedev) { - VFIOContainerBase *bcontainer = vbasedev->bcontainer; + VFIOContainer *bcontainer = vbasedev->bcontainer; VFIOAddressSpace *space = bcontainer->space; VFIOIOMMUFDContainer *container = container_of(bcontainer, VFIOIOMMUFDContainer, diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index e093833165983..3b6f17f0c3aa7 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -52,7 +52,7 @@ */ -static bool vfio_log_sync_needed(const VFIOContainerBase *bcontainer) +static bool vfio_log_sync_needed(const VFIOContainer *bcontainer) { VFIODevice *vbasedev; @@ -125,7 +125,7 @@ static MemoryRegion *vfio_translate_iotlb(IOMMUTLBEntry *iotlb, hwaddr *xlat_p, static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) { VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n); - VFIOContainerBase *bcontainer = giommu->bcontainer; + VFIOContainer *bcontainer = giommu->bcontainer; hwaddr iova = iotlb->iova + giommu->iommu_offset; MemoryRegion *mr; hwaddr xlat; @@ -202,7 +202,7 @@ static void vfio_ram_discard_notify_discard(RamDiscardListener *rdl, { VFIORamDiscardListener *vrdl = container_of(rdl, VFIORamDiscardListener, listener); - VFIOContainerBase *bcontainer = vrdl->bcontainer; + VFIOContainer *bcontainer = vrdl->bcontainer; const hwaddr size = int128_get64(section->size); const hwaddr iova = section->offset_within_address_space; int ret; @@ -220,7 +220,7 @@ static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl, { VFIORamDiscardListener *vrdl = container_of(rdl, VFIORamDiscardListener, listener); - VFIOContainerBase *bcontainer = vrdl->bcontainer; + VFIOContainer *bcontainer = vrdl->bcontainer; const hwaddr end = section->offset_within_region + int128_get64(section->size); hwaddr start, next, iova; @@ -250,7 +250,7 @@ static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl, return 0; } -static bool vfio_ram_discard_register_listener(VFIOContainerBase *bcontainer, +static bool vfio_ram_discard_register_listener(VFIOContainer *bcontainer, MemoryRegionSection *section, Error **errp) { @@ -328,7 +328,7 @@ static bool vfio_ram_discard_register_listener(VFIOContainerBase *bcontainer, return true; } -static void vfio_ram_discard_unregister_listener(VFIOContainerBase *bcontainer, +static void vfio_ram_discard_unregister_listener(VFIOContainer *bcontainer, MemoryRegionSection *section) { RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); @@ -396,7 +396,7 @@ static bool vfio_listener_valid_section(MemoryRegionSection *section, return true; } -static bool vfio_get_section_iova_range(VFIOContainerBase *bcontainer, +static bool vfio_get_section_iova_range(VFIOContainer *bcontainer, MemoryRegionSection *section, hwaddr *out_iova, hwaddr *out_end, Int128 *out_llend) @@ -423,9 +423,9 @@ static bool vfio_get_section_iova_range(VFIOContainerBase *bcontainer, static void vfio_listener_begin(MemoryListener *listener) { - VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, - listener); - void (*listener_begin)(VFIOContainerBase *bcontainer); + VFIOContainer *bcontainer = container_of(listener, VFIOContainer, + listener); + void (*listener_begin)(VFIOContainer *bcontainer); listener_begin = VFIO_IOMMU_GET_CLASS(bcontainer)->listener_begin; @@ -436,9 +436,9 @@ static void vfio_listener_begin(MemoryListener *listener) static void vfio_listener_commit(MemoryListener *listener) { - VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, - listener); - void (*listener_commit)(VFIOContainerBase *bcontainer); + VFIOContainer *bcontainer = container_of(listener, VFIOContainer, + listener); + void (*listener_commit)(VFIOContainer *bcontainer); listener_commit = VFIO_IOMMU_GET_CLASS(bcontainer)->listener_commit; @@ -460,7 +460,7 @@ static void vfio_device_error_append(VFIODevice *vbasedev, Error **errp) } VFIORamDiscardListener *vfio_find_ram_discard_listener( - VFIOContainerBase *bcontainer, MemoryRegionSection *section) + VFIOContainer *bcontainer, MemoryRegionSection *section) { VFIORamDiscardListener *vrdl = NULL; @@ -482,12 +482,12 @@ VFIORamDiscardListener *vfio_find_ram_discard_listener( static void vfio_listener_region_add(MemoryListener *listener, MemoryRegionSection *section) { - VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, - listener); + VFIOContainer *bcontainer = container_of(listener, VFIOContainer, + listener); vfio_container_region_add(bcontainer, section, false); } -void vfio_container_region_add(VFIOContainerBase *bcontainer, +void vfio_container_region_add(VFIOContainer *bcontainer, MemoryRegionSection *section, bool cpr_remap) { @@ -656,8 +656,8 @@ void vfio_container_region_add(VFIOContainerBase *bcontainer, static void vfio_listener_region_del(MemoryListener *listener, MemoryRegionSection *section) { - VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, - listener); + VFIOContainer *bcontainer = container_of(listener, VFIOContainer, + listener); hwaddr iova, end; Int128 llend, llsize; int ret; @@ -744,13 +744,13 @@ typedef struct VFIODirtyRanges { } VFIODirtyRanges; typedef struct VFIODirtyRangesListener { - VFIOContainerBase *bcontainer; + VFIOContainer *bcontainer; VFIODirtyRanges ranges; MemoryListener listener; } VFIODirtyRangesListener; static bool vfio_section_is_vfio_pci(MemoryRegionSection *section, - VFIOContainerBase *bcontainer) + VFIOContainer *bcontainer) { VFIOPCIDevice *pcidev; VFIODevice *vbasedev; @@ -835,7 +835,7 @@ static const MemoryListener vfio_dirty_tracking_listener = { .region_add = vfio_dirty_tracking_update, }; -static void vfio_dirty_tracking_init(VFIOContainerBase *bcontainer, +static void vfio_dirty_tracking_init(VFIOContainer *bcontainer, VFIODirtyRanges *ranges) { VFIODirtyRangesListener dirty; @@ -860,7 +860,7 @@ static void vfio_dirty_tracking_init(VFIOContainerBase *bcontainer, memory_listener_unregister(&dirty.listener); } -static void vfio_devices_dma_logging_stop(VFIOContainerBase *bcontainer) +static void vfio_devices_dma_logging_stop(VFIOContainer *bcontainer) { uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature), sizeof(uint64_t))] = {}; @@ -889,7 +889,7 @@ static void vfio_devices_dma_logging_stop(VFIOContainerBase *bcontainer) } static struct vfio_device_feature * -vfio_device_feature_dma_logging_start_create(VFIOContainerBase *bcontainer, +vfio_device_feature_dma_logging_start_create(VFIOContainer *bcontainer, VFIODirtyRanges *tracking) { struct vfio_device_feature *feature; @@ -962,7 +962,7 @@ static void vfio_device_feature_dma_logging_start_destroy( g_free(feature); } -static bool vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer, +static bool vfio_devices_dma_logging_start(VFIOContainer *bcontainer, Error **errp) { struct vfio_device_feature *feature; @@ -1006,8 +1006,8 @@ static bool vfio_listener_log_global_start(MemoryListener *listener, Error **errp) { ERRP_GUARD(); - VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, - listener); + VFIOContainer *bcontainer = container_of(listener, VFIOContainer, + listener); bool ret; if (vfio_container_devices_dirty_tracking_is_supported(bcontainer)) { @@ -1024,8 +1024,8 @@ static bool vfio_listener_log_global_start(MemoryListener *listener, static void vfio_listener_log_global_stop(MemoryListener *listener) { - VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, - listener); + VFIOContainer *bcontainer = container_of(listener, VFIOContainer, + listener); Error *local_err = NULL; int ret = 0; @@ -1057,7 +1057,7 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) vfio_giommu_dirty_notifier *gdn = container_of(n, vfio_giommu_dirty_notifier, n); VFIOGuestIOMMU *giommu = gdn->giommu; - VFIOContainerBase *bcontainer = giommu->bcontainer; + VFIOContainer *bcontainer = giommu->bcontainer; hwaddr iova = iotlb->iova + giommu->iommu_offset; ram_addr_t translated_addr; Error *local_err = NULL; @@ -1127,7 +1127,7 @@ static int vfio_ram_discard_query_dirty_bitmap(MemoryRegionSection *section, } static int -vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainerBase *bcontainer, +vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainer *bcontainer, MemoryRegionSection *section) { RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); @@ -1143,7 +1143,7 @@ vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainerBase *bcontainer, &vrdl); } -static int vfio_sync_iommu_dirty_bitmap(VFIOContainerBase *bcontainer, +static int vfio_sync_iommu_dirty_bitmap(VFIOContainer *bcontainer, MemoryRegionSection *section) { VFIOGuestIOMMU *giommu; @@ -1180,7 +1180,7 @@ static int vfio_sync_iommu_dirty_bitmap(VFIOContainerBase *bcontainer, return 0; } -static int vfio_sync_dirty_bitmap(VFIOContainerBase *bcontainer, +static int vfio_sync_dirty_bitmap(VFIOContainer *bcontainer, MemoryRegionSection *section, Error **errp) { ram_addr_t ram_addr; @@ -1209,8 +1209,8 @@ static int vfio_sync_dirty_bitmap(VFIOContainerBase *bcontainer, static void vfio_listener_log_sync(MemoryListener *listener, MemoryRegionSection *section) { - VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, - listener); + VFIOContainer *bcontainer = container_of(listener, VFIOContainer, + listener); int ret; Error *local_err = NULL; @@ -1241,7 +1241,7 @@ static const MemoryListener vfio_memory_listener = { .log_sync = vfio_listener_log_sync, }; -bool vfio_listener_register(VFIOContainerBase *bcontainer, Error **errp) +bool vfio_listener_register(VFIOContainer *bcontainer, Error **errp) { bcontainer->listener = vfio_memory_listener; memory_listener_register(&bcontainer->listener, bcontainer->space->as); @@ -1255,7 +1255,7 @@ bool vfio_listener_register(VFIOContainerBase *bcontainer, Error **errp) return true; } -void vfio_listener_unregister(VFIOContainerBase *bcontainer) +void vfio_listener_unregister(VFIOContainer *bcontainer) { memory_listener_unregister(&bcontainer->listener); } diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index b8bade90d7a0f..6d462aa13caec 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -62,7 +62,7 @@ static void vfio_prereg_listener_region_add(MemoryListener *listener, VFIOSpaprContainer *scontainer = container_of(listener, VFIOSpaprContainer, prereg_listener); VFIOLegacyContainer *container = &scontainer->container; - VFIOContainerBase *bcontainer = VFIO_IOMMU(container); + VFIOContainer *bcontainer = VFIO_IOMMU(container); const hwaddr gpa = section->offset_within_address_space; hwaddr end; int ret; @@ -244,7 +244,7 @@ static bool vfio_spapr_create_window(VFIOLegacyContainer *container, hwaddr *pgsize, Error **errp) { int ret = 0; - VFIOContainerBase *bcontainer = VFIO_IOMMU(container); + VFIOContainer *bcontainer = VFIO_IOMMU(container); VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, container); IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); @@ -348,7 +348,7 @@ static bool vfio_spapr_create_window(VFIOLegacyContainer *container, } static bool -vfio_spapr_container_add_section_window(VFIOContainerBase *bcontainer, +vfio_spapr_container_add_section_window(VFIOContainer *bcontainer, MemoryRegionSection *section, Error **errp) { @@ -439,7 +439,7 @@ vfio_spapr_container_add_section_window(VFIOContainerBase *bcontainer, } static void -vfio_spapr_container_del_section_window(VFIOContainerBase *bcontainer, +vfio_spapr_container_del_section_window(VFIOContainer *bcontainer, MemoryRegionSection *section) { VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); @@ -461,7 +461,7 @@ vfio_spapr_container_del_section_window(VFIOContainerBase *bcontainer, } } -static void vfio_spapr_container_release(VFIOContainerBase *bcontainer) +static void vfio_spapr_container_release(VFIOContainer *bcontainer) { VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, @@ -478,7 +478,7 @@ static void vfio_spapr_container_release(VFIOContainerBase *bcontainer) } } -static bool vfio_spapr_container_setup(VFIOContainerBase *bcontainer, +static bool vfio_spapr_container_setup(VFIOContainer *bcontainer, Error **errp) { VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); diff --git a/hw/vfio/vfio-iommufd.h b/hw/vfio/vfio-iommufd.h index 07ea0f4304964..6c049d9257638 100644 --- a/hw/vfio/vfio-iommufd.h +++ b/hw/vfio/vfio-iommufd.h @@ -23,7 +23,7 @@ typedef struct VFIOIOASHwpt { typedef struct IOMMUFDBackend IOMMUFDBackend; typedef struct VFIOIOMMUFDContainer { - VFIOContainerBase bcontainer; + VFIOContainer bcontainer; IOMMUFDBackend *be; uint32_t ioas_id; QLIST_HEAD(, VFIOIOASHwpt) hwpt_list; diff --git a/hw/vfio/vfio-listener.h b/hw/vfio/vfio-listener.h index eb69ddd374fa2..a90674ca96e8c 100644 --- a/hw/vfio/vfio-listener.h +++ b/hw/vfio/vfio-listener.h @@ -9,7 +9,7 @@ #ifndef HW_VFIO_VFIO_LISTENER_H #define HW_VFIO_VFIO_LISTENER_H -bool vfio_listener_register(VFIOContainerBase *bcontainer, Error **errp); -void vfio_listener_unregister(VFIOContainerBase *bcontainer); +bool vfio_listener_register(VFIOContainer *bcontainer, Error **errp); +void vfio_listener_unregister(VFIOContainer *bcontainer); #endif /* HW_VFIO_VFIO_LISTENER_H */ diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index acbd48a18a3a3..b580f4a02df92 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -26,14 +26,14 @@ typedef struct { typedef struct VFIOAddressSpace { AddressSpace *as; - QLIST_HEAD(, VFIOContainerBase) containers; + QLIST_HEAD(, VFIOContainer) containers; QLIST_ENTRY(VFIOAddressSpace) list; } VFIOAddressSpace; /* * This is the base object for vfio container backends */ -struct VFIOContainerBase { +struct VFIOContainer { Object parent_obj; VFIOAddressSpace *space; @@ -48,17 +48,17 @@ struct VFIOContainerBase { bool dirty_pages_started; /* Protected by BQL */ QLIST_HEAD(, VFIOGuestIOMMU) giommu_list; QLIST_HEAD(, VFIORamDiscardListener) vrdl_list; - QLIST_ENTRY(VFIOContainerBase) next; + QLIST_ENTRY(VFIOContainer) next; QLIST_HEAD(, VFIODevice) device_list; GList *iova_ranges; NotifierWithReturn cpr_reboot_notifier; }; #define TYPE_VFIO_IOMMU "vfio-iommu" -OBJECT_DECLARE_TYPE(VFIOContainerBase, VFIOIOMMUClass, VFIO_IOMMU) +OBJECT_DECLARE_TYPE(VFIOContainer, VFIOIOMMUClass, VFIO_IOMMU) typedef struct VFIOGuestIOMMU { - VFIOContainerBase *bcontainer; + VFIOContainer *bcontainer; IOMMUMemoryRegion *iommu_mr; hwaddr iommu_offset; IOMMUNotifier n; @@ -66,7 +66,7 @@ typedef struct VFIOGuestIOMMU { } VFIOGuestIOMMU; typedef struct VFIORamDiscardListener { - VFIOContainerBase *bcontainer; + VFIOContainer *bcontainer; MemoryRegion *mr; hwaddr offset_within_address_space; hwaddr size; @@ -78,32 +78,32 @@ typedef struct VFIORamDiscardListener { VFIOAddressSpace *vfio_address_space_get(AddressSpace *as); void vfio_address_space_put(VFIOAddressSpace *space); void vfio_address_space_insert(VFIOAddressSpace *space, - VFIOContainerBase *bcontainer); + VFIOContainer *bcontainer); -int vfio_container_dma_map(VFIOContainerBase *bcontainer, +int vfio_container_dma_map(VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mr); -int vfio_container_dma_unmap(VFIOContainerBase *bcontainer, +int vfio_container_dma_unmap(VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb, bool unmap_all); -bool vfio_container_add_section_window(VFIOContainerBase *bcontainer, +bool vfio_container_add_section_window(VFIOContainer *bcontainer, MemoryRegionSection *section, Error **errp); -void vfio_container_del_section_window(VFIOContainerBase *bcontainer, +void vfio_container_del_section_window(VFIOContainer *bcontainer, MemoryRegionSection *section); -int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, +int vfio_container_set_dirty_page_tracking(VFIOContainer *bcontainer, bool start, Error **errp); bool vfio_container_dirty_tracking_is_started( - const VFIOContainerBase *bcontainer); + const VFIOContainer *bcontainer); bool vfio_container_devices_dirty_tracking_is_supported( - const VFIOContainerBase *bcontainer); -int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + const VFIOContainer *bcontainer); +int vfio_container_query_dirty_bitmap(const VFIOContainer *bcontainer, uint64_t iova, uint64_t size, ram_addr_t ram_addr, Error **errp); -GList *vfio_container_get_iova_ranges(const VFIOContainerBase *bcontainer); +GList *vfio_container_get_iova_ranges(const VFIOContainer *bcontainer); static inline uint64_t -vfio_container_get_page_size_mask(const VFIOContainerBase *bcontainer) +vfio_container_get_page_size_mask(const VFIOContainer *bcontainer) { assert(bcontainer); return bcontainer->pgsizes; @@ -123,12 +123,12 @@ struct VFIOIOMMUClass { * Perform basic setup of the container, including configuring IOMMU * capabilities, IOVA ranges, supported page sizes, etc. * - * @bcontainer: #VFIOContainerBase + * @bcontainer: #VFIOContainer * @errp: pointer to Error*, to store an error if it happens. * * Returns true to indicate success and false for error. */ - bool (*setup)(VFIOContainerBase *bcontainer, Error **errp); + bool (*setup)(VFIOContainer *bcontainer, Error **errp); /** * @listener_begin @@ -136,9 +136,9 @@ struct VFIOIOMMUClass { * Called at the beginning of an address space update transaction. * See #MemoryListener. * - * @bcontainer: #VFIOContainerBase + * @bcontainer: #VFIOContainer */ - void (*listener_begin)(VFIOContainerBase *bcontainer); + void (*listener_begin)(VFIOContainer *bcontainer); /** * @listener_commit @@ -146,9 +146,9 @@ struct VFIOIOMMUClass { * Called at the end of an address space update transaction, * See #MemoryListener. * - * @bcontainer: #VFIOContainerBase + * @bcontainer: #VFIOContainer */ - void (*listener_commit)(VFIOContainerBase *bcontainer); + void (*listener_commit)(VFIOContainer *bcontainer); /** * @dma_map @@ -156,7 +156,7 @@ struct VFIOIOMMUClass { * Map an address range into the container. Note that the memory region is * referenced within an RCU read lock region across this call. * - * @bcontainer: #VFIOContainerBase to use + * @bcontainer: #VFIOContainer to use * @iova: start address to map * @size: size of the range to map * @vaddr: process virtual address of mapping @@ -165,7 +165,7 @@ struct VFIOIOMMUClass { * * Returns 0 to indicate success and -errno otherwise. */ - int (*dma_map)(const VFIOContainerBase *bcontainer, + int (*dma_map)(const VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mr); /** @@ -173,14 +173,14 @@ struct VFIOIOMMUClass { * * Map a file range for the container. * - * @bcontainer: #VFIOContainerBase to use for map + * @bcontainer: #VFIOContainer to use for map * @iova: start address to map * @size: size of the range to map * @fd: descriptor of the file to map * @start: starting file offset of the range to map * @readonly: map read only if true */ - int (*dma_map_file)(const VFIOContainerBase *bcontainer, + int (*dma_map_file)(const VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, int fd, unsigned long start, bool readonly); /** @@ -188,7 +188,7 @@ struct VFIOIOMMUClass { * * Unmap an address range from the container. * - * @bcontainer: #VFIOContainerBase to use for unmap + * @bcontainer: #VFIOContainer to use for unmap * @iova: start address to unmap * @size: size of the range to unmap * @iotlb: The IOMMU TLB mapping entry (or NULL) @@ -196,7 +196,7 @@ struct VFIOIOMMUClass { * * Returns 0 to indicate success and -errno otherwise. */ - int (*dma_unmap)(const VFIOContainerBase *bcontainer, + int (*dma_unmap)(const VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb, bool unmap_all); @@ -234,21 +234,21 @@ struct VFIOIOMMUClass { * * Start or stop dirty pages tracking on VFIO container * - * @bcontainer: #VFIOContainerBase on which to de/activate dirty + * @bcontainer: #VFIOContainer on which to de/activate dirty * page tracking * @start: indicates whether to start or stop dirty pages tracking * @errp: pointer to Error*, to store an error if it happens. * * Returns zero to indicate success and negative for error. */ - int (*set_dirty_page_tracking)(const VFIOContainerBase *bcontainer, + int (*set_dirty_page_tracking)(const VFIOContainer *bcontainer, bool start, Error **errp); /** * @query_dirty_bitmap * * Get bitmap of dirty pages from container * - * @bcontainer: #VFIOContainerBase from which to get dirty pages + * @bcontainer: #VFIOContainer from which to get dirty pages * @vbmap: #VFIOBitmap internal bitmap structure * @iova: iova base address * @size: size of iova range @@ -256,24 +256,24 @@ struct VFIOIOMMUClass { * * Returns zero to indicate success and negative for error. */ - int (*query_dirty_bitmap)(const VFIOContainerBase *bcontainer, + int (*query_dirty_bitmap)(const VFIOContainer *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp); /* PCI specific */ int (*pci_hot_reset)(VFIODevice *vbasedev, bool single); /* SPAPR specific */ - bool (*add_window)(VFIOContainerBase *bcontainer, + bool (*add_window)(VFIOContainer *bcontainer, MemoryRegionSection *section, Error **errp); - void (*del_window)(VFIOContainerBase *bcontainer, + void (*del_window)(VFIOContainer *bcontainer, MemoryRegionSection *section); - void (*release)(VFIOContainerBase *bcontainer); + void (*release)(VFIOContainer *bcontainer); }; VFIORamDiscardListener *vfio_find_ram_discard_listener( - VFIOContainerBase *bcontainer, MemoryRegionSection *section); + VFIOContainer *bcontainer, MemoryRegionSection *section); -void vfio_container_region_add(VFIOContainerBase *bcontainer, +void vfio_container_region_add(VFIOContainer *bcontainer, MemoryRegionSection *section, bool cpr_remap); #endif /* HW_VFIO_VFIO_CONTAINER_BASE_H */ diff --git a/include/hw/vfio/vfio-container.h b/include/hw/vfio/vfio-container.h index 712a6914004ae..a84dfb0dee79d 100644 --- a/include/hw/vfio/vfio-container.h +++ b/include/hw/vfio/vfio-container.h @@ -26,7 +26,7 @@ typedef struct VFIOGroup { } VFIOGroup; struct VFIOLegacyContainer { - VFIOContainerBase parent_obj; + VFIOContainer parent_obj; int fd; /* /dev/vfio/vfio, empowered by the attached groups */ unsigned iommu_type; diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index 04e987258719e..26ee0c4fe15ac 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -13,14 +13,14 @@ #include "system/memory.h" struct VFIOLegacyContainer; -struct VFIOContainerBase; +struct VFIOContainer; struct VFIOGroup; struct VFIODevice; struct VFIOPCIDevice; struct VFIOIOMMUFDContainer; struct IOMMUFDBackend; -typedef int (*dma_map_fn)(const struct VFIOContainerBase *bcontainer, +typedef int (*dma_map_fn)(const struct VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mr); @@ -65,11 +65,11 @@ int vfio_cpr_group_get_device_fd(int d, const char *name); bool vfio_cpr_container_match(struct VFIOLegacyContainer *container, struct VFIOGroup *group, int fd); -void vfio_cpr_giommu_remap(struct VFIOContainerBase *bcontainer, +void vfio_cpr_giommu_remap(struct VFIOContainer *bcontainer, MemoryRegionSection *section); bool vfio_cpr_ram_discard_register_listener( - struct VFIOContainerBase *bcontainer, MemoryRegionSection *section); + struct VFIOContainer *bcontainer, MemoryRegionSection *section); void vfio_cpr_save_vector_fd(struct VFIOPCIDevice *vdev, const char *name, int nr, int fd); diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index e7e6243e2dd7c..67b9fcdd2c011 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -54,7 +54,7 @@ typedef struct VFIODevice { QLIST_ENTRY(VFIODevice) container_next; QLIST_ENTRY(VFIODevice) global_next; struct VFIOGroup *group; - VFIOContainerBase *bcontainer; + VFIOContainer *bcontainer; char *sysfsdev; char *name; DeviceState *dev; @@ -252,7 +252,7 @@ struct VFIODeviceIOOps { void *data, bool post); }; -void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainerBase *bcontainer, +void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainer *bcontainer, struct vfio_device_info *info); void vfio_device_unprepare(VFIODevice *vbasedev); From 07cbbfb108586a03059975aa0e093b833cb9bf56 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:11 +0100 Subject: [PATCH 0901/1794] include/hw/vfio/vfio-container.h: rename file to vfio-container-legacy.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the rename of VFIOContainer to VFIOLegacyContainer, the vfio-container.h header file containing the struct definition is misleading. Rename it from vfio-container.h to vfio-container-legacy.h accordingly, fixing up the name of the include guard at the same time. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-4-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/ppc/spapr_pci_vfio.c | 2 +- hw/s390x/s390-pci-vfio.c | 2 +- hw/vfio/container.c | 2 +- hw/vfio/cpr-legacy.c | 2 +- hw/vfio/spapr.c | 2 +- .../hw/vfio/{vfio-container.h => vfio-container-legacy.h} | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) rename include/hw/vfio/{vfio-container.h => vfio-container-legacy.h} (88%) diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c index c8175dd8a89b1..a748a0bf4c987 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -24,7 +24,7 @@ #include "hw/pci-host/spapr.h" #include "hw/pci/msix.h" #include "hw/pci/pci_device.h" -#include "hw/vfio/vfio-container.h" +#include "hw/vfio/vfio-container-legacy.h" #include "qemu/error-report.h" #include CONFIG_DEVICES /* CONFIG_VFIO_PCI */ diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c index 938a5511713da..7760780aff7ae 100644 --- a/hw/s390x/s390-pci-vfio.c +++ b/hw/s390x/s390-pci-vfio.c @@ -20,7 +20,7 @@ #include "hw/s390x/s390-pci-clp.h" #include "hw/s390x/s390-pci-vfio.h" #include "hw/vfio/pci.h" -#include "hw/vfio/vfio-container.h" +#include "hw/vfio/vfio-container-legacy.h" #include "hw/vfio/vfio-helpers.h" /* diff --git a/hw/vfio/container.c b/hw/vfio/container.c index dc8425efb1f68..c0f87f774a008 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -34,7 +34,7 @@ #include "migration/cpr.h" #include "migration/blocker.h" #include "pci.h" -#include "hw/vfio/vfio-container.h" +#include "hw/vfio/vfio-container-legacy.h" #include "vfio-helpers.h" #include "vfio-listener.h" diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index bd3f6fc3d3936..bbf7a0d35f0ba 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -7,7 +7,7 @@ #include #include #include "qemu/osdep.h" -#include "hw/vfio/vfio-container.h" +#include "hw/vfio/vfio-container-legacy.h" #include "hw/vfio/vfio-device.h" #include "hw/vfio/vfio-listener.h" #include "migration/blocker.h" diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 6d462aa13caec..acaa9c141916f 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -15,7 +15,7 @@ #include "system/hostmem.h" #include "system/address-spaces.h" -#include "hw/vfio/vfio-container.h" +#include "hw/vfio/vfio-container-legacy.h" #include "hw/hw.h" #include "system/ram_addr.h" #include "qemu/error-report.h" diff --git a/include/hw/vfio/vfio-container.h b/include/hw/vfio/vfio-container-legacy.h similarity index 88% rename from include/hw/vfio/vfio-container.h rename to include/hw/vfio/vfio-container-legacy.h index a84dfb0dee79d..ab5130d26eaa0 100644 --- a/include/hw/vfio/vfio-container.h +++ b/include/hw/vfio/vfio-container-legacy.h @@ -6,8 +6,8 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef HW_VFIO_CONTAINER_H -#define HW_VFIO_CONTAINER_H +#ifndef HW_VFIO_CONTAINER_LEGACY_H +#define HW_VFIO_CONTAINER_LEGACY_H #include "hw/vfio/vfio-container-base.h" #include "hw/vfio/vfio-cpr.h" @@ -36,4 +36,4 @@ struct VFIOLegacyContainer { OBJECT_DECLARE_SIMPLE_TYPE(VFIOLegacyContainer, VFIO_IOMMU_LEGACY); -#endif /* HW_VFIO_CONTAINER_H */ +#endif /* HW_VFIO_CONTAINER_LEGACY_H */ From ef70eb32b8a15abea6af8180ba03e771ae041169 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:12 +0100 Subject: [PATCH 0902/1794] include/hw/vfio/vfio-container-base.h: rename file to vfio-container.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the rename of VFIOContainerBase to VFIOContainer, the vfio-container-base.h header file containing the struct definition is misleading. Rename it from vfio-container-base.h to vfio-container.h accordingly, fixing up the name of the include guard at the same time. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-5-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/container.h | 2 +- hw/vfio/container-base.c | 2 +- hw/vfio/vfio-iommufd.h | 2 +- include/hw/vfio/vfio-container-legacy.h | 2 +- include/hw/vfio/{vfio-container-base.h => vfio-container.h} | 6 +++--- include/hw/vfio/vfio-device.h | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) rename include/hw/vfio/{vfio-container-base.h => vfio-container.h} (98%) diff --git a/hw/vfio-user/container.h b/hw/vfio-user/container.h index 241863ef976f5..a2b42e3169dfb 100644 --- a/hw/vfio-user/container.h +++ b/hw/vfio-user/container.h @@ -9,7 +9,7 @@ #include "qemu/osdep.h" -#include "hw/vfio/vfio-container-base.h" +#include "hw/vfio/vfio-container.h" #include "hw/vfio-user/proxy.h" /* MMU container sub-class for vfio-user. */ diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 98c5198e5043b..250b20f424522 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -18,7 +18,7 @@ #include "system/ram_addr.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "hw/vfio/vfio-container-base.h" +#include "hw/vfio/vfio-container.h" #include "hw/vfio/vfio-device.h" /* vfio_device_reset_handler */ #include "system/reset.h" #include "vfio-helpers.h" diff --git a/hw/vfio/vfio-iommufd.h b/hw/vfio/vfio-iommufd.h index 6c049d9257638..13f412aad79ab 100644 --- a/hw/vfio/vfio-iommufd.h +++ b/hw/vfio/vfio-iommufd.h @@ -9,7 +9,7 @@ #ifndef HW_VFIO_VFIO_IOMMUFD_H #define HW_VFIO_VFIO_IOMMUFD_H -#include "hw/vfio/vfio-container-base.h" +#include "hw/vfio/vfio-container.h" typedef struct VFIODevice VFIODevice; diff --git a/include/hw/vfio/vfio-container-legacy.h b/include/hw/vfio/vfio-container-legacy.h index ab5130d26eaa0..74a72df018b50 100644 --- a/include/hw/vfio/vfio-container-legacy.h +++ b/include/hw/vfio/vfio-container-legacy.h @@ -9,7 +9,7 @@ #ifndef HW_VFIO_CONTAINER_LEGACY_H #define HW_VFIO_CONTAINER_LEGACY_H -#include "hw/vfio/vfio-container-base.h" +#include "hw/vfio/vfio-container.h" #include "hw/vfio/vfio-cpr.h" typedef struct VFIOLegacyContainer VFIOLegacyContainer; diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container.h similarity index 98% rename from include/hw/vfio/vfio-container-base.h rename to include/hw/vfio/vfio-container.h index b580f4a02df92..b8fb2b8b5d72b 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container.h @@ -10,8 +10,8 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef HW_VFIO_VFIO_CONTAINER_BASE_H -#define HW_VFIO_VFIO_CONTAINER_BASE_H +#ifndef HW_VFIO_VFIO_CONTAINER_H +#define HW_VFIO_VFIO_CONTAINER_H #include "system/memory.h" @@ -276,4 +276,4 @@ VFIORamDiscardListener *vfio_find_ram_discard_listener( void vfio_container_region_add(VFIOContainer *bcontainer, MemoryRegionSection *section, bool cpr_remap); -#endif /* HW_VFIO_VFIO_CONTAINER_BASE_H */ +#endif /* HW_VFIO_VFIO_CONTAINER_H */ diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index 67b9fcdd2c011..ed19e2e1e5859 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -27,7 +27,7 @@ #include #endif #include "system/system.h" -#include "hw/vfio/vfio-container-base.h" +#include "hw/vfio/vfio-container.h" #include "hw/vfio/vfio-cpr.h" #include "system/host_iommu_device.h" #include "system/iommufd.h" From a3bcae62b6a161dea4521f254f19c5a8551c28af Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:13 +0100 Subject: [PATCH 0903/1794] hw/vfio/container.c: rename file to container-legacy.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This file is mostly concerned with the VFIOLegacyContainer implementation so rename it to reflect the previous rename of VFIOContainer to VFIOLegacyContainer. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-6-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/{container.c => container-legacy.c} | 0 hw/vfio/meson.build | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename hw/vfio/{container.c => container-legacy.c} (100%) diff --git a/hw/vfio/container.c b/hw/vfio/container-legacy.c similarity index 100% rename from hw/vfio/container.c rename to hw/vfio/container-legacy.c diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index d3ed3cb7ac199..62b7a7eaaca3d 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -4,7 +4,7 @@ vfio_ss = ss.source_set() vfio_ss.add(files( 'listener.c', 'container-base.c', - 'container.c', + 'container-legacy.c', 'helpers.c', )) vfio_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr.c')) From dcce51b19385ea65ac6db295204716a9eb311fbf Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:14 +0100 Subject: [PATCH 0904/1794] hw/vfio/container-base.c: rename file to container.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the file to reflect the previous rename of VFIOContainerBase to VFIOContainer. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-7-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/{container-base.c => container.c} | 0 hw/vfio/meson.build | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename hw/vfio/{container-base.c => container.c} (100%) diff --git a/hw/vfio/container-base.c b/hw/vfio/container.c similarity index 100% rename from hw/vfio/container-base.c rename to hw/vfio/container.c diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 62b7a7eaaca3d..82f68698fb848 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -3,7 +3,7 @@ vfio_ss = ss.source_set() vfio_ss.add(files( 'listener.c', - 'container-base.c', + 'container.c', 'container-legacy.c', 'helpers.c', )) From cc5b394291f428a04ff7a622573c02560b183797 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:15 +0100 Subject: [PATCH 0905/1794] vfio/iommufd.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOIOMMUFDContainer and VFIOContainer instead of accessing bcontainer directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-8-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 7f2243d9d197d..f0ffe23591965 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -38,8 +38,7 @@ static int iommufd_cdev_map(const VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mr) { - const VFIOIOMMUFDContainer *container = - container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); + const VFIOIOMMUFDContainer *container = VFIO_IOMMU_IOMMUFD(bcontainer); return iommufd_backend_map_dma(container->be, container->ioas_id, @@ -50,8 +49,7 @@ static int iommufd_cdev_map_file(const VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, int fd, unsigned long start, bool readonly) { - const VFIOIOMMUFDContainer *container = - container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); + const VFIOIOMMUFDContainer *container = VFIO_IOMMU_IOMMUFD(bcontainer); return iommufd_backend_map_file_dma(container->be, container->ioas_id, @@ -62,8 +60,7 @@ static int iommufd_cdev_unmap(const VFIOContainer *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb, bool unmap_all) { - const VFIOIOMMUFDContainer *container = - container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); + const VFIOIOMMUFDContainer *container = VFIO_IOMMU_IOMMUFD(bcontainer); /* unmap in halves */ if (unmap_all) { @@ -162,8 +159,7 @@ static bool iommufd_hwpt_dirty_tracking(VFIOIOASHwpt *hwpt) static int iommufd_set_dirty_page_tracking(const VFIOContainer *bcontainer, bool start, Error **errp) { - const VFIOIOMMUFDContainer *container = - container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); + const VFIOIOMMUFDContainer *container = VFIO_IOMMU_IOMMUFD(bcontainer); VFIOIOASHwpt *hwpt; QLIST_FOREACH(hwpt, &container->hwpt_list, next) { @@ -194,9 +190,7 @@ static int iommufd_query_dirty_bitmap(const VFIOContainer *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) { - VFIOIOMMUFDContainer *container = container_of(bcontainer, - VFIOIOMMUFDContainer, - bcontainer); + VFIOIOMMUFDContainer *container = VFIO_IOMMU_IOMMUFD(bcontainer); unsigned long page_size = qemu_real_host_page_size(); VFIOIOASHwpt *hwpt; @@ -324,6 +318,7 @@ static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, { ERRP_GUARD(); IOMMUFDBackend *iommufd = vbasedev->iommufd; + VFIOContainer *bcontainer = VFIO_IOMMU(container); uint32_t type, flags = 0; uint64_t hw_caps; VFIOIOASHwpt *hwpt; @@ -408,9 +403,9 @@ static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, vbasedev->iommu_dirty_tracking = iommufd_hwpt_dirty_tracking(hwpt); QLIST_INSERT_HEAD(&hwpt->device_list, vbasedev, hwpt_next); QLIST_INSERT_HEAD(&container->hwpt_list, hwpt, next); - container->bcontainer.dirty_pages_supported |= + bcontainer->dirty_pages_supported |= vbasedev->iommu_dirty_tracking; - if (container->bcontainer.dirty_pages_supported && + if (bcontainer->dirty_pages_supported && !vbasedev->iommu_dirty_tracking) { warn_report("IOMMU instance for device %s doesn't support dirty tracking", vbasedev->name); @@ -464,7 +459,7 @@ static void iommufd_cdev_detach_container(VFIODevice *vbasedev, static void iommufd_cdev_container_destroy(VFIOIOMMUFDContainer *container) { - VFIOContainer *bcontainer = &container->bcontainer; + VFIOContainer *bcontainer = VFIO_IOMMU(container); if (!QLIST_EMPTY(&bcontainer->device_list)) { return; @@ -486,7 +481,7 @@ static int iommufd_cdev_ram_block_discard_disable(bool state) static bool iommufd_cdev_get_info_iova_range(VFIOIOMMUFDContainer *container, uint32_t ioas_id, Error **errp) { - VFIOContainer *bcontainer = &container->bcontainer; + VFIOContainer *bcontainer = VFIO_IOMMU(container); g_autofree struct iommu_ioas_iova_ranges *info = NULL; struct iommu_iova_range *iova_ranges; int sz, fd = container->be->fd; @@ -559,7 +554,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, /* try to attach to an existing container in this space */ QLIST_FOREACH(bcontainer, &space->containers, next) { - container = container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); + container = VFIO_IOMMU_IOMMUFD(bcontainer); if (VFIO_IOMMU_GET_CLASS(bcontainer) != iommufd_vioc || vbasedev->iommufd != container->be) { continue; @@ -609,7 +604,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, QLIST_INIT(&container->hwpt_list); vbasedev->cpr.ioas_id = ioas_id; - bcontainer = &container->bcontainer; + bcontainer = VFIO_IOMMU(container); vfio_address_space_insert(space, bcontainer); if (!iommufd_cdev_attach_container(vbasedev, container, errp)) { @@ -689,9 +684,8 @@ static void iommufd_cdev_detach(VFIODevice *vbasedev) { VFIOContainer *bcontainer = vbasedev->bcontainer; VFIOAddressSpace *space = bcontainer->space; - VFIOIOMMUFDContainer *container = container_of(bcontainer, - VFIOIOMMUFDContainer, - bcontainer); + VFIOIOMMUFDContainer *container = VFIO_IOMMU_IOMMUFD(bcontainer); + vfio_device_unprepare(vbasedev); if (!vbasedev->ram_block_discard_allowed) { From 91bdb2f32902c83490cbafe524c82005a64e1f68 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:16 +0100 Subject: [PATCH 0906/1794] vfio/cpr-iommufd.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOIOMMUFDContainer and VFIOContainer instead of accessing bcontainer directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-9-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr-iommufd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/cpr-iommufd.c b/hw/vfio/cpr-iommufd.c index 6aaf6f77a25a3..1d70c87996d0d 100644 --- a/hw/vfio/cpr-iommufd.c +++ b/hw/vfio/cpr-iommufd.c @@ -176,7 +176,7 @@ void vfio_iommufd_cpr_unregister_iommufd(IOMMUFDBackend *be) bool vfio_iommufd_cpr_register_container(VFIOIOMMUFDContainer *container, Error **errp) { - VFIOContainer *bcontainer = &container->bcontainer; + VFIOContainer *bcontainer = VFIO_IOMMU(container); migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier, vfio_cpr_reboot_notifier, @@ -189,7 +189,7 @@ bool vfio_iommufd_cpr_register_container(VFIOIOMMUFDContainer *container, void vfio_iommufd_cpr_unregister_container(VFIOIOMMUFDContainer *container) { - VFIOContainer *bcontainer = &container->bcontainer; + VFIOContainer *bcontainer = VFIO_IOMMU(container); migration_remove_notifier(&bcontainer->cpr_reboot_notifier); } From a7f185cbeca75555a7fa7272afbd3a06bc4f7f66 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:17 +0100 Subject: [PATCH 0907/1794] vfio/vfio-iommufd.h: rename VFIOContainer bcontainer field to parent_obj MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that nothing accesses the bcontainer field directly, rename bcontainer to parent_obj as per our current coding guidelines. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-10-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/vfio-iommufd.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/vfio/vfio-iommufd.h b/hw/vfio/vfio-iommufd.h index 13f412aad79ab..6b28e1ff7bbb1 100644 --- a/hw/vfio/vfio-iommufd.h +++ b/hw/vfio/vfio-iommufd.h @@ -22,12 +22,13 @@ typedef struct VFIOIOASHwpt { typedef struct IOMMUFDBackend IOMMUFDBackend; -typedef struct VFIOIOMMUFDContainer { - VFIOContainer bcontainer; +struct VFIOIOMMUFDContainer { + VFIOContainer parent_obj; + IOMMUFDBackend *be; uint32_t ioas_id; QLIST_HEAD(, VFIOIOASHwpt) hwpt_list; -} VFIOIOMMUFDContainer; +}; OBJECT_DECLARE_SIMPLE_TYPE(VFIOIOMMUFDContainer, VFIO_IOMMU_IOMMUFD); From 6c671235636cfb5e825040b4621ff06c77925731 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:18 +0100 Subject: [PATCH 0908/1794] vfio/spapr.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOSpaprContainer and VFIOLegacyContainer instead of accessing bcontainer directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-11-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/spapr.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index acaa9c141916f..c883ba6da9e98 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -61,7 +61,7 @@ static void vfio_prereg_listener_region_add(MemoryListener *listener, { VFIOSpaprContainer *scontainer = container_of(listener, VFIOSpaprContainer, prereg_listener); - VFIOLegacyContainer *container = &scontainer->container; + VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(scontainer); VFIOContainer *bcontainer = VFIO_IOMMU(container); const hwaddr gpa = section->offset_within_address_space; hwaddr end; @@ -121,7 +121,7 @@ static void vfio_prereg_listener_region_del(MemoryListener *listener, { VFIOSpaprContainer *scontainer = container_of(listener, VFIOSpaprContainer, prereg_listener); - VFIOLegacyContainer *container = &scontainer->container; + VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(scontainer); const hwaddr gpa = section->offset_within_address_space; hwaddr end; int ret; @@ -245,8 +245,7 @@ static bool vfio_spapr_create_window(VFIOLegacyContainer *container, { int ret = 0; VFIOContainer *bcontainer = VFIO_IOMMU(container); - VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, - container); + VFIOSpaprContainer *scontainer = VFIO_IOMMU_SPAPR(bcontainer); IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); uint64_t pagesize = memory_region_iommu_get_min_page_size(iommu_mr), pgmask; unsigned entries, bits_total, bits_per_level, max_levels, ddw_levels; @@ -353,8 +352,7 @@ vfio_spapr_container_add_section_window(VFIOContainer *bcontainer, Error **errp) { VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); - VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, - container); + VFIOSpaprContainer *scontainer = VFIO_IOMMU_SPAPR(container); VFIOHostDMAWindow *hostwin; hwaddr pgsize = 0; int ret; @@ -443,8 +441,7 @@ vfio_spapr_container_del_section_window(VFIOContainer *bcontainer, MemoryRegionSection *section) { VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); - VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, - container); + VFIOSpaprContainer *scontainer = VFIO_IOMMU_SPAPR(container); if (container->iommu_type != VFIO_SPAPR_TCE_v2_IOMMU) { return; @@ -464,8 +461,7 @@ vfio_spapr_container_del_section_window(VFIOContainer *bcontainer, static void vfio_spapr_container_release(VFIOContainer *bcontainer) { VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); - VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, - container); + VFIOSpaprContainer *scontainer = VFIO_IOMMU_SPAPR(container); VFIOHostDMAWindow *hostwin, *next; if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { @@ -482,8 +478,7 @@ static bool vfio_spapr_container_setup(VFIOContainer *bcontainer, Error **errp) { VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); - VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, - container); + VFIOSpaprContainer *scontainer = VFIO_IOMMU_SPAPR(container); struct vfio_iommu_spapr_tce_info info; bool v2 = container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU; int ret, fd = container->fd; From 1bd06d0385eb72bb54aa14af34e8361ec3ae8cee Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:19 +0100 Subject: [PATCH 0909/1794] vfio/spapr.c: rename VFIOContainer bcontainer field to parent_obj MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that nothing accesses the bcontainer field directly, rename bcontainer to parent_obj as per our current coding guidelines. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-12-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/spapr.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index c883ba6da9e98..8d9d68da4ec8f 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -30,12 +30,13 @@ typedef struct VFIOHostDMAWindow { QLIST_ENTRY(VFIOHostDMAWindow) hostwin_next; } VFIOHostDMAWindow; -typedef struct VFIOSpaprContainer { - VFIOLegacyContainer container; +struct VFIOSpaprContainer { + VFIOLegacyContainer parent_obj; + MemoryListener prereg_listener; QLIST_HEAD(, VFIOHostDMAWindow) hostwin_list; unsigned int levels; -} VFIOSpaprContainer; +}; OBJECT_DECLARE_SIMPLE_TYPE(VFIOSpaprContainer, VFIO_IOMMU_SPAPR); From d5db50dd819142748e10e488a2288052ec0235fd Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:20 +0100 Subject: [PATCH 0910/1794] vfio/pci.c: rename vfio_instance_init() to vfio_pci_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the more typical naming convention for QOM init() functions, in particular it changes the prefix to match the name of the QOM type. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-13-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index bc0b4c4d562bf..493f9da45de52 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3625,7 +3625,7 @@ static void vfio_pci_reset(DeviceState *dev) vfio_pci_post_reset(vdev); } -static void vfio_instance_init(Object *obj) +static void vfio_pci_init(Object *obj) { PCIDevice *pci_dev = PCI_DEVICE(obj); VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); @@ -3916,7 +3916,7 @@ static const TypeInfo vfio_pci_dev_info = { .name = TYPE_VFIO_PCI, .parent = TYPE_VFIO_PCI_BASE, .class_init = vfio_pci_dev_class_init, - .instance_init = vfio_instance_init, + .instance_init = vfio_pci_init, .instance_finalize = vfio_instance_finalize, }; From e6fd80873a24883dc204d09f11c16e30c8cd1c37 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:21 +0100 Subject: [PATCH 0911/1794] vfio/pci.c: rename vfio_instance_finalize() to vfio_pci_finalize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the more typical naming convention for QOM finalize() functions, in particular it changes the prefix to match the name of the QOM type. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-14-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 493f9da45de52..90458bc3cc669 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3550,7 +3550,7 @@ static void vfio_pci_realize(PCIDevice *pdev, Error **errp) error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->name); } -static void vfio_instance_finalize(Object *obj) +static void vfio_pci_finalize(Object *obj) { VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); @@ -3917,7 +3917,7 @@ static const TypeInfo vfio_pci_dev_info = { .parent = TYPE_VFIO_PCI_BASE, .class_init = vfio_pci_dev_class_init, .instance_init = vfio_pci_init, - .instance_finalize = vfio_instance_finalize, + .instance_finalize = vfio_pci_finalize, }; static const Property vfio_pci_dev_nohotplug_properties[] = { From 784fa15f02fc0adbf8897f5c3012652174cc7274 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:22 +0100 Subject: [PATCH 0912/1794] vfio/pci.c: rename vfio_pci_dev_class_init() to vfio_pci_class_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the function prefix to match the name of the QOM type. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-15-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 90458bc3cc669..1fd087de3c903 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3763,7 +3763,7 @@ static void vfio_pci_set_fd(Object *obj, const char *str, Error **errp) } #endif -static void vfio_pci_dev_class_init(ObjectClass *klass, const void *data) +static void vfio_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass); @@ -3915,7 +3915,7 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, const void *data) static const TypeInfo vfio_pci_dev_info = { .name = TYPE_VFIO_PCI, .parent = TYPE_VFIO_PCI_BASE, - .class_init = vfio_pci_dev_class_init, + .class_init = vfio_pci_class_init, .instance_init = vfio_pci_init, .instance_finalize = vfio_pci_finalize, }; From 25c8376b37948d7608b1a13f43e985fc801295e5 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:23 +0100 Subject: [PATCH 0913/1794] vfio/pci.c: rename vfio_pci_dev_info to vfio_pci_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the prefix to match the name of the QOM type. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-16-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 1fd087de3c903..479def72c5823 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3912,7 +3912,7 @@ static void vfio_pci_class_init(ObjectClass *klass, const void *data) "multifd channels"); } -static const TypeInfo vfio_pci_dev_info = { +static const TypeInfo vfio_pci_info = { .name = TYPE_VFIO_PCI, .parent = TYPE_VFIO_PCI_BASE, .class_init = vfio_pci_class_init, @@ -3970,7 +3970,7 @@ static void register_vfio_pci_dev_type(void) vfio_pci_migration_multifd_transfer_prop.realized_set_allowed = true; type_register_static(&vfio_pci_base_dev_info); - type_register_static(&vfio_pci_dev_info); + type_register_static(&vfio_pci_info); type_register_static(&vfio_pci_nohotplug_dev_info); } From af2a8bfb3cd38cceb39a152ff33c7daa4c81c506 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:24 +0100 Subject: [PATCH 0914/1794] hw/vfio/types.h: rename TYPE_VFIO_PCI_BASE to TYPE_VFIO_PCI_DEVICE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This brings the QOM type name in line with the underlying VFIOPCIDevice structure. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-17-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/s390x/s390-pci-vfio.c | 14 +++++++------- hw/vfio-user/pci.c | 13 +++++++------ hw/vfio/device.c | 2 +- hw/vfio/pci.c | 28 ++++++++++++++-------------- hw/vfio/pci.h | 2 +- hw/vfio/types.h | 4 ++-- 6 files changed, 32 insertions(+), 31 deletions(-) diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c index 7760780aff7ae..9e31029d7acbe 100644 --- a/hw/s390x/s390-pci-vfio.c +++ b/hw/s390x/s390-pci-vfio.c @@ -62,7 +62,7 @@ S390PCIDMACount *s390_pci_start_dma_count(S390pciState *s, { S390PCIDMACount *cnt; uint32_t avail; - VFIOPCIDevice *vpdev = VFIO_PCI_BASE(pbdev->pdev); + VFIOPCIDevice *vpdev = VFIO_PCI_DEVICE(pbdev->pdev); int id; assert(vpdev); @@ -108,7 +108,7 @@ static void s390_pci_read_base(S390PCIBusDevice *pbdev, { struct vfio_info_cap_header *hdr; struct vfio_device_info_cap_zpci_base *cap; - VFIOPCIDevice *vpci = VFIO_PCI_BASE(pbdev->pdev); + VFIOPCIDevice *vpci = VFIO_PCI_DEVICE(pbdev->pdev); uint64_t vfio_size; hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_BASE); @@ -162,7 +162,7 @@ static bool get_host_fh(S390PCIBusDevice *pbdev, struct vfio_device_info *info, { struct vfio_info_cap_header *hdr; struct vfio_device_info_cap_zpci_base *cap; - VFIOPCIDevice *vpci = VFIO_PCI_BASE(pbdev->pdev); + VFIOPCIDevice *vpci = VFIO_PCI_DEVICE(pbdev->pdev); hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_BASE); @@ -185,7 +185,7 @@ static void s390_pci_read_group(S390PCIBusDevice *pbdev, struct vfio_device_info_cap_zpci_group *cap; S390pciState *s = s390_get_phb(); ClpRspQueryPciGrp *resgrp; - VFIOPCIDevice *vpci = VFIO_PCI_BASE(pbdev->pdev); + VFIOPCIDevice *vpci = VFIO_PCI_DEVICE(pbdev->pdev); uint8_t start_gid = pbdev->zpci_fn.pfgid; hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_GROUP); @@ -264,7 +264,7 @@ static void s390_pci_read_util(S390PCIBusDevice *pbdev, { struct vfio_info_cap_header *hdr; struct vfio_device_info_cap_zpci_util *cap; - VFIOPCIDevice *vpci = VFIO_PCI_BASE(pbdev->pdev); + VFIOPCIDevice *vpci = VFIO_PCI_DEVICE(pbdev->pdev); hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_UTIL); @@ -291,7 +291,7 @@ static void s390_pci_read_pfip(S390PCIBusDevice *pbdev, { struct vfio_info_cap_header *hdr; struct vfio_device_info_cap_zpci_pfip *cap; - VFIOPCIDevice *vpci = VFIO_PCI_BASE(pbdev->pdev); + VFIOPCIDevice *vpci = VFIO_PCI_DEVICE(pbdev->pdev); hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_PFIP); @@ -314,7 +314,7 @@ static void s390_pci_read_pfip(S390PCIBusDevice *pbdev, static struct vfio_device_info *get_device_info(S390PCIBusDevice *pbdev) { - VFIOPCIDevice *vfio_pci = VFIO_PCI_BASE(pbdev->pdev); + VFIOPCIDevice *vfio_pci = VFIO_PCI_DEVICE(pbdev->pdev); return vfio_get_device_info(vfio_pci->vbasedev.fd); } diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index e2c309784fec5..efceae69dee09 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -234,9 +234,10 @@ static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp) { ERRP_GUARD(); VFIOUserPCIDevice *udev = VFIO_USER_PCI(pdev); - VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(pdev); VFIODevice *vbasedev = &vdev->vbasedev; const char *sock_name; + AddressSpace *as; SocketAddress addr; VFIOUserProxy *proxy; @@ -346,7 +347,7 @@ static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp) static void vfio_user_instance_init(Object *obj) { PCIDevice *pci_dev = PCI_DEVICE(obj); - VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(obj); VFIODevice *vbasedev = &vdev->vbasedev; device_add_bootindex_property(obj, &vdev->bootindex, @@ -371,7 +372,7 @@ static void vfio_user_instance_init(Object *obj) static void vfio_user_instance_finalize(Object *obj) { - VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(obj); VFIODevice *vbasedev = &vdev->vbasedev; if (vdev->msix != NULL) { @@ -387,7 +388,7 @@ static void vfio_user_instance_finalize(Object *obj) static void vfio_user_pci_reset(DeviceState *dev) { - VFIOPCIDevice *vdev = VFIO_PCI_BASE(dev); + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(dev); VFIODevice *vbasedev = &vdev->vbasedev; vfio_pci_pre_reset(vdev); @@ -421,7 +422,7 @@ static void vfio_user_pci_set_socket(Object *obj, Visitor *v, const char *name, VFIOUserPCIDevice *udev = VFIO_USER_PCI(obj); bool success; - if (VFIO_PCI_BASE(udev)->vbasedev.proxy) { + if (VFIO_PCI_DEVICE(udev)->vbasedev.proxy) { error_setg(errp, "Proxy is connected"); return; } @@ -464,7 +465,7 @@ static void vfio_user_pci_dev_class_init(ObjectClass *klass, const void *data) static const TypeInfo vfio_user_pci_dev_info = { .name = TYPE_VFIO_USER_PCI, - .parent = TYPE_VFIO_PCI_BASE, + .parent = TYPE_VFIO_PCI_DEVICE, .instance_size = sizeof(VFIOUserPCIDevice), .class_init = vfio_user_pci_dev_class_init, .instance_init = vfio_user_instance_init, diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 963cefc053d09..64f8750389479 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -423,7 +423,7 @@ bool vfio_device_hiod_create_and_realize(VFIODevice *vbasedev, VFIODevice *vfio_get_vfio_device(Object *obj) { if (object_dynamic_cast(obj, TYPE_VFIO_PCI)) { - return &VFIO_PCI_BASE(obj)->vbasedev; + return &VFIO_PCI_DEVICE(obj)->vbasedev; } else { return NULL; } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 479def72c5823..aa9d3e0354946 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -305,7 +305,7 @@ static void vfio_intx_update(VFIOPCIDevice *vdev, PCIINTxRoute *route) static void vfio_intx_routing_notifier(PCIDevice *pdev) { - VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(pdev); PCIINTxRoute route; if (vdev->interrupt != VFIO_INT_INTx) { @@ -660,7 +660,7 @@ void vfio_pci_vector_init(VFIOPCIDevice *vdev, int nr) static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, MSIMessage *msg, IOHandler *handler) { - VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(pdev); VFIOMSIVector *vector; int ret; bool resizing = !!(vdev->nr_vectors < nr + 1); @@ -755,7 +755,7 @@ static int vfio_msix_vector_use(PCIDevice *pdev, static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) { - VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(pdev); VFIOMSIVector *vector = &vdev->msi_vectors[nr]; trace_vfio_msix_vector_release(vdev->vbasedev.name, nr); @@ -1346,7 +1346,7 @@ static const MemoryRegionOps vfio_vga_ops = { */ static void vfio_sub_page_bar_update_mapping(PCIDevice *pdev, int bar) { - VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(pdev); VFIORegion *region = &vdev->bars[bar].region; MemoryRegion *mmap_mr, *region_mr, *base_mr; PCIIORegion *r; @@ -1392,7 +1392,7 @@ static void vfio_sub_page_bar_update_mapping(PCIDevice *pdev, int bar) */ uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) { - VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(pdev); VFIODevice *vbasedev = &vdev->vbasedev; uint32_t emu_bits = 0, emu_val = 0, phys_val = 0, val; @@ -1426,7 +1426,7 @@ uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, uint32_t val, int len) { - VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(pdev); VFIODevice *vbasedev = &vdev->vbasedev; uint32_t val_le = cpu_to_le32(val); int ret; @@ -3392,7 +3392,7 @@ bool vfio_pci_interrupt_setup(VFIOPCIDevice *vdev, Error **errp) static void vfio_pci_realize(PCIDevice *pdev, Error **errp) { ERRP_GUARD(); - VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(pdev); VFIODevice *vbasedev = &vdev->vbasedev; int i; char uuid[UUID_STR_LEN]; @@ -3552,14 +3552,14 @@ static void vfio_pci_realize(PCIDevice *pdev, Error **errp) static void vfio_pci_finalize(Object *obj) { - VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(obj); vfio_pci_put_device(vdev); } static void vfio_exitfn(PCIDevice *pdev) { - VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(pdev); VFIODevice *vbasedev = &vdev->vbasedev; vfio_unregister_req_notifier(vdev); @@ -3583,7 +3583,7 @@ static void vfio_exitfn(PCIDevice *pdev) static void vfio_pci_reset(DeviceState *dev) { - VFIOPCIDevice *vdev = VFIO_PCI_BASE(dev); + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(dev); /* Do not reset the device during qemu_system_reset prior to cpr load */ if (cpr_is_incoming()) { @@ -3628,7 +3628,7 @@ static void vfio_pci_reset(DeviceState *dev) static void vfio_pci_init(Object *obj) { PCIDevice *pci_dev = PCI_DEVICE(obj); - VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(obj); VFIODevice *vbasedev = &vdev->vbasedev; device_add_bootindex_property(obj, &vdev->bootindex, @@ -3669,7 +3669,7 @@ static void vfio_pci_base_dev_class_init(ObjectClass *klass, const void *data) } static const TypeInfo vfio_pci_base_dev_info = { - .name = TYPE_VFIO_PCI_BASE, + .name = TYPE_VFIO_PCI_DEVICE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VFIOPCIDevice), .abstract = true, @@ -3758,7 +3758,7 @@ static const Property vfio_pci_dev_properties[] = { #ifdef CONFIG_IOMMUFD static void vfio_pci_set_fd(Object *obj, const char *str, Error **errp) { - VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(obj); vfio_device_set_fd(&vdev->vbasedev, str, errp); } #endif @@ -3914,7 +3914,7 @@ static void vfio_pci_class_init(ObjectClass *klass, const void *data) static const TypeInfo vfio_pci_info = { .name = TYPE_VFIO_PCI, - .parent = TYPE_VFIO_PCI_BASE, + .parent = TYPE_VFIO_PCI_DEVICE, .class_init = vfio_pci_class_init, .instance_init = vfio_pci_init, .instance_finalize = vfio_pci_finalize, diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index e0aef82a8931c..0f78cf9cdbf18 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -120,7 +120,7 @@ typedef struct VFIOMSIXInfo { MemoryRegion *pba_region; } VFIOMSIXInfo; -OBJECT_DECLARE_SIMPLE_TYPE(VFIOPCIDevice, VFIO_PCI_BASE) +OBJECT_DECLARE_SIMPLE_TYPE(VFIOPCIDevice, VFIO_PCI_DEVICE) struct VFIOPCIDevice { PCIDevice parent_obj; diff --git a/hw/vfio/types.h b/hw/vfio/types.h index c19334ff25ae2..5482d908088af 100644 --- a/hw/vfio/types.h +++ b/hw/vfio/types.h @@ -9,11 +9,11 @@ #define HW_VFIO_VFIO_TYPES_H /* - * TYPE_VFIO_PCI_BASE is an abstract type used to share code + * TYPE_VFIO_PCI_DEVICE is an abstract type used to share code * between VFIO implementations that use a kernel driver * with those that use user sockets. */ -#define TYPE_VFIO_PCI_BASE "vfio-pci-base" +#define TYPE_VFIO_PCI_DEVICE "vfio-pci-device" #define TYPE_VFIO_PCI "vfio-pci" /* TYPE_VFIO_PCI shares struct VFIOPCIDevice. */ From 153273f2796a664b08120799f326c6319f640011 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:25 +0100 Subject: [PATCH 0915/1794] vfio/pci.c: rename vfio_pci_base_dev_class_init() to vfio_pci_device_class_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the function prefix to match the name of the QOM type. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-18-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index aa9d3e0354946..9278b4671188f 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3656,7 +3656,7 @@ static void vfio_pci_init(Object *obj) pci_dev->cap_present |= QEMU_PCI_SKIP_RESET_ON_CPR; } -static void vfio_pci_base_dev_class_init(ObjectClass *klass, const void *data) +static void vfio_pci_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass); @@ -3673,7 +3673,7 @@ static const TypeInfo vfio_pci_base_dev_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VFIOPCIDevice), .abstract = true, - .class_init = vfio_pci_base_dev_class_init, + .class_init = vfio_pci_device_class_init, .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, From 596b158ffd727465fbeb9fff9a22b7ea7a341bc3 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:26 +0100 Subject: [PATCH 0916/1794] vfio/pci.c: rename vfio_pci_base_dev_info to vfio_pci_device_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the prefix to match the name of the QOM type. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-19-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 9278b4671188f..6c4c47825bcea 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3668,7 +3668,7 @@ static void vfio_pci_device_class_init(ObjectClass *klass, const void *data) pdc->config_write = vfio_pci_write_config; } -static const TypeInfo vfio_pci_base_dev_info = { +static const TypeInfo vfio_pci_device_info = { .name = TYPE_VFIO_PCI_DEVICE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VFIOPCIDevice), @@ -3969,7 +3969,7 @@ static void register_vfio_pci_dev_type(void) vfio_pci_migration_multifd_transfer_prop = qdev_prop_on_off_auto; vfio_pci_migration_multifd_transfer_prop.realized_set_allowed = true; - type_register_static(&vfio_pci_base_dev_info); + type_register_static(&vfio_pci_device_info); type_register_static(&vfio_pci_info); type_register_static(&vfio_pci_nohotplug_dev_info); } From 7c53e1f43ee365ee5061dd30d60c21a55eefbfce Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:27 +0100 Subject: [PATCH 0917/1794] vfio/pci.c: rename vfio_pci_dev_properties[] to vfio_pci_properties[] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the prefix to match the name of the QOM type. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-20-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 6c4c47825bcea..9c3bb3725ccbb 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3683,7 +3683,7 @@ static const TypeInfo vfio_pci_device_info = { static PropertyInfo vfio_pci_migration_multifd_transfer_prop; -static const Property vfio_pci_dev_properties[] = { +static const Property vfio_pci_properties[] = { DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIOPCIDevice, host), DEFINE_PROP_UUID_NODEFAULT("vf-token", VFIOPCIDevice, vf_token), DEFINE_PROP_STRING("sysfsdev", VFIOPCIDevice, vbasedev.sysfsdev), @@ -3769,7 +3769,7 @@ static void vfio_pci_class_init(ObjectClass *klass, const void *data) PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass); device_class_set_legacy_reset(dc, vfio_pci_reset); - device_class_set_props(dc, vfio_pci_dev_properties); + device_class_set_props(dc, vfio_pci_properties); #ifdef CONFIG_IOMMUFD object_class_property_add_str(klass, "fd", NULL, vfio_pci_set_fd); #endif From 05530ba2462f37fd55d72bdab31a7a5c29ab7519 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:28 +0100 Subject: [PATCH 0918/1794] vfio/pci.c: rename vfio_pci_dev_nohotplug_properties[] to vfio_pci_nohotplug_properties[] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the prefix to match the name of the QOM type. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-21-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 9c3bb3725ccbb..6b3c97c56c9c2 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3920,7 +3920,7 @@ static const TypeInfo vfio_pci_info = { .instance_finalize = vfio_pci_finalize, }; -static const Property vfio_pci_dev_nohotplug_properties[] = { +static const Property vfio_pci_nohotplug_properties[] = { DEFINE_PROP_BOOL("ramfb", VFIOPCIDevice, enable_ramfb, false), DEFINE_PROP_BOOL("use-legacy-x86-rom", VFIOPCIDevice, use_legacy_x86_rom, false), @@ -3933,7 +3933,7 @@ static void vfio_pci_nohotplug_dev_class_init(ObjectClass *klass, { DeviceClass *dc = DEVICE_CLASS(klass); - device_class_set_props(dc, vfio_pci_dev_nohotplug_properties); + device_class_set_props(dc, vfio_pci_nohotplug_properties); dc->hotpluggable = false; object_class_property_set_description(klass, /* 3.1 */ From cc44b39c274b35f1510725f60dd95af59d0bd3fb Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:29 +0100 Subject: [PATCH 0919/1794] vfio/pci.c: rename vfio_pci_nohotplug_dev_class_init() to vfio_pci_nohotplug_class_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the function prefix to match the name of the QOM type. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-22-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 6b3c97c56c9c2..10313edc7ed7e 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3928,7 +3928,7 @@ static const Property vfio_pci_nohotplug_properties[] = { ON_OFF_AUTO_AUTO), }; -static void vfio_pci_nohotplug_dev_class_init(ObjectClass *klass, +static void vfio_pci_nohotplug_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -3953,7 +3953,7 @@ static const TypeInfo vfio_pci_nohotplug_dev_info = { .name = TYPE_VFIO_PCI_NOHOTPLUG, .parent = TYPE_VFIO_PCI, .instance_size = sizeof(VFIOPCIDevice), - .class_init = vfio_pci_nohotplug_dev_class_init, + .class_init = vfio_pci_nohotplug_class_init, }; static void register_vfio_pci_dev_type(void) From 5bdf0db823869a210434b5048d20dce5e686b7aa Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:30 +0100 Subject: [PATCH 0920/1794] vfio/pci.c: rename vfio_pci_nohotplug_dev_info to vfio_pci_nohotplug_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the prefix to match the name of the QOM type. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-23-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 10313edc7ed7e..5b022da19ef86 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3949,7 +3949,7 @@ static void vfio_pci_nohotplug_class_init(ObjectClass *klass, "Controls loading of a legacy VGA BIOS ROM"); } -static const TypeInfo vfio_pci_nohotplug_dev_info = { +static const TypeInfo vfio_pci_nohotplug_info = { .name = TYPE_VFIO_PCI_NOHOTPLUG, .parent = TYPE_VFIO_PCI, .instance_size = sizeof(VFIOPCIDevice), @@ -3971,7 +3971,7 @@ static void register_vfio_pci_dev_type(void) type_register_static(&vfio_pci_device_info); type_register_static(&vfio_pci_info); - type_register_static(&vfio_pci_nohotplug_dev_info); + type_register_static(&vfio_pci_nohotplug_info); } type_init(register_vfio_pci_dev_type) From c833f7a5c66e1264bfaaa389467c5af8d9c49082 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:31 +0100 Subject: [PATCH 0921/1794] vfio-user/pci.c: rename vfio_user_pci_dev_class_init() to vfio_user_pci_class_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the function prefix to match the name of the QOM type. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-24-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index efceae69dee09..e2c5b5744c410 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -446,7 +446,7 @@ static void vfio_user_pci_set_socket(Object *obj, Visitor *v, const char *name, } } -static void vfio_user_pci_dev_class_init(ObjectClass *klass, const void *data) +static void vfio_user_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass); @@ -467,7 +467,7 @@ static const TypeInfo vfio_user_pci_dev_info = { .name = TYPE_VFIO_USER_PCI, .parent = TYPE_VFIO_PCI_DEVICE, .instance_size = sizeof(VFIOUserPCIDevice), - .class_init = vfio_user_pci_dev_class_init, + .class_init = vfio_user_pci_class_init, .instance_init = vfio_user_instance_init, .instance_finalize = vfio_user_instance_finalize, }; From 78f4b77607b41909c294f15ae4e51d87e36aab4f Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:32 +0100 Subject: [PATCH 0922/1794] vfio-user/pci.c: rename vfio_user_pci_dev_properties[] to vfio_user_pci_properties[] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the prefix to match the name of the QOM type. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-25-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index e2c5b5744c410..30f485fdbb12a 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -400,7 +400,7 @@ static void vfio_user_pci_reset(DeviceState *dev) vfio_pci_post_reset(vdev); } -static const Property vfio_user_pci_dev_properties[] = { +static const Property vfio_user_pci_properties[] = { DEFINE_PROP_UINT32("x-pci-vendor-id", VFIOPCIDevice, vendor_id, PCI_ANY_ID), DEFINE_PROP_UINT32("x-pci-device-id", VFIOPCIDevice, @@ -452,7 +452,7 @@ static void vfio_user_pci_class_init(ObjectClass *klass, const void *data) PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass); device_class_set_legacy_reset(dc, vfio_user_pci_reset); - device_class_set_props(dc, vfio_user_pci_dev_properties); + device_class_set_props(dc, vfio_user_pci_properties); object_class_property_add(klass, "socket", "SocketAddress", NULL, vfio_user_pci_set_socket, NULL, NULL); From de837b5cbd489410aab1eda00b4b50e45a85d5d6 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:33 +0100 Subject: [PATCH 0923/1794] vfio-user/pci.c: rename vfio_user_instance_init() to vfio_user_pci_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the more typical naming convention for QOM init() functions, in particular it changes the prefix to match the name of the QOM type. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-26-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index 30f485fdbb12a..52561900fdf00 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -344,7 +344,7 @@ static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp) vfio_pci_put_device(vdev); } -static void vfio_user_instance_init(Object *obj) +static void vfio_user_pci_init(Object *obj) { PCIDevice *pci_dev = PCI_DEVICE(obj); VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(obj); @@ -468,7 +468,7 @@ static const TypeInfo vfio_user_pci_dev_info = { .parent = TYPE_VFIO_PCI_DEVICE, .instance_size = sizeof(VFIOUserPCIDevice), .class_init = vfio_user_pci_class_init, - .instance_init = vfio_user_instance_init, + .instance_init = vfio_user_pci_init, .instance_finalize = vfio_user_instance_finalize, }; From d5447437aef2d326728e0ad1590a5fa50c0b33a3 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:34 +0100 Subject: [PATCH 0924/1794] vfio-user/pci.c: rename vfio_user_instance_finalize() to vfio_user_pci_finalize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the more typical naming convention for QOM finalize() functions, in particular it changes the prefix to match the name of the QOM type. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-27-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index 52561900fdf00..d4f5c7b9d72b4 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -370,7 +370,7 @@ static void vfio_user_pci_init(Object *obj) pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; } -static void vfio_user_instance_finalize(Object *obj) +static void vfio_user_pci_finalize(Object *obj) { VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(obj); VFIODevice *vbasedev = &vdev->vbasedev; @@ -469,7 +469,7 @@ static const TypeInfo vfio_user_pci_dev_info = { .instance_size = sizeof(VFIOUserPCIDevice), .class_init = vfio_user_pci_class_init, .instance_init = vfio_user_pci_init, - .instance_finalize = vfio_user_instance_finalize, + .instance_finalize = vfio_user_pci_finalize, }; static void register_vfio_user_dev_type(void) From d0776b8c60fcc5833fba1d2ce8e0780c429108bc Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:35 +0100 Subject: [PATCH 0925/1794] vfio-user/pci.c: rename vfio_user_pci_dev_info to vfio_user_pci_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the prefix to match the name of the QOM type. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-28-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index d4f5c7b9d72b4..b53ed3b456f9c 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -463,7 +463,7 @@ static void vfio_user_pci_class_init(ObjectClass *klass, const void *data) pdc->realize = vfio_user_pci_realize; } -static const TypeInfo vfio_user_pci_dev_info = { +static const TypeInfo vfio_user_pci_info = { .name = TYPE_VFIO_USER_PCI, .parent = TYPE_VFIO_PCI_DEVICE, .instance_size = sizeof(VFIOUserPCIDevice), @@ -474,7 +474,7 @@ static const TypeInfo vfio_user_pci_dev_info = { static void register_vfio_user_dev_type(void) { - type_register_static(&vfio_user_pci_dev_info); + type_register_static(&vfio_user_pci_info); } - type_init(register_vfio_user_dev_type) +type_init(register_vfio_user_dev_type) From 7c773b4267ae10820ed5e3ec6b15219b39dbcebd Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 25 Sep 2025 12:31:36 +0100 Subject: [PATCH 0926/1794] include/hw/vfio/vfio-device.h: fix include header guard name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The header guard was incorrectly called HW_VFIO_VFIO_COMMON_H instead of HW_VFIO_VFIO_DEVICE_H. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250925113159.1760317-29-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- include/hw/vfio/vfio-device.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index ed19e2e1e5859..7e9aed6d3cd42 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -18,8 +18,8 @@ * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) */ -#ifndef HW_VFIO_VFIO_COMMON_H -#define HW_VFIO_VFIO_COMMON_H +#ifndef HW_VFIO_VFIO_DEVICE_H +#define HW_VFIO_VFIO_DEVICE_H #include "system/memory.h" #include "qemu/queue.h" @@ -288,4 +288,4 @@ void vfio_device_init(VFIODevice *vbasedev, int type, VFIODeviceOps *ops, int vfio_device_get_aw_bits(VFIODevice *vdev); void vfio_kvm_device_close(void); -#endif /* HW_VFIO_VFIO_COMMON_H */ +#endif /* HW_VFIO_VFIO_DEVICE_H */ From 5a449678d6594b70d3ead9ad78b47d76eb95a91e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 22 Sep 2025 10:36:47 +0100 Subject: [PATCH 0927/1794] scripts/ci: add gitlab-failure-analysis script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a script designed to collect data from multiple pipelines and analyse the failure modes they have. By default it will probe the last 3 failed jobs on the staging branch. However this can all be controlled by the CLI: ./scripts/ci/gitlab-failure-analysis --count 2 --branch=testing/next --id 39915562 --status= running pipeline 2028486060, total jobs 125, skipped 5, failed 0, 39742 tests, 0 failed tests success pipeline 2015018135, total jobs 125, skipped 5, failed 0, 49219 tests, 0 failed tests You can also skip failing jobs and just dump the tests: ./scripts/ci/gitlab-failure-analysis --branch= --id 39915562 --status= --skip-jobs --pipeline 1946202491 1919542960 failed pipeline 1946202491, total jobs 127, skipped 5, failed 26, 38742 tests, 278 skipped tests, 2 failed tests Failed test qemu.qemu:qtest+qtest-s390x / qtest-s390x/boot-serial-test, check-system-opensuse, 1 /s390x/boot-serial/s390-ccw-virtio - FATAL-ERROR: Failed to find expected string. Please check '/tmp/qtest-boot-serial-sW77EA3' Failed test qemu.qemu:qtest+qtest-aarch64 / qtest-aarch64/arm-cpu-features, check-system-opensuse, 1 /aarch64/arm/query-cpu-model-expansion - ERROR:../tests/qtest/arm-cpu-features.c:459:test_query_cpu_model_expansion: assertion failed (_error == "The CPU type 'host' requires KVM"): ("The CPU type 'host' requires hardware accelerator" == "The CPU type 'host' requires KVM") failed pipeline 1919542960, total jobs 127, skipped 5, failed 2, 48753 tests, 441 skipped tests, 1 failed tests Failed test qemu.qemu:unit / test-aio, msys2-64bit, 12 /aio/timer/schedule - ERROR:../tests/unit/test-aio.c:413:test_timer_schedule: assertion failed: (aio_poll(ctx, true)) Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-3-alex.bennee@linaro.org> --- scripts/ci/gitlab-failure-analysis | 117 +++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100755 scripts/ci/gitlab-failure-analysis diff --git a/scripts/ci/gitlab-failure-analysis b/scripts/ci/gitlab-failure-analysis new file mode 100755 index 0000000000000..906725be97312 --- /dev/null +++ b/scripts/ci/gitlab-failure-analysis @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +# +# A script to analyse failures in the gitlab pipelines. It requires an +# API key from gitlab with the following permissions: +# - api +# - read_repository +# - read_user +# + +import argparse +import gitlab +import os + +# +# Arguments +# +class NoneForEmptyStringAction(argparse.Action): + def __call__(self, parser, namespace, value, option_string=None): + if value == '': + setattr(namespace, self.dest, None) + else: + setattr(namespace, self.dest, value) + + +parser = argparse.ArgumentParser(description="Analyse failed GitLab CI runs.") + +parser.add_argument("--gitlab", + default="https://gitlab.com", + help="GitLab instance URL (default: https://gitlab.com).") +parser.add_argument("--id", default=11167699, + type=int, + help="GitLab project id (default: 11167699 for qemu-project/qemu)") +parser.add_argument("--token", + default=os.getenv("GITLAB_TOKEN"), + help="Your personal access token with 'api' scope.") +parser.add_argument("--branch", + type=str, + default="staging", + action=NoneForEmptyStringAction, + help="The name of the branch (default: 'staging')") +parser.add_argument("--status", + type=str, + action=NoneForEmptyStringAction, + default="failed", + help="Filter by branch status (default: 'failed')") +parser.add_argument("--count", type=int, + default=3, + help="The number of failed runs to fetch.") +parser.add_argument("--skip-jobs", + default=False, + action='store_true', + help="Skip dumping the job info") +parser.add_argument("--pipeline", type=int, + nargs="+", + default=None, + help="Explicit pipeline ID(s) to fetch.") + + +if __name__ == "__main__": + args = parser.parse_args() + + gl = gitlab.Gitlab(url=args.gitlab, private_token=args.token) + project = gl.projects.get(args.id) + + + pipelines_to_process = [] + + # Use explicit pipeline IDs if provided, otherwise fetch a list + if args.pipeline: + args.count = len(args.pipeline) + for p_id in args.pipeline: + pipelines_to_process.append(project.pipelines.get(p_id)) + else: + # Use an iterator to fetch the pipelines + pipe_iter = project.pipelines.list(iterator=True, + status=args.status, + ref=args.branch) + # Check each failed pipeline + pipelines_to_process = [next(pipe_iter) for _ in range(args.count)] + + # Check each pipeline + for p in pipelines_to_process: + + jobs = p.jobs.list(get_all=True) + failed_jobs = [j for j in jobs if j.status == "failed"] + skipped_jobs = [j for j in jobs if j.status == "skipped"] + manual_jobs = [j for j in jobs if j.status == "manual"] + + trs = p.test_report_summary.get() + total = trs.total["count"] + skipped = trs.total["skipped"] + failed = trs.total["failed"] + + print(f"{p.status} pipeline {p.id}, total jobs {len(jobs)}, " + f"skipped {len(skipped_jobs)}, " + f"failed {len(failed_jobs)}, ", + f"{total} tests, " + f"{skipped} skipped tests, " + f"{failed} failed tests") + + if not args.skip_jobs: + for j in failed_jobs: + print(f" Failed job {j.id}, {j.name}, {j.web_url}") + + # It seems we can only extract failing tests from the full + # test report, maybe there is some way to filter it. + + if failed > 0: + ftr = p.test_report.get() + failed_suites = [s for s in ftr.test_suites if + s["failed_count"] > 0] + for fs in failed_suites: + name = fs["name"] + tests = fs["test_cases"] + failed_tests = [t for t in tests if t["status"] == 'failed'] + for t in failed_tests: + print(f" Failed test {t["classname"]}, {name}, {t["name"]}") From 24e51fc773b63bd222e61226431f3b32ac9915c2 Mon Sep 17 00:00:00 2001 From: Nabih Estefan Date: Mon, 22 Sep 2025 10:36:48 +0100 Subject: [PATCH 0928/1794] checkpatch: Ignore removed lines in license check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running the license check, if we are updating a license it is possible for the checkpatch script to test against old license lines instead of newer ones, since the removal lines appear before the addition lines in a .patch file. Fix this by skipping over lines that start with "-" in the checkpatch script. Signed-off-by: Nabih Estefan Reviewed-by: Daniel P. Berrangé Reviewed-by: Alex Bennée Message-ID: <20250916165928.10048-1-nabihestefan@google.com> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-4-alex.bennee@linaro.org> --- scripts/checkpatch.pl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 91616c974f2c3..40b6955c69863 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1816,7 +1816,8 @@ sub process { } # Check SPDX-License-Identifier references a permitted license - if ($rawline =~ m,SPDX-License-Identifier: (.*?)(\*/)?\s*$,) { + if (($rawline =~ m,SPDX-License-Identifier: (.*?)(\*/)?\s*$,) && + $rawline !~ /^-/) { $fileinfo->{facts}->{sawspdx} = 1; &checkspdx($realfile, $1); } From abc4522d01e79a31494a35decf726ab347ad4db4 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:36:49 +0100 Subject: [PATCH 0929/1794] semihosting/syscalls: compile once in system and per target for user mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We replace target_ulong mechanically by uint64_t. We can't compile (easily) this code once for user, as it relies on various target/function types, so leave it in specific_ss for user mode. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-ID: <20250822150058.18692-2-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-5-alex.bennee@linaro.org> --- include/semihosting/syscalls.h | 30 ++++++------ semihosting/arm-compat-semi.c | 1 + semihosting/meson.build | 4 +- semihosting/syscalls.c | 89 +++++++++++++++++----------------- 4 files changed, 63 insertions(+), 61 deletions(-) diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index 6627c45fb281a..03aa45b7bb957 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -9,7 +9,7 @@ #ifndef SEMIHOSTING_SYSCALLS_H #define SEMIHOSTING_SYSCALLS_H -#include "exec/cpu-defs.h" +#include "exec/vaddr.h" #include "gdbstub/syscalls.h" /* @@ -24,23 +24,23 @@ typedef struct GuestFD GuestFD; void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong fname, target_ulong fname_len, + vaddr fname, uint64_t fname_len, int gdb_flags, int mode); void semihost_sys_close(CPUState *cs, gdb_syscall_complete_cb complete, int fd); void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete, - int fd, target_ulong buf, target_ulong len); + int fd, vaddr buf, uint64_t len); void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, target_ulong buf, target_ulong len); + GuestFD *gf, vaddr buf, uint64_t len); void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete, - int fd, target_ulong buf, target_ulong len); + int fd, vaddr buf, uint64_t len); void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, target_ulong buf, target_ulong len); + GuestFD *gf, vaddr buf, uint64_t len); void semihost_sys_lseek(CPUState *cs, gdb_syscall_complete_cb complete, int fd, int64_t off, int gdb_whence); @@ -50,27 +50,27 @@ void semihost_sys_isatty(CPUState *cs, gdb_syscall_complete_cb complete, void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb, gdb_syscall_complete_cb flen_cb, - int fd, target_ulong fstat_addr); + int fd, vaddr fstat_addr); void semihost_sys_fstat(CPUState *cs, gdb_syscall_complete_cb complete, - int fd, target_ulong addr); + int fd, vaddr addr); void semihost_sys_stat(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong fname, target_ulong fname_len, - target_ulong addr); + vaddr fname, uint64_t fname_len, + vaddr addr); void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong fname, target_ulong fname_len); + vaddr fname, uint64_t fname_len); void semihost_sys_rename(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong oname, target_ulong oname_len, - target_ulong nname, target_ulong nname_len); + vaddr oname, uint64_t oname_len, + vaddr nname, uint64_t nname_len); void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong cmd, target_ulong cmd_len); + vaddr cmd, uint64_t cmd_len); void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong tv_addr, target_ulong tz_addr); + vaddr tv_addr, vaddr tz_addr); void semihost_sys_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, int fd, GIOCondition cond, int timeout); diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index bcd13cd6dfd5a..64ddc749bf804 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -34,6 +34,7 @@ #include "qemu/osdep.h" #include "qemu/timer.h" #include "exec/gdbstub.h" +#include "cpu.h" #include "gdbstub/syscalls.h" #include "semihosting/semihost.h" #include "semihosting/console.h" diff --git a/semihosting/meson.build b/semihosting/meson.build index b1ab2506c6e38..77200a7f27b16 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -1,6 +1,5 @@ specific_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( 'guestfd.c', - 'syscalls.c', )) common_ss.add(when: 'CONFIG_SEMIHOSTING', if_false: files('stubs-all.c')) @@ -9,9 +8,12 @@ system_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( 'config.c', 'console.c', 'uaccess.c', + 'syscalls.c', ), if_false: files( 'stubs-system.c', )) +specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_USER_ONLY'], + if_true: files('syscalls.c')) specific_ss.add(when: ['CONFIG_ARM_COMPATIBLE_SEMIHOSTING'], if_true: files('arm-compat-semi.c')) diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index f6451d9bb0e65..f072d9191936a 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -8,7 +8,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "cpu.h" #include "gdbstub/syscalls.h" #include "semihosting/guestfd.h" #include "semihosting/syscalls.h" @@ -23,7 +22,7 @@ /* * Validate or compute the length of the string (including terminator). */ -static int validate_strlen(CPUState *cs, target_ulong str, target_ulong tlen) +static int validate_strlen(CPUState *cs, uint64_t str, uint64_t tlen) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char c; @@ -52,7 +51,7 @@ static int validate_strlen(CPUState *cs, target_ulong str, target_ulong tlen) } static int validate_lock_user_string(char **pstr, CPUState *cs, - target_ulong tstr, target_ulong tlen) + uint64_t tstr, uint64_t tlen) { int ret = validate_strlen(cs, tstr, tlen); CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); @@ -72,7 +71,7 @@ static int validate_lock_user_string(char **pstr, CPUState *cs, * big-endian. Until we do something with gdb, also produce the * same big-endian result from the host. */ -static int copy_stat_to_user(CPUState *cs, target_ulong addr, +static int copy_stat_to_user(CPUState *cs, uint64_t addr, const struct stat *s) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); @@ -129,7 +128,7 @@ static void gdb_open_cb(CPUState *cs, uint64_t ret, int err) } static void gdb_open(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong fname, target_ulong fname_len, + uint64_t fname, uint64_t fname_len, int gdb_flags, int mode) { int len = validate_strlen(cs, fname, fname_len); @@ -151,14 +150,14 @@ static void gdb_close(CPUState *cs, gdb_syscall_complete_cb complete, } static void gdb_read(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, target_ulong buf, target_ulong len) + GuestFD *gf, uint64_t buf, uint64_t len) { gdb_do_syscall(complete, "read,%x,%lx,%lx", (uint32_t)gf->hostfd, (uint64_t)buf, (uint64_t)len); } static void gdb_write(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, target_ulong buf, target_ulong len) + GuestFD *gf, uint64_t buf, uint64_t len) { gdb_do_syscall(complete, "write,%x,%lx,%lx", (uint32_t)gf->hostfd, (uint64_t)buf, (uint64_t)len); @@ -178,15 +177,15 @@ static void gdb_isatty(CPUState *cs, gdb_syscall_complete_cb complete, } static void gdb_fstat(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, target_ulong addr) + GuestFD *gf, uint64_t addr) { gdb_do_syscall(complete, "fstat,%x,%lx", (uint32_t)gf->hostfd, (uint64_t)addr); } static void gdb_stat(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong fname, target_ulong fname_len, - target_ulong addr) + uint64_t fname, uint64_t fname_len, + uint64_t addr) { int len = validate_strlen(cs, fname, fname_len); if (len < 0) { @@ -199,7 +198,7 @@ static void gdb_stat(CPUState *cs, gdb_syscall_complete_cb complete, } static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong fname, target_ulong fname_len) + uint64_t fname, uint64_t fname_len) { int len = validate_strlen(cs, fname, fname_len); if (len < 0) { @@ -211,8 +210,8 @@ static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete, } static void gdb_rename(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong oname, target_ulong oname_len, - target_ulong nname, target_ulong nname_len) + uint64_t oname, uint64_t oname_len, + uint64_t nname, uint64_t nname_len) { int olen, nlen; @@ -233,7 +232,7 @@ static void gdb_rename(CPUState *cs, gdb_syscall_complete_cb complete, } static void gdb_system(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong cmd, target_ulong cmd_len) + uint64_t cmd, uint64_t cmd_len) { int len = validate_strlen(cs, cmd, cmd_len); if (len < 0) { @@ -245,7 +244,7 @@ static void gdb_system(CPUState *cs, gdb_syscall_complete_cb complete, } static void gdb_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong tv_addr, target_ulong tz_addr) + uint64_t tv_addr, uint64_t tz_addr) { gdb_do_syscall(complete, "gettimeofday,%lx,%lx", (uint64_t)tv_addr, (uint64_t)tz_addr); @@ -256,7 +255,7 @@ static void gdb_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, */ static void host_open(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong fname, target_ulong fname_len, + uint64_t fname, uint64_t fname_len, int gdb_flags, int mode) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); @@ -316,7 +315,7 @@ static void host_close(CPUState *cs, gdb_syscall_complete_cb complete, } static void host_read(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, target_ulong buf, target_ulong len) + GuestFD *gf, uint64_t buf, uint64_t len) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); void *ptr = lock_user(VERIFY_WRITE, buf, len, 0); @@ -337,7 +336,7 @@ static void host_read(CPUState *cs, gdb_syscall_complete_cb complete, } static void host_write(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, target_ulong buf, target_ulong len) + GuestFD *gf, uint64_t buf, uint64_t len) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); void *ptr = lock_user(VERIFY_READ, buf, len, 1); @@ -395,7 +394,7 @@ static void host_flen(CPUState *cs, gdb_syscall_complete_cb complete, } static void host_fstat(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, target_ulong addr) + GuestFD *gf, uint64_t addr) { struct stat buf; int ret; @@ -410,8 +409,8 @@ static void host_fstat(CPUState *cs, gdb_syscall_complete_cb complete, } static void host_stat(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong fname, target_ulong fname_len, - target_ulong addr) + uint64_t fname, uint64_t fname_len, + uint64_t addr) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); struct stat buf; @@ -440,7 +439,7 @@ static void host_stat(CPUState *cs, gdb_syscall_complete_cb complete, } static void host_remove(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong fname, target_ulong fname_len) + uint64_t fname, uint64_t fname_len) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *p; @@ -458,8 +457,8 @@ static void host_remove(CPUState *cs, gdb_syscall_complete_cb complete, } static void host_rename(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong oname, target_ulong oname_len, - target_ulong nname, target_ulong nname_len) + uint64_t oname, uint64_t oname_len, + uint64_t nname, uint64_t nname_len) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *ostr, *nstr; @@ -484,7 +483,7 @@ static void host_rename(CPUState *cs, gdb_syscall_complete_cb complete, } static void host_system(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong cmd, target_ulong cmd_len) + uint64_t cmd, uint64_t cmd_len) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *p; @@ -502,7 +501,7 @@ static void host_system(CPUState *cs, gdb_syscall_complete_cb complete, } static void host_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong tv_addr, target_ulong tz_addr) + uint64_t tv_addr, uint64_t tz_addr) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); struct gdb_timeval *p; @@ -547,10 +546,10 @@ static void host_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, */ static void staticfile_read(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, target_ulong buf, target_ulong len) + GuestFD *gf, uint64_t buf, uint64_t len) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); - target_ulong rest = gf->staticfile.len - gf->staticfile.off; + uint64_t rest = gf->staticfile.len - gf->staticfile.off; void *ptr; if (len > rest) { @@ -605,7 +604,7 @@ static void staticfile_flen(CPUState *cs, gdb_syscall_complete_cb complete, */ static void console_read(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, target_ulong buf, target_ulong len) + GuestFD *gf, uint64_t buf, uint64_t len) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *ptr; @@ -622,7 +621,7 @@ static void console_read(CPUState *cs, gdb_syscall_complete_cb complete, } static void console_write(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, target_ulong buf, target_ulong len) + GuestFD *gf, uint64_t buf, uint64_t len) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *ptr = lock_user(VERIFY_READ, buf, len, 1); @@ -638,7 +637,7 @@ static void console_write(CPUState *cs, gdb_syscall_complete_cb complete, } static void console_fstat(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, target_ulong addr) + GuestFD *gf, uint64_t addr) { static const struct stat tty_buf = { .st_mode = 020666, /* S_IFCHR, ugo+rw */ @@ -683,7 +682,7 @@ static void console_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, */ void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong fname, target_ulong fname_len, + vaddr fname, uint64_t fname_len, int gdb_flags, int mode) { if (use_gdb_syscalls()) { @@ -719,7 +718,7 @@ void semihost_sys_close(CPUState *cs, gdb_syscall_complete_cb complete, int fd) } void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, target_ulong buf, target_ulong len) + GuestFD *gf, vaddr buf, uint64_t len) { /* * Bound length for 64-bit guests on 32-bit hosts, not overflowing ssize_t. @@ -748,7 +747,7 @@ void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete, } void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete, - int fd, target_ulong buf, target_ulong len) + int fd, vaddr buf, uint64_t len) { GuestFD *gf = get_guestfd(fd); @@ -760,7 +759,7 @@ void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete, } void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, target_ulong buf, target_ulong len) + GuestFD *gf, vaddr buf, uint64_t len) { /* * Bound length for 64-bit guests on 32-bit hosts, not overflowing ssize_t. @@ -790,7 +789,7 @@ void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete, } void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete, - int fd, target_ulong buf, target_ulong len) + int fd, vaddr buf, uint64_t len) { GuestFD *gf = get_guestfd(fd); @@ -856,7 +855,7 @@ void semihost_sys_isatty(CPUState *cs, gdb_syscall_complete_cb complete, int fd) void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb, gdb_syscall_complete_cb flen_cb, int fd, - target_ulong fstat_addr) + vaddr fstat_addr) { GuestFD *gf = get_guestfd(fd); @@ -881,7 +880,7 @@ void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb, } void semihost_sys_fstat(CPUState *cs, gdb_syscall_complete_cb complete, - int fd, target_ulong addr) + int fd, vaddr addr) { GuestFD *gf = get_guestfd(fd); @@ -906,8 +905,8 @@ void semihost_sys_fstat(CPUState *cs, gdb_syscall_complete_cb complete, } void semihost_sys_stat(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong fname, target_ulong fname_len, - target_ulong addr) + vaddr fname, uint64_t fname_len, + vaddr addr) { if (use_gdb_syscalls()) { gdb_stat(cs, complete, fname, fname_len, addr); @@ -917,7 +916,7 @@ void semihost_sys_stat(CPUState *cs, gdb_syscall_complete_cb complete, } void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong fname, target_ulong fname_len) + vaddr fname, uint64_t fname_len) { if (use_gdb_syscalls()) { gdb_remove(cs, complete, fname, fname_len); @@ -927,8 +926,8 @@ void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete, } void semihost_sys_rename(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong oname, target_ulong oname_len, - target_ulong nname, target_ulong nname_len) + vaddr oname, uint64_t oname_len, + vaddr nname, uint64_t nname_len) { if (use_gdb_syscalls()) { gdb_rename(cs, complete, oname, oname_len, nname, nname_len); @@ -938,7 +937,7 @@ void semihost_sys_rename(CPUState *cs, gdb_syscall_complete_cb complete, } void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong cmd, target_ulong cmd_len) + vaddr cmd, uint64_t cmd_len) { if (use_gdb_syscalls()) { gdb_system(cs, complete, cmd, cmd_len); @@ -948,7 +947,7 @@ void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete, } void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, - target_ulong tv_addr, target_ulong tz_addr) + vaddr tv_addr, vaddr tz_addr) { if (use_gdb_syscalls()) { gdb_gettimeofday(cs, complete, tv_addr, tz_addr); From 664172404aff3ba11f8ab1fc348d3c860c45f5d3 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:36:50 +0100 Subject: [PATCH 0930/1794] semihosting/syscalls: replace uint64_t with vaddr where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-ID: <20250822150058.18692-3-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-6-alex.bennee@linaro.org> --- semihosting/syscalls.c | 78 +++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index f072d9191936a..20f155f869a26 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -22,7 +22,7 @@ /* * Validate or compute the length of the string (including terminator). */ -static int validate_strlen(CPUState *cs, uint64_t str, uint64_t tlen) +static int validate_strlen(CPUState *cs, vaddr str, uint64_t tlen) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char c; @@ -51,7 +51,7 @@ static int validate_strlen(CPUState *cs, uint64_t str, uint64_t tlen) } static int validate_lock_user_string(char **pstr, CPUState *cs, - uint64_t tstr, uint64_t tlen) + vaddr tstr, uint64_t tlen) { int ret = validate_strlen(cs, tstr, tlen); CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); @@ -71,7 +71,7 @@ static int validate_lock_user_string(char **pstr, CPUState *cs, * big-endian. Until we do something with gdb, also produce the * same big-endian result from the host. */ -static int copy_stat_to_user(CPUState *cs, uint64_t addr, +static int copy_stat_to_user(CPUState *cs, vaddr addr, const struct stat *s) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); @@ -128,7 +128,7 @@ static void gdb_open_cb(CPUState *cs, uint64_t ret, int err) } static void gdb_open(CPUState *cs, gdb_syscall_complete_cb complete, - uint64_t fname, uint64_t fname_len, + vaddr fname, uint64_t fname_len, int gdb_flags, int mode) { int len = validate_strlen(cs, fname, fname_len); @@ -139,7 +139,7 @@ static void gdb_open(CPUState *cs, gdb_syscall_complete_cb complete, gdb_open_complete = complete; gdb_do_syscall(gdb_open_cb, "open,%s,%x,%x", - (uint64_t)fname, (uint32_t)len, + (vaddr)fname, (uint32_t)len, (uint32_t)gdb_flags, (uint32_t)mode); } @@ -150,17 +150,17 @@ static void gdb_close(CPUState *cs, gdb_syscall_complete_cb complete, } static void gdb_read(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, uint64_t buf, uint64_t len) + GuestFD *gf, vaddr buf, uint64_t len) { gdb_do_syscall(complete, "read,%x,%lx,%lx", - (uint32_t)gf->hostfd, (uint64_t)buf, (uint64_t)len); + (uint32_t)gf->hostfd, (vaddr)buf, (uint64_t)len); } static void gdb_write(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, uint64_t buf, uint64_t len) + GuestFD *gf, vaddr buf, uint64_t len) { gdb_do_syscall(complete, "write,%x,%lx,%lx", - (uint32_t)gf->hostfd, (uint64_t)buf, (uint64_t)len); + (uint32_t)gf->hostfd, (vaddr)buf, (uint64_t)len); } static void gdb_lseek(CPUState *cs, gdb_syscall_complete_cb complete, @@ -177,15 +177,15 @@ static void gdb_isatty(CPUState *cs, gdb_syscall_complete_cb complete, } static void gdb_fstat(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, uint64_t addr) + GuestFD *gf, vaddr addr) { gdb_do_syscall(complete, "fstat,%x,%lx", - (uint32_t)gf->hostfd, (uint64_t)addr); + (uint32_t)gf->hostfd, (vaddr)addr); } static void gdb_stat(CPUState *cs, gdb_syscall_complete_cb complete, - uint64_t fname, uint64_t fname_len, - uint64_t addr) + vaddr fname, uint64_t fname_len, + vaddr addr) { int len = validate_strlen(cs, fname, fname_len); if (len < 0) { @@ -194,11 +194,11 @@ static void gdb_stat(CPUState *cs, gdb_syscall_complete_cb complete, } gdb_do_syscall(complete, "stat,%s,%lx", - (uint64_t)fname, (uint32_t)len, (uint64_t)addr); + (vaddr)fname, (uint32_t)len, (vaddr)addr); } static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete, - uint64_t fname, uint64_t fname_len) + vaddr fname, uint64_t fname_len) { int len = validate_strlen(cs, fname, fname_len); if (len < 0) { @@ -206,12 +206,12 @@ static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete, return; } - gdb_do_syscall(complete, "unlink,%s", (uint64_t)fname, (uint32_t)len); + gdb_do_syscall(complete, "unlink,%s", (vaddr)fname, (uint32_t)len); } static void gdb_rename(CPUState *cs, gdb_syscall_complete_cb complete, - uint64_t oname, uint64_t oname_len, - uint64_t nname, uint64_t nname_len) + vaddr oname, uint64_t oname_len, + vaddr nname, uint64_t nname_len) { int olen, nlen; @@ -227,12 +227,12 @@ static void gdb_rename(CPUState *cs, gdb_syscall_complete_cb complete, } gdb_do_syscall(complete, "rename,%s,%s", - (uint64_t)oname, (uint32_t)olen, - (uint64_t)nname, (uint32_t)nlen); + (vaddr)oname, (uint32_t)olen, + (vaddr)nname, (uint32_t)nlen); } static void gdb_system(CPUState *cs, gdb_syscall_complete_cb complete, - uint64_t cmd, uint64_t cmd_len) + vaddr cmd, uint64_t cmd_len) { int len = validate_strlen(cs, cmd, cmd_len); if (len < 0) { @@ -240,14 +240,14 @@ static void gdb_system(CPUState *cs, gdb_syscall_complete_cb complete, return; } - gdb_do_syscall(complete, "system,%s", (uint64_t)cmd, (uint32_t)len); + gdb_do_syscall(complete, "system,%s", (vaddr)cmd, (uint32_t)len); } static void gdb_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, - uint64_t tv_addr, uint64_t tz_addr) + vaddr tv_addr, vaddr tz_addr) { gdb_do_syscall(complete, "gettimeofday,%lx,%lx", - (uint64_t)tv_addr, (uint64_t)tz_addr); + (vaddr)tv_addr, (vaddr)tz_addr); } /* @@ -255,7 +255,7 @@ static void gdb_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, */ static void host_open(CPUState *cs, gdb_syscall_complete_cb complete, - uint64_t fname, uint64_t fname_len, + vaddr fname, uint64_t fname_len, int gdb_flags, int mode) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); @@ -315,7 +315,7 @@ static void host_close(CPUState *cs, gdb_syscall_complete_cb complete, } static void host_read(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, uint64_t buf, uint64_t len) + GuestFD *gf, vaddr buf, uint64_t len) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); void *ptr = lock_user(VERIFY_WRITE, buf, len, 0); @@ -336,7 +336,7 @@ static void host_read(CPUState *cs, gdb_syscall_complete_cb complete, } static void host_write(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, uint64_t buf, uint64_t len) + GuestFD *gf, vaddr buf, uint64_t len) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); void *ptr = lock_user(VERIFY_READ, buf, len, 1); @@ -394,7 +394,7 @@ static void host_flen(CPUState *cs, gdb_syscall_complete_cb complete, } static void host_fstat(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, uint64_t addr) + GuestFD *gf, vaddr addr) { struct stat buf; int ret; @@ -409,8 +409,8 @@ static void host_fstat(CPUState *cs, gdb_syscall_complete_cb complete, } static void host_stat(CPUState *cs, gdb_syscall_complete_cb complete, - uint64_t fname, uint64_t fname_len, - uint64_t addr) + vaddr fname, uint64_t fname_len, + vaddr addr) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); struct stat buf; @@ -439,7 +439,7 @@ static void host_stat(CPUState *cs, gdb_syscall_complete_cb complete, } static void host_remove(CPUState *cs, gdb_syscall_complete_cb complete, - uint64_t fname, uint64_t fname_len) + vaddr fname, uint64_t fname_len) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *p; @@ -457,8 +457,8 @@ static void host_remove(CPUState *cs, gdb_syscall_complete_cb complete, } static void host_rename(CPUState *cs, gdb_syscall_complete_cb complete, - uint64_t oname, uint64_t oname_len, - uint64_t nname, uint64_t nname_len) + vaddr oname, uint64_t oname_len, + vaddr nname, uint64_t nname_len) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *ostr, *nstr; @@ -483,7 +483,7 @@ static void host_rename(CPUState *cs, gdb_syscall_complete_cb complete, } static void host_system(CPUState *cs, gdb_syscall_complete_cb complete, - uint64_t cmd, uint64_t cmd_len) + vaddr cmd, uint64_t cmd_len) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *p; @@ -501,7 +501,7 @@ static void host_system(CPUState *cs, gdb_syscall_complete_cb complete, } static void host_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, - uint64_t tv_addr, uint64_t tz_addr) + vaddr tv_addr, vaddr tz_addr) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); struct gdb_timeval *p; @@ -546,7 +546,7 @@ static void host_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, */ static void staticfile_read(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, uint64_t buf, uint64_t len) + GuestFD *gf, vaddr buf, uint64_t len) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); uint64_t rest = gf->staticfile.len - gf->staticfile.off; @@ -604,7 +604,7 @@ static void staticfile_flen(CPUState *cs, gdb_syscall_complete_cb complete, */ static void console_read(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, uint64_t buf, uint64_t len) + GuestFD *gf, vaddr buf, uint64_t len) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *ptr; @@ -621,7 +621,7 @@ static void console_read(CPUState *cs, gdb_syscall_complete_cb complete, } static void console_write(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, uint64_t buf, uint64_t len) + GuestFD *gf, vaddr buf, uint64_t len) { CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *ptr = lock_user(VERIFY_READ, buf, len, 1); @@ -637,7 +637,7 @@ static void console_write(CPUState *cs, gdb_syscall_complete_cb complete, } static void console_fstat(CPUState *cs, gdb_syscall_complete_cb complete, - GuestFD *gf, uint64_t addr) + GuestFD *gf, vaddr addr) { static const struct stat tty_buf = { .st_mode = 020666, /* S_IFCHR, ugo+rw */ From d8028d4ede536bbea82eab8bdebd753378d58b68 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:36:51 +0100 Subject: [PATCH 0931/1794] semihosting/guestfd: compile once for system/user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We move relevant code to semihosting/arm-compat-semi.c, and add functions to query CONFIG_ARM_COMPATIBLE_SEMIHOSTING at runtime. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-ID: <20250822150058.18692-4-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-7-alex.bennee@linaro.org> --- include/semihosting/guestfd.h | 7 ------- include/semihosting/semihost.h | 2 ++ semihosting/arm-compat-semi-stub.c | 19 +++++++++++++++++++ semihosting/arm-compat-semi.c | 26 ++++++++++++++++++++++++++ semihosting/guestfd.c | 26 +++++--------------------- semihosting/meson.build | 11 ++++++----- 6 files changed, 58 insertions(+), 33 deletions(-) create mode 100644 semihosting/arm-compat-semi-stub.c diff --git a/include/semihosting/guestfd.h b/include/semihosting/guestfd.h index 3d426fedab390..a7ea1041ea0b6 100644 --- a/include/semihosting/guestfd.h +++ b/include/semihosting/guestfd.h @@ -35,13 +35,6 @@ typedef struct GuestFD { }; } GuestFD; -/* - * For ARM semihosting, we have a separate structure for routing - * data for the console which is outside the guest fd address space. - */ -extern GuestFD console_in_gf; -extern GuestFD console_out_gf; - /** * alloc_guestfd: * diff --git a/include/semihosting/semihost.h b/include/semihosting/semihost.h index b03e6375787f7..231dc8903955d 100644 --- a/include/semihosting/semihost.h +++ b/include/semihosting/semihost.h @@ -33,6 +33,8 @@ typedef enum SemihostingTarget { * Return true if guest code is allowed to make semihosting calls. */ bool semihosting_enabled(bool is_user); +bool semihosting_arm_compatible(void); +void semihosting_arm_compatible_init(void); SemihostingTarget semihosting_get_target(void); const char *semihosting_get_arg(int i); diff --git a/semihosting/arm-compat-semi-stub.c b/semihosting/arm-compat-semi-stub.c new file mode 100644 index 0000000000000..bfa3681e2676d --- /dev/null +++ b/semihosting/arm-compat-semi-stub.c @@ -0,0 +1,19 @@ +/* + * Stubs for platforms different from ARM + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "semihosting/semihost.h" +#include + +bool semihosting_arm_compatible(void) +{ + return false; +} + +void semihosting_arm_compatible_init(void) +{ + g_assert_not_reached(); +} diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 64ddc749bf804..a239cfc5a9d3e 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -101,6 +101,13 @@ static int gdb_open_modeflags[12] = { GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND, }; +/* + * For ARM semihosting, we have a separate structure for routing + * data for the console which is outside the guest fd address space. + */ +static GuestFD console_in_gf; +static GuestFD console_out_gf; + #ifndef CONFIG_USER_ONLY /** @@ -353,6 +360,25 @@ static const uint8_t featurefile_data[] = { SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */ }; +bool semihosting_arm_compatible(void) +{ + return true; +} + +void semihosting_arm_compatible_init(void) +{ + /* For ARM-compat, the console is in a separate namespace. */ + if (use_gdb_syscalls()) { + console_in_gf.type = GuestFDGDB; + console_in_gf.hostfd = 0; + console_out_gf.type = GuestFDGDB; + console_out_gf.hostfd = 2; + } else { + console_in_gf.type = GuestFDConsole; + console_out_gf.type = GuestFDConsole; + } +} + /* * Do a semihosting call. * diff --git a/semihosting/guestfd.c b/semihosting/guestfd.c index d3241434c516f..e8f236c690c44 100644 --- a/semihosting/guestfd.c +++ b/semihosting/guestfd.c @@ -12,35 +12,20 @@ #include "gdbstub/syscalls.h" #include "semihosting/semihost.h" #include "semihosting/guestfd.h" -#ifndef CONFIG_USER_ONLY -#include CONFIG_DEVICES -#endif static GArray *guestfd_array; -#ifdef CONFIG_ARM_COMPATIBLE_SEMIHOSTING -GuestFD console_in_gf; -GuestFD console_out_gf; -#endif - void qemu_semihosting_guestfd_init(void) { /* New entries zero-initialized, i.e. type GuestFDUnused */ guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD)); -#ifdef CONFIG_ARM_COMPATIBLE_SEMIHOSTING - /* For ARM-compat, the console is in a separate namespace. */ - if (use_gdb_syscalls()) { - console_in_gf.type = GuestFDGDB; - console_in_gf.hostfd = 0; - console_out_gf.type = GuestFDGDB; - console_out_gf.hostfd = 2; - } else { - console_in_gf.type = GuestFDConsole; - console_out_gf.type = GuestFDConsole; + if (semihosting_arm_compatible()) { + semihosting_arm_compatible_init(); + return; } -#else - /* Otherwise, the stdio file descriptors apply. */ + + /* Out of ARM, the stdio file descriptors apply. */ guestfd_array = g_array_set_size(guestfd_array, 3); #ifndef CONFIG_USER_ONLY if (!use_gdb_syscalls()) { @@ -54,7 +39,6 @@ void qemu_semihosting_guestfd_init(void) associate_guestfd(0, 0); associate_guestfd(1, 1); associate_guestfd(2, 2); -#endif } /* diff --git a/semihosting/meson.build b/semihosting/meson.build index 77200a7f27b16..bb0db323937f0 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -1,17 +1,18 @@ -specific_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( - 'guestfd.c', -)) - common_ss.add(when: 'CONFIG_SEMIHOSTING', if_false: files('stubs-all.c')) -user_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files('user.c')) +user_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( + 'user.c', + 'guestfd.c')) system_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( 'config.c', 'console.c', + 'guestfd.c', 'uaccess.c', 'syscalls.c', ), if_false: files( 'stubs-system.c', )) +system_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING', + if_false: files('arm-compat-semi-stub.c')) specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_USER_ONLY'], if_true: files('syscalls.c')) From a92e151bd0d8c0dedbfb2d301eb31aaac94a1fb8 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:36:52 +0100 Subject: [PATCH 0932/1794] semihosting/arm-compat-semi: change common_semi_sys_exit_extended MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now check only is sys_exit is extended. This allows to break dependency to TARGET_SYS_EXIT_EXTENDED which will not be available anymore from this code. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-ID: <20250822150058.18692-5-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-8-alex.bennee@linaro.org> --- semihosting/arm-compat-semi.c | 3 ++- target/arm/common-semi-target.h | 4 ++-- target/riscv/common-semi-target.h | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index a239cfc5a9d3e..26263a06b7a90 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -755,7 +755,8 @@ void do_common_semihosting(CPUState *cs) { uint32_t ret; - if (common_semi_sys_exit_extended(cs, nr)) { + if (nr == TARGET_SYS_EXIT_EXTENDED || + common_semi_sys_exit_is_extended(cs)) { /* * The A64 version of SYS_EXIT takes a parameter block, * so the application-exit type can return a subcode which diff --git a/target/arm/common-semi-target.h b/target/arm/common-semi-target.h index da51f2d7f540d..7bb442f24ca7a 100644 --- a/target/arm/common-semi-target.h +++ b/target/arm/common-semi-target.h @@ -34,9 +34,9 @@ static inline void common_semi_set_ret(CPUState *cs, target_ulong ret) } } -static inline bool common_semi_sys_exit_extended(CPUState *cs, int nr) +static inline bool common_semi_sys_exit_is_extended(CPUState *cs) { - return nr == TARGET_SYS_EXIT_EXTENDED || is_a64(cpu_env(cs)); + return is_a64(cpu_env(cs)); } static inline bool is_64bit_semihosting(CPUArchState *env) diff --git a/target/riscv/common-semi-target.h b/target/riscv/common-semi-target.h index 7c8a59e0cc3cd..ba40e794dcc13 100644 --- a/target/riscv/common-semi-target.h +++ b/target/riscv/common-semi-target.h @@ -25,9 +25,9 @@ static inline void common_semi_set_ret(CPUState *cs, target_ulong ret) env->gpr[xA0] = ret; } -static inline bool common_semi_sys_exit_extended(CPUState *cs, int nr) +static inline bool common_semi_sys_exit_is_extended(CPUState *cs) { - return (nr == TARGET_SYS_EXIT_EXTENDED || sizeof(target_ulong) == 8); + return sizeof(target_ulong) == 8; } static inline bool is_64bit_semihosting(CPUArchState *env) From 2131f0dcdf97935b1c412e61da51f2de323bfa9c Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:36:53 +0100 Subject: [PATCH 0933/1794] target/riscv/common-semi-target: remove sizeof(target_ulong) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only riscv64 extends SYS_EXIT, similar to aarch64. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-ID: <20250822150058.18692-6-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-9-alex.bennee@linaro.org> --- target/riscv/common-semi-target.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/riscv/common-semi-target.h b/target/riscv/common-semi-target.h index ba40e794dcc13..7e6ea8da02c10 100644 --- a/target/riscv/common-semi-target.h +++ b/target/riscv/common-semi-target.h @@ -25,14 +25,14 @@ static inline void common_semi_set_ret(CPUState *cs, target_ulong ret) env->gpr[xA0] = ret; } -static inline bool common_semi_sys_exit_is_extended(CPUState *cs) +static inline bool is_64bit_semihosting(CPUArchState *env) { - return sizeof(target_ulong) == 8; + return riscv_cpu_mxl(env) != MXL_RV32; } -static inline bool is_64bit_semihosting(CPUArchState *env) +static inline bool common_semi_sys_exit_is_extended(CPUState *cs) { - return riscv_cpu_mxl(env) != MXL_RV32; + return is_64bit_semihosting(cpu_env(cs)); } static inline target_ulong common_semi_stack_bottom(CPUState *cs) From 632308c5912aab0dfad8bc1a1cbe7b37a5e1aeec Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:36:54 +0100 Subject: [PATCH 0934/1794] target/{arm, riscv}/common-semi-target: eradicate target_ulong MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We replace mechanically with uint64_t. There is no semantic change, and allows us to extract a proper API from this set of functions. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-ID: <20250822150058.18692-7-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-10-alex.bennee@linaro.org> --- target/arm/common-semi-target.h | 6 +++--- target/riscv/common-semi-target.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target/arm/common-semi-target.h b/target/arm/common-semi-target.h index 7bb442f24ca7a..6775a270aaaf5 100644 --- a/target/arm/common-semi-target.h +++ b/target/arm/common-semi-target.h @@ -12,7 +12,7 @@ #include "target/arm/cpu-qom.h" -static inline target_ulong common_semi_arg(CPUState *cs, int argno) +static inline uint64_t common_semi_arg(CPUState *cs, int argno) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; @@ -23,7 +23,7 @@ static inline target_ulong common_semi_arg(CPUState *cs, int argno) } } -static inline void common_semi_set_ret(CPUState *cs, target_ulong ret) +static inline void common_semi_set_ret(CPUState *cs, uint64_t ret) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; @@ -44,7 +44,7 @@ static inline bool is_64bit_semihosting(CPUArchState *env) return is_a64(env); } -static inline target_ulong common_semi_stack_bottom(CPUState *cs) +static inline uint64_t common_semi_stack_bottom(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; diff --git a/target/riscv/common-semi-target.h b/target/riscv/common-semi-target.h index 7e6ea8da02c10..663dedfdad22e 100644 --- a/target/riscv/common-semi-target.h +++ b/target/riscv/common-semi-target.h @@ -11,14 +11,14 @@ #ifndef TARGET_RISCV_COMMON_SEMI_TARGET_H #define TARGET_RISCV_COMMON_SEMI_TARGET_H -static inline target_ulong common_semi_arg(CPUState *cs, int argno) +static inline uint64_t common_semi_arg(CPUState *cs, int argno) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; return env->gpr[xA0 + argno]; } -static inline void common_semi_set_ret(CPUState *cs, target_ulong ret) +static inline void common_semi_set_ret(CPUState *cs, uint64_t ret) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; @@ -35,7 +35,7 @@ static inline bool common_semi_sys_exit_is_extended(CPUState *cs) return is_64bit_semihosting(cpu_env(cs)); } -static inline target_ulong common_semi_stack_bottom(CPUState *cs) +static inline uint64_t common_semi_stack_bottom(CPUState *cs) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; From 9e65902904cdad6d4b131b1791205ae4138c99eb Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:36:55 +0100 Subject: [PATCH 0935/1794] include/semihosting/common-semi: extract common_semi API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We transform target/{arm,riscv}/common-semi-target.h headers to proper compilation units, and use them in arm-compat-semi.c. This way, we can include only the declaration header (which is target agnostic), and selectively link the appropriate implementation based on current target. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-ID: <20250822150058.18692-8-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-11-alex.bennee@linaro.org> --- include/semihosting/common-semi.h | 6 ++++++ semihosting/arm-compat-semi.c | 3 +-- ...mon-semi-target.h => common-semi-target.c} | 20 +++++++++---------- target/arm/meson.build | 4 ++++ ...mon-semi-target.h => common-semi-target.c} | 19 +++++++++--------- target/riscv/meson.build | 4 ++++ 6 files changed, 33 insertions(+), 23 deletions(-) rename target/arm/{common-semi-target.h => common-semi-target.c} (64%) rename target/riscv/{common-semi-target.h => common-semi-target.c} (59%) diff --git a/include/semihosting/common-semi.h b/include/semihosting/common-semi.h index 0a91db7c4149a..aa511a46f42c8 100644 --- a/include/semihosting/common-semi.h +++ b/include/semihosting/common-semi.h @@ -35,5 +35,11 @@ #define COMMON_SEMI_H void do_common_semihosting(CPUState *cs); +uint64_t common_semi_arg(CPUState *cs, int argno); +void common_semi_set_ret(CPUState *cs, uint64_t ret); +bool is_64bit_semihosting(CPUArchState *env); +bool common_semi_sys_exit_is_extended(CPUState *cs); +uint64_t common_semi_stack_bottom(CPUState *cs); +bool common_semi_has_synccache(CPUArchState *env); #endif /* COMMON_SEMI_H */ diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 26263a06b7a90..604a69e364619 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -174,8 +174,7 @@ static LayoutInfo common_semi_find_bases(CPUState *cs) #endif -#include "cpu.h" -#include "common-semi-target.h" +#include "semihosting/common-semi.h" /* * Read the input value from the argument block; fail the semihosting diff --git a/target/arm/common-semi-target.h b/target/arm/common-semi-target.c similarity index 64% rename from target/arm/common-semi-target.h rename to target/arm/common-semi-target.c index 6775a270aaaf5..2b77ce9c17b65 100644 --- a/target/arm/common-semi-target.h +++ b/target/arm/common-semi-target.c @@ -7,12 +7,12 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef TARGET_ARM_COMMON_SEMI_TARGET_H -#define TARGET_ARM_COMMON_SEMI_TARGET_H - +#include "qemu/osdep.h" +#include "cpu.h" +#include "semihosting/common-semi.h" #include "target/arm/cpu-qom.h" -static inline uint64_t common_semi_arg(CPUState *cs, int argno) +uint64_t common_semi_arg(CPUState *cs, int argno) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; @@ -23,7 +23,7 @@ static inline uint64_t common_semi_arg(CPUState *cs, int argno) } } -static inline void common_semi_set_ret(CPUState *cs, uint64_t ret) +void common_semi_set_ret(CPUState *cs, uint64_t ret) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; @@ -34,27 +34,25 @@ static inline void common_semi_set_ret(CPUState *cs, uint64_t ret) } } -static inline bool common_semi_sys_exit_is_extended(CPUState *cs) +bool common_semi_sys_exit_is_extended(CPUState *cs) { return is_a64(cpu_env(cs)); } -static inline bool is_64bit_semihosting(CPUArchState *env) +bool is_64bit_semihosting(CPUArchState *env) { return is_a64(env); } -static inline uint64_t common_semi_stack_bottom(CPUState *cs) +uint64_t common_semi_stack_bottom(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; return is_a64(env) ? env->xregs[31] : env->regs[13]; } -static inline bool common_semi_has_synccache(CPUArchState *env) +bool common_semi_has_synccache(CPUArchState *env) { /* Ok for A64, invalid for A32/T32 */ return is_a64(env); } - -#endif diff --git a/target/arm/meson.build b/target/arm/meson.build index 914f1498fc54d..638ee62525fb7 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -28,12 +28,16 @@ arm_user_ss.add(files( 'vfp_fpscr.c', 'el2-stubs.c', )) +arm_user_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING', + if_true: files('common-semi-target.c')) arm_common_system_ss.add(files('cpu.c')) arm_common_system_ss.add(when: 'TARGET_AARCH64', if_false: files( 'cpu32-stubs.c')) arm_common_system_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) arm_common_system_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c')) +arm_common_system_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING', + if_true: files('common-semi-target.c')) arm_common_system_ss.add(files( 'arch_dump.c', 'arm-powerctl.c', diff --git a/target/riscv/common-semi-target.h b/target/riscv/common-semi-target.c similarity index 59% rename from target/riscv/common-semi-target.h rename to target/riscv/common-semi-target.c index 663dedfdad22e..aeaeb88d5360c 100644 --- a/target/riscv/common-semi-target.h +++ b/target/riscv/common-semi-target.c @@ -8,43 +8,42 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef TARGET_RISCV_COMMON_SEMI_TARGET_H -#define TARGET_RISCV_COMMON_SEMI_TARGET_H +#include "qemu/osdep.h" +#include "cpu.h" +#include "semihosting/common-semi.h" -static inline uint64_t common_semi_arg(CPUState *cs, int argno) +uint64_t common_semi_arg(CPUState *cs, int argno) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; return env->gpr[xA0 + argno]; } -static inline void common_semi_set_ret(CPUState *cs, uint64_t ret) +void common_semi_set_ret(CPUState *cs, uint64_t ret) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; env->gpr[xA0] = ret; } -static inline bool is_64bit_semihosting(CPUArchState *env) +bool is_64bit_semihosting(CPUArchState *env) { return riscv_cpu_mxl(env) != MXL_RV32; } -static inline bool common_semi_sys_exit_is_extended(CPUState *cs) +bool common_semi_sys_exit_is_extended(CPUState *cs) { return is_64bit_semihosting(cpu_env(cs)); } -static inline uint64_t common_semi_stack_bottom(CPUState *cs) +uint64_t common_semi_stack_bottom(CPUState *cs) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; return env->gpr[xSP]; } -static inline bool common_semi_has_synccache(CPUArchState *env) +bool common_semi_has_synccache(CPUArchState *env) { return true; } - -#endif diff --git a/target/riscv/meson.build b/target/riscv/meson.build index a4bd61e52a940..fdefe88ccdd3a 100644 --- a/target/riscv/meson.build +++ b/target/riscv/meson.build @@ -8,6 +8,10 @@ gen = [ riscv_ss = ss.source_set() riscv_ss.add(gen) + +riscv_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING', + if_true: files('common-semi-target.c')) + riscv_ss.add(files( 'cpu.c', 'cpu_helper.c', From feb49fa79bdbd96d9f3aa604c9217f70d2c36b8c Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:36:56 +0100 Subject: [PATCH 0936/1794] semihosting/arm-compat-semi: eradicate sizeof(target_ulong) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No semantic change. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-ID: <20250822150058.18692-9-pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-12-alex.bennee@linaro.org> --- semihosting/arm-compat-semi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 604a69e364619..6725dcf4ba775 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -786,7 +786,7 @@ void do_common_semihosting(CPUState *cs) case TARGET_SYS_ELAPSED: elapsed = get_clock() - clock_start; - if (sizeof(target_ulong) == 8) { + if (is_64bit_semihosting(env)) { if (SET_ARG(0, elapsed)) { goto do_fault; } From 6dfbf9b6cfe461ac60c8c9ee7326fb423faa7987 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:36:57 +0100 Subject: [PATCH 0937/1794] semihosting/arm-compat-semi: replace target_ulong MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace with vaddr or uint64_t where appropriate. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-ID: <20250822150058.18692-10-pierrick.bouvier@linaro.org> [AJB: tweak commit message] Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-13-alex.bennee@linaro.org> --- semihosting/arm-compat-semi.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 6725dcf4ba775..29cdab66f7366 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -123,7 +123,7 @@ static GuestFD console_out_gf; */ typedef struct LayoutInfo { - target_ulong rambase; + vaddr rambase; size_t ramsize; hwaddr heapbase; hwaddr heaplimit; @@ -214,7 +214,7 @@ static LayoutInfo common_semi_find_bases(CPUState *cs) * global, and we assume that the guest takes care of avoiding any races. */ #ifndef CONFIG_USER_ONLY -static target_ulong syscall_err; +static uint64_t syscall_err; #include "semihosting/uaccess.h" #endif @@ -260,8 +260,8 @@ static void common_semi_rw_cb(CPUState *cs, uint64_t ret, int err) { /* Recover the original length from the third argument. */ CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); - target_ulong args = common_semi_arg(cs, 1); - target_ulong arg2; + uint64_t args = common_semi_arg(cs, 1); + uint64_t arg2; GET_ARG(2); if (err) { @@ -300,9 +300,9 @@ static void common_semi_seek_cb(CPUState *cs, uint64_t ret, int err) * is defined by GDB's remote protocol and is not target-specific.) * We put this on the guest's stack just below SP. */ -static target_ulong common_semi_flen_buf(CPUState *cs) +static uint64_t common_semi_flen_buf(CPUState *cs) { - target_ulong sp = common_semi_stack_bottom(cs); + vaddr sp = common_semi_stack_bottom(cs); return sp - 64; } @@ -389,9 +389,9 @@ void semihosting_arm_compatible_init(void) void do_common_semihosting(CPUState *cs) { CPUArchState *env = cpu_env(cs); - target_ulong args; - target_ulong arg0, arg1, arg2, arg3; - target_ulong ul_ret; + uint64_t args; + uint64_t arg0, arg1, arg2, arg3; + uint64_t ul_ret; char * s; int nr; int64_t elapsed; @@ -462,7 +462,7 @@ void do_common_semihosting(CPUState *cs) case TARGET_SYS_WRITEC: /* - * FIXME: the byte to be written is in a target_ulong slot, + * FIXME: the byte to be written is in a uint64_t slot, * which means this is wrong for a big-endian guest. */ semihost_sys_write_gf(cs, common_semi_dead_cb, @@ -688,7 +688,7 @@ void do_common_semihosting(CPUState *cs) case TARGET_SYS_HEAPINFO: { - target_ulong retvals[4]; + uint64_t retvals[4]; int i; #ifdef CONFIG_USER_ONLY TaskState *ts = get_task_state(cs); From ab385d594c5189dd3182e902053276863dda6032 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:36:58 +0100 Subject: [PATCH 0938/1794] semihosting/arm-compat-semi: eradicate target_long MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We use int64_t or int32_t depending on ret size. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-ID: <20250822150058.18692-11-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-14-alex.bennee@linaro.org> --- semihosting/arm-compat-semi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 29cdab66f7366..3c9192d8684f1 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -501,10 +501,13 @@ void do_common_semihosting(CPUState *cs) break; case TARGET_SYS_ISERROR: + { GET_ARG(0); - common_semi_set_ret(cs, (target_long)arg0 < 0); + bool ret = is_64bit_semihosting(env) ? + (int64_t)arg0 < 0 : (int32_t)arg0 < 0; + common_semi_set_ret(cs, ret); break; - + } case TARGET_SYS_ISTTY: GET_ARG(0); semihost_sys_isatty(cs, common_semi_istty_cb, arg0); From d3fe75400746dfd102cf317ca9c2da744f580536 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:36:59 +0100 Subject: [PATCH 0939/1794] semihosting/arm-compat-semi: remove dependency on cpu.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-ID: <20250822150058.18692-12-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-15-alex.bennee@linaro.org> --- semihosting/arm-compat-semi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 3c9192d8684f1..61001267965ad 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -34,7 +34,6 @@ #include "qemu/osdep.h" #include "qemu/timer.h" #include "exec/gdbstub.h" -#include "cpu.h" #include "gdbstub/syscalls.h" #include "semihosting/semihost.h" #include "semihosting/console.h" From c9e79557e465dc5ec92803aca4f330b9dc3c2d41 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:37:00 +0100 Subject: [PATCH 0940/1794] semihosting/arm-compat-semi: compile once in system and per target for user mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't have any target dependency left in system mode, so we can compile once. User mode depends on qemu.h, which is duplicated between linux and bsd, so we can't easily compile it once. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-ID: <20250822150058.18692-13-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-16-alex.bennee@linaro.org> --- semihosting/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/semihosting/meson.build b/semihosting/meson.build index bb0db323937f0..99f10e2e2bbbd 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -12,9 +12,10 @@ system_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( 'stubs-system.c', )) system_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING', + if_true: files('arm-compat-semi.c'), if_false: files('arm-compat-semi-stub.c')) specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_USER_ONLY'], if_true: files('syscalls.c')) -specific_ss.add(when: ['CONFIG_ARM_COMPATIBLE_SEMIHOSTING'], +specific_ss.add(when: ['CONFIG_ARM_COMPATIBLE_SEMIHOSTING', 'CONFIG_USER_ONLY'], if_true: files('arm-compat-semi.c')) From 04387b4a056771bf4a95b310507c8302f12c4b65 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 22 Sep 2025 10:37:01 +0100 Subject: [PATCH 0941/1794] contrib/plugins/execlog: Explicitly check for qemu_plugin_read_register() failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In insn_check_regs() we don't explicitly check whether qemu_plugin_read_register() failed, which confuses Coverity into thinking that sz can be -1 in the memcmp(). In fact the assertion that sz == reg->last->len means this can't happen, but it's clearer to both humans and Coverity if we explicitly assert that sz > 0, as we already do in init_vcpu_register(). Coverity: CID 1611901, 1611902 Fixes: af6e4e0a22c1 ("contrib/plugins: extend execlog to track register changes") Signed-off-by: Peter Maydell Reviewed-by: Pierrick Bouvier Message-ID: <20250710144543.1187715-1-peter.maydell@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-17-alex.bennee@linaro.org> --- contrib/plugins/execlog.c | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c index 06ec76d6e9a35..811f32031994c 100644 --- a/contrib/plugins/execlog.c +++ b/contrib/plugins/execlog.c @@ -95,6 +95,7 @@ static void insn_check_regs(CPU *cpu) g_byte_array_set_size(reg->new, 0); sz = qemu_plugin_read_register(reg->handle, reg->new); + g_assert(sz > 0); g_assert(sz == reg->last->len); if (memcmp(reg->last->data, reg->new->data, sz)) { From 293028ca25fdf0c9b91e5e9eeea16fe4cc4b9217 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:37:02 +0100 Subject: [PATCH 0942/1794] contrib/plugins/uftrace: skeleton file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We define a scoreboard that will hold our data per cpu. As well, we define a buffer per cpu that will be used to read registers and memories in a thread-safe way. For now, we just instrument all instructions with an empty callback. Reviewed-by: Manos Pitsidianakis Signed-off-by: Pierrick Bouvier Message-ID: <20250902075042.223990-2-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-18-alex.bennee@linaro.org> --- contrib/plugins/meson.build | 3 +- contrib/plugins/uftrace.c | 73 +++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 contrib/plugins/uftrace.c diff --git a/contrib/plugins/meson.build b/contrib/plugins/meson.build index 1876bc78438ab..7eb3629c95dd9 100644 --- a/contrib/plugins/meson.build +++ b/contrib/plugins/meson.build @@ -1,5 +1,6 @@ contrib_plugins = ['bbv', 'cache', 'cflow', 'drcov', 'execlog', 'hotblocks', - 'hotpages', 'howvec', 'hwprofile', 'ips', 'stoptrigger'] + 'hotpages', 'howvec', 'hwprofile', 'ips', 'stoptrigger', + 'uftrace'] if host_os != 'windows' # lockstep uses socket.h contrib_plugins += 'lockstep' diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c new file mode 100644 index 0000000000000..4af0130b159e0 --- /dev/null +++ b/contrib/plugins/uftrace.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2025, Pierrick Bouvier + * + * Generates a trace compatible with uftrace (similar to uftrace record). + * https://github.com/namhyung/uftrace + * + * See docs/about/emulation.rst|Uftrace for details and examples. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +typedef struct Cpu { + GByteArray *buf; +} Cpu; + +static struct qemu_plugin_scoreboard *score; + +static void track_callstack(unsigned int cpu_index, void *udata) +{ +} + +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + size_t n_insns = qemu_plugin_tb_n_insns(tb); + + for (size_t i = 0; i < n_insns; i++) { + struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); + + uintptr_t pc = qemu_plugin_insn_vaddr(insn); + qemu_plugin_register_vcpu_insn_exec_cb(insn, track_callstack, + QEMU_PLUGIN_CB_R_REGS, + (void *) pc); + } +} + +static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) +{ + Cpu *cpu = qemu_plugin_scoreboard_find(score, vcpu_index); + cpu->buf = g_byte_array_new(); +} + +static void vcpu_end(unsigned int vcpu_index) +{ + Cpu *cpu = qemu_plugin_scoreboard_find(score, vcpu_index); + g_byte_array_free(cpu->buf, true); + memset(cpu, 0, sizeof(Cpu)); +} + +static void at_exit(qemu_plugin_id_t id, void *data) +{ + for (size_t i = 0; i < qemu_plugin_num_vcpus(); ++i) { + vcpu_end(i); + } + + qemu_plugin_scoreboard_free(score); +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, + int argc, char **argv) +{ + score = qemu_plugin_scoreboard_new(sizeof(Cpu)); + qemu_plugin_register_vcpu_init_cb(id, vcpu_init); + qemu_plugin_register_atexit_cb(id, at_exit, NULL); + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); + + return 0; +} From 490aa81855e1c85466368a707ddf6da61097ff13 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:37:03 +0100 Subject: [PATCH 0943/1794] contrib/plugins/uftrace: define cpu operations and implement aarch64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We define a new CpuOps structure that will be used to implement tracking independently of guest architecture. As well, we now instrument only instructions following ones that might have touched the frame pointer. Reviewed-by: Manos Pitsidianakis Signed-off-by: Pierrick Bouvier Message-ID: <20250902075042.223990-3-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-19-alex.bennee@linaro.org> --- contrib/plugins/uftrace.c | 114 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 110 insertions(+), 4 deletions(-) diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c index 4af0130b159e0..d060513446ccd 100644 --- a/contrib/plugins/uftrace.c +++ b/contrib/plugins/uftrace.c @@ -11,14 +11,94 @@ #include #include +#include QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; +typedef struct Cpu Cpu; + +typedef struct { + void (*init)(Cpu *cpu); + void (*end)(Cpu *cpu); + uint64_t (*get_frame_pointer)(Cpu *cpu); + bool (*does_insn_modify_frame_pointer)(const char *disas); +} CpuOps; + typedef struct Cpu { GByteArray *buf; + CpuOps ops; + void *arch; } Cpu; +typedef struct { + struct qemu_plugin_register *reg_fp; +} Aarch64Cpu; + static struct qemu_plugin_scoreboard *score; +static CpuOps arch_ops; + +static uint64_t cpu_read_register64(Cpu *cpu, struct qemu_plugin_register *reg) +{ + GByteArray *buf = cpu->buf; + g_byte_array_set_size(buf, 0); + size_t sz = qemu_plugin_read_register(reg, buf); + g_assert(sz == 8); + g_assert(buf->len == 8); + return *((uint64_t *) buf->data); +} + +static struct qemu_plugin_register *plugin_find_register(const char *name) +{ + g_autoptr(GArray) regs = qemu_plugin_get_registers(); + for (int i = 0; i < regs->len; ++i) { + qemu_plugin_reg_descriptor *reg; + reg = &g_array_index(regs, qemu_plugin_reg_descriptor, i); + if (!strcmp(reg->name, name)) { + return reg->handle; + } + } + return NULL; +} + +static uint64_t aarch64_get_frame_pointer(Cpu *cpu_) +{ + Aarch64Cpu *cpu = cpu_->arch; + return cpu_read_register64(cpu_, cpu->reg_fp); +} + +static void aarch64_init(Cpu *cpu_) +{ + Aarch64Cpu *cpu = g_new0(Aarch64Cpu, 1); + cpu_->arch = cpu; + cpu->reg_fp = plugin_find_register("x29"); + if (!cpu->reg_fp) { + fprintf(stderr, "uftrace plugin: frame pointer register (x29) is not " + "available. Please use an AArch64 cpu (or -cpu max).\n"); + g_abort(); + } +} + +static void aarch64_end(Cpu *cpu) +{ + g_free(cpu->arch); +} + +static bool aarch64_does_insn_modify_frame_pointer(const char *disas) +{ + /* + * Check if current instruction concerns fp register "x29". + * We add a prefix space to make sure we don't match addresses dump + * in disassembly. + */ + return strstr(disas, " x29"); +} + +static CpuOps aarch64_ops = { + .init = aarch64_init, + .end = aarch64_end, + .get_frame_pointer = aarch64_get_frame_pointer, + .does_insn_modify_frame_pointer = aarch64_does_insn_modify_frame_pointer, +}; static void track_callstack(unsigned int cpu_index, void *udata) { @@ -28,19 +108,37 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { size_t n_insns = qemu_plugin_tb_n_insns(tb); + /* + * Callbacks and inline instrumentation are inserted before an instruction. + * Thus, to see instruction effect, we need to wait for next one. + * Potentially, the last instruction of a block could modify the frame + * pointer. Thus, we need to always instrument first instruction in a tb. + */ + bool instrument_insn = true; for (size_t i = 0; i < n_insns; i++) { struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); - uintptr_t pc = qemu_plugin_insn_vaddr(insn); - qemu_plugin_register_vcpu_insn_exec_cb(insn, track_callstack, - QEMU_PLUGIN_CB_R_REGS, - (void *) pc); + if (instrument_insn) { + uintptr_t pc = qemu_plugin_insn_vaddr(insn); + qemu_plugin_register_vcpu_insn_exec_cb(insn, track_callstack, + QEMU_PLUGIN_CB_R_REGS, + (void *) pc); + instrument_insn = false; + } + + char *disas = qemu_plugin_insn_disas(insn); + if (arch_ops.does_insn_modify_frame_pointer(disas)) { + instrument_insn = true; + } } } static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) { Cpu *cpu = qemu_plugin_scoreboard_find(score, vcpu_index); + cpu->ops = arch_ops; + + cpu->ops.init(cpu); cpu->buf = g_byte_array_new(); } @@ -64,6 +162,14 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { + if (!strcmp(info->target_name, "aarch64")) { + arch_ops = aarch64_ops; + } else { + fprintf(stderr, "plugin uftrace: %s target is not supported\n", + info->target_name); + return 1; + } + score = qemu_plugin_scoreboard_new(sizeof(Cpu)); qemu_plugin_register_vcpu_init_cb(id, vcpu_init); qemu_plugin_register_atexit_cb(id, at_exit, NULL); From 992fe17bd7e9648e9e79eb72826eff26a660bb1b Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:37:04 +0100 Subject: [PATCH 0944/1794] contrib/plugins/uftrace: track callstack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now track callstack, based on frame pointer analysis. We can detect function calls, returns, and discontinuities. We implement a frame pointer based unwinding that is used for discontinuities. Reviewed-by: Manos Pitsidianakis Signed-off-by: Pierrick Bouvier Message-ID: <20250902075042.223990-4-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-20-alex.bennee@linaro.org> --- contrib/plugins/uftrace.c | 160 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c index d060513446ccd..bb77591627093 100644 --- a/contrib/plugins/uftrace.c +++ b/contrib/plugins/uftrace.c @@ -15,6 +15,15 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; +typedef struct { + GArray *s; +} Callstack; + +typedef struct { + uint64_t pc; + uint64_t frame_pointer; +} CallstackEntry; + typedef struct Cpu Cpu; typedef struct { @@ -25,6 +34,7 @@ typedef struct { } CpuOps; typedef struct Cpu { + Callstack *cs; GByteArray *buf; CpuOps ops; void *arch; @@ -37,6 +47,71 @@ typedef struct { static struct qemu_plugin_scoreboard *score; static CpuOps arch_ops; +static Callstack *callstack_new(void) +{ + Callstack *cs = g_new0(Callstack, 1); + cs->s = g_array_new(false, false, sizeof(CallstackEntry)); + return cs; +} + +static void callstack_free(Callstack *cs) +{ + g_array_free(cs->s, true); + cs->s = NULL; + g_free(cs); +} + +static size_t callstack_depth(const Callstack *cs) +{ + return cs->s->len; +} + +static size_t callstack_empty(const Callstack *cs) +{ + return callstack_depth(cs) == 0; +} + +static void callstack_clear(Callstack *cs) +{ + g_array_set_size(cs->s, 0); +} + +static const CallstackEntry *callstack_at(const Callstack *cs, size_t depth) +{ + g_assert(depth > 0); + g_assert(depth <= callstack_depth(cs)); + return &g_array_index(cs->s, CallstackEntry, depth - 1); +} + +static CallstackEntry callstack_top(const Callstack *cs) +{ + if (callstack_depth(cs) >= 1) { + return *callstack_at(cs, callstack_depth(cs)); + } + return (CallstackEntry){}; +} + +static CallstackEntry callstack_caller(const Callstack *cs) +{ + if (callstack_depth(cs) >= 2) { + return *callstack_at(cs, callstack_depth(cs) - 1); + } + return (CallstackEntry){}; +} + +static void callstack_push(Callstack *cs, CallstackEntry e) +{ + g_array_append_val(cs->s, e); +} + +static CallstackEntry callstack_pop(Callstack *cs) +{ + g_assert(!callstack_empty(cs)); + CallstackEntry e = callstack_top(cs); + g_array_set_size(cs->s, callstack_depth(cs) - 1); + return e; +} + static uint64_t cpu_read_register64(Cpu *cpu, struct qemu_plugin_register *reg) { GByteArray *buf = cpu->buf; @@ -47,6 +122,50 @@ static uint64_t cpu_read_register64(Cpu *cpu, struct qemu_plugin_register *reg) return *((uint64_t *) buf->data); } +static uint64_t cpu_read_memory64(Cpu *cpu, uint64_t addr) +{ + g_assert(addr); + GByteArray *buf = cpu->buf; + g_byte_array_set_size(buf, 0); + bool read = qemu_plugin_read_memory_vaddr(addr, buf, 8); + if (!read) { + return 0; + } + g_assert(buf->len == 8); + return *((uint64_t *) buf->data); +} + +static void cpu_unwind_stack(Cpu *cpu, uint64_t frame_pointer, uint64_t pc) +{ + g_assert(callstack_empty(cpu->cs)); + + #define UNWIND_STACK_MAX_DEPTH 1024 + CallstackEntry unwind[UNWIND_STACK_MAX_DEPTH]; + size_t depth = 0; + do { + /* check we don't have an infinite stack */ + for (size_t i = 0; i < depth; ++i) { + if (frame_pointer == unwind[i].frame_pointer) { + break; + } + } + CallstackEntry e = {.frame_pointer = frame_pointer, .pc = pc}; + unwind[depth] = e; + depth++; + if (frame_pointer) { + frame_pointer = cpu_read_memory64(cpu, frame_pointer); + } + pc = cpu_read_memory64(cpu, frame_pointer + 8); /* read previous lr */ + } while (frame_pointer && pc && depth < UNWIND_STACK_MAX_DEPTH); + #undef UNWIND_STACK_MAX_DEPTH + + /* push it from bottom to top */ + while (depth) { + callstack_push(cpu->cs, unwind[depth - 1]); + --depth; + } +} + static struct qemu_plugin_register *plugin_find_register(const char *name) { g_autoptr(GArray) regs = qemu_plugin_get_registers(); @@ -102,6 +221,43 @@ static CpuOps aarch64_ops = { static void track_callstack(unsigned int cpu_index, void *udata) { + uint64_t pc = (uintptr_t) udata; + Cpu *cpu = qemu_plugin_scoreboard_find(score, cpu_index); + Callstack *cs = cpu->cs; + + uint64_t fp = cpu->ops.get_frame_pointer(cpu); + if (!fp && callstack_empty(cs)) { + /* + * We simply push current pc. Note that we won't detect symbol change as + * long as a proper call does not happen. + */ + callstack_push(cs, (CallstackEntry){.frame_pointer = fp, .pc = pc}); + return; + } + + CallstackEntry top = callstack_top(cs); + if (fp == top.frame_pointer) { + /* same function */ + return; + } + + CallstackEntry caller = callstack_caller(cs); + if (fp == caller.frame_pointer) { + /* return */ + callstack_pop(cs); + return; + } + + uint64_t caller_fp = fp ? cpu_read_memory64(cpu, fp) : 0; + if (caller_fp == top.frame_pointer) { + /* call */ + callstack_push(cs, (CallstackEntry){.frame_pointer = fp, .pc = pc}); + return; + } + + /* discontinuity, exit current stack and unwind new one */ + callstack_clear(cs); + cpu_unwind_stack(cpu, fp, pc); } static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) @@ -140,12 +296,16 @@ static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) cpu->ops.init(cpu); cpu->buf = g_byte_array_new(); + + cpu->cs = callstack_new(); } static void vcpu_end(unsigned int vcpu_index) { Cpu *cpu = qemu_plugin_scoreboard_find(score, vcpu_index); g_byte_array_free(cpu->buf, true); + + callstack_free(cpu->cs); memset(cpu, 0, sizeof(Cpu)); } From bba94f7876436b798a87894d2e9886429ccda446 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:37:05 +0100 Subject: [PATCH 0945/1794] contrib/plugins/uftrace: implement tracing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We implement tracing, following uftrace format. Trace is flushed every 32 MB, so file operations don't impact performance at runtime. A different trace is generated per cpu, and we ensure they have a unique name, based on vcpu_index, while keeping room for privilege level coming in next commit. Uftrace format is not officially documented, but it can be found here: https://github.com/namhyung/uftrace/blob/v0.18/libmcount/record.c#L909 Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Pierrick Bouvier Message-ID: <20250902075042.223990-5-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-21-alex.bennee@linaro.org> --- contrib/plugins/uftrace.c | 152 +++++++++++++++++++++++++++++++++++++- 1 file changed, 151 insertions(+), 1 deletion(-) diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c index bb77591627093..b9dcd53198735 100644 --- a/contrib/plugins/uftrace.c +++ b/contrib/plugins/uftrace.c @@ -12,6 +12,15 @@ #include #include #include +#include +#include +#include +#include + +#define MiB (INT64_C(1) << 20) +#define NANOSECONDS_PER_SECOND 1000000000LL +#define TRACE_FLUSH_SIZE (32 * MiB) +#define TRACE_ID_SCALE 100 QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; @@ -24,6 +33,13 @@ typedef struct { uint64_t frame_pointer; } CallstackEntry; +typedef struct { + GArray *t; + GString *path; + GString *name; + uint32_t id; +} Trace; + typedef struct Cpu Cpu; typedef struct { @@ -34,6 +50,7 @@ typedef struct { } CpuOps; typedef struct Cpu { + Trace *trace; Callstack *cs; GByteArray *buf; CpuOps ops; @@ -44,9 +61,41 @@ typedef struct { struct qemu_plugin_register *reg_fp; } Aarch64Cpu; +typedef struct { + uint64_t timestamp; + uint64_t data; +} UftraceEntry; + +typedef enum { + UFTRACE_ENTRY, + UFTRACE_EXIT, + UFTRACE_LOST, + UFTRACE_EVENT, +} UftraceRecordType; + static struct qemu_plugin_scoreboard *score; static CpuOps arch_ops; +static uint64_t gettime_ns(void) +{ +#ifdef _WIN32 + /* + * On Windows, timespec_get is available only with UCRT, but not with + * MinGW64 environment. Simplify by using only gettimeofday on this + * platform. This may result in a precision loss. + */ + struct timeval tv; + gettimeofday(&tv, NULL); + uint64_t now_ns = tv.tv_sec * NANOSECONDS_PER_SECOND + tv.tv_usec * 1000; +#else + /* We need nanosecond precision for short lived functions. */ + struct timespec ts; + timespec_get(&ts, TIME_UTC); + uint64_t now_ns = ts.tv_sec * NANOSECONDS_PER_SECOND + ts.tv_nsec; +#endif + return now_ns; +} + static Callstack *callstack_new(void) { Callstack *cs = g_new0(Callstack, 1); @@ -112,6 +161,86 @@ static CallstackEntry callstack_pop(Callstack *cs) return e; } +static Trace *trace_new(uint32_t id, GString *name) +{ + Trace *t = g_new0(Trace, 1); + t->t = g_array_new(false, false, sizeof(UftraceEntry)); + t->path = g_string_new(NULL); + g_string_append_printf(t->path, "./uftrace.data/%"PRIu32".dat", id); + t->name = g_string_new(name->str); + t->id = id; + return t; +} + +static void trace_free(Trace *t) +{ + g_assert(t->t->len == 0); + g_array_free(t->t, true); + t->t = NULL; + g_string_free(t->path, true); + t->path = NULL; + g_string_free(t->name, true); + t->name = NULL; + g_free(t); +} + +static void trace_flush(Trace *t, bool append) +{ + int create_dir = g_mkdir_with_parents("./uftrace.data", + S_IRWXU | S_IRWXG | S_IRWXO); + g_assert(create_dir == 0); + FILE *dat = fopen(t->path->str, append ? "a" : "w"); + g_assert(dat); + GArray *data = t->t; + if (data->len) { + size_t wrote = fwrite(data->data, sizeof(UftraceEntry), data->len, dat); + g_assert(wrote == data->len); + } + fclose(dat); + g_array_set_size(data, 0); +} + +static void trace_add_entry(Trace *t, uint64_t timestamp, uint64_t pc, + size_t depth, UftraceRecordType type) +{ + /* https://github.com/namhyung/uftrace/blob/v0.18/libmcount/record.c#L909 */ + const uint64_t record_magic = 0x5; + uint64_t data = type | (record_magic << 3); + data += depth << 6; + data += pc << 16; + UftraceEntry e = {.timestamp = timestamp, .data = data}; + g_array_append_val(t->t, e); + if (t->t->len * sizeof(UftraceEntry) > TRACE_FLUSH_SIZE) { + trace_flush(t, true); + } +} + +static void trace_enter_function(Trace *t, uint64_t timestamp, + uint64_t pc, size_t depth) +{ + trace_add_entry(t, timestamp, pc, depth, UFTRACE_ENTRY); +} + +static void trace_exit_function(Trace *t, uint64_t timestamp, + uint64_t pc, size_t depth) +{ + trace_add_entry(t, timestamp, pc, depth, UFTRACE_EXIT); +} + +static void trace_enter_stack(Trace *t, Callstack *cs, uint64_t timestamp) +{ + for (size_t depth = 1; depth <= callstack_depth(cs); ++depth) { + trace_enter_function(t, timestamp, callstack_at(cs, depth)->pc, depth); + } +} + +static void trace_exit_stack(Trace *t, Callstack *cs, uint64_t timestamp) +{ + for (size_t depth = callstack_depth(cs); depth > 0; --depth) { + trace_exit_function(t, timestamp, callstack_at(cs, depth)->pc, depth); + } +} + static uint64_t cpu_read_register64(Cpu *cpu, struct qemu_plugin_register *reg) { GByteArray *buf = cpu->buf; @@ -223,7 +352,9 @@ static void track_callstack(unsigned int cpu_index, void *udata) { uint64_t pc = (uintptr_t) udata; Cpu *cpu = qemu_plugin_scoreboard_find(score, cpu_index); + uint64_t timestamp = gettime_ns(); Callstack *cs = cpu->cs; + Trace *t = cpu->trace; uint64_t fp = cpu->ops.get_frame_pointer(cpu); if (!fp && callstack_empty(cs)) { @@ -232,6 +363,7 @@ static void track_callstack(unsigned int cpu_index, void *udata) * long as a proper call does not happen. */ callstack_push(cs, (CallstackEntry){.frame_pointer = fp, .pc = pc}); + trace_enter_function(t, timestamp, pc, callstack_depth(cs)); return; } @@ -244,7 +376,8 @@ static void track_callstack(unsigned int cpu_index, void *udata) CallstackEntry caller = callstack_caller(cs); if (fp == caller.frame_pointer) { /* return */ - callstack_pop(cs); + CallstackEntry e = callstack_pop(cs); + trace_exit_function(t, timestamp, e.pc, callstack_depth(cs)); return; } @@ -252,12 +385,16 @@ static void track_callstack(unsigned int cpu_index, void *udata) if (caller_fp == top.frame_pointer) { /* call */ callstack_push(cs, (CallstackEntry){.frame_pointer = fp, .pc = pc}); + trace_enter_function(t, timestamp, pc, callstack_depth(cs)); return; } /* discontinuity, exit current stack and unwind new one */ + trace_exit_stack(t, cs, timestamp); callstack_clear(cs); + cpu_unwind_stack(cpu, fp, pc); + trace_enter_stack(t, cs, timestamp); } static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) @@ -297,6 +434,16 @@ static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) cpu->ops.init(cpu); cpu->buf = g_byte_array_new(); + g_assert(vcpu_index < UINT32_MAX / TRACE_ID_SCALE); + /* trace_id is: cpu_number * TRACE_ID_SCALE */ + uint32_t trace_id = (vcpu_index + 1) * TRACE_ID_SCALE; + + g_autoptr(GString) trace_name = g_string_new(NULL); + g_string_append_printf(trace_name, "cpu%u", vcpu_index); + cpu->trace = trace_new(trace_id, trace_name); + /* create/truncate trace file */ + trace_flush(cpu->trace, false); + cpu->cs = callstack_new(); } @@ -305,6 +452,7 @@ static void vcpu_end(unsigned int vcpu_index) Cpu *cpu = qemu_plugin_scoreboard_find(score, vcpu_index); g_byte_array_free(cpu->buf, true); + trace_free(cpu->trace); callstack_free(cpu->cs); memset(cpu, 0, sizeof(Cpu)); } @@ -312,6 +460,8 @@ static void vcpu_end(unsigned int vcpu_index) static void at_exit(qemu_plugin_id_t id, void *data) { for (size_t i = 0; i < qemu_plugin_num_vcpus(); ++i) { + Cpu *cpu = qemu_plugin_scoreboard_find(score, i); + trace_flush(cpu->trace, true); vcpu_end(i); } From 308c20108a476722d49d9e463d81662b0722bc40 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:37:06 +0100 Subject: [PATCH 0946/1794] contrib/plugins/uftrace: implement privilege level tracing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We add new option trace-privilege-level=bool, which will create a separate trace for each privilege level. This allows to follow changes of privilege during execution. We implement aarch64 operations to track current privilege level accordingly. Reviewed-by: Manos Pitsidianakis Signed-off-by: Pierrick Bouvier Message-ID: <20250902075042.223990-6-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-22-alex.bennee@linaro.org> --- contrib/plugins/uftrace.c | 190 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 182 insertions(+), 8 deletions(-) diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c index b9dcd53198735..10abad0673c41 100644 --- a/contrib/plugins/uftrace.c +++ b/contrib/plugins/uftrace.c @@ -46,19 +46,40 @@ typedef struct { void (*init)(Cpu *cpu); void (*end)(Cpu *cpu); uint64_t (*get_frame_pointer)(Cpu *cpu); + uint8_t (*get_privilege_level)(Cpu *cpu); + uint8_t (*num_privilege_levels)(void); + const char *(*get_privilege_level_name)(uint8_t pl); bool (*does_insn_modify_frame_pointer)(const char *disas); } CpuOps; typedef struct Cpu { Trace *trace; Callstack *cs; + uint8_t privilege_level; + GArray *traces; /* Trace *traces [] */ GByteArray *buf; CpuOps ops; void *arch; } Cpu; +typedef enum { + AARCH64_EL0_SECURE, + AARCH64_EL0_NONSECURE, + AARCH64_EL0_REALM, + AARCH64_EL1_SECURE, + AARCH64_EL1_NONSECURE, + AARCH64_EL1_REALM, + AARCH64_EL2_SECURE, + AARCH64_EL2_NONSECURE, + AARCH64_EL2_REALM, + AARCH64_EL3, + AARCH64_PRIVILEGE_LEVEL_MAX, +} Aarch64PrivilegeLevel; + typedef struct { struct qemu_plugin_register *reg_fp; + struct qemu_plugin_register *reg_cpsr; + struct qemu_plugin_register *reg_scr_el3; } Aarch64Cpu; typedef struct { @@ -74,6 +95,7 @@ typedef enum { } UftraceRecordType; static struct qemu_plugin_scoreboard *score; +static bool trace_privilege_level; static CpuOps arch_ops; static uint64_t gettime_ns(void) @@ -251,6 +273,16 @@ static uint64_t cpu_read_register64(Cpu *cpu, struct qemu_plugin_register *reg) return *((uint64_t *) buf->data); } +static uint32_t cpu_read_register32(Cpu *cpu, struct qemu_plugin_register *reg) +{ + GByteArray *buf = cpu->buf; + g_byte_array_set_size(buf, 0); + size_t sz = qemu_plugin_read_register(reg, buf); + g_assert(sz == 4); + g_assert(buf->len == 4); + return *((uint32_t *) buf->data); +} + static uint64_t cpu_read_memory64(Cpu *cpu, uint64_t addr) { g_assert(addr); @@ -308,6 +340,68 @@ static struct qemu_plugin_register *plugin_find_register(const char *name) return NULL; } +static uint8_t aarch64_num_privilege_levels(void) +{ + return AARCH64_PRIVILEGE_LEVEL_MAX; +} + +static const char *aarch64_get_privilege_level_name(uint8_t pl) +{ + switch (pl) { + case AARCH64_EL0_SECURE: return "S-EL0"; + case AARCH64_EL0_NONSECURE: return "NS-EL0"; + case AARCH64_EL0_REALM: return "R-EL0"; + case AARCH64_EL1_SECURE: return "S-EL1"; + case AARCH64_EL1_NONSECURE: return "NS-EL1"; + case AARCH64_EL1_REALM: return "R-EL1"; + case AARCH64_EL2_SECURE: return "S-EL2"; + case AARCH64_EL2_NONSECURE: return "NS-EL2"; + case AARCH64_EL2_REALM: return "R-EL2"; + case AARCH64_EL3: return "EL3"; + default: + g_assert_not_reached(); + } +} + +static uint8_t aarch64_get_privilege_level(Cpu *cpu_) +{ + Aarch64Cpu *cpu = cpu_->arch; + /* + * QEMU gdbstub does not provide access to CurrentEL, + * so we use CPSR instead. + */ + uint8_t el = cpu_read_register32(cpu_, cpu->reg_cpsr) >> 2 & 0b11; + + if (el == 3) { + return AARCH64_EL3; + } + + uint8_t ss = AARCH64_EL0_SECURE; + if (!cpu->reg_scr_el3) { + ss = AARCH64_EL0_NONSECURE; + } + uint64_t scr_el3 = cpu_read_register64(cpu_, cpu->reg_scr_el3); + uint64_t ns = (scr_el3 >> 0) & 0b1; + uint64_t nse = (scr_el3 >> 62) & 0b1; + switch (nse << 1 | ns) { + case 0b00: + ss = AARCH64_EL0_SECURE; + break; + case 0b01: + ss = AARCH64_EL0_NONSECURE; + break; + case 0b11: + ss = AARCH64_EL0_REALM; + break; + default: + g_assert_not_reached(); + } + + const uint8_t num_ss = 3; + Aarch64PrivilegeLevel pl = el * num_ss + ss; + return pl; +} + static uint64_t aarch64_get_frame_pointer(Cpu *cpu_) { Aarch64Cpu *cpu = cpu_->arch; @@ -324,6 +418,10 @@ static void aarch64_init(Cpu *cpu_) "available. Please use an AArch64 cpu (or -cpu max).\n"); g_abort(); } + cpu->reg_cpsr = plugin_find_register("cpsr"); + g_assert(cpu->reg_cpsr); + cpu->reg_scr_el3 = plugin_find_register("SCR_EL3"); + /* scr_el3 is optional */ } static void aarch64_end(Cpu *cpu) @@ -345,9 +443,34 @@ static CpuOps aarch64_ops = { .init = aarch64_init, .end = aarch64_end, .get_frame_pointer = aarch64_get_frame_pointer, + .get_privilege_level = aarch64_get_privilege_level, + .num_privilege_levels = aarch64_num_privilege_levels, + .get_privilege_level_name = aarch64_get_privilege_level_name, .does_insn_modify_frame_pointer = aarch64_does_insn_modify_frame_pointer, }; +static void track_privilege_change(unsigned int cpu_index, void *udata) +{ + Cpu *cpu = qemu_plugin_scoreboard_find(score, cpu_index); + uint8_t new_pl = cpu->ops.get_privilege_level(cpu); + + if (new_pl == cpu->privilege_level) { + return; + } + + uint64_t pc = (uintptr_t) udata; + uint64_t timestamp = gettime_ns(); + + trace_exit_stack(cpu->trace, cpu->cs, timestamp); + callstack_clear(cpu->cs); + + cpu->privilege_level = new_pl; + cpu->trace = g_array_index(cpu->traces, Trace*, new_pl); + + cpu_unwind_stack(cpu, cpu->ops.get_frame_pointer(cpu), pc); + trace_enter_stack(cpu->trace, cpu->cs, timestamp); +} + static void track_callstack(unsigned int cpu_index, void *udata) { uint64_t pc = (uintptr_t) udata; @@ -400,6 +523,13 @@ static void track_callstack(unsigned int cpu_index, void *udata) static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { size_t n_insns = qemu_plugin_tb_n_insns(tb); + uintptr_t tb_pc = qemu_plugin_tb_vaddr(tb); + + if (trace_privilege_level) { + qemu_plugin_register_vcpu_tb_exec_cb(tb, track_privilege_change, + QEMU_PLUGIN_CB_R_REGS, + (void *) tb_pc); + } /* * Callbacks and inline instrumentation are inserted before an instruction. @@ -433,18 +563,36 @@ static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) cpu->ops.init(cpu); cpu->buf = g_byte_array_new(); + cpu->traces = g_array_new(0, 0, sizeof(Trace *)); g_assert(vcpu_index < UINT32_MAX / TRACE_ID_SCALE); - /* trace_id is: cpu_number * TRACE_ID_SCALE */ + g_assert(cpu->ops.num_privilege_levels() < TRACE_ID_SCALE); + /* trace_id is: cpu_number * TRACE_ID_SCALE + privilege_level */ uint32_t trace_id = (vcpu_index + 1) * TRACE_ID_SCALE; - g_autoptr(GString) trace_name = g_string_new(NULL); - g_string_append_printf(trace_name, "cpu%u", vcpu_index); - cpu->trace = trace_new(trace_id, trace_name); - /* create/truncate trace file */ - trace_flush(cpu->trace, false); + if (trace_privilege_level) { + for (uint8_t pl = 0; pl < cpu->ops.num_privilege_levels(); ++pl) { + g_autoptr(GString) trace_name = g_string_new(NULL); + g_string_append_printf(trace_name, "cpu%u %s", vcpu_index, + cpu->ops.get_privilege_level_name(pl)); + Trace *t = trace_new(trace_id + pl, trace_name); + g_array_append_val(cpu->traces, t); + } + } else { + g_autoptr(GString) trace_name = g_string_new(NULL); + g_string_append_printf(trace_name, "cpu%u", vcpu_index); + Trace *t = trace_new(trace_id, trace_name); + g_array_append_val(cpu->traces, t); + } + + for (size_t i = 0; i < cpu->traces->len; ++i) { + /* create/truncate trace files */ + Trace *t = g_array_index(cpu->traces, Trace*, i); + trace_flush(t, false); + } cpu->cs = callstack_new(); + cpu->trace = g_array_index(cpu->traces, Trace*, cpu->privilege_level); } static void vcpu_end(unsigned int vcpu_index) @@ -452,7 +600,12 @@ static void vcpu_end(unsigned int vcpu_index) Cpu *cpu = qemu_plugin_scoreboard_find(score, vcpu_index); g_byte_array_free(cpu->buf, true); - trace_free(cpu->trace); + for (size_t i = 0; i < cpu->traces->len; ++i) { + Trace *t = g_array_index(cpu->traces, Trace*, i); + trace_free(t); + } + + g_array_free(cpu->traces, true); callstack_free(cpu->cs); memset(cpu, 0, sizeof(Cpu)); } @@ -461,7 +614,13 @@ static void at_exit(qemu_plugin_id_t id, void *data) { for (size_t i = 0; i < qemu_plugin_num_vcpus(); ++i) { Cpu *cpu = qemu_plugin_scoreboard_find(score, i); - trace_flush(cpu->trace, true); + for (size_t j = 0; j < cpu->traces->len; ++j) { + Trace *t = g_array_index(cpu->traces, Trace*, j); + trace_flush(t, true); + } + } + + for (size_t i = 0; i < qemu_plugin_num_vcpus(); ++i) { vcpu_end(i); } @@ -472,6 +631,21 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { + for (int i = 0; i < argc; i++) { + char *opt = argv[i]; + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); + if (g_strcmp0(tokens[0], "trace-privilege-level") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], + &trace_privilege_level)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + if (!strcmp(info->target_name, "aarch64")) { arch_ops = aarch64_ops; } else { From 727874759566fcec4c52d103f1abc4f4d409e583 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:37:07 +0100 Subject: [PATCH 0947/1794] contrib/plugins/uftrace: generate additional files for uftrace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Beyond traces per cpu, uftrace expect to find some specific files. - info: contains information about machine/program run those values are not impacting uftrace behaviour (only reported by uftrace info), and we simply added empty strings. - memory mapping: how every binary is mapped in memory. For system mode, we generate an empty mapping (uftrace_symbols.py, coming in future commit, will take care of that). For user mode, we copy current /proc/self/maps. We don't need to do any special filtering, as reported addresses will necessarily concern guest program, and not QEMU and its libraries. - task: list of tasks. We present every vcpu/privilege level as a separate process, as it's the best view we can have when generating a (visual) chrome trace. Using threads is less convenient in terms of UI. Reviewed-by: Manos Pitsidianakis Signed-off-by: Pierrick Bouvier Message-ID: <20250902075042.223990-7-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-23-alex.bennee@linaro.org> --- contrib/plugins/uftrace.c | 131 +++++++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-) diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c index 10abad0673c41..2386cc723bcd0 100644 --- a/contrib/plugins/uftrace.c +++ b/contrib/plugins/uftrace.c @@ -118,6 +118,127 @@ static uint64_t gettime_ns(void) return now_ns; } +static void uftrace_write_map(bool system_emulation) +{ + const char *path = "./uftrace.data/sid-0.map"; + + if (system_emulation && access(path, F_OK) == 0) { + /* do not erase existing map in system emulation, as a custom one might + * already have been generated by uftrace_symbols.py */ + return; + } + + FILE *sid_map = fopen(path, "w"); + g_assert(sid_map); + + if (system_emulation) { + fprintf(sid_map, + "# map stack on highest address possible, to prevent uftrace\n" + "# from considering any kernel address\n"); + fprintf(sid_map, + "ffffffffffff-ffffffffffff rw-p 00000000 00:00 0 [stack]\n"); + } else { + /* in user mode, copy /proc/self/maps instead */ + FILE *self_map = fopen("/proc/self/maps", "r"); + g_assert(self_map); + for (;;) { + int c = fgetc(self_map); + if (c == EOF) { + break; + } + fputc(c, sid_map); + } + fclose(self_map); + } + fclose(sid_map); +} + +static void uftrace_write_task(const GArray *traces) +{ + FILE *task = fopen("./uftrace.data/task.txt", "w"); + g_assert(task); + for (int i = 0; i < traces->len; ++i) { + Trace *t = g_array_index(traces, Trace*, i); + fprintf(task, "SESS timestamp=0.0 pid=%"PRIu32" sid=0 exename=\"%s\"\n", + t->id, t->name->str); + fprintf(task, "TASK timestamp=0.0 tid=%"PRIu32" pid=%"PRIu32"\n", + t->id, t->id); + } + fclose(task); +} + +static void uftrace_write_info(const GArray *traces) +{ + g_autoptr(GString) taskinfo_tids = g_string_new("taskinfo:tids="); + for (int i = 0; i < traces->len; ++i) { + Trace *t = g_array_index(traces, Trace*, i); + const char *delim = i > 0 ? "," : ""; + g_string_append_printf(taskinfo_tids, "%s%"PRIu32, delim, t->id); + } + + g_autoptr(GString) taskinfo_nr_tid = g_string_new("taskinfo:nr_tid="); + g_string_append_printf(taskinfo_nr_tid, "%d", traces->len); + + FILE *info = fopen("./uftrace.data/info", "w"); + g_assert(info); + /* + * $ uftrace dump --debug + * uftrace file header: magic = 4674726163652100 + * uftrace file header: version = 4 + * uftrace file header: header size = 40 + * uftrace file header: endian = 1 (little) + * uftrace file header: class = 2 (64 bit) + * uftrace file header: features = 0x1263 (PLTHOOK | ... + * uftrace file header: info = 0x7bff (EXE_NAME | ... + * <0000000000000000>: 46 74 72 61 63 65 21 00 04 00 00 00 28 00 01 02 + * <0000000000000010>: 63 12 00 00 00 00 00 00 ff 7b 00 00 00 00 00 00 + * <0000000000000020>: 00 04 00 00 00 00 00 00 + */ + const uint8_t header[] = {0x46, 0x74, 0x72, 0x61, 0x63, 0x65, 0x21, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x28, 0x00, 0x01, 0x02, + 0x63, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + size_t wrote = fwrite(header, sizeof(header), 1, info); + g_assert(wrote == 1); + const char *info_data[] = { + "exename:", + "build_id:0000000000000000000000000000000000000000", + "exit_status:", + "cmdline:", + "cpuinfo:lines=2", + "cpuinfo:nr_cpus=", + "cpuinfo:desc=", + "meminfo:", + "osinfo:lines=3", + "osinfo:kernel=", + "osinfo:hostname=", + "osinfo:distro=", + "taskinfo:lines=2", + taskinfo_nr_tid->str, + taskinfo_tids->str, + "usageinfo:lines=6", + "usageinfo:systime=", + "usageinfo:usrtime=", + "usageinfo:ctxsw=", + "usageinfo:maxrss=", + "usageinfo:pagefault=", + "usageinfo:iops=", + "loadinfo:", + "record_date:", + "elapsed_time:", + "pattern_type:regex", + "uftrace_version:", + "utc_offset:", + 0}; + const char **info_data_it = info_data; + while (*(info_data_it)) { + fprintf(info, "%s\n", *info_data_it); + ++info_data_it; + } + fclose(info); +} + static Callstack *callstack_new(void) { Callstack *cs = g_new0(Callstack, 1); @@ -612,14 +733,22 @@ static void vcpu_end(unsigned int vcpu_index) static void at_exit(qemu_plugin_id_t id, void *data) { + bool system_emulation = (bool) data; + g_autoptr(GArray) traces = g_array_new(0, 0, sizeof(Trace *)); + for (size_t i = 0; i < qemu_plugin_num_vcpus(); ++i) { Cpu *cpu = qemu_plugin_scoreboard_find(score, i); for (size_t j = 0; j < cpu->traces->len; ++j) { Trace *t = g_array_index(cpu->traces, Trace*, j); trace_flush(t, true); + g_array_append_val(traces, t); } } + uftrace_write_map(system_emulation); + uftrace_write_info(traces); + uftrace_write_task(traces); + for (size_t i = 0; i < qemu_plugin_num_vcpus(); ++i) { vcpu_end(i); } @@ -656,7 +785,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, score = qemu_plugin_scoreboard_new(sizeof(Cpu)); qemu_plugin_register_vcpu_init_cb(id, vcpu_init); - qemu_plugin_register_atexit_cb(id, at_exit, NULL); + qemu_plugin_register_atexit_cb(id, at_exit, (void *) info->system_emulation); qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); return 0; From b860d96f003de204a34e360437af824734bf5112 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:37:08 +0100 Subject: [PATCH 0948/1794] contrib/plugins/uftrace: implement x64 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's trivial to implement x64 support, as it's the same stack layout as aarch64. Reviewed-by: Manos Pitsidianakis Signed-off-by: Pierrick Bouvier Message-ID: <20250902075042.223990-8-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-24-alex.bennee@linaro.org> --- contrib/plugins/uftrace.c | 86 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c index 2386cc723bcd0..b7d6124d2f517 100644 --- a/contrib/plugins/uftrace.c +++ b/contrib/plugins/uftrace.c @@ -82,6 +82,21 @@ typedef struct { struct qemu_plugin_register *reg_scr_el3; } Aarch64Cpu; +typedef enum { + X64_RING0, + X64_RING1, + X64_RING2, + X64_RING3, + X64_REAL_MODE, + X64_PRIVILEGE_LEVEL_MAX, +} X64PrivilegeLevel; + +typedef struct { + struct qemu_plugin_register *reg_rbp; + struct qemu_plugin_register *reg_cs; + struct qemu_plugin_register *reg_cr0; +} X64Cpu; + typedef struct { uint64_t timestamp; uint64_t data; @@ -570,6 +585,75 @@ static CpuOps aarch64_ops = { .does_insn_modify_frame_pointer = aarch64_does_insn_modify_frame_pointer, }; +static uint8_t x64_num_privilege_levels(void) +{ + return X64_PRIVILEGE_LEVEL_MAX; +} + +static const char *x64_get_privilege_level_name(uint8_t pl) +{ + switch (pl) { + case X64_RING0: return "Ring0"; + case X64_RING1: return "Ring1"; + case X64_RING2: return "Ring2"; + case X64_RING3: return "Ring3"; + case X64_REAL_MODE: return "RealMode"; + default: + g_assert_not_reached(); + } +} + +static uint8_t x64_get_privilege_level(Cpu *cpu_) +{ + X64Cpu *cpu = cpu_->arch; + uint64_t cr0 = cpu_read_register64(cpu_, cpu->reg_cr0); + uint64_t protected_mode = (cr0 >> 0) & 0b1; + if (!protected_mode) { + return X64_REAL_MODE; + } + uint32_t cs = cpu_read_register32(cpu_, cpu->reg_cs); + uint32_t ring_level = (cs >> 0) & 0b11; + return ring_level; +} + +static uint64_t x64_get_frame_pointer(Cpu *cpu_) +{ + X64Cpu *cpu = cpu_->arch; + return cpu_read_register64(cpu_, cpu->reg_rbp); +} + +static void x64_init(Cpu *cpu_) +{ + X64Cpu *cpu = g_new0(X64Cpu, 1); + cpu_->arch = cpu; + cpu->reg_rbp = plugin_find_register("rbp"); + g_assert(cpu->reg_rbp); + cpu->reg_cs = plugin_find_register("cs"); + g_assert(cpu->reg_cs); + cpu->reg_cr0 = plugin_find_register("cr0"); + g_assert(cpu->reg_cr0); +} + +static void x64_end(Cpu *cpu) +{ + g_free(cpu->arch); +} + +static bool x64_does_insn_modify_frame_pointer(const char *disas) +{ + return strstr(disas, "rbp"); +} + +static CpuOps x64_ops = { + .init = x64_init, + .end = x64_end, + .get_frame_pointer = x64_get_frame_pointer, + .get_privilege_level = x64_get_privilege_level, + .num_privilege_levels = x64_num_privilege_levels, + .get_privilege_level_name = x64_get_privilege_level_name, + .does_insn_modify_frame_pointer = x64_does_insn_modify_frame_pointer, +}; + static void track_privilege_change(unsigned int cpu_index, void *udata) { Cpu *cpu = qemu_plugin_scoreboard_find(score, cpu_index); @@ -777,6 +861,8 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, if (!strcmp(info->target_name, "aarch64")) { arch_ops = aarch64_ops; + } else if (!strcmp(info->target_name, "x86_64")) { + arch_ops = x64_ops; } else { fprintf(stderr, "plugin uftrace: %s target is not supported\n", info->target_name); From a073d1149efa923ab492414f9fcb209f7242f3a3 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:37:09 +0100 Subject: [PATCH 0949/1794] contrib/plugins/uftrace_symbols.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit usage: contrib/plugins/uftrace_symbols.py \ --prefix-symbols \ arm-trusted-firmware/build/qemu/debug/bl1/bl1.elf \ arm-trusted-firmware/build/qemu/debug/bl2/bl2.elf \ arm-trusted-firmware/build/qemu/debug/bl31/bl31.elf \ u-boot/u-boot:0x60000000 \ u-boot/u-boot.relocated:0x000000023f6b6000 \ linux/vmlinux Will generate symbols and memory mapping files for uftrace, allowing to have an enhanced trace, instead of raw addresses. It takes a collection of elf files, and automatically find all their symbols, and generate an ordered memory map based on that. This script uses the python (native) pyelftools module. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Pierrick Bouvier Acked-by: Alex Bennée Message-ID: <20250902075042.223990-9-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-25-alex.bennee@linaro.org> --- contrib/plugins/uftrace_symbols.py | 152 +++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100755 contrib/plugins/uftrace_symbols.py diff --git a/contrib/plugins/uftrace_symbols.py b/contrib/plugins/uftrace_symbols.py new file mode 100755 index 0000000000000..b49e03203c8fc --- /dev/null +++ b/contrib/plugins/uftrace_symbols.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Create symbols and mapping files for uftrace. +# +# Copyright 2025 Linaro Ltd +# Author: Pierrick Bouvier +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import elftools # pip install pyelftools +import os + +from elftools.elf.elffile import ELFFile +from elftools.elf.sections import SymbolTableSection + +def elf_func_symbols(elf): + symbol_tables = [(idx, s) for idx, s in enumerate(elf.iter_sections()) + if isinstance(s, SymbolTableSection)] + symbols = [] + for _, section in symbol_tables: + for _, symbol in enumerate(section.iter_symbols()): + if symbol_size(symbol) == 0: + continue + type = symbol['st_info']['type'] + if type == 'STT_FUNC' or type == 'STT_NOTYPE': + symbols.append(symbol) + symbols.sort(key = lambda x: symbol_addr(x)) + return symbols + +def symbol_size(symbol): + return symbol['st_size'] + +def symbol_addr(symbol): + addr = symbol['st_value'] + # clamp addr to 48 bits, like uftrace entries + return addr & 0xffffffffffff + +def symbol_name(symbol): + return symbol.name + +class BinaryFile: + def __init__(self, path, map_offset): + self.fullpath = os.path.realpath(path) + self.map_offset = map_offset + with open(path, 'rb') as f: + self.elf = ELFFile(f) + self.symbols = elf_func_symbols(self.elf) + + def path(self): + return self.fullpath + + def addr_start(self): + return self.map_offset + + def addr_end(self): + last_sym = self.symbols[-1] + return symbol_addr(last_sym) + symbol_size(last_sym) + self.map_offset + + def generate_symbol_file(self, prefix_symbols): + binary_name = os.path.basename(self.fullpath) + sym_file_path = f'./uftrace.data/{binary_name}.sym' + print(f'{sym_file_path} ({len(self.symbols)} symbols)') + with open(sym_file_path, 'w') as sym_file: + # print hexadecimal addresses on 48 bits + addrx = "0>12x" + for s in self.symbols: + addr = symbol_addr(s) + addr = f'{addr:{addrx}}' + size = f'{symbol_size(s):{addrx}}' + name = symbol_name(s) + if prefix_symbols: + name = f'{binary_name}:{name}' + print(addr, size, 'T', name, file=sym_file) + +def parse_parameter(p): + s = p.split(":") + path = s[0] + if len(s) == 1: + return path, 0 + if len(s) > 2: + raise ValueError('only one offset can be set') + offset = s[1] + if not offset.startswith('0x'): + err = f'offset "{offset}" is not an hexadecimal constant. ' + err += 'It should starts with "0x".' + raise ValueError(err) + offset = int(offset, 16) + return path, offset + +def is_from_user_mode(map_file_path): + if os.path.exists(map_file_path): + with open(map_file_path, 'r') as map_file: + if not map_file.readline().startswith('# map stack on'): + return True + return False + +def generate_map(binaries): + map_file_path = './uftrace.data/sid-0.map' + + if is_from_user_mode(map_file_path): + print(f'do not overwrite {map_file_path} generated from qemu-user') + return + + mappings = [] + + # print hexadecimal addresses on 48 bits + addrx = "0>12x" + + mappings += ['# map stack on highest address possible, to prevent uftrace'] + mappings += ['# from considering any kernel address'] + mappings += ['ffffffffffff-ffffffffffff rw-p 00000000 00:00 0 [stack]'] + + for b in binaries: + m = f'{b.addr_start():{addrx}}-{b.addr_end():{addrx}}' + m += f' r--p 00000000 00:00 0 {b.path()}' + mappings.append(m) + + with open(map_file_path, 'w') as map_file: + print('\n'.join(mappings), file=map_file) + print(f'{map_file_path}') + print('\n'.join(mappings)) + +def main(): + parser = argparse.ArgumentParser(description= + 'generate symbol files for uftrace') + parser.add_argument('elf_file', nargs='+', + help='path to an ELF file. ' + 'Use /path/to/file:0xdeadbeef to add a mapping offset.') + parser.add_argument('--prefix-symbols', + help='prepend binary name to symbols', + action=argparse.BooleanOptionalAction) + args = parser.parse_args() + + if not os.path.exists('./uftrace.data'): + os.mkdir('./uftrace.data') + + binaries = [] + for file in args.elf_file: + path, offset = parse_parameter(file) + b = BinaryFile(path, offset) + binaries.append(b) + binaries.sort(key = lambda b: b.addr_end()); + + for b in binaries: + b.generate_symbol_file(args.prefix_symbols) + + generate_map(binaries) + +if __name__ == '__main__': + main() From 16b10fbf8bdb71ae20c7f74ab19c78d07c013ac7 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 22 Sep 2025 10:37:10 +0100 Subject: [PATCH 0950/1794] contrib/plugins/uftrace: add documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This documentation summarizes how to use the plugin, and present two examples of the possibilities offered by it, in system and user mode. As well, it explains how to rebuild and reproduce those examples. Reviewed-by: Manos Pitsidianakis Signed-off-by: Pierrick Bouvier Message-ID: <20250902075042.223990-10-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250922093711.2768983-26-alex.bennee@linaro.org> --- docs/about/emulation.rst | 199 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst index 456d01d5b08f6..8a5e128f677db 100644 --- a/docs/about/emulation.rst +++ b/docs/about/emulation.rst @@ -816,6 +816,205 @@ This plugin can limit the number of Instructions Per Second that are executed:: The lower the number the more accurate time will be, but the less efficient the plugin. Defaults to ips/10 +Uftrace +....... + +``contrib/plugins/uftrace.c`` + +This plugin generates a binary trace compatible with +`uftrace `_. + +Plugin supports aarch64 and x64, and works in user and system mode, allowing to +trace a system boot, which is not something possible usually. + +In user mode, the memory mapping is directly copied from ``/proc/self/maps`` at +the end of execution. Uftrace should be able to retrieve symbols by itself, +without any additional step. +In system mode, the default memory mapping is empty, and you can generate +one (and associated symbols) using ``contrib/plugins/uftrace_symbols.py``. +Symbols must be present in ELF binaries. + +It tracks the call stack (based on frame pointer analysis). Thus, your program +and its dependencies must be compiled using ``-fno-omit-frame-pointer +-mno-omit-leaf-frame-pointer``. In 2024, `Ubuntu and Fedora enabled it by +default again on x64 +`_. +On aarch64, this is less of a problem, as they are usually part of the ABI, +except for leaf functions. That's true for user space applications, but not +necessarily for bare metal code. You can read this `section +` to easily build a system with frame pointers. + +When tracing long scenarios (> 1 min), the generated trace can become very long, +making it hard to extract data from it. In this case, a simple solution is to +trace execution while generating a timestamped output log using +``qemu-system-aarch64 ... | ts "%s"``. Then, ``uftrace --time-range=start~end`` +can be used to reduce trace for only this part of execution. + +Performance wise, overhead compared to normal tcg execution is around x5-x15. + +.. list-table:: Uftrace plugin arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - trace-privilege-level=[on|off] + - Generate separate traces for each privilege level (Exception Level + + Security State on aarch64, Rings on x64). + +.. list-table:: uftrace_symbols.py arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - elf_file [elf_file ...] + - path to an ELF file. Use /path/to/file:0xdeadbeef to add a mapping offset. + * - --prefix-symbols + - prepend binary name to symbols + +Example user trace +++++++++++++++++++ + +As an example, we can trace qemu itself running git:: + + $ ./build/qemu-aarch64 -plugin \ + build/contrib/plugins/libuftrace.so \ + ./build/qemu-aarch64 /usr/bin/git --help + + # and generate a chrome trace directly + $ uftrace dump --chrome | gzip > ~/qemu_aarch64_git_help.json.gz + +For convenience, you can download this trace `qemu_aarch64_git_help.json.gz +`_. +Download it and open this trace on https://ui.perfetto.dev/. You can zoom in/out +using :kbd:`W`, :kbd:`A`, :kbd:`S`, :kbd:`D` keys. +Some sequences taken from this trace: + +- Loading program and its interpreter + +.. image:: https://fileserver.linaro.org/s/fie8JgX76yyL5cq/preview + :height: 200px + +- open syscall + +.. image:: https://fileserver.linaro.org/s/rsXPTeZZPza4PcE/preview + :height: 200px + +- TB creation + +.. image:: https://fileserver.linaro.org/s/GXY6NKMw5EeRCew/preview + :height: 200px + +It's usually better to use ``uftrace record`` directly. However, tracing +binaries through qemu-user can be convenient when you don't want to recompile +them (``uftrace record`` requires instrumentation), as long as symbols are +present. + +Example system trace +++++++++++++++++++++ + +A full trace example (chrome trace, from instructions below) generated from a +system boot can be found `here +`_. +Download it and open this trace on https://ui.perfetto.dev/. You can see code +executed for all privilege levels, and zoom in/out using +:kbd:`W`, :kbd:`A`, :kbd:`S`, :kbd:`D` keys. You can find below some sequences +taken from this trace: + +- Two first stages of boot sequence in Arm Trusted Firmware (EL3 and S-EL1) + +.. image:: https://fileserver.linaro.org/s/kkxBS552W7nYESX/preview + :height: 200px + +- U-boot initialization (until code relocation, after which we can't track it) + +.. image:: https://fileserver.linaro.org/s/LKTgsXNZFi5GFNC/preview + :height: 200px + +- Stat and open syscalls in kernel + +.. image:: https://fileserver.linaro.org/s/dXe4MfraKg2F476/preview + :height: 200px + +- Timer interrupt + +.. image:: https://fileserver.linaro.org/s/TM5yobYzJtP7P3C/preview + :height: 200px + +- Poweroff sequence (from kernel back to firmware, NS-EL2 to EL3) + +.. image:: https://fileserver.linaro.org/s/oR2PtyGKJrqnfRf/preview + :height: 200px + +Build and run system example +++++++++++++++++++++++++++++ + +.. _uftrace_build_system_example: + +Building a full system image with frame pointers is not trivial. + +We provide a `simple way `_ to +build an aarch64 system, combining Arm Trusted firmware, U-boot, Linux kernel +and debian userland. It's based on containers (``podman`` only) and +``qemu-user-static (binfmt)`` to make sure it's easily reproducible and does not depend +on machine where you build it. + +You can follow the exact same instructions for a x64 system, combining edk2, +Linux, and Ubuntu, simply by switching to +`x86_64 `_ branch. + +To build the system:: + + # Install dependencies + $ sudo apt install -y podman qemu-user-static + + $ git clone https://github.com/pbo-linaro/qemu-linux-stack + $ cd qemu-linux-stack + $ ./build.sh + + # system can be started using: + $ ./run.sh /path/to/qemu-system-aarch64 + +To generate a uftrace for a system boot from that:: + + # run true and poweroff the system + $ env INIT=true ./run.sh path/to/qemu-system-aarch64 \ + -plugin path/to/contrib/plugins/libuftrace.so,trace-privilege-level=on + + # generate symbols and memory mapping + $ path/to/contrib/plugins/uftrace_symbols.py \ + --prefix-symbols \ + arm-trusted-firmware/build/qemu/debug/bl1/bl1.elf \ + arm-trusted-firmware/build/qemu/debug/bl2/bl2.elf \ + arm-trusted-firmware/build/qemu/debug/bl31/bl31.elf \ + u-boot/u-boot:0x60000000 \ + linux/vmlinux + + # inspect trace with + $ uftrace replay + +Uftrace allows to filter the trace, and dump flamegraphs, or a chrome trace. +This last one is very interesting to see visually the boot process:: + + $ uftrace dump --chrome > boot.json + # Open your browser, and load boot.json on https://ui.perfetto.dev/. + +Long visual chrome traces can't be easily opened, thus, it might be +interesting to generate them around a particular point of execution:: + + # execute qemu and timestamp output log + $ env INIT=true ./run.sh path/to/qemu-system-aarch64 \ + -plugin path/to/contrib/plugins/libuftrace.so,trace-privilege-level=on |& + ts "%s" | tee exec.log + + $ cat exec.log | grep 'Run /init' + 1753122320 [ 11.834391] Run /init as init process + # init was launched at 1753122320 + + # generate trace around init execution (2 seconds): + $ uftrace dump --chrome --time-range=1753122320~1753122322 > init.json + Other emulation features ------------------------ From 674fe9066760e7744f2429840181ea39697b9af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 25 Sep 2025 05:21:51 +0200 Subject: [PATCH 0951/1794] target/arm: Replace magic GIC values by proper definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prefer the FIELD_DP64() macro and self-describing GIC definitions over magic values. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell --- target/arm/helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index c5a8ef50493a1..a18d920ac18f8 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -5007,7 +5007,7 @@ static uint64_t id_pfr1_read(CPUARMState *env, const ARMCPRegInfo *ri) uint64_t pfr1 = GET_IDREG(&cpu->isar, ID_PFR1); if (env->gicv3state) { - pfr1 |= 1 << 28; + pfr1 = FIELD_DP64(pfr1, ID_PFR1, GIC, 1); } return pfr1; } @@ -5018,7 +5018,7 @@ static uint64_t id_aa64pfr0_read(CPUARMState *env, const ARMCPRegInfo *ri) uint64_t pfr0 = GET_IDREG(&cpu->isar, ID_AA64PFR0); if (env->gicv3state) { - pfr0 |= 1 << 24; + pfr0 = FIELD_DP64(pfr0, ID_AA64PFR0, GIC, 1); } return pfr0; } From 436f4085a24f754683216d5b2e9fa98f69512b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 24 Sep 2025 18:32:52 +0200 Subject: [PATCH 0952/1794] target/arm: Convert power control DPRINTF() uses to trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/arm-powerctl.c | 26 ++++++++------------------ target/arm/trace-events | 6 ++++++ 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c index 20c70c7d6bb20..a788376d1d3e8 100644 --- a/target/arm/arm-powerctl.c +++ b/target/arm/arm-powerctl.c @@ -17,24 +17,12 @@ #include "qemu/main-loop.h" #include "system/tcg.h" #include "target/arm/multiprocessing.h" - -#ifndef DEBUG_ARM_POWERCTL -#define DEBUG_ARM_POWERCTL 0 -#endif - -#define DPRINTF(fmt, args...) \ - do { \ - if (DEBUG_ARM_POWERCTL) { \ - fprintf(stderr, "[ARM]%s: " fmt , __func__, ##args); \ - } \ - } while (0) +#include "trace.h" CPUState *arm_get_cpu_by_id(uint64_t id) { CPUState *cpu; - DPRINTF("cpu %" PRId64 "\n", id); - CPU_FOREACH(cpu) { ARMCPU *armcpu = ARM_CPU(cpu); @@ -102,9 +90,9 @@ int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id, assert(bql_locked()); - DPRINTF("cpu %" PRId64 " (EL %d, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64 - "\n", cpuid, target_el, target_aa64 ? "aarch64" : "aarch32", entry, - context_id); + trace_arm_powerctl_set_cpu_on(cpuid, target_el, + target_aa64 ? "aarch64" : "aarch32", + entry, context_id); /* requested EL level need to be in the 1 to 3 range */ assert((target_el > 0) && (target_el < 4)); @@ -208,6 +196,8 @@ int arm_set_cpu_on_and_reset(uint64_t cpuid) assert(bql_locked()); + trace_arm_powerctl_set_cpu_on_and_reset(cpuid); + /* Retrieve the cpu we are powering up */ target_cpu_state = arm_get_cpu_by_id(cpuid); if (!target_cpu_state) { @@ -261,7 +251,7 @@ int arm_set_cpu_off(uint64_t cpuid) assert(bql_locked()); - DPRINTF("cpu %" PRId64 "\n", cpuid); + trace_arm_powerctl_set_cpu_off(cpuid); /* change to the cpu we are powering up */ target_cpu_state = arm_get_cpu_by_id(cpuid); @@ -297,7 +287,7 @@ int arm_reset_cpu(uint64_t cpuid) assert(bql_locked()); - DPRINTF("cpu %" PRId64 "\n", cpuid); + trace_arm_powerctl_set_cpu_off(cpuid); /* change to the cpu we are resetting */ target_cpu_state = arm_get_cpu_by_id(cpuid); diff --git a/target/arm/trace-events b/target/arm/trace-events index 4438dce7becc2..252c05a9ebeb9 100644 --- a/target/arm/trace-events +++ b/target/arm/trace-events @@ -13,3 +13,9 @@ arm_gt_update_irq(int timer, int irqstate) "gt_update_irq: timer %d irqstate %d" # kvm.c kvm_arm_fixup_msi_route(uint64_t iova, uint64_t gpa) "MSI iova = 0x%"PRIx64" is translated into 0x%"PRIx64 + +# arm-powerctl.c +arm_powerctl_set_cpu_on(uint64_t mp_aff, unsigned target_el, const char *mode, uint64_t entry, uint64_t context_id) "cpu %" PRIu64 " (EL %u, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64 +arm_powerctl_set_cpu_on_and_reset(uint64_t mp_aff) "cpu %" PRIu64 +arm_powerctl_set_cpu_off(uint64_t mp_aff) "cpu %" PRIu64 +arm_powerctl_reset_cpu(uint64_t mp_aff) "cpu %" PRIu64 From e733dfcf907267ede51452f0e6f28846d5a6abb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 24 Sep 2025 18:32:53 +0200 Subject: [PATCH 0953/1794] target/arm: Trace emulated firmware reset call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpu.c | 4 ++++ target/arm/trace-events | 3 +++ 2 files changed, 7 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 91ae56dddb2f9..f8e6749ff9963 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -52,6 +52,8 @@ #include "target/arm/cpu-qom.h" #include "target/arm/gtimer.h" +#include "trace.h" + static void arm_cpu_set_pc(CPUState *cs, vaddr value) { ARMCPU *cpu = ARM_CPU(cs); @@ -574,6 +576,8 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el) bool have_el3 = arm_feature(env, ARM_FEATURE_EL3); bool have_el2 = arm_feature(env, ARM_FEATURE_EL2); + trace_arm_emulate_firmware_reset(arm_cpu_mp_affinity(cpu), target_el); + /* * Check we have the EL we're aiming for. If that is the * highest implemented EL, then cpu_reset has already done diff --git a/target/arm/trace-events b/target/arm/trace-events index 252c05a9ebeb9..badff2b2e46a0 100644 --- a/target/arm/trace-events +++ b/target/arm/trace-events @@ -14,6 +14,9 @@ arm_gt_update_irq(int timer, int irqstate) "gt_update_irq: timer %d irqstate %d" # kvm.c kvm_arm_fixup_msi_route(uint64_t iova, uint64_t gpa) "MSI iova = 0x%"PRIx64" is translated into 0x%"PRIx64 +# cpu.c +arm_emulate_firmware_reset(uint64_t mp_aff, unsigned target_el) "cpu %" PRIu64 " @EL%u" + # arm-powerctl.c arm_powerctl_set_cpu_on(uint64_t mp_aff, unsigned target_el, const char *mode, uint64_t entry, uint64_t context_id) "cpu %" PRIu64 " (EL %u, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64 arm_powerctl_set_cpu_on_and_reset(uint64_t mp_aff) "cpu %" PRIu64 From ded97005b2baf6669c102d92f87b5d622136ad18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 24 Sep 2025 18:32:54 +0200 Subject: [PATCH 0954/1794] target/arm: Trace vCPU reset call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpu.c | 2 ++ target/arm/trace-events | 1 + 2 files changed, 3 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index f8e6749ff9963..30e29fd3153a2 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -227,6 +227,8 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) ARMCPUClass *acc = ARM_CPU_GET_CLASS(obj); CPUARMState *env = &cpu->env; + trace_arm_cpu_reset(arm_cpu_mp_affinity(cpu)); + if (acc->parent_phases.hold) { acc->parent_phases.hold(obj, type); } diff --git a/target/arm/trace-events b/target/arm/trace-events index badff2b2e46a0..72a2c7d0969d4 100644 --- a/target/arm/trace-events +++ b/target/arm/trace-events @@ -15,6 +15,7 @@ arm_gt_update_irq(int timer, int irqstate) "gt_update_irq: timer %d irqstate %d" kvm_arm_fixup_msi_route(uint64_t iova, uint64_t gpa) "MSI iova = 0x%"PRIx64" is translated into 0x%"PRIx64 # cpu.c +arm_cpu_reset(uint64_t mp_aff) "cpu %" PRIu64 arm_emulate_firmware_reset(uint64_t mp_aff, unsigned target_el) "cpu %" PRIu64 " @EL%u" # arm-powerctl.c From ff197ae9a44741e92df47c78ce2a3d30a4e46455 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 23 Sep 2025 18:57:50 +0100 Subject: [PATCH 0955/1794] target/arm: Move ID register field defs to cpu-features.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we define constants for the ID register fields in cpu.h. This means they're defined for a lot more code in QEMU than actually needs them. Move them to cpu-features.h, which is where we define the feature functions that test fields in these registers. There's only one place where we need to use some of these macro definitions that we weren't already including cpu-features.h: linux-user/arm/target_proc.h. Otherwise this patch is a pure movement of code from one file to the other. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- linux-user/arm/target_proc.h | 2 + target/arm/cpu-features.h | 410 +++++++++++++++++++++++++++++++++++ target/arm/cpu.h | 410 ----------------------------------- 3 files changed, 412 insertions(+), 410 deletions(-) diff --git a/linux-user/arm/target_proc.h b/linux-user/arm/target_proc.h index a4cd6948c60d5..a28d7231cdcfa 100644 --- a/linux-user/arm/target_proc.h +++ b/linux-user/arm/target_proc.h @@ -6,6 +6,8 @@ #ifndef ARM_TARGET_PROC_H #define ARM_TARGET_PROC_H +#include "target/arm/cpu-features.h" /* for MIDR_EL1 field definitions */ + static int open_cpuinfo(CPUArchState *cpu_env, int fd) { ARMCPU *cpu = env_archcpu(cpu_env); diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 512eeaf551e8c..ad571e2ffee93 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -25,6 +25,416 @@ #include "cpu.h" #include "cpu-sysregs.h" +/* + * System register ID fields. + */ +FIELD(CLIDR_EL1, CTYPE1, 0, 3) +FIELD(CLIDR_EL1, CTYPE2, 3, 3) +FIELD(CLIDR_EL1, CTYPE3, 6, 3) +FIELD(CLIDR_EL1, CTYPE4, 9, 3) +FIELD(CLIDR_EL1, CTYPE5, 12, 3) +FIELD(CLIDR_EL1, CTYPE6, 15, 3) +FIELD(CLIDR_EL1, CTYPE7, 18, 3) +FIELD(CLIDR_EL1, LOUIS, 21, 3) +FIELD(CLIDR_EL1, LOC, 24, 3) +FIELD(CLIDR_EL1, LOUU, 27, 3) +FIELD(CLIDR_EL1, ICB, 30, 3) + +/* When FEAT_CCIDX is implemented */ +FIELD(CCSIDR_EL1, CCIDX_LINESIZE, 0, 3) +FIELD(CCSIDR_EL1, CCIDX_ASSOCIATIVITY, 3, 21) +FIELD(CCSIDR_EL1, CCIDX_NUMSETS, 32, 24) + +/* When FEAT_CCIDX is not implemented */ +FIELD(CCSIDR_EL1, LINESIZE, 0, 3) +FIELD(CCSIDR_EL1, ASSOCIATIVITY, 3, 10) +FIELD(CCSIDR_EL1, NUMSETS, 13, 15) + +FIELD(CTR_EL0, IMINLINE, 0, 4) +FIELD(CTR_EL0, L1IP, 14, 2) +FIELD(CTR_EL0, DMINLINE, 16, 4) +FIELD(CTR_EL0, ERG, 20, 4) +FIELD(CTR_EL0, CWG, 24, 4) +FIELD(CTR_EL0, IDC, 28, 1) +FIELD(CTR_EL0, DIC, 29, 1) +FIELD(CTR_EL0, TMINLINE, 32, 6) + +FIELD(MIDR_EL1, REVISION, 0, 4) +FIELD(MIDR_EL1, PARTNUM, 4, 12) +FIELD(MIDR_EL1, ARCHITECTURE, 16, 4) +FIELD(MIDR_EL1, VARIANT, 20, 4) +FIELD(MIDR_EL1, IMPLEMENTER, 24, 8) + +FIELD(ID_ISAR0, SWAP, 0, 4) +FIELD(ID_ISAR0, BITCOUNT, 4, 4) +FIELD(ID_ISAR0, BITFIELD, 8, 4) +FIELD(ID_ISAR0, CMPBRANCH, 12, 4) +FIELD(ID_ISAR0, COPROC, 16, 4) +FIELD(ID_ISAR0, DEBUG, 20, 4) +FIELD(ID_ISAR0, DIVIDE, 24, 4) + +FIELD(ID_ISAR1, ENDIAN, 0, 4) +FIELD(ID_ISAR1, EXCEPT, 4, 4) +FIELD(ID_ISAR1, EXCEPT_AR, 8, 4) +FIELD(ID_ISAR1, EXTEND, 12, 4) +FIELD(ID_ISAR1, IFTHEN, 16, 4) +FIELD(ID_ISAR1, IMMEDIATE, 20, 4) +FIELD(ID_ISAR1, INTERWORK, 24, 4) +FIELD(ID_ISAR1, JAZELLE, 28, 4) + +FIELD(ID_ISAR2, LOADSTORE, 0, 4) +FIELD(ID_ISAR2, MEMHINT, 4, 4) +FIELD(ID_ISAR2, MULTIACCESSINT, 8, 4) +FIELD(ID_ISAR2, MULT, 12, 4) +FIELD(ID_ISAR2, MULTS, 16, 4) +FIELD(ID_ISAR2, MULTU, 20, 4) +FIELD(ID_ISAR2, PSR_AR, 24, 4) +FIELD(ID_ISAR2, REVERSAL, 28, 4) + +FIELD(ID_ISAR3, SATURATE, 0, 4) +FIELD(ID_ISAR3, SIMD, 4, 4) +FIELD(ID_ISAR3, SVC, 8, 4) +FIELD(ID_ISAR3, SYNCHPRIM, 12, 4) +FIELD(ID_ISAR3, TABBRANCH, 16, 4) +FIELD(ID_ISAR3, T32COPY, 20, 4) +FIELD(ID_ISAR3, TRUENOP, 24, 4) +FIELD(ID_ISAR3, T32EE, 28, 4) + +FIELD(ID_ISAR4, UNPRIV, 0, 4) +FIELD(ID_ISAR4, WITHSHIFTS, 4, 4) +FIELD(ID_ISAR4, WRITEBACK, 8, 4) +FIELD(ID_ISAR4, SMC, 12, 4) +FIELD(ID_ISAR4, BARRIER, 16, 4) +FIELD(ID_ISAR4, SYNCHPRIM_FRAC, 20, 4) +FIELD(ID_ISAR4, PSR_M, 24, 4) +FIELD(ID_ISAR4, SWP_FRAC, 28, 4) + +FIELD(ID_ISAR5, SEVL, 0, 4) +FIELD(ID_ISAR5, AES, 4, 4) +FIELD(ID_ISAR5, SHA1, 8, 4) +FIELD(ID_ISAR5, SHA2, 12, 4) +FIELD(ID_ISAR5, CRC32, 16, 4) +FIELD(ID_ISAR5, RDM, 24, 4) +FIELD(ID_ISAR5, VCMA, 28, 4) + +FIELD(ID_ISAR6, JSCVT, 0, 4) +FIELD(ID_ISAR6, DP, 4, 4) +FIELD(ID_ISAR6, FHM, 8, 4) +FIELD(ID_ISAR6, SB, 12, 4) +FIELD(ID_ISAR6, SPECRES, 16, 4) +FIELD(ID_ISAR6, BF16, 20, 4) +FIELD(ID_ISAR6, I8MM, 24, 4) + +FIELD(ID_MMFR0, VMSA, 0, 4) +FIELD(ID_MMFR0, PMSA, 4, 4) +FIELD(ID_MMFR0, OUTERSHR, 8, 4) +FIELD(ID_MMFR0, SHARELVL, 12, 4) +FIELD(ID_MMFR0, TCM, 16, 4) +FIELD(ID_MMFR0, AUXREG, 20, 4) +FIELD(ID_MMFR0, FCSE, 24, 4) +FIELD(ID_MMFR0, INNERSHR, 28, 4) + +FIELD(ID_MMFR1, L1HVDVA, 0, 4) +FIELD(ID_MMFR1, L1UNIVA, 4, 4) +FIELD(ID_MMFR1, L1HVDSW, 8, 4) +FIELD(ID_MMFR1, L1UNISW, 12, 4) +FIELD(ID_MMFR1, L1HVD, 16, 4) +FIELD(ID_MMFR1, L1UNI, 20, 4) +FIELD(ID_MMFR1, L1TSTCLN, 24, 4) +FIELD(ID_MMFR1, BPRED, 28, 4) + +FIELD(ID_MMFR2, L1HVDFG, 0, 4) +FIELD(ID_MMFR2, L1HVDBG, 4, 4) +FIELD(ID_MMFR2, L1HVDRNG, 8, 4) +FIELD(ID_MMFR2, HVDTLB, 12, 4) +FIELD(ID_MMFR2, UNITLB, 16, 4) +FIELD(ID_MMFR2, MEMBARR, 20, 4) +FIELD(ID_MMFR2, WFISTALL, 24, 4) +FIELD(ID_MMFR2, HWACCFLG, 28, 4) + +FIELD(ID_MMFR3, CMAINTVA, 0, 4) +FIELD(ID_MMFR3, CMAINTSW, 4, 4) +FIELD(ID_MMFR3, BPMAINT, 8, 4) +FIELD(ID_MMFR3, MAINTBCST, 12, 4) +FIELD(ID_MMFR3, PAN, 16, 4) +FIELD(ID_MMFR3, COHWALK, 20, 4) +FIELD(ID_MMFR3, CMEMSZ, 24, 4) +FIELD(ID_MMFR3, SUPERSEC, 28, 4) + +FIELD(ID_MMFR4, SPECSEI, 0, 4) +FIELD(ID_MMFR4, AC2, 4, 4) +FIELD(ID_MMFR4, XNX, 8, 4) +FIELD(ID_MMFR4, CNP, 12, 4) +FIELD(ID_MMFR4, HPDS, 16, 4) +FIELD(ID_MMFR4, LSM, 20, 4) +FIELD(ID_MMFR4, CCIDX, 24, 4) +FIELD(ID_MMFR4, EVT, 28, 4) + +FIELD(ID_MMFR5, ETS, 0, 4) +FIELD(ID_MMFR5, NTLBPA, 4, 4) + +FIELD(ID_PFR0, STATE0, 0, 4) +FIELD(ID_PFR0, STATE1, 4, 4) +FIELD(ID_PFR0, STATE2, 8, 4) +FIELD(ID_PFR0, STATE3, 12, 4) +FIELD(ID_PFR0, CSV2, 16, 4) +FIELD(ID_PFR0, AMU, 20, 4) +FIELD(ID_PFR0, DIT, 24, 4) +FIELD(ID_PFR0, RAS, 28, 4) + +FIELD(ID_PFR1, PROGMOD, 0, 4) +FIELD(ID_PFR1, SECURITY, 4, 4) +FIELD(ID_PFR1, MPROGMOD, 8, 4) +FIELD(ID_PFR1, VIRTUALIZATION, 12, 4) +FIELD(ID_PFR1, GENTIMER, 16, 4) +FIELD(ID_PFR1, SEC_FRAC, 20, 4) +FIELD(ID_PFR1, VIRT_FRAC, 24, 4) +FIELD(ID_PFR1, GIC, 28, 4) + +FIELD(ID_PFR2, CSV3, 0, 4) +FIELD(ID_PFR2, SSBS, 4, 4) +FIELD(ID_PFR2, RAS_FRAC, 8, 4) + +FIELD(ID_AA64ISAR0, AES, 4, 4) +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) +FIELD(ID_AA64ISAR0, SM4, 40, 4) +FIELD(ID_AA64ISAR0, DP, 44, 4) +FIELD(ID_AA64ISAR0, FHM, 48, 4) +FIELD(ID_AA64ISAR0, TS, 52, 4) +FIELD(ID_AA64ISAR0, TLB, 56, 4) +FIELD(ID_AA64ISAR0, RNDR, 60, 4) + +FIELD(ID_AA64ISAR1, DPB, 0, 4) +FIELD(ID_AA64ISAR1, APA, 4, 4) +FIELD(ID_AA64ISAR1, API, 8, 4) +FIELD(ID_AA64ISAR1, JSCVT, 12, 4) +FIELD(ID_AA64ISAR1, FCMA, 16, 4) +FIELD(ID_AA64ISAR1, LRCPC, 20, 4) +FIELD(ID_AA64ISAR1, GPA, 24, 4) +FIELD(ID_AA64ISAR1, GPI, 28, 4) +FIELD(ID_AA64ISAR1, FRINTTS, 32, 4) +FIELD(ID_AA64ISAR1, SB, 36, 4) +FIELD(ID_AA64ISAR1, SPECRES, 40, 4) +FIELD(ID_AA64ISAR1, BF16, 44, 4) +FIELD(ID_AA64ISAR1, DGH, 48, 4) +FIELD(ID_AA64ISAR1, I8MM, 52, 4) +FIELD(ID_AA64ISAR1, XS, 56, 4) +FIELD(ID_AA64ISAR1, LS64, 60, 4) + +FIELD(ID_AA64ISAR2, WFXT, 0, 4) +FIELD(ID_AA64ISAR2, RPRES, 4, 4) +FIELD(ID_AA64ISAR2, GPA3, 8, 4) +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, LUT, 56, 4) +FIELD(ID_AA64ISAR2, ATS1A, 60, 4) + +FIELD(ID_AA64PFR0, EL0, 0, 4) +FIELD(ID_AA64PFR0, EL1, 4, 4) +FIELD(ID_AA64PFR0, EL2, 8, 4) +FIELD(ID_AA64PFR0, EL3, 12, 4) +FIELD(ID_AA64PFR0, FP, 16, 4) +FIELD(ID_AA64PFR0, ADVSIMD, 20, 4) +FIELD(ID_AA64PFR0, GIC, 24, 4) +FIELD(ID_AA64PFR0, RAS, 28, 4) +FIELD(ID_AA64PFR0, SVE, 32, 4) +FIELD(ID_AA64PFR0, SEL2, 36, 4) +FIELD(ID_AA64PFR0, MPAM, 40, 4) +FIELD(ID_AA64PFR0, AMU, 44, 4) +FIELD(ID_AA64PFR0, DIT, 48, 4) +FIELD(ID_AA64PFR0, RME, 52, 4) +FIELD(ID_AA64PFR0, CSV2, 56, 4) +FIELD(ID_AA64PFR0, CSV3, 60, 4) + +FIELD(ID_AA64PFR1, BT, 0, 4) +FIELD(ID_AA64PFR1, SSBS, 4, 4) +FIELD(ID_AA64PFR1, MTE, 8, 4) +FIELD(ID_AA64PFR1, RAS_FRAC, 12, 4) +FIELD(ID_AA64PFR1, MPAM_FRAC, 16, 4) +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) +FIELD(ID_AA64MMFR0, BIGEND, 8, 4) +FIELD(ID_AA64MMFR0, SNSMEM, 12, 4) +FIELD(ID_AA64MMFR0, BIGENDEL0, 16, 4) +FIELD(ID_AA64MMFR0, TGRAN16, 20, 4) +FIELD(ID_AA64MMFR0, TGRAN64, 24, 4) +FIELD(ID_AA64MMFR0, TGRAN4, 28, 4) +FIELD(ID_AA64MMFR0, TGRAN16_2, 32, 4) +FIELD(ID_AA64MMFR0, TGRAN64_2, 36, 4) +FIELD(ID_AA64MMFR0, TGRAN4_2, 40, 4) +FIELD(ID_AA64MMFR0, EXS, 44, 4) +FIELD(ID_AA64MMFR0, FGT, 56, 4) +FIELD(ID_AA64MMFR0, ECV, 60, 4) + +FIELD(ID_AA64MMFR1, HAFDBS, 0, 4) +FIELD(ID_AA64MMFR1, VMIDBITS, 4, 4) +FIELD(ID_AA64MMFR1, VH, 8, 4) +FIELD(ID_AA64MMFR1, HPDS, 12, 4) +FIELD(ID_AA64MMFR1, LO, 16, 4) +FIELD(ID_AA64MMFR1, PAN, 20, 4) +FIELD(ID_AA64MMFR1, SPECSEI, 24, 4) +FIELD(ID_AA64MMFR1, XNX, 28, 4) +FIELD(ID_AA64MMFR1, TWED, 32, 4) +FIELD(ID_AA64MMFR1, ETS, 36, 4) +FIELD(ID_AA64MMFR1, HCX, 40, 4) +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) +FIELD(ID_AA64MMFR2, LSM, 8, 4) +FIELD(ID_AA64MMFR2, IESB, 12, 4) +FIELD(ID_AA64MMFR2, VARANGE, 16, 4) +FIELD(ID_AA64MMFR2, CCIDX, 20, 4) +FIELD(ID_AA64MMFR2, NV, 24, 4) +FIELD(ID_AA64MMFR2, ST, 28, 4) +FIELD(ID_AA64MMFR2, AT, 32, 4) +FIELD(ID_AA64MMFR2, IDS, 36, 4) +FIELD(ID_AA64MMFR2, FWB, 40, 4) +FIELD(ID_AA64MMFR2, TTL, 48, 4) +FIELD(ID_AA64MMFR2, BBM, 52, 4) +FIELD(ID_AA64MMFR2, EVT, 56, 4) +FIELD(ID_AA64MMFR2, E0PD, 60, 4) + +FIELD(ID_AA64MMFR3, TCRX, 0, 4) +FIELD(ID_AA64MMFR3, SCTLRX, 4, 4) +FIELD(ID_AA64MMFR3, S1PIE, 8, 4) +FIELD(ID_AA64MMFR3, S2PIE, 12, 4) +FIELD(ID_AA64MMFR3, S1POE, 16, 4) +FIELD(ID_AA64MMFR3, S2POE, 20, 4) +FIELD(ID_AA64MMFR3, AIE, 24, 4) +FIELD(ID_AA64MMFR3, MEC, 28, 4) +FIELD(ID_AA64MMFR3, D128, 32, 4) +FIELD(ID_AA64MMFR3, D128_2, 36, 4) +FIELD(ID_AA64MMFR3, SNERR, 40, 4) +FIELD(ID_AA64MMFR3, ANERR, 44, 4) +FIELD(ID_AA64MMFR3, SDERR, 52, 4) +FIELD(ID_AA64MMFR3, ADERR, 56, 4) +FIELD(ID_AA64MMFR3, SPEC_FPACC, 60, 4) + +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) +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) +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) +FIELD(ID_AA64SMFR0, FA64, 63, 1) + +FIELD(ID_DFR0, COPDBG, 0, 4) +FIELD(ID_DFR0, COPSDBG, 4, 4) +FIELD(ID_DFR0, MMAPDBG, 8, 4) +FIELD(ID_DFR0, COPTRC, 12, 4) +FIELD(ID_DFR0, MMAPTRC, 16, 4) +FIELD(ID_DFR0, MPROFDBG, 20, 4) +FIELD(ID_DFR0, PERFMON, 24, 4) +FIELD(ID_DFR0, TRACEFILT, 28, 4) + +FIELD(ID_DFR1, MTPMU, 0, 4) +FIELD(ID_DFR1, HPMN0, 4, 4) + +FIELD(DBGDIDR, SE_IMP, 12, 1) +FIELD(DBGDIDR, NSUHD_IMP, 14, 1) +FIELD(DBGDIDR, VERSION, 16, 4) +FIELD(DBGDIDR, CTX_CMPS, 20, 4) +FIELD(DBGDIDR, BRPS, 24, 4) +FIELD(DBGDIDR, WRPS, 28, 4) + +FIELD(DBGDEVID, PCSAMPLE, 0, 4) +FIELD(DBGDEVID, WPADDRMASK, 4, 4) +FIELD(DBGDEVID, BPADDRMASK, 8, 4) +FIELD(DBGDEVID, VECTORCATCH, 12, 4) +FIELD(DBGDEVID, VIRTEXTNS, 16, 4) +FIELD(DBGDEVID, DOUBLELOCK, 20, 4) +FIELD(DBGDEVID, AUXREGS, 24, 4) +FIELD(DBGDEVID, CIDMASK, 28, 4) + +FIELD(DBGDEVID1, PCSROFFSET, 0, 4) + +FIELD(MVFR0, SIMDREG, 0, 4) +FIELD(MVFR0, FPSP, 4, 4) +FIELD(MVFR0, FPDP, 8, 4) +FIELD(MVFR0, FPTRAP, 12, 4) +FIELD(MVFR0, FPDIVIDE, 16, 4) +FIELD(MVFR0, FPSQRT, 20, 4) +FIELD(MVFR0, FPSHVEC, 24, 4) +FIELD(MVFR0, FPROUND, 28, 4) + +FIELD(MVFR1, FPFTZ, 0, 4) +FIELD(MVFR1, FPDNAN, 4, 4) +FIELD(MVFR1, SIMDLS, 8, 4) /* A-profile only */ +FIELD(MVFR1, SIMDINT, 12, 4) /* A-profile only */ +FIELD(MVFR1, SIMDSP, 16, 4) /* A-profile only */ +FIELD(MVFR1, SIMDHP, 20, 4) /* A-profile only */ +FIELD(MVFR1, MVE, 8, 4) /* M-profile only */ +FIELD(MVFR1, FP16, 20, 4) /* M-profile only */ +FIELD(MVFR1, FPHP, 24, 4) +FIELD(MVFR1, SIMDFMAC, 28, 4) + +FIELD(MVFR2, SIMDMISC, 0, 4) +FIELD(MVFR2, FPMISC, 4, 4) + /* * Naming convention for isar_feature functions: * Functions which test 32-bit ID registers should have _aa32_ in diff --git a/target/arm/cpu.h b/target/arm/cpu.h index d5534e358045e..2b9585dc80a63 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1994,416 +1994,6 @@ FIELD(V7M_VPR, P0, 0, 16) FIELD(V7M_VPR, MASK01, 16, 4) FIELD(V7M_VPR, MASK23, 20, 4) -/* - * System register ID fields. - */ -FIELD(CLIDR_EL1, CTYPE1, 0, 3) -FIELD(CLIDR_EL1, CTYPE2, 3, 3) -FIELD(CLIDR_EL1, CTYPE3, 6, 3) -FIELD(CLIDR_EL1, CTYPE4, 9, 3) -FIELD(CLIDR_EL1, CTYPE5, 12, 3) -FIELD(CLIDR_EL1, CTYPE6, 15, 3) -FIELD(CLIDR_EL1, CTYPE7, 18, 3) -FIELD(CLIDR_EL1, LOUIS, 21, 3) -FIELD(CLIDR_EL1, LOC, 24, 3) -FIELD(CLIDR_EL1, LOUU, 27, 3) -FIELD(CLIDR_EL1, ICB, 30, 3) - -/* When FEAT_CCIDX is implemented */ -FIELD(CCSIDR_EL1, CCIDX_LINESIZE, 0, 3) -FIELD(CCSIDR_EL1, CCIDX_ASSOCIATIVITY, 3, 21) -FIELD(CCSIDR_EL1, CCIDX_NUMSETS, 32, 24) - -/* When FEAT_CCIDX is not implemented */ -FIELD(CCSIDR_EL1, LINESIZE, 0, 3) -FIELD(CCSIDR_EL1, ASSOCIATIVITY, 3, 10) -FIELD(CCSIDR_EL1, NUMSETS, 13, 15) - -FIELD(CTR_EL0, IMINLINE, 0, 4) -FIELD(CTR_EL0, L1IP, 14, 2) -FIELD(CTR_EL0, DMINLINE, 16, 4) -FIELD(CTR_EL0, ERG, 20, 4) -FIELD(CTR_EL0, CWG, 24, 4) -FIELD(CTR_EL0, IDC, 28, 1) -FIELD(CTR_EL0, DIC, 29, 1) -FIELD(CTR_EL0, TMINLINE, 32, 6) - -FIELD(MIDR_EL1, REVISION, 0, 4) -FIELD(MIDR_EL1, PARTNUM, 4, 12) -FIELD(MIDR_EL1, ARCHITECTURE, 16, 4) -FIELD(MIDR_EL1, VARIANT, 20, 4) -FIELD(MIDR_EL1, IMPLEMENTER, 24, 8) - -FIELD(ID_ISAR0, SWAP, 0, 4) -FIELD(ID_ISAR0, BITCOUNT, 4, 4) -FIELD(ID_ISAR0, BITFIELD, 8, 4) -FIELD(ID_ISAR0, CMPBRANCH, 12, 4) -FIELD(ID_ISAR0, COPROC, 16, 4) -FIELD(ID_ISAR0, DEBUG, 20, 4) -FIELD(ID_ISAR0, DIVIDE, 24, 4) - -FIELD(ID_ISAR1, ENDIAN, 0, 4) -FIELD(ID_ISAR1, EXCEPT, 4, 4) -FIELD(ID_ISAR1, EXCEPT_AR, 8, 4) -FIELD(ID_ISAR1, EXTEND, 12, 4) -FIELD(ID_ISAR1, IFTHEN, 16, 4) -FIELD(ID_ISAR1, IMMEDIATE, 20, 4) -FIELD(ID_ISAR1, INTERWORK, 24, 4) -FIELD(ID_ISAR1, JAZELLE, 28, 4) - -FIELD(ID_ISAR2, LOADSTORE, 0, 4) -FIELD(ID_ISAR2, MEMHINT, 4, 4) -FIELD(ID_ISAR2, MULTIACCESSINT, 8, 4) -FIELD(ID_ISAR2, MULT, 12, 4) -FIELD(ID_ISAR2, MULTS, 16, 4) -FIELD(ID_ISAR2, MULTU, 20, 4) -FIELD(ID_ISAR2, PSR_AR, 24, 4) -FIELD(ID_ISAR2, REVERSAL, 28, 4) - -FIELD(ID_ISAR3, SATURATE, 0, 4) -FIELD(ID_ISAR3, SIMD, 4, 4) -FIELD(ID_ISAR3, SVC, 8, 4) -FIELD(ID_ISAR3, SYNCHPRIM, 12, 4) -FIELD(ID_ISAR3, TABBRANCH, 16, 4) -FIELD(ID_ISAR3, T32COPY, 20, 4) -FIELD(ID_ISAR3, TRUENOP, 24, 4) -FIELD(ID_ISAR3, T32EE, 28, 4) - -FIELD(ID_ISAR4, UNPRIV, 0, 4) -FIELD(ID_ISAR4, WITHSHIFTS, 4, 4) -FIELD(ID_ISAR4, WRITEBACK, 8, 4) -FIELD(ID_ISAR4, SMC, 12, 4) -FIELD(ID_ISAR4, BARRIER, 16, 4) -FIELD(ID_ISAR4, SYNCHPRIM_FRAC, 20, 4) -FIELD(ID_ISAR4, PSR_M, 24, 4) -FIELD(ID_ISAR4, SWP_FRAC, 28, 4) - -FIELD(ID_ISAR5, SEVL, 0, 4) -FIELD(ID_ISAR5, AES, 4, 4) -FIELD(ID_ISAR5, SHA1, 8, 4) -FIELD(ID_ISAR5, SHA2, 12, 4) -FIELD(ID_ISAR5, CRC32, 16, 4) -FIELD(ID_ISAR5, RDM, 24, 4) -FIELD(ID_ISAR5, VCMA, 28, 4) - -FIELD(ID_ISAR6, JSCVT, 0, 4) -FIELD(ID_ISAR6, DP, 4, 4) -FIELD(ID_ISAR6, FHM, 8, 4) -FIELD(ID_ISAR6, SB, 12, 4) -FIELD(ID_ISAR6, SPECRES, 16, 4) -FIELD(ID_ISAR6, BF16, 20, 4) -FIELD(ID_ISAR6, I8MM, 24, 4) - -FIELD(ID_MMFR0, VMSA, 0, 4) -FIELD(ID_MMFR0, PMSA, 4, 4) -FIELD(ID_MMFR0, OUTERSHR, 8, 4) -FIELD(ID_MMFR0, SHARELVL, 12, 4) -FIELD(ID_MMFR0, TCM, 16, 4) -FIELD(ID_MMFR0, AUXREG, 20, 4) -FIELD(ID_MMFR0, FCSE, 24, 4) -FIELD(ID_MMFR0, INNERSHR, 28, 4) - -FIELD(ID_MMFR1, L1HVDVA, 0, 4) -FIELD(ID_MMFR1, L1UNIVA, 4, 4) -FIELD(ID_MMFR1, L1HVDSW, 8, 4) -FIELD(ID_MMFR1, L1UNISW, 12, 4) -FIELD(ID_MMFR1, L1HVD, 16, 4) -FIELD(ID_MMFR1, L1UNI, 20, 4) -FIELD(ID_MMFR1, L1TSTCLN, 24, 4) -FIELD(ID_MMFR1, BPRED, 28, 4) - -FIELD(ID_MMFR2, L1HVDFG, 0, 4) -FIELD(ID_MMFR2, L1HVDBG, 4, 4) -FIELD(ID_MMFR2, L1HVDRNG, 8, 4) -FIELD(ID_MMFR2, HVDTLB, 12, 4) -FIELD(ID_MMFR2, UNITLB, 16, 4) -FIELD(ID_MMFR2, MEMBARR, 20, 4) -FIELD(ID_MMFR2, WFISTALL, 24, 4) -FIELD(ID_MMFR2, HWACCFLG, 28, 4) - -FIELD(ID_MMFR3, CMAINTVA, 0, 4) -FIELD(ID_MMFR3, CMAINTSW, 4, 4) -FIELD(ID_MMFR3, BPMAINT, 8, 4) -FIELD(ID_MMFR3, MAINTBCST, 12, 4) -FIELD(ID_MMFR3, PAN, 16, 4) -FIELD(ID_MMFR3, COHWALK, 20, 4) -FIELD(ID_MMFR3, CMEMSZ, 24, 4) -FIELD(ID_MMFR3, SUPERSEC, 28, 4) - -FIELD(ID_MMFR4, SPECSEI, 0, 4) -FIELD(ID_MMFR4, AC2, 4, 4) -FIELD(ID_MMFR4, XNX, 8, 4) -FIELD(ID_MMFR4, CNP, 12, 4) -FIELD(ID_MMFR4, HPDS, 16, 4) -FIELD(ID_MMFR4, LSM, 20, 4) -FIELD(ID_MMFR4, CCIDX, 24, 4) -FIELD(ID_MMFR4, EVT, 28, 4) - -FIELD(ID_MMFR5, ETS, 0, 4) -FIELD(ID_MMFR5, NTLBPA, 4, 4) - -FIELD(ID_PFR0, STATE0, 0, 4) -FIELD(ID_PFR0, STATE1, 4, 4) -FIELD(ID_PFR0, STATE2, 8, 4) -FIELD(ID_PFR0, STATE3, 12, 4) -FIELD(ID_PFR0, CSV2, 16, 4) -FIELD(ID_PFR0, AMU, 20, 4) -FIELD(ID_PFR0, DIT, 24, 4) -FIELD(ID_PFR0, RAS, 28, 4) - -FIELD(ID_PFR1, PROGMOD, 0, 4) -FIELD(ID_PFR1, SECURITY, 4, 4) -FIELD(ID_PFR1, MPROGMOD, 8, 4) -FIELD(ID_PFR1, VIRTUALIZATION, 12, 4) -FIELD(ID_PFR1, GENTIMER, 16, 4) -FIELD(ID_PFR1, SEC_FRAC, 20, 4) -FIELD(ID_PFR1, VIRT_FRAC, 24, 4) -FIELD(ID_PFR1, GIC, 28, 4) - -FIELD(ID_PFR2, CSV3, 0, 4) -FIELD(ID_PFR2, SSBS, 4, 4) -FIELD(ID_PFR2, RAS_FRAC, 8, 4) - -FIELD(ID_AA64ISAR0, AES, 4, 4) -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) -FIELD(ID_AA64ISAR0, SM4, 40, 4) -FIELD(ID_AA64ISAR0, DP, 44, 4) -FIELD(ID_AA64ISAR0, FHM, 48, 4) -FIELD(ID_AA64ISAR0, TS, 52, 4) -FIELD(ID_AA64ISAR0, TLB, 56, 4) -FIELD(ID_AA64ISAR0, RNDR, 60, 4) - -FIELD(ID_AA64ISAR1, DPB, 0, 4) -FIELD(ID_AA64ISAR1, APA, 4, 4) -FIELD(ID_AA64ISAR1, API, 8, 4) -FIELD(ID_AA64ISAR1, JSCVT, 12, 4) -FIELD(ID_AA64ISAR1, FCMA, 16, 4) -FIELD(ID_AA64ISAR1, LRCPC, 20, 4) -FIELD(ID_AA64ISAR1, GPA, 24, 4) -FIELD(ID_AA64ISAR1, GPI, 28, 4) -FIELD(ID_AA64ISAR1, FRINTTS, 32, 4) -FIELD(ID_AA64ISAR1, SB, 36, 4) -FIELD(ID_AA64ISAR1, SPECRES, 40, 4) -FIELD(ID_AA64ISAR1, BF16, 44, 4) -FIELD(ID_AA64ISAR1, DGH, 48, 4) -FIELD(ID_AA64ISAR1, I8MM, 52, 4) -FIELD(ID_AA64ISAR1, XS, 56, 4) -FIELD(ID_AA64ISAR1, LS64, 60, 4) - -FIELD(ID_AA64ISAR2, WFXT, 0, 4) -FIELD(ID_AA64ISAR2, RPRES, 4, 4) -FIELD(ID_AA64ISAR2, GPA3, 8, 4) -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, LUT, 56, 4) -FIELD(ID_AA64ISAR2, ATS1A, 60, 4) - -FIELD(ID_AA64PFR0, EL0, 0, 4) -FIELD(ID_AA64PFR0, EL1, 4, 4) -FIELD(ID_AA64PFR0, EL2, 8, 4) -FIELD(ID_AA64PFR0, EL3, 12, 4) -FIELD(ID_AA64PFR0, FP, 16, 4) -FIELD(ID_AA64PFR0, ADVSIMD, 20, 4) -FIELD(ID_AA64PFR0, GIC, 24, 4) -FIELD(ID_AA64PFR0, RAS, 28, 4) -FIELD(ID_AA64PFR0, SVE, 32, 4) -FIELD(ID_AA64PFR0, SEL2, 36, 4) -FIELD(ID_AA64PFR0, MPAM, 40, 4) -FIELD(ID_AA64PFR0, AMU, 44, 4) -FIELD(ID_AA64PFR0, DIT, 48, 4) -FIELD(ID_AA64PFR0, RME, 52, 4) -FIELD(ID_AA64PFR0, CSV2, 56, 4) -FIELD(ID_AA64PFR0, CSV3, 60, 4) - -FIELD(ID_AA64PFR1, BT, 0, 4) -FIELD(ID_AA64PFR1, SSBS, 4, 4) -FIELD(ID_AA64PFR1, MTE, 8, 4) -FIELD(ID_AA64PFR1, RAS_FRAC, 12, 4) -FIELD(ID_AA64PFR1, MPAM_FRAC, 16, 4) -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) -FIELD(ID_AA64MMFR0, BIGEND, 8, 4) -FIELD(ID_AA64MMFR0, SNSMEM, 12, 4) -FIELD(ID_AA64MMFR0, BIGENDEL0, 16, 4) -FIELD(ID_AA64MMFR0, TGRAN16, 20, 4) -FIELD(ID_AA64MMFR0, TGRAN64, 24, 4) -FIELD(ID_AA64MMFR0, TGRAN4, 28, 4) -FIELD(ID_AA64MMFR0, TGRAN16_2, 32, 4) -FIELD(ID_AA64MMFR0, TGRAN64_2, 36, 4) -FIELD(ID_AA64MMFR0, TGRAN4_2, 40, 4) -FIELD(ID_AA64MMFR0, EXS, 44, 4) -FIELD(ID_AA64MMFR0, FGT, 56, 4) -FIELD(ID_AA64MMFR0, ECV, 60, 4) - -FIELD(ID_AA64MMFR1, HAFDBS, 0, 4) -FIELD(ID_AA64MMFR1, VMIDBITS, 4, 4) -FIELD(ID_AA64MMFR1, VH, 8, 4) -FIELD(ID_AA64MMFR1, HPDS, 12, 4) -FIELD(ID_AA64MMFR1, LO, 16, 4) -FIELD(ID_AA64MMFR1, PAN, 20, 4) -FIELD(ID_AA64MMFR1, SPECSEI, 24, 4) -FIELD(ID_AA64MMFR1, XNX, 28, 4) -FIELD(ID_AA64MMFR1, TWED, 32, 4) -FIELD(ID_AA64MMFR1, ETS, 36, 4) -FIELD(ID_AA64MMFR1, HCX, 40, 4) -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) -FIELD(ID_AA64MMFR2, LSM, 8, 4) -FIELD(ID_AA64MMFR2, IESB, 12, 4) -FIELD(ID_AA64MMFR2, VARANGE, 16, 4) -FIELD(ID_AA64MMFR2, CCIDX, 20, 4) -FIELD(ID_AA64MMFR2, NV, 24, 4) -FIELD(ID_AA64MMFR2, ST, 28, 4) -FIELD(ID_AA64MMFR2, AT, 32, 4) -FIELD(ID_AA64MMFR2, IDS, 36, 4) -FIELD(ID_AA64MMFR2, FWB, 40, 4) -FIELD(ID_AA64MMFR2, TTL, 48, 4) -FIELD(ID_AA64MMFR2, BBM, 52, 4) -FIELD(ID_AA64MMFR2, EVT, 56, 4) -FIELD(ID_AA64MMFR2, E0PD, 60, 4) - -FIELD(ID_AA64MMFR3, TCRX, 0, 4) -FIELD(ID_AA64MMFR3, SCTLRX, 4, 4) -FIELD(ID_AA64MMFR3, S1PIE, 8, 4) -FIELD(ID_AA64MMFR3, S2PIE, 12, 4) -FIELD(ID_AA64MMFR3, S1POE, 16, 4) -FIELD(ID_AA64MMFR3, S2POE, 20, 4) -FIELD(ID_AA64MMFR3, AIE, 24, 4) -FIELD(ID_AA64MMFR3, MEC, 28, 4) -FIELD(ID_AA64MMFR3, D128, 32, 4) -FIELD(ID_AA64MMFR3, D128_2, 36, 4) -FIELD(ID_AA64MMFR3, SNERR, 40, 4) -FIELD(ID_AA64MMFR3, ANERR, 44, 4) -FIELD(ID_AA64MMFR3, SDERR, 52, 4) -FIELD(ID_AA64MMFR3, ADERR, 56, 4) -FIELD(ID_AA64MMFR3, SPEC_FPACC, 60, 4) - -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) -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) -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) -FIELD(ID_AA64SMFR0, FA64, 63, 1) - -FIELD(ID_DFR0, COPDBG, 0, 4) -FIELD(ID_DFR0, COPSDBG, 4, 4) -FIELD(ID_DFR0, MMAPDBG, 8, 4) -FIELD(ID_DFR0, COPTRC, 12, 4) -FIELD(ID_DFR0, MMAPTRC, 16, 4) -FIELD(ID_DFR0, MPROFDBG, 20, 4) -FIELD(ID_DFR0, PERFMON, 24, 4) -FIELD(ID_DFR0, TRACEFILT, 28, 4) - -FIELD(ID_DFR1, MTPMU, 0, 4) -FIELD(ID_DFR1, HPMN0, 4, 4) - -FIELD(DBGDIDR, SE_IMP, 12, 1) -FIELD(DBGDIDR, NSUHD_IMP, 14, 1) -FIELD(DBGDIDR, VERSION, 16, 4) -FIELD(DBGDIDR, CTX_CMPS, 20, 4) -FIELD(DBGDIDR, BRPS, 24, 4) -FIELD(DBGDIDR, WRPS, 28, 4) - -FIELD(DBGDEVID, PCSAMPLE, 0, 4) -FIELD(DBGDEVID, WPADDRMASK, 4, 4) -FIELD(DBGDEVID, BPADDRMASK, 8, 4) -FIELD(DBGDEVID, VECTORCATCH, 12, 4) -FIELD(DBGDEVID, VIRTEXTNS, 16, 4) -FIELD(DBGDEVID, DOUBLELOCK, 20, 4) -FIELD(DBGDEVID, AUXREGS, 24, 4) -FIELD(DBGDEVID, CIDMASK, 28, 4) - -FIELD(DBGDEVID1, PCSROFFSET, 0, 4) - -FIELD(MVFR0, SIMDREG, 0, 4) -FIELD(MVFR0, FPSP, 4, 4) -FIELD(MVFR0, FPDP, 8, 4) -FIELD(MVFR0, FPTRAP, 12, 4) -FIELD(MVFR0, FPDIVIDE, 16, 4) -FIELD(MVFR0, FPSQRT, 20, 4) -FIELD(MVFR0, FPSHVEC, 24, 4) -FIELD(MVFR0, FPROUND, 28, 4) - -FIELD(MVFR1, FPFTZ, 0, 4) -FIELD(MVFR1, FPDNAN, 4, 4) -FIELD(MVFR1, SIMDLS, 8, 4) /* A-profile only */ -FIELD(MVFR1, SIMDINT, 12, 4) /* A-profile only */ -FIELD(MVFR1, SIMDSP, 16, 4) /* A-profile only */ -FIELD(MVFR1, SIMDHP, 20, 4) /* A-profile only */ -FIELD(MVFR1, MVE, 8, 4) /* M-profile only */ -FIELD(MVFR1, FP16, 20, 4) /* M-profile only */ -FIELD(MVFR1, FPHP, 24, 4) -FIELD(MVFR1, SIMDFMAC, 28, 4) - -FIELD(MVFR2, SIMDMISC, 0, 4) -FIELD(MVFR2, FPMISC, 4, 4) - FIELD(GPCCR, PPS, 0, 3) FIELD(GPCCR, IRGN, 8, 2) FIELD(GPCCR, ORGN, 10, 2) From b71e2b281a23aca474e128a8487efb07e29f4019 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 23 Sep 2025 18:57:51 +0100 Subject: [PATCH 0956/1794] target/arm: Implement ID_AA64PFR2_EL1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we define the ID_AA64PFR2_EL1 encoding as reserved (with the required RAZ behaviour for unassigned system registers in the ID register encoding space). Newer architecture versions start to define fields in this ID register, so define the appropriate constants and implement it as an ID register backed by a field in cpu->isar. Since none of our CPUs set that isar field to non-zero, there is no behavioural change here (other than the name exposed to the user via the gdbstub), but this paves the way for implementing the new features that use fields in this register. The fields here are the ones documented in rev L.b of the Arm ARM. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- target/arm/cpu-features.h | 5 +++++ target/arm/cpu-sysregs.h.inc | 1 + target/arm/helper.c | 6 ++++-- target/arm/hvf/hvf.c | 1 + target/arm/hvf/sysreg.c.inc | 1 + target/arm/kvm.c | 1 + 6 files changed, 13 insertions(+), 2 deletions(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index ad571e2ffee93..602f6a88e532a 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -277,6 +277,11 @@ FIELD(ID_AA64PFR1, MTEX, 52, 4) FIELD(ID_AA64PFR1, DF2, 56, 4) FIELD(ID_AA64PFR1, PFAR, 60, 4) +FIELD(ID_AA64PFR2, MTEPERM, 0, 4) +FIELD(ID_AA64PFR2, MTESTOREONLY, 4, 4) +FIELD(ID_AA64PFR2, MTEFAR, 8, 4) +FIELD(ID_AA64PFR2, FPMR, 32, 4) + FIELD(ID_AA64MMFR0, PARANGE, 0, 4) FIELD(ID_AA64MMFR0, ASIDBITS, 4, 4) FIELD(ID_AA64MMFR0, BIGEND, 8, 4) diff --git a/target/arm/cpu-sysregs.h.inc b/target/arm/cpu-sysregs.h.inc index f48a9daa7c14c..2bb2861c62344 100644 --- a/target/arm/cpu-sysregs.h.inc +++ b/target/arm/cpu-sysregs.h.inc @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ DEF(ID_AA64PFR0_EL1, 3, 0, 0, 4, 0) DEF(ID_AA64PFR1_EL1, 3, 0, 0, 4, 1) +DEF(ID_AA64PFR2_EL1, 3, 0, 0, 4, 2) DEF(ID_AA64SMFR0_EL1, 3, 0, 0, 4, 5) DEF(ID_AA64DFR0_EL1, 3, 0, 0, 5, 0) DEF(ID_AA64DFR1_EL1, 3, 0, 0, 5, 1) diff --git a/target/arm/helper.c b/target/arm/helper.c index a18d920ac18f8..aa730addf2fc1 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6109,11 +6109,11 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, .resetvalue = GET_IDREG(isar, ID_AA64PFR1)}, - { .name = "ID_AA64PFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + { .name = "ID_AA64PFR2_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = 0 }, + .resetvalue = GET_IDREG(isar, ID_AA64PFR2)}, { .name = "ID_AA64PFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, @@ -6341,6 +6341,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) R_ID_AA64PFR1_SSBS_MASK | R_ID_AA64PFR1_MTE_MASK | R_ID_AA64PFR1_SME_MASK }, + { .name = "ID_AA64PFR2_EL1", + .exported_bits = 0 }, { .name = "ID_AA64PFR*_EL1_RESERVED", .is_glob = true }, { .name = "ID_AA64ZFR0_EL1", diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 8b467b3663884..0658a99a2d1fc 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -744,6 +744,7 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) } regs[] = { { HV_SYS_REG_ID_AA64PFR0_EL1, &host_isar.idregs[ID_AA64PFR0_EL1_IDX] }, { HV_SYS_REG_ID_AA64PFR1_EL1, &host_isar.idregs[ID_AA64PFR1_EL1_IDX] }, + /* Add ID_AA64PFR2_EL1 here when HVF supports it */ { HV_SYS_REG_ID_AA64DFR0_EL1, &host_isar.idregs[ID_AA64DFR0_EL1_IDX] }, { HV_SYS_REG_ID_AA64DFR1_EL1, &host_isar.idregs[ID_AA64DFR1_EL1_IDX] }, { HV_SYS_REG_ID_AA64ISAR0_EL1, &host_isar.idregs[ID_AA64ISAR0_EL1_IDX] }, diff --git a/target/arm/hvf/sysreg.c.inc b/target/arm/hvf/sysreg.c.inc index f2276d534e68b..067a8603fa785 100644 --- a/target/arm/hvf/sysreg.c.inc +++ b/target/arm/hvf/sysreg.c.inc @@ -92,6 +92,7 @@ DEF_SYSREG(HV_SYS_REG_ID_AA64PFR0_EL1, 3, 0, 0, 4, 0) #endif DEF_SYSREG(HV_SYS_REG_ID_AA64PFR1_EL1, 3, 0, 0, 4, 1) +/* Add ID_AA64PFR2_EL1 here when HVF supports it */ DEF_SYSREG(HV_SYS_REG_ID_AA64DFR0_EL1, 3, 0, 0, 5, 0) DEF_SYSREG(HV_SYS_REG_ID_AA64DFR1_EL1, 3, 0, 0, 5, 1) DEF_SYSREG(HV_SYS_REG_ID_AA64ISAR0_EL1, 3, 0, 0, 6, 0) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 5a75ff59271d8..b8a1c071f57a2 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -324,6 +324,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) err = 0; } else { err |= get_host_cpu_reg(fd, ahcf, ID_AA64PFR1_EL1_IDX); + err |= get_host_cpu_reg(fd, ahcf, ID_AA64PFR2_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_AA64SMFR0_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_AA64DFR0_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_AA64DFR1_EL1_IDX); From 2dad56b50d895925cd5b687bce444e90b7c132ae Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 3 Sep 2025 10:46:01 +0800 Subject: [PATCH 0957/1794] target/loongarch: Use mmu idx bitmap method when flush TLB With API tlb_flush_range_by_mmuidx(), bitmap of mmu idx should be used rather than itself. Also bitmap of MMU_KERNEL_IDX and MMU_USER_IDX are used rather than that of current running mmu idx when flush TLB. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 9365860c8c98e..0c31a346fe955 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -101,8 +101,7 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index) target_ulong addr, mask, pagesize; uint8_t tlb_ps; LoongArchTLB *tlb = &env->tlb[index]; - - int mmu_idx = cpu_mmu_index(env_cpu(env), false); + int idxmap = BIT(MMU_KERNEL_IDX) | BIT(MMU_USER_IDX); uint8_t tlb_v0 = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, V); uint8_t tlb_v1 = FIELD_EX64(tlb->tlb_entry1, TLBENTRY, V); uint64_t tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); @@ -120,12 +119,12 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index) if (tlb_v0) { tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, - mmu_idx, TARGET_LONG_BITS); + idxmap, TARGET_LONG_BITS); } if (tlb_v1) { tlb_flush_range_by_mmuidx(env_cpu(env), addr + pagesize, pagesize, - mmu_idx, TARGET_LONG_BITS); + idxmap, TARGET_LONG_BITS); } } From 5a1d5dbccc6af262419bce69068b3cb1a1ffdb4a Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 24 Jul 2025 19:57:34 +0800 Subject: [PATCH 0958/1794] target/loongarch: Add parameter tlb pointer with fill_tlb_entry With function fill_tlb_entry(), it will update LoongArch emulated TLB information. Here parameter tlb pointer is added so that TLB entry will be updated based on relative TLB CSR registers. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 0c31a346fe955..25dbbd0d77587 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -143,9 +143,8 @@ static void invalidate_tlb(CPULoongArchState *env, int index) invalidate_tlb_entry(env, index); } -static void fill_tlb_entry(CPULoongArchState *env, int index) +static void fill_tlb_entry(CPULoongArchState *env, LoongArchTLB *tlb) { - LoongArchTLB *tlb = &env->tlb[index]; uint64_t lo0, lo1, csr_vppn; uint16_t csr_asid; uint8_t csr_ps; @@ -312,7 +311,7 @@ void helper_tlbwr(CPULoongArchState *env) return; } - fill_tlb_entry(env, index); + fill_tlb_entry(env, env->tlb + index); } void helper_tlbfill(CPULoongArchState *env) @@ -350,7 +349,7 @@ void helper_tlbfill(CPULoongArchState *env) } invalidate_tlb(env, index); - fill_tlb_entry(env, index); + fill_tlb_entry(env, env->tlb + index); } void helper_tlbclr(CPULoongArchState *env) From 8fa3df98336ca2872dc51a8859142e8892844fb6 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 24 Jul 2025 20:34:35 +0800 Subject: [PATCH 0959/1794] target/loongarch: Reduce TLB flush with helper_tlbwr With function helper_tlbwr(), specified LoongArch TLB entry will be updated. There are two PTE pages in one TLB entry called even/odd pages. Supposing even/odd page is normal/none state, when odd page is added, TLB entry is changed as normal/normal state and even page keeps unchanged. In this situation, it is not necessary to flush QEMU TLB since even page keep unchanged and odd page is newly changed. Here check whether PTE page is the same or not, TLB flush can be skipped if both are the same or newly added. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 25dbbd0d77587..88dba9eb3ea44 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -302,16 +302,35 @@ void helper_tlbrd(CPULoongArchState *env) void helper_tlbwr(CPULoongArchState *env) { int index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); + LoongArchTLB *old, new = {}; + bool skip_inv = false; + uint8_t tlb_v0, tlb_v1; - invalidate_tlb(env, index); - + old = env->tlb + index; if (FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, NE)) { - env->tlb[index].tlb_misc = FIELD_DP64(env->tlb[index].tlb_misc, - TLB_MISC, E, 0); + invalidate_tlb(env, index); + old->tlb_misc = FIELD_DP64(old->tlb_misc, TLB_MISC, E, 0); return; } - fill_tlb_entry(env, env->tlb + index); + fill_tlb_entry(env, &new); + /* Check whether ASID/VPPN is the same */ + if (old->tlb_misc == new.tlb_misc) { + /* Check whether both even/odd pages is the same or invalid */ + tlb_v0 = FIELD_EX64(old->tlb_entry0, TLBENTRY, V); + tlb_v1 = FIELD_EX64(old->tlb_entry1, TLBENTRY, V); + if ((!tlb_v0 || new.tlb_entry0 == old->tlb_entry0) && + (!tlb_v1 || new.tlb_entry1 == old->tlb_entry1)) { + skip_inv = true; + } + } + + /* flush tlb before updating the entry */ + if (!skip_inv) { + invalidate_tlb(env, index); + } + + *old = new; } void helper_tlbfill(CPULoongArchState *env) From 349f3ec027cffc14ca6d7f83e2618714c7054a07 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 30 Jul 2025 10:32:54 +0800 Subject: [PATCH 0960/1794] target/loongarch: Update TLB index selection method With function helper_tlbfill(), since there is no suitable TLB entry, new TLB will be added and flush one old TLB entry. The old TLB entry index is selected randomly now, instead it can be optimized as following: 1. invalid TLB entry can be selected at first. 2. TLB entry with other ASID can be selected secondly 3. random method is used by last. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 49 ++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 88dba9eb3ea44..b46621f203ce7 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -336,8 +336,11 @@ void helper_tlbwr(CPULoongArchState *env) void helper_tlbfill(CPULoongArchState *env) { uint64_t address, entryhi; - int index, set, stlb_idx; + int index, set, i, stlb_idx; uint16_t pagesize, stlb_ps; + uint16_t asid, tlb_asid; + LoongArchTLB *tlb; + uint8_t tlb_e; if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { entryhi = env->CSR_TLBREHI; @@ -351,20 +354,52 @@ void helper_tlbfill(CPULoongArchState *env) /* Validity of stlb_ps is checked in helper_csrwr_stlbps() */ stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); + asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); if (pagesize == stlb_ps) { /* Only write into STLB bits [47:13] */ address = entryhi & ~MAKE_64BIT_MASK(0, R_CSR_TLBEHI_64_VPPN_SHIFT); - - /* Choose one set ramdomly */ - set = get_random_tlb(0, 7); - - /* Index in one set */ + set = -1; stlb_idx = (address >> (stlb_ps + 1)) & 0xff; /* [0,255] */ + for (i = 0; i < 8; ++i) { + tlb = &env->tlb[i * 256 + stlb_idx]; + tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); + if (!tlb_e) { + set = i; + break; + } + + tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); + if (asid != tlb_asid) { + set = i; + } + } + /* Choose one set randomly */ + if (set < 0) { + set = get_random_tlb(0, 7); + } index = set * 256 + stlb_idx; } else { /* Only write into MTLB */ - index = get_random_tlb(LOONGARCH_STLB, LOONGARCH_TLB_MAX - 1); + index = -1; + for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) { + tlb = &env->tlb[i]; + tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); + + if (!tlb_e) { + index = i; + break; + } + + tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); + if (asid != tlb_asid) { + index = i; + } + } + + if (index < 0) { + index = get_random_tlb(LOONGARCH_STLB, LOONGARCH_TLB_MAX - 1); + } } invalidate_tlb(env, index); From 27a26b48bfb71af85a9cf67d8702ecef31650653 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 3 Sep 2025 11:17:56 +0800 Subject: [PATCH 0961/1794] target/loongarch: Fix page size set issue with CSR_STLBPS When modify register CSR_STLBPS, the page size should come from input parameter rather than old value. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- target/loongarch/cpu-csr.h | 1 + target/loongarch/tcg/csr_helper.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/target/loongarch/cpu-csr.h b/target/loongarch/cpu-csr.h index 0834e91f30e3b..1a311bf06a5e9 100644 --- a/target/loongarch/cpu-csr.h +++ b/target/loongarch/cpu-csr.h @@ -106,6 +106,7 @@ FIELD(CSR_PWCH, DIR4_WIDTH, 18, 6) #define LOONGARCH_CSR_STLBPS 0x1e /* Stlb page size */ FIELD(CSR_STLBPS, PS, 0, 5) +FIELD(CSR_STLBPS, RESERVE, 5, 27) #define LOONGARCH_CSR_RVACFG 0x1f /* Reduced virtual address config */ FIELD(CSR_RVACFG, RBITS, 0, 4) diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c index 0d99e2c92b636..eb60fefa826fc 100644 --- a/target/loongarch/tcg/csr_helper.c +++ b/target/loongarch/tcg/csr_helper.c @@ -26,13 +26,14 @@ target_ulong helper_csrwr_stlbps(CPULoongArchState *env, target_ulong val) * The real hardware only supports the min tlb_ps is 12 * tlb_ps=0 may cause undefined-behavior. */ - uint8_t tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); + uint8_t tlb_ps = FIELD_EX64(val, CSR_STLBPS, PS); if (!check_ps(env, tlb_ps)) { qemu_log_mask(LOG_GUEST_ERROR, "Attempted set ps %d\n", tlb_ps); } else { /* Only update PS field, reserved bit keeps zero */ - env->CSR_STLBPS = FIELD_DP64(old_v, CSR_STLBPS, PS, tlb_ps); + val = FIELD_DP64(val, CSR_STLBPS, RESERVE, 0); + env->CSR_STLBPS = val; } return old_v; From e49d914ceec98a52e67438a8259768482ace639c Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Sat, 2 Aug 2025 10:58:40 +0800 Subject: [PATCH 0962/1794] target/loongarch: Add tlb search callback in loongarch_tlb_search() With function loongarch_tlb_search(), it is to search TLB entry with speficied virtual address, the difference is selection with asid and global bit. Here add selection callback with asid and global bit. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index b46621f203ce7..fda81f190a662 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -21,6 +21,13 @@ #include "cpu-csr.h" #include "tcg/tcg_loongarch.h" +typedef bool (*tlb_match)(bool global, int asid, int tlb_asid); + +static bool tlb_match_any(bool global, int asid, int tlb_asid) +{ + return global || tlb_asid == asid; +} + bool check_ps(CPULoongArchState *env, uint8_t tlb_ps) { if (tlb_ps >= 64) { @@ -201,12 +208,15 @@ static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr, { LoongArchTLB *tlb; uint16_t csr_asid, tlb_asid, stlb_idx; - uint8_t tlb_e, tlb_ps, tlb_g, stlb_ps; + uint8_t tlb_e, tlb_ps, stlb_ps; + bool tlb_g; int i, compare_shift; uint64_t vpn, tlb_vppn; + tlb_match func; + func = tlb_match_any; csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); - stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); + stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1); stlb_idx = vpn & 0xff; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */ compare_shift = stlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; @@ -218,9 +228,9 @@ static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr, if (tlb_e) { tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); + tlb_g = !!FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); - if ((tlb_g == 1 || tlb_asid == csr_asid) && + if (func(tlb_g, csr_asid, tlb_asid) && (vpn == (tlb_vppn >> compare_shift))) { *index = i * 256 + stlb_idx; return true; @@ -239,7 +249,7 @@ static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr, tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1); - if ((tlb_g == 1 || tlb_asid == csr_asid) && + if (func(tlb_g, csr_asid, tlb_asid) && (vpn == (tlb_vppn >> compare_shift))) { *index = i; return true; From 58c5522f1736404e6e6d396c1601ad3a1b15d7ed Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 4 Sep 2025 16:06:03 +0800 Subject: [PATCH 0963/1794] target/loongarch: Add common API loongarch_tlb_search_cb() Common API loongarch_tlb_search_cb() is added here to search TLB entry with specified address. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index fda81f190a662..5b78146769e1a 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -203,19 +203,16 @@ static uint32_t get_random_tlb(uint32_t low, uint32_t high) * field in tlb entry contains bit[47:13], so need adjust. * virt_vpn = vaddr[47:13] */ -static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr, - int *index) +static bool loongarch_tlb_search_cb(CPULoongArchState *env, vaddr vaddr, + int *index, int csr_asid, tlb_match func) { LoongArchTLB *tlb; - uint16_t csr_asid, tlb_asid, stlb_idx; + uint16_t tlb_asid, stlb_idx; uint8_t tlb_e, tlb_ps, stlb_ps; bool tlb_g; int i, compare_shift; uint64_t vpn, tlb_vppn; - tlb_match func; - func = tlb_match_any; - csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1); stlb_idx = vpn & 0xff; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */ @@ -259,6 +256,17 @@ static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr, return false; } +static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr, + int *index) +{ + int csr_asid; + tlb_match func; + + func = tlb_match_any; + csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); + return loongarch_tlb_search_cb(env, vaddr, index, csr_asid, func); +} + void helper_tlbsrch(CPULoongArchState *env) { int index, match; From 5e3ccedcf925ad4913baa953d05933df1f78e9ed Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 4 Sep 2025 17:32:05 +0800 Subject: [PATCH 0964/1794] target/loongarch: Change return value type with loongarch_tlb_search_cb() With function loongarch_tlb_search_cb(), change return value type from bool type to pointer LoongArchTLB *, the pointer type can be use directly in future. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- target/loongarch/tcg/tlb_helper.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 5b78146769e1a..c7f30eaf15d01 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -203,8 +203,9 @@ static uint32_t get_random_tlb(uint32_t low, uint32_t high) * field in tlb entry contains bit[47:13], so need adjust. * virt_vpn = vaddr[47:13] */ -static bool loongarch_tlb_search_cb(CPULoongArchState *env, vaddr vaddr, - int *index, int csr_asid, tlb_match func) +static LoongArchTLB *loongarch_tlb_search_cb(CPULoongArchState *env, + vaddr vaddr, int csr_asid, + tlb_match func) { LoongArchTLB *tlb; uint16_t tlb_asid, stlb_idx; @@ -229,8 +230,7 @@ static bool loongarch_tlb_search_cb(CPULoongArchState *env, vaddr vaddr, if (func(tlb_g, csr_asid, tlb_asid) && (vpn == (tlb_vppn >> compare_shift))) { - *index = i * 256 + stlb_idx; - return true; + return tlb; } } } @@ -248,12 +248,11 @@ static bool loongarch_tlb_search_cb(CPULoongArchState *env, vaddr vaddr, vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1); if (func(tlb_g, csr_asid, tlb_asid) && (vpn == (tlb_vppn >> compare_shift))) { - *index = i; - return true; + return tlb; } } } - return false; + return NULL; } static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr, @@ -261,10 +260,17 @@ static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr, { int csr_asid; tlb_match func; + LoongArchTLB *tlb; func = tlb_match_any; csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); - return loongarch_tlb_search_cb(env, vaddr, index, csr_asid, func); + tlb = loongarch_tlb_search_cb(env, vaddr, csr_asid, func); + if (tlb) { + *index = tlb - env->tlb; + return true; + } + + return false; } void helper_tlbsrch(CPULoongArchState *env) From 96e654cf3e6e03d61de447ad7117f53ed2bff998 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 4 Sep 2025 17:46:12 +0800 Subject: [PATCH 0965/1794] target/loongarch: Use loongarch_tlb_search_cb in helper_invtlb_page_asid_or_g With function helper_invtlb_page_asid_or_g(), currently it is to search TLB entry one by one. Instead STLB can be searched at first with hash method, and then search MTLB with one by one method. Here common API loongarch_tlb_search_cb() is used in function helper_invtlb_page_asid_or_g(). Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index c7f30eaf15d01..0a87354ba90dd 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -559,31 +559,16 @@ void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info, void helper_invtlb_page_asid_or_g(CPULoongArchState *env, target_ulong info, target_ulong addr) { - uint16_t asid = info & 0x3ff; - - for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { - LoongArchTLB *tlb = &env->tlb[i]; - uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); - uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - uint64_t vpn, tlb_vppn; - uint8_t tlb_ps, compare_shift; - uint8_t tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); - - if (!tlb_e) { - continue; - } - - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); - vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); - compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; + int asid = info & 0x3ff; + LoongArchTLB *tlb; + tlb_match func; - if ((tlb_g || (tlb_asid == asid)) && - (vpn == (tlb_vppn >> compare_shift))) { - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); - } + func = tlb_match_any; + tlb = loongarch_tlb_search_cb(env, addr, asid, func); + if (tlb) { + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); + tlb_flush(env_cpu(env)); } - tlb_flush(env_cpu(env)); } bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, From eae570bdcd2d22c0e4bc058faff7f5ff874cb801 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 4 Sep 2025 17:52:03 +0800 Subject: [PATCH 0966/1794] target/loongarch: Use loongarch_tlb_search_cb in helper_invtlb_page_asid With function helper_invtlb_page_asid(), currently it is to search TLB entry one by one. Instead STLB can be searched at first with hash method, and then search MTLB with one by one method Here common API loongarch_tlb_search_cb() is used in function helper_invtlb_page_asid() Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 36 +++++++++++-------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 0a87354ba90dd..7a85d9ca55ae7 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -28,6 +28,11 @@ static bool tlb_match_any(bool global, int asid, int tlb_asid) return global || tlb_asid == asid; } +static bool tlb_match_asid(bool global, int asid, int tlb_asid) +{ + return !global && tlb_asid == asid; +} + bool check_ps(CPULoongArchState *env, uint8_t tlb_ps) { if (tlb_ps >= 64) { @@ -529,31 +534,16 @@ void helper_invtlb_all_asid(CPULoongArchState *env, target_ulong info) void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info, target_ulong addr) { - uint16_t asid = info & 0x3ff; - - for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { - LoongArchTLB *tlb = &env->tlb[i]; - uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); - uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - uint64_t vpn, tlb_vppn; - uint8_t tlb_ps, compare_shift; - uint8_t tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); - - if (!tlb_e) { - continue; - } - - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); - vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); - compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; + int asid = info & 0x3ff; + LoongArchTLB *tlb; + tlb_match func; - if (!tlb_g && (tlb_asid == asid) && - (vpn == (tlb_vppn >> compare_shift))) { - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); - } + func = tlb_match_asid; + tlb = loongarch_tlb_search_cb(env, addr, asid, func); + if (tlb) { + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); + tlb_flush(env_cpu(env)); } - tlb_flush(env_cpu(env)); } void helper_invtlb_page_asid_or_g(CPULoongArchState *env, From 56db997db04b14c15fbfbdaaa5eb924c53b730fa Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 4 Sep 2025 18:07:18 +0800 Subject: [PATCH 0967/1794] target/loongarch: Invalid tlb entry in invalidate_tlb() Invalid tlb entry in function invalidate_tlb(), and its usage is simple and easy to use. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- target/loongarch/tcg/tlb_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 7a85d9ca55ae7..b777f68f7148b 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -123,6 +123,7 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index) return; } + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); pagesize = MAKE_64BIT_MASK(tlb_ps, 1); mask = MAKE_64BIT_MASK(0, tlb_ps + 1); @@ -338,7 +339,6 @@ void helper_tlbwr(CPULoongArchState *env) old = env->tlb + index; if (FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, NE)) { invalidate_tlb(env, index); - old->tlb_misc = FIELD_DP64(old->tlb_misc, TLB_MISC, E, 0); return; } From 66746876fed5eb3c04c2f958bef927df3389d42c Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 4 Sep 2025 19:11:25 +0800 Subject: [PATCH 0968/1794] target/loongarch: Only flush one TLB entry in helper_invtlb_page_asid_or_g() With function helper_invtlb_page_asid_or_g(), only one TLB entry in LoongArch emulated TLB is invalidated. so with QEMU TLB, it is not necessary to flush all QEMU TLB, only flush address range specified LoongArch emulated TLB is ok. Here invalidate_tlb_entry() is called so that only QEMU TLB entry with specified address range is flushed. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index b777f68f7148b..243f9456129b1 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -556,8 +556,7 @@ void helper_invtlb_page_asid_or_g(CPULoongArchState *env, func = tlb_match_any; tlb = loongarch_tlb_search_cb(env, addr, asid, func); if (tlb) { - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); - tlb_flush(env_cpu(env)); + invalidate_tlb(env, tlb - env->tlb); } } From 8d26856fabf8faac60de03a2e0fc35e5338e248e Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 4 Sep 2025 19:16:57 +0800 Subject: [PATCH 0969/1794] target/loongarch: Only flush one TLB entry in helper_invtlb_page_asid() With function helper_invtlb_page_asid(), only one TLB entry in LoongArch emulated TLB is invalidated. so with QEMU TLB, it is not necessary to flush all QEMU TLB, only flush address range specified LoongArch emulated TLB is ok. Here invalidate_tlb_entry() is called so that only QEMU TLB entry with specified address range is flushed. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 243f9456129b1..8cfce48a297ac 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -541,8 +541,7 @@ void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info, func = tlb_match_asid; tlb = loongarch_tlb_search_cb(env, addr, asid, func); if (tlb) { - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); - tlb_flush(env_cpu(env)); + invalidate_tlb(env, tlb - env->tlb); } } From caf1704c56a38dbd032fd0174c07eded3a624072 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 16 Sep 2025 20:20:59 +0800 Subject: [PATCH 0970/1794] target/loongarch: move some machine define to virt.h move some machine define to virt.h Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-ID: <20250916122109.749813-2-gaosong@loongson.cn> --- include/hw/loongarch/virt.h | 19 +++++++++++++++++++ target/loongarch/cpu.h | 21 --------------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 602feab0f0dc7..7120b46714f92 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -13,6 +13,25 @@ #include "hw/block/flash.h" #include "hw/loongarch/boot.h" +#define IOCSRF_TEMP 0 +#define IOCSRF_NODECNT 1 +#define IOCSRF_MSI 2 +#define IOCSRF_EXTIOI 3 +#define IOCSRF_CSRIPI 4 +#define IOCSRF_FREQCSR 5 +#define IOCSRF_FREQSCALE 6 +#define IOCSRF_DVFSV1 7 +#define IOCSRF_GMOD 9 +#define IOCSRF_VM 11 + +#define VERSION_REG 0x0 +#define FEATURE_REG 0x8 +#define VENDOR_REG 0x10 +#define CPUNAME_REG 0x20 +#define MISC_FUNC_REG 0x420 +#define IOCSRM_EXTIOI_EN 48 +#define IOCSRM_EXTIOI_INT_ENCODE 49 + #define LOONGARCH_MAX_CPUS 256 #define VIRT_FWCFG_BASE 0x1e020000UL diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index c8b96f74dc011..8c35bf120a217 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -21,27 +21,6 @@ #include "cpu-csr.h" #include "cpu-qom.h" -#define IOCSRF_TEMP 0 -#define IOCSRF_NODECNT 1 -#define IOCSRF_MSI 2 -#define IOCSRF_EXTIOI 3 -#define IOCSRF_CSRIPI 4 -#define IOCSRF_FREQCSR 5 -#define IOCSRF_FREQSCALE 6 -#define IOCSRF_DVFSV1 7 -#define IOCSRF_GMOD 9 -#define IOCSRF_VM 11 - -#define VERSION_REG 0x0 -#define FEATURE_REG 0x8 -#define VENDOR_REG 0x10 -#define CPUNAME_REG 0x20 -#define MISC_FUNC_REG 0x420 -#define IOCSRM_EXTIOI_EN 48 -#define IOCSRM_EXTIOI_INT_ENCODE 49 - -#define IOCSR_MEM_SIZE 0x428 - #define FCSR0_M1 0x1f /* FCSR1 mask, Enables */ #define FCSR0_M2 0x1f1f0000 /* FCSR2 mask, Cause and Flags */ #define FCSR0_M3 0x300 /* FCSR3 mask, Round Mode */ From 2002711e3dec0f0bb3eb86ee1e108ec9e95ed46f Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 16 Sep 2025 20:21:00 +0800 Subject: [PATCH 0971/1794] hw/loongarch: add virt feature dmsi support dmsi feature is added in LoongArchVirtMachinState, and it is used to check whether virt machine supports the directy Message-Interrupts. and by default set dmsi with ON_OFF_AUTO_AUTO. LoongArchVirtMachineState adds misc_feature and misc_status for misc features and status. and set the default dintc feature bit. Msgint feature is added in LoongArchCPU, and it is used to check whether th cpu supports the Message-Interrupts and by default set mesgint with ON_OFF_AUTO_AUTO. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-ID: <20250916122109.749813-3-gaosong@loongson.cn> --- hw/loongarch/virt.c | 50 +++++++++++++++++++++++++++++++++++++ include/hw/loongarch/virt.h | 14 +++++++++++ target/loongarch/cpu.c | 29 +++++++++++++++++++++ target/loongarch/cpu.h | 1 + 4 files changed, 94 insertions(+) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index bd5cff1f1e01d..256af63d732b0 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -48,6 +48,30 @@ #include "qemu/error-report.h" #include "kvm/kvm_loongarch.h" +static void virt_get_dmsi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + OnOffAuto dmsi = lvms->dmsi; + + visit_type_OnOffAuto(v, name, &dmsi, errp); + +} +static void virt_set_dmsi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &lvms->dmsi, errp); + + if (lvms->dmsi == ON_OFF_AUTO_OFF) { + lvms->misc_feature &= ~BIT(IOCSRF_DMSI); + lvms->misc_status &= ~BIT_ULL(IOCSRM_DMSI_EN); + } else if (lvms->dmsi == ON_OFF_AUTO_ON) { + lvms->misc_feature = BIT(IOCSRF_DMSI); + } +} + static void virt_get_veiointc(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -683,6 +707,25 @@ static void fw_cfg_add_memory(MachineState *ms) } } +static void virt_check_dmsi(MachineState *machine) +{ + LoongArchCPU *cpu; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); + + cpu = LOONGARCH_CPU(first_cpu); + if (lvms->dmsi == ON_OFF_AUTO_AUTO) { + if (cpu->msgint != ON_OFF_AUTO_OFF) { + lvms->misc_feature = BIT(IOCSRF_DMSI); + } + } + + if (lvms->dmsi == ON_OFF_AUTO_ON && cpu->msgint == ON_OFF_AUTO_OFF) { + error_report("Fail to enable dmsi , cpu msgint is off " + "pleass add cpu feature mesgint=on."); + exit(EXIT_FAILURE); + } +} + static void virt_init(MachineState *machine) { const char *cpu_model = machine->cpu_type; @@ -717,6 +760,7 @@ static void virt_init(MachineState *machine) } qdev_realize_and_unref(DEVICE(cpuobj), NULL, &error_fatal); } + virt_check_dmsi(machine); fw_cfg_add_memory(machine); /* Node0 memory */ @@ -847,6 +891,8 @@ static void virt_initfn(Object *obj) if (tcg_enabled()) { lvms->veiointc = ON_OFF_AUTO_OFF; } + + lvms->dmsi = ON_OFF_AUTO_AUTO; lvms->acpi = ON_OFF_AUTO_AUTO; lvms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); lvms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); @@ -1241,6 +1287,10 @@ static void virt_class_init(ObjectClass *oc, const void *data) NULL, NULL); object_class_property_set_description(oc, "v-eiointc", "Enable Virt Extend I/O Interrupt Controller."); + object_class_property_add(oc, "dmsi", "OnOffAuto", + virt_get_dmsi, virt_set_dmsi, NULL, NULL); + object_class_property_set_description(oc, "dmsi", + "Enable direct Message-interrupts Controller."); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); #ifdef CONFIG_TPM diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 7120b46714f92..98e9bf0737f93 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -23,6 +23,7 @@ #define IOCSRF_DVFSV1 7 #define IOCSRF_GMOD 9 #define IOCSRF_VM 11 +#define IOCSRF_DMSI 15 #define VERSION_REG 0x0 #define FEATURE_REG 0x8 @@ -31,6 +32,7 @@ #define MISC_FUNC_REG 0x420 #define IOCSRM_EXTIOI_EN 48 #define IOCSRM_EXTIOI_INT_ENCODE 49 +#define IOCSRM_DMSI_EN 51 #define LOONGARCH_MAX_CPUS 256 @@ -69,6 +71,7 @@ struct LoongArchVirtMachineState { Notifier powerdown_notifier; OnOffAuto acpi; OnOffAuto veiointc; + OnOffAuto dmsi; char *oem_id; char *oem_table_id; DeviceState *acpi_ged; @@ -84,6 +87,8 @@ struct LoongArchVirtMachineState { DeviceState *extioi; struct memmap_entry *memmap_table; unsigned int memmap_entries; + uint64_t misc_feature; + uint64_t misc_status; }; #define TYPE_LOONGARCH_VIRT_MACHINE MACHINE_TYPE_NAME("virt") @@ -91,6 +96,15 @@ OBJECT_DECLARE_SIMPLE_TYPE(LoongArchVirtMachineState, LOONGARCH_VIRT_MACHINE) void virt_acpi_setup(LoongArchVirtMachineState *lvms); void virt_fdt_setup(LoongArchVirtMachineState *lvms); +static inline bool virt_has_dmsi(LoongArchVirtMachineState *lvms) +{ + if (!(lvms->misc_feature & BIT(IOCSRF_DMSI))) { + return false; + } + + return true; +} + static inline bool virt_is_veiointc_enabled(LoongArchVirtMachineState *lvms) { if (lvms->veiointc == ON_OFF_AUTO_OFF) { diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 55ee317bf2dfe..993602fb8cb7d 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -495,6 +495,25 @@ static void loongarch_set_lasx(Object *obj, bool value, Error **errp) cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, value); } +static bool loongarch_get_msgint(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->msgint != ON_OFF_AUTO_OFF; +} + +static void loongarch_set_msgint(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu->msgint = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + + if (kvm_enabled()) { + /* kvm feature detection in function kvm_arch_init_vcpu */ + return; + } + + cpu->env.cpucfg[1] = FIELD_DP32(cpu->env.cpucfg[1], CPUCFG1, MSG_INT, value); +} + static void loongarch_cpu_post_init(Object *obj) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); @@ -507,6 +526,8 @@ static void loongarch_cpu_post_init(Object *obj) loongarch_set_lsx); object_property_add_bool(obj, "lasx", loongarch_get_lasx, loongarch_set_lasx); + object_property_add_bool(obj, "msgint", loongarch_get_msgint, + loongarch_set_msgint); /* lbt is enabled only in kvm mode, not supported in tcg mode */ if (kvm_enabled()) { kvm_loongarch_cpu_post_init(cpu); @@ -614,6 +635,7 @@ static void loongarch_la464_initfn(Object *obj) env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7); env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8); + cpu->msgint = ON_OFF_AUTO_OFF; loongarch_la464_init_csr(obj); loongarch_cpu_post_init(obj); } @@ -644,12 +666,19 @@ static void loongarch_la132_initfn(Object *obj) data = FIELD_DP32(data, CPUCFG1, HP, 1); data = FIELD_DP32(data, CPUCFG1, CRC, 1); env->cpucfg[1] = data; + cpu->msgint = ON_OFF_AUTO_OFF; } static void loongarch_max_initfn(Object *obj) { + LoongArchCPU *cpu = LOONGARCH_CPU(obj); /* '-cpu max' for TCG: we use cpu la464. */ loongarch_la464_initfn(obj); + + if (tcg_enabled()) { + cpu->env.cpucfg[1] = FIELD_DP32(cpu->env.cpucfg[1], CPUCFG1, MSG_INT, 1); + cpu->msgint = ON_OFF_AUTO_AUTO; + } } static void loongarch_cpu_reset_hold(Object *obj, ResetType type) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 8c35bf120a217..19e00325ca760 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -392,6 +392,7 @@ struct ArchCPU { OnOffAuto pmu; OnOffAuto lsx; OnOffAuto lasx; + OnOffAuto msgint; OnOffAuto kvm_pv_ipi; OnOffAuto kvm_steal_time; int32_t socket_id; /* socket-id of this CPU */ From 86f4c80ab4a0d7a76d000515425d025004d6cd8b Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 16 Sep 2025 20:21:01 +0800 Subject: [PATCH 0972/1794] hw/loongarch: add misc register support dmsi Add feature register and misc register for dmsi feature checking and setting Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-ID: <20250916122109.749813-4-gaosong@loongson.cn> --- hw/loongarch/virt.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 256af63d732b0..a89d1a1ca1653 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -564,6 +564,10 @@ static MemTxResult virt_iocsr_misc_write(void *opaque, hwaddr addr, return MEMTX_OK; } + if (virt_has_dmsi(lvms) && val & BIT_ULL(IOCSRM_DMSI_EN)) { + lvms->misc_status |= BIT_ULL(IOCSRM_DMSI_EN); + } + features = address_space_ldl(&lvms->as_iocsr, EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG, attrs, NULL); @@ -599,6 +603,9 @@ static MemTxResult virt_iocsr_misc_read(void *opaque, hwaddr addr, break; case FEATURE_REG: ret = BIT(IOCSRF_MSI) | BIT(IOCSRF_EXTIOI) | BIT(IOCSRF_CSRIPI); + if (virt_has_dmsi(lvms)) { + ret |= BIT(IOCSRF_DMSI); + } if (kvm_enabled()) { ret |= BIT(IOCSRF_VM); } @@ -628,6 +635,10 @@ static MemTxResult virt_iocsr_misc_read(void *opaque, hwaddr addr, if (features & BIT(EXTIOI_ENABLE_INT_ENCODE)) { ret |= BIT_ULL(IOCSRM_EXTIOI_INT_ENCODE); } + if (virt_has_dmsi(lvms) && + (lvms->misc_status & BIT_ULL(IOCSRM_DMSI_EN))) { + ret |= BIT_ULL(IOCSRM_DMSI_EN); + } break; default: g_assert_not_reached(); From 4d4baab24179b51072f5e182aa41d44306ed593c Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 16 Sep 2025 20:21:02 +0800 Subject: [PATCH 0973/1794] loongarch: add a direct interrupt controller device Add Loongarch direct interrupt controller device base Definition. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-ID: <20250916122109.749813-5-gaosong@loongson.cn> --- hw/intc/Kconfig | 3 ++ hw/intc/loongarch_dintc.c | 68 +++++++++++++++++++++++++++++++ hw/intc/meson.build | 1 + hw/loongarch/Kconfig | 1 + include/hw/intc/loongarch_dintc.h | 35 ++++++++++++++++ 5 files changed, 108 insertions(+) create mode 100644 hw/intc/loongarch_dintc.c create mode 100644 include/hw/intc/loongarch_dintc.h diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index 7547528f2c27b..9f456d7e4312a 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -109,3 +109,6 @@ config LOONGARCH_PCH_MSI config LOONGARCH_EXTIOI bool + +config LOONGARCH_DINTC + bool diff --git a/hw/intc/loongarch_dintc.c b/hw/intc/loongarch_dintc.c new file mode 100644 index 0000000000000..b2465cb02240b --- /dev/null +++ b/hw/intc/loongarch_dintc.c @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch direct interrupt controller. + * + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/irq.h" +#include "hw/intc/loongarch_pch_msi.h" +#include "hw/intc/loongarch_pch_pic.h" +#include "hw/intc/loongarch_dintc.h" +#include "hw/pci/msi.h" +#include "hw/misc/unimp.h" +#include "migration/vmstate.h" +#include "trace.h" +#include "hw/qdev-properties.h" + + +static void loongarch_dintc_realize(DeviceState *dev, Error **errp) +{ + LoongArchDINTCClass *lac = LOONGARCH_DINTC_GET_CLASS(dev); + + Error *local_err = NULL; + lac->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + return; +} + +static void loongarch_dintc_unrealize(DeviceState *dev) +{ + return; +} + +static void loongarch_dintc_init(Object *obj) +{ + return; +} + +static void loongarch_dintc_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + LoongArchDINTCClass *lac = LOONGARCH_DINTC_CLASS(klass); + + dc->unrealize = loongarch_dintc_unrealize; + device_class_set_parent_realize(dc, loongarch_dintc_realize, + &lac->parent_realize); +} + +static const TypeInfo loongarch_dintc_info = { + .name = TYPE_LOONGARCH_DINTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LoongArchDINTCState), + .instance_init = loongarch_dintc_init, + .class_init = loongarch_dintc_class_init, +}; + +static void loongarch_dintc_register_types(void) +{ + type_register_static(&loongarch_dintc_info); +} + +type_init(loongarch_dintc_register_types) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 3efb276b6e628..faae20b93d333 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -80,3 +80,4 @@ specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_ specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c', 'loongarch_extioi_common.c')) specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_LOONGARCH_EXTIOI'], if_true: files('loongarch_extioi_kvm.c')) +specific_ss.add(when: 'CONFIG_LOONGARCH_DINTC', if_true: files('loongarch_dintc.c')) diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index bb2838b7b53a2..8024ddf1f34c9 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -15,6 +15,7 @@ config LOONGARCH_VIRT select LOONGARCH_PCH_PIC select LOONGARCH_PCH_MSI select LOONGARCH_EXTIOI + select LOONGARCH_DINTC select LS7A_RTC select SMBIOS select ACPI_CPU_HOTPLUG diff --git a/include/hw/intc/loongarch_dintc.h b/include/hw/intc/loongarch_dintc.h new file mode 100644 index 0000000000000..aa94cd1003474 --- /dev/null +++ b/include/hw/intc/loongarch_dintc.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch direct interrupt controller definitions + * + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ + +#include "qom/object.h" +#include "hw/sysbus.h" +#include "hw/loongarch/virt.h" + + +#define NR_VECTORS 256 + +#define TYPE_LOONGARCH_DINTC "loongarch_dintc" +OBJECT_DECLARE_TYPE(LoongArchDINTCState, LoongArchDINTCClass, LOONGARCH_DINTC) + +typedef struct DINTCCore { + CPUState *cpu; + qemu_irq parent_irq; + uint64_t arch_id; +} DINTCCore; + +struct LoongArchDINTCState { + SysBusDevice parent_obj; + DINTCCore *cpu; + uint32_t num_cpu; +}; + +struct LoongArchDINTCClass { + SysBusDeviceClass parent_class; + + DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; +}; From 3ff989d566ec880a5d06de7bb65d3dc35fc3b63b Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 16 Sep 2025 20:21:03 +0800 Subject: [PATCH 0974/1794] target/loongarch: add msg interrupt CSR registers include CSR_MSGIS0-3, CSR_MSGIR and CSR_MSGIE. Reviewed-by: Bibo Mao Signed-off-by: Song Gao Message-ID: <20250916122109.749813-6-gaosong@loongson.cn> --- target/loongarch/cpu-csr.h | 3 +++ target/loongarch/cpu.h | 11 +++++++++++ target/loongarch/machine.c | 25 +++++++++++++++++++++++-- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/target/loongarch/cpu-csr.h b/target/loongarch/cpu-csr.h index 0834e91f30e3b..479267708617a 100644 --- a/target/loongarch/cpu-csr.h +++ b/target/loongarch/cpu-csr.h @@ -186,6 +186,9 @@ FIELD(CSR_MERRCTL, ISMERR, 0, 1) #define LOONGARCH_CSR_CTAG 0x98 /* TagLo + TagHi */ +#define LOONGARCH_CSR_MSGIS(N) (0xa0 + N) +#define LOONGARCH_CSR_MSGIR 0xa4 + /* Direct map windows CSRs*/ #define LOONGARCH_CSR_DMW(N) (0x180 + N) FIELD(CSR_DMW, PLV0, 0, 1) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 19e00325ca760..78210a46d85c0 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -233,6 +233,13 @@ FIELD(TLB_MISC, ASID, 1, 10) FIELD(TLB_MISC, VPPN, 13, 35) FIELD(TLB_MISC, PS, 48, 6) +/*Msg interrupt registers */ +#define N_MSGIS 4 +FIELD(CSR_MSGIS, IS, 0, 63) +FIELD(CSR_MSGIR, INTNUM, 0, 8) +FIELD(CSR_MSGIR, ACTIVE, 31, 1) +FIELD(CSR_MSGIE, PT, 0, 8) + #define LSX_LEN (128) #define LASX_LEN (256) @@ -350,6 +357,10 @@ typedef struct CPUArchState { uint64_t CSR_DBG; uint64_t CSR_DERA; uint64_t CSR_DSAVE; + /* Msg interrupt registers */ + uint64_t CSR_MSGIS[N_MSGIS]; + uint64_t CSR_MSGIR; + uint64_t CSR_MSGIE; struct { uint64_t guest_addr; } stealtime; diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c index 4e70f5c8798bd..73190fb3672a4 100644 --- a/target/loongarch/machine.c +++ b/target/loongarch/machine.c @@ -45,6 +45,26 @@ static const VMStateDescription vmstate_fpu = { }, }; +static bool msgint_needed(void *opaque) +{ + LoongArchCPU *cpu = opaque; + + return FIELD_EX64(cpu->env.cpucfg[1], CPUCFG1, MSG_INT); +} + +static const VMStateDescription vmstate_msgint = { + .name = "cpu/msgint", + .version_id = 1, + .minimum_version_id = 1, + .needed = msgint_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT64_ARRAY(env.CSR_MSGIS, LoongArchCPU, N_MSGIS), + VMSTATE_UINT64(env.CSR_MSGIR, LoongArchCPU), + VMSTATE_UINT64(env.CSR_MSGIE, LoongArchCPU), + VMSTATE_END_OF_LIST() + }, +}; + static const VMStateDescription vmstate_lsxh_reg = { .name = "lsxh_reg", .version_id = 1, @@ -168,8 +188,8 @@ static const VMStateDescription vmstate_tlb = { /* LoongArch CPU state */ const VMStateDescription vmstate_loongarch_cpu = { .name = "cpu", - .version_id = 3, - .minimum_version_id = 3, + .version_id = 4, + .minimum_version_id = 4, .fields = (const VMStateField[]) { VMSTATE_UINTTL_ARRAY(env.gpr, LoongArchCPU, 32), VMSTATE_UINTTL(env.pc, LoongArchCPU), @@ -245,6 +265,7 @@ const VMStateDescription vmstate_loongarch_cpu = { &vmstate_tlb, #endif &vmstate_lbt, + &vmstate_msgint, NULL } }; From 07f3e5203ade3fd2e3d8d0593bcdb0aa39e022d4 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 16 Sep 2025 20:21:04 +0800 Subject: [PATCH 0975/1794] hw/loongarch: DINTC add a MemoryRegion the DINTC use [2fe00000-2ff00000) Memory. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-ID: <20250916122109.749813-7-gaosong@loongson.cn> --- hw/intc/loongarch_dintc.c | 24 +++++++++++++++++++ hw/loongarch/virt.c | 38 ++++++++++++++++++++++++++++++- include/hw/intc/loongarch_dintc.h | 1 + include/hw/loongarch/virt.h | 1 + include/hw/pci-host/ls7a.h | 2 ++ 5 files changed, 65 insertions(+), 1 deletion(-) diff --git a/hw/intc/loongarch_dintc.c b/hw/intc/loongarch_dintc.c index b2465cb02240b..7173a6aa29dd8 100644 --- a/hw/intc/loongarch_dintc.c +++ b/hw/intc/loongarch_dintc.c @@ -17,6 +17,24 @@ #include "trace.h" #include "hw/qdev-properties.h" +static uint64_t loongarch_dintc_mem_read(void *opaque, + hwaddr addr, unsigned size) +{ + return 0; +} + +static void loongarch_dintc_mem_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + return; +} + + +static const MemoryRegionOps loongarch_dintc_ops = { + .read = loongarch_dintc_mem_read, + .write = loongarch_dintc_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; static void loongarch_dintc_realize(DeviceState *dev, Error **errp) { @@ -39,6 +57,12 @@ static void loongarch_dintc_unrealize(DeviceState *dev) static void loongarch_dintc_init(Object *obj) { + LoongArchDINTCState *s = LOONGARCH_DINTC(obj); + SysBusDevice *shd = SYS_BUS_DEVICE(obj); + memory_region_init_io(&s->dintc_mmio, OBJECT(s), &loongarch_dintc_ops, + s, TYPE_LOONGARCH_DINTC, VIRT_DINTC_SIZE); + sysbus_init_mmio(shd, &s->dintc_mmio); + msi_nonbroken = true; return; } diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index a89d1a1ca1653..a7171a5ecc5d1 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -28,6 +28,7 @@ #include "hw/intc/loongarch_extioi.h" #include "hw/intc/loongarch_pch_pic.h" #include "hw/intc/loongarch_pch_msi.h" +#include "hw/intc/loongarch_dintc.h" #include "hw/pci-host/ls7a.h" #include "hw/pci-host/gpex.h" #include "hw/misc/unimp.h" @@ -386,7 +387,7 @@ static void virt_cpu_irq_init(LoongArchVirtMachineState *lvms) static void virt_irq_init(LoongArchVirtMachineState *lvms) { DeviceState *pch_pic, *pch_msi; - DeviceState *ipi, *extioi; + DeviceState *ipi, *extioi, *dintc; SysBusDevice *d; int i, start, num; @@ -432,6 +433,33 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) * +--------+ +---------+ +---------+ * | UARTs | | Devices | | Devices | * +--------+ +---------+ +---------+ + * + * + * Advanced Extended IRQ model + * + * +-----+ +---------------------------------+ +-------+ + * | IPI | --> | CPUINTC | <-- | Timer | + * +-----+ +---------------------------------+ +-------+ + * ^ ^ ^ + * | | | + * +-------------+ +----------+ +---------+ +-------+ + * | EIOINTC | | DINTC | | LIOINTC | <-- | UARTs | + * +-------------+ +----------+ +---------+ +-------+ + * ^ ^ ^ + * | | | + * +---------+ +---------+ | + * | PCH-PIC | | PCH-MSI | | + * +---------+ +---------+ | + * ^ ^ ^ | + * | | | | + * +---------+ +---------+ +---------+ + * | Devices | | PCH-LPC | | Devices | + * +---------+ +---------+ +---------+ + * ^ + * | + * +---------+ + * | Devices | + * +---------+ */ /* Create IPI device */ @@ -439,6 +467,14 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) lvms->ipi = ipi; sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); + /* Create DINTC device*/ + if (virt_has_dmsi(lvms)) { + dintc = qdev_new(TYPE_LOONGARCH_DINTC); + lvms->dintc = dintc; + sysbus_realize_and_unref(SYS_BUS_DEVICE(dintc), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dintc), 0, VIRT_DINTC_BASE); + } + /* Create EXTIOI device */ extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); lvms->extioi = extioi; diff --git a/include/hw/intc/loongarch_dintc.h b/include/hw/intc/loongarch_dintc.h index aa94cd1003474..0b0b5347b2c8f 100644 --- a/include/hw/intc/loongarch_dintc.h +++ b/include/hw/intc/loongarch_dintc.h @@ -23,6 +23,7 @@ typedef struct DINTCCore { struct LoongArchDINTCState { SysBusDevice parent_obj; + MemoryRegion dintc_mmio; DINTCCore *cpu; uint32_t num_cpu; }; diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 98e9bf0737f93..cd97bdfb8d45a 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -89,6 +89,7 @@ struct LoongArchVirtMachineState { unsigned int memmap_entries; uint64_t misc_feature; uint64_t misc_status; + DeviceState *dintc; }; #define TYPE_LOONGARCH_VIRT_MACHINE MACHINE_TYPE_NAME("virt") diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h index 79d4ea8501274..bfdbfe36146da 100644 --- a/include/hw/pci-host/ls7a.h +++ b/include/hw/pci-host/ls7a.h @@ -24,6 +24,8 @@ #define VIRT_PCH_REG_BASE 0x10000000UL #define VIRT_IOAPIC_REG_BASE (VIRT_PCH_REG_BASE) #define VIRT_PCH_MSI_ADDR_LOW 0x2FF00000UL +#define VIRT_DINTC_SIZE 0x100000UL +#define VIRT_DINTC_BASE 0x2FE00000UL #define VIRT_PCH_REG_SIZE 0x400 #define VIRT_PCH_MSI_SIZE 0x8 From 386aef3e4a7fbfb8c051f8c24832492288fe6185 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 16 Sep 2025 20:21:05 +0800 Subject: [PATCH 0976/1794] hw/loongarch: Implement dintc realize and unrealize Implement th DINTC realize and unrealize. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-ID: <20250916122109.749813-8-gaosong@loongson.cn> --- hw/intc/loongarch_dintc.c | 23 ++++++++++++++++++++++- target/loongarch/cpu.h | 3 ++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/hw/intc/loongarch_dintc.c b/hw/intc/loongarch_dintc.c index 7173a6aa29dd8..598c666ec62f5 100644 --- a/hw/intc/loongarch_dintc.c +++ b/hw/intc/loongarch_dintc.c @@ -38,7 +38,12 @@ static const MemoryRegionOps loongarch_dintc_ops = { static void loongarch_dintc_realize(DeviceState *dev, Error **errp) { + LoongArchDINTCState *s = LOONGARCH_DINTC(dev); LoongArchDINTCClass *lac = LOONGARCH_DINTC_GET_CLASS(dev); + MachineState *machine = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(machine); + const CPUArchIdList *id_list; + int i; Error *local_err = NULL; lac->parent_realize(dev, &local_err); @@ -47,12 +52,28 @@ static void loongarch_dintc_realize(DeviceState *dev, Error **errp) return; } + assert(mc->possible_cpu_arch_ids); + id_list = mc->possible_cpu_arch_ids(machine); + s->num_cpu = id_list->len; + s->cpu = g_new(DINTCCore, s->num_cpu); + if (s->cpu == NULL) { + error_setg(errp, "Memory allocation for DINTCCore fail"); + return; + } + + for (i = 0; i < s->num_cpu; i++) { + s->cpu[i].arch_id = id_list->cpus[i].arch_id; + s->cpu[i].cpu = CPU(id_list->cpus[i].cpu); + qdev_init_gpio_out(dev, &s->cpu[i].parent_irq, 1); + } + return; } static void loongarch_dintc_unrealize(DeviceState *dev) { - return; + LoongArchDINTCState *s = LOONGARCH_DINTC(dev); + g_free(s->cpu); } static void loongarch_dintc_init(Object *obj) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 78210a46d85c0..b8e3b46c3a012 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -217,9 +217,10 @@ FIELD(CSR_CRMD, WE, 9, 1) extern const char * const regnames[32]; extern const char * const fregnames[32]; -#define N_IRQS 13 +#define N_IRQS 15 #define IRQ_TIMER 11 #define IRQ_IPI 12 +#define INT_DMSI 14 #define LOONGARCH_STLB 2048 /* 2048 STLB */ #define LOONGARCH_MTLB 64 /* 64 MTLB */ From 0d148eaf5a3eb14469b536e37bb1936fd0085f03 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 16 Sep 2025 20:21:06 +0800 Subject: [PATCH 0977/1794] hw/loongarch: Implement dintc set irq Implement dintc set irq and update CSR_MSGIS. Reviewed-by: Bibo Mao Signed-off-by: Song Gao Message-ID: <20250916122109.749813-9-gaosong@loongson.cn> --- hw/intc/loongarch_dintc.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/hw/intc/loongarch_dintc.c b/hw/intc/loongarch_dintc.c index 598c666ec62f5..962fe10bf84f8 100644 --- a/hw/intc/loongarch_dintc.c +++ b/hw/intc/loongarch_dintc.c @@ -16,6 +16,14 @@ #include "migration/vmstate.h" #include "trace.h" #include "hw/qdev-properties.h" +#include "target/loongarch/cpu.h" +#include "qemu/error-report.h" +#include "system/hw_accel.h" + +/* msg addr field */ +FIELD(MSG_ADDR, IRQ_NUM, 4, 8) +FIELD(MSG_ADDR, CPU_NUM, 12, 8) +FIELD(MSG_ADDR, FIX, 28, 12) static uint64_t loongarch_dintc_mem_read(void *opaque, hwaddr addr, unsigned size) @@ -23,13 +31,33 @@ static uint64_t loongarch_dintc_mem_read(void *opaque, return 0; } +static void do_set_vcpu_dintc_irq(CPUState *cs, run_on_cpu_data data) +{ + int irq = data.host_int; + CPULoongArchState *env; + + env = &LOONGARCH_CPU(cs)->env; + cpu_synchronize_state(cs); + set_bit(irq, (unsigned long *)&env->CSR_MSGIS); +} + static void loongarch_dintc_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - return; + int irq_num, cpu_num = 0; + LoongArchDINTCState *s = LOONGARCH_DINTC(opaque); + uint64_t msg_addr = addr + VIRT_DINTC_BASE; + CPUState *cs; + + cpu_num = FIELD_EX64(msg_addr, MSG_ADDR, CPU_NUM); + cs = cpu_by_arch_id(cpu_num); + irq_num = FIELD_EX64(msg_addr, MSG_ADDR, IRQ_NUM); + + async_run_on_cpu(cs, do_set_vcpu_dintc_irq, + RUN_ON_CPU_HOST_INT(irq_num)); + qemu_set_irq(s->cpu[cpu_num].parent_irq, 1); } - static const MemoryRegionOps loongarch_dintc_ops = { .read = loongarch_dintc_mem_read, .write = loongarch_dintc_mem_write, From c2396bfd4892091032a482118895a02ac87ab3e0 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 16 Sep 2025 20:21:07 +0800 Subject: [PATCH 0978/1794] target/loongarch: Add CSR_ESTAT.bit15 and CSR_ECFG.bit15 for msg interrupts. Add CSR_ESTAT.bit15 and CSR_ECFG.bit15 for DINTC irq. Reviewed-by: Bibo Mao Signed-off-by: Song Gao Message-ID: <20250916122109.749813-10-gaosong@loongson.cn> --- target/loongarch/cpu-csr.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/loongarch/cpu-csr.h b/target/loongarch/cpu-csr.h index 479267708617a..f296eb8d0612c 100644 --- a/target/loongarch/cpu-csr.h +++ b/target/loongarch/cpu-csr.h @@ -34,11 +34,13 @@ FIELD(CSR_MISC, ALCL, 12, 4) FIELD(CSR_MISC, DWPL, 16, 3) #define LOONGARCH_CSR_ECFG 0x4 /* Exception config */ -FIELD(CSR_ECFG, LIE, 0, 13) +FIELD(CSR_ECFG, LIE, 0, 15) /* bit 15 is msg interrupt enabled */ +FIELD(CSR_ECFG, MSGINT, 14, 1) FIELD(CSR_ECFG, VS, 16, 3) #define LOONGARCH_CSR_ESTAT 0x5 /* Exception status */ -FIELD(CSR_ESTAT, IS, 0, 13) +FIELD(CSR_ESTAT, IS, 0, 15) /* bit 15 is msg interrupt enabled */ +FIELD(CSR_ESTAT, MSGINT, 14, 1) FIELD(CSR_ESTAT, ECODE, 16, 6) FIELD(CSR_ESTAT, ESUBCODE, 22, 9) From ce47eaadbd6acb466095465049f8433459b022a8 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 16 Sep 2025 20:21:08 +0800 Subject: [PATCH 0979/1794] target/loongarch:Implement csrrd CSR_MSGIR register implement the read-clear feature for CSR_MSGIR register. Reviewed-by: Bibo Mao Signed-off-by: Song Gao Message-ID: <20250916122109.749813-11-gaosong@loongson.cn> --- target/loongarch/csr.c | 5 +++++ target/loongarch/tcg/csr_helper.c | 21 +++++++++++++++++++ target/loongarch/tcg/helper.h | 1 + .../tcg/insn_trans/trans_privileged.c.inc | 1 + 4 files changed, 28 insertions(+) diff --git a/target/loongarch/csr.c b/target/loongarch/csr.c index 7ea0a3045066d..f973780bba367 100644 --- a/target/loongarch/csr.c +++ b/target/loongarch/csr.c @@ -97,6 +97,11 @@ static CSRInfo csr_info[] = { CSR_OFF(DBG), CSR_OFF(DERA), CSR_OFF(DSAVE), + CSR_OFF_ARRAY(MSGIS, 0), + CSR_OFF_ARRAY(MSGIS, 1), + CSR_OFF_ARRAY(MSGIS, 2), + CSR_OFF_ARRAY(MSGIS, 3), + CSR_OFF(MSGIR), }; CSRInfo *get_csr(unsigned int csr_num) diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c index 0d99e2c92b636..7bfe6c6c0cf8b 100644 --- a/target/loongarch/tcg/csr_helper.c +++ b/target/loongarch/tcg/csr_helper.c @@ -73,6 +73,27 @@ target_ulong helper_csrrd_tval(CPULoongArchState *env) return cpu_loongarch_get_constant_timer_ticks(cpu); } +target_ulong helper_csrrd_msgir(CPULoongArchState *env) +{ + int irq, new; + + irq = find_first_bit((unsigned long *)env->CSR_MSGIS, 256); + if (irq < 256) { + clear_bit(irq, (unsigned long *)env->CSR_MSGIS); + new = find_first_bit((unsigned long *)env->CSR_MSGIS, 256); + if (new < 256) { + return irq; + } + + env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, MSGINT, 0); + } else { + /* bit 31 set 1 for no invalid irq */ + irq = BIT(31); + } + + return irq; +} + target_ulong helper_csrwr_estat(CPULoongArchState *env, target_ulong val) { int64_t old_v = env->CSR_ESTAT; diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h index 1d5cb0198c971..db57dbfc16763 100644 --- a/target/loongarch/tcg/helper.h +++ b/target/loongarch/tcg/helper.h @@ -100,6 +100,7 @@ DEF_HELPER_1(rdtime_d, i64, env) DEF_HELPER_1(csrrd_pgd, i64, env) DEF_HELPER_1(csrrd_cpuid, i64, env) DEF_HELPER_1(csrrd_tval, i64, env) +DEF_HELPER_1(csrrd_msgir, i64, env) DEF_HELPER_2(csrwr_stlbps, i64, env, tl) DEF_HELPER_2(csrwr_estat, i64, env, tl) DEF_HELPER_2(csrwr_asid, i64, env, tl) diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc index 34cfab8879072..a407ab51b74f2 100644 --- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc @@ -83,6 +83,7 @@ void loongarch_csr_translate_init(void) SET_CSR_FUNC(TCFG, NULL, gen_helper_csrwr_tcfg); SET_CSR_FUNC(TVAL, gen_helper_csrrd_tval, NULL); SET_CSR_FUNC(TICLR, NULL, gen_helper_csrwr_ticlr); + SET_CSR_FUNC(MSGIR, gen_helper_csrrd_msgir, NULL); } #undef SET_CSR_FUNC From 7470657ec157d4526752147165b2d368e2c7002e Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 16 Sep 2025 20:21:09 +0800 Subject: [PATCH 0980/1794] hw/loongarch: Implement DINTC plug/unplug interfaces when cpu added, connect dintc irq to cpu INT_DMSI irq pin. Reviewed-by: Bibo Mao Signed-off-by: Song Gao Message-ID: <20250916122109.749813-12-gaosong@loongson.cn> --- hw/intc/loongarch_dintc.c | 71 +++++++++++++++++++++++++++++++++++++++ hw/loongarch/virt.c | 11 ++++++ 2 files changed, 82 insertions(+) diff --git a/hw/intc/loongarch_dintc.c b/hw/intc/loongarch_dintc.c index 962fe10bf84f8..dc8f7ffdf6c1c 100644 --- a/hw/intc/loongarch_dintc.c +++ b/hw/intc/loongarch_dintc.c @@ -115,14 +115,81 @@ static void loongarch_dintc_init(Object *obj) return; } +static DINTCCore *loongarch_dintc_get_cpu(LoongArchDINTCState *s, + DeviceState *dev) +{ + CPUClass *k = CPU_GET_CLASS(dev); + uint64_t arch_id = k->get_arch_id(CPU(dev)); + int i; + + for (i = 0; i < s->num_cpu; i++) { + if (s->cpu[i].arch_id == arch_id) { + return &s->cpu[i]; + } + } + + return NULL; +} + +static void loongarch_dintc_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongArchDINTCState *s = LOONGARCH_DINTC(hotplug_dev); + Object *obj = OBJECT(dev); + DINTCCore *core; + int index; + + if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { + warn_report("LoongArch DINTC: Invalid %s device type", + object_get_typename(obj)); + return; + } + core = loongarch_dintc_get_cpu(s, dev); + if (!core) { + return; + } + + core->cpu = CPU(dev); + index = core - s->cpu; + + /* connect dintc msg irq to cpu irq */ + qdev_connect_gpio_out(DEVICE(s), index, qdev_get_gpio_in(dev, INT_DMSI)); + return; +} + +static void loongarch_dintc_cpu_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongArchDINTCState *s = LOONGARCH_DINTC(hotplug_dev); + Object *obj = OBJECT(dev); + DINTCCore *core; + + if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { + warn_report("LoongArch DINTC: Invalid %s device type", + object_get_typename(obj)); + return; + } + + core = loongarch_dintc_get_cpu(s, dev); + + if (!core) { + return; + } + + core->cpu = NULL; +} + static void loongarch_dintc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); LoongArchDINTCClass *lac = LOONGARCH_DINTC_CLASS(klass); dc->unrealize = loongarch_dintc_unrealize; device_class_set_parent_realize(dc, loongarch_dintc_realize, &lac->parent_realize); + hc->plug = loongarch_dintc_cpu_plug; + hc->unplug = loongarch_dintc_cpu_unplug; } static const TypeInfo loongarch_dintc_info = { @@ -131,6 +198,10 @@ static const TypeInfo loongarch_dintc_info = { .instance_size = sizeof(LoongArchDINTCState), .instance_init = loongarch_dintc_init, .class_init = loongarch_dintc_class_init, + .interfaces = (const InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, }; static void loongarch_dintc_register_types(void) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index a7171a5ecc5d1..c1760423ee1f9 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -381,6 +381,10 @@ static void virt_cpu_irq_init(LoongArchVirtMachineState *lvms) &error_abort); hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), DEVICE(cs), &error_abort); + if (lvms->dintc) { + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->dintc), DEVICE(cs), + &error_abort); + } } } @@ -1103,6 +1107,9 @@ static void virt_cpu_unplug(HotplugHandler *hotplug_dev, /* Notify ipi and extioi irqchip to remove interrupt routing to CPU */ hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->ipi), dev, &error_abort); hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->extioi), dev, &error_abort); + if (lvms->dintc) { + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->dintc), dev, &error_abort); + } /* Notify acpi ged CPU removed */ hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &error_abort); @@ -1127,6 +1134,10 @@ static void virt_cpu_plug(HotplugHandler *hotplug_dev, hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), dev, &error_abort); } + if (lvms->dintc) { + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->dintc), dev, &error_abort); + } + if (lvms->acpi_ged) { hotplug_handler_plug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &error_abort); From 98ee172538abdc1ac42d094bc8859765e5f8a79b Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 25 Sep 2025 23:00:42 +0530 Subject: [PATCH 0981/1794] ppc/pnv: Introduce Pnv11Chip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement Pnv11Chip, currently without chiptod, xive and phb. Chiptod, XIVE, PHB are implemented in later patches. Since Power11 core is same as Power10, the implementation of Pnv11Chip is a duplicate of corresponding Pnv10Chip. Reviewed-by: Cédric Le Goater Signed-off-by: Aditya Gupta Tested-by: Amit Machhiwal Tested-by: Cédric Le Goater Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925173049.891406-2-adityag@linux.ibm.com Message-ID: <20250925173049.891406-2-adityag@linux.ibm.com> --- hw/ppc/pnv.c | 325 +++++++++++++++++++++++++++++++++++++ hw/ppc/pnv_core.c | 17 ++ include/hw/ppc/pnv.h | 20 +++ include/hw/ppc/pnv_chip.h | 7 + include/hw/ppc/pnv_xscom.h | 49 ++++++ 5 files changed, 418 insertions(+) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 9c74f46091a7f..77136091bbcad 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -491,6 +491,37 @@ static void pnv_chip_power10_dt_populate(PnvChip *chip, void *fdt) pnv_dt_lpc(chip, fdt, 0, PNV10_LPCM_BASE(chip), PNV10_LPCM_SIZE); } +static void pnv_chip_power11_dt_populate(PnvChip *chip, void *fdt) +{ + static const char compat[] = "ibm,power11-xscom\0ibm,xscom"; + int i; + + pnv_dt_xscom(chip, fdt, 0, + cpu_to_be64(PNV11_XSCOM_BASE(chip)), + cpu_to_be64(PNV11_XSCOM_SIZE), + compat, sizeof(compat)); + + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pnv_core = chip->cores[i]; + int offset; + + offset = pnv_dt_core(chip, pnv_core, fdt); + + _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", + pa_features_31, sizeof(pa_features_31)))); + + if (pnv_core->big_core) { + i++; /* Big-core groups two QEMU cores */ + } + } + + if (chip->ram_size) { + pnv_dt_memory(fdt, chip->chip_id, chip->ram_start, chip->ram_size); + } + + pnv_dt_lpc(chip, fdt, 0, PNV11_LPCM_BASE(chip), PNV11_LPCM_SIZE); +} + static void pnv_dt_rtc(ISADevice *d, void *fdt, int lpc_off) { uint32_t io_base = d->ioport_id; @@ -823,6 +854,26 @@ static ISABus *pnv_chip_power10_isa_create(PnvChip *chip, Error **errp) return pnv_lpc_isa_create(&chip10->lpc, false, errp); } +static ISABus *pnv_chip_power11_isa_create(PnvChip *chip, Error **errp) +{ + Pnv11Chip *chip11 = PNV11_CHIP(chip); + qemu_irq irq; + + irq = qdev_get_gpio_in(DEVICE(&chip11->psi), PSIHB9_IRQ_LPCHC); + qdev_connect_gpio_out_named(DEVICE(&chip11->lpc), "LPCHC", 0, irq); + + irq = qdev_get_gpio_in(DEVICE(&chip11->psi), PSIHB9_IRQ_LPC_SIRQ0); + qdev_connect_gpio_out_named(DEVICE(&chip11->lpc), "SERIRQ", 0, irq); + irq = qdev_get_gpio_in(DEVICE(&chip11->psi), PSIHB9_IRQ_LPC_SIRQ1); + qdev_connect_gpio_out_named(DEVICE(&chip11->lpc), "SERIRQ", 1, irq); + irq = qdev_get_gpio_in(DEVICE(&chip11->psi), PSIHB9_IRQ_LPC_SIRQ2); + qdev_connect_gpio_out_named(DEVICE(&chip11->lpc), "SERIRQ", 2, irq); + irq = qdev_get_gpio_in(DEVICE(&chip11->psi), PSIHB9_IRQ_LPC_SIRQ3); + qdev_connect_gpio_out_named(DEVICE(&chip11->lpc), "SERIRQ", 3, irq); + + return pnv_lpc_isa_create(&chip11->lpc, false, errp); +} + static ISABus *pnv_isa_create(PnvChip *chip, Error **errp) { return PNV_CHIP_GET_CLASS(chip)->isa_create(chip, errp); @@ -886,6 +937,12 @@ static uint64_t pnv_chip_power10_xscom_core_base(PnvChip *chip, return PNV10_XSCOM_EC_BASE(core_id); } +static uint64_t pnv_chip_power11_xscom_core_base(PnvChip *chip, + uint32_t core_id) +{ + return PNV11_XSCOM_EC_BASE(core_id); +} + static bool pnv_match_cpu(const char *default_type, const char *cpu_type) { PowerPCCPUClass *ppc_default = @@ -915,6 +972,13 @@ static void pnv_chip_power10_pic_print_info(PnvChip *chip, GString *buf) pnv_chip_power9_pic_print_info_child, buf); } +static void pnv_chip_power11_pic_print_info(PnvChip *chip, GString *buf) +{ + Pnv11Chip *chip11 = PNV11_CHIP(chip); + + pnv_psi_pic_print_info(&chip11->psi, buf); +} + /* Always give the first 1GB to chip 0 else we won't boot */ static uint64_t pnv_chip_get_ram_size(PnvMachineState *pnv, int chip_id) { @@ -1452,6 +1516,8 @@ static void pnv_chip_power10_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, #define POWER10_CORE_MASK (0xffffffffffffffull) +#define POWER11_CORE_MASK (0xffffffffffffffull) + static void pnv_chip_power8_instance_init(Object *obj) { Pnv8Chip *chip8 = PNV8_CHIP(obj); @@ -2350,6 +2416,219 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) } } +static void pnv_chip_power11_instance_init(Object *obj) +{ + Pnv11Chip *chip11 = PNV11_CHIP(obj); + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(obj); + int i; + + object_initialize_child(obj, "adu", &chip11->adu, TYPE_PNV_ADU); + + /* + * Use Power10 device models for PSI/LPC/OCC/SBE/HOMER as corresponding + * device models for Power11 are same + */ + object_initialize_child(obj, "psi", &chip11->psi, TYPE_PNV10_PSI); + object_initialize_child(obj, "lpc", &chip11->lpc, TYPE_PNV10_LPC); + object_initialize_child(obj, "occ", &chip11->occ, TYPE_PNV10_OCC); + object_initialize_child(obj, "sbe", &chip11->sbe, TYPE_PNV10_SBE); + object_initialize_child(obj, "homer", &chip11->homer, TYPE_PNV10_HOMER); + object_initialize_child(obj, "n1-chiplet", &chip11->n1_chiplet, + TYPE_PNV_N1_CHIPLET); + + for (i = 0; i < pcc->i2c_num_engines; i++) { + object_initialize_child(obj, "i2c[*]", &chip11->i2c[i], TYPE_PNV_I2C); + } + + for (i = 0; i < PNV10_CHIP_MAX_PIB_SPIC; i++) { + object_initialize_child(obj, "pib_spic[*]", &chip11->pib_spic[i], + TYPE_PNV_SPI); + } +} + +static void pnv_chip_power11_quad_realize(Pnv11Chip *chip11, Error **errp) +{ + PnvChip *chip = PNV_CHIP(chip11); + int i; + + chip11->nr_quads = DIV_ROUND_UP(chip->nr_cores, 4); + chip11->quads = g_new0(PnvQuad, chip11->nr_quads); + + for (i = 0; i < chip11->nr_quads; i++) { + PnvQuad *eq = &chip11->quads[i]; + + pnv_chip_quad_realize_one(chip, eq, chip->cores[i * 4], + PNV_QUAD_TYPE_NAME("power11")); + + pnv_xscom_add_subregion(chip, PNV11_XSCOM_EQ_BASE(eq->quad_id), + &eq->xscom_regs); + + pnv_xscom_add_subregion(chip, PNV11_XSCOM_QME_BASE(eq->quad_id), + &eq->xscom_qme_regs); + } +} + +static void pnv_chip_power11_realize(DeviceState *dev, Error **errp) +{ + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); + PnvChip *chip = PNV_CHIP(dev); + Pnv11Chip *chip11 = PNV11_CHIP(dev); + PowerPCCPU *cpu; + PowerPCCPUClass *cpu_class; + Error *local_err = NULL; + int i; + + /* XSCOM bridge is first */ + pnv_xscom_init(chip, PNV11_XSCOM_SIZE, PNV11_XSCOM_BASE(chip)); + + pcc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Set handlers for Special registers, such as SPRD */ + cpu = chip->cores[0]->threads[0]; + cpu_class = POWERPC_CPU_GET_CLASS(cpu); + cpu_class->load_sprd = pnv_handle_sprd_load; + cpu_class->store_sprd = pnv_handle_sprd_store; + + /* ADU */ + object_property_set_link(OBJECT(&chip11->adu), "lpc", OBJECT(&chip11->lpc), + &error_abort); + if (!qdev_realize(DEVICE(&chip11->adu), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_ADU_BASE, + &chip11->adu.xscom_regs); + + pnv_chip_power11_quad_realize(chip11, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* WIP: XIVE added in future patch */ + + /* Processor Service Interface (PSI) Host Bridge */ + object_property_set_int(OBJECT(&chip11->psi), "bar", + PNV11_PSIHB_BASE(chip), &error_fatal); + /* PSI can be configured to use 64k ESB pages on Power11 */ + object_property_set_int(OBJECT(&chip11->psi), "shift", XIVE_ESB_64K, + &error_fatal); + if (!qdev_realize(DEVICE(&chip11->psi), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_PSIHB_BASE, + &PNV_PSI(&chip11->psi)->xscom_regs); + + /* LPC */ + if (!qdev_realize(DEVICE(&chip11->lpc), NULL, errp)) { + return; + } + memory_region_add_subregion(get_system_memory(), PNV11_LPCM_BASE(chip), + &chip11->lpc.xscom_regs); + + chip->fw_mr = &chip11->lpc.isa_fw; + chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0", + (uint64_t) PNV11_LPCM_BASE(chip)); + + /* HOMER (must be created before OCC) */ + object_property_set_link(OBJECT(&chip11->homer), "chip", OBJECT(chip), + &error_abort); + if (!qdev_realize(DEVICE(&chip11->homer), NULL, errp)) { + return; + } + /* Homer Xscom region */ + pnv_xscom_add_subregion(chip, PNV11_XSCOM_PBA_BASE, + &chip11->homer.pba_regs); + /* Homer RAM region */ + memory_region_add_subregion(get_system_memory(), chip11->homer.base, + &chip11->homer.mem); + + /* Create the simplified OCC model */ + object_property_set_link(OBJECT(&chip11->occ), "homer", + OBJECT(&chip11->homer), &error_abort); + if (!qdev_realize(DEVICE(&chip11->occ), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_OCC_BASE, + &chip11->occ.xscom_regs); + qdev_connect_gpio_out(DEVICE(&chip11->occ), 0, qdev_get_gpio_in( + DEVICE(&chip11->psi), PSIHB9_IRQ_OCC)); + + /* OCC SRAM model */ + memory_region_add_subregion(get_system_memory(), + PNV11_OCC_SENSOR_BASE(chip), + &chip11->occ.sram_regs); + + /* SBE */ + if (!qdev_realize(DEVICE(&chip11->sbe), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_SBE_CTRL_BASE, + &chip11->sbe.xscom_ctrl_regs); + pnv_xscom_add_subregion(chip, PNV11_XSCOM_SBE_MBOX_BASE, + &chip11->sbe.xscom_mbox_regs); + qdev_connect_gpio_out(DEVICE(&chip11->sbe), 0, qdev_get_gpio_in( + DEVICE(&chip11->psi), PSIHB9_IRQ_PSU)); + + /* N1 chiplet */ + if (!qdev_realize(DEVICE(&chip11->n1_chiplet), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_N1_CHIPLET_CTRL_REGS_BASE, + &chip11->n1_chiplet.nest_pervasive.xscom_ctrl_regs_mr); + + pnv_xscom_add_subregion(chip, PNV11_XSCOM_N1_PB_SCOM_EQ_BASE, + &chip11->n1_chiplet.xscom_pb_eq_mr); + + pnv_xscom_add_subregion(chip, PNV11_XSCOM_N1_PB_SCOM_ES_BASE, + &chip11->n1_chiplet.xscom_pb_es_mr); + + /* WIP: PHB added in future patch */ + + /* + * I2C + */ + for (i = 0; i < pcc->i2c_num_engines; i++) { + Object *obj = OBJECT(&chip11->i2c[i]); + + object_property_set_int(obj, "engine", i + 1, &error_fatal); + object_property_set_int(obj, "num-busses", + pcc->i2c_ports_per_engine[i], + &error_fatal); + object_property_set_link(obj, "chip", OBJECT(chip), &error_abort); + if (!qdev_realize(DEVICE(obj), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_I2CM_BASE + + (chip11->i2c[i].engine - 1) * + PNV11_XSCOM_I2CM_SIZE, + &chip11->i2c[i].xscom_regs); + qdev_connect_gpio_out(DEVICE(&chip11->i2c[i]), 0, + qdev_get_gpio_in(DEVICE(&chip11->psi), + PSIHB9_IRQ_SBE_I2C)); + } + /* PIB SPI Controller */ + for (i = 0; i < PNV10_CHIP_MAX_PIB_SPIC; i++) { + object_property_set_int(OBJECT(&chip11->pib_spic[i]), "spic_num", + i, &error_fatal); + /* pib_spic[2] connected to 25csm04 which implements 1 byte transfer */ + object_property_set_int(OBJECT(&chip11->pib_spic[i]), "transfer_len", + (i == 2) ? 1 : 4, &error_fatal); + object_property_set_int(OBJECT(&chip11->pib_spic[i]), "chip-id", + chip->chip_id, &error_fatal); + if (!sysbus_realize(SYS_BUS_DEVICE(OBJECT + (&chip11->pib_spic[i])), errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_PIB_SPIC_BASE + + i * PNV11_XSCOM_PIB_SPIC_SIZE, + &chip11->pib_spic[i].xscom_spic_regs); + } +} + static void pnv_rainier_i2c_init(PnvMachineState *pnv) { int i; @@ -2415,6 +2694,34 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, const void *data) &k->parent_realize); } +static uint32_t pnv_chip_power11_xscom_pcba(PnvChip *chip, uint64_t addr) +{ + addr &= (PNV11_XSCOM_SIZE - 1); + return addr >> 3; +} + +static void pnv_chip_power11_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvChipClass *k = PNV_CHIP_CLASS(klass); + static const int i2c_ports_per_engine[PNV10_CHIP_MAX_I2C] = {14, 14, 2, 16}; + + k->chip_cfam_id = 0x220da04980000000ull; /* P11 DD2.0 (with NX) */ + k->cores_mask = POWER11_CORE_MASK; + k->get_pir_tir = pnv_get_pir_tir_p10; + k->isa_create = pnv_chip_power11_isa_create; + k->dt_populate = pnv_chip_power11_dt_populate; + k->pic_print_info = pnv_chip_power11_pic_print_info; + k->xscom_core_base = pnv_chip_power11_xscom_core_base; + k->xscom_pcba = pnv_chip_power11_xscom_pcba; + dc->desc = "PowerNV Chip Power11"; + k->i2c_num_engines = PNV10_CHIP_MAX_I2C; + k->i2c_ports_per_engine = i2c_ports_per_engine; + + device_class_set_parent_realize(dc, pnv_chip_power11_realize, + &k->parent_realize); +} + static void pnv_chip_core_sanitize(PnvMachineState *pnv, PnvChip *chip, Error **errp) { @@ -3033,6 +3340,13 @@ static void pnv_machine_class_init(ObjectClass *oc, const void *data) .parent = TYPE_PNV10_CHIP, \ } +#define DEFINE_PNV11_CHIP_TYPE(type, class_initfn) \ + { \ + .name = type, \ + .class_init = class_initfn, \ + .parent = TYPE_PNV11_CHIP, \ + } + static const TypeInfo types[] = { { .name = MACHINE_TYPE_NAME("powernv10-rainier"), @@ -3088,6 +3402,17 @@ static const TypeInfo types[] = { .abstract = true, }, + /* + * P11 chip and variants + */ + { + .name = TYPE_PNV11_CHIP, + .parent = TYPE_PNV_CHIP, + .instance_init = pnv_chip_power11_instance_init, + .instance_size = sizeof(Pnv11Chip), + }, + DEFINE_PNV11_CHIP_TYPE(TYPE_PNV_CHIP_POWER11, pnv_chip_power11_class_init), + /* * P10 chip and variants */ diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 08c20224b97d5..fb2dfc7ba2124 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -473,6 +473,11 @@ static void pnv_core_power10_class_init(ObjectClass *oc, const void *data) pcc->xscom_size = PNV10_XSCOM_EC_SIZE; } +static void pnv_core_power11_class_init(ObjectClass *oc, const void *data) +{ + pnv_core_power10_class_init(oc, data); +} + static void pnv_core_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -504,6 +509,7 @@ static const TypeInfo pnv_core_infos[] = { DEFINE_PNV_CORE_TYPE(power8, "power8nvl_v1.0"), DEFINE_PNV_CORE_TYPE(power9, "power9_v2.2"), DEFINE_PNV_CORE_TYPE(power10, "power10_v2.0"), + DEFINE_PNV_CORE_TYPE(power11, "power11_v2.0"), }; DEFINE_TYPES(pnv_core_infos) @@ -725,6 +731,12 @@ static void pnv_quad_power10_class_init(ObjectClass *oc, const void *data) pqc->xscom_qme_size = PNV10_XSCOM_QME_SIZE; } +static void pnv_quad_power11_class_init(ObjectClass *oc, const void *data) +{ + /* Power11 quad is similar to Power10 quad */ + pnv_quad_power10_class_init(oc, data); +} + static void pnv_quad_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -752,6 +764,11 @@ static const TypeInfo pnv_quad_infos[] = { .name = PNV_QUAD_TYPE_NAME("power10"), .class_init = pnv_quad_power10_class_init, }, + { + .parent = TYPE_PNV_QUAD, + .name = PNV_QUAD_TYPE_NAME("power11"), + .class_init = pnv_quad_power11_class_init, + }, }; DEFINE_TYPES(pnv_quad_infos); diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index d8fca079f2feb..f0002627bcab0 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -33,6 +33,7 @@ typedef struct PnvChip PnvChip; typedef struct Pnv8Chip Pnv8Chip; typedef struct Pnv9Chip Pnv9Chip; typedef struct Pnv10Chip Pnv10Chip; +typedef struct Pnv10Chip Pnv11Chip; #define PNV_CHIP_TYPE_SUFFIX "-" TYPE_PNV_CHIP #define PNV_CHIP_TYPE_NAME(cpu_model) cpu_model PNV_CHIP_TYPE_SUFFIX @@ -57,6 +58,10 @@ DECLARE_INSTANCE_CHECKER(PnvChip, PNV_CHIP_POWER9, DECLARE_INSTANCE_CHECKER(PnvChip, PNV_CHIP_POWER10, TYPE_PNV_CHIP_POWER10) +#define TYPE_PNV_CHIP_POWER11 PNV_CHIP_TYPE_NAME("power11_v2.0") +DECLARE_INSTANCE_CHECKER(PnvChip, PNV_CHIP_POWER11, + TYPE_PNV_CHIP_POWER11) + PnvCore *pnv_chip_find_core(PnvChip *chip, uint32_t core_id); PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir); @@ -252,4 +257,19 @@ void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor); #define PNV10_HOMER_BASE(chip) \ (0x300ffd800000ll + ((uint64_t)(chip)->chip_id) * PNV_HOMER_SIZE) +/* Power11 */ +#define PNV11_XSCOM_SIZE PNV10_XSCOM_SIZE +#define PNV11_XSCOM_BASE(chip) PNV10_XSCOM_BASE(chip) + +#define PNV11_LPCM_SIZE PNV10_LPCM_SIZE +#define PNV11_LPCM_BASE(chip) PNV10_LPCM_BASE(chip) + +#define PNV11_PSIHB_ESB_SIZE PNV10_PSIHB_ESB_SIZE +#define PNV11_PSIHB_ESB_BASE(chip) PNV10_PSIHB_ESB_BASE(chip) + +#define PNV11_PSIHB_SIZE PNV10_PSIHB_SIZE +#define PNV11_PSIHB_BASE(chip) PNV10_PSIHB_BASE(chip) + +#define PNV11_OCC_SENSOR_BASE(chip) PNV10_OCC_SENSOR_BASE(chip) + #endif /* PPC_PNV_H */ diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h index 24ce37a9c8e47..6bd930f8b439c 100644 --- a/include/hw/ppc/pnv_chip.h +++ b/include/hw/ppc/pnv_chip.h @@ -141,6 +141,13 @@ struct Pnv10Chip { #define PNV10_PIR2CHIP(pir) (((pir) >> 8) & 0x7f) #define PNV10_PIR2THREAD(pir) (((pir) & 0x7f)) +#define TYPE_PNV11_CHIP "pnv11-chip" +DECLARE_INSTANCE_CHECKER(Pnv11Chip, PNV11_CHIP, + TYPE_PNV11_CHIP) + +/* Power11 core is same as Power10 */ +typedef struct Pnv10Chip Pnv11Chip; + struct PnvChipClass { /*< private >*/ SysBusDeviceClass parent_class; diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index b14549db7033a..610b075a27c3f 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -207,6 +207,55 @@ struct PnvXScomInterfaceClass { #define PNV10_XSCOM_PIB_SPIC_BASE 0xc0000 #define PNV10_XSCOM_PIB_SPIC_SIZE 0x20 +/* + * Power11 core is same as Power10 + */ +#define PNV11_XSCOM_EC_BASE(core) PNV10_XSCOM_EC_BASE(core) + +#define PNV11_XSCOM_ADU_BASE PNV10_XSCOM_ADU_BASE +#define PNV11_XSCOM_ADU_SIZE PNV10_XSCOM_ADU_SIZE + +#define PNV11_XSCOM_QME_BASE(core) PNV10_XSCOM_QME_BASE(core) + +#define PNV11_XSCOM_EQ_BASE(core) PNV10_XSCOM_EQ_BASE(core) + +#define PNV11_XSCOM_PSIHB_BASE PNV10_XSCOM_PSIHB_BASE +#define PNV11_XSCOM_PSIHB_SIZE PNV10_XSCOM_PSIHB_SIZE + +#define PNV11_XSCOM_I2CM_BASE PNV10_XSCOM_I2CM_BASE +#define PNV11_XSCOM_I2CM_SIZE PNV10_XSCOM_I2CM_SIZE + +#define PNV11_XSCOM_CHIPTOD_BASE PNV10_XSCOM_CHIPTOD_BASE +#define PNV11_XSCOM_CHIPTOD_SIZE PNV10_XSCOM_CHIPTOD_SIZE + +#define PNV11_XSCOM_OCC_BASE PNV10_XSCOM_OCC_BASE +#define PNV11_XSCOM_OCC_SIZE PNV10_XSCOM_OCC_SIZE + +#define PNV11_XSCOM_SBE_CTRL_BASE PNV10_XSCOM_SBE_CTRL_BASE +#define PNV11_XSCOM_SBE_CTRL_SIZE PNV10_XSCOM_SBE_CTRL_SIZE + +#define PNV11_XSCOM_SBE_MBOX_BASE PNV10_XSCOM_SBE_MBOX_BASE +#define PNV11_XSCOM_SBE_MBOX_SIZE PNV10_XSCOM_SBE_MBOX_SIZE + +#define PNV11_XSCOM_PBA_BASE PNV10_XSCOM_PBA_BASE +#define PNV11_XSCOM_PBA_SIZE PNV10_XSCOM_PBA_SIZE + +#define PNV11_XSCOM_XIVE2_BASE PNV10_XSCOM_XIVE2_BASE +#define PNV11_XSCOM_XIVE2_SIZE PNV10_XSCOM_XIVE2_SIZE + +#define PNV11_XSCOM_N1_CHIPLET_CTRL_REGS_BASE \ + PNV10_XSCOM_N1_CHIPLET_CTRL_REGS_BASE +#define PNV11_XSCOM_CHIPLET_CTRL_REGS_SIZE PNV10_XSCOM_CHIPLET_CTRL_REGS_SIZE + +#define PNV11_XSCOM_N1_PB_SCOM_EQ_BASE PNV10_XSCOM_N1_PB_SCOM_EQ_BASE +#define PNV11_XSCOM_N1_PB_SCOM_EQ_SIZE PNV10_XSCOM_N1_PB_SCOM_EQ_SIZE + +#define PNV11_XSCOM_N1_PB_SCOM_ES_BASE PNV10_XSCOM_N1_PB_SCOM_ES_BASE +#define PNV11_XSCOM_N1_PB_SCOM_ES_SIZE PNV10_XSCOM_N1_PB_SCOM_ES_SIZE + +#define PNV11_XSCOM_PIB_SPIC_BASE PNV10_XSCOM_PIB_SPIC_BASE +#define PNV11_XSCOM_PIB_SPIC_SIZE PNV10_XSCOM_PIB_SPIC_SIZE + void pnv_xscom_init(PnvChip *chip, uint64_t size, hwaddr addr); int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset, uint64_t xscom_base, uint64_t xscom_size, From 73a911e966acd8314e39a4ce4bc58e2254e68324 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 25 Sep 2025 23:00:43 +0530 Subject: [PATCH 0982/1794] ppc/pnv: Introduce Power11 PowerNV machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Powernv11 machine doesn't have XIVE & PHBs as of now XIVE2 interface and PHB5 added in later patches to Powernv11 machine Also add mention of Power11 to powernv documentation Note: A difference from P10's and P11's machine_class_init is, in P11 different number of PHBs cannot be used on the command line, ie. the following line does NOT exist in pnv_machine_power11_class_init, which existed in case of Power10: machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); Reviewed-by: Cédric Le Goater Signed-off-by: Aditya Gupta Tested-by: Amit Machhiwal Tested-by: Cédric Le Goater Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925173049.891406-3-adityag@linux.ibm.com Message-ID: <20250925173049.891406-3-adityag@linux.ibm.com> --- docs/system/ppc/powernv.rst | 9 +++++---- hw/ppc/pnv.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/docs/system/ppc/powernv.rst b/docs/system/ppc/powernv.rst index f3ec2cc69c0da..5154794cc8cd4 100644 --- a/docs/system/ppc/powernv.rst +++ b/docs/system/ppc/powernv.rst @@ -1,5 +1,5 @@ -PowerNV family boards (``powernv8``, ``powernv9``, ``powernv10``) -================================================================== +PowerNV family boards (``powernv8``, ``powernv9``, ``powernv10``, ``powernv11``) +================================================================================ PowerNV (as Non-Virtualized) is the "bare metal" platform using the OPAL firmware. It runs Linux on IBM and OpenPOWER systems and it can @@ -15,11 +15,12 @@ beyond the scope of what QEMU addresses today. Supported devices ----------------- - * Multi processor support for POWER8, POWER8NVL and POWER9. + * Multi processor support for POWER8, POWER8NVL, POWER9, Power10 and Power11. * XSCOM, serial communication sideband bus to configure chiplets. * Simple LPC Controller. * Processor Service Interface (PSI) Controller. - * Interrupt Controller, XICS (POWER8) and XIVE (POWER9) and XIVE2 (Power10). + * Interrupt Controller, XICS (POWER8) and XIVE (POWER9) and XIVE2 (Power10 & + Power11). * POWER8 PHB3 PCIe Host bridge and POWER9 PHB4 PCIe Host bridge. * Simple OCC is an on-chip micro-controller used for power management tasks. * iBT device to handle BMC communication, with the internal BMC simulator diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 77136091bbcad..423954ba7e0cd 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -3235,6 +3235,35 @@ static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, pmc->i2c_init = pnv_rainier_i2c_init; } +static void pnv_machine_power11_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); + static const char compat[] = "qemu,powernv11\0ibm,powernv"; + + pmc->compat = compat; + pmc->compat_size = sizeof(compat); + pmc->max_smt_threads = 4; + pmc->has_lpar_per_thread = true; + pmc->quirk_tb_big_core = true; + pmc->dt_power_mgt = pnv_dt_power_mgt; + + mc->desc = "IBM PowerNV (Non-Virtualized) Power11"; + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power11_v2.0"); + + object_class_property_add_bool(oc, "big-core", + pnv_machine_get_big_core, + pnv_machine_set_big_core); + object_class_property_set_description(oc, "big-core", + "Use big-core (aka fused-core) mode"); + + object_class_property_add_bool(oc, "lpar-per-core", + pnv_machine_get_lpar_per_core, + pnv_machine_set_lpar_per_core); + object_class_property_set_description(oc, "lpar-per-core", + "Use 1 LPAR per core mode"); +} + static void pnv_cpu_do_nmi_on_cpu(CPUState *cs, run_on_cpu_data arg) { CPUPPCState *env = cpu_env(cs); @@ -3348,6 +3377,11 @@ static void pnv_machine_class_init(ObjectClass *oc, const void *data) } static const TypeInfo types[] = { + { + .name = MACHINE_TYPE_NAME("powernv11"), + .parent = TYPE_PNV_MACHINE, + .class_init = pnv_machine_power11_class_init, + }, { .name = MACHINE_TYPE_NAME("powernv10-rainier"), .parent = MACHINE_TYPE_NAME("powernv10"), From 849a6bb854b48d757fab5e408f6a17dff568ec13 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 25 Sep 2025 23:00:44 +0530 Subject: [PATCH 0983/1794] ppc/pnv: Add PnvChipClass handler to get reference to interrupt controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Existing code in XIVE2 assumes the chip to be a Power10 Chip. Instead add a handler to get reference to the interrupt controller (XIVE) for a given Power Chip. Signed-off-by: Aditya Gupta Reviewed-by: Cédric Le Goater Tested-by: Amit Machhiwal Tested-by: Cédric Le Goater Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925173049.891406-4-adityag@linux.ibm.com Message-ID: <20250925173049.891406-4-adityag@linux.ibm.com> --- hw/intc/pnv_xive2.c | 4 ++-- hw/ppc/pnv.c | 12 ++++++++++++ include/hw/ppc/pnv_chip.h | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index e019cad5c14c1..0663baab544ca 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -110,8 +110,8 @@ static PnvXive2 *pnv_xive2_get_remote(uint32_t vsd_type, hwaddr fwd_addr) int i; for (i = 0; i < pnv->num_chips; i++) { - Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]); - PnvXive2 *xive = &chip10->xive; + PnvChipClass *k = PNV_CHIP_GET_CLASS(pnv->chips[i]); + PnvXive2 *xive = PNV_XIVE2(k->intc_get(pnv->chips[i])); /* * Is this the XIVE matching the forwarded VSD address is for this diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 423954ba7e0cd..a4fdf59207fae 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1486,6 +1486,16 @@ static void pnv_chip_power10_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), buf); } +static void *pnv_chip_power10_intc_get(PnvChip *chip) +{ + return &PNV10_CHIP(chip)->xive; +} + +static void *pnv_chip_power11_intc_get(PnvChip *chip) +{ + return &PNV11_CHIP(chip)->xive; +} + /* * Allowed core identifiers on a POWER8 Processor Chip : * @@ -2680,6 +2690,7 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, const void *data) k->intc_reset = pnv_chip_power10_intc_reset; k->intc_destroy = pnv_chip_power10_intc_destroy; k->intc_print_info = pnv_chip_power10_intc_print_info; + k->intc_get = pnv_chip_power10_intc_get; k->isa_create = pnv_chip_power10_isa_create; k->dt_populate = pnv_chip_power10_dt_populate; k->pic_print_info = pnv_chip_power10_pic_print_info; @@ -2709,6 +2720,7 @@ static void pnv_chip_power11_class_init(ObjectClass *klass, const void *data) k->chip_cfam_id = 0x220da04980000000ull; /* P11 DD2.0 (with NX) */ k->cores_mask = POWER11_CORE_MASK; k->get_pir_tir = pnv_get_pir_tir_p10; + k->intc_get = pnv_chip_power11_intc_get; k->isa_create = pnv_chip_power11_isa_create; k->dt_populate = pnv_chip_power11_dt_populate; k->pic_print_info = pnv_chip_power11_pic_print_info; diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h index 6bd930f8b439c..a5b8c49680d37 100644 --- a/include/hw/ppc/pnv_chip.h +++ b/include/hw/ppc/pnv_chip.h @@ -170,6 +170,7 @@ struct PnvChipClass { void (*intc_reset)(PnvChip *chip, PowerPCCPU *cpu); void (*intc_destroy)(PnvChip *chip, PowerPCCPU *cpu); void (*intc_print_info)(PnvChip *chip, PowerPCCPU *cpu, GString *buf); + void* (*intc_get)(PnvChip *chip); ISABus *(*isa_create)(PnvChip *chip, Error **errp); void (*dt_populate)(PnvChip *chip, void *fdt); void (*pic_print_info)(PnvChip *chip, GString *buf); From 0dcbc88865432504dc452d9ded8218dff3bdf0d2 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 25 Sep 2025 23:00:45 +0530 Subject: [PATCH 0984/1794] ppc/pnv: Add XIVE2 controller to Power11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a XIVE2 controller to Power11 chip and machine. The controller has the same logic as Power10. Reviewed-by: Cédric Le Goater Signed-off-by: Aditya Gupta Tested-by: Amit Machhiwal Tested-by: Cédric Le Goater Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925173049.891406-5-adityag@linux.ibm.com Message-ID: <20250925173049.891406-5-adityag@linux.ibm.com> --- hw/ppc/pnv.c | 121 ++++++++++++++++++++++++++++++++++++++++++- include/hw/ppc/pnv.h | 18 +++++++ 2 files changed, 138 insertions(+), 1 deletion(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index a4fdf59207fae..8097d3c09a2fc 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -976,6 +976,7 @@ static void pnv_chip_power11_pic_print_info(PnvChip *chip, GString *buf) { Pnv11Chip *chip11 = PNV11_CHIP(chip); + pnv_xive2_pic_print_info(&chip11->xive, buf); pnv_psi_pic_print_info(&chip11->psi, buf); } @@ -1491,6 +1492,50 @@ static void *pnv_chip_power10_intc_get(PnvChip *chip) return &PNV10_CHIP(chip)->xive; } +static void pnv_chip_power11_intc_create(PnvChip *chip, PowerPCCPU *cpu, + Error **errp) +{ + Pnv11Chip *chip11 = PNV11_CHIP(chip); + Error *local_err = NULL; + Object *obj; + PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); + + /* + * The core creates its interrupt presenter but the XIVE2 interrupt + * controller object is initialized afterwards. Hopefully, it's + * only used at runtime. + */ + obj = xive_tctx_create(OBJECT(cpu), XIVE_PRESENTER(&chip11->xive), + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + pnv_cpu->intc = obj; +} + +static void pnv_chip_power11_intc_reset(PnvChip *chip, PowerPCCPU *cpu) +{ + PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); + + xive_tctx_reset(XIVE_TCTX(pnv_cpu->intc)); +} + +static void pnv_chip_power11_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) +{ + PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); + + xive_tctx_destroy(XIVE_TCTX(pnv_cpu->intc)); + pnv_cpu->intc = NULL; +} + +static void pnv_chip_power11_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, + GString *buf) +{ + xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), buf); +} + static void *pnv_chip_power11_intc_get(PnvChip *chip) { return &PNV11_CHIP(chip)->xive; @@ -2443,6 +2488,10 @@ static void pnv_chip_power11_instance_init(Object *obj) object_initialize_child(obj, "occ", &chip11->occ, TYPE_PNV10_OCC); object_initialize_child(obj, "sbe", &chip11->sbe, TYPE_PNV10_SBE); object_initialize_child(obj, "homer", &chip11->homer, TYPE_PNV10_HOMER); + + object_initialize_child(obj, "xive", &chip11->xive, TYPE_PNV_XIVE2); + object_property_add_alias(obj, "xive-fabric", OBJECT(&chip11->xive), + "xive-fabric"); object_initialize_child(obj, "n1-chiplet", &chip11->n1_chiplet, TYPE_PNV_N1_CHIPLET); @@ -2518,7 +2567,26 @@ static void pnv_chip_power11_realize(DeviceState *dev, Error **errp) return; } - /* WIP: XIVE added in future patch */ + /* XIVE2 interrupt controller */ + object_property_set_int(OBJECT(&chip11->xive), "ic-bar", + PNV11_XIVE2_IC_BASE(chip), &error_fatal); + object_property_set_int(OBJECT(&chip11->xive), "esb-bar", + PNV11_XIVE2_ESB_BASE(chip), &error_fatal); + object_property_set_int(OBJECT(&chip11->xive), "end-bar", + PNV11_XIVE2_END_BASE(chip), &error_fatal); + object_property_set_int(OBJECT(&chip11->xive), "nvpg-bar", + PNV11_XIVE2_NVPG_BASE(chip), &error_fatal); + object_property_set_int(OBJECT(&chip11->xive), "nvc-bar", + PNV11_XIVE2_NVC_BASE(chip), &error_fatal); + object_property_set_int(OBJECT(&chip11->xive), "tm-bar", + PNV11_XIVE2_TM_BASE(chip), &error_fatal); + object_property_set_link(OBJECT(&chip11->xive), "chip", OBJECT(chip), + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&chip11->xive), errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_XIVE2_BASE, + &chip11->xive.xscom_regs); /* Processor Service Interface (PSI) Host Bridge */ object_property_set_int(OBJECT(&chip11->psi), "bar", @@ -2720,6 +2788,10 @@ static void pnv_chip_power11_class_init(ObjectClass *klass, const void *data) k->chip_cfam_id = 0x220da04980000000ull; /* P11 DD2.0 (with NX) */ k->cores_mask = POWER11_CORE_MASK; k->get_pir_tir = pnv_get_pir_tir_p10; + k->intc_create = pnv_chip_power11_intc_create; + k->intc_reset = pnv_chip_power11_intc_reset; + k->intc_destroy = pnv_chip_power11_intc_destroy; + k->intc_print_info = pnv_chip_power11_intc_print_info; k->intc_get = pnv_chip_power11_intc_get; k->isa_create = pnv_chip_power11_isa_create; k->dt_populate = pnv_chip_power11_dt_populate; @@ -3073,6 +3145,45 @@ static int pnv10_xive_broadcast(XiveFabric *xfb, return 0; } +static bool pnv11_xive_match_nvt(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, + XiveTCTXMatch *match) +{ + PnvMachineState *pnv = PNV_MACHINE(xfb); + int i; + + for (i = 0; i < pnv->num_chips; i++) { + Pnv11Chip *chip11 = PNV11_CHIP(pnv->chips[i]); + XivePresenter *xptr = XIVE_PRESENTER(&chip11->xive); + XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); + + xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, + cam_ignore, priority, logic_serv, match); + } + + return !!match->count; +} + +static int pnv11_xive_broadcast(XiveFabric *xfb, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, + uint8_t priority) +{ + PnvMachineState *pnv = PNV_MACHINE(xfb); + int i; + + for (i = 0; i < pnv->num_chips; i++) { + Pnv11Chip *chip11 = PNV11_CHIP(pnv->chips[i]); + XivePresenter *xptr = XIVE_PRESENTER(&chip11->xive); + XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); + + xpc->broadcast(xptr, nvt_blk, nvt_idx, crowd, cam_ignore, priority); + } + return 0; +} + static bool pnv_machine_get_big_core(Object *obj, Error **errp) { PnvMachineState *pnv = PNV_MACHINE(obj); @@ -3251,6 +3362,7 @@ static void pnv_machine_power11_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); + XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc); static const char compat[] = "qemu,powernv11\0ibm,powernv"; pmc->compat = compat; @@ -3260,6 +3372,9 @@ static void pnv_machine_power11_class_init(ObjectClass *oc, const void *data) pmc->quirk_tb_big_core = true; pmc->dt_power_mgt = pnv_dt_power_mgt; + xfc->match_nvt = pnv11_xive_match_nvt; + xfc->broadcast = pnv11_xive_broadcast; + mc->desc = "IBM PowerNV (Non-Virtualized) Power11"; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power11_v2.0"); @@ -3393,6 +3508,10 @@ static const TypeInfo types[] = { .name = MACHINE_TYPE_NAME("powernv11"), .parent = TYPE_PNV_MACHINE, .class_init = pnv_machine_power11_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_XIVE_FABRIC }, + { }, + }, }, { .name = MACHINE_TYPE_NAME("powernv10-rainier"), diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index f0002627bcab0..cbdddfc73cd42 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -270,6 +270,24 @@ void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor); #define PNV11_PSIHB_SIZE PNV10_PSIHB_SIZE #define PNV11_PSIHB_BASE(chip) PNV10_PSIHB_BASE(chip) +#define PNV11_XIVE2_IC_SIZE PNV10_XIVE2_IC_SIZE +#define PNV11_XIVE2_IC_BASE(chip) PNV10_XIVE2_IC_BASE(chip) + +#define PNV11_XIVE2_TM_SIZE PNV10_XIVE2_TM_SIZE +#define PNV11_XIVE2_TM_BASE(chip) PNV10_XIVE2_TM_BASE(chip) + +#define PNV11_XIVE2_NVC_SIZE PNV10_XIVE2_NVC_SIZE +#define PNV11_XIVE2_NVC_BASE(chip) PNV10_XIVE2_NVC_BASE(chip) + +#define PNV11_XIVE2_NVPG_SIZE PNV10_XIVE2_NVPG_SIZE +#define PNV11_XIVE2_NVPG_BASE(chip) PNV10_XIVE2_NVPG_BASE(chip) + +#define PNV11_XIVE2_ESB_SIZE PNV10_XIVE2_ESB_SIZE +#define PNV11_XIVE2_ESB_BASE(chip) PNV10_XIVE2_ESB_BASE(chip) + +#define PNV11_XIVE2_END_SIZE PNV10_XIVE2_END_SIZE +#define PNV11_XIVE2_END_BASE(chip) PNV10_XIVE2_END_BASE(chip) + #define PNV11_OCC_SENSOR_BASE(chip) PNV10_OCC_SENSOR_BASE(chip) #endif /* PPC_PNV_H */ From 91a1cc6a2ce72aefbc9084ce245b562c1d21e082 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 25 Sep 2025 23:00:46 +0530 Subject: [PATCH 0985/1794] ppc/pnv: Add PHB5 PCIe Host bridge to Power11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Power11 also uses PHB5, same as Power10. Add Power11 PHBs with similar code as the corresponding Power10 implementation. Reviewed-by: Cédric Le Goater Signed-off-by: Aditya Gupta Tested-by: Amit Machhiwal Tested-by: Cédric Le Goater Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925173049.891406-6-adityag@linux.ibm.com Message-ID: <20250925173049.891406-6-adityag@linux.ibm.com> --- hw/ppc/pnv.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 8097d3c09a2fc..2b4df6076c4c5 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -978,6 +978,8 @@ static void pnv_chip_power11_pic_print_info(PnvChip *chip, GString *buf) pnv_xive2_pic_print_info(&chip11->xive, buf); pnv_psi_pic_print_info(&chip11->psi, buf); + object_child_foreach_recursive(OBJECT(chip), + pnv_chip_power9_pic_print_info_child, buf); } /* Always give the first 1GB to chip 0 else we won't boot */ @@ -2473,6 +2475,7 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) static void pnv_chip_power11_instance_init(Object *obj) { + PnvChip *chip = PNV_CHIP(obj); Pnv11Chip *chip11 = PNV11_CHIP(obj); PnvChipClass *pcc = PNV_CHIP_GET_CLASS(obj); int i; @@ -2495,6 +2498,13 @@ static void pnv_chip_power11_instance_init(Object *obj) object_initialize_child(obj, "n1-chiplet", &chip11->n1_chiplet, TYPE_PNV_N1_CHIPLET); + chip->num_pecs = pcc->num_pecs; + + for (i = 0; i < chip->num_pecs; i++) { + object_initialize_child(obj, "pec[*]", &chip11->pecs[i], + TYPE_PNV_PHB5_PEC); + } + for (i = 0; i < pcc->i2c_num_engines; i++) { object_initialize_child(obj, "i2c[*]", &chip11->i2c[i], TYPE_PNV_I2C); } @@ -2527,6 +2537,38 @@ static void pnv_chip_power11_quad_realize(Pnv11Chip *chip11, Error **errp) } } +static void pnv_chip_power11_phb_realize(PnvChip *chip, Error **errp) +{ + Pnv11Chip *chip11 = PNV11_CHIP(chip); + int i; + + for (i = 0; i < chip->num_pecs; i++) { + PnvPhb4PecState *pec = &chip11->pecs[i]; + PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec); + uint32_t pec_cplt_base; + uint32_t pec_nest_base; + uint32_t pec_pci_base; + + object_property_set_int(OBJECT(pec), "index", i, &error_fatal); + object_property_set_int(OBJECT(pec), "chip-id", chip->chip_id, + &error_fatal); + object_property_set_link(OBJECT(pec), "chip", OBJECT(chip), + &error_fatal); + if (!qdev_realize(DEVICE(pec), NULL, errp)) { + return; + } + + pec_cplt_base = pecc->xscom_cplt_base(pec); + pec_nest_base = pecc->xscom_nest_base(pec); + pec_pci_base = pecc->xscom_pci_base(pec); + + pnv_xscom_add_subregion(chip, pec_cplt_base, + &pec->nest_pervasive.xscom_ctrl_regs_mr); + pnv_xscom_add_subregion(chip, pec_nest_base, &pec->nest_regs_mr); + pnv_xscom_add_subregion(chip, pec_pci_base, &pec->pci_regs_mr); + } +} + static void pnv_chip_power11_realize(DeviceState *dev, Error **errp) { PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); @@ -2664,7 +2706,12 @@ static void pnv_chip_power11_realize(DeviceState *dev, Error **errp) pnv_xscom_add_subregion(chip, PNV11_XSCOM_N1_PB_SCOM_ES_BASE, &chip11->n1_chiplet.xscom_pb_es_mr); - /* WIP: PHB added in future patch */ + /* PHBs */ + pnv_chip_power11_phb_realize(chip, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } /* * I2C @@ -2799,6 +2846,7 @@ static void pnv_chip_power11_class_init(ObjectClass *klass, const void *data) k->xscom_core_base = pnv_chip_power11_xscom_core_base; k->xscom_pcba = pnv_chip_power11_xscom_pcba; dc->desc = "PowerNV Chip Power11"; + k->num_pecs = PNV10_CHIP_MAX_PEC; k->i2c_num_engines = PNV10_CHIP_MAX_I2C; k->i2c_ports_per_engine = i2c_ports_per_engine; @@ -3365,6 +3413,13 @@ static void pnv_machine_power11_class_init(ObjectClass *oc, const void *data) XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc); static const char compat[] = "qemu,powernv11\0ibm,powernv"; + static GlobalProperty phb_compat[] = { + { TYPE_PNV_PHB, "version", "5" }, + { TYPE_PNV_PHB_ROOT_PORT, "version", "5" }, + }; + + compat_props_add(mc->compat_props, phb_compat, G_N_ELEMENTS(phb_compat)); + pmc->compat = compat; pmc->compat_size = sizeof(compat); pmc->max_smt_threads = 4; From 46835d806aaacc6c89d7eaf961e21e550d0222f5 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 25 Sep 2025 23:00:47 +0530 Subject: [PATCH 0986/1794] ppc/pnv: Add ChipTOD model for Power11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce Power11 ChipTod. The code has been copied from Power10 ChipTod code as the Power11 core is same as Power10 core. Reviewed-by: Cédric Le Goater Signed-off-by: Aditya Gupta Tested-by: Amit Machhiwal Tested-by: Cédric Le Goater Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925173049.891406-7-adityag@linux.ibm.com Message-ID: <20250925173049.891406-7-adityag@linux.ibm.com> --- hw/ppc/pnv.c | 15 +++++++++ hw/ppc/pnv_chiptod.c | 59 ++++++++++++++++++++++++++++++++++++ include/hw/ppc/pnv_chiptod.h | 2 ++ 3 files changed, 76 insertions(+) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 2b4df6076c4c5..f0469cdb8b657 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -2495,6 +2495,8 @@ static void pnv_chip_power11_instance_init(Object *obj) object_initialize_child(obj, "xive", &chip11->xive, TYPE_PNV_XIVE2); object_property_add_alias(obj, "xive-fabric", OBJECT(&chip11->xive), "xive-fabric"); + object_initialize_child(obj, "chiptod", &chip11->chiptod, + TYPE_PNV11_CHIPTOD); object_initialize_child(obj, "n1-chiplet", &chip11->n1_chiplet, TYPE_PNV_N1_CHIPLET); @@ -2653,6 +2655,19 @@ static void pnv_chip_power11_realize(DeviceState *dev, Error **errp) chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0", (uint64_t) PNV11_LPCM_BASE(chip)); + /* ChipTOD */ + object_property_set_bool(OBJECT(&chip11->chiptod), "primary", + chip->chip_id == 0, &error_abort); + object_property_set_bool(OBJECT(&chip11->chiptod), "secondary", + chip->chip_id == 1, &error_abort); + object_property_set_link(OBJECT(&chip11->chiptod), "chip", OBJECT(chip), + &error_abort); + if (!qdev_realize(DEVICE(&chip11->chiptod), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_CHIPTOD_BASE, + &chip11->chiptod.xscom_regs); + /* HOMER (must be created before OCC) */ object_property_set_link(OBJECT(&chip11->homer), "chip", OBJECT(chip), &error_abort); diff --git a/hw/ppc/pnv_chiptod.c b/hw/ppc/pnv_chiptod.c index b9e9c7ba3dbbe..f887a18cde8d1 100644 --- a/hw/ppc/pnv_chiptod.c +++ b/hw/ppc/pnv_chiptod.c @@ -210,6 +210,22 @@ static void chiptod_power10_broadcast_ttype(PnvChipTOD *sender, } } +static void chiptod_power11_broadcast_ttype(PnvChipTOD *sender, + uint32_t trigger) +{ + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + int i; + + for (i = 0; i < pnv->num_chips; i++) { + Pnv11Chip *chip11 = PNV11_CHIP(pnv->chips[i]); + PnvChipTOD *chiptod = &chip11->chiptod; + + if (chiptod != sender) { + chiptod_receive_ttype(chiptod, trigger); + } + } +} + static PnvCore *pnv_chip_get_core_by_xscom_base(PnvChip *chip, uint32_t xscom_base) { @@ -283,6 +299,12 @@ static PnvCore *chiptod_power10_tx_ttype_target(PnvChipTOD *chiptod, } } +static PnvCore *chiptod_power11_tx_ttype_target(PnvChipTOD *chiptod, + uint64_t val) +{ + return chiptod_power10_tx_ttype_target(chiptod, val); +} + static void pnv_chiptod_xscom_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -520,6 +542,42 @@ static const TypeInfo pnv_chiptod_power10_type_info = { } }; +static int pnv_chiptod_power11_dt_xscom(PnvXScomInterface *dev, void *fdt, + int xscom_offset) +{ + const char compat[] = "ibm,power-chiptod\0ibm,power11-chiptod"; + + return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat)); +} + +static void pnv_chiptod_power11_class_init(ObjectClass *klass, const void *data) +{ + PnvChipTODClass *pctc = PNV_CHIPTOD_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); + + dc->desc = "PowerNV ChipTOD Controller (Power11)"; + device_class_set_props(dc, pnv_chiptod_properties); + + xdc->dt_xscom = pnv_chiptod_power11_dt_xscom; + + pctc->broadcast_ttype = chiptod_power11_broadcast_ttype; + pctc->tx_ttype_target = chiptod_power11_tx_ttype_target; + + pctc->xscom_size = PNV_XSCOM_CHIPTOD_SIZE; +} + +static const TypeInfo pnv_chiptod_power11_type_info = { + .name = TYPE_PNV11_CHIPTOD, + .parent = TYPE_PNV_CHIPTOD, + .instance_size = sizeof(PnvChipTOD), + .class_init = pnv_chiptod_power11_class_init, + .interfaces = (const InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + static void pnv_chiptod_reset(void *dev) { PnvChipTOD *chiptod = PNV_CHIPTOD(dev); @@ -579,6 +637,7 @@ static void pnv_chiptod_register_types(void) type_register_static(&pnv_chiptod_type_info); type_register_static(&pnv_chiptod_power9_type_info); type_register_static(&pnv_chiptod_power10_type_info); + type_register_static(&pnv_chiptod_power11_type_info); } type_init(pnv_chiptod_register_types); diff --git a/include/hw/ppc/pnv_chiptod.h b/include/hw/ppc/pnv_chiptod.h index fde569bcbfa9c..466b06560a28f 100644 --- a/include/hw/ppc/pnv_chiptod.h +++ b/include/hw/ppc/pnv_chiptod.h @@ -17,6 +17,8 @@ OBJECT_DECLARE_TYPE(PnvChipTOD, PnvChipTODClass, PNV_CHIPTOD) DECLARE_INSTANCE_CHECKER(PnvChipTOD, PNV9_CHIPTOD, TYPE_PNV9_CHIPTOD) #define TYPE_PNV10_CHIPTOD TYPE_PNV_CHIPTOD "-POWER10" DECLARE_INSTANCE_CHECKER(PnvChipTOD, PNV10_CHIPTOD, TYPE_PNV10_CHIPTOD) +#define TYPE_PNV11_CHIPTOD TYPE_PNV_CHIPTOD "-POWER11" +DECLARE_INSTANCE_CHECKER(PnvChipTOD, PNV11_CHIPTOD, TYPE_PNV11_CHIPTOD) enum tod_state { tod_error = 0, From 968f1af16aa8da8927eb167e57b224dc58f67637 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 25 Sep 2025 23:00:48 +0530 Subject: [PATCH 0987/1794] tests/powernv: Switch to buildroot images instead of op-build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As op-build images haven't been updated from long time (and may not get updated in future), use buildroot images provided by cedric [1]. Use existing nvme device being used in the test to mount the initrd. Also replace the check for "zImage loaded message" to skiboot's message when it starts the kernel: "Starting kernel at", since we are no longer using zImage from op-build This is required for newer processor tests such as Power11, as the op-build kernel image is old and doesn't support Power11. Power11 test has been added in a later patch. [1]: https://github.com/legoater/qemu-ppc-boot/tree/main/buildroot/qemu_ppc64le_powernv8-2025.02 Suggested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Signed-off-by: Aditya Gupta Tested-by: Amit Machhiwal Tested-by: Cédric Le Goater Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925173049.891406-8-adityag@linux.ibm.com Message-ID: <20250925173049.891406-8-adityag@linux.ibm.com> --- tests/functional/ppc64/test_powernv.py | 30 ++++++++++++++------------ 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/tests/functional/ppc64/test_powernv.py b/tests/functional/ppc64/test_powernv.py index 685e2178ed78f..2b4db1cf99b4e 100755 --- a/tests/functional/ppc64/test_powernv.py +++ b/tests/functional/ppc64/test_powernv.py @@ -18,9 +18,14 @@ class powernvMachine(LinuxKernelTest): good_message = 'VFS: Cannot open root device' ASSET_KERNEL = Asset( - ('https://archives.fedoraproject.org/pub/archive/fedora-secondary/' - 'releases/29/Everything/ppc64le/os/ppc/ppc64/vmlinuz'), - '383c2f5c23bc0d9d32680c3924d3fd7ee25cc5ef97091ac1aa5e1d853422fc5f') + ('https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/' + 'buildroot/qemu_ppc64le_powernv8-2025.02/vmlinux'), + '6fd29aff9ad4362511ea5d0acbb510667c7031928e97d64ec15bbc5daf4b8151') + + ASSET_INITRD = Asset( + ('https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/' + 'buildroot/qemu_ppc64le_powernv8-2025.02/rootfs.ext2'), + 'aee2192b692077c4bde31cb56ce474424b358f17cec323d5c94af3970c9aada2') def do_test_linux_boot(self, command_line = KERNEL_COMMON_COMMAND_LINE): self.require_accelerator("tcg") @@ -78,27 +83,24 @@ def test_linux_big_boot(self): wait_for_console_pattern(self, console_pattern, self.panic_message) wait_for_console_pattern(self, self.good_message, self.panic_message) - - ASSET_EPAPR_KERNEL = Asset( - ('https://github.com/open-power/op-build/releases/download/v2.7/' - 'zImage.epapr'), - '0ab237df661727e5392cee97460e8674057a883c5f74381a128fa772588d45cd') - def do_test_ppc64_powernv(self, proc): self.require_accelerator("tcg") - kernel_path = self.ASSET_EPAPR_KERNEL.fetch() + kernel_path = self.ASSET_KERNEL.fetch() + initrd_path = self.ASSET_INITRD.fetch() self.vm.set_console() self.vm.add_args('-kernel', kernel_path, - '-append', 'console=tty0 console=hvc0', + '-drive', + f'file={initrd_path},format=raw,if=none,id=drive0,readonly=on', + '-append', 'root=/dev/nvme0n1 console=tty0 console=hvc0', '-device', 'pcie-pci-bridge,id=bridge1,bus=pcie.1,addr=0x0', - '-device', 'nvme,bus=pcie.2,addr=0x0,serial=1234', + '-device', 'nvme,drive=drive0,bus=pcie.2,addr=0x0,serial=1234', '-device', 'e1000e,bus=bridge1,addr=0x3', '-device', 'nec-usb-xhci,bus=bridge1,addr=0x2') self.vm.launch() self.wait_for_console_pattern("CPU: " + proc + " generation processor") - self.wait_for_console_pattern("zImage starting: loaded") - self.wait_for_console_pattern("Run /init as init process") + self.wait_for_console_pattern("INIT: Starting kernel at ") + self.wait_for_console_pattern("Run /sbin/init as init process") # Device detection output driven by udev probing is sometimes cut off # from console output, suspect S14silence-console init script. From 5f810b912eea7d91c55927a1bde94f61d7c9e8a0 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 25 Sep 2025 23:00:49 +0530 Subject: [PATCH 0988/1794] tests/powernv: Add PowerNV test for Power11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With all Power11 support in place, add Power11 PowerNV test. Reviewed-by: Cédric Le Goater Signed-off-by: Aditya Gupta Tested-by: Amit Machhiwal Tested-by: Cédric Le Goater Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925173049.891406-9-adityag@linux.ibm.com Message-ID: <20250925173049.891406-9-adityag@linux.ibm.com> --- tests/functional/ppc64/test_powernv.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/functional/ppc64/test_powernv.py b/tests/functional/ppc64/test_powernv.py index 2b4db1cf99b4e..9ada832b78161 100755 --- a/tests/functional/ppc64/test_powernv.py +++ b/tests/functional/ppc64/test_powernv.py @@ -116,5 +116,9 @@ def test_powernv10(self): self.set_machine('powernv10') self.do_test_ppc64_powernv('P10') + def test_powernv11(self): + self.set_machine('powernv11') + self.do_test_ppc64_powernv('Power11') + if __name__ == '__main__': LinuxKernelTest.main() From 556d19fdcb1a07afc05bde7b38cfd2a1f4a89552 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 25 Sep 2025 15:17:39 -0500 Subject: [PATCH 0989/1794] target/ppc: IBM PPE42 general regs and flags Introduces general IBM PPE42 processor register definitions and flags. Signed-off-by: Glenn Miles Reviewed-by: Chinmay Rath Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925201758.652077-2-milesg@linux.ibm.com Message-ID: <20250925201758.652077-2-milesg@linux.ibm.com> --- target/ppc/cpu-models.h | 4 ++++ target/ppc/cpu.h | 49 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/target/ppc/cpu-models.h b/target/ppc/cpu-models.h index 72ad31ba50d73..c6cd27f390e7b 100644 --- a/target/ppc/cpu-models.h +++ b/target/ppc/cpu-models.h @@ -69,6 +69,10 @@ enum { /* Xilinx cores */ CPU_POWERPC_X2VP4 = 0x20010820, CPU_POWERPC_X2VP20 = 0x20010860, + /* IBM PPE42 Family */ + CPU_POWERPC_PPE42 = 0x42000000, + CPU_POWERPC_PPE42X = 0x42100000, + CPU_POWERPC_PPE42XM = 0x42200000, /* PowerPC 440 family */ /* Generic PowerPC 440 */ #define CPU_POWERPC_440 CPU_POWERPC_440GXf diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 0e26e4343de71..8e13ce41a96c8 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -282,6 +282,8 @@ typedef enum powerpc_input_t { PPC_FLAGS_INPUT_POWER9, /* Freescale RCPU bus */ PPC_FLAGS_INPUT_RCPU, + /* PPE42 bus */ + PPC_FLAGS_INPUT_PPE42, } powerpc_input_t; #define PPC_INPUT(env) ((env)->bus_model) @@ -433,39 +435,64 @@ typedef enum { #define MSR_TM PPC_BIT_NR(31) /* Transactional Memory Available (Book3s) */ #define MSR_CM PPC_BIT_NR(32) /* Computation mode for BookE hflags */ #define MSR_ICM PPC_BIT_NR(33) /* Interrupt computation mode for BookE */ +#define MSR_SEM0 PPC_BIT_NR(33) /* SIB Error Mask Bit 0 (PPE42) */ +#define MSR_SEM1 PPC_BIT_NR(34) /* SIB Error Mask Bit 1 (PPE42) */ +#define MSR_SEM2 PPC_BIT_NR(35) /* SIB Error Mask Bit 2 (PPE42) */ #define MSR_GS PPC_BIT_NR(35) /* guest state for BookE */ +#define MSR_SEM3 PPC_BIT_NR(36) /* SIB Error Mask Bit 3 (PPE42) */ +#define MSR_SEM4 PPC_BIT_NR(37) /* SIB Error Mask Bit 4 (PPE42) */ #define MSR_UCLE PPC_BIT_NR(37) /* User-mode cache lock enable for BookE */ #define MSR_VR PPC_BIT_NR(38) /* altivec available x hflags */ #define MSR_SPE PPC_BIT_NR(38) /* SPE enable for BookE x hflags */ +#define MSR_SEM5 PPC_BIT_NR(38) /* SIB Error Mask Bit 5 (PPE42) */ +#define MSR_SEM6 PPC_BIT_NR(39) /* SIB Error Mask Bit 6 (PPE42) */ #define MSR_VSX PPC_BIT_NR(40) /* Vector Scalar Extension (>= 2.06)x hflags */ +#define MSR_IS0 PPC_BIT_NR(40) /* Instance Specific Bit 0 (PPE42) */ #define MSR_S PPC_BIT_NR(41) /* Secure state */ +#define MSR_SIBRC0 PPC_BIT_NR(41) /* Last SIB return code Bit 0 (PPE42) */ +#define MSR_SIBRC1 PPC_BIT_NR(42) /* Last SIB return code Bit 1 (PPE42) */ +#define MSR_SIBRC2 PPC_BIT_NR(43) /* Last SIB return code Bit 2 (PPE42) */ +#define MSR_LP PPC_BIT_NR(44) /* Low Priority (PPE42) */ #define MSR_KEY PPC_BIT_NR(44) /* key bit on 603e */ #define MSR_POW PPC_BIT_NR(45) /* Power management */ #define MSR_WE PPC_BIT_NR(45) /* Wait State Enable on 405 */ +#define MSR_IS1 PPC_BIT_NR(46) /* Instance Specific Bit 1 (PPE42) */ #define MSR_TGPR PPC_BIT_NR(46) /* TGPR usage on 602/603 x */ #define MSR_CE PPC_BIT_NR(46) /* Critical int. enable on embedded PPC x */ #define MSR_ILE PPC_BIT_NR(47) /* Interrupt little-endian mode */ +#define MSR_UIE PPC_BIT_NR(47) /* Unmaskable Interrupt Enable (PPE42) */ #define MSR_EE PPC_BIT_NR(48) /* External interrupt enable */ #define MSR_PR PPC_BIT_NR(49) /* Problem state hflags */ #define MSR_FP PPC_BIT_NR(50) /* Floating point available hflags */ #define MSR_ME PPC_BIT_NR(51) /* Machine check interrupt enable */ #define MSR_FE0 PPC_BIT_NR(52) /* Floating point exception mode 0 */ +#define MSR_IS2 PPC_BIT_NR(52) /* Instance Specific Bit 2 (PPE42) */ +#define MSR_IS3 PPC_BIT_NR(53) /* Instance Specific Bit 3 (PPE42) */ #define MSR_SE PPC_BIT_NR(53) /* Single-step trace enable x hflags */ #define MSR_DWE PPC_BIT_NR(53) /* Debug wait enable on 405 x */ #define MSR_UBLE PPC_BIT_NR(53) /* User BTB lock enable on e500 x */ #define MSR_BE PPC_BIT_NR(54) /* Branch trace enable x hflags */ #define MSR_DE PPC_BIT_NR(54) /* Debug int. enable on embedded PPC x */ #define MSR_FE1 PPC_BIT_NR(55) /* Floating point exception mode 1 */ +#define MSR_IPE PPC_BIT_NR(55) /* Imprecise Mode Enable (PPE42) */ #define MSR_AL PPC_BIT_NR(56) /* AL bit on POWER */ +#define MSR_SIBRCA0 PPC_BIT_NR(56) /* SIB Return Code Accumulator 0 (PPE42) */ +#define MSR_SIBRCA1 PPC_BIT_NR(57) /* SIB Return Code Accumulator 1 (PPE42) */ #define MSR_EP PPC_BIT_NR(57) /* Exception prefix on 601 */ #define MSR_IR PPC_BIT_NR(58) /* Instruction relocate */ #define MSR_IS PPC_BIT_NR(58) /* Instruction address space (BookE) */ +#define MSR_SIBRCA2 PPC_BIT_NR(58) /* SIB Return Code Accumulator 2 (PPE42) */ +#define MSR_SIBRCA3 PPC_BIT_NR(59) /* SIB Return Code Accumulator 3 (PPE42) */ #define MSR_DR PPC_BIT_NR(59) /* Data relocate */ #define MSR_DS PPC_BIT_NR(59) /* Data address space (BookE) */ #define MSR_PE PPC_BIT_NR(60) /* Protection enable on 403 */ +#define MSR_SIBRCA4 PPC_BIT_NR(60) /* SIB Return Code Accumulator 4 (PPE42) */ +#define MSR_SIBRCA5 PPC_BIT_NR(61) /* SIB Return Code Accumulator 5 (PPE42) */ #define MSR_PX PPC_BIT_NR(61) /* Protection exclusive on 403 x */ #define MSR_PMM PPC_BIT_NR(61) /* Performance monitor mark on POWER x */ #define MSR_RI PPC_BIT_NR(62) /* Recoverable interrupt 1 */ +#define MSR_SIBRCA6 PPC_BIT_NR(62) /* SIB Return Code Accumulator 6 (PPE42) */ +#define MSR_SIBRCA7 PPC_BIT_NR(63) /* SIB Return Code Accumulator 7 (PPE42) */ #define MSR_LE PPC_BIT_NR(63) /* Little-endian mode 1 hflags */ FIELD(MSR, SF, MSR_SF, 1) @@ -517,6 +544,9 @@ FIELD(MSR, PX, MSR_PX, 1) FIELD(MSR, PMM, MSR_PMM, 1) FIELD(MSR, RI, MSR_RI, 1) FIELD(MSR, LE, MSR_LE, 1) +FIELD(MSR, SEM, MSR_SEM6, 7) +FIELD(MSR, SIBRC, MSR_SIBRC2, 3) +FIELD(MSR, SIBRCA, MSR_SIBRCA7, 8) /* * FE0 and FE1 bits are not side-by-side @@ -785,6 +815,8 @@ enum { POWERPC_FLAG_SMT_1LPAR = 0x00800000, /* Has BHRB */ POWERPC_FLAG_BHRB = 0x01000000, + /* Use PPE42-specific behavior */ + POWERPC_FLAG_PPE42 = 0x02000000, }; /* @@ -1754,9 +1786,12 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_BOOKE_CSRR0 (0x03A) #define SPR_BOOKE_CSRR1 (0x03B) #define SPR_BOOKE_DEAR (0x03D) +#define SPR_PPE42_EDR (0x03D) #define SPR_IAMR (0x03D) #define SPR_BOOKE_ESR (0x03E) +#define SPR_PPE42_ISR (0x03E) #define SPR_BOOKE_IVPR (0x03F) +#define SPR_PPE42_IVPR (0x03F) #define SPR_MPC_EIE (0x050) #define SPR_MPC_EID (0x051) #define SPR_MPC_NRI (0x052) @@ -1822,6 +1857,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_TBU40 (0x11E) #define SPR_SVR (0x11E) #define SPR_BOOKE_PIR (0x11E) +#define SPR_PPE42_PIR (0x11E) #define SPR_PVR (0x11F) #define SPR_HSPRG0 (0x130) #define SPR_BOOKE_DBSR (0x130) @@ -1831,6 +1867,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_BOOKE_EPCR (0x133) #define SPR_SPURR (0x134) #define SPR_BOOKE_DBCR0 (0x134) +#define SPR_PPE42_DBCR (0x134) #define SPR_IBCR (0x135) #define SPR_PURR (0x135) #define SPR_BOOKE_DBCR1 (0x135) @@ -1848,6 +1885,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_HSRR1 (0x13B) #define SPR_BOOKE_IAC4 (0x13B) #define SPR_BOOKE_DAC1 (0x13C) +#define SPR_PPE42_DACR (0x13C) #define SPR_MMCRH (0x13C) #define SPR_DABR2 (0x13D) #define SPR_BOOKE_DAC2 (0x13D) @@ -1857,12 +1895,14 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_BOOKE_DVC2 (0x13F) #define SPR_LPIDR (0x13F) #define SPR_BOOKE_TSR (0x150) +#define SPR_PPE42_TSR (0x150) #define SPR_HMER (0x150) #define SPR_HMEER (0x151) #define SPR_PCR (0x152) #define SPR_HEIR (0x153) #define SPR_BOOKE_LPIDR (0x152) #define SPR_BOOKE_TCR (0x154) +#define SPR_PPE42_TCR (0x154) #define SPR_BOOKE_TLB0PS (0x158) #define SPR_BOOKE_TLB1PS (0x159) #define SPR_BOOKE_TLB2PS (0x15A) @@ -2532,6 +2572,12 @@ enum { PPC2_MEM_LWSYNC = 0x0000000000200000ULL, /* ISA 2.06 BCD assist instructions */ PPC2_BCDA_ISA206 = 0x0000000000400000ULL, + /* PPE42 instructions */ + PPC2_PPE42 = 0x0000000000800000ULL, + /* PPE42X instructions */ + PPC2_PPE42X = 0x0000000001000000ULL, + /* PPE42XM instructions */ + PPC2_PPE42XM = 0x0000000002000000ULL, #define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_VSX | PPC2_PRCNTL | PPC2_DBRX | \ PPC2_ISA205 | PPC2_VSX207 | PPC2_PERM_ISA206 | \ @@ -2541,7 +2587,8 @@ enum { PPC2_ALTIVEC_207 | PPC2_ISA207S | PPC2_DFP | \ PPC2_FP_CVT_S64 | PPC2_TM | PPC2_PM_ISA206 | \ PPC2_ISA300 | PPC2_ISA310 | PPC2_MEM_LWSYNC | \ - PPC2_BCDA_ISA206) + PPC2_BCDA_ISA206 | PPC2_PPE42 | PPC2_PPE42X | \ + PPC2_PPE42XM) }; /*****************************************************************************/ From 0af45e550b50f1c41164e5c0440654319942707e Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 25 Sep 2025 15:17:40 -0500 Subject: [PATCH 0990/1794] target/ppc: Add IBM PPE42 family of processors Adds the IBM PPE42 family of 32-bit processors supporting the PPE42, PPE42X and PPE42XM processor versions. These processors are used as embedded processors in the IBM Power9, Power10 and Power12 processors for various tasks. It is basically a stripped down version of the IBM PowerPC 405 processor, with some added instructions for handling 64-bit loads and stores. For more information on the PPE 42 processor please visit: https://wiki.raptorcs.com/w/images/a/a3/PPE_42X_Core_Users_Manual.pdf Supports PPE42 SPR's (Including the MSR). Does not yet support exceptions, new PPE42 instructions and does not prevent access to some invalid instructions and registers (currently allows access to invalid GPR's and CR fields). Signed-off-by: Glenn Miles Reviewed-by: Chinmay Rath Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925201758.652077-3-milesg@linux.ibm.com Message-ID: <20250925201758.652077-3-milesg@linux.ibm.com> --- target/ppc/cpu-models.c | 7 ++ target/ppc/cpu_init.c | 207 ++++++++++++++++++++++++++++++++------- target/ppc/helper_regs.c | 41 +++++--- target/ppc/translate.c | 6 +- 4 files changed, 205 insertions(+), 56 deletions(-) diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c index ea86ea202abea..09f73e23a8909 100644 --- a/target/ppc/cpu-models.c +++ b/target/ppc/cpu-models.c @@ -116,6 +116,13 @@ NULL) POWERPC_DEF("x2vp20", CPU_POWERPC_X2VP20, 405, NULL) + /* PPE42 Embedded Controllers */ + POWERPC_DEF("PPE42", CPU_POWERPC_PPE42, ppe42, + "Generic PPE 42") + POWERPC_DEF("PPE42X", CPU_POWERPC_PPE42X, ppe42x, + "Generic PPE 42X") + POWERPC_DEF("PPE42XM", CPU_POWERPC_PPE42XM, ppe42xm, + "Generic PPE 42XM") /* PowerPC 440 family */ #if defined(TODO_USER_ONLY) POWERPC_DEF("440", CPU_POWERPC_440, 440GP, diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index db841f126039f..c78b255085bec 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -1653,6 +1653,47 @@ static void register_8xx_sprs(CPUPPCState *env) * ... and more (thermal management, performance counters, ...) */ +static void register_ppe42_sprs(CPUPPCState *env) +{ + spr_register(env, SPR_PPE42_EDR, "EDR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_PPE42_ISR, "ISR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_PPE42_IVPR, "IVPR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0xfff80000); + spr_register(env, SPR_PPE42_PIR, "PIR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_pir, + 0x00000000); + spr_register(env, SPR_PPE42_DBCR, "DBCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_40x_dbcr0, + 0x00000000); + spr_register(env, SPR_PPE42_DACR, "DACR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Timer */ + spr_register(env, SPR_DECR, "DECR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_decr, &spr_write_decr, + 0x00000000); + spr_register(env, SPR_PPE42_TSR, "TSR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_booke_tsr, + 0x00000000); + spr_register(env, SPR_BOOKE_TCR, "TCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_booke_tcr, + 0x00000000); +} + /*****************************************************************************/ /* Exception vectors models */ static void init_excp_4xx(CPUPPCState *env) @@ -2200,6 +2241,79 @@ POWERPC_FAMILY(405)(ObjectClass *oc, const void *data) POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; } +static void init_proc_ppe42(CPUPPCState *env) +{ + register_ppe42_sprs(env); + + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc40x_irq_init(env_archcpu(env)); + + SET_FIT_PERIOD(8, 12, 16, 20); + SET_WDT_PERIOD(16, 20, 24, 28); +} + +static void ppe42_class_common_init(PowerPCCPUClass *pcc) +{ + pcc->init_proc = init_proc_ppe42; + pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; + pcc->insns_flags = PPC_INSNS_BASE | + PPC_WRTEE | + PPC_CACHE | + PPC_CACHE_DCBZ | + PPC_MEM_SYNC; + pcc->msr_mask = R_MSR_SEM_MASK | + (1ull << MSR_IS0) | + R_MSR_SIBRC_MASK | + (1ull << MSR_LP) | + (1ull << MSR_WE) | + (1ull << MSR_IS1) | + (1ull << MSR_UIE) | + (1ull << MSR_EE) | + (1ull << MSR_ME) | + (1ull << MSR_IS2) | + (1ull << MSR_IS3) | + (1ull << MSR_IPE) | + R_MSR_SIBRCA_MASK; + pcc->mmu_model = POWERPC_MMU_REAL; + pcc->excp_model = POWERPC_EXCP_40x; + pcc->bus_model = PPC_FLAGS_INPUT_PPE42; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_PPE42 | POWERPC_FLAG_BUS_CLK; +} + +POWERPC_FAMILY(ppe42)(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PPE 42"; + pcc->insns_flags2 = PPC2_PPE42; + ppe42_class_common_init(pcc); +} + +POWERPC_FAMILY(ppe42x)(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PPE 42X"; + pcc->insns_flags2 = PPC2_PPE42 | PPC2_PPE42X; + ppe42_class_common_init(pcc); +} + +POWERPC_FAMILY(ppe42xm)(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PPE 42XM"; + pcc->insns_flags2 = PPC2_PPE42 | PPC2_PPE42X | PPC2_PPE42XM; + ppe42_class_common_init(pcc); +} + static void init_proc_440EP(CPUPPCState *env) { register_BookE_sprs(env, 0x000000000000FFFFULL); @@ -6802,53 +6916,64 @@ static void init_ppc_proc(PowerPCCPU *cpu) /* MSR bits & flags consistency checks */ if (env->msr_mask & (1 << 25)) { - switch (env->flags & (POWERPC_FLAG_SPE | POWERPC_FLAG_VRE)) { + switch (env->flags & (POWERPC_FLAG_SPE | POWERPC_FLAG_VRE | + POWERPC_FLAG_PPE42)) { case POWERPC_FLAG_SPE: case POWERPC_FLAG_VRE: + case POWERPC_FLAG_PPE42: break; default: fprintf(stderr, "PowerPC MSR definition inconsistency\n" - "Should define POWERPC_FLAG_SPE or POWERPC_FLAG_VRE\n"); + "Should define POWERPC_FLAG_SPE or POWERPC_FLAG_VRE\n" + "or POWERPC_FLAG_PPE42\n"); exit(1); } - } else if (env->flags & (POWERPC_FLAG_SPE | POWERPC_FLAG_VRE)) { + } else if (env->flags & (POWERPC_FLAG_SPE | POWERPC_FLAG_VRE | + POWERPC_FLAG_PPE42)) { fprintf(stderr, "PowerPC MSR definition inconsistency\n" - "Should not define POWERPC_FLAG_SPE nor POWERPC_FLAG_VRE\n"); + "Should not define POWERPC_FLAG_SPE nor POWERPC_FLAG_VRE\n" + "nor POWERPC_FLAG_PPE42\n"); exit(1); } if (env->msr_mask & (1 << 17)) { - switch (env->flags & (POWERPC_FLAG_TGPR | POWERPC_FLAG_CE)) { + switch (env->flags & (POWERPC_FLAG_TGPR | POWERPC_FLAG_CE | + POWERPC_FLAG_PPE42)) { case POWERPC_FLAG_TGPR: case POWERPC_FLAG_CE: + case POWERPC_FLAG_PPE42: break; default: fprintf(stderr, "PowerPC MSR definition inconsistency\n" - "Should define POWERPC_FLAG_TGPR or POWERPC_FLAG_CE\n"); + "Should define POWERPC_FLAG_TGPR or POWERPC_FLAG_CE\n" + "or POWERPC_FLAG_PPE42\n"); exit(1); } - } else if (env->flags & (POWERPC_FLAG_TGPR | POWERPC_FLAG_CE)) { + } else if (env->flags & (POWERPC_FLAG_TGPR | POWERPC_FLAG_CE | + POWERPC_FLAG_PPE42)) { fprintf(stderr, "PowerPC MSR definition inconsistency\n" - "Should not define POWERPC_FLAG_TGPR nor POWERPC_FLAG_CE\n"); + "Should not define POWERPC_FLAG_TGPR nor POWERPC_FLAG_CE\n" + "nor POWERPC_FLAG_PPE42\n"); exit(1); } if (env->msr_mask & (1 << 10)) { switch (env->flags & (POWERPC_FLAG_SE | POWERPC_FLAG_DWE | - POWERPC_FLAG_UBLE)) { + POWERPC_FLAG_UBLE | POWERPC_FLAG_PPE42)) { case POWERPC_FLAG_SE: case POWERPC_FLAG_DWE: case POWERPC_FLAG_UBLE: + case POWERPC_FLAG_PPE42: break; default: fprintf(stderr, "PowerPC MSR definition inconsistency\n" "Should define POWERPC_FLAG_SE or POWERPC_FLAG_DWE or " - "POWERPC_FLAG_UBLE\n"); + "POWERPC_FLAG_UBLE or POWERPC_FLAG_PPE42\n"); exit(1); } } else if (env->flags & (POWERPC_FLAG_SE | POWERPC_FLAG_DWE | - POWERPC_FLAG_UBLE)) { + POWERPC_FLAG_UBLE | POWERPC_FLAG_PPE42)) { fprintf(stderr, "PowerPC MSR definition inconsistency\n" "Should not define POWERPC_FLAG_SE nor POWERPC_FLAG_DWE nor " - "POWERPC_FLAG_UBLE\n"); + "POWERPC_FLAG_UBLE nor POWERPC_FLAG_PPE42\n"); exit(1); } if (env->msr_mask & (1 << 9)) { @@ -6867,18 +6992,23 @@ static void init_ppc_proc(PowerPCCPU *cpu) exit(1); } if (env->msr_mask & (1 << 2)) { - switch (env->flags & (POWERPC_FLAG_PX | POWERPC_FLAG_PMM)) { + switch (env->flags & (POWERPC_FLAG_PX | POWERPC_FLAG_PMM | + POWERPC_FLAG_PPE42)) { case POWERPC_FLAG_PX: case POWERPC_FLAG_PMM: + case POWERPC_FLAG_PPE42: break; default: fprintf(stderr, "PowerPC MSR definition inconsistency\n" - "Should define POWERPC_FLAG_PX or POWERPC_FLAG_PMM\n"); + "Should define POWERPC_FLAG_PX or POWERPC_FLAG_PMM\n" + "or POWERPC_FLAG_PPE42\n"); exit(1); } - } else if (env->flags & (POWERPC_FLAG_PX | POWERPC_FLAG_PMM)) { + } else if (env->flags & (POWERPC_FLAG_PX | POWERPC_FLAG_PMM | + POWERPC_FLAG_PPE42)) { fprintf(stderr, "PowerPC MSR definition inconsistency\n" - "Should not define POWERPC_FLAG_PX nor POWERPC_FLAG_PMM\n"); + "Should not define POWERPC_FLAG_PX nor POWERPC_FLAG_PMM\n" + "nor POWERPC_FLAG_PPE42\n"); exit(1); } if ((env->flags & POWERPC_FLAG_BUS_CLK) == 0) { @@ -7243,39 +7373,40 @@ static void ppc_cpu_reset_hold(Object *obj, ResetType type) } msr = (target_ulong)0; - msr |= (target_ulong)MSR_HVB; - msr |= (target_ulong)1 << MSR_EP; + if (!(env->flags & POWERPC_FLAG_PPE42)) { + msr |= (target_ulong)MSR_HVB; + msr |= (target_ulong)1 << MSR_EP; #if defined(DO_SINGLE_STEP) && 0 - /* Single step trace mode */ - msr |= (target_ulong)1 << MSR_SE; - msr |= (target_ulong)1 << MSR_BE; + /* Single step trace mode */ + msr |= (target_ulong)1 << MSR_SE; + msr |= (target_ulong)1 << MSR_BE; #endif #if defined(CONFIG_USER_ONLY) - msr |= (target_ulong)1 << MSR_FP; /* Allow floating point usage */ - msr |= (target_ulong)1 << MSR_FE0; /* Allow floating point exceptions */ - msr |= (target_ulong)1 << MSR_FE1; - msr |= (target_ulong)1 << MSR_VR; /* Allow altivec usage */ - msr |= (target_ulong)1 << MSR_VSX; /* Allow VSX usage */ - msr |= (target_ulong)1 << MSR_SPE; /* Allow SPE usage */ - msr |= (target_ulong)1 << MSR_PR; + msr |= (target_ulong)1 << MSR_FP; /* Allow floating point usage */ + msr |= (target_ulong)1 << MSR_FE0; /* Allow floating point exceptions */ + msr |= (target_ulong)1 << MSR_FE1; + msr |= (target_ulong)1 << MSR_VR; /* Allow altivec usage */ + msr |= (target_ulong)1 << MSR_VSX; /* Allow VSX usage */ + msr |= (target_ulong)1 << MSR_SPE; /* Allow SPE usage */ + msr |= (target_ulong)1 << MSR_PR; #if defined(TARGET_PPC64) - msr |= (target_ulong)1 << MSR_TM; /* Transactional memory */ + msr |= (target_ulong)1 << MSR_TM; /* Transactional memory */ #endif #if !TARGET_BIG_ENDIAN - msr |= (target_ulong)1 << MSR_LE; /* Little-endian user mode */ - if (!((env->msr_mask >> MSR_LE) & 1)) { - fprintf(stderr, "Selected CPU does not support little-endian.\n"); - exit(1); - } + msr |= (target_ulong)1 << MSR_LE; /* Little-endian user mode */ + if (!((env->msr_mask >> MSR_LE) & 1)) { + fprintf(stderr, "Selected CPU does not support little-endian.\n"); + exit(1); + } #endif #endif #if defined(TARGET_PPC64) - if (mmu_is_64bit(env->mmu_model)) { - msr |= (1ULL << MSR_SF); - } + if (mmu_is_64bit(env->mmu_model)) { + msr |= (1ULL << MSR_SF); + } #endif - + } hreg_store_msr(env, msr, 1); #if !defined(CONFIG_USER_ONLY) diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 5f217397490ab..41b7b939ec75b 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -308,9 +308,6 @@ int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv) value &= ~(1 << MSR_ME); value |= env->msr & (1 << MSR_ME); } - if ((value ^ env->msr) & (R_MSR_IR_MASK | R_MSR_DR_MASK)) { - cpu_interrupt_exittb(cs); - } if ((env->mmu_model == POWERPC_MMU_BOOKE || env->mmu_model == POWERPC_MMU_BOOKE206) && ((value ^ env->msr) & R_MSR_GS_MASK)) { @@ -321,8 +318,14 @@ int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv) /* Swap temporary saved registers with GPRs */ hreg_swap_gpr_tgpr(env); } - if (unlikely((value ^ env->msr) & R_MSR_EP_MASK)) { - env->excp_prefix = FIELD_EX64(value, MSR, EP) * 0xFFF00000; + /* PPE42 uses IR, DR and EP MSR bits for other purposes */ + if (likely(!(env->flags & POWERPC_FLAG_PPE42))) { + if ((value ^ env->msr) & (R_MSR_IR_MASK | R_MSR_DR_MASK)) { + cpu_interrupt_exittb(cs); + } + if (unlikely((value ^ env->msr) & R_MSR_EP_MASK)) { + env->excp_prefix = FIELD_EX64(value, MSR, EP) * 0xFFF00000; + } } /* * If PR=1 then EE, IR and DR must be 1 @@ -464,6 +467,23 @@ void register_generic_sprs(PowerPCCPU *cpu) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); + + spr_register(env, SPR_PVR, "PVR", + /* Linux permits userspace to read PVR */ +#if defined(CONFIG_LINUX_USER) + &spr_read_generic, +#else + SPR_NOACCESS, +#endif + SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + pcc->pvr); + + /* PPE42 doesn't support SPRG1-3, SVR or TB regs */ + if (env->insns_flags2 & PPC2_PPE42) { + return; + } + spr_register(env, SPR_SPRG1, "SPRG1", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, @@ -477,17 +497,6 @@ void register_generic_sprs(PowerPCCPU *cpu) &spr_read_generic, &spr_write_generic, 0x00000000); - spr_register(env, SPR_PVR, "PVR", - /* Linux permits userspace to read PVR */ -#if defined(CONFIG_LINUX_USER) - &spr_read_generic, -#else - SPR_NOACCESS, -#endif - SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - pcc->pvr); - /* Register SVR if it's defined to anything else than POWERPC_SVR_NONE */ if (pcc->svr != POWERPC_SVR_NONE) { if (pcc->svr & POWERPC_SVR_E500) { diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 27f90c3cc5671..fc817dab54ca1 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -4264,8 +4264,10 @@ static void gen_mtmsr(DisasContext *ctx) /* L=1 form only updates EE and RI */ mask &= (1ULL << MSR_RI) | (1ULL << MSR_EE); } else { - /* mtmsr does not alter S, ME, or LE */ - mask &= ~((1ULL << MSR_LE) | (1ULL << MSR_ME) | (1ULL << MSR_S)); + if (likely(!(ctx->insns_flags2 & PPC2_PPE42))) { + /* mtmsr does not alter S, ME, or LE */ + mask &= ~((1ULL << MSR_LE) | (1ULL << MSR_ME) | (1ULL << MSR_S)); + } /* * XXX: we need to update nip before the store if we enter From 27eee49e6e3cecf69ab02f84fc2d9e19ef8bd3e2 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 25 Sep 2025 15:17:41 -0500 Subject: [PATCH 0991/1794] target/ppc: IBM PPE42 exception flags and regs Introduces flags and register definitions needed for the IBM PPE42 exception model. Signed-off-by: Glenn Miles Reviewed-by: Chinmay Rath Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925201758.652077-4-milesg@linux.ibm.com Message-ID: <20250925201758.652077-4-milesg@linux.ibm.com> --- target/ppc/cpu.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 8e13ce41a96c8..787020f6f90cb 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -220,6 +220,8 @@ typedef enum powerpc_excp_t { POWERPC_EXCP_POWER10, /* POWER11 exception model */ POWERPC_EXCP_POWER11, + /* PPE42 exception model */ + POWERPC_EXCP_PPE42, } powerpc_excp_t; /*****************************************************************************/ @@ -760,6 +762,31 @@ FIELD(MSR, SIBRCA, MSR_SIBRCA7, 8) #define ESR_VLEMI PPC_BIT(58) /* VLE operation */ #define ESR_MIF PPC_BIT(62) /* Misaligned instruction (VLE) */ +/* PPE42 Interrupt Status Register bits */ +#define PPE42_ISR_SRSMS0 PPC_BIT_NR(48) /* Sys Reset State Machine State 0 */ +#define PPE42_ISR_SRSMS1 PPC_BIT_NR(49) /* Sys Reset State Machine State 1 */ +#define PPE42_ISR_SRSMS2 PPC_BIT_NR(50) /* Sys Reset State Machine State 2 */ +#define PPE42_ISR_SRSMS3 PPC_BIT_NR(51) /* Sys Reset State Machine State 3 */ +#define PPE42_ISR_EP PPC_BIT_NR(53) /* MSR[EE] Maskable Event Pending */ +#define PPE42_ISR_PTR PPC_BIT_NR(56) /* Program Interrupt from trap */ +#define PPE42_ISR_ST PPC_BIT_NR(57) /* Data Interrupt caused by store */ +#define PPE42_ISR_MFE PPC_BIT_NR(60) /* Multiple Fault Error */ +#define PPE42_ISR_MCS0 PPC_BIT_NR(61) /* Machine Check Status bit0 */ +#define PPE42_ISR_MCS1 PPC_BIT_NR(62) /* Machine Check Status bit1 */ +#define PPE42_ISR_MCS2 PPC_BIT_NR(63) /* Machine Check Status bit2 */ +FIELD(PPE42_ISR, SRSMS, PPE42_ISR_SRSMS3, 4) +FIELD(PPE42_ISR, MCS, PPE42_ISR_MCS2, 3) + +/* PPE42 Machine Check Status field values */ +#define PPE42_ISR_MCS_INSTRUCTION 0 +#define PPE42_ISR_MCS_DATA_LOAD 1 +#define PPE42_ISR_MCS_DATA_PRECISE_STORE 2 +#define PPE42_ISR_MCS_DATA_IMPRECISE_STORE 3 +#define PPE42_ISR_MCS_PROGRAM 4 +#define PPE42_ISR_MCS_ISI 5 +#define PPE42_ISR_MCS_ALIGNMENT 6 +#define PPE42_ISR_MCS_DSI 7 + /* Transaction EXception And Summary Register bits */ #define TEXASR_FAILURE_PERSISTENT (63 - 7) #define TEXASR_DISALLOWED (63 - 8) From 7928680e0aaf0c755ce97800791bf73185749a88 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 25 Sep 2025 15:17:42 -0500 Subject: [PATCH 0992/1794] target/ppc: Add IBM PPE42 exception model Add support for the IBM PPE42 exception model including new exception vectors, exception priorities and setting of PPE42 SPRs for determining the cause of an exception. Signed-off-by: Glenn Miles Reviewed-by: Chinmay Rath Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925201758.652077-5-milesg@linux.ibm.com Message-ID: <20250925201758.652077-5-milesg@linux.ibm.com> --- target/ppc/cpu_init.c | 39 ++++++++- target/ppc/excp_helper.c | 163 +++++++++++++++++++++++++++++++++++ target/ppc/tcg-excp_helper.c | 12 +++ 3 files changed, 213 insertions(+), 1 deletion(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index c78b255085bec..9e4ea8fd13bec 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -1720,6 +1720,30 @@ static void init_excp_4xx(CPUPPCState *env) #endif } +static void init_excp_ppe42(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + /* Machine Check vector changed after version 0 */ + if (((env->spr[SPR_PVR] & 0xf00000ul) >> 20) == 0) { + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000000; + } else { + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000020; + } + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000040; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000060; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000080; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x000000A0; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x000000C0; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x000000E0; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_FIT] = 0x00000120; + env->excp_vectors[POWERPC_EXCP_WDT] = 0x00000140; + env->ivpr_mask = 0xFFFFFE00UL; + /* Hardware reset vector */ + env->hreset_vector = 0x00000040UL; +#endif +} + static void init_excp_MPC5xx(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) @@ -2245,6 +2269,7 @@ static void init_proc_ppe42(CPUPPCState *env) { register_ppe42_sprs(env); + init_excp_ppe42(env); env->dcache_line_size = 32; env->icache_line_size = 32; /* Allocate hardware IRQ controller */ @@ -2278,7 +2303,7 @@ static void ppe42_class_common_init(PowerPCCPUClass *pcc) (1ull << MSR_IPE) | R_MSR_SIBRCA_MASK; pcc->mmu_model = POWERPC_MMU_REAL; - pcc->excp_model = POWERPC_EXCP_40x; + pcc->excp_model = POWERPC_EXCP_PPE42; pcc->bus_model = PPC_FLAGS_INPUT_PPE42; pcc->bfd_mach = bfd_mach_ppc_403; pcc->flags = POWERPC_FLAG_PPE42 | POWERPC_FLAG_BUS_CLK; @@ -7856,6 +7881,18 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags) * they can be read with "p $ivor0", "p $ivor1", etc. */ break; + case POWERPC_EXCP_PPE42: + qemu_fprintf(f, "SRR0 " TARGET_FMT_lx " SRR1 " TARGET_FMT_lx "\n", + env->spr[SPR_SRR0], env->spr[SPR_SRR1]); + + qemu_fprintf(f, " TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx + " ISR " TARGET_FMT_lx " EDR " TARGET_FMT_lx "\n", + env->spr[SPR_PPE42_TCR], env->spr[SPR_PPE42_TSR], + env->spr[SPR_PPE42_ISR], env->spr[SPR_PPE42_EDR]); + + qemu_fprintf(f, " PIR " TARGET_FMT_lx " IVPR " TARGET_FMT_lx "\n", + env->spr[SPR_PPE42_PIR], env->spr[SPR_PPE42_IVPR]); + break; case POWERPC_EXCP_40x: qemu_fprintf(f, " TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " ESR " TARGET_FMT_lx " DEAR " TARGET_FMT_lx "\n", diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 1efdc4066ebb9..d8bca19fff516 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -949,6 +949,125 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) powerpc_set_excp_state(cpu, vector, new_msr); } +static void powerpc_excp_ppe42(PowerPCCPU *cpu, int excp) +{ + CPUPPCState *env = &cpu->env; + target_ulong msr, new_msr, vector; + target_ulong mcs = PPE42_ISR_MCS_INSTRUCTION; + bool promote_unmaskable; + + msr = env->msr; + + /* + * New interrupt handler msr preserves SIBRC and ME unless explicitly + * overridden by the exception. All other MSR bits are zeroed out. + */ + new_msr = env->msr & (((target_ulong)1 << MSR_ME) | R_MSR_SIBRC_MASK); + + /* HV emu assistance interrupt only exists on server arch 2.05 or later */ + if (excp == POWERPC_EXCP_HV_EMU) { + excp = POWERPC_EXCP_PROGRAM; + } + + /* + * Unmaskable interrupts (Program, ISI, Alignment and DSI) are promoted to + * machine check if MSR_UIE is 0. + */ + promote_unmaskable = !(msr & ((target_ulong)1 << MSR_UIE)); + + + switch (excp) { + case POWERPC_EXCP_MCHECK: /* Machine check exception */ + break; + case POWERPC_EXCP_DSI: /* Data storage exception */ + trace_ppc_excp_dsi(env->spr[SPR_PPE42_ISR], env->spr[SPR_PPE42_EDR]); + if (promote_unmaskable) { + excp = POWERPC_EXCP_MCHECK; + mcs = PPE42_ISR_MCS_DSI; + } + break; + case POWERPC_EXCP_ISI: /* Instruction storage exception */ + trace_ppc_excp_isi(msr, env->nip); + if (promote_unmaskable) { + excp = POWERPC_EXCP_MCHECK; + mcs = PPE42_ISR_MCS_ISI; + } + break; + case POWERPC_EXCP_EXTERNAL: /* External input */ + break; + case POWERPC_EXCP_ALIGN: /* Alignment exception */ + if (promote_unmaskable) { + excp = POWERPC_EXCP_MCHECK; + mcs = PPE42_ISR_MCS_ALIGNMENT; + } + break; + case POWERPC_EXCP_PROGRAM: /* Program exception */ + if (promote_unmaskable) { + excp = POWERPC_EXCP_MCHECK; + mcs = PPE42_ISR_MCS_PROGRAM; + } + switch (env->error_code & ~0xF) { + case POWERPC_EXCP_INVAL: + trace_ppc_excp_inval(env->nip); + env->spr[SPR_PPE42_ISR] &= ~((target_ulong)1 << PPE42_ISR_PTR); + break; + case POWERPC_EXCP_TRAP: + env->spr[SPR_PPE42_ISR] |= ((target_ulong)1 << PPE42_ISR_PTR); + break; + default: + /* Should never occur */ + cpu_abort(env_cpu(env), "Invalid program exception %d. Aborting\n", + env->error_code); + break; + } +#ifdef CONFIG_TCG + env->spr[SPR_PPE42_EDR] = ppc_ldl_code(env, env->nip); +#endif + break; + case POWERPC_EXCP_DECR: /* Decrementer exception */ + break; + case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ + trace_ppc_excp_print("FIT"); + break; + case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ + trace_ppc_excp_print("WDT"); + break; + case POWERPC_EXCP_RESET: /* System reset exception */ + /* reset exceptions don't have ME set */ + new_msr &= ~((target_ulong)1 << MSR_ME); + break; + default: + cpu_abort(env_cpu(env), "Invalid PPE42 exception %d. Aborting\n", + excp); + break; + } + + env->spr[SPR_SRR0] = env->nip; + env->spr[SPR_SRR1] = msr; + + vector = env->excp_vectors[excp]; + if (vector == (target_ulong)-1ULL) { + cpu_abort(env_cpu(env), + "Raised an exception without defined vector %d\n", excp); + } + vector |= env->spr[SPR_PPE42_IVPR]; + + if (excp == POWERPC_EXCP_MCHECK) { + /* Also set the Machine Check Status (MCS) */ + env->spr[SPR_PPE42_ISR] &= ~R_PPE42_ISR_MCS_MASK; + env->spr[SPR_PPE42_ISR] |= (mcs & R_PPE42_ISR_MCS_MASK); + env->spr[SPR_PPE42_ISR] &= ~((target_ulong)1 << PPE42_ISR_MFE); + + /* Machine checks halt execution if MSR_ME is 0 */ + powerpc_mcheck_checkstop(env); + + /* machine check exceptions don't have ME set */ + new_msr &= ~((target_ulong)1 << MSR_ME); + } + + powerpc_set_excp_state(cpu, vector, new_msr); +} + static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) { CPUPPCState *env = &cpu->env; @@ -1589,6 +1708,9 @@ void powerpc_excp(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_POWER11: powerpc_excp_books(cpu, excp); break; + case POWERPC_EXCP_PPE42: + powerpc_excp_ppe42(cpu, excp); + break; default: g_assert_not_reached(); } @@ -1945,6 +2067,43 @@ static int p9_next_unmasked_interrupt(CPUPPCState *env, } #endif /* TARGET_PPC64 */ +static int ppe42_next_unmasked_interrupt(CPUPPCState *env) +{ + bool async_deliver; + + /* External reset */ + if (env->pending_interrupts & PPC_INTERRUPT_RESET) { + return PPC_INTERRUPT_RESET; + } + /* Machine check exception */ + if (env->pending_interrupts & PPC_INTERRUPT_MCK) { + return PPC_INTERRUPT_MCK; + } + + async_deliver = FIELD_EX64(env->msr, MSR, EE); + + if (async_deliver != 0) { + /* Watchdog timer */ + if (env->pending_interrupts & PPC_INTERRUPT_WDT) { + return PPC_INTERRUPT_WDT; + } + /* External Interrupt */ + if (env->pending_interrupts & PPC_INTERRUPT_EXT) { + return PPC_INTERRUPT_EXT; + } + /* Fixed interval timer */ + if (env->pending_interrupts & PPC_INTERRUPT_FIT) { + return PPC_INTERRUPT_FIT; + } + /* Decrementer exception */ + if (env->pending_interrupts & PPC_INTERRUPT_DECR) { + return PPC_INTERRUPT_DECR; + } + } + + return 0; +} + static int ppc_next_unmasked_interrupt(CPUPPCState *env) { uint32_t pending_interrupts = env->pending_interrupts; @@ -1970,6 +2129,10 @@ static int ppc_next_unmasked_interrupt(CPUPPCState *env) } #endif + if (env->excp_model == POWERPC_EXCP_PPE42) { + return ppe42_next_unmasked_interrupt(env); + } + /* External reset */ if (pending_interrupts & PPC_INTERRUPT_RESET) { return PPC_INTERRUPT_RESET; diff --git a/target/ppc/tcg-excp_helper.c b/target/ppc/tcg-excp_helper.c index f835be515635a..edecfb857258b 100644 --- a/target/ppc/tcg-excp_helper.c +++ b/target/ppc/tcg-excp_helper.c @@ -229,6 +229,18 @@ void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, case POWERPC_MMU_BOOKE206: env->spr[SPR_BOOKE_DEAR] = vaddr; break; + case POWERPC_MMU_REAL: + if (env->flags & POWERPC_FLAG_PPE42) { + env->spr[SPR_PPE42_EDR] = vaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_PPE42_ISR] |= PPE42_ISR_ST; + } else { + env->spr[SPR_PPE42_ISR] &= ~PPE42_ISR_ST; + } + } else { + env->spr[SPR_DAR] = vaddr; + } + break; default: env->spr[SPR_DAR] = vaddr; break; From 880aa4cb06ff5e86b6feab3030e14a16fea1dced Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 25 Sep 2025 15:17:43 -0500 Subject: [PATCH 0993/1794] target/ppc: Support for IBM PPE42 MMU The IBM PPE42 processor only supports real mode addressing and does not distinguish between problem and supervisor states. It also uses the IR and DR MSR bits for other purposes. Therefore, add a check for PPE42 when we update hflags and cause it to ignore the IR and DR bits when calculating MMU indexes. Signed-off-by: Glenn Miles Reviewed-by: Chinmay Rath Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925201758.652077-6-milesg@linux.ibm.com Message-ID: <20250925201758.652077-6-milesg@linux.ibm.com> --- target/ppc/helper_regs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 41b7b939ec75b..a07e6a7b7b646 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -186,6 +186,10 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env) if (env->spr[SPR_LPCR] & LPCR_HR) { hflags |= 1 << HFLAGS_HR; } + if (unlikely(ppc_flags & POWERPC_FLAG_PPE42)) { + /* PPE42 has a single address space and no problem state */ + msr = 0; + } #ifndef CONFIG_USER_ONLY if (!env->has_hv_mode || (msr & (1ull << MSR_HV))) { From f7ec91c23906ca364650e95f2a28e0ef6c411386 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 25 Sep 2025 15:17:44 -0500 Subject: [PATCH 0994/1794] target/ppc: Add IBM PPE42 special instructions Adds the following instructions exclusively for IBM PPE42 processors: LSKU LCXU STSKU STCXU LVD LVDU LVDX STVD STVDU STVDX SLVD SRVD CMPWBC CMPLWBC CMPWIBC BNBWI BNBW CLRBWIBC CLRWBC DCBQ RLDICL RLDICR RLDIMI A PPE42 GCC compiler is available here: https://github.com/open-power/ppe42-gcc For more information on the PPE42 processors please visit: https://wiki.raptorcs.com/w/images/a/a3/PPE_42X_Core_Users_Manual.pdf Signed-off-by: Glenn Miles Reviewed-by: Chinmay Rath Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925201758.652077-7-milesg@linux.ibm.com Message-ID: <20250925201758.652077-7-milesg@linux.ibm.com> --- target/ppc/insn32.decode | 66 ++- target/ppc/translate.c | 29 +- target/ppc/translate/ppe-impl.c.inc | 609 ++++++++++++++++++++++++++++ 3 files changed, 694 insertions(+), 10 deletions(-) create mode 100644 target/ppc/translate/ppe-impl.c.inc diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index e53fd2840d445..16652b5c13066 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -58,6 +58,10 @@ %ds_rtp 22:4 !function=times_2 @DS_rtp ...... ....0 ra:5 .............. .. &D rt=%ds_rtp si=%ds_si +%dd_si 3:s13 +&DD rt ra si:int64_t +@DD ...... rt:5 ra:5 ............. . .. &DD si=%dd_si + &DX_b vrt b %dx_b 6:10 16:5 0:1 @DX_b ...... vrt:5 ..... .......... ..... . &DX_b b=%dx_b @@ -66,6 +70,11 @@ %dx_d 6:s10 16:5 0:1 @DX ...... rt:5 ..... .......... ..... . &DX d=%dx_d +%md_sh 1:1 11:5 +%md_mb 5:1 6:5 +&MD rs ra sh mb rc +@MD ...... rs:5 ra:5 ..... ...... ... . rc:1 &MD sh=%md_sh mb=%md_mb + &VA vrt vra vrb rc @VA ...... vrt:5 vra:5 vrb:5 rc:5 ...... &VA @@ -322,6 +331,13 @@ LDUX 011111 ..... ..... ..... 0000110101 - @X LQ 111000 ..... ..... ............ ---- @DQ_rtp +LVD 000101 ..... ..... ................ @D +LVDU 001001 ..... ..... ................ @D +LVDX 011111 ..... ..... ..... 0000010001 - @X +LSKU 111010 ..... ..... ............. 0 11 @DD +LCXU 111010 ..... ..... ............. 1 11 @DD + + ### Fixed-Point Store Instructions STB 100110 ..... ..... ................ @D @@ -346,6 +362,11 @@ STDUX 011111 ..... ..... ..... 0010110101 - @X STQ 111110 ..... ..... ..............10 @DS_rtp +STVDU 010110 ..... ..... ................ @D +STVDX 011111 ..... ..... ..... 0010010001 - @X +STSKU 111110 ..... ..... ............. 0 11 @DD +STCXU 111110 ..... ..... ............. 1 11 @DD + ### Fixed-Point Compare Instructions CMP 011111 ... - . ..... ..... 0000000000 - @X_bfl @@ -461,8 +482,14 @@ PRTYD 011111 ..... ..... ----- 0010111010 - @X_sa BPERMD 011111 ..... ..... ..... 0011111100 - @X CFUGED 011111 ..... ..... ..... 0011011100 - @X -CNTLZDM 011111 ..... ..... ..... 0000111011 - @X -CNTTZDM 011111 ..... ..... ..... 1000111011 - @X +{ + SLVD 011111 ..... ..... ..... 0000111011 . @X_rc + CNTLZDM 011111 ..... ..... ..... 0000111011 - @X +} +{ + SRVD 011111 ..... ..... ..... 1000111011 . @X_rc + CNTTZDM 011111 ..... ..... ..... 1000111011 - @X +} PDEPD 011111 ..... ..... ..... 0010011100 - @X PEXTD 011111 ..... ..... ..... 0010111100 - @X @@ -981,8 +1008,16 @@ LXSSP 111001 ..... ..... .............. 11 @DS STXSSP 111101 ..... ..... .............. 11 @DS LXV 111101 ..... ..... ............ . 001 @DQ_TSX STXV 111101 ..... ..... ............ . 101 @DQ_TSX -LXVP 000110 ..... ..... ............ 0000 @DQ_TSXP -STXVP 000110 ..... ..... ............ 0001 @DQ_TSXP + +# STVD PPE instruction overlaps with the LXVP and STXVP instructions +{ + STVD 000110 ..... ..... ................ @D + [ + LXVP 000110 ..... ..... ............ 0000 @DQ_TSXP + STXVP 000110 ..... ..... ............ 0001 @DQ_TSXP + ] +} + LXVX 011111 ..... ..... ..... 0100 - 01100 . @X_TSX STXVX 011111 ..... ..... ..... 0110001100 . @X_TSX LXVPX 011111 ..... ..... ..... 0101001101 - @X_TSXP @@ -1300,3 +1335,26 @@ CLRBHRB 011111 ----- ----- ----- 0110101110 - ## Misc POWER instructions ATTN 000000 00000 00000 00000 0100000000 0 + +# Fused compare-branch instructions for PPE only +%fcb_bdx 1:s10 !function=times_4 +&FCB px:bool ra rb:uint64_t bdx lk:bool +@FCB ...... .. px:1 .. ra:5 rb:5 .......... lk:1 &FCB bdx=%fcb_bdx +&FCB_bix px:bool bix ra rb:uint64_t bdx lk:bool +@FCB_bix ...... .. px:1 bix:2 ra:5 rb:5 .......... lk:1 &FCB_bix bdx=%fcb_bdx + +CMPWBC 000001 00 . .. ..... ..... .......... . @FCB_bix +CMPLWBC 000001 01 . .. ..... ..... .......... . @FCB_bix +CMPWIBC 000001 10 . .. ..... ..... .......... . @FCB_bix +BNBWI 000001 11 . 00 ..... ..... .......... . @FCB +BNBW 000001 11 . 01 ..... ..... .......... . @FCB +CLRBWIBC 000001 11 . 10 ..... ..... .......... . @FCB +CLRBWBC 000001 11 . 11 ..... ..... .......... . @FCB + +# Data Cache Block Query for PPE only +DCBQ 011111 ..... ..... ..... 0110010110 - @X + +# Rotate Doubleword Instructions for PPE only +RLDICL 011110 ..... ..... ..... ...... 000 . . @MD +RLDICR 011110 ..... ..... ..... ...... 001 . . @MD +RLDIMI 011110 ..... ..... ..... ...... 011 . . @MD diff --git a/target/ppc/translate.c b/target/ppc/translate.c index fc817dab54ca1..d422789a1d0d2 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -209,6 +209,11 @@ struct DisasContext { #define DISAS_CHAIN DISAS_TARGET_2 /* lookup next tb, pc updated */ #define DISAS_CHAIN_UPDATE DISAS_TARGET_3 /* lookup next tb, pc stale */ +static inline bool is_ppe(const DisasContext *ctx) +{ + return !!(ctx->flags & POWERPC_FLAG_PPE42); +} + /* Return true iff byteswap is needed in a scalar memop */ static inline bool need_byteswap(const DisasContext *ctx) { @@ -556,11 +561,8 @@ void spr_access_nop(DisasContext *ctx, int sprn, int gprn) #endif -/* SPR common to all PowerPC */ -/* XER */ -void spr_read_xer(DisasContext *ctx, int gprn, int sprn) +static void gen_get_xer(DisasContext *ctx, TCGv dst) { - TCGv dst = cpu_gpr[gprn]; TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); @@ -579,9 +581,16 @@ void spr_read_xer(DisasContext *ctx, int gprn, int sprn) } } -void spr_write_xer(DisasContext *ctx, int sprn, int gprn) +/* SPR common to all PowerPC */ +/* XER */ +void spr_read_xer(DisasContext *ctx, int gprn, int sprn) +{ + TCGv dst = cpu_gpr[gprn]; + gen_get_xer(ctx, dst); +} + +static void gen_set_xer(DisasContext *ctx, TCGv src) { - TCGv src = cpu_gpr[gprn]; /* Write all flags, while reading back check for isa300 */ tcg_gen_andi_tl(cpu_xer, src, ~((1u << XER_SO) | @@ -594,6 +603,12 @@ void spr_write_xer(DisasContext *ctx, int sprn, int gprn) tcg_gen_extract_tl(cpu_ca, src, XER_CA, 1); } +void spr_write_xer(DisasContext *ctx, int sprn, int gprn) +{ + TCGv src = cpu_gpr[gprn]; + gen_set_xer(ctx, src); +} + /* LR */ void spr_read_lr(DisasContext *ctx, int gprn, int sprn) { @@ -5755,6 +5770,8 @@ static bool resolve_PLS_D(DisasContext *ctx, arg_D *d, arg_PLS_D *a) #include "translate/bhrb-impl.c.inc" +#include "translate/ppe-impl.c.inc" + /* Handles lfdp */ static void gen_dform39(DisasContext *ctx) { diff --git a/target/ppc/translate/ppe-impl.c.inc b/target/ppc/translate/ppe-impl.c.inc new file mode 100644 index 0000000000000..0a0590344ea5c --- /dev/null +++ b/target/ppc/translate/ppe-impl.c.inc @@ -0,0 +1,609 @@ +/* + * IBM PPE Instructions + * + * Copyright (c) 2025, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +static bool vdr_is_valid(uint32_t vdr) +{ + const uint32_t valid_bitmap = 0xf00003ff; + return !!((1ul << (vdr & 0x1f)) & valid_bitmap); +} + +static bool ppe_gpr_is_valid(uint32_t reg) +{ + const uint32_t valid_bitmap = 0xf00027ff; + return !!((1ul << (reg & 0x1f)) & valid_bitmap); +} + +#define CHECK_VDR(CTX, VDR) \ + do { \ + if (unlikely(!vdr_is_valid(VDR))) { \ + gen_invalid(CTX); \ + return true; \ + } \ + } while (0) + +#define CHECK_PPE_GPR(CTX, REG) \ + do { \ + if (unlikely(!ppe_gpr_is_valid(REG))) { \ + gen_invalid(CTX); \ + return true; \ + } \ + } while (0) + +#define VDR_PAIR_REG(VDR) (((VDR) + 1) & 0x1f) + +#define CHECK_PPE_LEVEL(CTX, LVL) \ + do { \ + if (unlikely(!((CTX)->insns_flags2 & (LVL)))) { \ + gen_invalid(CTX); \ + return true; \ + } \ + } while (0) + +static bool trans_LCXU(DisasContext *ctx, arg_LCXU *a) +{ + int i; + TCGv base, EA; + TCGv lo, hi; + TCGv_i64 t8; + const uint8_t vd_list[] = {9, 7, 5, 3, 0}; + + if (unlikely(!is_ppe(ctx))) { + return false; + } + CHECK_PPE_LEVEL(ctx, PPC2_PPE42X); + CHECK_PPE_GPR(ctx, a->rt); + + if (unlikely((a->rt != a->ra) || (a->ra == 0) || (a->si < 0xB))) { + gen_invalid(ctx); + return true; + } + + EA = tcg_temp_new(); + base = tcg_temp_new(); + + tcg_gen_addi_tl(base, cpu_gpr[a->ra], a->si * 8); + gen_store_spr(SPR_PPE42_EDR, base); + + t8 = tcg_temp_new_i64(); + + tcg_gen_addi_tl(EA, base, -8); + tcg_gen_qemu_ld_i64(t8, EA, ctx->mem_idx, DEF_MEMOP(MO_64) | MO_ALIGN); + tcg_gen_extr_i64_tl(cpu_gpr[31], cpu_gpr[30], t8); + + tcg_gen_addi_tl(EA, EA, -8); + tcg_gen_qemu_ld_i64(t8, EA, ctx->mem_idx, DEF_MEMOP(MO_64) | MO_ALIGN); + tcg_gen_extr_i64_tl(cpu_gpr[29], cpu_gpr[28], t8); + + lo = tcg_temp_new(); + hi = tcg_temp_new(); + + tcg_gen_addi_tl(EA, EA, -8); + tcg_gen_qemu_ld_i64(t8, EA, ctx->mem_idx, DEF_MEMOP(MO_64) | MO_ALIGN); + tcg_gen_extr_i64_tl(lo, hi, t8); + gen_store_spr(SPR_SRR0, hi); + gen_store_spr(SPR_SRR1, lo); + + tcg_gen_addi_tl(EA, EA, -8); + tcg_gen_qemu_ld_i64(t8, EA, ctx->mem_idx, DEF_MEMOP(MO_64) | MO_ALIGN); + tcg_gen_extr_i64_tl(lo, hi, t8); + gen_set_xer(ctx, hi); + tcg_gen_mov_tl(cpu_ctr, lo); + + for (i = 0; i < sizeof(vd_list); i++) { + int vd = vd_list[i]; + tcg_gen_addi_tl(EA, EA, -8); + tcg_gen_qemu_ld_i64(t8, EA, ctx->mem_idx, DEF_MEMOP(MO_64) | MO_ALIGN); + tcg_gen_extr_i64_tl(cpu_gpr[VDR_PAIR_REG(vd)], cpu_gpr[vd], t8); + } + + tcg_gen_addi_tl(EA, EA, -8); + tcg_gen_qemu_ld_i64(t8, EA, ctx->mem_idx, DEF_MEMOP(MO_64) | MO_ALIGN); + tcg_gen_extr_i64_tl(lo, hi, t8); + tcg_gen_shri_tl(hi, hi, 28); + tcg_gen_trunc_tl_i32(cpu_crf[0], hi); + gen_store_spr(SPR_SPRG0, lo); + + tcg_gen_addi_tl(EA, base, 4); + tcg_gen_qemu_ld_tl(cpu_lr, EA, ctx->mem_idx, DEF_MEMOP(MO_32) | MO_ALIGN); + tcg_gen_mov_tl(cpu_gpr[a->ra], base); + return true; +} + +static bool trans_LSKU(DisasContext *ctx, arg_LSKU *a) +{ + int64_t n; + TCGv base, EA; + TCGv lo, hi; + TCGv_i64 t8; + + if (unlikely(!is_ppe(ctx))) { + return false; + } + + CHECK_PPE_LEVEL(ctx, PPC2_PPE42X); + CHECK_PPE_GPR(ctx, a->rt); + + if (unlikely((a->rt != a->ra) || (a->ra == 0) || + (a->si & PPC_BIT(0)) || (a->si == 0))) { + gen_invalid(ctx); + return true; + } + + EA = tcg_temp_new(); + base = tcg_temp_new(); + gen_addr_register(ctx, base); + + + tcg_gen_addi_tl(base, base, a->si * 8); + gen_store_spr(SPR_PPE42_EDR, base); + + n = a->si - 1; + t8 = tcg_temp_new_i64(); + if (n > 0) { + tcg_gen_addi_tl(EA, base, -8); + tcg_gen_qemu_ld_i64(t8, EA, ctx->mem_idx, DEF_MEMOP(MO_64) | MO_ALIGN); + hi = cpu_gpr[30]; + lo = cpu_gpr[31]; + tcg_gen_extr_i64_tl(lo, hi, t8); + } + if (n > 1) { + tcg_gen_addi_tl(EA, base, -16); + tcg_gen_qemu_ld_i64(t8, EA, ctx->mem_idx, DEF_MEMOP(MO_64) | MO_ALIGN); + hi = cpu_gpr[28]; + lo = cpu_gpr[29]; + tcg_gen_extr_i64_tl(lo, hi, t8); + } + tcg_gen_addi_tl(EA, base, 4); + tcg_gen_qemu_ld_tl(cpu_lr, EA, ctx->mem_idx, DEF_MEMOP(MO_32) | MO_ALIGN); + tcg_gen_mov_tl(cpu_gpr[a->ra], base); + return true; +} + +static bool trans_STCXU(DisasContext *ctx, arg_STCXU *a) +{ + TCGv EA; + TCGv lo, hi; + TCGv_i64 t8; + int i; + const uint8_t vd_list[] = {9, 7, 5, 3, 0}; + + if (unlikely(!is_ppe(ctx))) { + return false; + } + + CHECK_PPE_LEVEL(ctx, PPC2_PPE42X); + CHECK_PPE_GPR(ctx, a->rt); + + if (unlikely((a->rt != a->ra) || (a->ra == 0) || !(a->si & PPC_BIT(0)))) { + gen_invalid(ctx); + return true; + } + + EA = tcg_temp_new(); + tcg_gen_addi_tl(EA, cpu_gpr[a->ra], 4); + tcg_gen_qemu_st_tl(cpu_lr, EA, ctx->mem_idx, DEF_MEMOP(MO_32) | MO_ALIGN); + + gen_store_spr(SPR_PPE42_EDR, cpu_gpr[a->ra]); + + t8 = tcg_temp_new_i64(); + + tcg_gen_concat_tl_i64(t8, cpu_gpr[31], cpu_gpr[30]); + tcg_gen_addi_tl(EA, cpu_gpr[a->ra], -8); + tcg_gen_qemu_st_i64(t8, EA, ctx->mem_idx, DEF_MEMOP(MO_64) | MO_ALIGN); + + tcg_gen_concat_tl_i64(t8, cpu_gpr[29], cpu_gpr[28]); + tcg_gen_addi_tl(EA, EA, -8); + tcg_gen_qemu_st_i64(t8, EA, ctx->mem_idx, DEF_MEMOP(MO_64) | MO_ALIGN); + + lo = tcg_temp_new(); + hi = tcg_temp_new(); + + gen_load_spr(hi, SPR_SRR0); + gen_load_spr(lo, SPR_SRR1); + tcg_gen_concat_tl_i64(t8, lo, hi); + tcg_gen_addi_tl(EA, EA, -8); + tcg_gen_qemu_st_i64(t8, EA, ctx->mem_idx, DEF_MEMOP(MO_64) | MO_ALIGN); + + gen_get_xer(ctx, hi); + tcg_gen_mov_tl(lo, cpu_ctr); + tcg_gen_concat_tl_i64(t8, lo, hi); + tcg_gen_addi_tl(EA, EA, -8); + tcg_gen_qemu_st_i64(t8, EA, ctx->mem_idx, DEF_MEMOP(MO_64) | MO_ALIGN); + + for (i = 0; i < sizeof(vd_list); i++) { + int vd = vd_list[i]; + tcg_gen_concat_tl_i64(t8, cpu_gpr[VDR_PAIR_REG(vd)], cpu_gpr[vd]); + tcg_gen_addi_tl(EA, EA, -8); + tcg_gen_qemu_st_i64(t8, EA, ctx->mem_idx, DEF_MEMOP(MO_64) | MO_ALIGN); + } + + gen_load_spr(lo, SPR_SPRG0); + tcg_gen_extu_i32_tl(hi, cpu_crf[0]); + tcg_gen_shli_tl(hi, hi, 28); + tcg_gen_concat_tl_i64(t8, lo, hi); + tcg_gen_addi_tl(EA, EA, -8); + tcg_gen_qemu_st_i64(t8, EA, ctx->mem_idx, DEF_MEMOP(MO_64) | MO_ALIGN); + + tcg_gen_addi_tl(EA, cpu_gpr[a->ra], a->si * 8); + tcg_gen_qemu_st_tl(cpu_gpr[a->rt], EA, ctx->mem_idx, DEF_MEMOP(MO_32) | + MO_ALIGN); + tcg_gen_mov_tl(cpu_gpr[a->ra], EA); + return true; +} + +static bool trans_STSKU(DisasContext *ctx, arg_STSKU *a) +{ + int64_t n; + TCGv base, EA; + TCGv lo, hi; + TCGv_i64 t8; + + if (unlikely(!is_ppe(ctx))) { + return false; + } + + CHECK_PPE_LEVEL(ctx, PPC2_PPE42X); + CHECK_PPE_GPR(ctx, a->rt); + + if (unlikely((a->rt != a->ra) || (a->ra == 0) || !(a->si & PPC_BIT(0)))) { + gen_invalid(ctx); + return true; + } + + EA = tcg_temp_new(); + base = tcg_temp_new(); + gen_addr_register(ctx, base); + tcg_gen_addi_tl(EA, base, 4); + tcg_gen_qemu_st_tl(cpu_lr, EA, ctx->mem_idx, DEF_MEMOP(MO_32) | MO_ALIGN); + + gen_store_spr(SPR_PPE42_EDR, base); + + n = ~(a->si); + + t8 = tcg_temp_new_i64(); + if (n > 0) { + hi = cpu_gpr[30]; + lo = cpu_gpr[31]; + tcg_gen_concat_tl_i64(t8, lo, hi); + tcg_gen_addi_tl(EA, base, -8); + tcg_gen_qemu_st_i64(t8, EA, ctx->mem_idx, DEF_MEMOP(MO_64) | MO_ALIGN); + } + if (n > 1) { + hi = cpu_gpr[28]; + lo = cpu_gpr[29]; + tcg_gen_concat_tl_i64(t8, lo, hi); + tcg_gen_addi_tl(EA, base, -16); + tcg_gen_qemu_st_i64(t8, EA, ctx->mem_idx, DEF_MEMOP(MO_64) | MO_ALIGN); + } + + tcg_gen_addi_tl(EA, base, a->si * 8); + tcg_gen_qemu_st_tl(cpu_gpr[a->rt], EA, ctx->mem_idx, DEF_MEMOP(MO_32) | + MO_ALIGN); + tcg_gen_mov_tl(cpu_gpr[a->ra], EA); + return true; +} + +static bool do_ppe_ldst(DisasContext *ctx, int rt, int ra, TCGv disp, + bool update, bool store) +{ + TCGv ea; + int rt_lo; + TCGv_i64 t8; + + CHECK_VDR(ctx, rt); + CHECK_PPE_GPR(ctx, ra); + rt_lo = VDR_PAIR_REG(rt); + if (update && (ra == 0 || (!store && ((ra == rt) || (ra == rt_lo))))) { + gen_invalid(ctx); + return true; + } + gen_set_access_type(ctx, ACCESS_INT); + + ea = do_ea_calc(ctx, ra, disp); + t8 = tcg_temp_new_i64(); + if (store) { + tcg_gen_concat_tl_i64(t8, cpu_gpr[rt_lo], cpu_gpr[rt]); + tcg_gen_qemu_st_i64(t8, ea, ctx->mem_idx, DEF_MEMOP(MO_64)); + } else { + tcg_gen_qemu_ld_i64(t8, ea, ctx->mem_idx, DEF_MEMOP(MO_64)); + tcg_gen_extr_i64_tl(cpu_gpr[rt_lo], cpu_gpr[rt], t8); + } + if (update) { + tcg_gen_mov_tl(cpu_gpr[ra], ea); + } + return true; +} + +static bool do_ppe_ldst_D(DisasContext *ctx, arg_D *a, bool update, bool store) +{ + if (unlikely(!is_ppe(ctx))) { + return false; + } + return do_ppe_ldst(ctx, a->rt, a->ra, tcg_constant_tl(a->si), update, + store); +} + +static bool do_ppe_ldst_X(DisasContext *ctx, arg_X *a, bool store) +{ + if (unlikely(!is_ppe(ctx))) { + return false; + } + CHECK_PPE_GPR(ctx, a->rb); + return do_ppe_ldst(ctx, a->rt, a->ra, cpu_gpr[a->rb], false, store); +} + +TRANS(LVD, do_ppe_ldst_D, false, false) +TRANS(LVDU, do_ppe_ldst_D, true, false) +TRANS(STVD, do_ppe_ldst_D, false, true) +TRANS(STVDU, do_ppe_ldst_D, true, true) +TRANS(LVDX, do_ppe_ldst_X, false) +TRANS(STVDX, do_ppe_ldst_X, true) + + +static bool do_fcb(DisasContext *ctx, TCGv ra_val, TCGv rb_val, int bix, + int32_t bdx, bool s, bool px, bool lk) +{ + TCGCond cond; + uint32_t mask; + TCGLabel *no_branch; + target_ulong dest; + + /* Update CR0 */ + gen_op_cmp32(ra_val, rb_val, s, 0); + + if (lk) { + gen_setlr(ctx, ctx->base.pc_next); + } + + + mask = PPC_BIT32(28 + bix); + cond = (px) ? TCG_COND_TSTEQ : TCG_COND_TSTNE; + no_branch = gen_new_label(); + dest = ctx->cia + bdx; + + /* Do the branch if CR0[bix] == PX */ + tcg_gen_brcondi_i32(cond, cpu_crf[0], mask, no_branch); + gen_goto_tb(ctx, 0, dest); + gen_set_label(no_branch); + gen_goto_tb(ctx, 1, ctx->base.pc_next); + ctx->base.is_jmp = DISAS_NORETURN; + return true; +} + +static bool do_cmp_branch(DisasContext *ctx, arg_FCB_bix *a, bool s, + bool rb_is_gpr) +{ + TCGv old_ra; + TCGv rb_val; + + if (unlikely(!is_ppe(ctx))) { + return false; + } + CHECK_PPE_GPR(ctx, a->ra); + if (rb_is_gpr) { + CHECK_PPE_GPR(ctx, a->rb); + rb_val = cpu_gpr[a->rb]; + } else { + rb_val = tcg_constant_tl(a->rb); + } + if (a->bix == 3) { + old_ra = tcg_temp_new(); + tcg_gen_mov_tl(old_ra, cpu_gpr[a->ra]); + tcg_gen_sub_tl(cpu_gpr[a->ra], cpu_gpr[a->ra], rb_val); + return do_fcb(ctx, old_ra, rb_val, 2, + a->bdx, s, a->px, a->lk); + } else { + return do_fcb(ctx, cpu_gpr[a->ra], rb_val, a->bix, + a->bdx, s, a->px, a->lk); + } +} + +TRANS(CMPWBC, do_cmp_branch, true, true) +TRANS(CMPLWBC, do_cmp_branch, false, true) +TRANS(CMPWIBC, do_cmp_branch, true, false) + +static bool do_mask_branch(DisasContext *ctx, arg_FCB * a, bool invert, + bool update, bool rb_is_gpr) +{ + TCGv r; + TCGv mask, shift; + + if (unlikely(!is_ppe(ctx))) { + return false; + } + CHECK_PPE_GPR(ctx, a->ra); + if (rb_is_gpr) { + CHECK_PPE_GPR(ctx, a->rb); + mask = tcg_temp_new(); + shift = tcg_temp_new(); + tcg_gen_andi_tl(shift, cpu_gpr[a->rb], 0x1f); + tcg_gen_shr_tl(mask, tcg_constant_tl(0x80000000), shift); + } else { + mask = tcg_constant_tl(PPC_BIT32(a->rb)); + } + if (invert) { + tcg_gen_not_tl(mask, mask); + } + + /* apply mask to ra */ + r = tcg_temp_new(); + tcg_gen_and_tl(r, cpu_gpr[a->ra], mask); + if (update) { + tcg_gen_mov_tl(cpu_gpr[a->ra], r); + } + return do_fcb(ctx, r, tcg_constant_tl(0), 2, + a->bdx, false, a->px, a->lk); +} + +TRANS(BNBWI, do_mask_branch, false, false, false) +TRANS(BNBW, do_mask_branch, false, false, true) +TRANS(CLRBWIBC, do_mask_branch, true, true, false) +TRANS(CLRBWBC, do_mask_branch, true, true, true) + +static void gen_set_Rc0_i64(DisasContext *ctx, TCGv_i64 reg) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_movi_i64(t0, CRF_EQ); + tcg_gen_movi_i64(t1, CRF_LT); + tcg_gen_movcond_i64(TCG_COND_LT, t0, reg, tcg_constant_i64(0), t1, t0); + tcg_gen_movi_i64(t1, CRF_GT); + tcg_gen_movcond_i64(TCG_COND_GT, t0, reg, tcg_constant_i64(0), t1, t0); + tcg_gen_extrl_i64_i32(t, t0); + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); + tcg_gen_or_i32(cpu_crf[0], cpu_crf[0], t); +} + +static bool do_shift64(DisasContext *ctx, arg_X_rc *a, bool left) +{ + int rt_lo, ra_lo; + TCGv_i64 t0, t8; + + if (unlikely(!is_ppe(ctx))) { + return false; + } + CHECK_PPE_LEVEL(ctx, PPC2_PPE42X); + CHECK_VDR(ctx, a->rt); + CHECK_VDR(ctx, a->ra); + CHECK_PPE_GPR(ctx, a->rb); + rt_lo = VDR_PAIR_REG(a->rt); + ra_lo = VDR_PAIR_REG(a->ra); + t8 = tcg_temp_new_i64(); + + /* AND rt with a mask that is 0 when rb >= 0x40 */ + t0 = tcg_temp_new_i64(); + tcg_gen_extu_tl_i64(t0, cpu_gpr[a->rb]); + tcg_gen_shli_i64(t0, t0, 0x39); + tcg_gen_sari_i64(t0, t0, 0x3f); + + /* form 64bit value from two 32bit regs */ + tcg_gen_concat_tl_i64(t8, cpu_gpr[rt_lo], cpu_gpr[a->rt]); + + /* apply mask */ + tcg_gen_andc_i64(t8, t8, t0); + + /* do the shift */ + tcg_gen_extu_tl_i64(t0, cpu_gpr[a->rb]); + tcg_gen_andi_i64(t0, t0, 0x3f); + if (left) { + tcg_gen_shl_i64(t8, t8, t0); + } else { + tcg_gen_shr_i64(t8, t8, t0); + } + + /* split the 64bit word back into two 32bit regs */ + tcg_gen_extr_i64_tl(cpu_gpr[ra_lo], cpu_gpr[a->ra], t8); + + /* update CR0 if requested */ + if (unlikely(a->rc != 0)) { + gen_set_Rc0_i64(ctx, t8); + } + return true; +} + +TRANS(SRVD, do_shift64, false) +TRANS(SLVD, do_shift64, true) + +static bool trans_DCBQ(DisasContext *ctx, arg_DCBQ * a) +{ + if (unlikely(!is_ppe(ctx))) { + return false; + } + + CHECK_PPE_GPR(ctx, a->rt); + CHECK_PPE_GPR(ctx, a->ra); + CHECK_PPE_GPR(ctx, a->rb); + + /* No cache exists, so just set RT to 0 */ + tcg_gen_movi_tl(cpu_gpr[a->rt], 0); + return true; +} + +static bool trans_RLDIMI(DisasContext *ctx, arg_RLDIMI *a) +{ + TCGv_i64 t_rs, t_ra; + int ra_lo, rs_lo; + uint32_t sh = a->sh; + uint32_t mb = a->mb; + uint32_t me = 63 - sh; + + if (unlikely(!is_ppe(ctx))) { + return false; + } + CHECK_PPE_LEVEL(ctx, PPC2_PPE42X); + CHECK_VDR(ctx, a->rs); + CHECK_VDR(ctx, a->ra); + + rs_lo = VDR_PAIR_REG(a->rs); + ra_lo = VDR_PAIR_REG(a->ra); + + t_rs = tcg_temp_new_i64(); + t_ra = tcg_temp_new_i64(); + + tcg_gen_concat_tl_i64(t_rs, cpu_gpr[rs_lo], cpu_gpr[a->rs]); + tcg_gen_concat_tl_i64(t_ra, cpu_gpr[ra_lo], cpu_gpr[a->ra]); + + if (mb <= me) { + tcg_gen_deposit_i64(t_ra, t_ra, t_rs, sh, me - mb + 1); + } else { + uint64_t mask = mask_u64(mb, me); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_rotli_i64(t1, t_rs, sh); + tcg_gen_andi_i64(t1, t1, mask); + tcg_gen_andi_i64(t_ra, t_ra, ~mask); + tcg_gen_or_i64(t_ra, t_ra, t1); + } + + tcg_gen_extr_i64_tl(cpu_gpr[ra_lo], cpu_gpr[a->ra], t_ra); + + if (unlikely(a->rc != 0)) { + gen_set_Rc0_i64(ctx, t_ra); + } + return true; +} + + +static bool gen_rldinm_i64(DisasContext *ctx, arg_MD *a, int mb, int me, int sh) +{ + int len = me - mb + 1; + int rsh = (64 - sh) & 63; + int ra_lo, rs_lo; + TCGv_i64 t8; + + if (unlikely(!is_ppe(ctx))) { + return false; + } + CHECK_PPE_LEVEL(ctx, PPC2_PPE42X); + CHECK_VDR(ctx, a->rs); + CHECK_VDR(ctx, a->ra); + + rs_lo = VDR_PAIR_REG(a->rs); + ra_lo = VDR_PAIR_REG(a->ra); + t8 = tcg_temp_new_i64(); + tcg_gen_concat_tl_i64(t8, cpu_gpr[rs_lo], cpu_gpr[a->rs]); + if (sh != 0 && len > 0 && me == (63 - sh)) { + tcg_gen_deposit_z_i64(t8, t8, sh, len); + } else if (me == 63 && rsh + len <= 64) { + tcg_gen_extract_i64(t8, t8, rsh, len); + } else { + tcg_gen_rotli_i64(t8, t8, sh); + tcg_gen_andi_i64(t8, t8, mask_u64(mb, me)); + } + tcg_gen_extr_i64_tl(cpu_gpr[ra_lo], cpu_gpr[a->ra], t8); + if (unlikely(a->rc != 0)) { + gen_set_Rc0_i64(ctx, t8); + } + return true; +} + +TRANS(RLDICL, gen_rldinm_i64, a->mb, 63, a->sh) +TRANS(RLDICR, gen_rldinm_i64, 0, a->mb, a->sh) From 7197f6f7baf29fd76486920e5603e43b25880d3b Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 25 Sep 2025 15:17:45 -0500 Subject: [PATCH 0995/1794] hw/ppc: Support for an IBM PPE42 CPU decrementer The IBM PPE42 processors support a 32-bit decrementer that can raise an external interrupt when DEC[0] transitions from a 0 to a -1 (a non-negative value to a negative value). It also continues decrementing even after this condition is met. The BookE timer is slightly different in that it raises an interrupt when the DEC value reaches 0 and stops decrementing at that point. Support a PPE42 version of the BookE timer by adding a new PPC_TIMER_PPE flag that has the timer code look for the transition from a non-negative value to a negative value and allows the value to continue decrementing. Signed-off-by: Glenn Miles Reviewed-by: Harsh Prateek Bora Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925201758.652077-8-milesg@linux.ibm.com Message-ID: <20250925201758.652077-8-milesg@linux.ibm.com> --- hw/ppc/ppc_booke.c | 7 ++++++- include/hw/ppc/ppc.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/ppc/ppc_booke.c b/hw/ppc/ppc_booke.c index 3872ae2822256..13403a56b1a48 100644 --- a/hw/ppc/ppc_booke.c +++ b/hw/ppc/ppc_booke.c @@ -352,7 +352,12 @@ void ppc_booke_timers_init(PowerPCCPU *cpu, uint32_t freq, uint32_t flags) booke_timer = g_new0(booke_timer_t, 1); cpu->env.tb_env = tb_env; - tb_env->flags = flags | PPC_TIMER_BOOKE | PPC_DECR_ZERO_TRIGGERED; + if (flags & PPC_TIMER_PPE) { + /* PPE's use a modified version of the booke behavior */ + tb_env->flags = flags | PPC_DECR_UNDERFLOW_TRIGGERED; + } else { + tb_env->flags = flags | PPC_TIMER_BOOKE | PPC_DECR_ZERO_TRIGGERED; + } tb_env->tb_freq = freq; tb_env->decr_freq = freq; diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h index 8a14d623f8caa..cb51d704c6d3b 100644 --- a/include/hw/ppc/ppc.h +++ b/include/hw/ppc/ppc.h @@ -52,6 +52,7 @@ struct ppc_tb_t { #define PPC_DECR_UNDERFLOW_LEVEL (1 << 4) /* Decr interrupt active when * the most significant bit is 1. */ +#define PPC_TIMER_PPE (1 << 5) /* Enable PPE support */ uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset); void cpu_ppc_tb_init(CPUPPCState *env, uint32_t freq); From 0ad5b8d9c8fdaa21c8384404d53fc2d86842beba Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 25 Sep 2025 15:17:46 -0500 Subject: [PATCH 0996/1794] hw/ppc: Add a test machine for the IBM PPE42 CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a test machine for the IBM PPE42 processor, including a DEC, FIT, WDT and 512 KiB of ram. The purpose of this machine is only to provide a generic platform for testing instructions of the recently added PPE42 processor model which is used extensively in the IBM Power9, Power10 and future Power server processors. Signed-off-by: Glenn Miles Reviewed-by: Cédric Le Goater Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925201758.652077-9-milesg@linux.ibm.com Message-ID: <20250925201758.652077-9-milesg@linux.ibm.com> --- MAINTAINERS | 6 +++ hw/ppc/Kconfig | 5 ++ hw/ppc/meson.build | 2 + hw/ppc/ppe42_machine.c | 101 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 hw/ppc/ppe42_machine.c diff --git a/MAINTAINERS b/MAINTAINERS index 7d134a85e6661..2ed9eb9353afe 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1530,6 +1530,12 @@ F: include/hw/pci-host/grackle.h F: pc-bios/qemu_vga.ndrv F: tests/functional/ppc/test_mac.py +PPE42 +M: Glenn Miles +L: qemu-ppc@nongnu.org +S: Odd Fixes +F: hw/ppc/ppe42_machine.c + PReP M: Hervé Poussineau L: qemu-ppc@nongnu.org diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index ced6bbc7404e3..7091d72fd88f8 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -44,6 +44,11 @@ config POWERNV select SSI_M25P80 select PNV_SPI +config PPC405 + bool + default y + depends on PPC + config PPC440 bool default y diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build index 9893f8adebb0e..170b90ae7d05e 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -57,6 +57,8 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files( 'pnv_n1_chiplet.c', )) # PowerPC 4xx boards +ppc_ss.add(when: 'CONFIG_PPC405', if_true: files( + 'ppe42_machine.c')) ppc_ss.add(when: 'CONFIG_PPC440', if_true: files( 'ppc440_bamboo.c', 'ppc440_uc.c')) diff --git a/hw/ppc/ppe42_machine.c b/hw/ppc/ppe42_machine.c new file mode 100644 index 0000000000000..f14a91b4e4dbd --- /dev/null +++ b/hw/ppc/ppe42_machine.c @@ -0,0 +1,101 @@ +/* + * Test Machine for the IBM PPE42 processor + * + * Copyright (c) 2025, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/error-report.h" +#include "system/address-spaces.h" +#include "hw/boards.h" +#include "hw/ppc/ppc.h" +#include "system/system.h" +#include "system/reset.h" +#include "system/kvm.h" +#include "qapi/error.h" + +#define TYPE_PPE42_MACHINE MACHINE_TYPE_NAME("ppe42_machine") +typedef MachineClass Ppe42MachineClass; +typedef struct Ppe42MachineState Ppe42MachineState; +DECLARE_OBJ_CHECKERS(Ppe42MachineState, Ppe42MachineClass, + PPE42_MACHINE, TYPE_PPE42_MACHINE) + +struct Ppe42MachineState { + MachineState parent_obj; + + PowerPCCPU cpu; +}; + +static void main_cpu_reset(void *opaque) +{ + PowerPCCPU *cpu = opaque; + + cpu_reset(CPU(cpu)); +} + +static void ppe42_machine_init(MachineState *machine) +{ + Ppe42MachineState *pms = PPE42_MACHINE(machine); + PowerPCCPU *cpu = &pms->cpu; + + if (kvm_enabled()) { + error_report("machine %s does not support the KVM accelerator", + MACHINE_GET_CLASS(machine)->name); + exit(EXIT_FAILURE); + } + if (machine->ram_size > 512 * KiB) { + error_report("RAM size more than 512 KiB is not supported"); + exit(1); + } + + /* init CPU */ + object_initialize_child(OBJECT(pms), "cpu", cpu, machine->cpu_type); + if (!qdev_realize(DEVICE(cpu), NULL, &error_fatal)) { + return; + } + + qemu_register_reset(main_cpu_reset, cpu); + + /* This sets the decrementer timebase */ + ppc_booke_timers_init(cpu, 37500000, PPC_TIMER_PPE); + + /* RAM */ + memory_region_add_subregion(get_system_memory(), 0xfff80000, machine->ram); +} + + +static void ppe42_machine_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + POWERPC_CPU_TYPE_NAME("PPE42"), + POWERPC_CPU_TYPE_NAME("PPE42X"), + POWERPC_CPU_TYPE_NAME("PPE42XM"), + NULL, + }; + + mc->desc = "PPE42 Test Machine"; + mc->init = ppe42_machine_init; + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("PPE42XM"); + mc->valid_cpu_types = valid_cpu_types; + mc->default_ram_id = "ram"; + mc->default_ram_size = 512 * KiB; +} + +static const TypeInfo ppe42_machine_info = { + .name = TYPE_PPE42_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(Ppe42MachineState), + .class_init = ppe42_machine_class_init, + .class_size = sizeof(Ppe42MachineClass), +}; + +static void ppe42_machine_register_types(void) +{ + type_register_static(&ppe42_machine_info); +} + +type_init(ppe42_machine_register_types); From fd93e521b01a5416bb7f106ac29d755bc3059aaa Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 25 Sep 2025 15:17:47 -0500 Subject: [PATCH 0997/1794] tests/functional: Add test for IBM PPE42 instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a functional test for the IBM PPE42 instructions which downloads a test image from a public github repo and then loads and executes the image. (see https://github.com/milesg-github/ppe42-tests for details) Test status is checked by periodically issuing 'info register' commands and checking the NIP value. If the NIP is 0xFFF80200 then the test successfully executed to completion. If the machine stops before the test completes or if a 90 second timeout is reached, then the test is marked as having failed. This test does not test any PowerPC instructions as it is expected that these instructions are well covered in other tests. Only instructions that are unique to the IBM PPE42 processor are tested. Signed-off-by: Glenn Miles Reviewed-by: Thomas Huth Tested-by: Cédric Le Goater Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250925201758.652077-10-milesg@linux.ibm.com Message-ID: <20250925201758.652077-10-milesg@linux.ibm.com> --- MAINTAINERS | 1 + tests/functional/ppc/meson.build | 1 + tests/functional/ppc/test_ppe42.py | 79 ++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 tests/functional/ppc/test_ppe42.py diff --git a/MAINTAINERS b/MAINTAINERS index 2ed9eb9353afe..406cef88f0ca8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1535,6 +1535,7 @@ M: Glenn Miles L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/ppe42_machine.c +F: tests/functional/ppc/test_ppe42.py PReP M: Hervé Poussineau diff --git a/tests/functional/ppc/meson.build b/tests/functional/ppc/meson.build index 3d562010d8cb0..ae061fe5a6125 100644 --- a/tests/functional/ppc/meson.build +++ b/tests/functional/ppc/meson.build @@ -15,6 +15,7 @@ tests_ppc_system_thorough = [ 'bamboo', 'mac', 'mpc8544ds', + 'ppe42', 'replay', 'sam460ex', 'tuxrun', diff --git a/tests/functional/ppc/test_ppe42.py b/tests/functional/ppc/test_ppe42.py new file mode 100644 index 0000000000000..26bbe11b2d37d --- /dev/null +++ b/tests/functional/ppc/test_ppe42.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# +# Functional tests for the IBM PPE42 processor +# +# Copyright (c) 2025, IBM Corporation +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset +import asyncio + +class Ppe42Machine(QemuSystemTest): + + timeout = 90 + poll_period = 1.0 + + ASSET_PPE42_TEST_IMAGE = Asset( + ('https://github.com/milesg-github/ppe42-tests/raw/refs/heads/main/' + 'images/ppe42-test.out'), + '03c1ac0fb7f6c025102a02776a93b35101dae7c14b75e4eab36a337e39042ea8') + + def _test_completed(self): + self.log.info("Checking for test completion...") + try: + output = self.vm.cmd('human-monitor-command', + command_line='info registers') + except Exception as err: + self.log.debug(f"'info registers' cmd failed due to {err=}," + " {type(err)=}") + raise + + self.log.info(output) + if "NIP fff80200" in output: + self.log.info("") + return True + else: + self.log.info("") + return False + + def _wait_pass_fail(self, timeout): + while not self._test_completed(): + if timeout >= self.poll_period: + timeout = timeout - self.poll_period + self.log.info(f"Waiting {self.poll_period} seconds for test" + " to complete...") + e = None + try: + e = self.vm.event_wait('STOP', self.poll_period) + + except asyncio.TimeoutError: + self.log.info("Poll period ended.") + pass + + except Exception as err: + self.log.debug(f"event_wait() failed due to {err=}," + " {type(err)=}") + raise + + if e != None: + self.log.debug(f"Execution stopped: {e}") + self.log.debug("Exiting due to test failure") + self.fail("Failure detected!") + break + else: + self.fail("Timed out waiting for test completion.") + + def test_ppe42_instructions(self): + self.set_machine('ppe42_machine') + self.require_accelerator("tcg") + image_path = self.ASSET_PPE42_TEST_IMAGE.fetch() + self.vm.add_args('-nographic') + self.vm.add_args('-device', f'loader,file={image_path}') + self.vm.add_args('-device', 'loader,addr=0xfff80040,cpu-num=0') + self.vm.add_args('-action', 'panic=pause') + self.vm.launch() + self._wait_pass_fail(self.timeout) + +if __name__ == '__main__': + QemuSystemTest.main() From f5738aedc21790bd07dbead6b6272a605d5c1138 Mon Sep 17 00:00:00 2001 From: Fabian Vogt Date: Tue, 19 Aug 2025 19:39:02 -0300 Subject: [PATCH 0998/1794] hw/intc/xics: Add missing call to register vmstate_icp_server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An obsolete wrapper function with a workaround was removed entirely, without restoring the call it wrapped. Without this, the guest is stuck after savevm/loadvm. Fixes: 24ee9229fe31 ("ppc/spapr: remove deprecated machine pseries-2.9") Signed-off-by: Fabian Vogt Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/6187781.lOV4Wx5bFT@fvogt-thinkpad Signed-off-by: Fabiano Rosas Reviewed-by: Gautam Menghani Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250819223905.2247-2-farosas@suse.de Message-ID: <20250819223905.2247-2-farosas@suse.de> --- hw/intc/xics.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index d9a199e88341b..200710eb6ca9c 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -335,6 +335,8 @@ static void icp_realize(DeviceState *dev, Error **errp) return; } } + + vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp); } static void icp_unrealize(DeviceState *dev) From 6285eebd3a5fea018eb51d696b51079f44dd1eb3 Mon Sep 17 00:00:00 2001 From: Harsh Prateek Bora Date: Tue, 6 May 2025 00:29:03 -0400 Subject: [PATCH 0999/1794] ppc/spapr: init lrdr-capapcity phys with ram size if maxmem not provided lrdr-capacity contains phys field which communicates the maximum address in bytes and therefore, the most memory that can be allocated to this partition. This is usually populated when maxmem is provided alongwith memory size on qemu command line. However since maxmem is an optional param, this leads to bits being set to 0 in absence of maxmem param. Fix this by initializing the respective bits as per total mem size in such case. Reported-by: Gaurav Batra Tested-by: David Christensen Signed-off-by: Harsh Prateek Bora Reviewed-by: Shivaprasad G Bhat Link: https://lore.kernel.org/r/20250506042903.76250-1-harshpb@linux.ibm.com Message-ID: <20250506042903.76250-1-harshpb@linux.ibm.com> --- hw/ppc/spapr.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index eb22333404d37..82fb23beaa8d6 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -907,6 +907,7 @@ static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) int rtas; GString *hypertas = g_string_sized_new(256); GString *qemu_hypertas = g_string_sized_new(256); + uint64_t max_device_addr = 0; uint32_t lrdr_capacity[] = { 0, 0, @@ -917,13 +918,15 @@ static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) /* Do we have device memory? */ if (MACHINE(spapr)->device_memory) { - uint64_t max_device_addr = MACHINE(spapr)->device_memory->base + + max_device_addr = MACHINE(spapr)->device_memory->base + memory_region_size(&MACHINE(spapr)->device_memory->mr); - - lrdr_capacity[0] = cpu_to_be32(max_device_addr >> 32); - lrdr_capacity[1] = cpu_to_be32(max_device_addr & 0xffffffff); + } else if (ms->ram_size == ms->maxram_size) { + max_device_addr = ms->ram_size; } + lrdr_capacity[0] = cpu_to_be32(max_device_addr >> 32); + lrdr_capacity[1] = cpu_to_be32(max_device_addr & 0xffffffff); + _FDT(rtas = fdt_add_subnode(fdt, 0, "rtas")); /* hypertas */ From 936a0f208833083fc4f4f8aebfe0f63751d2b7d6 Mon Sep 17 00:00:00 2001 From: Gautam Menghani Date: Mon, 11 Aug 2025 13:19:11 +0530 Subject: [PATCH 1000/1794] ppc/xive2: Fix integer overflow warning in xive2_redistribute() Coverity reported an integer overflow warning in xive2_redistribute() where the code does a left shift operation "0xffffffff << crowd". Fix the warning by using a 64 byte integer type. Also refactor the calculation into dedicated routines. Resolves: Coverity CID 1612608 Fixes: 555e446019f5 ("ppc/xive2: Support redistribution of group interrupts") Reviewed-by: Glenn Miles Signed-off-by: Gautam Menghani Reviewed-by: Amit Machhiwal Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250811074912.162774-1-gautam@linux.ibm.com Message-ID: <20250811074912.162774-1-gautam@linux.ibm.com> --- hw/intc/xive2.c | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index ee5fa26178497..fbb3b7975e50b 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -95,6 +95,35 @@ static void xive2_nvgc_set_backlog(Xive2Nvgc *nvgc, uint8_t priority, } } +static uint32_t xive2_nvgc_get_idx(uint32_t nvp_idx, uint8_t group) +{ + uint32_t nvgc_idx; + + if (group > 0) { + nvgc_idx = (nvp_idx & (0xffffffffULL << group)) | + ((1 << (group - 1)) - 1); + } else { + nvgc_idx = nvp_idx; + } + + return nvgc_idx; +} + +static uint8_t xive2_nvgc_get_blk(uint8_t nvp_blk, uint8_t crowd) +{ + uint8_t nvgc_blk; + + if (crowd > 0) { + crowd = (crowd == 3) ? 4 : crowd; + nvgc_blk = (nvp_blk & (0xffffffffULL << crowd)) | + ((1 << (crowd - 1)) - 1); + } else { + nvgc_blk = nvp_blk; + } + + return nvgc_blk; +} + uint64_t xive2_presenter_nvgc_backlog_op(XivePresenter *xptr, bool crowd, uint8_t blk, uint32_t idx, @@ -638,20 +667,8 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) trace_xive_redistribute(tctx->cs->cpu_index, ring, nvp_blk, nvp_idx); /* convert crowd/group to blk/idx */ - if (group > 0) { - nvgc_idx = (nvp_idx & (0xffffffff << group)) | - ((1 << (group - 1)) - 1); - } else { - nvgc_idx = nvp_idx; - } - - if (crowd > 0) { - crowd = (crowd == 3) ? 4 : crowd; - nvgc_blk = (nvp_blk & (0xffffffff << crowd)) | - ((1 << (crowd - 1)) - 1); - } else { - nvgc_blk = nvp_blk; - } + nvgc_idx = xive2_nvgc_get_idx(nvp_idx, group); + nvgc_blk = xive2_nvgc_get_blk(nvp_blk, crowd); /* Use blk/idx to retrieve the NVGC */ if (xive2_router_get_nvgc(xrtr, crowd, nvgc_blk, nvgc_idx, &nvgc)) { From f291e329e20e1ab4aafdde38533cadc11f2cf5a4 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Thu, 19 Jun 2025 15:28:36 +0530 Subject: [PATCH 1001/1794] target/ppc: Move floating-point rounding and conversion instructions to decodetree. Move below instructions to decodetree specification : fr{sp, in, iz, im}[s][.], fcti{w, d}[u, z, uz][s][.], fcfid[s, u, us][s][.] : X-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Signed-off-by: Chinmay Rath Reviewed-by: Richard Henderson Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250619095840.369351-2-rathc@linux.ibm.com Message-ID: <20250619095840.369351-2-rathc@linux.ibm.com> --- target/ppc/fpu_helper.c | 34 +++++----- target/ppc/helper.h | 34 +++++----- target/ppc/insn32.decode | 24 ++++++++ target/ppc/translate/fp-impl.c.inc | 99 ++++++++++++------------------ target/ppc/translate/fp-ops.c.inc | 21 ------- 5 files changed, 98 insertions(+), 114 deletions(-) diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 07b782f971be3..503cbd98adec7 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -562,14 +562,14 @@ uint64_t helper_##op(CPUPPCState *env, float64 arg) \ return ret; \ } -FPU_FCTI(fctiw, int32, 0x80000000U) -FPU_FCTI(fctiwz, int32_round_to_zero, 0x80000000U) -FPU_FCTI(fctiwu, uint32, 0x00000000U) -FPU_FCTI(fctiwuz, uint32_round_to_zero, 0x00000000U) -FPU_FCTI(fctid, int64, 0x8000000000000000ULL) -FPU_FCTI(fctidz, int64_round_to_zero, 0x8000000000000000ULL) -FPU_FCTI(fctidu, uint64, 0x0000000000000000ULL) -FPU_FCTI(fctiduz, uint64_round_to_zero, 0x0000000000000000ULL) +FPU_FCTI(FCTIW, int32, 0x80000000U) +FPU_FCTI(FCTIWZ, int32_round_to_zero, 0x80000000U) +FPU_FCTI(FCTIWU, uint32, 0x00000000U) +FPU_FCTI(FCTIWUZ, uint32_round_to_zero, 0x00000000U) +FPU_FCTI(FCTID, int64, 0x8000000000000000ULL) +FPU_FCTI(FCTIDZ, int64_round_to_zero, 0x8000000000000000ULL) +FPU_FCTI(FCTIDU, uint64, 0x0000000000000000ULL) +FPU_FCTI(FCTIDUZ, uint64_round_to_zero, 0x0000000000000000ULL) #define FPU_FCFI(op, cvtr, is_single) \ uint64_t helper_##op(CPUPPCState *env, uint64_t arg) \ @@ -586,10 +586,10 @@ uint64_t helper_##op(CPUPPCState *env, uint64_t arg) \ return farg.ll; \ } -FPU_FCFI(fcfid, int64_to_float64, 0) -FPU_FCFI(fcfids, int64_to_float32, 1) -FPU_FCFI(fcfidu, uint64_to_float64, 0) -FPU_FCFI(fcfidus, uint64_to_float32, 1) +FPU_FCFI(FCFID, int64_to_float64, 0) +FPU_FCFI(FCFIDS, int64_to_float32, 1) +FPU_FCFI(FCFIDU, uint64_to_float64, 0) +FPU_FCFI(FCFIDUS, uint64_to_float32, 1) static uint64_t do_fri(CPUPPCState *env, uint64_t arg, FloatRoundMode rounding_mode) @@ -613,22 +613,22 @@ static uint64_t do_fri(CPUPPCState *env, uint64_t arg, return arg; } -uint64_t helper_frin(CPUPPCState *env, uint64_t arg) +uint64_t helper_FRIN(CPUPPCState *env, uint64_t arg) { return do_fri(env, arg, float_round_ties_away); } -uint64_t helper_friz(CPUPPCState *env, uint64_t arg) +uint64_t helper_FRIZ(CPUPPCState *env, uint64_t arg) { return do_fri(env, arg, float_round_to_zero); } -uint64_t helper_frip(CPUPPCState *env, uint64_t arg) +uint64_t helper_FRIP(CPUPPCState *env, uint64_t arg) { return do_fri(env, arg, float_round_up); } -uint64_t helper_frim(CPUPPCState *env, uint64_t arg) +uint64_t helper_FRIM(CPUPPCState *env, uint64_t arg) { return do_fri(env, arg, float_round_down); } @@ -697,7 +697,7 @@ static uint64_t do_frsp(CPUPPCState *env, uint64_t arg, uintptr_t retaddr) return helper_todouble(f32); } -uint64_t helper_frsp(CPUPPCState *env, uint64_t arg) +uint64_t helper_FRSP(CPUPPCState *env, uint64_t arg) { return do_frsp(env, arg, GETPC()); } diff --git a/target/ppc/helper.h b/target/ppc/helper.h index ca414f2f43d3a..96000f4f0d0ea 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -97,23 +97,23 @@ DEF_HELPER_FLAGS_1(tosingle, TCG_CALL_NO_RWG_SE, i32, i64) DEF_HELPER_4(fcmpo, void, env, i64, i64, i32) DEF_HELPER_4(fcmpu, void, env, i64, i64, i32) -DEF_HELPER_2(fctiw, i64, env, i64) -DEF_HELPER_2(fctiwu, i64, env, i64) -DEF_HELPER_2(fctiwz, i64, env, i64) -DEF_HELPER_2(fctiwuz, i64, env, i64) -DEF_HELPER_2(fcfid, i64, env, i64) -DEF_HELPER_2(fcfidu, i64, env, i64) -DEF_HELPER_2(fcfids, i64, env, i64) -DEF_HELPER_2(fcfidus, i64, env, i64) -DEF_HELPER_2(fctid, i64, env, i64) -DEF_HELPER_2(fctidu, i64, env, i64) -DEF_HELPER_2(fctidz, i64, env, i64) -DEF_HELPER_2(fctiduz, i64, env, i64) -DEF_HELPER_2(frsp, i64, env, i64) -DEF_HELPER_2(frin, i64, env, i64) -DEF_HELPER_2(friz, i64, env, i64) -DEF_HELPER_2(frip, i64, env, i64) -DEF_HELPER_2(frim, i64, env, i64) +DEF_HELPER_2(FCTIW, i64, env, i64) +DEF_HELPER_2(FCTIWU, i64, env, i64) +DEF_HELPER_2(FCTIWZ, i64, env, i64) +DEF_HELPER_2(FCTIWUZ, i64, env, i64) +DEF_HELPER_2(FCFID, i64, env, i64) +DEF_HELPER_2(FCFIDU, i64, env, i64) +DEF_HELPER_2(FCFIDS, i64, env, i64) +DEF_HELPER_2(FCFIDUS, i64, env, i64) +DEF_HELPER_2(FCTID, i64, env, i64) +DEF_HELPER_2(FCTIDU, i64, env, i64) +DEF_HELPER_2(FCTIDZ, i64, env, i64) +DEF_HELPER_2(FCTIDUZ, i64, env, i64) +DEF_HELPER_2(FRSP, i64, env, i64) +DEF_HELPER_2(FRIN, i64, env, i64) +DEF_HELPER_2(FRIZ, i64, env, i64) +DEF_HELPER_2(FRIP, i64, env, i64) +DEF_HELPER_2(FRIM, i64, env, i64) DEF_HELPER_3(FADD, f64, env, f64, f64) DEF_HELPER_3(FADDS, f64, env, f64, f64) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 16652b5c13066..0c7472d92934c 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -568,6 +568,30 @@ FNMADDS 111011 ..... ..... ..... ..... 11111 . @A FNMSUB 111111 ..... ..... ..... ..... 11110 . @A FNMSUBS 111011 ..... ..... ..... ..... 11110 . @A +### Floating-Point Rounding and Conversion Instructions + +FRSP 111111 ..... ----- ..... 0000001100 . @X_tb_rc + +FRIN 111111 ..... ----- ..... 0110001000 . @X_tb_rc +FRIZ 111111 ..... ----- ..... 0110101000 . @X_tb_rc +FRIP 111111 ..... ----- ..... 0111001000 . @X_tb_rc +FRIM 111111 ..... ----- ..... 0111101000 . @X_tb_rc + +FCTIW 111111 ..... ----- ..... 0000001110 . @X_tb_rc +FCTIWU 111111 ..... ----- ..... 0010001110 . @X_tb_rc +FCTIWZ 111111 ..... ----- ..... 0000001111 . @X_tb_rc +FCTIWUZ 111111 ..... ----- ..... 0010001111 . @X_tb_rc + +FCTID 111111 ..... ----- ..... 1100101110 . @X_tb_rc +FCTIDU 111111 ..... ----- ..... 1110101110 . @X_tb_rc +FCTIDZ 111111 ..... ----- ..... 1100101111 . @X_tb_rc +FCTIDUZ 111111 ..... ----- ..... 1110101111 . @X_tb_rc + +FCFID 111111 ..... ----- ..... 1101001110 . @X_tb_rc +FCFIDS 111011 ..... ----- ..... 1101001110 . @X_tb_rc +FCFIDU 111111 ..... ----- ..... 1111001110 . @X_tb_rc +FCFIDUS 111011 ..... ----- ..... 1111001110 . @X_tb_rc + ### Floating-Point Select Instruction FSEL 111111 ..... ..... ..... ..... 10111 . @A diff --git a/target/ppc/translate/fp-impl.c.inc b/target/ppc/translate/fp-impl.c.inc index a66b83398b6ca..f296cfcdb0f34 100644 --- a/target/ppc/translate/fp-impl.c.inc +++ b/target/ppc/translate/fp-impl.c.inc @@ -98,28 +98,26 @@ static bool do_helper_ac(DisasContext *ctx, arg_A_tac *a, return true; } -#define GEN_FLOAT_B(name, op2, op3, set_fprf, type) \ -static void gen_f##name(DisasContext *ctx) \ -{ \ - TCGv_i64 t0; \ - TCGv_i64 t1; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - t1 = tcg_temp_new_i64(); \ - gen_reset_fpstatus(); \ - get_fpr(t0, rB(ctx->opcode)); \ - gen_helper_f##name(t1, tcg_env, t0); \ - set_fpr(rD(ctx->opcode), t1); \ - if (set_fprf) { \ - gen_helper_compute_fprf_float64(tcg_env, t1); \ - } \ - gen_helper_float_check_status(tcg_env); \ - if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr1_from_fpscr(ctx); \ - } \ +static bool do_round_convert(DisasContext *ctx, arg_X_tb_rc *a, + void (*helper)(TCGv_i64, TCGv_env, TCGv_i64), + bool set_fprf) +{ + TCGv_i64 t0, t1; + REQUIRE_FPU(ctx); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + gen_reset_fpstatus(); + get_fpr(t0, a->rb); + helper(t1, tcg_env, t0); + set_fpr(a->rt, t1); + if (set_fprf) { + gen_helper_compute_fprf_float64(tcg_env, t1); + } + gen_helper_float_check_status(tcg_env); + if (unlikely(a->rc)) { + gen_set_cr1_from_fpscr(ctx); + } + return true; } static bool do_helper_bs(DisasContext *ctx, arg_A_tb *a, @@ -213,41 +211,26 @@ TRANS(FSQRT, do_helper_fsqrt, gen_helper_FSQRT); TRANS(FSQRTS, do_helper_fsqrt, gen_helper_FSQRTS); /*** Floating-Point round & convert ***/ -/* fctiw */ -GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT); -/* fctiwu */ -GEN_FLOAT_B(ctiwu, 0x0E, 0x04, 0, PPC2_FP_CVT_ISA206); -/* fctiwz */ -GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT); -/* fctiwuz */ -GEN_FLOAT_B(ctiwuz, 0x0F, 0x04, 0, PPC2_FP_CVT_ISA206); -/* frsp */ -GEN_FLOAT_B(rsp, 0x0C, 0x00, 1, PPC_FLOAT); -/* fcfid */ -GEN_FLOAT_B(cfid, 0x0E, 0x1A, 1, PPC2_FP_CVT_S64); -/* fcfids */ -GEN_FLOAT_B(cfids, 0x0E, 0x1A, 0, PPC2_FP_CVT_ISA206); -/* fcfidu */ -GEN_FLOAT_B(cfidu, 0x0E, 0x1E, 0, PPC2_FP_CVT_ISA206); -/* fcfidus */ -GEN_FLOAT_B(cfidus, 0x0E, 0x1E, 0, PPC2_FP_CVT_ISA206); -/* fctid */ -GEN_FLOAT_B(ctid, 0x0E, 0x19, 0, PPC2_FP_CVT_S64); -/* fctidu */ -GEN_FLOAT_B(ctidu, 0x0E, 0x1D, 0, PPC2_FP_CVT_ISA206); -/* fctidz */ -GEN_FLOAT_B(ctidz, 0x0F, 0x19, 0, PPC2_FP_CVT_S64); -/* fctidu */ -GEN_FLOAT_B(ctiduz, 0x0F, 0x1D, 0, PPC2_FP_CVT_ISA206); - -/* frin */ -GEN_FLOAT_B(rin, 0x08, 0x0C, 1, PPC_FLOAT_EXT); -/* friz */ -GEN_FLOAT_B(riz, 0x08, 0x0D, 1, PPC_FLOAT_EXT); -/* frip */ -GEN_FLOAT_B(rip, 0x08, 0x0E, 1, PPC_FLOAT_EXT); -/* frim */ -GEN_FLOAT_B(rim, 0x08, 0x0F, 1, PPC_FLOAT_EXT); +TRANS_FLAGS(FLOAT, FRSP, do_round_convert, gen_helper_FRSP, true); +TRANS_FLAGS(FLOAT_EXT, FRIN, do_round_convert, gen_helper_FRIN, true); +TRANS_FLAGS(FLOAT_EXT, FRIZ, do_round_convert, gen_helper_FRIZ, true); +TRANS_FLAGS(FLOAT_EXT, FRIP, do_round_convert, gen_helper_FRIP, true); +TRANS_FLAGS(FLOAT_EXT, FRIM, do_round_convert, gen_helper_FRIM, true); + +TRANS_FLAGS(FLOAT, FCTIW, do_round_convert, gen_helper_FCTIW, false); +TRANS_FLAGS2(FP_CVT_ISA206, FCTIWU, do_round_convert, gen_helper_FCTIWU, false); +TRANS_FLAGS(FLOAT, FCTIWZ, do_round_convert, gen_helper_FCTIWZ, false); +TRANS_FLAGS2(FP_CVT_ISA206, FCTIWUZ, do_round_convert, gen_helper_FCTIWUZ, false); + +TRANS_FLAGS2(FP_CVT_S64, FCTID, do_round_convert, gen_helper_FCTID, false); +TRANS_FLAGS2(FP_CVT_ISA206, FCTIDU, do_round_convert, gen_helper_FCTIDU, false); +TRANS_FLAGS2(FP_CVT_S64, FCTIDZ, do_round_convert, gen_helper_FCTIDZ, false); +TRANS_FLAGS2(FP_CVT_ISA206, FCTIDUZ, do_round_convert, gen_helper_FCTIDUZ, false); + +TRANS_FLAGS2(FP_CVT_S64, FCFID, do_round_convert, gen_helper_FCFID, true); +TRANS_FLAGS2(FP_CVT_ISA206, FCFIDS, do_round_convert, gen_helper_FCFIDS, false); +TRANS_FLAGS2(FP_CVT_ISA206, FCFIDU, do_round_convert, gen_helper_FCFIDU, false); +TRANS_FLAGS2(FP_CVT_ISA206, FCFIDUS, do_round_convert, gen_helper_FCFIDUS, false); static bool trans_FTDIV(DisasContext *ctx, arg_X_bf *a) { @@ -1051,8 +1034,6 @@ TRANS(STFDX, do_lsfp_X, false, true, false) TRANS(STFDUX, do_lsfp_X, true, true, false) TRANS(PSTFD, do_lsfp_PLS_D, false, true, false) -#undef GEN_FLOAT_B - #undef GEN_LDF #undef GEN_LDUF #undef GEN_LDUXF diff --git a/target/ppc/translate/fp-ops.c.inc b/target/ppc/translate/fp-ops.c.inc index cef4b5dfcbe6d..acb8ac32da4fc 100644 --- a/target/ppc/translate/fp-ops.c.inc +++ b/target/ppc/translate/fp-ops.c.inc @@ -1,24 +1,3 @@ -#define GEN_FLOAT_B(name, op2, op3, set_fprf, type) \ -GEN_HANDLER(f##name, 0x3F, op2, op3, 0x001F0000, type) - -GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT), -GEN_HANDLER_E(fctiwu, 0x3F, 0x0E, 0x04, 0, PPC_NONE, PPC2_FP_CVT_ISA206), -GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT), -GEN_HANDLER_E(fctiwuz, 0x3F, 0x0F, 0x04, 0, PPC_NONE, PPC2_FP_CVT_ISA206), -GEN_FLOAT_B(rsp, 0x0C, 0x00, 1, PPC_FLOAT), -GEN_HANDLER_E(fcfid, 0x3F, 0x0E, 0x1A, 0x001F0000, PPC_NONE, PPC2_FP_CVT_S64), -GEN_HANDLER_E(fcfids, 0x3B, 0x0E, 0x1A, 0, PPC_NONE, PPC2_FP_CVT_ISA206), -GEN_HANDLER_E(fcfidu, 0x3F, 0x0E, 0x1E, 0, PPC_NONE, PPC2_FP_CVT_ISA206), -GEN_HANDLER_E(fcfidus, 0x3B, 0x0E, 0x1E, 0, PPC_NONE, PPC2_FP_CVT_ISA206), -GEN_HANDLER_E(fctid, 0x3F, 0x0E, 0x19, 0x001F0000, PPC_NONE, PPC2_FP_CVT_S64), -GEN_HANDLER_E(fctidu, 0x3F, 0x0E, 0x1D, 0, PPC_NONE, PPC2_FP_CVT_ISA206), -GEN_HANDLER_E(fctidz, 0x3F, 0x0F, 0x19, 0x001F0000, PPC_NONE, PPC2_FP_CVT_S64), -GEN_HANDLER_E(fctiduz, 0x3F, 0x0F, 0x1D, 0, PPC_NONE, PPC2_FP_CVT_ISA206), -GEN_FLOAT_B(rin, 0x08, 0x0C, 1, PPC_FLOAT_EXT), -GEN_FLOAT_B(riz, 0x08, 0x0D, 1, PPC_FLOAT_EXT), -GEN_FLOAT_B(rip, 0x08, 0x0E, 1, PPC_FLOAT_EXT), -GEN_FLOAT_B(rim, 0x08, 0x0F, 1, PPC_FLOAT_EXT), - GEN_HANDLER_E(lfdepx, 0x1F, 0x1F, 0x12, 0x00000001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER_E(lfiwax, 0x1f, 0x17, 0x1a, 0x00000001, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(lfiwzx, 0x1f, 0x17, 0x1b, 0x1, PPC_NONE, PPC2_FP_CVT_ISA206), From f22aae5d8a2d8de807d6fa77a1901f815207c627 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Thu, 19 Jun 2025 15:28:37 +0530 Subject: [PATCH 1002/1794] target/ppc: Move floating-point compare instructions to decodetree. Move below instructions to decodetree specification : fcmp{u, o} : X-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Signed-off-by: Chinmay Rath Reviewed-by: Richard Henderson Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250619095840.369351-3-rathc@linux.ibm.com Message-ID: <20250619095840.369351-3-rathc@linux.ibm.com> --- target/ppc/fpu_helper.c | 4 +-- target/ppc/helper.h | 4 +-- target/ppc/insn32.decode | 5 ++++ target/ppc/translate/fp-impl.c.inc | 45 +++++++++--------------------- target/ppc/translate/fp-ops.c.inc | 2 -- 5 files changed, 22 insertions(+), 38 deletions(-) diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 503cbd98adec7..850aca6ed13e7 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -871,7 +871,7 @@ uint32_t helper_FTSQRT(uint64_t frb) return 0x8 | (fg_flag ? 4 : 0) | (fe_flag ? 2 : 0); } -void helper_fcmpu(CPUPPCState *env, uint64_t arg1, uint64_t arg2, +void helper_FCMPU(CPUPPCState *env, uint64_t arg1, uint64_t arg2, uint32_t crfD) { CPU_DoubleU farg1, farg2; @@ -902,7 +902,7 @@ void helper_fcmpu(CPUPPCState *env, uint64_t arg1, uint64_t arg2, } } -void helper_fcmpo(CPUPPCState *env, uint64_t arg1, uint64_t arg2, +void helper_FCMPO(CPUPPCState *env, uint64_t arg1, uint64_t arg2, uint32_t crfD) { CPU_DoubleU farg1, farg2; diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 96000f4f0d0ea..e99c8c824b452 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -94,8 +94,8 @@ DEF_HELPER_2(fpscr_setbit, void, env, i32) DEF_HELPER_FLAGS_1(todouble, TCG_CALL_NO_RWG_SE, i64, i32) DEF_HELPER_FLAGS_1(tosingle, TCG_CALL_NO_RWG_SE, i32, i64) -DEF_HELPER_4(fcmpo, void, env, i64, i64, i32) -DEF_HELPER_4(fcmpu, void, env, i64, i64, i32) +DEF_HELPER_4(FCMPO, void, env, i64, i64, i32) +DEF_HELPER_4(FCMPU, void, env, i64, i64, i32) DEF_HELPER_2(FCTIW, i64, env, i64) DEF_HELPER_2(FCTIWU, i64, env, i64) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 0c7472d92934c..d446ec534df1d 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -592,6 +592,11 @@ FCFIDS 111011 ..... ----- ..... 1101001110 . @X_tb_rc FCFIDU 111111 ..... ----- ..... 1111001110 . @X_tb_rc FCFIDUS 111011 ..... ----- ..... 1111001110 . @X_tb_rc +### Floating-Point Compare Instructions + +FCMPU 111111 ... -- ..... ..... 0000000000 - @X_bf +FCMPO 111111 ... -- ..... ..... 0000100000 - @X_bf + ### Floating-Point Select Instruction FSEL 111111 ..... ..... ..... ..... 10111 . @A diff --git a/target/ppc/translate/fp-impl.c.inc b/target/ppc/translate/fp-impl.c.inc index f296cfcdb0f34..4e18d350c0dc2 100644 --- a/target/ppc/translate/fp-impl.c.inc +++ b/target/ppc/translate/fp-impl.c.inc @@ -257,46 +257,27 @@ static bool trans_FTSQRT(DisasContext *ctx, arg_X_bf_b *a) } /*** Floating-Point compare ***/ - -/* fcmpo */ -static void gen_fcmpo(DisasContext *ctx) +static bool do_helper_cmp(DisasContext *ctx, arg_X_bf *a, + void (*helper)(TCGv_env, TCGv_i64, TCGv_i64, + TCGv_i32)) { TCGv_i32 crf; - TCGv_i64 t0; - TCGv_i64 t1; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + TCGv_i64 t0, t1; + REQUIRE_INSNS_FLAGS(ctx, FLOAT); + REQUIRE_FPU(ctx); t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); gen_reset_fpstatus(); - crf = tcg_constant_i32(crfD(ctx->opcode)); - get_fpr(t0, rA(ctx->opcode)); - get_fpr(t1, rB(ctx->opcode)); - gen_helper_fcmpo(tcg_env, t0, t1, crf); + crf = tcg_constant_i32(a->bf); + get_fpr(t0, a->ra); + get_fpr(t1, a->rb); + helper(tcg_env, t0, t1, crf); gen_helper_float_check_status(tcg_env); + return true; } -/* fcmpu */ -static void gen_fcmpu(DisasContext *ctx) -{ - TCGv_i32 crf; - TCGv_i64 t0; - TCGv_i64 t1; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - gen_reset_fpstatus(); - crf = tcg_constant_i32(crfD(ctx->opcode)); - get_fpr(t0, rA(ctx->opcode)); - get_fpr(t1, rB(ctx->opcode)); - gen_helper_fcmpu(tcg_env, t0, t1, crf); - gen_helper_float_check_status(tcg_env); -} +TRANS(FCMPU, do_helper_cmp, gen_helper_FCMPU); +TRANS(FCMPO, do_helper_cmp, gen_helper_FCMPO); /*** Floating-point move ***/ /* fabs */ diff --git a/target/ppc/translate/fp-ops.c.inc b/target/ppc/translate/fp-ops.c.inc index acb8ac32da4fc..502453da3529f 100644 --- a/target/ppc/translate/fp-ops.c.inc +++ b/target/ppc/translate/fp-ops.c.inc @@ -10,8 +10,6 @@ GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX) GEN_HANDLER_E(stfdepx, 0x1F, 0x1F, 0x16, 0x00000001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER_E(stfdpx, 0x1F, 0x17, 0x1C, 0x00200001, PPC_NONE, PPC2_ISA205), -GEN_HANDLER(fcmpo, 0x3F, 0x00, 0x01, 0x00600001, PPC_FLOAT), -GEN_HANDLER(fcmpu, 0x3F, 0x00, 0x00, 0x00600001, PPC_FLOAT), GEN_HANDLER(fabs, 0x3F, 0x08, 0x08, 0x001F0000, PPC_FLOAT), GEN_HANDLER(fmr, 0x3F, 0x08, 0x02, 0x001F0000, PPC_FLOAT), GEN_HANDLER(fnabs, 0x3F, 0x08, 0x04, 0x001F0000, PPC_FLOAT), From 5c89a201a92f49c8946727f451014ec05587645b Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Thu, 19 Jun 2025 15:28:38 +0530 Subject: [PATCH 1003/1794] target/ppc: Move floating-point move instructions to decodetree. Move below instructions to decodetree specification: f{mr, neg, abs, nabs} : X-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Signed-off-by: Chinmay Rath Reviewed-by: Richard Henderson Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250619095840.369351-4-rathc@linux.ibm.com Message-ID: <20250619095840.369351-4-rathc@linux.ibm.com> --- target/ppc/insn32.decode | 7 +++ target/ppc/translate/fp-impl.c.inc | 80 ++++++++---------------------- target/ppc/translate/fp-ops.c.inc | 4 -- 3 files changed, 28 insertions(+), 63 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index d446ec534df1d..063d5726cb553 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -530,6 +530,13 @@ STFDU 110111 ..... ...... ............... @D STFDX 011111 ..... ...... .... 1011010111 - @X STFDUX 011111 ..... ...... .... 1011110111 - @X +### Floating-Point Move Instructions + +FMR 111111 ..... ----- ..... 0001001000 . @X_tb_rc +FNEG 111111 ..... ----- ..... 0000101000 . @X_tb_rc +FABS 111111 ..... ----- ..... 0100001000 . @X_tb_rc +FNABS 111111 ..... ----- ..... 0010001000 . @X_tb_rc + ### Floating-Point Arithmetic Instructions FADD 111111 ..... ..... ..... ----- 10101 . @A_tab diff --git a/target/ppc/translate/fp-impl.c.inc b/target/ppc/translate/fp-impl.c.inc index 4e18d350c0dc2..2843f7112200c 100644 --- a/target/ppc/translate/fp-impl.c.inc +++ b/target/ppc/translate/fp-impl.c.inc @@ -280,82 +280,44 @@ TRANS(FCMPU, do_helper_cmp, gen_helper_FCMPU); TRANS(FCMPO, do_helper_cmp, gen_helper_FCMPO); /*** Floating-point move ***/ -/* fabs */ -/* XXX: beware that fabs never checks for NaNs nor update FPSCR */ -static void gen_fabs(DisasContext *ctx) -{ - TCGv_i64 t0; - TCGv_i64 t1; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - get_fpr(t0, rB(ctx->opcode)); - tcg_gen_andi_i64(t1, t0, ~(1ULL << 63)); - set_fpr(rD(ctx->opcode), t1); - if (unlikely(Rc(ctx->opcode))) { - gen_set_cr1_from_fpscr(ctx); - } -} /* fmr - fmr. */ /* XXX: beware that fmr never checks for NaNs nor update FPSCR */ -static void gen_fmr(DisasContext *ctx) +static bool trans_FMR(DisasContext *ctx, arg_FMR *a) { TCGv_i64 t0; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + REQUIRE_INSNS_FLAGS(ctx, FLOAT); + REQUIRE_FPU(ctx); t0 = tcg_temp_new_i64(); - get_fpr(t0, rB(ctx->opcode)); - set_fpr(rD(ctx->opcode), t0); - if (unlikely(Rc(ctx->opcode))) { + get_fpr(t0, a->rb); + set_fpr(a->rt, t0); + if (unlikely(a->rc)) { gen_set_cr1_from_fpscr(ctx); } + return true; } -/* fnabs */ -/* XXX: beware that fnabs never checks for NaNs nor update FPSCR */ -static void gen_fnabs(DisasContext *ctx) +/* XXX: beware that f{neg, abs, nabs} never checks for NaNs nor update FPSCR */ +static bool do_move_b(DisasContext *ctx, arg_X_tb_rc *a, int64_t val, + void (*tcg_op)(TCGv_i64, TCGv_i64, int64_t)) { - TCGv_i64 t0; - TCGv_i64 t1; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + TCGv_i64 t0, t1; + REQUIRE_INSNS_FLAGS(ctx, FLOAT); + REQUIRE_FPU(ctx); t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); - get_fpr(t0, rB(ctx->opcode)); - tcg_gen_ori_i64(t1, t0, 1ULL << 63); - set_fpr(rD(ctx->opcode), t1); - if (unlikely(Rc(ctx->opcode))) { + get_fpr(t0, a->rb); + tcg_op(t1, t0, val); + set_fpr(a->rt, t1); + if (unlikely(a->rc)) { gen_set_cr1_from_fpscr(ctx); } + return true; } -/* fneg */ -/* XXX: beware that fneg never checks for NaNs nor update FPSCR */ -static void gen_fneg(DisasContext *ctx) -{ - TCGv_i64 t0; - TCGv_i64 t1; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - get_fpr(t0, rB(ctx->opcode)); - tcg_gen_xori_i64(t1, t0, 1ULL << 63); - set_fpr(rD(ctx->opcode), t1); - if (unlikely(Rc(ctx->opcode))) { - gen_set_cr1_from_fpscr(ctx); - } -} +TRANS(FNEG, do_move_b, 1ULL << 63, tcg_gen_xori_i64); +TRANS(FABS, do_move_b, ~(1ULL << 63), tcg_gen_andi_i64); +TRANS(FNABS, do_move_b, 1ULL << 63, tcg_gen_ori_i64); /* fcpsgn: PowerPC 2.05 specification */ /* XXX: beware that fcpsgn never checks for NaNs nor update FPSCR */ diff --git a/target/ppc/translate/fp-ops.c.inc b/target/ppc/translate/fp-ops.c.inc index 502453da3529f..5053cb135ce09 100644 --- a/target/ppc/translate/fp-ops.c.inc +++ b/target/ppc/translate/fp-ops.c.inc @@ -10,10 +10,6 @@ GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX) GEN_HANDLER_E(stfdepx, 0x1F, 0x1F, 0x16, 0x00000001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER_E(stfdpx, 0x1F, 0x17, 0x1C, 0x00200001, PPC_NONE, PPC2_ISA205), -GEN_HANDLER(fabs, 0x3F, 0x08, 0x08, 0x001F0000, PPC_FLOAT), -GEN_HANDLER(fmr, 0x3F, 0x08, 0x02, 0x001F0000, PPC_FLOAT), -GEN_HANDLER(fnabs, 0x3F, 0x08, 0x04, 0x001F0000, PPC_FLOAT), -GEN_HANDLER(fneg, 0x3F, 0x08, 0x01, 0x001F0000, PPC_FLOAT), GEN_HANDLER_E(fcpsgn, 0x3F, 0x08, 0x00, 0x00000000, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(fmrgew, 0x3F, 0x06, 0x1E, 0x00000001, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(fmrgow, 0x3F, 0x06, 0x1A, 0x00000001, PPC_NONE, PPC2_VSX207), From 241f6f7994a3b448d9471e43d8e31ed6245b47c7 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Thu, 19 Jun 2025 15:28:39 +0530 Subject: [PATCH 1004/1794] target/ppc: Move remaining floating-point move instructions to decodetree. Move below instructions to decodetree specification: fcpsgn, fmrg{e, o}w : X-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Signed-off-by: Chinmay Rath Reviewed-by: Richard Henderson Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250619095840.369351-5-rathc@linux.ibm.com Message-ID: <20250619095840.369351-5-rathc@linux.ibm.com> --- target/ppc/insn32.decode | 4 ++ target/ppc/translate/fp-impl.c.inc | 65 +++++++++++++----------------- target/ppc/translate/fp-ops.c.inc | 3 -- 3 files changed, 32 insertions(+), 40 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 063d5726cb553..0e9c68f2fb407 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -537,6 +537,10 @@ FNEG 111111 ..... ----- ..... 0000101000 . @X_tb_rc FABS 111111 ..... ----- ..... 0100001000 . @X_tb_rc FNABS 111111 ..... ----- ..... 0010001000 . @X_tb_rc +FCPSGN 111111 ..... ..... ..... 0000001000 . @X_rc +FMRGEW 111111 ..... ..... ..... 1111000110 - @X +FMRGOW 111111 ..... ..... ..... 1101000110 - @X + ### Floating-Point Arithmetic Instructions FADD 111111 ..... ..... ..... ----- 10101 . @A_tab diff --git a/target/ppc/translate/fp-impl.c.inc b/target/ppc/translate/fp-impl.c.inc index 2843f7112200c..28dda15040088 100644 --- a/target/ppc/translate/fp-impl.c.inc +++ b/target/ppc/translate/fp-impl.c.inc @@ -321,62 +321,53 @@ TRANS(FNABS, do_move_b, 1ULL << 63, tcg_gen_ori_i64); /* fcpsgn: PowerPC 2.05 specification */ /* XXX: beware that fcpsgn never checks for NaNs nor update FPSCR */ -static void gen_fcpsgn(DisasContext *ctx) +static bool trans_FCPSGN(DisasContext *ctx, arg_FCPSGN *a) { - TCGv_i64 t0; - TCGv_i64 t1; - TCGv_i64 t2; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + TCGv_i64 t0, t1, t2; + REQUIRE_INSNS_FLAGS2(ctx, ISA205); + REQUIRE_FPU(ctx); t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); t2 = tcg_temp_new_i64(); - get_fpr(t0, rA(ctx->opcode)); - get_fpr(t1, rB(ctx->opcode)); + get_fpr(t0, a->ra); + get_fpr(t1, a->rb); tcg_gen_deposit_i64(t2, t0, t1, 0, 63); - set_fpr(rD(ctx->opcode), t2); - if (unlikely(Rc(ctx->opcode))) { + set_fpr(a->rt, t2); + if (unlikely(a->rc)) { gen_set_cr1_from_fpscr(ctx); } + return true; } -static void gen_fmrgew(DisasContext *ctx) +static bool trans_FMRGEW(DisasContext *ctx, arg_FMRGEW *a) { - TCGv_i64 b0; - TCGv_i64 t0; - TCGv_i64 t1; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } - b0 = tcg_temp_new_i64(); + TCGv_i64 t0, t1, t2; + REQUIRE_INSNS_FLAGS2(ctx, VSX207); + REQUIRE_FPU(ctx); t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); - get_fpr(t0, rB(ctx->opcode)); - tcg_gen_shri_i64(b0, t0, 32); - get_fpr(t0, rA(ctx->opcode)); - tcg_gen_deposit_i64(t1, t0, b0, 0, 32); - set_fpr(rD(ctx->opcode), t1); + t2 = tcg_temp_new_i64(); + get_fpr(t1, a->rb); + tcg_gen_shri_i64(t0, t1, 32); + get_fpr(t1, a->ra); + tcg_gen_deposit_i64(t2, t1, t0, 0, 32); + set_fpr(a->rt, t2); + return true; } -static void gen_fmrgow(DisasContext *ctx) +static bool trans_FMRGOW(DisasContext *ctx, arg_FMRGOW *a) { - TCGv_i64 t0; - TCGv_i64 t1; - TCGv_i64 t2; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + TCGv_i64 t0, t1, t2; + REQUIRE_INSNS_FLAGS2(ctx, VSX207); + REQUIRE_FPU(ctx); t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); t2 = tcg_temp_new_i64(); - get_fpr(t0, rB(ctx->opcode)); - get_fpr(t1, rA(ctx->opcode)); + get_fpr(t0, a->rb); + get_fpr(t1, a->ra); tcg_gen_deposit_i64(t2, t0, t1, 32, 32); - set_fpr(rD(ctx->opcode), t2); + set_fpr(a->rt, t2); + return true; } /*** Floating-Point status & ctrl register ***/ diff --git a/target/ppc/translate/fp-ops.c.inc b/target/ppc/translate/fp-ops.c.inc index 5053cb135ce09..9bc9c3a3c3ffb 100644 --- a/target/ppc/translate/fp-ops.c.inc +++ b/target/ppc/translate/fp-ops.c.inc @@ -10,9 +10,6 @@ GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX) GEN_HANDLER_E(stfdepx, 0x1F, 0x1F, 0x16, 0x00000001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER_E(stfdpx, 0x1F, 0x17, 0x1C, 0x00200001, PPC_NONE, PPC2_ISA205), -GEN_HANDLER_E(fcpsgn, 0x3F, 0x08, 0x00, 0x00000000, PPC_NONE, PPC2_ISA205), -GEN_HANDLER_E(fmrgew, 0x3F, 0x06, 0x1E, 0x00000001, PPC_NONE, PPC2_VSX207), -GEN_HANDLER_E(fmrgow, 0x3F, 0x06, 0x1A, 0x00000001, PPC_NONE, PPC2_VSX207), GEN_HANDLER(mcrfs, 0x3F, 0x00, 0x02, 0x0063F801, PPC_FLOAT), GEN_HANDLER(mtfsb0, 0x3F, 0x06, 0x02, 0x001FF800, PPC_FLOAT), GEN_HANDLER(mtfsb1, 0x3F, 0x06, 0x01, 0x001FF800, PPC_FLOAT), From f19052110e32afd94f897ef069735bbd7172a800 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Sat, 7 Jun 2025 16:34:11 +0530 Subject: [PATCH 1005/1794] target/ppc: Introduce macro for deprecating PowerPC CPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU has a way to deprecate CPUs by setting the 'deprecation_note' in CPUClass. Currently PowerPC CPUs don't use this deprecation process. Introduce 'POWERPC_DEPRECATED_CPU' macro to deprecate particular PowerPC CPUs in future. With the change, QEMU will print a warning like below when the deprecated CPU/Chips are used (example output if power8nvl is deprecated): $ ./build/qemu-system-ppc64 -M powernv8 --cpu power8nvl -nographic qemu-system-ppc64: warning: CPU model power8nvl_v1.0-powerpc64-cpu is deprecated -- CPU is unmaintained. ... Also, print '(deprecated)' for deprecated CPUs in 'qemu-system-ppc64 --cpu ?' (example output if power8nvl is deprecated): $ ./build/qemu-system-ppc64 --cpu help ... power8e (alias for power8e_v2.1) power8nvl_v1.0 PVR 004c0100 (deprecated) power8nvl (alias for power8nvl_v1.0) power8_v2.0 PVR 004d0200 ... Suggested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Aditya Gupta Tested-by: Anushree Mathur Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250607110412.2342511-2-adityag@linux.ibm.com Message-ID: <20250607110412.2342511-2-adityag@linux.ibm.com> --- target/ppc/cpu-models.c | 12 +++++++++++- target/ppc/cpu_init.c | 7 ++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c index 09f73e23a8909..334b1d2ff39a7 100644 --- a/target/ppc/cpu-models.c +++ b/target/ppc/cpu-models.c @@ -32,17 +32,20 @@ /* PowerPC CPU definitions */ #define POWERPC_DEF_PREFIX(pvr, svr, type) \ glue(glue(glue(glue(pvr, _), svr), _), type) -#define POWERPC_DEF_SVR(_name, _desc, _pvr, _svr, _type) \ +#define POWERPC_DEF_SVR_DEPR(_name, _desc, _pvr, _svr, _type, _deprecation_note) \ static void \ glue(POWERPC_DEF_PREFIX(_pvr, _svr, _type), _cpu_class_init) \ (ObjectClass *oc, const void *data) \ { \ DeviceClass *dc = DEVICE_CLASS(oc); \ + CPUClass *cc = CPU_CLASS(oc); \ PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); \ \ pcc->pvr = _pvr; \ pcc->svr = _svr; \ dc->desc = _desc; \ + \ + cc->deprecation_note = _deprecation_note; \ } \ \ static const TypeInfo \ @@ -63,6 +66,13 @@ type_init( \ glue(POWERPC_DEF_PREFIX(_pvr, _svr, _type), _cpu_register_types)) +#define POWERPC_DEF_SVR(_name, _desc, _pvr, _svr, _type) \ + POWERPC_DEF_SVR_DEPR(_name, _desc, _pvr, _svr, _type, NULL) + +#define POWERPC_DEPRECATED_CPU(_name, _pvr, _type, _desc, _deprecation_note)\ + POWERPC_DEF_SVR_DEPR(_name, _desc, _pvr, POWERPC_SVR_NONE, _type, \ + _deprecation_note) + #define POWERPC_DEF(_name, _pvr, _type, _desc) \ POWERPC_DEF_SVR(_name, _desc, _pvr, POWERPC_SVR_NONE, _type) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 9e4ea8fd13bec..3aa3aefc1366e 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7298,6 +7298,7 @@ static void ppc_cpu_list_entry(gpointer data, gpointer user_data) { ObjectClass *oc = data; PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); DeviceClass *family = DEVICE_CLASS(ppc_cpu_get_family_class(pcc)); const char *typename = object_class_get_name(oc); char *name; @@ -7308,7 +7309,11 @@ static void ppc_cpu_list_entry(gpointer data, gpointer user_data) } name = cpu_model_from_type(typename); - qemu_printf(" %-16s PVR %08x\n", name, pcc->pvr); + if (cc->deprecation_note) { + qemu_printf(" %-16s PVR %08x (deprecated)\n", name, pcc->pvr); + } else { + qemu_printf(" %-16s PVR %08x\n", name, pcc->pvr); + } for (i = 0; ppc_cpu_aliases[i].alias != NULL; i++) { PowerPCCPUAlias *alias = &ppc_cpu_aliases[i]; ObjectClass *alias_oc = ppc_cpu_class_by_name(alias->model); From 264a604e71636bd04bfbbe3cf887259f246dccb3 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Sat, 7 Jun 2025 16:34:12 +0530 Subject: [PATCH 1006/1794] target/ppc: Deprecate Power8E and Power8NVL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Power8E and Power8NVL variants are not of much use in QEMU now, and not being maintained either. Power8NVL CPU doesn't boot since skiboot v7.0, or following skiboot commit to be exact: commit c5424f683ee3 ("Remove support for POWER8 DD1") Deprecate the 8E and 8NVL variants. Suggested-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Signed-off-by: Aditya Gupta Tested-by: Anushree Mathur Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250607110412.2342511-3-adityag@linux.ibm.com Message-ID: <20250607110412.2342511-3-adityag@linux.ibm.com> --- docs/about/deprecated.rst | 9 +++++++++ target/ppc/cpu-models.c | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index aa300bbd50735..b8d60c1a90bac 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -253,6 +253,15 @@ embedded 405 for power management (OCC) and other internal tasks, it is theoretically possible to use QEMU to model them. Let's keep the CPU implementation for a while before removing all support. +Power8E and Power8NVL CPUs and corresponding Pnv chips (since 10.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The Power8E and Power8NVL variants of Power8 are not really useful anymore +in qemu, and are old and unmaintained now. + +The CPUs as well as corresponding Power8NVL and Power8E PnvChips will also +be considered deprecated. + System emulator machines ------------------------ diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c index 334b1d2ff39a7..89ae763c7f6e2 100644 --- a/target/ppc/cpu-models.c +++ b/target/ppc/cpu-models.c @@ -739,12 +739,12 @@ "POWER7 v2.3") POWERPC_DEF("power7p_v2.1", CPU_POWERPC_POWER7P_v21, POWER7, "POWER7+ v2.1") - POWERPC_DEF("power8e_v2.1", CPU_POWERPC_POWER8E_v21, POWER8, - "POWER8E v2.1") + POWERPC_DEPRECATED_CPU("power8e_v2.1", CPU_POWERPC_POWER8E_v21, POWER8, + "POWER8E v2.1", "CPU is unmaintained.") POWERPC_DEF("power8_v2.0", CPU_POWERPC_POWER8_v20, POWER8, "POWER8 v2.0") - POWERPC_DEF("power8nvl_v1.0", CPU_POWERPC_POWER8NVL_v10, POWER8, - "POWER8NVL v1.0") + POWERPC_DEPRECATED_CPU("power8nvl_v1.0", CPU_POWERPC_POWER8NVL_v10, POWER8, + "POWER8NVL v1.0", "CPU is unmaintained.") POWERPC_DEF("power9_v2.0", CPU_POWERPC_POWER9_DD20, POWER9, "POWER9 v2.0") POWERPC_DEF("power9_v2.2", CPU_POWERPC_POWER9_DD22, POWER9, From 6c51df580d2a64b4e1ef7bdbffeb3615ffe25d43 Mon Sep 17 00:00:00 2001 From: Denis Sergeev Date: Mon, 15 Sep 2025 11:01:18 +0300 Subject: [PATCH 1007/1794] target/ppc: use MAKE_64BIT_MASK for mcrfs exception clear mask In gen_mcrfs() the FPSCR nibble mask is computed as: `~((0xF << shift) & FP_EX_CLEAR_BITS)` Here, 0xF is of type int, so the left shift is performed in 32-bit signed arithmetic. For bfa=0 we get shift=28, and (0xF << 28) = 0xF0000000, which is not representable as a 32-bit signed int. Static analyzers flag this as a potential integer overflow. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Denis Sergeev Reviewed-by: Chinmay Rath Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20250915080118.29898-1-zeff@altlinux.org Message-ID: <20250915080118.29898-1-zeff@altlinux.org> --- target/ppc/translate/fp-impl.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/translate/fp-impl.c.inc b/target/ppc/translate/fp-impl.c.inc index 28dda15040088..464fb1d90f738 100644 --- a/target/ppc/translate/fp-impl.c.inc +++ b/target/ppc/translate/fp-impl.c.inc @@ -396,7 +396,7 @@ static void gen_mcrfs(DisasContext *ctx) tcg_gen_extu_tl_i64(tnew_fpscr, cpu_fpscr); /* Only the exception bits (including FX) should be cleared if read */ tcg_gen_andi_i64(tnew_fpscr, tnew_fpscr, - ~((0xF << shift) & FP_EX_CLEAR_BITS)); + ~(MAKE_64BIT_MASK(shift, 4) & FP_EX_CLEAR_BITS)); /* FEX and VX need to be updated, so don't set fpscr directly */ tmask = tcg_constant_i32(1 << nibble); gen_helper_store_fpscr(tcg_env, tnew_fpscr, tmask); From 688a3dae7828ca5ee6f45d510eed083420d72d8a Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Tue, 12 Aug 2025 17:39:58 +0800 Subject: [PATCH 1008/1794] hw/nvram/aspeed_otp: Add ASPEED OTP memory device model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a QEMU device model for ASPEED's One-Time Programmable (OTP) memory. This model simulates a word-addressable OTP region used for secure fuse storage. The OTP memory can operate with an internal memory buffer. The OTP model provides a memory-like interface through a dedicated AddressSpace, allowing other device models (e.g., SBC) to issue transactions as if accessing a memory-mapped region. Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250812094011.2617526-2-kane_chen@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/nvram/aspeed_otp.c | 99 +++++++++++++++++++++++++++++++++++ hw/nvram/meson.build | 4 ++ include/hw/nvram/aspeed_otp.h | 33 ++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 hw/nvram/aspeed_otp.c create mode 100644 include/hw/nvram/aspeed_otp.h diff --git a/hw/nvram/aspeed_otp.c b/hw/nvram/aspeed_otp.c new file mode 100644 index 0000000000000..e5b7ca96767c8 --- /dev/null +++ b/hw/nvram/aspeed_otp.c @@ -0,0 +1,99 @@ +/* + * ASPEED OTP (One-Time Programmable) memory + * + * Copyright (C) 2025 Aspeed + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "system/block-backend.h" +#include "hw/qdev-properties.h" +#include "hw/nvram/aspeed_otp.h" + +static uint64_t aspeed_otp_read(void *opaque, hwaddr offset, unsigned size) +{ + AspeedOTPState *s = opaque; + uint64_t val = 0; + + memcpy(&val, s->storage + offset, size); + + return val; +} + +static void aspeed_otp_write(void *opaque, hwaddr otp_addr, + uint64_t val, unsigned size) +{ + AspeedOTPState *s = opaque; + + memcpy(s->storage + otp_addr, &val, size); +} + +static bool aspeed_otp_init_storage(AspeedOTPState *s, Error **errp) +{ + uint32_t *p; + int i, num; + + num = s->size / sizeof(uint32_t); + p = (uint32_t *)s->storage; + for (i = 0; i < num; i++) { + p[i] = (i % 2 == 0) ? 0x00000000 : 0xFFFFFFFF; + } + + return true; +} + +static const MemoryRegionOps aspeed_otp_ops = { + .read = aspeed_otp_read, + .write = aspeed_otp_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 4, +}; + +static void aspeed_otp_realize(DeviceState *dev, Error **errp) +{ + AspeedOTPState *s = ASPEED_OTP(dev); + + if (s->size == 0) { + error_setg(errp, "aspeed.otp: 'size' property must be set"); + return; + } + + s->storage = blk_blockalign(s->blk, s->size); + + if (!aspeed_otp_init_storage(s, errp)) { + return; + } + + memory_region_init_io(&s->mmio, OBJECT(dev), &aspeed_otp_ops, + s, "aspeed.otp", s->size); + address_space_init(&s->as, &s->mmio, NULL); +} + +static const Property aspeed_otp_properties[] = { + DEFINE_PROP_UINT64("size", AspeedOTPState, size, 0), +}; + +static void aspeed_otp_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = aspeed_otp_realize; + device_class_set_props(dc, aspeed_otp_properties); +} + +static const TypeInfo aspeed_otp_info = { + .name = TYPE_ASPEED_OTP, + .parent = TYPE_DEVICE, + .instance_size = sizeof(AspeedOTPState), + .class_init = aspeed_otp_class_init, +}; + +static void aspeed_otp_register_types(void) +{ + type_register_static(&aspeed_otp_info); +} + +type_init(aspeed_otp_register_types) diff --git a/hw/nvram/meson.build b/hw/nvram/meson.build index 10f3639db6aea..b66f23605b772 100644 --- a/hw/nvram/meson.build +++ b/hw/nvram/meson.build @@ -19,3 +19,7 @@ system_ss.add(when: 'CONFIG_XLNX_BBRAM', if_true: files('xlnx-bbram.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_nvram.c')) specific_ss.add(when: 'CONFIG_ACPI', if_true: files('fw_cfg-acpi.c')) + +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( + 'aspeed_otp.c', + )) \ No newline at end of file diff --git a/include/hw/nvram/aspeed_otp.h b/include/hw/nvram/aspeed_otp.h new file mode 100644 index 0000000000000..3752353860796 --- /dev/null +++ b/include/hw/nvram/aspeed_otp.h @@ -0,0 +1,33 @@ +/* + * ASPEED OTP (One-Time Programmable) memory + * + * Copyright (C) 2025 Aspeed + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ASPEED_OTP_H +#define ASPEED_OTP_H + +#include "system/memory.h" +#include "hw/block/block.h" +#include "system/address-spaces.h" + +#define TYPE_ASPEED_OTP "aspeed-otp" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedOTPState, ASPEED_OTP) + +typedef struct AspeedOTPState { + DeviceState parent_obj; + + BlockBackend *blk; + + uint64_t size; + + AddressSpace as; + + MemoryRegion mmio; + + uint8_t *storage; +} AspeedOTPState; + +#endif /* ASPEED_OTP_H */ From 9f58dd0a8c30a6b84016db30949fe2f86f8bc38b Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Tue, 12 Aug 2025 17:39:59 +0800 Subject: [PATCH 1009/1794] hw/misc/aspeed_sbc: Connect ASPEED OTP memory device to SBC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch connects the aspeed.otp device to the ASPEED Secure Boot Controller (SBC) model. It implements OTP memory access via the SBC's command interface and enables emulation of secure fuse programming flows. The following OTP commands are supported: - READ: reads a 32-bit word from OTP memory into internal registers - PROG: programs a 32-bit word value to the specified OTP address Trace events are added to observe read/program operations and command handling flow. Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250812094011.2617526-3-kane_chen@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_sbc.c | 111 +++++++++++++++++++++++++++++++++++ hw/misc/trace-events | 5 ++ include/hw/misc/aspeed_sbc.h | 5 ++ 3 files changed, 121 insertions(+) diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c index a7d101ba71f40..46a038337c314 100644 --- a/hw/misc/aspeed_sbc.c +++ b/hw/misc/aspeed_sbc.c @@ -15,9 +15,13 @@ #include "hw/misc/aspeed_sbc.h" #include "qapi/error.h" #include "migration/vmstate.h" +#include "trace.h" #define R_PROT (0x000 / 4) +#define R_CMD (0x004 / 4) +#define R_ADDR (0x010 / 4) #define R_STATUS (0x014 / 4) +#define R_CAMP1 (0x020 / 4) #define R_QSR (0x040 / 4) /* R_STATUS */ @@ -41,6 +45,11 @@ #define QSR_RSA_MASK (0x3 << 12) #define QSR_HASH_MASK (0x3 << 10) +#define OTP_MEMORY_SIZE 0x4000 +/* OTP command */ +#define SBC_OTP_CMD_READ 0x23b1e361 +#define SBC_OTP_CMD_PROG 0x23b1e364 + static uint64_t aspeed_sbc_read(void *opaque, hwaddr addr, unsigned int size) { AspeedSBCState *s = ASPEED_SBC(opaque); @@ -57,6 +66,84 @@ static uint64_t aspeed_sbc_read(void *opaque, hwaddr addr, unsigned int size) return s->regs[addr]; } +static bool aspeed_sbc_otp_read(AspeedSBCState *s, + uint32_t otp_addr) +{ + MemTxResult ret; + AspeedOTPState *otp = &s->otp; + uint32_t value, otp_offset; + + otp_offset = otp_addr << 2; + ret = address_space_read(&otp->as, otp_offset, MEMTXATTRS_UNSPECIFIED, + &value, sizeof(value)); + if (ret != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "Failed to read OTP memory, addr = %x\n", + otp_addr); + return false; + } + s->regs[R_CAMP1] = value; + trace_aspeed_sbc_otp_read(otp_addr, value); + + return true; +} + +static bool aspeed_sbc_otp_prog(AspeedSBCState *s, + uint32_t otp_addr) +{ + MemTxResult ret; + AspeedOTPState *otp = &s->otp; + uint32_t value = s->regs[R_CAMP1]; + + ret = address_space_write(&otp->as, otp_addr, MEMTXATTRS_UNSPECIFIED, + &value, sizeof(value)); + if (ret != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "Failed to write OTP memory, addr = %x\n", + otp_addr); + return false; + } + + trace_aspeed_sbc_otp_prog(otp_addr, value); + + return true; +} + +static void aspeed_sbc_handle_command(void *opaque, uint32_t cmd) +{ + AspeedSBCState *s = ASPEED_SBC(opaque); + AspeedSBCClass *sc = ASPEED_SBC_GET_CLASS(opaque); + bool ret = false; + uint32_t otp_addr; + + if (!sc->has_otp) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: OTP memory is not supported\n", + __func__); + return; + } + + s->regs[R_STATUS] &= ~(OTP_MEM_IDLE | OTP_IDLE); + otp_addr = s->regs[R_ADDR]; + + switch (cmd) { + case SBC_OTP_CMD_READ: + ret = aspeed_sbc_otp_read(s, otp_addr); + break; + case SBC_OTP_CMD_PROG: + ret = aspeed_sbc_otp_prog(s, otp_addr); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Unknown command 0x%x\n", + __func__, cmd); + break; + } + + trace_aspeed_sbc_handle_cmd(cmd, otp_addr, ret); + s->regs[R_STATUS] |= (OTP_MEM_IDLE | OTP_IDLE); +} + static void aspeed_sbc_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) { @@ -78,6 +165,9 @@ static void aspeed_sbc_write(void *opaque, hwaddr addr, uint64_t data, "%s: write to read only register 0x%" HWADDR_PRIx "\n", __func__, addr << 2); return; + case R_CMD: + aspeed_sbc_handle_command(opaque, data); + return; default: break; } @@ -115,10 +205,30 @@ static void aspeed_sbc_reset(DeviceState *dev) s->regs[R_QSR] = s->signing_settings; } +static void aspeed_sbc_instance_init(Object *obj) +{ + AspeedSBCClass *sc = ASPEED_SBC_GET_CLASS(obj); + AspeedSBCState *s = ASPEED_SBC(obj); + + if (sc->has_otp) { + object_initialize_child(OBJECT(s), "otp", &s->otp, + TYPE_ASPEED_OTP); + } +} + static void aspeed_sbc_realize(DeviceState *dev, Error **errp) { AspeedSBCState *s = ASPEED_SBC(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedSBCClass *sc = ASPEED_SBC_GET_CLASS(dev); + + if (sc->has_otp) { + object_property_set_int(OBJECT(&s->otp), "size", + OTP_MEMORY_SIZE, &error_abort); + if (!qdev_realize(DEVICE(&s->otp), NULL, errp)) { + return; + } + } memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sbc_ops, s, TYPE_ASPEED_SBC, 0x1000); @@ -155,6 +265,7 @@ static const TypeInfo aspeed_sbc_info = { .name = TYPE_ASPEED_SBC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(AspeedSBCState), + .instance_init = aspeed_sbc_instance_init, .class_init = aspeed_sbc_class_init, .class_size = sizeof(AspeedSBCClass) }; diff --git a/hw/misc/trace-events b/hw/misc/trace-events index e3f64c0ff6b0a..9e05b82f371ec 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -90,6 +90,11 @@ slavio_sysctrl_mem_readl(uint32_t ret) "Read system control 0x%08x" slavio_led_mem_writew(uint32_t val) "Write diagnostic LED 0x%04x" slavio_led_mem_readw(uint32_t ret) "Read diagnostic LED 0x%04x" +# aspeed_sbc.c +aspeed_sbc_handle_cmd(uint32_t cmd, uint32_t addr, bool ret) "Handling command 0x%" PRIx32 " for OTP addr 0x%" PRIx32 " Result: %d" +aspeed_sbc_otp_read(uint32_t addr, uint32_t value) "OTP Memory read: addr 0x%" PRIx32 " value 0x%" PRIx32 +aspeed_sbc_otp_prog(uint32_t addr, uint32_t value) "OTP Memory write: addr 0x%" PRIx32 " value 0x%" PRIx32 + # aspeed_scu.c aspeed_scu_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 aspeed_scu_read(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 diff --git a/include/hw/misc/aspeed_sbc.h b/include/hw/misc/aspeed_sbc.h index 405e6782b97a0..0c2746d392a47 100644 --- a/include/hw/misc/aspeed_sbc.h +++ b/include/hw/misc/aspeed_sbc.h @@ -10,6 +10,7 @@ #define ASPEED_SBC_H #include "hw/sysbus.h" +#include "hw/nvram/aspeed_otp.h" #define TYPE_ASPEED_SBC "aspeed.sbc" #define TYPE_ASPEED_AST2600_SBC TYPE_ASPEED_SBC "-ast2600" @@ -36,10 +37,14 @@ struct AspeedSBCState { MemoryRegion iomem; uint32_t regs[ASPEED_SBC_NR_REGS]; + + AspeedOTPState otp; }; struct AspeedSBCClass { SysBusDeviceClass parent_class; + + bool has_otp; }; #endif /* ASPEED_SBC_H */ From fe4159cb34f36a63a1759b907f51f8d458f5c940 Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Tue, 12 Aug 2025 17:40:00 +0800 Subject: [PATCH 1010/1794] hw/arm: Integrate ASPEED OTP memory support into AST2600 SoCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The has_otp attribute is enabled in the SBC subclasses for AST2600 to control the presence of OTP support per SoC type. Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250812094011.2617526-4-kane_chen@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast2600.c | 2 +- hw/misc/aspeed_sbc.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index d12707f0abee0..59ffd41a4ab07 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -261,7 +261,7 @@ static void aspeed_soc_ast2600_init(Object *obj) object_initialize_child(obj, "i3c", &s->i3c, TYPE_ASPEED_I3C); - object_initialize_child(obj, "sbc", &s->sbc, TYPE_ASPEED_SBC); + object_initialize_child(obj, "sbc", &s->sbc, TYPE_ASPEED_AST2600_SBC); object_initialize_child(obj, "iomem", &s->iomem, TYPE_UNIMPLEMENTED_DEVICE); object_initialize_child(obj, "video", &s->video, TYPE_UNIMPLEMENTED_DEVICE); diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c index 46a038337c314..b56a8b7678158 100644 --- a/hw/misc/aspeed_sbc.c +++ b/hw/misc/aspeed_sbc.c @@ -273,8 +273,10 @@ static const TypeInfo aspeed_sbc_info = { static void aspeed_ast2600_sbc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSBCClass *sc = ASPEED_SBC_CLASS(klass); dc->desc = "AST2600 Secure Boot Controller"; + sc->has_otp = true; } static const TypeInfo aspeed_ast2600_sbc_info = { From c6b4279a9240a838632e2d689a138da96545c540 Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Tue, 12 Aug 2025 17:40:01 +0800 Subject: [PATCH 1011/1794] hw/nvram/aspeed_otp: Add 'drive' property to support block backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces a 'drive' property to the Aspeed OTP device, allowing it to be backed by a block device. Users can now preload OTP data via QEMU CLI using a block backend. Example usage: ./qemu-system-arm \ -blockdev driver=file,filename=otpmem.img,node-name=otp \ -global aspeed-otp.drive=otp \ ... If the drive is provided, its content will be loaded as the initial OTP state. Otherwise, an internal memory buffer will be used. Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250812094011.2617526-5-kane_chen@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/nvram/aspeed_otp.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/hw/nvram/aspeed_otp.c b/hw/nvram/aspeed_otp.c index e5b7ca96767c8..abb37318231b2 100644 --- a/hw/nvram/aspeed_otp.c +++ b/hw/nvram/aspeed_otp.c @@ -35,13 +35,25 @@ static bool aspeed_otp_init_storage(AspeedOTPState *s, Error **errp) { uint32_t *p; int i, num; + uint64_t perm; + if (s->blk) { + perm = BLK_PERM_CONSISTENT_READ | + (blk_supports_write_perm(s->blk) ? BLK_PERM_WRITE : 0); + if (blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp) < 0) { + return false; + } + if (blk_pread(s->blk, 0, s->size, s->storage, 0) < 0) { + error_setg(errp, "Failed to read the initial flash content"); + return false; + } + } else { num = s->size / sizeof(uint32_t); p = (uint32_t *)s->storage; for (i = 0; i < num; i++) { p[i] = (i % 2 == 0) ? 0x00000000 : 0xFFFFFFFF; } - + } return true; } @@ -75,6 +87,7 @@ static void aspeed_otp_realize(DeviceState *dev, Error **errp) static const Property aspeed_otp_properties[] = { DEFINE_PROP_UINT64("size", AspeedOTPState, size, 0), + DEFINE_PROP_DRIVE("drive", AspeedOTPState, blk), }; static void aspeed_otp_class_init(ObjectClass *klass, const void *data) From 8970c95c4db99dd44ff463acc14c69510776cdee Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Tue, 12 Aug 2025 17:40:02 +0800 Subject: [PATCH 1012/1794] hw/nvram/aspeed_otp: Add OTP programming semantics and tracing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement correct OTP programming behavior for Aspeed OTP: - Support read-modify-write flow with one-way bit programming: * prog_bit uses 0s as the "to-be-programmed" mask. * Even-indexed words: 0->1, odd-indexed words: 1->0. * Reject non-programmable requests and log conflicts. - Enable unaligned accesses in MemoryRegionOps. Since each OTP address maps to a 1DW (4B) or 2DW (8B) block in the backing store, upper-layer accesses may be unaligned to block boundaries. This matches the irreversible, word-parity-dependent programming rules of Aspeed SoCs and exposes changes via QEMU trace events. Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250812094011.2617526-6-kane_chen@aspeedtech.com [ clg: Fixed PRIx64 format in aspeed_otp_write() ] Signed-off-by: Cédric Le Goater --- hw/nvram/aspeed_otp.c | 80 ++++++++++++++++++++++++++++++++++++++++++- hw/nvram/trace-events | 5 +++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/hw/nvram/aspeed_otp.c b/hw/nvram/aspeed_otp.c index abb37318231b2..dcf8ed3917e29 100644 --- a/hw/nvram/aspeed_otp.c +++ b/hw/nvram/aspeed_otp.c @@ -12,6 +12,7 @@ #include "system/block-backend.h" #include "hw/qdev-properties.h" #include "hw/nvram/aspeed_otp.h" +#include "hw/nvram/trace.h" static uint64_t aspeed_otp_read(void *opaque, hwaddr offset, unsigned size) { @@ -23,12 +24,87 @@ static uint64_t aspeed_otp_read(void *opaque, hwaddr offset, unsigned size) return val; } +static bool valid_program_data(uint32_t otp_addr, + uint32_t value, uint32_t prog_bit) +{ + uint32_t programmed_bits, has_programmable_bits; + bool is_odd = otp_addr & 1; + + /* + * prog_bit uses 0s to indicate target bits to program: + * - if OTP word is even-indexed, programmed bits flip 0->1 + * - if odd, bits flip 1->0 + * Bit programming is one-way only and irreversible. + */ + if (is_odd) { + programmed_bits = ~value & prog_bit; + } else { + programmed_bits = value & (~prog_bit); + } + + /* If any bit can be programmed, accept the request */ + has_programmable_bits = value ^ (~prog_bit); + + if (programmed_bits) { + trace_aspeed_otp_prog_conflict(otp_addr, programmed_bits); + for (int i = 0; i < 32; ++i) { + if (programmed_bits & (1U << i)) { + trace_aspeed_otp_prog_bit(i); + } + } + } + + return has_programmable_bits != 0; +} + +static bool program_otpmem_data(void *opaque, uint32_t otp_addr, + uint32_t prog_bit, uint32_t *value) +{ + AspeedOTPState *s = opaque; + bool is_odd = otp_addr & 1; + uint32_t otp_offset = otp_addr << 2; + + memcpy(value, s->storage + otp_offset, sizeof(uint32_t)); + + if (!valid_program_data(otp_addr, *value, prog_bit)) { + return false; + } + + if (is_odd) { + *value &= ~prog_bit; + } else { + *value |= ~prog_bit; + } + + return true; +} + static void aspeed_otp_write(void *opaque, hwaddr otp_addr, uint64_t val, unsigned size) { AspeedOTPState *s = opaque; + uint32_t otp_offset, value; - memcpy(s->storage + otp_addr, &val, size); + if (!program_otpmem_data(s, otp_addr, val, &value)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to program data, value = %x, bit = %"PRIx64"\n", + __func__, value, val); + return; + } + + otp_offset = otp_addr << 2; + memcpy(s->storage + otp_offset, &value, size); + + if (s->blk) { + if (blk_pwrite(s->blk, otp_offset, size, &value, 0) < 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to write %x to %x\n", + __func__, value, otp_offset); + + return; + } + } + trace_aspeed_otp_prog(otp_offset, val, value); } static bool aspeed_otp_init_storage(AspeedOTPState *s, Error **errp) @@ -63,6 +139,8 @@ static const MemoryRegionOps aspeed_otp_ops = { .endianness = DEVICE_LITTLE_ENDIAN, .valid.min_access_size = 1, .valid.max_access_size = 4, + .valid.unaligned = true, + .impl.unaligned = true }; static void aspeed_otp_realize(DeviceState *dev, Error **errp) diff --git a/hw/nvram/trace-events b/hw/nvram/trace-events index 5e33b24d47a0d..7084bf70d35fa 100644 --- a/hw/nvram/trace-events +++ b/hw/nvram/trace-events @@ -1,5 +1,10 @@ # See docs/devel/tracing.rst for syntax documentation. +# aspeed_otp.c +aspeed_otp_prog(uint32_t addr, uint32_t prog_value, uint32_t value) "OTP Memory program: addr 0x%" PRIx32 " prog_value 0x%" PRIx32 " value 0x%" PRIx32 +aspeed_otp_prog_conflict(uint32_t addr, uint32_t bits) "Conflict at addr=0x%x, bits=0x%08x" +aspeed_otp_prog_bit(int bit) "Programmed bit %d" + # ds1225y.c nvram_read(uint32_t addr, uint32_t ret) "read addr %d: 0x%02x" nvram_write(uint32_t addr, uint32_t old, uint32_t val) "write addr %d: 0x%02x -> 0x%02x" From fdad6ec30dfb87df0980887a4c22e55ccf65f9c3 Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Tue, 12 Aug 2025 17:40:03 +0800 Subject: [PATCH 1013/1794] hw/arm: Integrate ASPEED OTP memory support into AST1030 SoCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The has_otp attribute is enabled in the SBC subclasses for AST1030 to control the presence of OTP support per SoC type. Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250812094011.2617526-7-kane_chen@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast10x0.c | 2 +- hw/misc/aspeed_sbc.c | 16 ++++++++++++++++ include/hw/misc/aspeed_sbc.h | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index e6e1ee63c1c4e..c446e70b24a9e 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -154,7 +154,7 @@ static void aspeed_soc_ast1030_init(Object *obj) object_initialize_child(obj, "peci", &s->peci, TYPE_ASPEED_PECI); - object_initialize_child(obj, "sbc", &s->sbc, TYPE_ASPEED_SBC); + object_initialize_child(obj, "sbc", &s->sbc, TYPE_ASPEED_AST10X0_SBC); for (i = 0; i < sc->wdts_num; i++) { snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname); diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c index b56a8b7678158..052c70fd422f9 100644 --- a/hw/misc/aspeed_sbc.c +++ b/hw/misc/aspeed_sbc.c @@ -285,9 +285,25 @@ static const TypeInfo aspeed_ast2600_sbc_info = { .class_init = aspeed_ast2600_sbc_class_init, }; +static void aspeed_ast10x0_sbc_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSBCClass *sc = ASPEED_SBC_CLASS(klass); + + dc->desc = "AST10X0 Secure Boot Controller"; + sc->has_otp = true; +} + +static const TypeInfo aspeed_ast10x0_sbc_info = { + .name = TYPE_ASPEED_AST10X0_SBC, + .parent = TYPE_ASPEED_SBC, + .class_init = aspeed_ast10x0_sbc_class_init, +}; + static void aspeed_sbc_register_types(void) { type_register_static(&aspeed_ast2600_sbc_info); + type_register_static(&aspeed_ast10x0_sbc_info); type_register_static(&aspeed_sbc_info); } diff --git a/include/hw/misc/aspeed_sbc.h b/include/hw/misc/aspeed_sbc.h index 0c2746d392a47..7d640a022e4d2 100644 --- a/include/hw/misc/aspeed_sbc.h +++ b/include/hw/misc/aspeed_sbc.h @@ -14,6 +14,7 @@ #define TYPE_ASPEED_SBC "aspeed.sbc" #define TYPE_ASPEED_AST2600_SBC TYPE_ASPEED_SBC "-ast2600" +#define TYPE_ASPEED_AST10X0_SBC TYPE_ASPEED_SBC "-ast10x0" OBJECT_DECLARE_TYPE(AspeedSBCState, AspeedSBCClass, ASPEED_SBC) #define ASPEED_SBC_NR_REGS (0x93c >> 2) From 02cebef5a140a0008a8bb0d3db6e33a4e7e4a9d2 Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Tue, 12 Aug 2025 17:40:04 +0800 Subject: [PATCH 1014/1794] hw/misc/aspeed_sbc: Add CAMP2 support for OTP data reads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OTP space contains three types of entries: data, conf, and strap. Data entries consist of two DWORDs, while the other types contain only one DWORD. This change adds the R_CAMP2 register (0x024 / 4) to store the second DWORD when reading from the OTP data region. With this enhancement, OTP reads now correctly return both DWORDs for data entries via the CAMP registers, along with improved address validation and error handling. Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250812094011.2617526-8-kane_chen@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_sbc.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c index 052c70fd422f9..787e2d0489975 100644 --- a/hw/misc/aspeed_sbc.c +++ b/hw/misc/aspeed_sbc.c @@ -22,6 +22,7 @@ #define R_ADDR (0x010 / 4) #define R_STATUS (0x014 / 4) #define R_CAMP1 (0x020 / 4) +#define R_CAMP2 (0x024 / 4) #define R_QSR (0x040 / 4) /* R_STATUS */ @@ -50,6 +51,8 @@ #define SBC_OTP_CMD_READ 0x23b1e361 #define SBC_OTP_CMD_PROG 0x23b1e364 +#define OTP_DATA_DWORD_COUNT (0x800) +#define OTP_TOTAL_DWORD_COUNT (0x1000) static uint64_t aspeed_sbc_read(void *opaque, hwaddr addr, unsigned int size) { AspeedSBCState *s = ASPEED_SBC(opaque); @@ -72,6 +75,16 @@ static bool aspeed_sbc_otp_read(AspeedSBCState *s, MemTxResult ret; AspeedOTPState *otp = &s->otp; uint32_t value, otp_offset; + bool is_data = false; + + if (otp_addr < OTP_DATA_DWORD_COUNT) { + is_data = true; + } else if (otp_addr >= OTP_TOTAL_DWORD_COUNT) { + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid OTP addr 0x%x\n", + otp_addr); + return false; + } otp_offset = otp_addr << 2; ret = address_space_read(&otp->as, otp_offset, MEMTXATTRS_UNSPECIFIED, @@ -85,6 +98,20 @@ static bool aspeed_sbc_otp_read(AspeedSBCState *s, s->regs[R_CAMP1] = value; trace_aspeed_sbc_otp_read(otp_addr, value); + if (is_data) { + ret = address_space_read(&otp->as, otp_offset + 4, + MEMTXATTRS_UNSPECIFIED, + &value, sizeof(value)); + if (ret != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "Failed to read OTP memory, addr = %x\n", + otp_addr); + return false; + } + s->regs[R_CAMP2] = value; + trace_aspeed_sbc_otp_read(otp_addr + 1, value); + } + return true; } From 61dcc2c2db0236fde878d053028c39fe27dc8f36 Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Tue, 12 Aug 2025 17:40:05 +0800 Subject: [PATCH 1015/1794] hw/misc/aspeed_sbc: Handle OTP write command for voltage mode registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend OTP command handling to recognize specific voltage mode register addresses and emulate the expected hardware behavior. Without this change, legitimate voltage mode change requests would be incorrectly reported as "Unknown command" and logged as an error. This implementation does not perform actual mode changes, but ensures that valid requests are accepted and ignored as per hardware behavior. Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250812094011.2617526-9-kane_chen@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_sbc.c | 41 +++++++++++++++++++++++++++++++++++++++++ hw/misc/trace-events | 1 + 2 files changed, 42 insertions(+) diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c index 787e2d0489975..2fc5db749d23e 100644 --- a/hw/misc/aspeed_sbc.c +++ b/hw/misc/aspeed_sbc.c @@ -49,10 +49,17 @@ #define OTP_MEMORY_SIZE 0x4000 /* OTP command */ #define SBC_OTP_CMD_READ 0x23b1e361 +#define SBC_OTP_CMD_WRITE 0x23b1e362 #define SBC_OTP_CMD_PROG 0x23b1e364 #define OTP_DATA_DWORD_COUNT (0x800) #define OTP_TOTAL_DWORD_COUNT (0x1000) + +/* Voltage mode */ +#define MODE_REGISTER (0x1000) +#define MODE_REGISTER_A (0x3000) +#define MODE_REGISTER_B (0x5000) + static uint64_t aspeed_sbc_read(void *opaque, hwaddr addr, unsigned int size) { AspeedSBCState *s = ASPEED_SBC(opaque); @@ -115,6 +122,37 @@ static bool aspeed_sbc_otp_read(AspeedSBCState *s, return true; } +static bool mode_handler(uint32_t otp_addr) +{ + switch (otp_addr) { + case MODE_REGISTER: + case MODE_REGISTER_A: + case MODE_REGISTER_B: + /* HW behavior, do nothing here */ + return true; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "Unsupported address 0x%x\n", + otp_addr); + return false; + } +} + +static bool aspeed_sbc_otp_write(AspeedSBCState *s, + uint32_t otp_addr) +{ + if (otp_addr == 0) { + trace_aspeed_sbc_ignore_cmd(otp_addr); + return true; + } else { + if (mode_handler(otp_addr) == false) { + return false; + } + } + + return true; +} + static bool aspeed_sbc_otp_prog(AspeedSBCState *s, uint32_t otp_addr) { @@ -157,6 +195,9 @@ static void aspeed_sbc_handle_command(void *opaque, uint32_t cmd) case SBC_OTP_CMD_READ: ret = aspeed_sbc_otp_read(s, otp_addr); break; + case SBC_OTP_CMD_WRITE: + ret = aspeed_sbc_otp_write(s, otp_addr); + break; case SBC_OTP_CMD_PROG: ret = aspeed_sbc_otp_prog(s, otp_addr); break; diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 9e05b82f371ec..eeb9243898e9c 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -91,6 +91,7 @@ slavio_led_mem_writew(uint32_t val) "Write diagnostic LED 0x%04x" slavio_led_mem_readw(uint32_t ret) "Read diagnostic LED 0x%04x" # aspeed_sbc.c +aspeed_sbc_ignore_cmd(uint32_t cmd) "Ignoring command 0x%" PRIx32 aspeed_sbc_handle_cmd(uint32_t cmd, uint32_t addr, bool ret) "Handling command 0x%" PRIx32 " for OTP addr 0x%" PRIx32 " Result: %d" aspeed_sbc_otp_read(uint32_t addr, uint32_t value) "OTP Memory read: addr 0x%" PRIx32 " value 0x%" PRIx32 aspeed_sbc_otp_prog(uint32_t addr, uint32_t value) "OTP Memory write: addr 0x%" PRIx32 " value 0x%" PRIx32 From ea82822b7962df7846fc71a387e0c44867a66e39 Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Tue, 12 Aug 2025 17:40:07 +0800 Subject: [PATCH 1016/1794] docs/system/arm/aspeed: Document OTP memory options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add documentation for the OTP memory module used by AST2600 and AST1030 SoCs, and describe options for using a pre-generated image or an internal buffer. Include example commands for configuration and image generation. Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250812094011.2617526-11-kane_chen@aspeedtech.com Signed-off-by: Cédric Le Goater --- docs/system/arm/aspeed.rst | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index bf18c56347008..6317c0e910a23 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -243,6 +243,37 @@ under Linux), use : -M ast2500-evb,bmc-console=uart3 +OTP Option +^^^^^^^^^^ + +Both the AST2600 and AST1030 chips use the same One Time Programmable +(OTP) memory module, which is utilized for configuration, key storage, +and storing user-programmable data. This OTP memory module is managed +by the Secure Boot Controller (SBC). The following options can be +specified or omitted based on your needs. + + * When the options are specified, the pre-generated configuration + file will be used as the OTP memory storage. + + * When the options are omitted, an internal memory buffer will be + used to store the OTP memory data. + +.. code-block:: bash + + -blockdev driver=file,filename=otpmem.img,node-name=otp \ + -global aspeed-otp.drive=otp \ + +The following bash command can be used to generate a default +configuration file for OTP memory: + +.. code-block:: bash + + if [ ! -f otpmem.img ]; then + for i in $(seq 1 2048); do + printf '\x00\x00\x00\x00\xff\xff\xff\xff' + done > otpmem.img + fi + Aspeed 2700 family boards (``ast2700-evb``) ================================================================== From b28103bb52161e2d240be3e03bf0e8680cb873c9 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 2 Sep 2025 14:25:50 +0800 Subject: [PATCH 1017/1794] hw/arm/aspeed Move ast2700-evb alias to ast2700a1-evb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch moves the "ast2700-evb" alias from the A0 to A1. The A0 machine remains available via its explicit name ("ast2700a0-evb"), while functional tests are updated to target A0 by name instead of relying on the generic alias. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250902062550.3797040-1-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 2 +- tests/functional/aarch64/test_aspeed_ast2700.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index c31bbe7701381..d21b21965aa67 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1995,7 +1995,6 @@ static void aspeed_machine_ast2700a0_evb_class_init(ObjectClass *oc, MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); - mc->alias = "ast2700-evb"; mc->desc = "Aspeed AST2700 A0 EVB (Cortex-A35)"; amc->soc_name = "ast2700-a0"; amc->hw_strap1 = AST2700_EVB_HW_STRAP1; @@ -2018,6 +2017,7 @@ static void aspeed_machine_ast2700a1_evb_class_init(ObjectClass *oc, MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + mc->alias = "ast2700-evb"; mc->desc = "Aspeed AST2700 A1 EVB (Cortex-A35)"; amc->soc_name = "ast2700-a1"; amc->hw_strap1 = AST2700_EVB_HW_STRAP1; diff --git a/tests/functional/aarch64/test_aspeed_ast2700.py b/tests/functional/aarch64/test_aspeed_ast2700.py index 8a08bc46888eb..a3db267294991 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700.py +++ b/tests/functional/aarch64/test_aspeed_ast2700.py @@ -115,8 +115,8 @@ def start_ast2700_test_vbootrom(self, name): self.do_test_aarch64_aspeed_sdk_start( self.scratch_file(name, 'image-bmc')) - def test_aarch64_ast2700_evb_sdk_v09_06(self): - self.set_machine('ast2700-evb') + def test_aarch64_ast2700a0_evb_sdk_v09_06(self): + self.set_machine('ast2700a0-evb') self.archive_extract(self.ASSET_SDK_V906_AST2700) self.start_ast2700_test('ast2700-a0-default') From 54623e94623cd75aec87ebd1e49b538033676c3f Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Wed, 17 Sep 2025 11:59:15 +0800 Subject: [PATCH 1018/1794] tests/functional/arm: Add helper to generate OTP images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a small helper that generates OTP images at test time. This lets multiple test cases create default OTP contents without shipping prebuilt fixtures and keeps the tests self-contained. Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250917035917.4141723-2-kane_chen@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/aspeed.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/functional/aspeed.py b/tests/functional/aspeed.py index b131703c5283a..47e84e035bd0a 100644 --- a/tests/functional/aspeed.py +++ b/tests/functional/aspeed.py @@ -61,3 +61,11 @@ def do_test_arm_aspeed_sdk_start(self, image): self.wait_for_console_pattern('U-Boot 2019.04') self.wait_for_console_pattern('## Loading kernel from FIT Image') self.wait_for_console_pattern('Starting kernel ...') + + def generate_otpmem_image(self): + path = self.scratch_file("otpmem.img") + pattern = b'\x00\x00\x00\x00\xff\xff\xff\xff' * (16 * 1024 // 8) + with open(path, "wb") as f: + f.write(pattern) + return path + From fb89ceac92012d154f1113190e24e45a42f396fc Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Wed, 17 Sep 2025 11:59:16 +0800 Subject: [PATCH 1019/1794] tests/functional/arm: Add AST1030 boot test with generated OTP image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a functional test that boots an AST1030 machine with a generated OTP image. The test verifies that OTP contents are read during early boot and that the system reaches the expected console prompt. Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250917035917.4141723-3-kane_chen@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/arm/test_aspeed_ast1030.py | 24 +++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/functional/arm/test_aspeed_ast1030.py b/tests/functional/arm/test_aspeed_ast1030.py index 42126b514ff9f..e47b597d0bd97 100755 --- a/tests/functional/arm/test_aspeed_ast1030.py +++ b/tests/functional/arm/test_aspeed_ast1030.py @@ -7,17 +7,18 @@ # 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 -class AST1030Machine(LinuxKernelTest): +class AST1030Machine(AspeedTest): ASSET_ZEPHYR_3_02 = Asset( ('https://github.com/AspeedTech-BMC' '/zephyr/releases/download/v00.03.02/ast1030-evb-demo.zip'), '1ec83caab3ddd5d09481772801be7210e222cb015ce22ec6fffb8a76956dcd4f') - def test_ast1030_zephyros_3_02(self): + def test_arm_ast1030_zephyros_3_02(self): self.set_machine('ast1030-evb') kernel_name = "ast1030-evb-demo-3/zephyr.elf" @@ -36,7 +37,7 @@ def test_ast1030_zephyros_3_02(self): '/zephyr/releases/download/v00.01.07/ast1030-evb-demo.zip'), 'ad52e27959746988afaed8429bf4e12ab988c05c4d07c9d90e13ec6f7be4574c') - def test_ast1030_zephyros_1_07(self): + def test_arm_ast1030_zephyros_1_07(self): self.set_machine('ast1030-evb') kernel_name = "ast1030-evb-demo/zephyr.bin" @@ -68,6 +69,21 @@ def test_ast1030_zephyros_1_07(self): 'kernel uptime', ]: exec_command_and_wait_for_pattern(self, shell_cmd, "uart:~$") + def test_arm_ast1030_otp_blockdev_device(self): + self.vm.set_machine("ast1030-evb") + + kernel_name = "ast1030-evb-demo-3/zephyr.elf" + kernel_file = self.archive_extract(self.ASSET_ZEPHYR_3_02, member=kernel_name) + otp_img = self.generate_otpmem_image() + + self.vm.set_console() + self.vm.add_args( + "-kernel", kernel_file, + "-blockdev", f"driver=file,filename={otp_img},node-name=otp", + "-global", "aspeed-otp.drive=otp", + ) + self.vm.launch() + self.wait_for_console_pattern("Booting Zephyr OS") if __name__ == '__main__': - LinuxKernelTest.main() + AspeedTest.main() From 2057685f0b02f06ae2aec0627489421f06bc7796 Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Wed, 17 Sep 2025 11:59:17 +0800 Subject: [PATCH 1020/1794] tests/functional/arm: Add AST2600 boot test with generated OTP image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a functional test that boots an AST2600 machine with a generated OTP image. The test verifies that OTP contents are read during early boot and that the system reaches the expected console prompt. Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250917035917.4141723-4-kane_chen@aspeedtech.com [ clg: checkpath fixes ] Signed-off-by: Cédric Le Goater --- tests/functional/arm/test_aspeed_ast2600.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/functional/arm/test_aspeed_ast2600.py b/tests/functional/arm/test_aspeed_ast2600.py index 129695ca4ec7f..62949b0b4fe95 100755 --- a/tests/functional/arm/test_aspeed_ast2600.py +++ b/tests/functional/arm/test_aspeed_ast2600.py @@ -136,5 +136,20 @@ def test_arm_ast2600_evb_sdk(self): exec_command_and_wait_for_pattern(self, '/sbin/hwclock -f /dev/rtc1', year) + def test_arm_ast2600_otp_blockdev_device(self): + self.vm.set_machine("ast2600-evb") + + image_path = self.archive_extract(self.ASSET_SDK_V907_AST2600) + otp_img = self.generate_otpmem_image() + + self.vm.set_console() + self.vm.add_args( + "-blockdev", f"driver=file,filename={otp_img},node-name=otp", + "-global", "aspeed-otp.drive=otp", + ) + self.do_test_arm_aspeed_sdk_start( + self.scratch_file("ast2600-default", "image-bmc")) + self.wait_for_console_pattern("ast2600-default login:") + if __name__ == '__main__': AspeedTest.main() From 42dd3c4f7dbceb6ea374b3fbe104781f68008e1f Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 19 Sep 2025 17:30:00 +0800 Subject: [PATCH 1021/1794] hw/pci/pci_ids: Add PCI vendor ID for ASPEED MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250919093017.338309-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- include/hw/pci/pci_ids.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h index 33e2898be9576..16034aaa2c7e4 100644 --- a/include/hw/pci/pci_ids.h +++ b/include/hw/pci/pci_ids.h @@ -291,4 +291,6 @@ #define PCI_VENDOR_ID_NVIDIA 0x10de +#define PCI_VENDOR_ID_ASPEED 0x1A03 + #endif From 303e8dc29ffe6dc2417a0240cea604f18371ea12 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 19 Sep 2025 17:30:01 +0800 Subject: [PATCH 1022/1794] hw/pci-host/aspeed: Add AST2600 PCIe PHY model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces an initial ASPEED PCIe PHY/host controller model to support the AST2600 SoC. It provides a simple register block with MMIO read/write callbacks, integration into the build system, and trace events for debugging. Key changes: 1. PCIe PHY MMIO read/write callbacks Implemented aspeed_pcie_phy_read() and aspeed_pcie_phy_write() to handle 32-bit register accesses. 2. Build system and Kconfig integration Added CONFIG_PCI_EXPRESS_ASPEED in hw/pci-host/Kconfig and meson rules. Updated ASPEED_SOC in hw/arm/Kconfig to imply PCI_DEVICES and select PCI_EXPRESS_ASPEED. 3. Trace events for debug New tracepoints aspeed_pcie_phy_read and aspeed_pcie_phy_write allow monitoring MMIO accesses. 4. Register space and defaults (AST2600 reference) Expose a 0x100 register space, as documented in the AST2600 datasheet. On reset, set default values: PEHR_ID: Vendor ID = ASPEED, Device ID = 0x1150 PEHR_CLASS_CODE = 0x06040006 PEHR_DATALINK = 0xD7040022 PEHR_LINK: bit[5] set to 1 to indicate link up. This provides a skeleton device for the AST2600 platform. It enables firmware to detect the PCIe link as up by default and allows future extension. This commit is the starting point of the series to introduce ASPEED PCIe Root Complex (RC) support. Based on previous work from Cédric Le Goater, the following commits in this series extend and refine the implementation: - Add a PCIe Root Port so that devices can be attached without requiring an extra bridge. - Restrict the Root Port device instantiation to the AST2600 platform. - Integrate aspeed_cfg_translate_write() to support both AST2600 and AST2700. - Add MSI support and a preliminary RC IOMMU address space. - Fix issues with MSI interrupt clearing. - Extend support to the AST2700 SoC. - Drop the AST2600 RC_L support. - Introduce PCIe RC functional tests covering both AST2600 and AST2700. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250919093017.338309-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/Kconfig | 3 + hw/pci-host/Kconfig | 4 + hw/pci-host/aspeed_pcie.c | 157 ++++++++++++++++++++++++++++++ hw/pci-host/meson.build | 1 + hw/pci-host/trace-events | 4 + include/hw/pci-host/aspeed_pcie.h | 42 ++++++++ 6 files changed, 211 insertions(+) create mode 100644 hw/pci-host/aspeed_pcie.c create mode 100644 include/hw/pci-host/aspeed_pcie.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 3baa6c6c74778..b44b85f436121 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -541,6 +541,7 @@ config ASPEED_SOC bool default y depends on TCG && ARM + imply PCI_DEVICES select DS1338 select FTGMAC100 select I2C @@ -561,6 +562,8 @@ config ASPEED_SOC select MAX31785 select FSI_APB2OPB_ASPEED select AT24C + select PCI_EXPRESS + select PCI_EXPRESS_ASPEED config MPS2 bool diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig index 9824fa188d6b8..8cbb8304a3ff6 100644 --- a/hw/pci-host/Kconfig +++ b/hw/pci-host/Kconfig @@ -46,6 +46,10 @@ config PCI_I440FX select PCI select PAM +config PCI_EXPRESS_ASPEED + bool + select PCI_EXPRESS + config PCI_EXPRESS_Q35 bool select PCI_EXPRESS diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c new file mode 100644 index 0000000000000..4c74520052a43 --- /dev/null +++ b/hw/pci-host/aspeed_pcie.c @@ -0,0 +1,157 @@ +/* + * ASPEED PCIe Host Controller + * + * Copyright (C) 2025 ASPEED Technology Inc. + * Copyright (c) 2022 Cédric Le Goater + * + * Authors: + * Cédric Le Goater + * Jamin Lin + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Based on previous work from Cédric Le Goater. + * Modifications extend support for the ASPEED AST2600 and AST2700 platforms. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "hw/registerfields.h" +#include "hw/irq.h" +#include "hw/pci/pci_host.h" +#include "hw/pci-host/aspeed_pcie.h" +#include "hw/pci/msi.h" +#include "trace.h" + +/* + * PCIe PHY + * + * PCIe Host Controller (PCIEH) + */ + +/* AST2600 */ +REG32(PEHR_ID, 0x00) + FIELD(PEHR_ID, DEV, 16, 16) +REG32(PEHR_CLASS_CODE, 0x04) +REG32(PEHR_DATALINK, 0x10) +REG32(PEHR_PROTECT, 0x7C) + FIELD(PEHR_PROTECT, LOCK, 0, 8) +REG32(PEHR_LINK, 0xC0) + FIELD(PEHR_LINK, STS, 5, 1) + +#define ASPEED_PCIE_PHY_UNLOCK 0xA8 + +static uint64_t aspeed_pcie_phy_read(void *opaque, hwaddr addr, + unsigned int size) +{ + AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(opaque); + uint32_t reg = addr >> 2; + uint32_t value = 0; + + value = s->regs[reg]; + + trace_aspeed_pcie_phy_read(s->id, addr, value); + + return value; +} + +static void aspeed_pcie_phy_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(opaque); + uint32_t reg = addr >> 2; + + trace_aspeed_pcie_phy_write(s->id, addr, data); + + switch (reg) { + case R_PEHR_PROTECT: + data &= R_PEHR_PROTECT_LOCK_MASK; + s->regs[reg] = !!(data == ASPEED_PCIE_PHY_UNLOCK); + break; + default: + s->regs[reg] = data; + break; + } +} + +static const MemoryRegionOps aspeed_pcie_phy_ops = { + .read = aspeed_pcie_phy_read, + .write = aspeed_pcie_phy_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void aspeed_pcie_phy_reset(DeviceState *dev) +{ + AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev); + AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_GET_CLASS(s); + + memset(s->regs, 0, apc->nr_regs << 2); + + s->regs[R_PEHR_ID] = + (0x1150 << R_PEHR_ID_DEV_SHIFT) | PCI_VENDOR_ID_ASPEED; + s->regs[R_PEHR_CLASS_CODE] = 0x06040006; + s->regs[R_PEHR_DATALINK] = 0xD7040022; + s->regs[R_PEHR_LINK] = R_PEHR_LINK_STS_MASK; +} + +static void aspeed_pcie_phy_realize(DeviceState *dev, Error **errp) +{ + AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev); + AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_GET_CLASS(s); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + g_autofree char *name = NULL; + + s->regs = g_new(uint32_t, apc->nr_regs); + name = g_strdup_printf(TYPE_ASPEED_PCIE_PHY ".regs.%d", s->id); + memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_pcie_phy_ops, s, name, + apc->nr_regs << 2); + sysbus_init_mmio(sbd, &s->mmio); +} + +static void aspeed_pcie_phy_unrealize(DeviceState *dev) +{ + AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev); + + g_free(s->regs); + s->regs = NULL; +} + +static const Property aspeed_pcie_phy_props[] = { + DEFINE_PROP_UINT32("id", AspeedPCIEPhyState, id, 0), +}; + +static void aspeed_pcie_phy_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_CLASS(klass); + + dc->desc = "ASPEED PCIe Phy"; + dc->realize = aspeed_pcie_phy_realize; + dc->unrealize = aspeed_pcie_phy_unrealize; + device_class_set_legacy_reset(dc, aspeed_pcie_phy_reset); + device_class_set_props(dc, aspeed_pcie_phy_props); + + apc->nr_regs = 0x100 >> 2; +} + +static const TypeInfo aspeed_pcie_phy_info = { + .name = TYPE_ASPEED_PCIE_PHY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedPCIEPhyState), + .class_init = aspeed_pcie_phy_class_init, + .class_size = sizeof(AspeedPCIEPhyClass), +}; + +static void aspeed_pcie_register_types(void) +{ + type_register_static(&aspeed_pcie_phy_info); +} + +type_init(aspeed_pcie_register_types); + diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build index 937a0f72acf94..86b754d0b0d98 100644 --- a/hw/pci-host/meson.build +++ b/hw/pci-host/meson.build @@ -2,6 +2,7 @@ pci_ss = ss.source_set() pci_ss.add(when: 'CONFIG_PAM', if_true: files('pam.c')) pci_ss.add(when: 'CONFIG_PCI_BONITO', if_true: files('bonito.c')) pci_ss.add(when: 'CONFIG_GT64120', if_true: files('gt64120.c')) +pci_ss.add(when: 'CONFIG_PCI_EXPRESS_ASPEED', if_true: files('aspeed_pcie.c')) pci_ss.add(when: 'CONFIG_PCI_EXPRESS_DESIGNWARE', if_true: files('designware.c')) pci_ss.add(when: 'CONFIG_PCI_EXPRESS_GENERIC_BRIDGE', if_true: files('gpex.c')) pci_ss.add(when: ['CONFIG_PCI_EXPRESS_GENERIC_BRIDGE', 'CONFIG_ACPI'], if_true: files('gpex-acpi.c')) diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events index 0a816b9aa129b..3438516756db7 100644 --- a/hw/pci-host/trace-events +++ b/hw/pci-host/trace-events @@ -1,5 +1,9 @@ # See docs/devel/tracing.rst for syntax documentation. +# aspeed_pcie.c +aspeed_pcie_phy_read(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32 +aspeed_pcie_phy_write(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32 + # bonito.c bonito_spciconf_small_access(uint64_t addr, unsigned size) "PCI config address is smaller then 32-bit, addr: 0x%"PRIx64", size: %u" diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h new file mode 100644 index 0000000000000..d9fb829048a0f --- /dev/null +++ b/include/hw/pci-host/aspeed_pcie.h @@ -0,0 +1,42 @@ +/* + * ASPEED PCIe Host Controller + * + * Copyright (C) 2025 ASPEED Technology Inc. + * Copyright (c) 2022 Cédric Le Goater + * + * Authors: + * Cédric Le Goater + * Jamin Lin + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Based on previous work from Cédric Le Goater. + * Modifications extend support for the ASPEED AST2600 and AST2700 platforms. + */ + +#ifndef ASPEED_PCIE_H +#define ASPEED_PCIE_H + +#include "hw/sysbus.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pcie_host.h" +#include "qom/object.h" + +#define TYPE_ASPEED_PCIE_PHY "aspeed.pcie-phy" +OBJECT_DECLARE_TYPE(AspeedPCIEPhyState, AspeedPCIEPhyClass, ASPEED_PCIE_PHY); + +struct AspeedPCIEPhyState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + uint32_t *regs; + uint32_t id; +}; + +struct AspeedPCIEPhyClass { + SysBusDeviceClass parent_class; + + uint64_t nr_regs; +}; + +#endif /* ASPEED_PCIE_H */ From f002aa35f3205d55bbe4a68670ba19a6dcf1d6a8 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 19 Sep 2025 17:30:02 +0800 Subject: [PATCH 1023/1794] hw/pci-host/aspeed: Add AST2600 PCIe config space and host bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce PCIe config and host bridge model for the AST2600 platform. This patch adds support for the H2X (AHB to PCIe Bus Bridge) controller with a 0x100 byte register space. The register layout is shared between two root complexes: 0x00–0x7f is common, 0x80–0xbf for RC_L, and 0xc0–0xff for RC_H. Only RC_H is modeled in this implementation. The RC_H bus uses bus numbers in the 0x80–0xff range instead of the standard root bus 0x00. To allow the PCI subsystem to discover devices, the host bridge logic remaps the root bus number back to 0x00 whenever the configured bus number matches the "bus-nr" property. New MMIO callbacks are added for the H2X config space: - aspeed_pcie_cfg_read() and aspeed_pcie_cfg_write() handle register accesses. - aspeed_pcie_cfg_readwrite() provides configuration read/write support. - aspeed_pcie_cfg_translate_write() handles PCIe byte-enable semantics for write operations. The reset handler initializes the H2X register block with default values as defined in the AST2600 datasheet. Additional changes: - Implement ASPEED PCIe root complex (TYPE_ASPEED_PCIE_RC). - Wire up interrupt propagation via aspeed_pcie_rc_set_irq(). - Add tracepoints for config read/write and INTx handling. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250919093017.338309-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/pci-host/aspeed_pcie.c | 421 ++++++++++++++++++++++++++++++ hw/pci-host/trace-events | 4 + include/hw/pci-host/aspeed_pcie.h | 59 +++++ 3 files changed, 484 insertions(+) diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c index 4c74520052a43..c3e92ee44926a 100644 --- a/hw/pci-host/aspeed_pcie.c +++ b/hw/pci-host/aspeed_pcie.c @@ -25,6 +25,425 @@ #include "hw/pci/msi.h" #include "trace.h" +/* + * PCIe Root Complex (RC) + */ + +static void aspeed_pcie_rc_set_irq(void *opaque, int irq, int level) +{ + AspeedPCIERcState *rc = (AspeedPCIERcState *) opaque; + AspeedPCIECfgState *cfg = + container_of(rc, AspeedPCIECfgState, rc); + bool intx; + + assert(irq < PCI_NUM_PINS); + + if (level) { + cfg->regs[cfg->rc_regs->int_sts_reg] |= BIT(irq); + } else { + cfg->regs[cfg->rc_regs->int_sts_reg] &= ~BIT(irq); + } + + intx = !!(cfg->regs[cfg->rc_regs->int_sts_reg] & + cfg->regs[cfg->rc_regs->int_en_reg]); + trace_aspeed_pcie_rc_intx_set_irq(cfg->id, irq, intx); + qemu_set_irq(rc->irq, intx); +} + +static int aspeed_pcie_rc_map_irq(PCIDevice *pci_dev, int irq_num) +{ + return irq_num % PCI_NUM_PINS; +} + +static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp) +{ + PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev); + AspeedPCIERcState *rc = ASPEED_PCIE_RC(dev); + AspeedPCIECfgState *cfg = + container_of(rc, AspeedPCIECfgState, rc); + PCIHostState *pci = PCI_HOST_BRIDGE(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + g_autofree char *ioport_window_name = NULL; + g_autofree char *mmio_window_name = NULL; + g_autofree char *root_bus_name = NULL; + + /* PCI configuration space */ + pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MAX); + sysbus_init_mmio(sbd, &pex->mmio); + + /* MMIO and IO region */ + memory_region_init(&rc->mmio, OBJECT(rc), "mmio", UINT64_MAX); + memory_region_init(&rc->io, OBJECT(rc), "io", 0x10000); + + mmio_window_name = g_strdup_printf("pcie.%d.mmio_window", cfg->id); + memory_region_init_io(&rc->mmio_window, OBJECT(rc), &unassigned_io_ops, + OBJECT(rc), mmio_window_name, UINT64_MAX); + ioport_window_name = g_strdup_printf("pcie.%d.ioport_window", cfg->id); + memory_region_init_io(&rc->io_window, OBJECT(rc), &unassigned_io_ops, + OBJECT(rc), ioport_window_name, 0x10000); + + memory_region_add_subregion(&rc->mmio_window, 0, &rc->mmio); + memory_region_add_subregion(&rc->io_window, 0, &rc->io); + sysbus_init_mmio(sbd, &rc->mmio_window); + sysbus_init_mmio(sbd, &rc->io_window); + + sysbus_init_irq(sbd, &rc->irq); + root_bus_name = g_strdup_printf("pcie.rc%d", cfg->id); + pci->bus = pci_register_root_bus(dev, root_bus_name, + aspeed_pcie_rc_set_irq, + aspeed_pcie_rc_map_irq, rc, &rc->mmio, + &rc->io, 0, 4, TYPE_PCIE_BUS); + pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE; +} + +static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge, + PCIBus *rootbus) +{ + AspeedPCIERcState *rc = ASPEED_PCIE_RC(host_bridge); + AspeedPCIECfgState *cfg = + container_of(rc, AspeedPCIECfgState, rc); + + snprintf(rc->name, sizeof(rc->name), "%04x:%02x", cfg->id, rc->bus_nr); + + return rc->name; +} + +static const Property aspeed_pcie_rc_props[] = { + DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0), +}; + +static void aspeed_pcie_rc_class_init(ObjectClass *klass, const void *data) +{ + PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "ASPEED PCIe RC"; + dc->realize = aspeed_pcie_rc_realize; + dc->fw_name = "pci"; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + + hc->root_bus_path = aspeed_pcie_rc_root_bus_path; + device_class_set_props(dc, aspeed_pcie_rc_props); + + msi_nonbroken = true; +} + +static const TypeInfo aspeed_pcie_rc_info = { + .name = TYPE_ASPEED_PCIE_RC, + .parent = TYPE_PCIE_HOST_BRIDGE, + .instance_size = sizeof(AspeedPCIERcState), + .class_init = aspeed_pcie_rc_class_init, +}; + +/* + * PCIe Config + * + * AHB to PCIe Bus Bridge (H2X) + * + * On the AST2600: + * NOTE: rc_l is not supported by this model. + * - Registers 0x00 - 0x7F are shared by both PCIe0 (rc_l) and PCIe1 (rc_h). + * - Registers 0x80 - 0xBF are specific to PCIe0. + * - Registers 0xC0 - 0xFF are specific to PCIe1. + */ + +/* AST2600 */ +REG32(H2X_CTRL, 0x00) + FIELD(H2X_CTRL, CLEAR_RX, 4, 1) +REG32(H2X_TX_CLEAR, 0x08) + FIELD(H2X_TX_CLEAR, IDLE, 0, 1) +REG32(H2X_RDATA, 0x0C) +REG32(H2X_TX_DESC0, 0x10) +REG32(H2X_TX_DESC1, 0x14) +REG32(H2X_TX_DESC2, 0x18) +REG32(H2X_TX_DESC3, 0x1C) +REG32(H2X_TX_DATA, 0x20) +REG32(H2X_TX_STS, 0x24) + FIELD(H2X_TX_STS, IDLE, 31, 1) + FIELD(H2X_TX_STS, RC_L_TX_COMP, 24, 1) + FIELD(H2X_TX_STS, RC_H_TX_COMP, 25, 1) + FIELD(H2X_TX_STS, TRIG, 0, 1) +REG32(H2X_RC_H_CTRL, 0xC0) +REG32(H2X_RC_H_INT_EN, 0xC4) +REG32(H2X_RC_H_INT_STS, 0xC8) + SHARED_FIELD(H2X_RC_INT_INTDONE, 4, 1) + SHARED_FIELD(H2X_RC_INT_INTX, 0, 4) +REG32(H2X_RC_H_RDATA, 0xCC) + +#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */ +#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */ +#define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */ +#define TLP_FMTTYPE_CFGWR1 0x45 /* Configuration Write Type 1 */ + +#define PCIE_CFG_FMTTYPE_MASK(x) (((x) >> 24) & 0xff) +#define PCIE_CFG_BYTE_EN(x) ((x) & 0xf) + +static const AspeedPCIERegMap aspeed_regmap = { + .rc = { + .int_en_reg = R_H2X_RC_H_INT_EN, + .int_sts_reg = R_H2X_RC_H_INT_STS, + }, +}; + +static uint64_t aspeed_pcie_cfg_read(void *opaque, hwaddr addr, + unsigned int size) +{ + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque); + uint32_t reg = addr >> 2; + uint32_t value = 0; + + value = s->regs[reg]; + + trace_aspeed_pcie_cfg_read(s->id, addr, value); + + return value; +} + +static void aspeed_pcie_cfg_translate_write(uint8_t byte_en, uint32_t *addr, + uint64_t *val, int *len) +{ + uint64_t packed_val = 0; + int first_bit = -1; + int index = 0; + int i; + + *len = ctpop8(byte_en); + + if (*len == 0 || *len > 4) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid byte enable: 0x%x\n", + __func__, byte_en); + return; + } + + /* Special case: full 4-byte write must be 4-byte aligned */ + if (byte_en == 0x0f) { + if ((*addr & 0x3) != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: 4-byte write not 4-byte aligned: addr=0x%x\n", + __func__, *addr); + return; + } + *val &= 0xffffffffULL; + return; + } + + for (i = 0; i < 4; i++) { + if (byte_en & (1 << i)) { + if (first_bit < 0) { + first_bit = i; + } + packed_val |= ((*val >> (i * 8)) & 0xff) << (index * 8); + index++; + } + } + + *addr += first_bit; + *val = packed_val; +} + +static void aspeed_pcie_cfg_readwrite(AspeedPCIECfgState *s, + const AspeedPCIECfgTxDesc *desc) +{ + AspeedPCIERcState *rc = &s->rc; + PCIHostState *pci = NULL; + PCIDevice *pdev = NULL; + uint32_t cfg_addr; + uint32_t offset; + uint8_t byte_en; + bool is_write; + uint8_t devfn; + uint64_t val; + uint8_t bus; + int len; + + val = ~0; + is_write = !!(desc->desc0 & BIT(30)); + cfg_addr = desc->desc2; + + bus = (cfg_addr >> 24) & 0xff; + devfn = (cfg_addr >> 16) & 0xff; + offset = cfg_addr & 0xffc; + + pci = PCI_HOST_BRIDGE(rc); + + /* + * On the AST2600, the RC_H bus number range from 0x80 to 0xFF, with the + * root device and root port assigned to bus 0x80 instead of the standard + * 0x00. To allow the PCI subsystem to correctly discover devices on the + * root bus, bus 0x80 is remapped to 0x00. + */ + if (bus == rc->bus_nr) { + bus = 0; + } + + pdev = pci_find_device(pci->bus, bus, devfn); + if (!pdev) { + s->regs[desc->rdata_reg] = ~0; + goto out; + } + + switch (PCIE_CFG_FMTTYPE_MASK(desc->desc0)) { + case TLP_FMTTYPE_CFGWR0: + case TLP_FMTTYPE_CFGWR1: + byte_en = PCIE_CFG_BYTE_EN(desc->desc1); + val = desc->wdata; + aspeed_pcie_cfg_translate_write(byte_en, &offset, &val, &len); + pci_host_config_write_common(pdev, offset, pci_config_size(pdev), + val, len); + break; + case TLP_FMTTYPE_CFGRD0: + case TLP_FMTTYPE_CFGRD1: + val = pci_host_config_read_common(pdev, offset, + pci_config_size(pdev), 4); + s->regs[desc->rdata_reg] = val; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid CFG type. DESC0=0x%x\n", + __func__, desc->desc0); + } + +out: + trace_aspeed_pcie_cfg_rw(s->id, is_write ? "write" : "read", bus, devfn, + cfg_addr, val); +} + +static void aspeed_pcie_cfg_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque); + AspeedPCIECfgTxDesc desc; + uint32_t reg = addr >> 2; + uint32_t rc_reg; + + trace_aspeed_pcie_cfg_write(s->id, addr, data); + + switch (reg) { + case R_H2X_CTRL: + if (data & R_H2X_CTRL_CLEAR_RX_MASK) { + s->regs[R_H2X_RDATA] = ~0; + } + break; + case R_H2X_TX_CLEAR: + if (data & R_H2X_TX_CLEAR_IDLE_MASK) { + s->regs[R_H2X_TX_STS] &= ~R_H2X_TX_STS_IDLE_MASK; + } + break; + case R_H2X_TX_STS: + if (data & R_H2X_TX_STS_TRIG_MASK) { + desc.desc0 = s->regs[R_H2X_TX_DESC0]; + desc.desc1 = s->regs[R_H2X_TX_DESC1]; + desc.desc2 = s->regs[R_H2X_TX_DESC2]; + desc.desc3 = s->regs[R_H2X_TX_DESC3]; + desc.wdata = s->regs[R_H2X_TX_DATA]; + desc.rdata_reg = R_H2X_RC_H_RDATA; + aspeed_pcie_cfg_readwrite(s, &desc); + rc_reg = s->rc_regs->int_sts_reg; + s->regs[rc_reg] |= H2X_RC_INT_INTDONE_MASK; + s->regs[R_H2X_TX_STS] |= + BIT(R_H2X_TX_STS_RC_H_TX_COMP_SHIFT); + s->regs[R_H2X_TX_STS] |= R_H2X_TX_STS_IDLE_MASK; + } + break; + /* preserve INTx status */ + case R_H2X_RC_H_INT_STS: + if (data & H2X_RC_INT_INTDONE_MASK) { + s->regs[R_H2X_TX_STS] &= ~R_H2X_TX_STS_RC_H_TX_COMP_MASK; + } + s->regs[reg] &= ~data | H2X_RC_INT_INTX_MASK; + break; + default: + s->regs[reg] = data; + break; + } +} + +static const MemoryRegionOps aspeed_pcie_cfg_ops = { + .read = aspeed_pcie_cfg_read, + .write = aspeed_pcie_cfg_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void aspeed_pcie_cfg_instance_init(Object *obj) +{ + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(obj); + + object_initialize_child(obj, "rc", &s->rc, TYPE_ASPEED_PCIE_RC); + + return; +} + +static void aspeed_pcie_cfg_reset(DeviceState *dev) +{ + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev); + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s); + + memset(s->regs, 0, apc->nr_regs << 2); +} + +static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev); + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s); + g_autofree char *name = NULL; + + s->rc_regs = &apc->reg_map->rc; + s->regs = g_new(uint32_t, apc->nr_regs); + name = g_strdup_printf(TYPE_ASPEED_PCIE_CFG ".regs.%d", s->id); + memory_region_init_io(&s->mmio, OBJECT(s), apc->reg_ops, s, name, + apc->nr_regs << 2); + sysbus_init_mmio(sbd, &s->mmio); + + object_property_set_int(OBJECT(&s->rc), "bus-nr", + apc->rc_bus_nr, + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->rc), errp)) { + return; + } +} + +static void aspeed_pcie_cfg_unrealize(DeviceState *dev) +{ + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev); + + g_free(s->regs); + s->regs = NULL; +} + +static const Property aspeed_pcie_cfg_props[] = { + DEFINE_PROP_UINT32("id", AspeedPCIECfgState, id, 0), +}; + +static void aspeed_pcie_cfg_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_CLASS(klass); + + dc->desc = "ASPEED PCIe Config"; + dc->realize = aspeed_pcie_cfg_realize; + dc->unrealize = aspeed_pcie_cfg_unrealize; + device_class_set_legacy_reset(dc, aspeed_pcie_cfg_reset); + device_class_set_props(dc, aspeed_pcie_cfg_props); + + apc->reg_ops = &aspeed_pcie_cfg_ops; + apc->reg_map = &aspeed_regmap; + apc->nr_regs = 0x100 >> 2; + apc->rc_bus_nr = 0x80; +} + +static const TypeInfo aspeed_pcie_cfg_info = { + .name = TYPE_ASPEED_PCIE_CFG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = aspeed_pcie_cfg_instance_init, + .instance_size = sizeof(AspeedPCIECfgState), + .class_init = aspeed_pcie_cfg_class_init, + .class_size = sizeof(AspeedPCIECfgClass), +}; + /* * PCIe PHY * @@ -150,6 +569,8 @@ static const TypeInfo aspeed_pcie_phy_info = { static void aspeed_pcie_register_types(void) { + type_register_static(&aspeed_pcie_rc_info); + type_register_static(&aspeed_pcie_cfg_info); type_register_static(&aspeed_pcie_phy_info); } diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events index 3438516756db7..2584ea56e2cc0 100644 --- a/hw/pci-host/trace-events +++ b/hw/pci-host/trace-events @@ -1,6 +1,10 @@ # See docs/devel/tracing.rst for syntax documentation. # aspeed_pcie.c +aspeed_pcie_rc_intx_set_irq(uint32_t id, int num, int level) "%d: num %d set IRQ leve %d" +aspeed_pcie_cfg_read(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32 +aspeed_pcie_cfg_write(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32 +aspeed_pcie_cfg_rw(uint32_t id, const char *dir, uint8_t bus, uint8_t devfn, uint64_t addr, uint64_t data) "%d: %s bus:0x%x devfn:0x%x addr 0x%" PRIx64 " data 0x%" PRIx64 aspeed_pcie_phy_read(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32 aspeed_pcie_phy_write(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32 diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h index d9fb829048a0f..850d579189eb9 100644 --- a/include/hw/pci-host/aspeed_pcie.h +++ b/include/hw/pci-host/aspeed_pcie.h @@ -22,6 +22,65 @@ #include "hw/pci/pcie_host.h" #include "qom/object.h" +typedef struct AspeedPCIECfgTxDesc { + uint32_t desc0; + uint32_t desc1; + uint32_t desc2; + uint32_t desc3; + uint32_t wdata; + uint32_t rdata_reg; +} AspeedPCIECfgTxDesc; + +typedef struct AspeedPCIERcRegs { + uint32_t int_en_reg; + uint32_t int_sts_reg; +} AspeedPCIERcRegs; + +typedef struct AspeedPCIERegMap { + AspeedPCIERcRegs rc; +} AspeedPCIERegMap; + +#define TYPE_ASPEED_PCIE_RC "aspeed.pcie-rc" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERcState, ASPEED_PCIE_RC); + +struct AspeedPCIERcState { + PCIExpressHost parent_obj; + + MemoryRegion mmio_window; + MemoryRegion io_window; + MemoryRegion mmio; + MemoryRegion io; + + uint32_t bus_nr; + char name[16]; + qemu_irq irq; +}; + +/* Bridge between AHB bus and PCIe RC. */ +#define TYPE_ASPEED_PCIE_CFG "aspeed.pcie-cfg" +OBJECT_DECLARE_TYPE(AspeedPCIECfgState, AspeedPCIECfgClass, ASPEED_PCIE_CFG); + +struct AspeedPCIECfgState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + uint32_t *regs; + uint32_t id; + + const AspeedPCIERcRegs *rc_regs; + AspeedPCIERcState rc; +}; + +struct AspeedPCIECfgClass { + SysBusDeviceClass parent_class; + + const AspeedPCIERegMap *reg_map; + const MemoryRegionOps *reg_ops; + + uint64_t rc_bus_nr; + uint64_t nr_regs; +}; + #define TYPE_ASPEED_PCIE_PHY "aspeed.pcie-phy" OBJECT_DECLARE_TYPE(AspeedPCIEPhyState, AspeedPCIEPhyClass, ASPEED_PCIE_PHY); From cac2f082936de3c35c7b9c04fef4dc99b7af9898 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 19 Sep 2025 17:30:03 +0800 Subject: [PATCH 1024/1794] hw/pci-host/aspeed: Add AST2600 PCIe Root Device support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a PCIe Root Device for AST2600 platform. The AST2600 root complex exposes a PCIe root device at bus 80, devfn 0. This root device is implemented as a child of the PCIe RC and modeled as a host bridge PCI function (class_id = PCI_CLASS_BRIDGE_HOST). Key changes: - Add a new device type "aspeed.pcie-root-device". - Instantiate the root device as part of AspeedPCIERcState. - Initialize it during RC realize() and attach it to the root bus. - Mark the root device as non-user-creatable. - Add RC boolean property "has-rd" to control whether the Root Device is created (platforms can enable/disable it as needed). Note: Only AST2600 implements this PCIe root device. AST2700 does not provide one. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250919093017.338309-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/pci-host/aspeed_pcie.c | 56 +++++++++++++++++++++++++++++++ include/hw/pci-host/aspeed_pcie.h | 11 ++++++ 2 files changed, 67 insertions(+) diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c index c3e92ee44926a..6e563a07a3f2f 100644 --- a/hw/pci-host/aspeed_pcie.c +++ b/hw/pci-host/aspeed_pcie.c @@ -25,6 +25,44 @@ #include "hw/pci/msi.h" #include "trace.h" +/* + * PCIe Root Device + * This device exists only on AST2600. + */ + +static void aspeed_pcie_root_device_class_init(ObjectClass *klass, + const void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->desc = "ASPEED PCIe Root Device"; + k->vendor_id = PCI_VENDOR_ID_ASPEED; + k->device_id = 0x2600; + k->class_id = PCI_CLASS_BRIDGE_HOST; + k->subsystem_vendor_id = k->vendor_id; + k->subsystem_id = k->device_id; + k->revision = 0; + + /* + * PCI-facing part of the host bridge, + * not usable without the host-facing part + */ + dc->user_creatable = false; +} + +static const TypeInfo aspeed_pcie_root_device_info = { + .name = TYPE_ASPEED_PCIE_ROOT_DEVICE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(AspeedPCIERootDeviceState), + .class_init = aspeed_pcie_root_device_class_init, + .interfaces = (const InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + /* * PCIe Root Complex (RC) */ @@ -94,6 +132,18 @@ static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp) aspeed_pcie_rc_map_irq, rc, &rc->mmio, &rc->io, 0, 4, TYPE_PCIE_BUS); pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE; + + /* setup root device */ + if (rc->has_rd) { + object_initialize_child(OBJECT(rc), "root_device", &rc->root_device, + TYPE_ASPEED_PCIE_ROOT_DEVICE); + qdev_prop_set_int32(DEVICE(&rc->root_device), "addr", + PCI_DEVFN(0, 0)); + qdev_prop_set_bit(DEVICE(&rc->root_device), "multifunction", false); + if (!qdev_realize(DEVICE(&rc->root_device), BUS(pci->bus), errp)) { + return; + } + } } static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge, @@ -110,6 +160,7 @@ static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge, static const Property aspeed_pcie_rc_props[] = { DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0), + DEFINE_PROP_BOOL("has-rd", AspeedPCIERcState, has_rd, 0), }; static void aspeed_pcie_rc_class_init(ObjectClass *klass, const void *data) @@ -401,6 +452,9 @@ static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp) object_property_set_int(OBJECT(&s->rc), "bus-nr", apc->rc_bus_nr, &error_abort); + object_property_set_bool(OBJECT(&s->rc), "has-rd", + apc->rc_has_rd, + &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->rc), errp)) { return; } @@ -433,6 +487,7 @@ static void aspeed_pcie_cfg_class_init(ObjectClass *klass, const void *data) apc->reg_map = &aspeed_regmap; apc->nr_regs = 0x100 >> 2; apc->rc_bus_nr = 0x80; + apc->rc_has_rd = true; } static const TypeInfo aspeed_pcie_cfg_info = { @@ -570,6 +625,7 @@ static const TypeInfo aspeed_pcie_phy_info = { static void aspeed_pcie_register_types(void) { type_register_static(&aspeed_pcie_rc_info); + type_register_static(&aspeed_pcie_root_device_info); type_register_static(&aspeed_pcie_cfg_info); type_register_static(&aspeed_pcie_phy_info); } diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h index 850d579189eb9..fe30ac02aeee2 100644 --- a/include/hw/pci-host/aspeed_pcie.h +++ b/include/hw/pci-host/aspeed_pcie.h @@ -40,6 +40,13 @@ typedef struct AspeedPCIERegMap { AspeedPCIERcRegs rc; } AspeedPCIERegMap; +#define TYPE_ASPEED_PCIE_ROOT_DEVICE "aspeed.pcie-root-device" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERootDeviceState, ASPEED_PCIE_ROOT_DEVICE); + +struct AspeedPCIERootDeviceState { + PCIBridge parent_obj; +}; + #define TYPE_ASPEED_PCIE_RC "aspeed.pcie-rc" OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERcState, ASPEED_PCIE_RC); @@ -53,7 +60,10 @@ struct AspeedPCIERcState { uint32_t bus_nr; char name[16]; + bool has_rd; qemu_irq irq; + + AspeedPCIERootDeviceState root_device; }; /* Bridge between AHB bus and PCIe RC. */ @@ -79,6 +89,7 @@ struct AspeedPCIECfgClass { uint64_t rc_bus_nr; uint64_t nr_regs; + bool rc_has_rd; }; #define TYPE_ASPEED_PCIE_PHY "aspeed.pcie-phy" From 2af56518fa911b8370adaaabc8823bfbab303613 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 19 Sep 2025 17:30:04 +0800 Subject: [PATCH 1025/1794] hw/pci-host/aspeed: Add AST2600 PCIe Root Port and make address configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce an ASPEED PCIe Root Port and wire it under the RC. The root port is modeled as TYPE_ASPEED_PCIE_ROOT_PORT (subclass of TYPE_PCIE_ROOT_PORT). Key changes: - Add TYPE_ASPEED_PCIE_ROOT_PORT (PCIESlot-based) with vendor/device IDs and AER capability offset. - Extend AspeedPCIERcState to embed a root_port instance and a configurable rp_addr. - Add "rp-addr" property to the RC to place the root port at a specific devfn on the root bus. - Set the root port's "chassis" property to ensure a unique chassis per RC. - Extend AspeedPCIECfgClass with rc_rp_addr defaulting to PCI_DEVFN(8,0). Rationale: - AST2600 places the root port at 80:08.0 (bus 0x80, dev 8, fn 0). - AST2700 must place the root port at 00:00.0, and it supports three RCs. Each root port must therefore be uniquely identifiable; uses the PCIe "chassis" ID for that. - Providing a configurable "rp-addr" lets platforms select the correct devfn per SoC family, while the "chassis" property ensures uniqueness across multiple RC instances on AST2700. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250919093017.338309-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/pci-host/aspeed_pcie.c | 50 +++++++++++++++++++++++++++++++ include/hw/pci-host/aspeed_pcie.h | 11 +++++++ 2 files changed, 61 insertions(+) diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c index 6e563a07a3f2f..dafffbde61b9e 100644 --- a/hw/pci-host/aspeed_pcie.c +++ b/hw/pci-host/aspeed_pcie.c @@ -21,6 +21,7 @@ #include "hw/registerfields.h" #include "hw/irq.h" #include "hw/pci/pci_host.h" +#include "hw/pci/pcie_port.h" #include "hw/pci-host/aspeed_pcie.h" #include "hw/pci/msi.h" #include "trace.h" @@ -63,6 +64,32 @@ static const TypeInfo aspeed_pcie_root_device_info = { }, }; +/* + * PCIe Root Port + */ + +static void aspeed_pcie_root_port_class_init(ObjectClass *klass, + const void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass); + + dc->desc = "ASPEED PCIe Root Port"; + k->vendor_id = PCI_VENDOR_ID_ASPEED; + k->device_id = 0x1150; + dc->user_creatable = true; + + rpc->aer_offset = 0x100; +} + +static const TypeInfo aspeed_pcie_root_port_info = { + .name = TYPE_ASPEED_PCIE_ROOT_PORT, + .parent = TYPE_PCIE_ROOT_PORT, + .instance_size = sizeof(AspeedPCIERootPortState), + .class_init = aspeed_pcie_root_port_class_init, +}; + /* * PCIe Root Complex (RC) */ @@ -144,6 +171,13 @@ static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp) return; } } + + /* setup root port */ + qdev_prop_set_int32(DEVICE(&rc->root_port), "addr", rc->rp_addr); + qdev_prop_set_uint16(DEVICE(&rc->root_port), "chassis", cfg->id); + if (!qdev_realize(DEVICE(&rc->root_port), BUS(pci->bus), errp)) { + return; + } } static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge, @@ -158,9 +192,19 @@ static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge, return rc->name; } +static void aspeed_pcie_rc_instance_init(Object *obj) +{ + AspeedPCIERcState *rc = ASPEED_PCIE_RC(obj); + AspeedPCIERootPortState *root_port = &rc->root_port; + + object_initialize_child(obj, "root_port", root_port, + TYPE_ASPEED_PCIE_ROOT_PORT); +} + static const Property aspeed_pcie_rc_props[] = { DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0), DEFINE_PROP_BOOL("has-rd", AspeedPCIERcState, has_rd, 0), + DEFINE_PROP_UINT32("rp-addr", AspeedPCIERcState, rp_addr, 0), }; static void aspeed_pcie_rc_class_init(ObjectClass *klass, const void *data) @@ -183,6 +227,7 @@ static const TypeInfo aspeed_pcie_rc_info = { .name = TYPE_ASPEED_PCIE_RC, .parent = TYPE_PCIE_HOST_BRIDGE, .instance_size = sizeof(AspeedPCIERcState), + .instance_init = aspeed_pcie_rc_instance_init, .class_init = aspeed_pcie_rc_class_init, }; @@ -455,6 +500,9 @@ static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp) object_property_set_bool(OBJECT(&s->rc), "has-rd", apc->rc_has_rd, &error_abort); + object_property_set_int(OBJECT(&s->rc), "rp-addr", + apc->rc_rp_addr, + &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->rc), errp)) { return; } @@ -488,6 +536,7 @@ static void aspeed_pcie_cfg_class_init(ObjectClass *klass, const void *data) apc->nr_regs = 0x100 >> 2; apc->rc_bus_nr = 0x80; apc->rc_has_rd = true; + apc->rc_rp_addr = PCI_DEVFN(8, 0); } static const TypeInfo aspeed_pcie_cfg_info = { @@ -626,6 +675,7 @@ static void aspeed_pcie_register_types(void) { type_register_static(&aspeed_pcie_rc_info); type_register_static(&aspeed_pcie_root_device_info); + type_register_static(&aspeed_pcie_root_port_info); type_register_static(&aspeed_pcie_cfg_info); type_register_static(&aspeed_pcie_phy_info); } diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h index fe30ac02aeee2..5346c15c81015 100644 --- a/include/hw/pci-host/aspeed_pcie.h +++ b/include/hw/pci-host/aspeed_pcie.h @@ -20,6 +20,7 @@ #include "hw/sysbus.h" #include "hw/pci/pci_bridge.h" #include "hw/pci/pcie_host.h" +#include "hw/pci/pcie_port.h" #include "qom/object.h" typedef struct AspeedPCIECfgTxDesc { @@ -40,6 +41,13 @@ typedef struct AspeedPCIERegMap { AspeedPCIERcRegs rc; } AspeedPCIERegMap; +#define TYPE_ASPEED_PCIE_ROOT_PORT "aspeed.pcie-root-port" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERootPortState, ASPEED_PCIE_ROOT_PORT) + +typedef struct AspeedPCIERootPortState { + PCIESlot parent_obj; +} AspeedPCIERootPortState; + #define TYPE_ASPEED_PCIE_ROOT_DEVICE "aspeed.pcie-root-device" OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERootDeviceState, ASPEED_PCIE_ROOT_DEVICE); @@ -58,12 +66,14 @@ struct AspeedPCIERcState { MemoryRegion mmio; MemoryRegion io; + uint32_t rp_addr; uint32_t bus_nr; char name[16]; bool has_rd; qemu_irq irq; AspeedPCIERootDeviceState root_device; + AspeedPCIERootPortState root_port; }; /* Bridge between AHB bus and PCIe RC. */ @@ -87,6 +97,7 @@ struct AspeedPCIECfgClass { const AspeedPCIERegMap *reg_map; const MemoryRegionOps *reg_ops; + uint32_t rc_rp_addr; uint64_t rc_bus_nr; uint64_t nr_regs; bool rc_has_rd; From 89f949e515f1bcc4858993f9a47ac7d2656e361a Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 19 Sep 2025 17:30:05 +0800 Subject: [PATCH 1026/1794] hw/pci-host/aspeed: Add MSI support and per-RC IOMMU address space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add MSI support to the ASPEED PCIe RC/Config model and introduce a per-RC "IOMMU root" address space to correctly route MSI writes. On AST2700 all RCs use the same MSI address, and the MSI target is PCI system memory (not normal DRAM). If the MSI window were mapped into real system RAM, an endpoint's write could be observed by other RCs and spuriously trigger their interrupts. To avoid this, each RC now owns an isolated IOMMU root AddressSpace that contains a small MSI window and a DRAM alias region for normal DMA. The MSI window captures writes and asserts the RC IRQ. MSI status bits are tracked in new H2X RC_H registers (R_H2X_RC_H_MSI_EN{0,1} and R_H2X_RC_H_MSI_STS{0,1}). Clearing all status bits drops the IRQ. The default MSI address is set to 0x1e77005c and can be overridden via the msi-addr property. This keeps MSI traffic contained within each RC while preserving normal DMA to system DRAM. It enables correct MSI/MSI-X interrupt delivery when multiple RCs use the same MSI target address. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250919093017.338309-7-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/pci-host/aspeed_pcie.c | 132 ++++++++++++++++++++++++++++++ hw/pci-host/trace-events | 3 + include/hw/pci-host/aspeed_pcie.h | 10 +++ 3 files changed, 145 insertions(+) diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c index dafffbde61b9e..8be55b962fc7d 100644 --- a/hw/pci-host/aspeed_pcie.c +++ b/hw/pci-host/aspeed_pcie.c @@ -94,6 +94,8 @@ static const TypeInfo aspeed_pcie_root_port_info = { * PCIe Root Complex (RC) */ +#define ASPEED_PCIE_CFG_RC_MAX_MSI 64 + static void aspeed_pcie_rc_set_irq(void *opaque, int irq, int level) { AspeedPCIERcState *rc = (AspeedPCIERcState *) opaque; @@ -120,6 +122,58 @@ static int aspeed_pcie_rc_map_irq(PCIDevice *pci_dev, int irq_num) return irq_num % PCI_NUM_PINS; } +static void aspeed_pcie_rc_msi_notify(AspeedPCIERcState *rc, uint64_t data) +{ + AspeedPCIECfgState *cfg = + container_of(rc, AspeedPCIECfgState, rc); + uint32_t reg; + + /* Written data is the HW IRQ number */ + assert(data < ASPEED_PCIE_CFG_RC_MAX_MSI); + + reg = (data < 32) ? + cfg->rc_regs->msi_sts0_reg : cfg->rc_regs->msi_sts1_reg; + cfg->regs[reg] |= BIT(data % 32); + + trace_aspeed_pcie_rc_msi_set_irq(cfg->id, data, 1); + qemu_set_irq(rc->irq, 1); +} + +static void aspeed_pcie_rc_msi_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + AspeedPCIERcState *rc = ASPEED_PCIE_RC(opaque); + AspeedPCIECfgState *cfg = + container_of(rc, AspeedPCIECfgState, rc); + + trace_aspeed_pcie_rc_msi_notify(cfg->id, addr + rc->msi_addr, data); + aspeed_pcie_rc_msi_notify(rc, data); +} + +static const MemoryRegionOps aspeed_pcie_rc_msi_ops = { + .write = aspeed_pcie_rc_msi_write, + .read = NULL, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static AddressSpace *aspeed_pcie_rc_get_as(PCIBus *bus, void *opaque, int devfn) +{ + AspeedPCIERcState *rc = ASPEED_PCIE_RC(opaque); + return &rc->iommu_as; +} + +static const PCIIOMMUOps aspeed_pcie_rc_iommu_ops = { + .get_address_space = aspeed_pcie_rc_get_as, +}; + static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp) { PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev); @@ -130,6 +184,8 @@ static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp) SysBusDevice *sbd = SYS_BUS_DEVICE(dev); g_autofree char *ioport_window_name = NULL; g_autofree char *mmio_window_name = NULL; + g_autofree char *iommu_root_name = NULL; + g_autofree char *dram_alias_name = NULL; g_autofree char *root_bus_name = NULL; /* PCI configuration space */ @@ -160,6 +216,43 @@ static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp) &rc->io, 0, 4, TYPE_PCIE_BUS); pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE; + /* + * PCIe memory view setup + * + * Background: + * - On AST2700, all Root Complexes use the same MSI address. This MSI + * address is not normal system RAM - it is a PCI system memory address. + * If we map the MSI/MSI-X window into real system memory, a write from + * one EP can be seen by all RCs and wrongly trigger interrupts on them. + * + * Design: + * - MSI/MSI-X here is just a placeholder address so RC and EP can talk. + * We make a separate MMIO space (iommu_root) for the MSI window so the + * writes stay local to each RC. + * + * DMA: + * - EPs still need access to real system memory for DMA. We add a DRAM + * alias in the PCI space so DMA works as expected. + */ + iommu_root_name = g_strdup_printf("pcie.%d.iommu_root", cfg->id); + memory_region_init(&rc->iommu_root, OBJECT(rc), iommu_root_name, + UINT64_MAX); + address_space_init(&rc->iommu_as, &rc->iommu_root, iommu_root_name); + /* setup MSI */ + memory_region_init_io(&rc->msi_window, OBJECT(rc), + &aspeed_pcie_rc_msi_ops, rc, + "msi_window", 4); + memory_region_add_subregion(&rc->iommu_root, rc->msi_addr, + &rc->msi_window); + /* setup DRAM for DMA */ + assert(rc->dram_mr != NULL); + dram_alias_name = g_strdup_printf("pcie.%d.dram_alias", cfg->id); + memory_region_init_alias(&rc->dram_alias, OBJECT(rc), dram_alias_name, + rc->dram_mr, 0, memory_region_size(rc->dram_mr)); + memory_region_add_subregion(&rc->iommu_root, rc->dram_base, + &rc->dram_alias); + pci_setup_iommu(pci->bus, &aspeed_pcie_rc_iommu_ops, rc); + /* setup root device */ if (rc->has_rd) { object_initialize_child(OBJECT(rc), "root_device", &rc->root_device, @@ -205,6 +298,10 @@ static const Property aspeed_pcie_rc_props[] = { DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0), DEFINE_PROP_BOOL("has-rd", AspeedPCIERcState, has_rd, 0), DEFINE_PROP_UINT32("rp-addr", AspeedPCIERcState, rp_addr, 0), + DEFINE_PROP_UINT32("msi-addr", AspeedPCIERcState, msi_addr, 0), + DEFINE_PROP_UINT64("dram-base", AspeedPCIERcState, dram_base, 0), + DEFINE_PROP_LINK("dram", AspeedPCIERcState, dram_mr, TYPE_MEMORY_REGION, + MemoryRegion *), }; static void aspeed_pcie_rc_class_init(ObjectClass *klass, const void *data) @@ -265,6 +362,10 @@ REG32(H2X_RC_H_INT_STS, 0xC8) SHARED_FIELD(H2X_RC_INT_INTDONE, 4, 1) SHARED_FIELD(H2X_RC_INT_INTX, 0, 4) REG32(H2X_RC_H_RDATA, 0xCC) +REG32(H2X_RC_H_MSI_EN0, 0xE0) +REG32(H2X_RC_H_MSI_EN1, 0xE4) +REG32(H2X_RC_H_MSI_STS0, 0xE8) +REG32(H2X_RC_H_MSI_STS1, 0xEC) #define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */ #define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */ @@ -278,6 +379,8 @@ static const AspeedPCIERegMap aspeed_regmap = { .rc = { .int_en_reg = R_H2X_RC_H_INT_EN, .int_sts_reg = R_H2X_RC_H_INT_STS, + .msi_sts0_reg = R_H2X_RC_H_MSI_STS0, + .msi_sts1_reg = R_H2X_RC_H_MSI_STS1, }, }; @@ -447,6 +550,29 @@ static void aspeed_pcie_cfg_write(void *opaque, hwaddr addr, uint64_t data, } s->regs[reg] &= ~data | H2X_RC_INT_INTX_MASK; break; + /* + * These status registers are used for notify sources ISR are executed. + * If one source ISR is executed, it will clear one bit. + * If it clear all bits, it means to initialize this register status + * rather than sources ISR are executed. + */ + case R_H2X_RC_H_MSI_STS0: + case R_H2X_RC_H_MSI_STS1: + if (data == 0) { + return ; + } + + s->regs[reg] &= ~data; + if (data == 0xffffffff) { + return; + } + + if (!s->regs[R_H2X_RC_H_MSI_STS0] && + !s->regs[R_H2X_RC_H_MSI_STS1]) { + trace_aspeed_pcie_rc_msi_clear_irq(s->id, 0); + qemu_set_irq(s->rc.irq, 0); + } + break; default: s->regs[reg] = data; break; @@ -468,6 +594,8 @@ static void aspeed_pcie_cfg_instance_init(Object *obj) AspeedPCIECfgState *s = ASPEED_PCIE_CFG(obj); object_initialize_child(obj, "rc", &s->rc, TYPE_ASPEED_PCIE_RC); + object_property_add_alias(obj, "dram", OBJECT(&s->rc), "dram"); + object_property_add_alias(obj, "dram-base", OBJECT(&s->rc), "dram-base"); return; } @@ -503,6 +631,9 @@ static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp) object_property_set_int(OBJECT(&s->rc), "rp-addr", apc->rc_rp_addr, &error_abort); + object_property_set_int(OBJECT(&s->rc), "msi-addr", + apc->rc_msi_addr, + &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->rc), errp)) { return; } @@ -534,6 +665,7 @@ static void aspeed_pcie_cfg_class_init(ObjectClass *klass, const void *data) apc->reg_ops = &aspeed_pcie_cfg_ops; apc->reg_map = &aspeed_regmap; apc->nr_regs = 0x100 >> 2; + apc->rc_msi_addr = 0x1e77005C; apc->rc_bus_nr = 0x80; apc->rc_has_rd = true; apc->rc_rp_addr = PCI_DEVFN(8, 0); diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events index 2584ea56e2cc0..a6fd88c2c4667 100644 --- a/hw/pci-host/trace-events +++ b/hw/pci-host/trace-events @@ -2,6 +2,9 @@ # aspeed_pcie.c aspeed_pcie_rc_intx_set_irq(uint32_t id, int num, int level) "%d: num %d set IRQ leve %d" +aspeed_pcie_rc_msi_notify(uint32_t id, uint64_t addr, uint64_t data) "%d: 0x%" PRIx64 " data 0x%" PRIx64 +aspeed_pcie_rc_msi_set_irq(uint32_t id, uint64_t unm, int level) "%d: num 0x%" PRIx64 " set IRQ level %d" +aspeed_pcie_rc_msi_clear_irq(uint32_t id, int level) "%d: clear IRQ level %d" aspeed_pcie_cfg_read(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32 aspeed_pcie_cfg_write(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32 aspeed_pcie_cfg_rw(uint32_t id, const char *dir, uint8_t bus, uint8_t devfn, uint64_t addr, uint64_t data) "%d: %s bus:0x%x devfn:0x%x addr 0x%" PRIx64 " data 0x%" PRIx64 diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h index 5346c15c81015..5e60cba07b66b 100644 --- a/include/hw/pci-host/aspeed_pcie.h +++ b/include/hw/pci-host/aspeed_pcie.h @@ -35,6 +35,8 @@ typedef struct AspeedPCIECfgTxDesc { typedef struct AspeedPCIERcRegs { uint32_t int_en_reg; uint32_t int_sts_reg; + uint32_t msi_sts0_reg; + uint32_t msi_sts1_reg; } AspeedPCIERcRegs; typedef struct AspeedPCIERegMap { @@ -61,11 +63,18 @@ OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERcState, ASPEED_PCIE_RC); struct AspeedPCIERcState { PCIExpressHost parent_obj; + MemoryRegion iommu_root; + AddressSpace iommu_as; + MemoryRegion dram_alias; + MemoryRegion *dram_mr; MemoryRegion mmio_window; + MemoryRegion msi_window; MemoryRegion io_window; MemoryRegion mmio; MemoryRegion io; + uint64_t dram_base; + uint32_t msi_addr; uint32_t rp_addr; uint32_t bus_nr; char name[16]; @@ -97,6 +106,7 @@ struct AspeedPCIECfgClass { const AspeedPCIERegMap *reg_map; const MemoryRegionOps *reg_ops; + uint32_t rc_msi_addr; uint32_t rc_rp_addr; uint64_t rc_bus_nr; uint64_t nr_regs; From 575846c056a320b3008436fc8c11d10616677722 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 19 Sep 2025 17:30:06 +0800 Subject: [PATCH 1027/1794] hw/arm/aspeed: Wire up PCIe devices in SoC model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add PCIe controller and PHY instances to the Aspeed SoC state and device enum. This prepares the SoC model to host PCIe Root Complexes and their associated PHYs. Although the AST2600 supports only a single Root Complex, the AST2700 provides three Root Complexes. For this reason, the model defines arrays of three PCIe config/PHY objects and enumerates three PCIe device IDs so that both SoCs can be represented consistently. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250919093017.338309-8-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- include/hw/arm/aspeed_soc.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 217ef0eafd6c7..79fe353f83b07 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -37,6 +37,7 @@ #include "qom/object.h" #include "hw/misc/aspeed_lpc.h" #include "hw/misc/unimp.h" +#include "hw/pci-host/aspeed_pcie.h" #include "hw/misc/aspeed_peci.h" #include "hw/fsi/aspeed_apb2opb.h" #include "hw/char/serial-mm.h" @@ -49,6 +50,7 @@ #define ASPEED_MACS_NUM 4 #define ASPEED_UARTS_NUM 13 #define ASPEED_JTAG_NUM 2 +#define ASPEED_PCIE_NUM 3 struct AspeedSoCState { DeviceState parent; @@ -87,6 +89,8 @@ struct AspeedSoCState { AspeedSDHCIState sdhci; AspeedSDHCIState emmc; AspeedLPCState lpc; + AspeedPCIECfgState pcie[ASPEED_PCIE_NUM]; + AspeedPCIEPhyState pcie_phy[ASPEED_PCIE_NUM]; AspeedPECIState peci; SerialMM uart[ASPEED_UARTS_NUM]; Clock *sysclk; @@ -254,6 +258,15 @@ enum { ASPEED_DEV_LPC, ASPEED_DEV_IBT, ASPEED_DEV_I2C, + ASPEED_DEV_PCIE0, + ASPEED_DEV_PCIE1, + ASPEED_DEV_PCIE2, + ASPEED_DEV_PCIE_PHY0, + ASPEED_DEV_PCIE_PHY1, + ASPEED_DEV_PCIE_PHY2, + ASPEED_DEV_PCIE_MMIO0, + ASPEED_DEV_PCIE_MMIO1, + ASPEED_DEV_PCIE_MMIO2, ASPEED_DEV_PECI, ASPEED_DEV_ETH1, ASPEED_DEV_ETH2, From a498916ba292ada19cc47df80e0e9507c63356fc Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 19 Sep 2025 17:30:07 +0800 Subject: [PATCH 1028/1794] hw/arm/aspeed_ast2600: Add PCIe RC support (RC_H only) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire up the PCIe Root Complex in the AST2600 SoC model. According to the AST2600 firmware driver, only the RC_H controller is supported. RC_H uses PCIe PHY1 at 0x1e6ed200 and the PCIe config (H2X) register block at 0x1e770000. The RC_H MMIO window is mapped at 0x70000000–0x80000000. RC_L is not modeled. The RC_H interrupt is wired to IRQ 168. Only RC_H is realized and connected to the SoC interrupt controller. The SoC integration initializes PCIe PHY1, instantiates a single RC instance, wires its MMIO regions, and connects its interrupt. An alias region is added to map the RC MMIO space into the guest physical address space. This provides enough functionality for firmware and guest drivers to discover and use the AST2600 RC_H Root Complex while leaving RC_L unimplemented. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250919093017.338309-9-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast2600.c | 74 +++++++++++++++++++++++++++++++++++++ include/hw/arm/aspeed_soc.h | 1 + 2 files changed, 75 insertions(+) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index 59ffd41a4ab07..03e5df96bb4f3 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -48,11 +48,13 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = { [ASPEED_DEV_XDMA] = 0x1E6E7000, [ASPEED_DEV_ADC] = 0x1E6E9000, [ASPEED_DEV_DP] = 0x1E6EB000, + [ASPEED_DEV_PCIE_PHY1] = 0x1E6ED200, [ASPEED_DEV_SBC] = 0x1E6F2000, [ASPEED_DEV_EMMC_BC] = 0x1E6f5000, [ASPEED_DEV_VIDEO] = 0x1E700000, [ASPEED_DEV_SDHCI] = 0x1E740000, [ASPEED_DEV_EMMC] = 0x1E750000, + [ASPEED_DEV_PCIE0] = 0x1E770000, [ASPEED_DEV_GPIO] = 0x1E780000, [ASPEED_DEV_GPIO_1_8V] = 0x1E780800, [ASPEED_DEV_RTC] = 0x1E781000, @@ -79,6 +81,7 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = { [ASPEED_DEV_FSI1] = 0x1E79B000, [ASPEED_DEV_FSI2] = 0x1E79B100, [ASPEED_DEV_I3C] = 0x1E7A0000, + [ASPEED_DEV_PCIE_MMIO1] = 0x70000000, [ASPEED_DEV_SDRAM] = 0x80000000, }; @@ -127,6 +130,7 @@ static const int aspeed_soc_ast2600_irqmap[] = { [ASPEED_DEV_LPC] = 35, [ASPEED_DEV_IBT] = 143, [ASPEED_DEV_I2C] = 110, /* 110 -> 125 */ + [ASPEED_DEV_PCIE0] = 168, [ASPEED_DEV_PECI] = 38, [ASPEED_DEV_ETH1] = 2, [ASPEED_DEV_ETH2] = 3, @@ -191,6 +195,10 @@ static void aspeed_soc_ast2600_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname); object_initialize_child(obj, "i2c", &s->i2c, typename); + object_initialize_child(obj, "pcie-cfg", &s->pcie[0], TYPE_ASPEED_PCIE_CFG); + object_initialize_child(obj, "pcie-phy[*]", &s->pcie_phy[0], + TYPE_ASPEED_PCIE_PHY); + object_initialize_child(obj, "peci", &s->peci, TYPE_ASPEED_PECI); snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname); @@ -285,6 +293,67 @@ static uint64_t aspeed_calc_affinity(int cpu) return (0xf << ARM_AFF1_SHIFT) | cpu; } +/* + * PCIe Root Complex (RC) + * + * H2X register space (single block 0x00-0xFF): + * 0x00-0x7F : shared by RC_L (PCIe0) and RC_H (PCIe1) + * 0x80-0xBF : RC_L only + * 0xC0-0xFF : RC_H only + * + * Model scope / limitations: + * - Firmware supports RC_H only; this QEMU model does not support RC_L. + * - RC_H uses PHY1 and the MMIO window [0x70000000, 0x80000000] + * (aka MMIO1). + * + * Indexing convention (this model): + * - Expose a single logical instance at index 0. + * - pcie[0] -> hardware RC_H (PCIe1) + * - phy[0] -> hardware PHY1 + * - mmio.0 -> guest address range MMIO1: 0x70000000-0x80000000 + * - RC_L / PCIe0 is not created and mapped. + */ +static bool aspeed_soc_ast2600_pcie_realize(DeviceState *dev, Error **errp) +{ + Aspeed2600SoCState *a = ASPEED2600_SOC(dev); + AspeedSoCState *s = ASPEED_SOC(dev); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + MemoryRegion *mmio_mr = NULL; + qemu_irq irq; + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie_phy[0]), errp)) { + return false; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->pcie_phy[0]), 0, + sc->memmap[ASPEED_DEV_PCIE_PHY1]); + + object_property_set_int(OBJECT(&s->pcie[0]), "dram-base", + sc->memmap[ASPEED_DEV_SDRAM], + &error_abort); + object_property_set_link(OBJECT(&s->pcie[0]), "dram", OBJECT(s->dram_mr), + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie[0]), errp)) { + return false; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->pcie[0]), 0, + sc->memmap[ASPEED_DEV_PCIE0]); + + irq = qdev_get_gpio_in(DEVICE(&a->a7mpcore), + sc->irqmap[ASPEED_DEV_PCIE0]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie[0].rc), 0, irq); + + mmio_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->pcie[0].rc), 1); + memory_region_init_alias(&s->pcie_mmio_alias[0], OBJECT(&s->pcie[0].rc), + "aspeed.pcie-mmio", mmio_mr, + sc->memmap[ASPEED_DEV_PCIE_MMIO1], + 0x10000000); + memory_region_add_subregion(s->memory, + sc->memmap[ASPEED_DEV_PCIE_MMIO1], + &s->pcie_mmio_alias[0]); + + return true; +} + static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) { int i; @@ -438,6 +507,11 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->peci), 0, aspeed_soc_get_irq(s, ASPEED_DEV_PECI)); + /* PCIe Root Complex (RC) */ + if (!aspeed_soc_ast2600_pcie_realize(dev, errp)) { + return; + } + /* FMC, The number of CS is set at the board level */ object_property_set_link(OBJECT(&s->fmc), "dram", OBJECT(s->dram_mr), &error_abort); diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 79fe353f83b07..a0cf43377507f 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -62,6 +62,7 @@ struct AspeedSoCState { MemoryRegion spi_boot_container; MemoryRegion spi_boot; MemoryRegion vbootrom; + MemoryRegion pcie_mmio_alias[ASPEED_PCIE_NUM]; AddressSpace dram_as; AspeedRtcState rtc; AspeedTimerCtrlState timerctrl; From ed2df979ab229feaa81327ad4941383c024116e6 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 19 Sep 2025 17:30:08 +0800 Subject: [PATCH 1029/1794] hw/pci-host/aspeed: Add AST2700 PCIe PHY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a PCIe Host Controller PHY model for AST2700. This adds an AST2700 specific PHY type (TYPE_ASPEED_2700_PCIE_PHY) with a 0x800 byte register space and link-status bits compatible with the firmware’s expectations. AST2700 provides three PCIe RCs; PCIe0 and PCIe1 are GEN4, PCIe2 is GEN2. The PHY exposes: PEHR_2700_LINK_GEN2 at 0x344, bit 18 indicates GEN2 link up PEHR_2700_LINK_GEN4 at 0x358, bit 8 indicates GEN4 link up In real hardware these GEN2/GEN4 link bits are mutually exclusive. QEMU does not model GEN2 vs GEN4 signaling differences, so the reset handler sets both bits to 1. This keeps the model simple and lets firmware see the link as up; firmware will read the appropriate register per RC port to infer the intended mode. The header gains TYPE_ASPEED_2700_PCIE_PHY; the new class derives from TYPE_ASPEED_PCIE_PHY, sets nr_regs to 0x800 >> 2, and installs an AST2700 reset routine that programs the class code (0x06040011) and the GEN2/GEN4 status bits. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250919093017.338309-10-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/pci-host/aspeed_pcie.c | 39 +++++++++++++++++++++++++++++++ include/hw/pci-host/aspeed_pcie.h | 1 + 2 files changed, 40 insertions(+) diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c index 8be55b962fc7d..788160d5325cf 100644 --- a/hw/pci-host/aspeed_pcie.c +++ b/hw/pci-host/aspeed_pcie.c @@ -696,6 +696,12 @@ REG32(PEHR_PROTECT, 0x7C) REG32(PEHR_LINK, 0xC0) FIELD(PEHR_LINK, STS, 5, 1) +/* AST2700 */ +REG32(PEHR_2700_LINK_GEN2, 0x344) + FIELD(PEHR_2700_LINK_GEN2, STS, 18, 1) +REG32(PEHR_2700_LINK_GEN4, 0x358) + FIELD(PEHR_2700_LINK_GEN4, STS, 8, 1) + #define ASPEED_PCIE_PHY_UNLOCK 0xA8 static uint64_t aspeed_pcie_phy_read(void *opaque, hwaddr addr, @@ -803,6 +809,38 @@ static const TypeInfo aspeed_pcie_phy_info = { .class_size = sizeof(AspeedPCIEPhyClass), }; +static void aspeed_2700_pcie_phy_reset(DeviceState *dev) +{ + AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev); + AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_GET_CLASS(s); + + memset(s->regs, 0, apc->nr_regs << 2); + + s->regs[R_PEHR_ID] = + (0x1150 << R_PEHR_ID_DEV_SHIFT) | PCI_VENDOR_ID_ASPEED; + s->regs[R_PEHR_CLASS_CODE] = 0x06040011; + s->regs[R_PEHR_2700_LINK_GEN2] = R_PEHR_2700_LINK_GEN2_STS_MASK; + s->regs[R_PEHR_2700_LINK_GEN4] = R_PEHR_2700_LINK_GEN4_STS_MASK; +} + +static void aspeed_2700_pcie_phy_class_init(ObjectClass *klass, + const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_CLASS(klass); + + dc->desc = "ASPEED AST2700 PCIe Phy"; + device_class_set_legacy_reset(dc, aspeed_2700_pcie_phy_reset); + + apc->nr_regs = 0x800 >> 2; +} + +static const TypeInfo aspeed_2700_pcie_phy_info = { + .name = TYPE_ASPEED_2700_PCIE_PHY, + .parent = TYPE_ASPEED_PCIE_PHY, + .class_init = aspeed_2700_pcie_phy_class_init, +}; + static void aspeed_pcie_register_types(void) { type_register_static(&aspeed_pcie_rc_info); @@ -810,6 +848,7 @@ static void aspeed_pcie_register_types(void) type_register_static(&aspeed_pcie_root_port_info); type_register_static(&aspeed_pcie_cfg_info); type_register_static(&aspeed_pcie_phy_info); + type_register_static(&aspeed_2700_pcie_phy_info); } type_init(aspeed_pcie_register_types); diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h index 5e60cba07b66b..5806505f30efe 100644 --- a/include/hw/pci-host/aspeed_pcie.h +++ b/include/hw/pci-host/aspeed_pcie.h @@ -114,6 +114,7 @@ struct AspeedPCIECfgClass { }; #define TYPE_ASPEED_PCIE_PHY "aspeed.pcie-phy" +#define TYPE_ASPEED_2700_PCIE_PHY TYPE_ASPEED_PCIE_PHY "-ast2700" OBJECT_DECLARE_TYPE(AspeedPCIEPhyState, AspeedPCIEPhyClass, ASPEED_PCIE_PHY); struct AspeedPCIEPhyState { From ba6fb09048198a2952d731cd063f4307b137e7ec Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 19 Sep 2025 17:30:09 +0800 Subject: [PATCH 1030/1794] hw/pci-host/aspeed: Add AST2700 PCIe config with dedicated H2X blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce PCIe config (H2X) support for the AST2700 SoC. Unlike the AST2600, the AST2700 provides three independent Root Complexes, each with its own H2X (AHB to PCIe bridge) register block of size 0x100. All RCs use the same MSI address (0x000000F0). The H2X block includes two different access paths: 1. CFGI (internal bridge): used to access the host bridge itself, always with BDF=0. The AST2700 controller simplifies the design by exposing only one register (H2X_CFGI_TLP) with fields for ADDR[15:0], BEN[19:16], and WR[20]. This is not a full TLP descriptor as in the external case. For QEMU readability and code reuse, the model converts H2X_CFGI_TLP into a standard TLP TX descriptor with BDF forced to 0 and then calls the existing helpers aspeed_pcie_cfg_readwrite() and aspeed_pcie_cfg_translate_write(). 2. CFGE (external EP access): used to access external endpoints. The AST2700 design provides H2X_CFGE_TLP1 and a small FIFO at H2X_CFGE_TLPN. For reads, TX DESC0 is stored in TLP1 and DESC1/DESC2 in TLPN FIFO slots. For writes, TX DESC0 is stored in TLP1, DESC1/DESC2 in TLPN FIFO[0..1], and TX write data in TLPN FIFO[2]. The implementation extends AspeedPCIECfgState with a small FIFO and index, wires up new register definitions for AST2700, and adds a specific ops table and class (TYPE_ASPEED_2700_PCIE_CFG). The reset handler clears the FIFO state. Interrupt and MSI status registers are also supported. This provides enough modeling for firmware and drivers to use any of the three PCIe RCs on AST2700 with their own dedicated H2X config window, while reusing existing TLP decode helpers in QEMU. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250919093017.338309-11-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/pci-host/aspeed_pcie.c | 158 ++++++++++++++++++++++++++++++ include/hw/pci-host/aspeed_pcie.h | 3 + 2 files changed, 161 insertions(+) diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c index 788160d5325cf..a757fd7ec850f 100644 --- a/hw/pci-host/aspeed_pcie.c +++ b/hw/pci-host/aspeed_pcie.c @@ -338,6 +338,11 @@ static const TypeInfo aspeed_pcie_rc_info = { * - Registers 0x00 - 0x7F are shared by both PCIe0 (rc_l) and PCIe1 (rc_h). * - Registers 0x80 - 0xBF are specific to PCIe0. * - Registers 0xC0 - 0xFF are specific to PCIe1. + * + * On the AST2700: + * - The register range 0x00 - 0xFF is assigned to a single PCIe configuration. + * - There are three PCIe Root Complexes (RCs), each with its own dedicated H2X + * register set of size 0x100 (covering offsets 0x00 to 0xFF). */ /* AST2600 */ @@ -367,6 +372,31 @@ REG32(H2X_RC_H_MSI_EN1, 0xE4) REG32(H2X_RC_H_MSI_STS0, 0xE8) REG32(H2X_RC_H_MSI_STS1, 0xEC) +/* AST2700 */ +REG32(H2X_CFGE_INT_STS, 0x08) + FIELD(H2X_CFGE_INT_STS, TX_IDEL, 0, 1) + FIELD(H2X_CFGE_INT_STS, RX_BUSY, 1, 1) +REG32(H2X_CFGI_TLP, 0x20) + FIELD(H2X_CFGI_TLP, ADDR, 0, 16) + FIELD(H2X_CFGI_TLP, BEN, 16, 4) + FIELD(H2X_CFGI_TLP, WR, 20, 1) +REG32(H2X_CFGI_WDATA, 0x24) +REG32(H2X_CFGI_CTRL, 0x28) + FIELD(H2X_CFGI_CTRL, FIRE, 0, 1) +REG32(H2X_CFGI_RDATA, 0x2C) +REG32(H2X_CFGE_TLP1, 0x30) +REG32(H2X_CFGE_TLPN, 0x34) +REG32(H2X_CFGE_CTRL, 0x38) + FIELD(H2X_CFGE_CTRL, FIRE, 0, 1) +REG32(H2X_CFGE_RDATA, 0x3C) +REG32(H2X_INT_EN, 0x40) +REG32(H2X_INT_STS, 0x48) + FIELD(H2X_INT_STS, INTX, 0, 4) +REG32(H2X_MSI_EN0, 0x50) +REG32(H2X_MSI_EN1, 0x54) +REG32(H2X_MSI_STS0, 0x58) +REG32(H2X_MSI_STS1, 0x5C) + #define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */ #define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */ #define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */ @@ -384,6 +414,15 @@ static const AspeedPCIERegMap aspeed_regmap = { }, }; +static const AspeedPCIERegMap aspeed_2700_regmap = { + .rc = { + .int_en_reg = R_H2X_INT_EN, + .int_sts_reg = R_H2X_INT_STS, + .msi_sts0_reg = R_H2X_MSI_STS0, + .msi_sts1_reg = R_H2X_MSI_STS1, + }, +}; + static uint64_t aspeed_pcie_cfg_read(void *opaque, hwaddr addr, unsigned int size) { @@ -606,6 +645,8 @@ static void aspeed_pcie_cfg_reset(DeviceState *dev) AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s); memset(s->regs, 0, apc->nr_regs << 2); + memset(s->tlpn_fifo, 0, sizeof(s->tlpn_fifo)); + s->tlpn_idx = 0; } static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp) @@ -680,6 +721,122 @@ static const TypeInfo aspeed_pcie_cfg_info = { .class_size = sizeof(AspeedPCIECfgClass), }; +static void aspeed_2700_pcie_cfg_write(void *opaque, hwaddr addr, + uint64_t data, unsigned int size) +{ + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque); + AspeedPCIECfgTxDesc desc; + uint32_t reg = addr >> 2; + + trace_aspeed_pcie_cfg_write(s->id, addr, data); + + switch (reg) { + case R_H2X_CFGE_INT_STS: + if (data & R_H2X_CFGE_INT_STS_TX_IDEL_MASK) { + s->regs[R_H2X_CFGE_INT_STS] &= ~R_H2X_CFGE_INT_STS_TX_IDEL_MASK; + } + + if (data & R_H2X_CFGE_INT_STS_RX_BUSY_MASK) { + s->regs[R_H2X_CFGE_INT_STS] &= ~R_H2X_CFGE_INT_STS_RX_BUSY_MASK; + } + break; + case R_H2X_CFGI_CTRL: + if (data & R_H2X_CFGI_CTRL_FIRE_MASK) { + /* + * Internal access to bridge + * Type and BDF are 0 + */ + desc.desc0 = 0x04000001 | + (ARRAY_FIELD_EX32(s->regs, H2X_CFGI_TLP, WR) << 30); + desc.desc1 = 0x00401000 | + ARRAY_FIELD_EX32(s->regs, H2X_CFGI_TLP, BEN); + desc.desc2 = 0x00000000 | + ARRAY_FIELD_EX32(s->regs, H2X_CFGI_TLP, ADDR); + desc.wdata = s->regs[R_H2X_CFGI_WDATA]; + desc.rdata_reg = R_H2X_CFGI_RDATA; + aspeed_pcie_cfg_readwrite(s, &desc); + } + break; + case R_H2X_CFGE_TLPN: + s->tlpn_fifo[s->tlpn_idx] = data; + s->tlpn_idx = (s->tlpn_idx + 1) % ARRAY_SIZE(s->tlpn_fifo); + break; + case R_H2X_CFGE_CTRL: + if (data & R_H2X_CFGE_CTRL_FIRE_MASK) { + desc.desc0 = s->regs[R_H2X_CFGE_TLP1]; + desc.desc1 = s->tlpn_fifo[0]; + desc.desc2 = s->tlpn_fifo[1]; + desc.wdata = s->tlpn_fifo[2]; + desc.rdata_reg = R_H2X_CFGE_RDATA; + aspeed_pcie_cfg_readwrite(s, &desc); + s->regs[R_H2X_CFGE_INT_STS] |= R_H2X_CFGE_INT_STS_TX_IDEL_MASK; + s->regs[R_H2X_CFGE_INT_STS] |= R_H2X_CFGE_INT_STS_RX_BUSY_MASK; + s->tlpn_idx = 0; + } + break; + + case R_H2X_INT_STS: + s->regs[reg] &= ~data | R_H2X_INT_STS_INTX_MASK; + break; + /* + * These status registers are used for notify sources ISR are executed. + * If one source ISR is executed, it will clear one bit. + * If it clear all bits, it means to initialize this register status + * rather than sources ISR are executed. + */ + case R_H2X_MSI_STS0: + case R_H2X_MSI_STS1: + if (data == 0) { + return ; + } + + s->regs[reg] &= ~data; + if (data == 0xffffffff) { + return; + } + + if (!s->regs[R_H2X_MSI_STS0] && + !s->regs[R_H2X_MSI_STS1]) { + trace_aspeed_pcie_rc_msi_clear_irq(s->id, 0); + qemu_set_irq(s->rc.irq, 0); + } + break; + default: + s->regs[reg] = data; + break; + } +} + +static const MemoryRegionOps aspeed_2700_pcie_cfg_ops = { + .read = aspeed_pcie_cfg_read, + .write = aspeed_2700_pcie_cfg_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void aspeed_2700_pcie_cfg_class_init(ObjectClass *klass, + const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_CLASS(klass); + + dc->desc = "ASPEED 2700 PCIe Config"; + apc->reg_ops = &aspeed_2700_pcie_cfg_ops; + apc->reg_map = &aspeed_2700_regmap; + apc->nr_regs = 0x100 >> 2; + apc->rc_msi_addr = 0x000000F0; + apc->rc_bus_nr = 0; +} + +static const TypeInfo aspeed_2700_pcie_cfg_info = { + .name = TYPE_ASPEED_2700_PCIE_CFG, + .parent = TYPE_ASPEED_PCIE_CFG, + .class_init = aspeed_2700_pcie_cfg_class_init, +}; + /* * PCIe PHY * @@ -847,6 +1004,7 @@ static void aspeed_pcie_register_types(void) type_register_static(&aspeed_pcie_root_device_info); type_register_static(&aspeed_pcie_root_port_info); type_register_static(&aspeed_pcie_cfg_info); + type_register_static(&aspeed_2700_pcie_cfg_info); type_register_static(&aspeed_pcie_phy_info); type_register_static(&aspeed_2700_pcie_phy_info); } diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h index 5806505f30efe..be53ea96b9078 100644 --- a/include/hw/pci-host/aspeed_pcie.h +++ b/include/hw/pci-host/aspeed_pcie.h @@ -87,6 +87,7 @@ struct AspeedPCIERcState { /* Bridge between AHB bus and PCIe RC. */ #define TYPE_ASPEED_PCIE_CFG "aspeed.pcie-cfg" +#define TYPE_ASPEED_2700_PCIE_CFG TYPE_ASPEED_PCIE_CFG "-ast2700" OBJECT_DECLARE_TYPE(AspeedPCIECfgState, AspeedPCIECfgClass, ASPEED_PCIE_CFG); struct AspeedPCIECfgState { @@ -98,6 +99,8 @@ struct AspeedPCIECfgState { const AspeedPCIERcRegs *rc_regs; AspeedPCIERcState rc; + uint32_t tlpn_fifo[3]; + uint32_t tlpn_idx; }; struct AspeedPCIECfgClass { From ac30597ee859ecaa4a87688fbd722bb5bfe80bd7 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 19 Sep 2025 17:30:10 +0800 Subject: [PATCH 1031/1794] hw/pci-host/aspeed: Disable Root Device and place Root Port at 00:00.0 to AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 does not implement a PCIe Root Device; each RC exposes a single PCIe Root Port at devfn 0:0.0. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250919093017.338309-12-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/pci-host/aspeed_pcie.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c index a757fd7ec850f..f7593444fc42f 100644 --- a/hw/pci-host/aspeed_pcie.c +++ b/hw/pci-host/aspeed_pcie.c @@ -829,6 +829,8 @@ static void aspeed_2700_pcie_cfg_class_init(ObjectClass *klass, apc->nr_regs = 0x100 >> 2; apc->rc_msi_addr = 0x000000F0; apc->rc_bus_nr = 0; + apc->rc_has_rd = false; + apc->rc_rp_addr = PCI_DEVFN(0, 0); } static const TypeInfo aspeed_2700_pcie_cfg_info = { From 65996e84089459a4b52514a928988816e5c0bae0 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 19 Sep 2025 17:30:11 +0800 Subject: [PATCH 1032/1794] hw/arm/aspeed_ast27x0: Introduce 3 PCIe RCs for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add PCIe Root Complex support to the AST2700 SoC model. The AST2700 A1 silicon revision provides three PCIe Root Complexes: PCIe0 with its PHY at 0x12C15000, config (H2X) block at 0x120E0000, MMIO window at 0x60000000, and GIC IRQ 56. PCIe1 with its PHY at 0x12C15800, config (H2X) block at 0x120F0000, MMIO window at 0x80000000, and GIC IRQ 57. PCIe2 with its PHY at 0x14C1C000, config (H2X) block at 0x140D0000, MMIO window at 0xA0000000, and IRQ routed through INTC4 bit 31 mapped to GIC IRQ 196. Each RC instantiates a PHY device, a PCIe config (H2X) bridge, and an MMIO alias region. The per-RC MMIO alias size is 0x20000000. The AST2700 A0 silicon revision does not support PCIe Root Complexes, so pcie_num is set to 0 in that variant. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250919093017.338309-13-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 74 +++++++++++++++++++++++++++++++++++++ include/hw/arm/aspeed_soc.h | 1 + 2 files changed, 75 insertions(+) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 6aa3841b6911f..853339119ff65 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -38,6 +38,8 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_EHCI2] = 0x12063000, [ASPEED_DEV_HACE] = 0x12070000, [ASPEED_DEV_EMMC] = 0x12090000, + [ASPEED_DEV_PCIE0] = 0x120E0000, + [ASPEED_DEV_PCIE1] = 0x120F0000, [ASPEED_DEV_INTC] = 0x12100000, [ASPEED_GIC_DIST] = 0x12200000, [ASPEED_GIC_REDIST] = 0x12280000, @@ -45,6 +47,8 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_SCU] = 0x12C02000, [ASPEED_DEV_RTC] = 0x12C0F000, [ASPEED_DEV_TIMER1] = 0x12C10000, + [ASPEED_DEV_PCIE_PHY0] = 0x12C15000, + [ASPEED_DEV_PCIE_PHY1] = 0x12C15800, [ASPEED_DEV_SLI] = 0x12C17000, [ASPEED_DEV_UART4] = 0x12C1A000, [ASPEED_DEV_IOMEM1] = 0x14000000, @@ -59,6 +63,7 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_ETH2] = 0x14060000, [ASPEED_DEV_ETH3] = 0x14070000, [ASPEED_DEV_SDHCI] = 0x14080000, + [ASPEED_DEV_PCIE2] = 0x140D0000, [ASPEED_DEV_EHCI3] = 0x14121000, [ASPEED_DEV_EHCI4] = 0x14123000, [ASPEED_DEV_ADC] = 0x14C00000, @@ -66,6 +71,7 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_GPIO] = 0x14C0B000, [ASPEED_DEV_I2C] = 0x14C0F000, [ASPEED_DEV_INTCIO] = 0x14C18000, + [ASPEED_DEV_PCIE_PHY2] = 0x14C1C000, [ASPEED_DEV_SLIIO] = 0x14C1E000, [ASPEED_DEV_VUART] = 0x14C30000, [ASPEED_DEV_UART0] = 0x14C33000, @@ -81,6 +87,9 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_UART11] = 0x14C33A00, [ASPEED_DEV_UART12] = 0x14C33B00, [ASPEED_DEV_WDT] = 0x14C37000, + [ASPEED_DEV_PCIE_MMIO0] = 0x60000000, + [ASPEED_DEV_PCIE_MMIO1] = 0x80000000, + [ASPEED_DEV_PCIE_MMIO2] = 0xA0000000, [ASPEED_DEV_SPI_BOOT] = 0x100000000, [ASPEED_DEV_LTPI] = 0x300000000, [ASPEED_DEV_SDRAM] = 0x400000000, @@ -156,6 +165,8 @@ static const int aspeed_soc_ast2700a1_irqmap[] = { [ASPEED_DEV_DP] = 28, [ASPEED_DEV_EHCI1] = 33, [ASPEED_DEV_EHCI2] = 37, + [ASPEED_DEV_PCIE0] = 56, + [ASPEED_DEV_PCIE1] = 57, [ASPEED_DEV_LPC] = 192, [ASPEED_DEV_IBT] = 192, [ASPEED_DEV_KCS] = 192, @@ -166,6 +177,7 @@ static const int aspeed_soc_ast2700a1_irqmap[] = { [ASPEED_DEV_WDT] = 195, [ASPEED_DEV_PWM] = 195, [ASPEED_DEV_I3C] = 195, + [ASPEED_DEV_PCIE2] = 196, [ASPEED_DEV_UART0] = 196, [ASPEED_DEV_UART1] = 196, [ASPEED_DEV_UART2] = 196, @@ -233,6 +245,7 @@ static const int ast2700_gic132_gic196_intcmap[] = { [ASPEED_DEV_UART12] = 18, [ASPEED_DEV_EHCI3] = 28, [ASPEED_DEV_EHCI4] = 29, + [ASPEED_DEV_PCIE2] = 31, }; /* GICINT 133 */ @@ -519,6 +532,17 @@ static void aspeed_soc_ast2700_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.hace-%s", socname); object_initialize_child(obj, "hace", &s->hace, typename); + + for (i = 0; i < sc->pcie_num; i++) { + snprintf(typename, sizeof(typename), "aspeed.pcie-phy-%s", socname); + object_initialize_child(obj, "pcie-phy[*]", &s->pcie_phy[i], typename); + object_property_set_int(OBJECT(&s->pcie_phy[i]), "id", i, &error_abort); + + snprintf(typename, sizeof(typename), "aspeed.pcie-cfg-%s", socname); + object_initialize_child(obj, "pcie-cfg[*]", &s->pcie[i], typename); + object_property_set_int(OBJECT(&s->pcie[i]), "id", i, &error_abort); + } + object_initialize_child(obj, "dpmcu", &s->dpmcu, TYPE_UNIMPLEMENTED_DEVICE); object_initialize_child(obj, "ltpi", &s->ltpi, @@ -610,6 +634,49 @@ static bool aspeed_soc_ast2700_gic_realize(DeviceState *dev, Error **errp) return true; } +static bool aspeed_soc_ast2700_pcie_realize(DeviceState *dev, Error **errp) +{ + AspeedSoCState *s = ASPEED_SOC(dev); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + MemoryRegion *mmio_mr = NULL; + char name[64]; + qemu_irq irq; + int i; + + for (i = 0; i < sc->pcie_num; i++) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie_phy[i]), errp)) { + return false; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->pcie_phy[i]), 0, + sc->memmap[ASPEED_DEV_PCIE_PHY0 + i]); + + object_property_set_int(OBJECT(&s->pcie[i]), "dram-base", + sc->memmap[ASPEED_DEV_SDRAM], + &error_abort); + object_property_set_link(OBJECT(&s->pcie[i]), "dram", + OBJECT(s->dram_mr), &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie[i]), errp)) { + return false; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->pcie[i]), 0, + sc->memmap[ASPEED_DEV_PCIE0 + i]); + irq = aspeed_soc_get_irq(s, ASPEED_DEV_PCIE0 + i); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie[i].rc), 0, irq); + + mmio_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->pcie[i].rc), 1); + snprintf(name, sizeof(name), "aspeed.pcie-mmio.%d", i); + memory_region_init_alias(&s->pcie_mmio_alias[i], OBJECT(&s->pcie[i].rc), + name, mmio_mr, + sc->memmap[ASPEED_DEV_PCIE_MMIO0 + i], + 0x20000000); + memory_region_add_subregion(s->memory, + sc->memmap[ASPEED_DEV_PCIE_MMIO0 + i], + &s->pcie_mmio_alias[i]); + } + + return true; +} + static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) { int i; @@ -936,6 +1003,11 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); + /* PCIe Root Complex (RC) */ + if (!aspeed_soc_ast2700_pcie_realize(dev, errp)) { + return; + } + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->dpmcu), "aspeed.dpmcu", sc->memmap[ASPEED_DEV_DPMCU], @@ -974,6 +1046,7 @@ static void aspeed_soc_ast2700a0_class_init(ObjectClass *oc, const void *data) sc->valid_cpu_types = valid_cpu_types; sc->silicon_rev = AST2700_A0_SILICON_REV; sc->sram_size = 0x20000; + sc->pcie_num = 0; sc->spis_num = 3; sc->ehcis_num = 2; sc->wdts_num = 8; @@ -1002,6 +1075,7 @@ static void aspeed_soc_ast2700a1_class_init(ObjectClass *oc, const void *data) sc->valid_cpu_types = valid_cpu_types; sc->silicon_rev = AST2700_A1_SILICON_REV; sc->sram_size = 0x20000; + sc->pcie_num = 3; sc->spis_num = 3; sc->ehcis_num = 4; sc->wdts_num = 8; diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index a0cf43377507f..aaf518d1799e1 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -186,6 +186,7 @@ struct AspeedSoCClass { uint32_t silicon_rev; uint64_t sram_size; uint64_t secsram_size; + int pcie_num; int spis_num; int ehcis_num; int wdts_num; From d43f6db6548c997263949d8908b62dbab5fbd0ff Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 19 Sep 2025 17:30:12 +0800 Subject: [PATCH 1033/1794] tests/functional/arm/test_aspeed_ast2600: Add PCIe and network test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the AST2600 functional tests with PCIe and network checks. This patch introduces a new helper "do_ast2600_pcie_test()" that runs "lspci" on the emulated system and verifies the presence of the expected PCIe devices: - 80:00.0 Host bridge: ASPEED Technology, Inc. Device 2600 - 80:08.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge - 81:00.0 Ethernet controller: Intel Corporation 82574L Gigabit Network Connection To exercise the PCIe network device, the test adds: -device e1000e,netdev=net1,bus=pcie.0 -netdev user,id=net1 and assigns an IP address to the interface, verifying it with `ip addr`. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250919093017.338309-14-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/arm/test_aspeed_ast2600.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/functional/arm/test_aspeed_ast2600.py b/tests/functional/arm/test_aspeed_ast2600.py index 62949b0b4fe95..f655c0ba0c7a2 100755 --- a/tests/functional/arm/test_aspeed_ast2600.py +++ b/tests/functional/arm/test_aspeed_ast2600.py @@ -101,8 +101,26 @@ def test_arm_ast2600_evb_buildroot_tpm(self): 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.07/ast2600-default-obmc.tar.gz', 'cb6c08595bcbba1672ce716b068ba4e48eda1ed9abe78a07b30392ba2278feba') + def do_ast2600_pcie_test(self): + exec_command_and_wait_for_pattern(self, + 'lspci -s 80:00.0', + '80:00.0 Host bridge: ' + 'ASPEED Technology, Inc. Device 2600') + exec_command_and_wait_for_pattern(self, + 'lspci -s 80:08.0', + '80:08.0 PCI bridge: ' + 'ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge') + exec_command_and_wait_for_pattern(self, + 'lspci -s 81:00.0', + '81:00.0 Ethernet controller: ' + 'Intel Corporation 82574L Gigabit Network Connection') + exec_command_and_wait_for_pattern(self, + 'ip addr show dev eth4', + 'inet 10.0.2.15/24') + def test_arm_ast2600_evb_sdk(self): self.set_machine('ast2600-evb') + self.require_netdev('user') self.archive_extract(self.ASSET_SDK_V907_AST2600) @@ -110,6 +128,8 @@ def test_arm_ast2600_evb_sdk(self): 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test') self.vm.add_args('-device', 'ds1338,bus=aspeed.i2c.bus.5,address=0x32') + self.vm.add_args('-device', 'e1000e,netdev=net1,bus=pcie.0') + self.vm.add_args('-netdev', 'user,id=net1') self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2600-default", "image-bmc")) @@ -135,6 +155,7 @@ def test_arm_ast2600_evb_sdk(self): year = time.strftime("%Y") exec_command_and_wait_for_pattern(self, '/sbin/hwclock -f /dev/rtc1', year) + self.do_ast2600_pcie_test() def test_arm_ast2600_otp_blockdev_device(self): self.vm.set_machine("ast2600-evb") From f821557f4f7fc8ff7ed5c1950e6a12cfe47cdac5 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 25 Sep 2025 13:05:27 +0800 Subject: [PATCH 1034/1794] hw/arm/aspeed: Move aspeed_board_init_flashes() to common SoC code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Relocate aspeed_board_init_flashes() from hw/arm/aspeed.c into hw/arm/aspeed_soc_common.c so the helper can be reused by all ASPEED machines. The API was already declared in include/hw/arm/aspeed_soc.h; this change moves its implementation out of the machine file to keep aspeed.c cleaner. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925050535.2657256-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 22 ---------------------- hw/arm/aspeed_soc_common.c | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index d21b21965aa67..55f0afe0a499f 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -337,28 +337,6 @@ static void aspeed_load_vbootrom(AspeedMachineState *bmc, const char *bios_name, } } -void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, - unsigned int count, int unit0) -{ - int i; - - if (!flashtype) { - return; - } - - for (i = 0; i < count; ++i) { - DriveInfo *dinfo = drive_get(IF_MTD, 0, unit0 + i); - DeviceState *dev; - - dev = qdev_new(flashtype); - if (dinfo) { - qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo)); - } - qdev_prop_set_uint8(dev, "cs", i); - qdev_realize_and_unref(dev, BUS(s->spi), &error_fatal); - } -} - static void sdhci_attach_drive(SDHCIState *sdhci, DriveInfo *dinfo, bool emmc, bool boot_emmc) { diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index 1c4ac93a0ff81..31b1e683c3757 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -16,6 +16,7 @@ #include "hw/misc/unimp.h" #include "hw/arm/aspeed_soc.h" #include "hw/char/serial-mm.h" +#include "system/blockdev.h" const char *aspeed_soc_cpu_type(AspeedSoCClass *sc) @@ -124,6 +125,28 @@ void aspeed_mmio_map_unimplemented(AspeedSoCState *s, SysBusDevice *dev, sysbus_mmio_get_region(dev, 0), -1000); } +void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, + unsigned int count, int unit0) +{ + int i; + + if (!flashtype) { + return; + } + + for (i = 0; i < count; ++i) { + DriveInfo *dinfo = drive_get(IF_MTD, 0, unit0 + i); + DeviceState *dev; + + dev = qdev_new(flashtype); + if (dinfo) { + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo)); + } + qdev_prop_set_uint8(dev, "cs", i); + qdev_realize_and_unref(dev, BUS(s->spi), &error_fatal); + } +} + static void aspeed_soc_realize(DeviceState *dev, Error **errp) { AspeedSoCState *s = ASPEED_SOC(dev); From 597165ec2fb018be8aa7cefa5e3424c39fcec226 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 25 Sep 2025 13:05:28 +0800 Subject: [PATCH 1035/1794] hw/arm/aspeed: Move write_boot_rom to common SoC code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the write_boot_rom helper from hw/arm/aspeed.c into hw/arm/aspeed_soc_common.c so it can be reused by all ASPEED machines. Export the API as aspeed_write_boot_rom() in include/hw/arm/aspeed_soc.h and update the existing call site to use the new helper. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925050535.2657256-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 33 ++------------------------------- hw/arm/aspeed_soc_common.c | 31 +++++++++++++++++++++++++++++++ include/hw/arm/aspeed_soc.h | 2 ++ 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 55f0afe0a499f..4d0d93583659a 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -263,35 +263,6 @@ static void aspeed_reset_secondary(ARMCPU *cpu, cpu_set_pc(cs, info->smp_loader_start); } -static void write_boot_rom(BlockBackend *blk, hwaddr addr, size_t rom_size, - Error **errp) -{ - g_autofree void *storage = NULL; - int64_t size; - - /* - * The block backend size should have already been 'validated' by - * the creation of the m25p80 object. - */ - size = blk_getlength(blk); - if (size <= 0) { - error_setg(errp, "failed to get flash size"); - return; - } - - if (rom_size > size) { - rom_size = size; - } - - storage = g_malloc0(rom_size); - if (blk_pread(blk, 0, rom_size, storage, 0) < 0) { - error_setg(errp, "failed to read the initial flash content"); - return; - } - - rom_add_blob_fixed("aspeed.boot_rom", storage, rom_size, addr); -} - /* * Create a ROM and copy the flash contents at the expected address * (0x0). Boots faster than execute-in-place. @@ -306,8 +277,8 @@ static void aspeed_install_boot_rom(AspeedMachineState *bmc, BlockBackend *blk, &error_abort); memory_region_add_subregion_overlap(&soc->spi_boot_container, 0, &bmc->boot_rom, 1); - write_boot_rom(blk, sc->memmap[ASPEED_DEV_SPI_BOOT], - rom_size, &error_abort); + aspeed_write_boot_rom(blk, sc->memmap[ASPEED_DEV_SPI_BOOT], rom_size, + &error_abort); } #define VBOOTROM_FILE_NAME "ast27x0_bootrom.bin" diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index 31b1e683c3757..d0a400725f24a 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -17,6 +17,8 @@ #include "hw/arm/aspeed_soc.h" #include "hw/char/serial-mm.h" #include "system/blockdev.h" +#include "system/block-backend.h" +#include "hw/loader.h" const char *aspeed_soc_cpu_type(AspeedSoCClass *sc) @@ -147,6 +149,35 @@ void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, } } +void aspeed_write_boot_rom(BlockBackend *blk, hwaddr addr, size_t rom_size, + Error **errp) +{ + g_autofree void *storage = NULL; + int64_t size; + + /* + * The block backend size should have already been 'validated' by + * the creation of the m25p80 object. + */ + size = blk_getlength(blk); + if (size <= 0) { + error_setg(errp, "failed to get flash size"); + return; + } + + if (rom_size > size) { + rom_size = size; + } + + storage = g_malloc0(rom_size); + if (blk_pread(blk, 0, rom_size, storage, 0) < 0) { + error_setg(errp, "failed to read the initial flash content"); + return; + } + + rom_add_blob_fixed("aspeed.boot_rom", storage, rom_size, addr); +} + static void aspeed_soc_realize(DeviceState *dev, Error **errp) { AspeedSoCState *s = ASPEED_SOC(dev); diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index aaf518d1799e1..5567bdcb699ff 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -312,6 +312,8 @@ void aspeed_mmio_map_unimplemented(AspeedSoCState *s, SysBusDevice *dev, uint64_t size); void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, unsigned int count, int unit0); +void aspeed_write_boot_rom(BlockBackend *blk, hwaddr addr, size_t rom_size, + Error **errp); static inline int aspeed_uart_index(int uart_dev) { From a705e9997fd571427fc72d88c1235b1e2bd0c6fb Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 25 Sep 2025 13:05:29 +0800 Subject: [PATCH 1036/1794] hw/arm/aspeed: Move aspeed_install_boot_rom to common SoC code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the boot ROM install helper into common SoC code so it can be reused by all ASPEED boards, and decouple the API from AspeedMachineState. Specifically: - Move aspeed_install_boot_rom() to hw/arm/aspeed_soc_common.c and declare it in include/hw/arm/aspeed_soc.h. - Change the helper’s signature to take AspeedSoCState * and a MemoryRegion * provided by the caller, instead of AspeedMachineState *. - Update aspeed_machine_init() call sites accordingly. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925050535.2657256-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 23 +++-------------------- hw/arm/aspeed_soc_common.c | 17 +++++++++++++++++ include/hw/arm/aspeed_soc.h | 2 ++ 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 4d0d93583659a..429f4c6d77813 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -263,24 +263,6 @@ static void aspeed_reset_secondary(ARMCPU *cpu, cpu_set_pc(cs, info->smp_loader_start); } -/* - * Create a ROM and copy the flash contents at the expected address - * (0x0). Boots faster than execute-in-place. - */ -static void aspeed_install_boot_rom(AspeedMachineState *bmc, BlockBackend *blk, - uint64_t rom_size) -{ - AspeedSoCState *soc = bmc->soc; - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(soc); - - memory_region_init_rom(&bmc->boot_rom, NULL, "aspeed.boot_rom", rom_size, - &error_abort); - memory_region_add_subregion_overlap(&soc->spi_boot_container, 0, - &bmc->boot_rom, 1); - aspeed_write_boot_rom(blk, sc->memmap[ASPEED_DEV_SPI_BOOT], rom_size, - &error_abort); -} - #define VBOOTROM_FILE_NAME "ast27x0_bootrom.bin" /* @@ -460,9 +442,10 @@ static void aspeed_machine_init(MachineState *machine) if (fmc0 && !boot_emmc) { uint64_t rom_size = memory_region_size(&bmc->soc->spi_boot); - aspeed_install_boot_rom(bmc, fmc0, rom_size); + aspeed_install_boot_rom(bmc->soc, fmc0, &bmc->boot_rom, rom_size); } else if (emmc0) { - aspeed_install_boot_rom(bmc, blk_by_legacy_dinfo(emmc0), 64 * KiB); + aspeed_install_boot_rom(bmc->soc, blk_by_legacy_dinfo(emmc0), + &bmc->boot_rom, 64 * KiB); } } diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index d0a400725f24a..7f104f8de517e 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -178,6 +178,23 @@ void aspeed_write_boot_rom(BlockBackend *blk, hwaddr addr, size_t rom_size, rom_add_blob_fixed("aspeed.boot_rom", storage, rom_size, addr); } +/* + * Create a ROM and copy the flash contents at the expected address + * (0x0). Boots faster than execute-in-place. + */ +void aspeed_install_boot_rom(AspeedSoCState *soc, BlockBackend *blk, + MemoryRegion *boot_rom, uint64_t rom_size) +{ + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(soc); + + memory_region_init_rom(boot_rom, NULL, "aspeed.boot_rom", rom_size, + &error_abort); + memory_region_add_subregion_overlap(&soc->spi_boot_container, 0, + boot_rom, 1); + aspeed_write_boot_rom(blk, sc->memmap[ASPEED_DEV_SPI_BOOT], rom_size, + &error_abort); +} + static void aspeed_soc_realize(DeviceState *dev, Error **errp) { AspeedSoCState *s = ASPEED_SOC(dev); diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 5567bdcb699ff..aea210a8e2ec7 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -314,6 +314,8 @@ void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, unsigned int count, int unit0); void aspeed_write_boot_rom(BlockBackend *blk, hwaddr addr, size_t rom_size, Error **errp); +void aspeed_install_boot_rom(AspeedSoCState *soc, BlockBackend *blk, + MemoryRegion *boot_rom, uint64_t rom_size); static inline int aspeed_uart_index(int uart_dev) { From ecabf8ba94961a23f473e7060f8589e93967cfd2 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 25 Sep 2025 13:05:30 +0800 Subject: [PATCH 1037/1794] hw/arm/aspeed: Move aspeed_load_vbootrom to common SoC code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the vbootrom loader helper into common SoC code so it can be reused by all ASPEED boards, and decouple the API from AspeedMachineState. Specifically: - Move aspeed_load_vbootrom() to hw/arm/aspeed_soc_common.c and declare it in include/hw/arm/aspeed_soc.h. - Change the helper’s signature to take AspeedSoCState * instead of AspeedMachineState *. - Update aspeed_machine_init() call sites accordingly. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925050535.2657256-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 31 +------------------------------ hw/arm/aspeed_soc_common.c | 25 +++++++++++++++++++++++++ include/hw/arm/aspeed_soc.h | 4 ++++ 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 429f4c6d77813..6046ec0bb2a2d 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -26,9 +26,7 @@ #include "hw/qdev-properties.h" #include "system/block-backend.h" #include "system/reset.h" -#include "hw/loader.h" #include "qemu/error-report.h" -#include "qemu/datadir.h" #include "qemu/units.h" #include "hw/qdev-clock.h" #include "system/system.h" @@ -263,33 +261,6 @@ static void aspeed_reset_secondary(ARMCPU *cpu, cpu_set_pc(cs, info->smp_loader_start); } -#define VBOOTROM_FILE_NAME "ast27x0_bootrom.bin" - -/* - * This function locates the vbootrom image file specified via the command line - * using the -bios option. It loads the specified image into the vbootrom - * memory region and handles errors if the file cannot be found or loaded. - */ -static void aspeed_load_vbootrom(AspeedMachineState *bmc, const char *bios_name, - Error **errp) -{ - g_autofree char *filename = NULL; - AspeedSoCState *soc = bmc->soc; - int ret; - - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (!filename) { - error_setg(errp, "Could not find vbootrom image '%s'", bios_name); - return; - } - - ret = load_image_mr(filename, &soc->vbootrom); - if (ret < 0) { - error_setg(errp, "Failed to load vbootrom image '%s'", bios_name); - return; - } -} - static void sdhci_attach_drive(SDHCIState *sdhci, DriveInfo *dinfo, bool emmc, bool boot_emmc) { @@ -451,7 +422,7 @@ static void aspeed_machine_init(MachineState *machine) if (amc->vbootrom) { bios_name = machine->firmware ?: VBOOTROM_FILE_NAME; - aspeed_load_vbootrom(bmc, bios_name, &error_abort); + aspeed_load_vbootrom(bmc->soc, bios_name, &error_abort); } arm_load_kernel(ARM_CPU(first_cpu), machine, &aspeed_board_binfo); diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index 7f104f8de517e..bc70e864fba7c 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -19,6 +19,7 @@ #include "system/blockdev.h" #include "system/block-backend.h" #include "hw/loader.h" +#include "qemu/datadir.h" const char *aspeed_soc_cpu_type(AspeedSoCClass *sc) @@ -195,6 +196,30 @@ void aspeed_install_boot_rom(AspeedSoCState *soc, BlockBackend *blk, &error_abort); } +/* + * This function locates the vbootrom image file specified via the command line + * using the -bios option. It loads the specified image into the vbootrom + * memory region and handles errors if the file cannot be found or loaded. + */ +void aspeed_load_vbootrom(AspeedSoCState *soc, const char *bios_name, + Error **errp) +{ + g_autofree char *filename = NULL; + int ret; + + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!filename) { + error_setg(errp, "Could not find vbootrom image '%s'", bios_name); + return; + } + + ret = load_image_mr(filename, &soc->vbootrom); + if (ret < 0) { + error_setg(errp, "Failed to load vbootrom image '%s'", bios_name); + return; + } +} + static void aspeed_soc_realize(DeviceState *dev, Error **errp) { AspeedSoCState *s = ASPEED_SOC(dev); diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index aea210a8e2ec7..ed32efb543f44 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -43,6 +43,8 @@ #include "hw/char/serial-mm.h" #include "hw/intc/arm_gicv3.h" +#define VBOOTROM_FILE_NAME "ast27x0_bootrom.bin" + #define ASPEED_SPIS_NUM 3 #define ASPEED_EHCIS_NUM 4 #define ASPEED_WDTS_NUM 8 @@ -316,6 +318,8 @@ void aspeed_write_boot_rom(BlockBackend *blk, hwaddr addr, size_t rom_size, Error **errp); void aspeed_install_boot_rom(AspeedSoCState *soc, BlockBackend *blk, MemoryRegion *boot_rom, uint64_t rom_size); +void aspeed_load_vbootrom(AspeedSoCState *soc, const char *bios_name, + Error **errp); static inline int aspeed_uart_index(int uart_dev) { From 8d3aaefdb3bb13df7c57091a4319f3afe3c351ac Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 25 Sep 2025 13:05:31 +0800 Subject: [PATCH 1038/1794] hw/arm/aspeed_ast27x0-fc: Drop dead return checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. object_property_set_link() can return false only when it fails, and it sets an error when it fails. Since passing &error_abort causes an abort, the function never returns false, and the return statement is effectively dead code. 2. object_property_set_int() is considered as a routine which shouldn't fail. So the common practice in models is to pass &error_abort and ignore the returned value. https://patchwork.kernel.org/project/qemu-devel/patch/20250717034054.1903991-3-jamin_lin@aspeedtech.com/#26540626 No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925050535.2657256-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 43 +++++++++++++------------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index 7087be4288bbc..ebf3784df588f 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -74,19 +74,12 @@ static void ast2700fc_ca35_init(MachineState *machine) AST2700FC_BMC_RAM_SIZE, &error_abort)) { return; } - if (!object_property_set_link(OBJECT(&s->ca35), "memory", - OBJECT(&s->ca35_memory), - &error_abort)) { - return; - }; - if (!object_property_set_link(OBJECT(&s->ca35), "dram", - OBJECT(&s->ca35_dram), &error_abort)) { - return; - } - if (!object_property_set_int(OBJECT(&s->ca35), "ram-size", - AST2700FC_BMC_RAM_SIZE, &error_abort)) { - return; - } + object_property_set_link(OBJECT(&s->ca35), "memory", + OBJECT(&s->ca35_memory), &error_abort); + object_property_set_link(OBJECT(&s->ca35), "dram", OBJECT(&s->ca35_dram), + &error_abort); + object_property_set_int(OBJECT(&s->ca35), "ram-size", + AST2700FC_BMC_RAM_SIZE, &error_abort); for (int i = 0; i < sc->macs_num; i++) { if (!qemu_configure_nic_device(DEVICE(&soc->ftgmac100[i]), @@ -94,14 +87,10 @@ static void ast2700fc_ca35_init(MachineState *machine) break; } } - if (!object_property_set_int(OBJECT(&s->ca35), "hw-strap1", - AST2700FC_HW_STRAP1, &error_abort)) { - return; - } - if (!object_property_set_int(OBJECT(&s->ca35), "hw-strap2", - AST2700FC_HW_STRAP2, &error_abort)) { - return; - } + object_property_set_int(OBJECT(&s->ca35), "hw-strap1", + AST2700FC_HW_STRAP1, &error_abort); + object_property_set_int(OBJECT(&s->ca35), "hw-strap2", + AST2700FC_HW_STRAP2, &error_abort); aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART12, serial_hd(0)); if (!qdev_realize(DEVICE(&s->ca35), NULL, &error_abort)) { return; @@ -133,10 +122,8 @@ static void ast2700fc_ssp_init(MachineState *machine) UINT64_MAX); qdev_connect_clock_in(DEVICE(&s->ssp), "sysclk", s->ssp_sysclk); - if (!object_property_set_link(OBJECT(&s->ssp), "memory", - OBJECT(&s->ssp_memory), &error_abort)) { - return; - } + object_property_set_link(OBJECT(&s->ssp), "memory", + OBJECT(&s->ssp_memory), &error_abort); soc = ASPEED_SOC(&s->ssp); aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART4, serial_hd(1)); @@ -157,10 +144,8 @@ static void ast2700fc_tsp_init(MachineState *machine) UINT64_MAX); qdev_connect_clock_in(DEVICE(&s->tsp), "sysclk", s->tsp_sysclk); - if (!object_property_set_link(OBJECT(&s->tsp), "memory", - OBJECT(&s->tsp_memory), &error_abort)) { - return; - } + object_property_set_link(OBJECT(&s->tsp), "memory", + OBJECT(&s->tsp_memory), &error_abort); soc = ASPEED_SOC(&s->tsp); aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART7, serial_hd(2)); From 9ec30a07483640ecb8417fce3dfa9273f7a036c9 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 25 Sep 2025 13:05:32 +0800 Subject: [PATCH 1039/1794] hw/arm/aspeed_ast27x0-fc: Make sub-init functions return bool with errp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor ast2700fc_ca35_init(), ast2700fc_ssp_init(), and ast2700fc_tsp_init() to take an Error **errp parameter and return a bool. Each function now reports failure through the error object and returns false. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250925050535.2657256-7-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index ebf3784df588f..2e16a0340a7ba 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -56,7 +56,7 @@ struct Ast2700FCState { #define AST2700FC_FMC_MODEL "w25q01jvq" #define AST2700FC_SPI_MODEL "w25q512jv" -static void ast2700fc_ca35_init(MachineState *machine) +static bool ast2700fc_ca35_init(MachineState *machine, Error **errp) { Ast2700FCState *s = AST2700A1FC(machine); AspeedSoCState *soc; @@ -71,8 +71,8 @@ static void ast2700fc_ca35_init(MachineState *machine) memory_region_add_subregion(get_system_memory(), 0, &s->ca35_memory); if (!memory_region_init_ram(&s->ca35_dram, OBJECT(&s->ca35), "ca35-dram", - AST2700FC_BMC_RAM_SIZE, &error_abort)) { - return; + AST2700FC_BMC_RAM_SIZE, errp)) { + return false; } object_property_set_link(OBJECT(&s->ca35), "memory", OBJECT(&s->ca35_memory), &error_abort); @@ -92,8 +92,8 @@ static void ast2700fc_ca35_init(MachineState *machine) object_property_set_int(OBJECT(&s->ca35), "hw-strap2", AST2700FC_HW_STRAP2, &error_abort); aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART12, serial_hd(0)); - if (!qdev_realize(DEVICE(&s->ca35), NULL, &error_abort)) { - return; + if (!qdev_realize(DEVICE(&s->ca35), NULL, errp)) { + return false; } /* @@ -108,9 +108,11 @@ static void ast2700fc_ca35_init(MachineState *machine) ast2700fc_board_info.loader_start = sc->memmap[ASPEED_DEV_SDRAM]; arm_load_kernel(ARM_CPU(first_cpu), machine, &ast2700fc_board_info); + + return true; } -static void ast2700fc_ssp_init(MachineState *machine) +static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) { AspeedSoCState *soc; Ast2700FCState *s = AST2700A1FC(machine); @@ -127,12 +129,14 @@ static void ast2700fc_ssp_init(MachineState *machine) soc = ASPEED_SOC(&s->ssp); aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART4, serial_hd(1)); - if (!qdev_realize(DEVICE(&s->ssp), NULL, &error_abort)) { - return; + if (!qdev_realize(DEVICE(&s->ssp), NULL, errp)) { + return false; } + + return true; } -static void ast2700fc_tsp_init(MachineState *machine) +static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) { AspeedSoCState *soc; Ast2700FCState *s = AST2700A1FC(machine); @@ -149,16 +153,18 @@ static void ast2700fc_tsp_init(MachineState *machine) soc = ASPEED_SOC(&s->tsp); aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART7, serial_hd(2)); - if (!qdev_realize(DEVICE(&s->tsp), NULL, &error_abort)) { - return; + if (!qdev_realize(DEVICE(&s->tsp), NULL, errp)) { + return false; } + + return true; } static void ast2700fc_init(MachineState *machine) { - ast2700fc_ca35_init(machine); - ast2700fc_ssp_init(machine); - ast2700fc_tsp_init(machine); + ast2700fc_ca35_init(machine, &error_abort); + ast2700fc_ssp_init(machine, &error_abort); + ast2700fc_tsp_init(machine, &error_abort); } static void ast2700fc_class_init(ObjectClass *oc, const void *data) From e01b61963faf810ceee1f15c97ec3a2210112c47 Mon Sep 17 00:00:00 2001 From: Weifeng Liu Date: Mon, 14 Jul 2025 22:17:54 +0800 Subject: [PATCH 1040/1794] gtk: Skip drawing if console surface is NULL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In gtk draw/render callbacks, add an early NULL check for the console surface and skip drawing if it's NULL. Otherwise, attempting to fetch its width and height crash. This change fixes Coverity CID 1610328. In practice, this case wouldn't happen at all because we always install a placeholder surface to the console when there is nothing to display. Resolves: Coverity CID 1610328 Signed-off-by: Weifeng Liu Reviewed-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-ID: <20250714141758.10062-1-weifeng.liu.z@gmail.com> --- ui/gtk-egl.c | 5 +---- ui/gtk-gl-area.c | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 0b787bea25eee..ae9239999cdb6 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -72,7 +72,7 @@ void gd_egl_draw(VirtualConsole *vc) #endif int ww, wh, pw, ph, gs; - if (!vc->gfx.gls) { + if (!vc->gfx.gls || !vc->gfx.ds) { return; } @@ -112,9 +112,6 @@ void gd_egl_draw(VirtualConsole *vc) } #endif } else { - if (!vc->gfx.ds) { - return; - } eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, vc->gfx.esurface, vc->gfx.ectx); diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index 8151cc413cf7f..05fc38096eca3 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -48,7 +48,7 @@ void gd_gl_area_draw(VirtualConsole *vc) int fbw, fbh; int wx_offset, wy_offset; - if (!vc->gfx.gls) { + if (!vc->gfx.gls || !vc->gfx.ds) { return; } @@ -135,9 +135,6 @@ void gd_gl_area_draw(VirtualConsole *vc) } #endif } else { - if (!vc->gfx.ds) { - return; - } gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, pw, ph); From e7ecb533ee0dbfbe30c90abb213247f4943a9a12 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Mon, 29 Sep 2025 15:42:24 +0000 Subject: [PATCH 1041/1794] ui/spice: Fix abort on macOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The check is faulty because the thread variable was assigned in the main thread while the main loop runs in a different thread on macOS. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3070 Signed-off-by: Mohamed Akram Acked-by: Marc-André Lureau Message-ID: --- ui/spice-core.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ui/spice-core.c b/ui/spice-core.c index 5992f9daecc25..2645e96ef66fd 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -50,8 +50,6 @@ static int spice_migration_completed; static int spice_display_is_running; static int spice_have_target_host; -static QemuThread me; - struct SpiceTimer { QEMUTimer *timer; }; @@ -222,7 +220,7 @@ static void channel_event(int event, SpiceChannelEventInfo *info) * thread and grab the BQL if so before calling qemu * functions. */ - bool need_lock = !qemu_thread_is_self(&me); + bool need_lock = !bql_locked(); if (need_lock) { bql_lock(); } @@ -675,8 +673,6 @@ static void qemu_spice_init(void) spice_wan_compression_t wan_compr; bool seamless_migration; - qemu_thread_get_self(&me); - if (!opts) { return; } From 62fd247a24290dba2b2de4ee8575624a7993973c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 3 Sep 2025 23:38:18 +0400 Subject: [PATCH 1042/1794] ui/spice: fix crash when disabling GL scanout on MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When spice_qxl_gl_scanout2() isn't available, the fallback code incorrectly handles NULL arguments to disable the scanout, leading to: Program terminated with signal SIGSEGV, Segmentation fault. #0 spice_server_gl_scanout (qxl=0x55a25ce57ae8, fd=0x0, width=0, height=0, offset=0x0, stride=0x0, num_planes=0, format=0, modifier=72057594037927935, y_0_top=0) at ../ui/spice-display.c:983 983 if (num_planes <= 1) { Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2391334 Fixes: 98a050ca93afd8 ("ui/spice: support multi plane dmabuf scanout") Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Reviewed-by: Michael Tokarev Message-Id: <20250903193818.2460914-1-marcandre.lureau@redhat.com> --- ui/spice-display.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/spice-display.c b/ui/spice-display.c index 669832c5612a0..db71e866f89ff 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -980,7 +980,9 @@ static void spice_server_gl_scanout(QXLInstance *qxl, spice_qxl_gl_scanout2(qxl, fd, width, height, offset, stride, num_planes, format, modifier, y_0_top); #else - if (num_planes <= 1) { + if (fd == NULL) { + spice_qxl_gl_scanout(qxl, -1, 0, 0, 0, 0, false); + } else if (num_planes <= 1) { spice_qxl_gl_scanout(qxl, fd[0], width, height, stride[0], format, y_0_top); } else { error_report("SPICE server does not support multi plane GL scanout"); From 15421f71137b4a1b6bab8c12257b013dae1aebb8 Mon Sep 17 00:00:00 2001 From: Nir Lichtman Date: Wed, 10 Sep 2025 11:49:29 +0000 Subject: [PATCH 1043/1794] ui/sdl2: fix reset scaling binding to be consistent with gtk Problem: Currently the reset scaling hotkey is inconsistent between SDL and GTK graphics modes. Solution: Fix SDL to use MOD+0 instead of MOD+u which is in line with GTK and generally more consistent with other apps. This is also related to my previously sent patch fixing the docs. Suggested-by: Gerd Hoffmann Signed-off-by: Nir Lichtman Reviewed-by: Thomas Huth Message-ID: <20250910114929.GA1783677@lichtman.org> --- ui/sdl2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/sdl2.c b/ui/sdl2.c index b00e421f7f869..032dc14bc3988 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -421,7 +421,7 @@ static void handle_keydown(SDL_Event *ev) sdl_grab_end(scon); } break; - case SDL_SCANCODE_U: + case SDL_SCANCODE_0: sdl2_window_resize(scon); if (!scon->opengl) { /* re-create scon->texture */ From 9163424c50981dbc4ded9990228ac01a3b193656 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 30 Sep 2025 09:14:18 +0200 Subject: [PATCH 1044/1794] ui/icons/qemu.svg: Add metadata information (author, license) to the logo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We've got two versions of the QEMU logo in the repository, one with the whole word "QEMU" (pc-bios/qemu_logo.svg) and one that only contains the letter "Q" (ui/icons/qemu.svg). While qemu_logo.svg contains the proper metadata with license and author information, this is missing from the ui/icons/qemu.svg file. Copy the meta data there so that people have a chance to know the license of the file if they only look at the qemu.svg file. Closes: https://gitlab.com/qemu-project/qemu/-/issues/3139 Signed-off-by: Thomas Huth Reviewed-by: Marc-André Lureau Message-ID: <20250930071419.117592-1-thuth@redhat.com> --- ui/icons/qemu.svg | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/ui/icons/qemu.svg b/ui/icons/qemu.svg index 24ca23a1e95dd..f2500de339170 100644 --- a/ui/icons/qemu.svg +++ b/ui/icons/qemu.svg @@ -918,7 +918,26 @@ image/svg+xml - + Kew the Angry Emu + + + Benoît Canet + + + + + CC BY 3.0 + + + + + QEMU Community + + + 2012-02-15 + + https://lists.gnu.org/archive/html/qemu-devel/2012-02/msg02865.html From 548f1abacbcd53947060a8b05b74d5d1539f87b3 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 23 Sep 2025 11:09:48 +0200 Subject: [PATCH 1045/1794] monitor: Clean up HMP gdbserver error reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HMP command gdbserver used to emit two error messages for certain errors. For instance, with -M none: (qemu) gdbserver gdbstub: meaningless to attach gdb to a machine without any CPU. Could not open gdbserver on device 'tcp::1234' The first message is the specific error, and the second one a generic additional message that feels superfluous to me. Commit c0e6b8b798b (system: propagate Error to gdbserver_start (and other device setups)) turned the first message into a warning: warning: gdbstub: meaningless to attach gdb to a machine without any CPU. Could not open gdbserver on device 'tcp::1234' This is arguably worse. hmp_gdbserver() passes &error_warn to gdbserver_start(), so that failure gets reported as warning, and then additionally emits the generic error on failure. This is a misuse of &error_warn. Instead, receive the error in &err and report it, as usual. With this, gdbserver reports just the error: gdbstub: meaningless to attach gdb to a machine without any CPU. Cc: Alex Bennée Signed-off-by: Markus Armbruster Reviewed-by: Daniel P. Berrangé Message-ID: <20250923091000.3180122-2-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Akihiko Odaki --- include/exec/gdbstub.h | 3 --- monitor/hmp-cmds.c | 7 ++++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index a16c0051ce0a9..bd7182c4d324f 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -55,9 +55,6 @@ void gdb_unregister_coprocessor_all(CPUState *cpu); * system emulation you can use a full chardev spec for your gdbserver * port. * - * The error handle should be either &error_fatal (for start-up) or - * &error_warn (for QMP/HMP initiated sessions). - * * Returns true when server successfully started. */ bool gdbserver_start(const char *port_or_device, Error **errp); diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 74a0f56566e71..33a88ce205a82 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -280,14 +280,15 @@ void hmp_log(Monitor *mon, const QDict *qdict) void hmp_gdbserver(Monitor *mon, const QDict *qdict) { + Error *err = NULL; const char *device = qdict_get_try_str(qdict, "device"); + if (!device) { device = "tcp::" DEFAULT_GDBSTUB_PORT; } - if (!gdbserver_start(device, &error_warn)) { - monitor_printf(mon, "Could not open gdbserver on device '%s'\n", - device); + if (!gdbserver_start(device, &err)) { + error_report_err(err); } else if (strcmp(device, "none") == 0) { monitor_printf(mon, "Disabled gdbserver\n"); } else { From 82b5e6cc309292b3490be87bb020cbe74fe618fc Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 23 Sep 2025 11:09:49 +0200 Subject: [PATCH 1046/1794] tcg: Fix error reporting on mprotect() failure in tcg_region_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tcg_region_init() calls one of qemu_mprotect_rwx(), qemu_mprotect_rw(), and mprotect(), then reports failure with error_setg_errno(&error_fatal, errno, ...). The use of &error_fatal is undesirable. qapi/error.h advises: * Please don't error_setg(&error_fatal, ...), use error_report() and * exit(), because that's more obvious. The use of errno is wrong. qemu_mprotect_rwx() and qemu_mprotect_rw() wrap around qemu_mprotect__osdep(). qemu_mprotect__osdep() calls mprotect() on POSIX, VirtualProtect() on Windows, and reports failure with error_report(). VirtualProtect() doesn't set errno. mprotect() does, but error_report() may clobber it. Fix tcg_region_init() to report errors only when it calls mprotect(), and rely on qemu_mprotect_rwx()'s and qemu_mprotect_rw()'s error reporting otherwise. Use error_report(), not error_setg(). Fixes: 22c6a9938f75 (tcg: Merge buffer protection and guard page protection) Fixes: 6bc144237a85 (tcg: Use Error with alloc_code_gen_buffer) Cc: Richard Henderson Signed-off-by: Markus Armbruster Reviewed-by: Daniel P. Berrangé Reviewed-by: Richard Henderson Message-ID: <20250923091000.3180122-3-armbru@redhat.com> Reviewed-by: Akihiko Odaki --- tcg/region.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tcg/region.c b/tcg/region.c index 7ea0b37a84c33..2181267e48f7b 100644 --- a/tcg/region.c +++ b/tcg/region.c @@ -832,13 +832,16 @@ void tcg_region_init(size_t tb_size, int splitwx, unsigned max_threads) } else { #ifdef CONFIG_POSIX rc = mprotect(start, end - start, need_prot); + if (rc) { + error_report("mprotect of jit buffer: %s", + strerror(errno)); + } #else g_assert_not_reached(); #endif } if (rc) { - error_setg_errno(&error_fatal, errno, - "mprotect of jit buffer"); + exit(1); } } if (have_prot != 0) { From b8df7dfc04a88228a6bf35530c10b1326f5cb6d6 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 23 Sep 2025 11:09:50 +0200 Subject: [PATCH 1047/1794] hw/cxl: Convert cxl_fmws_link() to Error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Functions that use an Error **errp parameter to return errors should not also report them to the user, because reporting is the caller's job. When the caller does, the error is reported twice. When it doesn't (because it recovered from the error), there is no error to report, i.e. the report is bogus. cxl_fmws_link_targets() violates this principle: it calls error_setg(&error_fatal, ...) via cxl_fmws_link(). Goes back to commit 584f722eb3ab (hw/cxl: Make the CXL fixed memory windows devices.) Currently harmless, because cxl_fmws_link_targets()'s callers always pass &error_fatal. Clean this up by converting cxl_fmws_link() to Error. Also change its return value on error from 1 to -1 to conform to the rules laid in qapi/error.h. It's call chain cxl_fmws_link_targets() via object_child_foreach_recursive() is fine with that. Cc: Jonathan Cameron Signed-off-by: Markus Armbruster Reviewed-by: Jonathan Cameron Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250923091000.3180122-4-armbru@redhat.com> Reviewed-by: Akihiko Odaki --- hw/cxl/cxl-host.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/cxl/cxl-host.c b/hw/cxl/cxl-host.c index 5c2ce25a19cbc..0d891c651dfe8 100644 --- a/hw/cxl/cxl-host.c +++ b/hw/cxl/cxl-host.c @@ -72,6 +72,7 @@ static void cxl_fixed_memory_window_config(CXLFixedMemoryWindowOptions *object, static int cxl_fmws_link(Object *obj, void *opaque) { + Error **errp = opaque; struct CXLFixedWindow *fw; int i; @@ -87,9 +88,9 @@ static int cxl_fmws_link(Object *obj, void *opaque) o = object_resolve_path_type(fw->targets[i], TYPE_PXB_CXL_DEV, &ambig); if (!o) { - error_setg(&error_fatal, "Could not resolve CXLFM target %s", + error_setg(errp, "Could not resolve CXLFM target %s", fw->targets[i]); - return 1; + return -1; } fw->target_hbs[i] = PXB_CXL_DEV(o); } @@ -99,7 +100,7 @@ static int cxl_fmws_link(Object *obj, void *opaque) void cxl_fmws_link_targets(Error **errp) { /* Order doesn't matter for this, so no need to build list */ - object_child_foreach_recursive(object_get_root(), cxl_fmws_link, NULL); + object_child_foreach_recursive(object_get_root(), cxl_fmws_link, errp); } static bool cxl_hdm_find_target(uint32_t *cache_mem, hwaddr addr, From 897071bb27bfba578af15300973b7a4a1fb65ad2 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 23 Sep 2025 11:09:51 +0200 Subject: [PATCH 1048/1794] migration/cpr: Clean up error reporting in cpr_resave_fd() qapi/error.h advises: * Please don't error_setg(&error_fatal, ...), use error_report() and * exit(), because that's more obvious. Do that, and replace exit() by g_assert_not_reached(), since this is actually a programming error. Cc: Steve Sistare Signed-off-by: Markus Armbruster Reviewed-by: Steve Sistare Message-ID: <20250923091000.3180122-5-armbru@redhat.com> Reviewed-by: Akihiko Odaki --- migration/cpr.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/migration/cpr.c b/migration/cpr.c index 42ad0b0d500e5..9848a21ea6ed2 100644 --- a/migration/cpr.c +++ b/migration/cpr.c @@ -7,6 +7,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "hw/vfio/vfio-device.h" #include "migration/cpr.h" #include "migration/misc.h" @@ -100,10 +101,10 @@ void cpr_resave_fd(const char *name, int id, int fd) if (old_fd < 0) { cpr_save_fd(name, id, fd); } else if (old_fd != fd) { - error_setg(&error_fatal, - "internal error: cpr fd '%s' id %d value %d " - "already saved with a different value %d", - name, id, fd, old_fd); + error_report("internal error: cpr fd '%s' id %d value %d " + "already saved with a different value %d", + name, id, fd, old_fd); + g_assert_not_reached(); } } From 789f1adefba726d2b0bf4d4254c829b5912e32ee Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 23 Sep 2025 11:09:52 +0200 Subject: [PATCH 1049/1794] hw/remote/vfio-user: Clean up error reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VFU_OBJECT_ERROR() reports the error with error_setg(&error_abort, ...) when auto-shutdown is enabled, else with error_report(). Issues: 1. The error is serious enough to warrant aborting the process when auto-shutdown is enabled, yet harmless enough to permit carrying on when it's disabled. This makes no sense to me. 2. Like assert(), &error_abort is strictly for programming errors. Is this one? Vladimir Sementsov-Ogievskiy tells me it's not. Should we exit(1) instead? 3. qapi/error.h advises "don't error_setg(&error_abort, ...), use assert()." This patch addresses just 3. It adds a FIXME comment for the other two. Cc: Jagannathan Raman Signed-off-by: Markus Armbruster Message-ID: <20250923091000.3180122-6-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Vladimir Sementsov-Ogievskiy [FIXME comment added, commit message adjusted accordingly] Reviewed-by: Akihiko Odaki --- hw/remote/vfio-user-obj.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c index ea6165ebdcea3..216b4876e245f 100644 --- a/hw/remote/vfio-user-obj.c +++ b/hw/remote/vfio-user-obj.c @@ -75,12 +75,17 @@ OBJECT_DECLARE_TYPE(VfuObject, VfuObjectClass, VFU_OBJECT) */ #define VFU_OBJECT_ERROR(o, fmt, ...) \ { \ + error_report((fmt), ## __VA_ARGS__); \ if (vfu_object_auto_shutdown()) { \ - error_setg(&error_abort, (fmt), ## __VA_ARGS__); \ - } else { \ - error_report((fmt), ## __VA_ARGS__); \ + /* \ + * FIXME This looks inappropriate. The error is serious \ + * enough programming error to warrant aborting the process \ + * when auto-shutdown is enabled, yet harmless enough to \ + * permit carrying on when it's disabled. Makes no sense. \ + */ \ + abort(); \ } \ - } \ + } struct VfuObjectClass { ObjectClass parent_class; From 3cacecb9f83754a61f4a524f686deae790e2df15 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 23 Sep 2025 11:09:53 +0200 Subject: [PATCH 1050/1794] net/slirp: Clean up error reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit net_slirp_register_poll_sock() and net_slirp_unregister_poll_sock() report WSAEventSelect() failure with error_setg(&error_warn, ...). error_setg_win32(&error_warn, ...) is undesirable just like error_setg(&error_fatal, ...) and error_setg(&error_abort, ...) are. Replace by warn_report(). The failures should probably be errors, but these functions implement callbacks that cannot fail, exit(1) would be too harsh, and silent failure we don't want. Thus, warnings. Cc: Marc-André Lureau Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-ID: <20250923091000.3180122-7-armbru@redhat.com> Reviewed-by: Akihiko Odaki --- net/slirp.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/slirp.c b/net/slirp.c index 9657e86a8415b..0a1c2a5eac3a5 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -258,11 +258,13 @@ static void net_slirp_register_poll_sock(slirp_os_socket fd, void *opaque) { #ifdef WIN32 AioContext *ctxt = qemu_get_aio_context(); + g_autofree char *msg = NULL; if (WSAEventSelect(fd, event_notifier_get_handle(&ctxt->notifier), FD_READ | FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_WRITE | FD_OOB) != 0) { - error_setg_win32(&error_warn, WSAGetLastError(), "failed to WSAEventSelect()"); + msg = g_win32_error_message(WSAGetLastError()); + warn_report("failed to WSAEventSelect(): %s", msg); } #endif } @@ -270,8 +272,11 @@ static void net_slirp_register_poll_sock(slirp_os_socket fd, void *opaque) static void net_slirp_unregister_poll_sock(slirp_os_socket fd, void *opaque) { #ifdef WIN32 + g_autofree char *msg = NULL; + if (WSAEventSelect(fd, NULL, 0) != 0) { - error_setg_win32(&error_warn, WSAGetLastError(), "failed to WSAEventSelect()"); + msg = g_win32_error_message(WSAGetLastError()); + warn_report("failed to WSAEventSelect(): %s", msg); } #endif } From b46b8cf29c56e423b227021f1a5b7aa9b11181b4 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 23 Sep 2025 11:09:54 +0200 Subject: [PATCH 1051/1794] ui/spice-core: Clean up error reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit watch_add() reports _open_osfhandle() failure with error_setg(&error_warn, ...). error_setg_win32(&error_warn, ...) is undesirable just like error_setg(&error_fatal, ...) and error_setg(&error_abort, ...) are. Replace by warn_report(). The failure should probably be an error, but this function implements a callback that doesn't take Error **. I believe the failure will make spice_server_init() fail in qemu_spice_init(), which is treated as a fatal error. The warning here provides more detail than the error message there. Cc: Marc-André Lureau Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Reviewed-by: Richard Henderson Message-ID: <20250923091000.3180122-8-armbru@redhat.com> Reviewed-by: Akihiko Odaki --- ui/spice-core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/spice-core.c b/ui/spice-core.c index 5992f9daecc25..7836202664646 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -128,11 +128,13 @@ static void watch_update_mask(SpiceWatch *watch, int event_mask) static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque) { SpiceWatch *watch; - #ifdef WIN32 + g_autofree char *msg = NULL; + fd = _open_osfhandle(fd, _O_BINARY); if (fd < 0) { - error_setg_win32(&error_warn, WSAGetLastError(), "Couldn't associate a FD with the SOCKET"); + msg = g_win32_error_message(WSAGetLastError()); + warn_report("Couldn't associate a FD with the SOCKET: %s", msg); return NULL; } #endif From 5bd58f04b831f7086f21ae70c90d1a86b6565762 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 23 Sep 2025 11:09:55 +0200 Subject: [PATCH 1052/1794] util/oslib-win32: Do not treat null @errp as &error_warn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu_socket_select() and its wrapper qemu_socket_unselect() treat a null @errp as &error_warn. This is wildly inappropriate. A caller passing null @errp specifies that errors are to be ignored. If warnings are wanted, the caller must pass &error_warn. Change callers to do that, and drop the inappropriate treatment of null @errp. This assumes that warnings are wanted. I'm not familiar with the calling code, so I can't say whether it will work when the socket is invalid, or WSAEventSelect() fails. If it doesn't, then this should be an error instead of a warning. Invalid socket might even be a programming error. These warnings were introduced in commit f5fd677ae7cf (win32/socket: introduce qemu_socket_select() helper). I considered reverting to silence, but Daniel Berrangé asked for the warnings to be preserved. Cc: Marc-André Lureau Signed-off-by: Markus Armbruster Reviewed-by: Richard Henderson Message-ID: <20250923091000.3180122-9-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Akihiko Odaki --- io/channel-socket.c | 4 ++-- io/channel-watch.c | 2 +- util/aio-win32.c | 2 +- util/oslib-win32.c | 6 +----- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/io/channel-socket.c b/io/channel-socket.c index e53d9ac76fff5..fdc767086753c 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -454,7 +454,7 @@ static void qio_channel_socket_finalize(Object *obj) } } #ifdef WIN32 - qemu_socket_unselect(ioc->fd, NULL); + qemu_socket_unselect(ioc->fd, &error_warn); #endif close(ioc->fd); ioc->fd = -1; @@ -929,7 +929,7 @@ qio_channel_socket_close(QIOChannel *ioc, if (sioc->fd != -1) { #ifdef WIN32 - qemu_socket_unselect(sioc->fd, NULL); + qemu_socket_unselect(sioc->fd, &error_warn); #endif if (qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_LISTEN)) { socket_listen_cleanup(sioc->fd, errp); diff --git a/io/channel-watch.c b/io/channel-watch.c index 64b486e378184..ec76bd1ec6a17 100644 --- a/io/channel-watch.c +++ b/io/channel-watch.c @@ -283,7 +283,7 @@ GSource *qio_channel_create_socket_watch(QIOChannel *ioc, qemu_socket_select(sockfd, ioc->event, FD_READ | FD_ACCEPT | FD_CLOSE | - FD_CONNECT | FD_WRITE | FD_OOB, NULL); + FD_CONNECT | FD_WRITE | FD_OOB, &error_warn); source = g_source_new(&qio_channel_socket_source_funcs, sizeof(QIOChannelSocketSource)); diff --git a/util/aio-win32.c b/util/aio-win32.c index 6583d5c5f31a3..b125924433fa2 100644 --- a/util/aio-win32.c +++ b/util/aio-win32.c @@ -121,7 +121,7 @@ void aio_set_fd_handler(AioContext *ctx, QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, node, node); event = event_notifier_get_handle(&ctx->notifier); - qemu_socket_select(fd, event, bitmask, NULL); + qemu_socket_select(fd, event, bitmask, &error_warn); } if (old_node) { aio_remove_fd_handler(ctx, old_node); diff --git a/util/oslib-win32.c b/util/oslib-win32.c index b9ce2f96eefa7..6a2367c89defc 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -182,7 +182,7 @@ bool qemu_set_blocking(int fd, bool block, Error **errp) unsigned long opt = block ? 0 : 1; if (block) { - qemu_socket_unselect(fd, NULL); + qemu_socket_unselect(fd, &error_warn); } if (ioctlsocket(fd, FIONBIO, &opt) != NO_ERROR) { @@ -293,10 +293,6 @@ bool qemu_socket_select(int sockfd, WSAEVENT hEventObject, { SOCKET s = _get_osfhandle(sockfd); - if (errp == NULL) { - errp = &error_warn; - } - if (s == INVALID_SOCKET) { error_setg(errp, "invalid socket fd=%d", sockfd); return false; From b296b29d341429b82d9a76885f8e7fc3abd85ce2 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 23 Sep 2025 11:09:56 +0200 Subject: [PATCH 1053/1794] ui/pixman: Consistent error handling in qemu_pixman_shareable_free() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu_pixman_shareable_free() wraps around either qemu_memfd_free() or qemu_win32_map_free(). The former reports trouble as error, with error_report(), then succeeds. The latter reports it as warning (we pass it &error_warn), then succeeds. Change the latter to report as error, too. Cc: Marc-André Lureau Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-ID: <20250923091000.3180122-10-armbru@redhat.com> Reviewed-by: Akihiko Odaki --- ui/qemu-pixman.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index ef4e71da111d3..e46c6232cfaa9 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -288,7 +288,10 @@ qemu_pixman_shareable_free(qemu_pixman_shareable handle, void *ptr, size_t size) { #ifdef WIN32 - qemu_win32_map_free(ptr, handle, &error_warn); + Error *err = NULL; + + qemu_win32_map_free(ptr, handle, &err); + error_report_err(err); #else qemu_memfd_free(ptr, size, handle); #endif From 96ae3f67a222197ae633225ba4543551126dbd95 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 23 Sep 2025 11:09:57 +0200 Subject: [PATCH 1054/1794] ui/dbus: Clean up dbus_update_gl_cb() error checking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From GLib "Rules for use of GError": A GError* must be initialized to NULL before passing its address to a function that can report errors. dbus_update_gl_cb() seemingly violates this rule: it passes &err to qemu_dbus_display1_listener_call_update_dmabuf_finish() and to qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d_finish() without clearing it in between. Harmless, because the first call is guarded by #ifdef CONFIG_GBM, the second by #ifdef WIN32, and the two are mutually exclusive. I think. Clean this up to be obviously correct. Cc: Marc-André Lureau Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-ID: <20250923091000.3180122-11-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Akihiko Odaki --- ui/dbus-listener.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 42875b8eed8f1..09d7a319b10a8 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -221,18 +221,21 @@ static void dbus_update_gl_cb(GObject *source_object, #ifdef CONFIG_GBM success = qemu_dbus_display1_listener_call_update_dmabuf_finish( ddl->proxy, res, &err); + if (!success) { + error_report("Failed to call update: %s", err->message); + } #endif #ifdef WIN32 success = qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d_finish( ddl->d3d11_proxy, res, &err); - d3d_texture2d_acquire0(ddl->d3d_texture, &error_warn); -#endif - if (!success) { error_report("Failed to call update: %s", err->message); } + d3d_texture2d_acquire0(ddl->d3d_texture, &error_warn); +#endif + graphic_hw_gl_block(ddl->dcl.con, false); g_object_unref(ddl); } From 59a4c0973aa1709d764d9479a5abd421a9408982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:03:58 +0400 Subject: [PATCH 1055/1794] build-sys: require -lrt when no shm_open() in std libs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fail during configure time if the shm functions are missing, as required by oslib-posix.c. Note, we could further check the presence of the function in librt. This is a minor cleanup/improvement. Signed-off-by: Marc-André Lureau Reviewed-by: Alex Bennée Message-ID: <20250924120426.2158655-2-marcandre.lureau@redhat.com> --- meson.build | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/meson.build b/meson.build index bdfb6214e6f20..c08e2df6bb262 100644 --- a/meson.build +++ b/meson.build @@ -1350,7 +1350,13 @@ if get_option('spice') \ endif spice_headers = spice.partial_dependency(compile_args: true, includes: true) -rt = cc.find_library('rt', required: false) +rt = not_found +if host_os != 'windows' + have_shm_open = cc.has_function('shm_open') + if not have_shm_open + rt = cc.find_library('rt', required: true) + endif +endif libiscsi = not_found if not get_option('libiscsi').auto() or have_block @@ -3812,14 +3818,10 @@ util_ss = util_ss.apply({}) libqemuutil = static_library('qemuutil', build_by_default: false, sources: util_ss.sources() + stub_ss.sources() + genh, - dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc]) -qemuutil_deps = [event_loop_base] -if host_os != 'windows' - qemuutil_deps += [rt] -endif + dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc, rt]) qemuutil = declare_dependency(link_with: libqemuutil, sources: genh + version_res, - dependencies: qemuutil_deps) + dependencies: [event_loop_base]) if have_system or have_user decodetree = generator(find_program('scripts/decodetree.py'), From 932ac9f8eb75c9db58dbf85f7de25c9eabf2261c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:03:59 +0400 Subject: [PATCH 1056/1794] gitlab-ci: fix 'needs' property type must be array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gitlab "Pipeline editor" has some warnings, and gitlab-ci-local fails. Read also from the docs https://docs.gitlab.com/ci/yaml/#needs "Supported values: An array of jobs (maximum of 50 jobs). An empty array ([]), to set the job to start as soon as the pipeline is created." Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-ID: <20250924120426.2158655-3-marcandre.lureau@redhat.com> --- .gitlab-ci.d/buildtest.yml | 56 +++++++++++++++++----------------- .gitlab-ci.d/crossbuilds.yml | 46 ++++++++++++++-------------- .gitlab-ci.d/static_checks.yml | 6 ++-- 3 files changed, 54 insertions(+), 54 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index e296fc3c14ae2..3be36e5499cd7 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -36,7 +36,7 @@ build-system-ubuntu: - .native_build_job_template - .native_build_artifact_template needs: - job: amd64-ubuntu2204-container + - job: amd64-ubuntu2204-container variables: IMAGE: ubuntu2204 CONFIGURE_ARGS: --enable-docs @@ -66,7 +66,7 @@ build-system-debian: - .native_build_job_template - .native_build_artifact_template needs: - job: amd64-debian-container + - job: amd64-debian-container variables: IMAGE: debian CONFIGURE_ARGS: --with-coroutine=sigaltstack --enable-rust @@ -109,7 +109,7 @@ build-system-fedora: - .native_build_job_template - .native_build_artifact_template needs: - job: amd64-fedora-container + - job: amd64-fedora-container variables: IMAGE: fedora CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs --enable-crypto-afalg --enable-rust @@ -122,7 +122,7 @@ build-system-fedora-rust-nightly: - .native_build_job_template - .native_build_artifact_template needs: - job: amd64-fedora-rust-nightly-container + - job: amd64-fedora-rust-nightly-container variables: IMAGE: fedora-rust-nightly CONFIGURE_ARGS: --disable-docs --enable-rust --enable-strict-rust-lints @@ -167,7 +167,7 @@ build-system-centos: - .native_build_job_template - .native_build_artifact_template needs: - job: amd64-centos9-container + - job: amd64-centos9-container variables: IMAGE: centos9 CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-vfio-user-server @@ -189,7 +189,7 @@ build-previous-qemu: - build-previous/tests/qtest/migration-test - build-previous/scripts needs: - job: amd64-opensuse-leap-container + - job: amd64-opensuse-leap-container variables: IMAGE: opensuse-leap TARGETS: x86_64-softmmu aarch64-softmmu @@ -274,7 +274,7 @@ build-system-opensuse: - .native_build_job_template - .native_build_artifact_template needs: - job: amd64-opensuse-leap-container + - job: amd64-opensuse-leap-container variables: IMAGE: opensuse-leap TARGETS: s390x-softmmu x86_64-softmmu aarch64-softmmu @@ -308,7 +308,7 @@ build-system-flaky: - .native_build_job_template - .native_build_artifact_template needs: - job: amd64-debian-container + - job: amd64-debian-container variables: IMAGE: debian QEMU_JOB_OPTIONAL: 1 @@ -338,7 +338,7 @@ functional-system-flaky: build-tcg-disabled: extends: .native_build_job_template needs: - job: amd64-centos9-container + - job: amd64-centos9-container variables: IMAGE: centos9 script: @@ -364,7 +364,7 @@ build-tcg-disabled: build-user: extends: .native_build_job_template needs: - job: amd64-debian-user-cross-container + - job: amd64-debian-user-cross-container variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --disable-system @@ -374,7 +374,7 @@ build-user: build-user-static: extends: .native_build_job_template needs: - job: amd64-debian-user-cross-container + - job: amd64-debian-user-cross-container variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --disable-system --static @@ -385,7 +385,7 @@ build-user-static: build-legacy: extends: .native_build_job_template needs: - job: amd64-debian-legacy-cross-container + - job: amd64-debian-legacy-cross-container variables: IMAGE: debian-legacy-test-cross TARGETS: alpha-linux-user alpha-softmmu sh4-linux-user @@ -395,7 +395,7 @@ build-legacy: build-user-hexagon: extends: .native_build_job_template needs: - job: hexagon-cross-container + - job: hexagon-cross-container variables: IMAGE: debian-hexagon-cross TARGETS: hexagon-linux-user @@ -408,7 +408,7 @@ build-user-hexagon: build-some-softmmu: extends: .native_build_job_template needs: - job: amd64-debian-user-cross-container + - job: amd64-debian-user-cross-container variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --enable-debug @@ -419,7 +419,7 @@ build-some-softmmu: build-loongarch64: extends: .native_build_job_template needs: - job: loongarch-debian-cross-container + - job: loongarch-debian-cross-container variables: IMAGE: debian-loongarch-cross CONFIGURE_ARGS: --disable-tools --enable-debug @@ -430,7 +430,7 @@ build-loongarch64: build-tricore-softmmu: extends: .native_build_job_template needs: - job: tricore-debian-cross-container + - job: tricore-debian-cross-container variables: IMAGE: debian-tricore-cross CONFIGURE_ARGS: --disable-tools --disable-fdt --enable-debug @@ -440,7 +440,7 @@ build-tricore-softmmu: clang-system: extends: .native_build_job_template needs: - job: amd64-fedora-container + - job: amd64-fedora-container variables: IMAGE: fedora CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-ubsan @@ -451,7 +451,7 @@ clang-system: clang-user: extends: .native_build_job_template needs: - job: amd64-debian-user-cross-container + - job: amd64-debian-user-cross-container timeout: 70m variables: IMAGE: debian-all-test-cross @@ -582,7 +582,7 @@ functional-cfi-x86_64: tsan-build: extends: .native_build_job_template needs: - job: amd64-ubuntu2204-container + - job: amd64-ubuntu2204-container variables: IMAGE: ubuntu2204 CONFIGURE_ARGS: --enable-tsan --cc=clang --cxx=clang++ @@ -596,7 +596,7 @@ tsan-build: gcov: extends: .native_build_job_template needs: - job: amd64-ubuntu2204-container + - job: amd64-ubuntu2204-container timeout: 80m variables: IMAGE: ubuntu2204 @@ -623,7 +623,7 @@ gcov: build-oss-fuzz: extends: .native_build_job_template needs: - job: amd64-fedora-container + - job: amd64-fedora-container variables: IMAGE: fedora script: @@ -645,7 +645,7 @@ build-oss-fuzz: build-tci: extends: .native_build_job_template needs: - job: amd64-debian-user-cross-container + - job: amd64-debian-user-cross-container variables: IMAGE: debian-all-test-cross script: @@ -670,7 +670,7 @@ build-tci: build-without-defaults: extends: .native_build_job_template needs: - job: amd64-centos9-container + - job: amd64-centos9-container variables: IMAGE: centos9 CONFIGURE_ARGS: @@ -688,7 +688,7 @@ build-libvhost-user: stage: build image: $CI_REGISTRY_IMAGE/qemu/fedora:$QEMU_CI_CONTAINER_TAG needs: - job: amd64-fedora-container + - job: amd64-fedora-container script: - mkdir subprojects/libvhost-user/build - cd subprojects/libvhost-user/build @@ -702,9 +702,9 @@ build-tools-and-docs-debian: - .native_build_job_template - .native_build_artifact_template needs: - job: amd64-debian-container - # when running on 'master' we use pre-existing container - optional: true + - job: amd64-debian-container + # when running on 'master' we use pre-existing container + optional: true variables: IMAGE: debian MAKE_CHECK_ARGS: check-unit ctags TAGS cscope @@ -791,7 +791,7 @@ build-wasm: extends: .wasm_build_job_template timeout: 2h needs: - job: wasm-emsdk-cross-container + - job: wasm-emsdk-cross-container variables: IMAGE: emsdk-wasm32-cross CONFIGURE_ARGS: --static --disable-tools --enable-debug --enable-tcg-interpreter diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml index 3f76c901ba8bf..8ff0c27f74d65 100644 --- a/.gitlab-ci.d/crossbuilds.yml +++ b/.gitlab-ci.d/crossbuilds.yml @@ -4,28 +4,28 @@ include: cross-armhf-user: extends: .cross_user_build_job needs: - job: armhf-debian-cross-container + - job: armhf-debian-cross-container variables: IMAGE: debian-armhf-cross cross-arm64-system: extends: .cross_system_build_job needs: - job: arm64-debian-cross-container + - job: arm64-debian-cross-container variables: IMAGE: debian-arm64-cross cross-arm64-user: extends: .cross_user_build_job needs: - job: arm64-debian-cross-container + - job: arm64-debian-cross-container variables: IMAGE: debian-arm64-cross cross-arm64-kvm-only: extends: .cross_accel_build_job needs: - job: arm64-debian-cross-container + - job: arm64-debian-cross-container variables: IMAGE: debian-arm64-cross EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-features @@ -35,7 +35,7 @@ cross-i686-system: - .cross_system_build_job - .cross_test_artifacts needs: - job: i686-debian-cross-container + - job: i686-debian-cross-container variables: IMAGE: debian-i686-cross EXTRA_CONFIGURE_OPTS: --disable-kvm @@ -46,7 +46,7 @@ cross-i686-user: - .cross_user_build_job - .cross_test_artifacts needs: - job: i686-debian-cross-container + - job: i686-debian-cross-container variables: IMAGE: debian-i686-cross MAKE_CHECK_ARGS: check @@ -57,7 +57,7 @@ cross-i686-tci: - .cross_test_artifacts timeout: 60m needs: - job: i686-debian-cross-container + - job: i686-debian-cross-container variables: IMAGE: debian-i686-cross ACCEL: tcg-interpreter @@ -71,49 +71,49 @@ cross-i686-tci: cross-mipsel-system: extends: .cross_system_build_job needs: - job: mipsel-debian-cross-container + - job: mipsel-debian-cross-container variables: IMAGE: debian-mipsel-cross cross-mipsel-user: extends: .cross_user_build_job needs: - job: mipsel-debian-cross-container + - job: mipsel-debian-cross-container variables: IMAGE: debian-mipsel-cross cross-mips64el-system: extends: .cross_system_build_job needs: - job: mips64el-debian-cross-container + - job: mips64el-debian-cross-container variables: IMAGE: debian-mips64el-cross cross-mips64el-user: extends: .cross_user_build_job needs: - job: mips64el-debian-cross-container + - job: mips64el-debian-cross-container variables: IMAGE: debian-mips64el-cross cross-ppc64el-system: extends: .cross_system_build_job needs: - job: ppc64el-debian-cross-container + - job: ppc64el-debian-cross-container variables: IMAGE: debian-ppc64el-cross cross-ppc64el-user: extends: .cross_user_build_job needs: - job: ppc64el-debian-cross-container + - job: ppc64el-debian-cross-container variables: IMAGE: debian-ppc64el-cross cross-ppc64el-kvm-only: extends: .cross_accel_build_job needs: - job: ppc64el-debian-cross-container + - job: ppc64el-debian-cross-container variables: IMAGE: debian-ppc64el-cross EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-devices @@ -121,35 +121,35 @@ cross-ppc64el-kvm-only: cross-riscv64-system: extends: .cross_system_build_job needs: - job: riscv64-debian-cross-container + - job: riscv64-debian-cross-container variables: IMAGE: debian-riscv64-cross cross-riscv64-user: extends: .cross_user_build_job needs: - job: riscv64-debian-cross-container + - job: riscv64-debian-cross-container variables: IMAGE: debian-riscv64-cross cross-s390x-system: extends: .cross_system_build_job needs: - job: s390x-debian-cross-container + - job: s390x-debian-cross-container variables: IMAGE: debian-s390x-cross cross-s390x-user: extends: .cross_user_build_job needs: - job: s390x-debian-cross-container + - job: s390x-debian-cross-container variables: IMAGE: debian-s390x-cross cross-s390x-kvm-only: extends: .cross_accel_build_job needs: - job: s390x-debian-cross-container + - job: s390x-debian-cross-container variables: IMAGE: debian-s390x-cross EXTRA_CONFIGURE_OPTS: --disable-tcg --enable-trace-backends=ftrace @@ -157,7 +157,7 @@ cross-s390x-kvm-only: cross-mips64el-kvm-only: extends: .cross_accel_build_job needs: - job: mips64el-debian-cross-container + - job: mips64el-debian-cross-container variables: IMAGE: debian-mips64el-cross EXTRA_CONFIGURE_OPTS: --disable-tcg --target-list=mips64el-softmmu @@ -165,7 +165,7 @@ cross-mips64el-kvm-only: cross-win64-system: extends: .cross_system_build_job needs: - job: win64-fedora-cross-container + - job: win64-fedora-cross-container variables: IMAGE: fedora-win64-cross EXTRA_CONFIGURE_OPTS: --enable-fdt=internal --disable-plugins @@ -181,7 +181,7 @@ cross-win64-system: cross-amd64-xen-only: extends: .cross_accel_build_job needs: - job: amd64-debian-cross-container + - job: amd64-debian-cross-container variables: IMAGE: debian-amd64-cross ACCEL: xen @@ -190,7 +190,7 @@ cross-amd64-xen-only: cross-arm64-xen-only: extends: .cross_accel_build_job needs: - job: arm64-debian-cross-container + - job: arm64-debian-cross-container variables: IMAGE: debian-arm64-cross ACCEL: xen diff --git a/.gitlab-ci.d/static_checks.yml b/.gitlab-ci.d/static_checks.yml index c3ed6de453d07..61fe2fa39ab28 100644 --- a/.gitlab-ci.d/static_checks.yml +++ b/.gitlab-ci.d/static_checks.yml @@ -32,7 +32,7 @@ check-python-minreqs: variables: GIT_DEPTH: 1 needs: - job: python-container + - job: python-container check-python-tox: extends: .base_job_template @@ -45,7 +45,7 @@ check-python-tox: QEMU_TOX_EXTRA_ARGS: --skip-missing-interpreters=false QEMU_JOB_OPTIONAL: 1 needs: - job: python-container + - job: python-container check-rust-tools-nightly: extends: .base_job_template @@ -76,7 +76,7 @@ check-build-units: stage: build image: $CI_REGISTRY_IMAGE/qemu/debian:$QEMU_CI_CONTAINER_TAG needs: - job: amd64-debian-container + - job: amd64-debian-container before_script: - source scripts/ci/gitlab-ci-section - section_start setup "Install Tools" From bf3706d2c3433bc14431d260a66210c58201d8ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:00 +0400 Subject: [PATCH 1057/1794] scripts/archive-source: speed up downloading subprojects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Running meson on each subproject is quite slow. According to Paolo, meson will run download tasks in parallel. Signed-off-by: Marc-André Lureau Reviewed-by: Alex Bennée Message-ID: <20250924120426.2158655-4-marcandre.lureau@redhat.com> --- scripts/archive-source.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index 476a996a70d52..34ea86b6d3d71 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -77,9 +77,10 @@ function subproject_dir() { git archive --format tar "$(tree_ish)" > "$tar_file" test $? -ne 0 && error "failed to archive qemu" +meson subprojects download $subprojects +test $? -ne 0 && error "failed to download subprojects $subprojects" + for sp in $subprojects; do - meson subprojects download $sp - test $? -ne 0 && error "failed to download subproject $sp" tar --append --file "$tar_file" --exclude=.git subprojects/"$(subproject_dir $sp)" test $? -ne 0 && error "failed to append subproject $sp to $tar_file" done From 027d05c5c41dd5ac822e60e245aff30bc4f2fa69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:01 +0400 Subject: [PATCH 1058/1794] scripts/archive-source: silence subprojects downloads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's too verbose. Signed-off-by: Marc-André Lureau Reviewed-by: Alex Bennée Message-ID: <20250924120426.2158655-5-marcandre.lureau@redhat.com> --- scripts/archive-source.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index 34ea86b6d3d71..b86e632161c8e 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -77,7 +77,7 @@ function subproject_dir() { git archive --format tar "$(tree_ish)" > "$tar_file" test $? -ne 0 && error "failed to archive qemu" -meson subprojects download $subprojects +meson subprojects download $subprojects >/dev/null test $? -ne 0 && error "failed to download subprojects $subprojects" for sp in $subprojects; do From 51a6211908c898a9994d9323b5937fec70605e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:02 +0400 Subject: [PATCH 1059/1794] scripts/archive-source: use a bash array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Alex Bennée Message-ID: <20250924120426.2158655-6-marcandre.lureau@redhat.com> --- scripts/archive-source.sh | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index b86e632161c8e..a725dd923dc81 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -26,12 +26,27 @@ sub_file="${sub_tdir}/submodule.tar" # independent of what the developer currently has initialized # in their checkout, because the build environment is completely # different to the host OS. -subprojects="keycodemapdb libvfio-user berkeley-softfloat-3 - berkeley-testfloat-3 anyhow-1-rs arbitrary-int-1-rs attrs-0.2-rs bilge-0.2-rs - bilge-impl-0.2-rs either-1-rs foreign-0.3-rs itertools-0.11-rs - libc-0.2-rs proc-macro2-1-rs - proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs - syn-2-rs unicode-ident-1-rs" +subprojects=( + anyhow-1-rs + arbitrary-int-1-rs + attrs-0.2-rs + berkeley-softfloat-3 + berkeley-testfloat-3 + bilge-0.2-rs + bilge-impl-0.2-rs + either-1-rs + foreign-0.3-rs + itertools-0.11-rs + keycodemapdb + libc-0.2-rs + libvfio-user + proc-macro-error-1-rs + proc-macro-error-attr-1-rs + proc-macro2-1-rs + quote-1-rs + syn-2-rs + unicode-ident-1-rs +) sub_deinit="" function cleanup() { @@ -77,10 +92,10 @@ function subproject_dir() { git archive --format tar "$(tree_ish)" > "$tar_file" test $? -ne 0 && error "failed to archive qemu" -meson subprojects download $subprojects >/dev/null +meson subprojects download ${subprojects[@]} >/dev/null test $? -ne 0 && error "failed to download subprojects $subprojects" -for sp in $subprojects; do +for sp in "${subprojects[@]}"; do tar --append --file "$tar_file" --exclude=.git subprojects/"$(subproject_dir $sp)" test $? -ne 0 && error "failed to append subproject $sp to $tar_file" done From c102f036bf601434de7d83f861bb74b5d2efc6da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:03 +0400 Subject: [PATCH 1060/1794] configure: fix rust meson configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was incorrectly set on the [host_machine] and caused error: File "/tmp/qemu-test/build/pyvenv/lib/python3.11/site-packages/mesonbuild/envconfig.py", line 281, in from_literal assert all(isinstance(v, str) for v in raw.values()), 'for mypy' AssertionError: for mypy Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-ID: <20250924120426.2158655-7-marcandre.lureau@redhat.com> --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 0f7eb95586b02..66613f3f4c645 100755 --- a/configure +++ b/configure @@ -1958,7 +1958,7 @@ if test "$skip_meson" = no; then echo "[binaries]" >> $native echo "c = [$(meson_quote $host_cc)]" >> $native if test "$rust" != disabled; then - echo "rust = [$(meson_quote $rustc)]" >> $cross + echo "rust = [$(meson_quote $rustc)]" >> $native fi mv $native config-meson.native meson_option_add --native-file From 0d4fb8f746d7ff215146dbd96efbe9cc87cb98ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:04 +0400 Subject: [PATCH 1061/1794] configure: set the bindgen cross target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement a bash version of rust-bindgen rust_to_clang_target() to convert from rust target to clang target. Signed-off-by: Marc-André Lureau Reviewed-by: Paolo Bonzini Message-ID: <20250924120426.2158655-8-marcandre.lureau@redhat.com> --- configure | 7 ++++ scripts/rust-to-clang-target-test.sh | 43 ++++++++++++++++++++ scripts/rust-to-clang-target.sh | 60 ++++++++++++++++++++++++++++ tests/lcitool/libvirt-ci | 2 +- 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100755 scripts/rust-to-clang-target-test.sh create mode 100644 scripts/rust-to-clang-target.sh diff --git a/configure b/configure index 66613f3f4c645..bf964947b8a2a 100755 --- a/configure +++ b/configure @@ -1878,6 +1878,13 @@ if test "$skip_meson" = no; then eval "c=\$devices_${a}" echo "${a}-softmmu = '$c'" >> $cross done + if test "$rust" != disabled; then + if test "$cross_compile" = "yes"; then + . "$source_path/scripts/rust-to-clang-target.sh" + clang_target=$(rust_to_clang_target "$rust_target_triple") + echo "bindgen_clang_arguments = [$(meson_quote --target="$clang_target")]" >> $cross + fi + fi echo "[built-in options]" >> $cross echo "c_args = [$(meson_quote $CFLAGS $EXTRA_CFLAGS)]" >> $cross diff --git a/scripts/rust-to-clang-target-test.sh b/scripts/rust-to-clang-target-test.sh new file mode 100755 index 0000000000000..ff6f8fcdc56e9 --- /dev/null +++ b/scripts/rust-to-clang-target-test.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env sh +# +# Copyright (C) 2025 Red Hat, Inc. +# +# Based on rust_to_clang_target() tests from rust-bindgen. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +scripts_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +. "$scripts_dir/rust-to-clang-target.sh" + +test_case() { + input="$1" + expected="$2" + result=$(rust_to_clang_target "$input") + + if [ "$result" = "$expected" ]; then + echo " OK: '$input' -> '$result'" + else + echo " FAILED: '$input'" + echo " Expected: '$expected'" + echo " Got: '$result'" + exit 1 + fi +} + +echo "Running tests..." + +test_case "aarch64-apple-ios" "arm64-apple-ios" +test_case "riscv64gc-unknown-linux-gnu" "riscv64-unknown-linux-gnu" +test_case "riscv64imac-unknown-none-elf" "riscv64-unknown-none-elf" +test_case "riscv32imc-unknown-none-elf" "riscv32-unknown-none-elf" +test_case "riscv32imac-unknown-none-elf" "riscv32-unknown-none-elf" +test_case "riscv32imafc-unknown-none-elf" "riscv32-unknown-none-elf" +test_case "riscv32i-unknown-none-elf" "riscv32-unknown-none-elf" +test_case "riscv32imc-esp-espidf" "riscv32-esp-elf" +test_case "xtensa-esp32-espidf" "xtensa-esp32-elf" +test_case "aarch64-apple-ios-sim" "arm64-apple-ios-simulator" +test_case "aarch64-apple-tvos-sim" "arm64-apple-tvos-simulator" +test_case "aarch64-apple-watchos-sim" "arm64-apple-watchos-simulator" + +echo "" +echo "All tests passed!" diff --git a/scripts/rust-to-clang-target.sh b/scripts/rust-to-clang-target.sh new file mode 100644 index 0000000000000..72db7e1300f7f --- /dev/null +++ b/scripts/rust-to-clang-target.sh @@ -0,0 +1,60 @@ +# Copyright (C) 2025 Red Hat, Inc. +# +# Based on rust_to_clang_target() from rust-bindgen. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +rust_to_clang_target() { + rust_target="$1" + + # Split the string by hyphens + triple_parts="" + old_IFS="$IFS" + IFS='-' + for part in $rust_target; do + triple_parts="$triple_parts $part" + done + IFS="$old_IFS" + set -- $triple_parts + + # RISC-V + case "$1" in + riscv32*) + set -- "riscv32" "${2}" "${3}" "${4}" + ;; + riscv64*) + set -- "riscv64" "${2}" "${3}" "${4}" + ;; + esac + + # Apple + if [ "$2" = "apple" ]; then + if [ "$1" = "aarch64" ]; then + set -- "arm64" "${2}" "${3}" "${4}" + fi + if [ "$4" = "sim" ]; then + set -- "${1}" "${2}" "${3}" "simulator" + fi + fi + + # ESP-IDF + if [ "$3" = "espidf" ]; then + set -- "${1}" "${2}" "elf" "${4}" + fi + + # Reassemble the string + new_triple="" + first=1 + for part in "$@"; do + if [ -n "$part" ]; then + if [ "$first" -eq 1 ]; then + new_triple="$part" + first=0 + else + new_triple="$new_triple-$part" + fi + fi + done + + echo "$new_triple" +} diff --git a/tests/lcitool/libvirt-ci b/tests/lcitool/libvirt-ci index 18c4bfe02c467..9da20ff7c3bc9 160000 --- a/tests/lcitool/libvirt-ci +++ b/tests/lcitool/libvirt-ci @@ -1 +1 @@ -Subproject commit 18c4bfe02c467e5639bf9a687139735ccd7a3fff +Subproject commit 9da20ff7c3bc9067804a7561c2ff87583b434853 From cb29fda6c9a5bf7bf945e53b05f53c2c90542f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:05 +0400 Subject: [PATCH 1062/1794] tests/docker/common: print errors to stderr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-ID: <20250924120426.2158655-9-marcandre.lureau@redhat.com> --- tests/docker/common.rc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/docker/common.rc b/tests/docker/common.rc index a611e6adf970c..2ed2365a615ba 100755 --- a/tests/docker/common.rc +++ b/tests/docker/common.rc @@ -50,7 +50,7 @@ configure_qemu() echo "Configure options:" echo $config_opts $QEMU_SRC/configure $config_opts || \ - { cat config.log && test_fail "Failed to run 'configure'"; } + { cat config.log >&2 && test_fail "Failed to run 'configure'"; } } build_qemu() @@ -73,7 +73,7 @@ check_qemu() test_fail() { - echo "$@" + echo "$@" >&2 exit 1 } From 1e752fcc979c1a2ced73967956ddbac178540cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:06 +0400 Subject: [PATCH 1063/1794] tests/docker: use fully qualified image name for emsdk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without it, at least it fails with podman on fc42: [1/6] STEP 1/15: FROM emscripten/emsdk:3.1.50 AS build-base Error: creating build container: short-name resolution enforced but cannot prompt without a TTY Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Reviewed-by: Kohei Tokunaga Message-ID: <20250924120426.2158655-10-marcandre.lureau@redhat.com> --- tests/docker/dockerfiles/emsdk-wasm32-cross.docker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker/dockerfiles/emsdk-wasm32-cross.docker b/tests/docker/dockerfiles/emsdk-wasm32-cross.docker index 60a7d02f5613e..6b1642a207c74 100644 --- a/tests/docker/dockerfiles/emsdk-wasm32-cross.docker +++ b/tests/docker/dockerfiles/emsdk-wasm32-cross.docker @@ -8,7 +8,7 @@ ARG PIXMAN_VERSION=0.44.2 ARG FFI_VERSION=v3.4.7 ARG MESON_VERSION=1.5.0 -FROM emscripten/emsdk:$EMSDK_VERSION_QEMU AS build-base +FROM docker.io/emscripten/emsdk:$EMSDK_VERSION_QEMU AS build-base ARG MESON_VERSION ENV TARGET=/builddeps/target ENV CPATH="$TARGET/include" From 59352b789a48cd8f2f946a2e86c50230b66099b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:07 +0400 Subject: [PATCH 1064/1794] tests/docker/common: print meson log on configure failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-ID: <20250924120426.2158655-11-marcandre.lureau@redhat.com> --- tests/docker/common.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker/common.rc b/tests/docker/common.rc index 2ed2365a615ba..d202c0c666598 100755 --- a/tests/docker/common.rc +++ b/tests/docker/common.rc @@ -50,7 +50,7 @@ configure_qemu() echo "Configure options:" echo $config_opts $QEMU_SRC/configure $config_opts || \ - { cat config.log >&2 && test_fail "Failed to run 'configure'"; } + { cat config.log >&2 ; cat meson-logs/meson-log.txt >&2 ; test_fail "Failed to run 'configure'"; } } build_qemu() From c3ff31fa2b441272f85c8c4a190ec2c31d282d10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:08 +0400 Subject: [PATCH 1065/1794] build-sys: cfi_debug and safe_stack are not compatible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It fails to link on fedora >= 41: /usr/bin/ld: /usr/bin/../lib/clang/20/lib/x86_64-redhat-linux-gnu/libclang_rt.safestack.a(safestack.cpp.o): in function `__sanitizer_internal_memcpy': (.text.__sanitizer_internal_memcpy+0x0): multiple definition of `__sanitizer_internal_memcpy'; /usr/bin/../lib/clang/20/lib/x86_64-redhat-linux-gnu/libclang_rt.ubsan_standalone.a(sanitizer_libc.cpp.o):(.text.__sanitizer_internal_memcpy+0x0): first defined here /usr/bin/ld: /usr/bin/../lib/clang/20/lib/x86_64-redhat-linux-gnu/libclang_rt.safestack.a(safestack.cpp.o): in function `__sanitizer_internal_memmove': (.text.__sanitizer_internal_memmove+0x0): multiple definition of `__sanitizer_internal_memmove'; /usr/bin/../lib/clang/20/lib/x86_64-redhat-linux-gnu/libclang_rt.ubsan_standalone.a(sanitizer_libc.cpp.o):(.text.__sanitizer_internal_memmove+0x0): first defined here /usr/bin/ld: /usr/bin/../lib/clang/20/lib/x86_64-redhat-linux-gnu/libclang_rt.safestack.a(safestack.cpp.o): in function `__sanitizer_internal_memset': (.text.__sanitizer_internal_memset+0x0): multiple definition of `__sanitizer_internal_memset'; /usr/bin/../lib/clang/20/lib/x86_64-redhat-linux-gnu/libclang_rt.ubsan_standalone.a(sanitizer_libc.cpp.o):(.text.__sanitizer_internal_memset+0x0): first defined here cfi_debug seems to pull ubsan which has conflicting symbols with safe_stack. See also: https://bugzilla.redhat.com/show_bug.cgi?id=2397265 Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-ID: <20250924120426.2158655-12-marcandre.lureau@redhat.com> --- .gitlab-ci.d/buildtest.yml | 6 +++--- meson.build | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 3be36e5499cd7..0502094b9aa67 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -479,7 +479,7 @@ build-cfi-aarch64: LD_JOBS: 1 AR: llvm-ar IMAGE: fedora - CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug + CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-safe-stack --disable-slirp TARGETS: aarch64-softmmu MAKE_CHECK_ARGS: check-build @@ -517,7 +517,7 @@ build-cfi-ppc64-s390x: LD_JOBS: 1 AR: llvm-ar IMAGE: fedora - CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug + CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-safe-stack --disable-slirp TARGETS: ppc64-softmmu s390x-softmmu MAKE_CHECK_ARGS: check-build @@ -555,7 +555,7 @@ build-cfi-x86_64: LD_JOBS: 1 AR: llvm-ar IMAGE: fedora - CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug + CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-safe-stack --disable-slirp TARGETS: x86_64-softmmu MAKE_CHECK_ARGS: check-build diff --git a/meson.build b/meson.build index c08e2df6bb262..14e626aa1ee05 100644 --- a/meson.build +++ b/meson.build @@ -681,6 +681,9 @@ if get_option('cfi') error('-fsanitize-cfi-icall-generalize-pointers is not supported by the compiler') endif if get_option('cfi_debug') + if get_option('safe_stack') + error('cfi_debug is not compatible with safe_stack') + endif if cc.compiles('int main () { return 0; }', name: '-fno-sanitize-trap=cfi-icall', args: ['-flto', '-fsanitize=cfi-icall', From a679aa8e434cafceea95de11cb893ed598d0de03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:09 +0400 Subject: [PATCH 1066/1794] lcitool: update, switch to f41 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Newer lcitool version has various fixes helping QEMU CI and this series. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-ID: <20250924120426.2158655-13-marcandre.lureau@redhat.com> --- scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml | 3 ++- scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml | 3 ++- tests/docker/dockerfiles/alpine.docker | 2 +- tests/docker/dockerfiles/debian-amd64-cross.docker | 5 ++++- tests/docker/dockerfiles/debian-arm64-cross.docker | 5 ++++- tests/docker/dockerfiles/debian-armhf-cross.docker | 5 ++++- tests/docker/dockerfiles/debian-i686-cross.docker | 5 ++++- .../dockerfiles/debian-mips64el-cross.docker | 5 ++++- .../docker/dockerfiles/debian-mipsel-cross.docker | 5 ++++- .../docker/dockerfiles/debian-ppc64el-cross.docker | 5 ++++- .../docker/dockerfiles/debian-riscv64-cross.docker | 8 ++++++-- tests/docker/dockerfiles/debian-s390x-cross.docker | 5 ++++- tests/docker/dockerfiles/debian.docker | 5 ++++- .../docker/dockerfiles/fedora-rust-nightly.docker | 14 ++++++++------ tests/docker/dockerfiles/fedora-win64-cross.docker | 11 +++++++---- tests/docker/dockerfiles/fedora.docker | 14 ++++++++------ tests/docker/dockerfiles/opensuse-leap.docker | 4 ++-- tests/docker/dockerfiles/ubuntu2204.docker | 5 ++++- tests/lcitool/projects/qemu.yml | 1 - tests/lcitool/refresh | 6 +++--- 20 files changed, 79 insertions(+), 37 deletions(-) diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml index f11e9808267e0..bbcb5dd4aca22 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml @@ -95,7 +95,6 @@ packages: - llvm - locales - make - - meson - mtools - multipath-tools - ncat @@ -108,10 +107,12 @@ packages: - python3-opencv - python3-pillow - python3-pip + - python3-setuptools - python3-sphinx - python3-sphinx-rtd-theme - python3-tomli - python3-venv + - python3-wheel - python3-yaml - rpm2cpio - rustc-1.77 diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml index 6559cb29343f9..00eb1b0f91a90 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml @@ -93,7 +93,6 @@ packages: - llvm - locales - make - - meson - mtools - multipath-tools - ncat @@ -106,10 +105,12 @@ packages: - python3-opencv - python3-pillow - python3-pip + - python3-setuptools - python3-sphinx - python3-sphinx-rtd-theme - python3-tomli - python3-venv + - python3-wheel - python3-yaml - rpm2cpio - rustc-1.77 diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index bf3bd5a30dd95..bd1ef5505da15 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -78,7 +78,7 @@ RUN apk update && \ nmap-ncat \ numactl-dev \ openssh-client \ - pcre-dev \ + pcre2-dev \ pipewire-dev \ pixman-dev \ pkgconf \ diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index 081f3e00f7bc6..bba6de4e2d513 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -34,7 +34,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ llvm \ locales \ make \ - meson \ mtools \ ncat \ ninja-build \ @@ -45,9 +44,11 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ + python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ + python3-wheel \ python3-yaml \ rpm2cpio \ rustc-web \ @@ -67,6 +68,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED +RUN /usr/bin/pip3 install meson==1.8.1 + ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index 91c555a36e977..8bbcd75157884 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -34,7 +34,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ llvm \ locales \ make \ - meson \ mtools \ ncat \ ninja-build \ @@ -45,9 +44,11 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ + python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ + python3-wheel \ python3-yaml \ rpm2cpio \ rustc-web \ @@ -67,6 +68,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED +RUN /usr/bin/pip3 install meson==1.8.1 + ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index f0e2efcda099f..455b84424c4ef 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -34,7 +34,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ llvm \ locales \ make \ - meson \ mtools \ ncat \ ninja-build \ @@ -45,9 +44,11 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ + python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ + python3-wheel \ python3-yaml \ rpm2cpio \ rustc-web \ @@ -67,6 +68,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED +RUN /usr/bin/pip3 install meson==1.8.1 + ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index 025beb1ce25b7..8b8993ffe4415 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -34,7 +34,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ llvm \ locales \ make \ - meson \ mtools \ ncat \ ninja-build \ @@ -45,9 +44,11 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ + python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ + python3-wheel \ python3-yaml \ rpm2cpio \ rustc-web \ @@ -67,6 +68,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED +RUN /usr/bin/pip3 install meson==1.8.1 + ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index 4a941dd870eec..c89cb6c68036d 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -34,7 +34,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ llvm \ locales \ make \ - meson \ mtools \ ncat \ ninja-build \ @@ -45,9 +44,11 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ + python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ + python3-wheel \ python3-yaml \ rpm2cpio \ rustc-web \ @@ -67,6 +68,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED +RUN /usr/bin/pip3 install meson==1.8.1 + ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index 4d3e5d711bdc5..4e10d9501fc64 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -34,7 +34,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ llvm \ locales \ make \ - meson \ mtools \ ncat \ ninja-build \ @@ -45,9 +44,11 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ + python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ + python3-wheel \ python3-yaml \ rpm2cpio \ rustc-web \ @@ -67,6 +68,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED +RUN /usr/bin/pip3 install meson==1.8.1 + ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index 22b4457ba996a..cf61cb7f4aa7e 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -34,7 +34,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ llvm \ locales \ make \ - meson \ mtools \ ncat \ ninja-build \ @@ -45,9 +44,11 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ + python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ + python3-wheel \ python3-yaml \ rpm2cpio \ rustc-web \ @@ -67,6 +68,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED +RUN /usr/bin/pip3 install meson==1.8.1 + ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker index b0386cd3a1fc9..79ec37616d169 100644 --- a/tests/docker/dockerfiles/debian-riscv64-cross.docker +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -4,7 +4,7 @@ # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:trixie-slim +FROM docker.io/library/debian:13-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -23,11 +23,13 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglib2.0-dev \ locales \ make \ - meson \ ninja-build \ pkgconf \ python3 \ + python3-pip \ + python3-setuptools \ python3-venv \ + python3-wheel \ sed \ tar && \ eatmydata apt-get autoremove -y && \ @@ -36,6 +38,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED +RUN /usr/bin/pip3 install meson==1.8.1 + ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index 13ec52c8ad0b7..1782e0e90f598 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -34,7 +34,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ llvm \ locales \ make \ - meson \ mtools \ ncat \ ninja-build \ @@ -45,9 +44,11 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ + python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ + python3-wheel \ python3-yaml \ rpm2cpio \ rustc-web \ @@ -67,6 +68,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED +RUN /usr/bin/pip3 install meson==1.8.1 + ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker index 0a57c1a1d3753..62f89e4d8c37f 100644 --- a/tests/docker/dockerfiles/debian.docker +++ b/tests/docker/dockerfiles/debian.docker @@ -104,7 +104,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ llvm \ locales \ make \ - meson \ mtools \ multipath-tools \ ncat \ @@ -117,9 +116,11 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ + python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ + python3-wheel \ python3-yaml \ rpm2cpio \ rustc-web \ @@ -146,6 +147,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc +RUN /usr/bin/pip3 install meson==1.8.1 + ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" diff --git a/tests/docker/dockerfiles/fedora-rust-nightly.docker b/tests/docker/dockerfiles/fedora-rust-nightly.docker index 4a033309b38f5..fd71dd8790247 100644 --- a/tests/docker/dockerfiles/fedora-rust-nightly.docker +++ b/tests/docker/dockerfiles/fedora-rust-nightly.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all fedora-40 qemu +# $ lcitool dockerfile --layers all fedora-41 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM registry.fedoraproject.org/fedora:40 +FROM registry.fedoraproject.org/fedora:41 RUN dnf install -y nosync && \ printf '#!/bin/sh\n\ @@ -91,7 +91,6 @@ exec "$@"\n' > /usr/bin/nosync && \ lzo-devel \ make \ mesa-libgbm-devel \ - meson \ mtools \ ncurses-devel \ nettle-devel \ @@ -100,7 +99,7 @@ exec "$@"\n' > /usr/bin/nosync && \ numactl-devel \ openssh-clients \ pam-devel \ - pcre-static \ + pcre2-static \ pipewire-devel \ pixman-devel \ pkgconfig \ @@ -111,9 +110,10 @@ exec "$@"\n' > /usr/bin/nosync && \ python3-opencv \ python3-pillow \ python3-pip \ + python3-setuptools \ python3-sphinx \ python3-sphinx_rtd_theme \ - python3-zombie-imp \ + python3-wheel \ rdma-core-devel \ rust \ sed \ @@ -124,7 +124,7 @@ exec "$@"\n' > /usr/bin/nosync && \ spice-server-devel \ swtpm \ systemd-devel \ - systemtap-sdt-devel \ + systemtap-sdt-dtrace \ tar \ tesseract \ tesseract-langpack-eng \ @@ -148,6 +148,8 @@ exec "$@"\n' > /usr/bin/nosync && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc +RUN /usr/bin/pip3 install meson==1.8.1 + ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index a950344402704..ed7d270984d14 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch mingw64 fedora-40 qemu,qemu-win-installer +# $ lcitool dockerfile --layers all --cross-arch mingw64 fedora-41 qemu,qemu-win-installer # # https://gitlab.com/libvirt/libvirt-ci -FROM registry.fedoraproject.org/fedora:40 +FROM registry.fedoraproject.org/fedora:41 RUN dnf install -y nosync && \ printf '#!/bin/sh\n\ @@ -38,7 +38,6 @@ exec "$@"\n' > /usr/bin/nosync && \ hostname \ llvm \ make \ - meson \ mtools \ ninja-build \ nmap-ncat \ @@ -49,9 +48,10 @@ exec "$@"\n' > /usr/bin/nosync && \ python3-opencv \ python3-pillow \ python3-pip \ + python3-setuptools \ python3-sphinx \ python3-sphinx_rtd_theme \ - python3-zombie-imp \ + python3-wheel \ rust \ sed \ socat \ @@ -69,6 +69,8 @@ exec "$@"\n' > /usr/bin/nosync && \ nosync dnf clean all -y && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED +RUN /usr/bin/pip3 install meson==1.8.1 + ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" @@ -90,6 +92,7 @@ RUN nosync dnf install -y \ mingw64-gtk-vnc2 \ mingw64-gtk3 \ mingw64-libepoxy \ + mingw64-libfdt \ mingw64-libgcrypt \ mingw64-libjpeg-turbo \ mingw64-libpng \ diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 014e3ccf17dde..e367c53c09a80 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all fedora-40 qemu +# $ lcitool dockerfile --layers all fedora-41 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM registry.fedoraproject.org/fedora:40 +FROM registry.fedoraproject.org/fedora:41 RUN dnf install -y nosync && \ printf '#!/bin/sh\n\ @@ -91,7 +91,6 @@ exec "$@"\n' > /usr/bin/nosync && \ lzo-devel \ make \ mesa-libgbm-devel \ - meson \ mtools \ ncurses-devel \ nettle-devel \ @@ -100,7 +99,7 @@ exec "$@"\n' > /usr/bin/nosync && \ numactl-devel \ openssh-clients \ pam-devel \ - pcre-static \ + pcre2-static \ pipewire-devel \ pixman-devel \ pkgconfig \ @@ -111,9 +110,10 @@ exec "$@"\n' > /usr/bin/nosync && \ python3-opencv \ python3-pillow \ python3-pip \ + python3-setuptools \ python3-sphinx \ python3-sphinx_rtd_theme \ - python3-zombie-imp \ + python3-wheel \ rdma-core-devel \ rust \ sed \ @@ -124,7 +124,7 @@ exec "$@"\n' > /usr/bin/nosync && \ spice-server-devel \ swtpm \ systemd-devel \ - systemtap-sdt-devel \ + systemtap-sdt-dtrace \ tar \ tesseract \ tesseract-langpack-eng \ @@ -148,6 +148,8 @@ exec "$@"\n' > /usr/bin/nosync && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc +RUN /usr/bin/pip3 install meson==1.8.1 + ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index e90225dc23518..60763857bb367 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -89,7 +89,7 @@ RUN zypper update -y && \ ninja \ openssh \ pam-devel \ - pcre-devel-static \ + pcre2-devel-static \ pipewire-devel \ pkgconfig \ python311 \ @@ -132,7 +132,7 @@ RUN zypper update -y && \ RUN /usr/bin/pip3.11 install \ PyYAML \ - meson==1.5.0 \ + meson==1.8.1 \ pillow \ sphinx \ sphinx-rtd-theme diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker index 28a6f932430cd..ea67c7602a831 100644 --- a/tests/docker/dockerfiles/ubuntu2204.docker +++ b/tests/docker/dockerfiles/ubuntu2204.docker @@ -102,7 +102,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ llvm \ locales \ make \ - meson \ mtools \ multipath-tools \ ncat \ @@ -115,10 +114,12 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ + python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-tomli \ python3-venv \ + python3-wheel \ python3-yaml \ rpm2cpio \ rustc-1.77 \ @@ -145,6 +146,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc +RUN /usr/bin/pip3 install meson==1.8.1 + ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index c07242f272873..f22fc46fdc21b 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -91,7 +91,6 @@ packages: - pkg-config - pulseaudio - python3 - - python3-imp - python3-numpy - python3-opencv - python3-pillow diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index d3488b2679e9f..f49eb638f866f 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -171,7 +171,7 @@ try: generate_dockerfile("centos9", "centos-stream-9") generate_dockerfile("debian", "debian-12", trailer="".join(debian12_extras)) - generate_dockerfile("fedora", "fedora-40") + generate_dockerfile("fedora", "fedora-41") generate_dockerfile("opensuse-leap", "opensuse-leap-15") generate_dockerfile("ubuntu2204", "ubuntu-2204", trailer="".join(ubuntu2204_rust_extras)) @@ -179,7 +179,7 @@ try: # # Non-fatal Rust-enabled build # - generate_dockerfile("fedora-rust-nightly", "fedora-40", + generate_dockerfile("fedora-rust-nightly", "fedora-41", trailer="".join(fedora_rustup_nightly_extras)) # @@ -237,7 +237,7 @@ try: trailer=cross_build("s390x-linux-gnu-", "s390x-softmmu,s390x-linux-user")) - generate_dockerfile("fedora-win64-cross", "fedora-40", + generate_dockerfile("fedora-win64-cross", "fedora-41", project='qemu,qemu-win-installer', cross="mingw64", trailer=cross_build("x86_64-w64-mingw32-", From 4ef07afe8e02664a0596b22ad17fbca58c01519c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:10 +0400 Subject: [PATCH 1067/1794] lcitool/qemu: include libclang-rt for TSAN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-ID: <20250924120426.2158655-14-marcandre.lureau@redhat.com> --- scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml | 1 + scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml | 1 + tests/docker/dockerfiles/centos9.docker | 1 + tests/docker/dockerfiles/debian-amd64-cross.docker | 1 + tests/docker/dockerfiles/debian-arm64-cross.docker | 1 + tests/docker/dockerfiles/debian-armhf-cross.docker | 1 + tests/docker/dockerfiles/debian-i686-cross.docker | 1 + tests/docker/dockerfiles/debian-mips64el-cross.docker | 1 + tests/docker/dockerfiles/debian-mipsel-cross.docker | 1 + tests/docker/dockerfiles/debian-ppc64el-cross.docker | 1 + tests/docker/dockerfiles/debian-s390x-cross.docker | 1 + tests/docker/dockerfiles/debian.docker | 1 + tests/docker/dockerfiles/fedora-rust-nightly.docker | 1 + tests/docker/dockerfiles/fedora-win64-cross.docker | 1 + tests/docker/dockerfiles/fedora.docker | 1 + tests/docker/dockerfiles/opensuse-leap.docker | 1 + tests/docker/dockerfiles/ubuntu2204.docker | 1 + tests/lcitool/projects/qemu.yml | 1 + 18 files changed, 18 insertions(+) diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml index bbcb5dd4aca22..f446217f2c69f 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml @@ -36,6 +36,7 @@ packages: - libcap-ng-dev - libcapstone-dev - libcbor-dev + - libclang-dev - libcmocka-dev - libcurl4-gnutls-dev - libdaxctl-dev diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml index 00eb1b0f91a90..25c3368c540ae 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml @@ -36,6 +36,7 @@ packages: - libcap-ng-dev - libcapstone-dev - libcbor-dev + - libclang-dev - libcmocka-dev - libcurl4-gnutls-dev - libdaxctl-dev diff --git a/tests/docker/dockerfiles/centos9.docker b/tests/docker/dockerfiles/centos9.docker index a942835a1d280..33c62033cef50 100644 --- a/tests/docker/dockerfiles/centos9.docker +++ b/tests/docker/dockerfiles/centos9.docker @@ -25,6 +25,7 @@ RUN dnf distro-sync -y && \ capstone-devel \ ccache \ clang \ + compiler-rt \ ctags \ cyrus-sasl-devel \ daxctl-devel \ diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index bba6de4e2d513..d4d3cebfdc892 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ gettext \ git \ hostname \ + libclang-rt-dev \ libglib2.0-dev \ llvm \ locales \ diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index 8bbcd75157884..cd9c9a0defb55 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ gettext \ git \ hostname \ + libclang-rt-dev \ libglib2.0-dev \ llvm \ locales \ diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index 455b84424c4ef..21a5bbc81dfce 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ gettext \ git \ hostname \ + libclang-rt-dev \ libglib2.0-dev \ llvm \ locales \ diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index 8b8993ffe4415..c41a4805fe8c1 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ gettext \ git \ hostname \ + libclang-rt-dev \ libglib2.0-dev \ llvm \ locales \ diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index c89cb6c68036d..77d5d43f4bbf4 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ gettext \ git \ hostname \ + libclang-rt-dev \ libglib2.0-dev \ llvm \ locales \ diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index 4e10d9501fc64..9e60fe0eb4a87 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ gettext \ git \ hostname \ + libclang-rt-dev \ libglib2.0-dev \ llvm \ locales \ diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index cf61cb7f4aa7e..dc683bd4984f4 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ gettext \ git \ hostname \ + libclang-rt-dev \ libglib2.0-dev \ llvm \ locales \ diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index 1782e0e90f598..ef6e9614606d6 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ gettext \ git \ hostname \ + libclang-rt-dev \ libglib2.0-dev \ llvm \ locales \ diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker index 62f89e4d8c37f..ef9ba62f2a786 100644 --- a/tests/docker/dockerfiles/debian.docker +++ b/tests/docker/dockerfiles/debian.docker @@ -43,6 +43,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcap-ng-dev \ libcapstone-dev \ libcbor-dev \ + libclang-rt-dev \ libcmocka-dev \ libcurl4-gnutls-dev \ libdaxctl-dev \ diff --git a/tests/docker/dockerfiles/fedora-rust-nightly.docker b/tests/docker/dockerfiles/fedora-rust-nightly.docker index fd71dd8790247..bdbcebda8d81a 100644 --- a/tests/docker/dockerfiles/fedora-rust-nightly.docker +++ b/tests/docker/dockerfiles/fedora-rust-nightly.docker @@ -32,6 +32,7 @@ exec "$@"\n' > /usr/bin/nosync && \ capstone-devel \ ccache \ clang \ + compiler-rt \ ctags \ cyrus-sasl-devel \ daxctl-devel \ diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index ed7d270984d14..3c54486d2fa32 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -25,6 +25,7 @@ exec "$@"\n' > /usr/bin/nosync && \ bzip2 \ ca-certificates \ ccache \ + compiler-rt \ ctags \ dbus-daemon \ diffutils \ diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index e367c53c09a80..f758efd2b7c1f 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -32,6 +32,7 @@ exec "$@"\n' > /usr/bin/nosync && \ capstone-devel \ ccache \ clang \ + compiler-rt \ ctags \ cyrus-sasl-devel \ daxctl-devel \ diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index 60763857bb367..d71dbc30c70fe 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -19,6 +19,7 @@ RUN zypper update -y && \ ca-certificates \ ccache \ clang \ + clang-devel \ ctags \ cyrus-sasl-devel \ dbus-1 \ diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker index ea67c7602a831..146ad7fce255e 100644 --- a/tests/docker/dockerfiles/ubuntu2204.docker +++ b/tests/docker/dockerfiles/ubuntu2204.docker @@ -42,6 +42,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcap-ng-dev \ libcapstone-dev \ libcbor-dev \ + libclang-dev \ libcmocka-dev \ libcurl4-gnutls-dev \ libdaxctl-dev \ diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index f22fc46fdc21b..301d0e1ea0a8e 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -44,6 +44,7 @@ packages: - libcacard - libcap-ng - libcbor + - libclang-rt - libcurl - libdrm - libepoxy From 2e6d3417aaa4d34dca72b25d86c07098f03ca0b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:11 +0400 Subject: [PATCH 1068/1794] lcitool/alpine: workaround bindgen issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-ID: <20250924120426.2158655-15-marcandre.lureau@redhat.com> --- tests/docker/dockerfiles/alpine.docker | 2 ++ tests/lcitool/refresh | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index bd1ef5505da15..b50fbc3dba67f 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -131,6 +131,8 @@ ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" +# https://gitlab.alpinelinux.org/alpine/aports/-/issues/17463 +RUN apk add clang19-libclang # As a final step configure the user (if env is defined) ARG USER ARG UID diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index f49eb638f866f..6f98a9127769b 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -97,6 +97,11 @@ def generate_yaml(os, target, arch, trailer=None): generate(filename, cmd, trailer) +alpine_extras = [ + "# https://gitlab.alpinelinux.org/alpine/aports/-/issues/17463\n", + "RUN apk add clang19-libclang\n", +] + # Netmap still needs to be manually built as it is yet to be packaged # into a distro. We also add cscope and gtags which are used in the CI # test @@ -167,7 +172,8 @@ try: # # Standard native builds # - generate_dockerfile("alpine", "alpine-321") + generate_dockerfile("alpine", "alpine-321", + trailer="".join(alpine_extras)) generate_dockerfile("centos9", "centos-stream-9") generate_dockerfile("debian", "debian-12", trailer="".join(debian12_extras)) From bc3830f920810df862d93ed4737fa841d94917fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:12 +0400 Subject: [PATCH 1069/1794] tests/lcitool: add missing rust-std dep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some distros/targets may pull it by default, but some don't. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-ID: <20250924120426.2158655-16-marcandre.lureau@redhat.com> --- scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml | 1 + scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml | 1 + tests/docker/dockerfiles/centos9.docker | 1 + tests/docker/dockerfiles/debian-amd64-cross.docker | 1 + tests/docker/dockerfiles/debian-arm64-cross.docker | 1 + tests/docker/dockerfiles/debian-armhf-cross.docker | 1 + tests/docker/dockerfiles/debian-i686-cross.docker | 1 + tests/docker/dockerfiles/debian-mips64el-cross.docker | 1 + tests/docker/dockerfiles/debian-mipsel-cross.docker | 1 + tests/docker/dockerfiles/debian-ppc64el-cross.docker | 1 + tests/docker/dockerfiles/debian-s390x-cross.docker | 1 + tests/docker/dockerfiles/debian.docker | 1 + tests/docker/dockerfiles/fedora-rust-nightly.docker | 1 + tests/docker/dockerfiles/fedora-win64-cross.docker | 3 ++- tests/docker/dockerfiles/fedora.docker | 1 + tests/docker/dockerfiles/ubuntu2204.docker | 1 + tests/lcitool/projects/qemu.yml | 1 + 17 files changed, 18 insertions(+), 1 deletion(-) diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml index f446217f2c69f..2ca4a5392f57f 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml @@ -81,6 +81,7 @@ packages: - libspice-protocol-dev - libspice-server-dev - libssh-dev + - libstd-rust-dev - libsystemd-dev - libtasn1-6-dev - libubsan1 diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml index 25c3368c540ae..7198fbbcbb76f 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml @@ -80,6 +80,7 @@ packages: - libsndio-dev - libspice-protocol-dev - libssh-dev + - libstd-rust-dev - libsystemd-dev - libtasn1-6-dev - libubsan1 diff --git a/tests/docker/dockerfiles/centos9.docker b/tests/docker/dockerfiles/centos9.docker index 33c62033cef50..e7fc688ee9f4d 100644 --- a/tests/docker/dockerfiles/centos9.docker +++ b/tests/docker/dockerfiles/centos9.docker @@ -105,6 +105,7 @@ RUN dnf distro-sync -y && \ python3-tomli \ rdma-core-devel \ rust \ + rust-std-static \ sed \ snappy-devel \ socat \ diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index d4d3cebfdc892..f3ad2205a7d33 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -141,6 +141,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libspice-protocol-dev:amd64 \ libspice-server-dev:amd64 \ libssh-dev:amd64 \ + libstd-rust-dev:amd64 \ libsystemd-dev:amd64 \ libtasn1-6-dev:amd64 \ libubsan1:amd64 \ diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index cd9c9a0defb55..7d42227fa18a2 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -140,6 +140,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libspice-protocol-dev:arm64 \ libspice-server-dev:arm64 \ libssh-dev:arm64 \ + libstd-rust-dev:arm64 \ libsystemd-dev:arm64 \ libtasn1-6-dev:arm64 \ libubsan1:arm64 \ diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index 21a5bbc81dfce..8ad4d2bebfd0f 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -140,6 +140,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libspice-protocol-dev:armhf \ libspice-server-dev:armhf \ libssh-dev:armhf \ + libstd-rust-dev:armhf \ libsystemd-dev:armhf \ libtasn1-6-dev:armhf \ libubsan1:armhf \ diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index c41a4805fe8c1..e7e8d8e0f1ea3 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -140,6 +140,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libspice-protocol-dev:i386 \ libspice-server-dev:i386 \ libssh-dev:i386 \ + libstd-rust-dev:i386 \ libsystemd-dev:i386 \ libtasn1-6-dev:i386 \ libubsan1:i386 \ diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index 77d5d43f4bbf4..cca04a45947f3 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -139,6 +139,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libspice-protocol-dev:mips64el \ libspice-server-dev:mips64el \ libssh-dev:mips64el \ + libstd-rust-dev:mips64el \ libsystemd-dev:mips64el \ libtasn1-6-dev:mips64el \ libudev-dev:mips64el \ diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index 9e60fe0eb4a87..59c6f9224888f 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -139,6 +139,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libspice-protocol-dev:mipsel \ libspice-server-dev:mipsel \ libssh-dev:mipsel \ + libstd-rust-dev:mipsel \ libsystemd-dev:mipsel \ libtasn1-6-dev:mipsel \ libudev-dev:mipsel \ diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index dc683bd4984f4..97ef64d934948 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -140,6 +140,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libspice-protocol-dev:ppc64el \ libspice-server-dev:ppc64el \ libssh-dev:ppc64el \ + libstd-rust-dev:ppc64el \ libsystemd-dev:ppc64el \ libtasn1-6-dev:ppc64el \ libubsan1:ppc64el \ diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index ef6e9614606d6..3afe91494dbc7 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -139,6 +139,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libsndio-dev:s390x \ libspice-protocol-dev:s390x \ libssh-dev:s390x \ + libstd-rust-dev:s390x \ libsystemd-dev:s390x \ libtasn1-6-dev:s390x \ libubsan1:s390x \ diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker index ef9ba62f2a786..f68fcc83a9e1c 100644 --- a/tests/docker/dockerfiles/debian.docker +++ b/tests/docker/dockerfiles/debian.docker @@ -89,6 +89,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libspice-protocol-dev \ libspice-server-dev \ libssh-dev \ + libstd-rust-dev \ libsystemd-dev \ libtasn1-6-dev \ libubsan1 \ diff --git a/tests/docker/dockerfiles/fedora-rust-nightly.docker b/tests/docker/dockerfiles/fedora-rust-nightly.docker index bdbcebda8d81a..167246f0f58a4 100644 --- a/tests/docker/dockerfiles/fedora-rust-nightly.docker +++ b/tests/docker/dockerfiles/fedora-rust-nightly.docker @@ -117,6 +117,7 @@ exec "$@"\n' > /usr/bin/nosync && \ python3-wheel \ rdma-core-devel \ rust \ + rust-std-static \ sed \ snappy-devel \ socat \ diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index 3c54486d2fa32..c76a70c368412 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -100,7 +100,8 @@ RUN nosync dnf install -y \ mingw64-libtasn1 \ mingw64-nettle \ mingw64-pixman \ - mingw64-pkg-config && \ + mingw64-pkg-config \ + rust-std-static-x86_64-pc-windows-gnu && \ nosync dnf clean all -y && \ rpm -qa | sort > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index f758efd2b7c1f..56a1ad8a182e8 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -117,6 +117,7 @@ exec "$@"\n' > /usr/bin/nosync && \ python3-wheel \ rdma-core-devel \ rust \ + rust-std-static \ sed \ snappy-devel \ socat \ diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker index 146ad7fce255e..b393db55a8cd0 100644 --- a/tests/docker/dockerfiles/ubuntu2204.docker +++ b/tests/docker/dockerfiles/ubuntu2204.docker @@ -88,6 +88,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libspice-protocol-dev \ libspice-server-dev \ libssh-dev \ + libstd-rust-dev \ libsystemd-dev \ libtasn1-6-dev \ libubsan1 \ diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index 301d0e1ea0a8e..82812e773651b 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -104,6 +104,7 @@ packages: - python3-venv - rpm2cpio - rust + - rust-std - sdl2 - sdl2-image - sed From 8234e77f06d76eb40a60edaa66652dd3b41daad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:13 +0400 Subject: [PATCH 1070/1794] tests/lcitool: update to debian13 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit riscv64 is now a supported architecture. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250924120426.2158655-17-marcandre.lureau@redhat.com> --- .../dockerfiles/debian-amd64-cross.docker | 9 +- .../dockerfiles/debian-arm64-cross.docker | 9 +- .../dockerfiles/debian-armhf-cross.docker | 12 +- .../dockerfiles/debian-i686-cross.docker | 11 +- .../dockerfiles/debian-ppc64el-cross.docker | 9 +- .../dockerfiles/debian-riscv64-cross.docker | 106 +++++++++++++++++- .../dockerfiles/debian-s390x-cross.docker | 9 +- tests/docker/dockerfiles/debian.docker | 9 +- tests/lcitool/refresh | 21 ++-- 9 files changed, 149 insertions(+), 46 deletions(-) diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index f3ad2205a7d33..08621879dd40a 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch x86_64 debian-12 qemu +# $ lcitool dockerfile --layers all --cross-arch x86_64 debian-13 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:12-slim +FROM docker.io/library/debian:13-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -48,11 +48,12 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ + python3-tomli \ python3-venv \ python3-wheel \ python3-yaml \ rpm2cpio \ - rustc-web \ + rustc \ sed \ socat \ sparse \ @@ -85,7 +86,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ gcc-x86-64-linux-gnu \ libaio-dev:amd64 \ - libasan6:amd64 \ + libasan8:amd64 \ libasound2-dev:amd64 \ libattr1-dev:amd64 \ libbpf-dev:amd64 \ diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index 7d42227fa18a2..725cccbee1afe 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch aarch64 debian-12 qemu +# $ lcitool dockerfile --layers all --cross-arch aarch64 debian-13 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:12-slim +FROM docker.io/library/debian:13-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -48,11 +48,12 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ + python3-tomli \ python3-venv \ python3-wheel \ python3-yaml \ rpm2cpio \ - rustc-web \ + rustc \ sed \ socat \ sparse \ @@ -85,7 +86,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ gcc-aarch64-linux-gnu \ libaio-dev:arm64 \ - libasan6:arm64 \ + libasan8:arm64 \ libasound2-dev:arm64 \ libattr1-dev:arm64 \ libbpf-dev:arm64 \ diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index 8ad4d2bebfd0f..50f7e0e986e42 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch armv7l debian-12 qemu +# $ lcitool dockerfile --layers all --cross-arch armv7l debian-13 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:12-slim +FROM docker.io/library/debian:13-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -48,11 +48,12 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ + python3-tomli \ python3-venv \ python3-wheel \ python3-yaml \ rpm2cpio \ - rustc-web \ + rustc \ sed \ socat \ sparse \ @@ -85,7 +86,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ gcc-arm-linux-gnueabihf \ libaio-dev:armhf \ - libasan6:armhf \ + libasan8:armhf \ libasound2-dev:armhf \ libattr1-dev:armhf \ libbpf-dev:armhf \ @@ -107,7 +108,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libgbm-dev:armhf \ libgcrypt20-dev:armhf \ libglib2.0-dev:armhf \ - libglusterfs-dev:armhf \ libgnutls28-dev:armhf \ libgtk-3-dev:armhf \ libgtk-vnc-2.0-dev:armhf \ @@ -127,7 +127,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libpixman-1-dev:armhf \ libpng-dev:armhf \ libpulse-dev:armhf \ - librbd-dev:armhf \ librdmacm-dev:armhf \ libsasl2-dev:armhf \ libsdl2-dev:armhf \ @@ -152,7 +151,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libvirglrenderer-dev:armhf \ libvte-2.91-dev:armhf \ libxdp-dev:armhf \ - libxen-dev:armhf \ libzstd-dev:armhf \ nettle-dev:armhf \ systemtap-sdt-dev:armhf \ diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index e7e8d8e0f1ea3..f53b77cb622f3 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch i686 debian-12 qemu +# $ lcitool dockerfile --layers all --cross-arch i686 debian-13 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:12-slim +FROM docker.io/library/debian:13-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -48,11 +48,12 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ + python3-tomli \ python3-venv \ python3-wheel \ python3-yaml \ rpm2cpio \ - rustc-web \ + rustc \ sed \ socat \ sparse \ @@ -85,7 +86,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ gcc-i686-linux-gnu \ libaio-dev:i386 \ - libasan6:i386 \ + libasan8:i386 \ libasound2-dev:i386 \ libattr1-dev:i386 \ libbpf-dev:i386 \ @@ -107,7 +108,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libgbm-dev:i386 \ libgcrypt20-dev:i386 \ libglib2.0-dev:i386 \ - libglusterfs-dev:i386 \ libgnutls28-dev:i386 \ libgtk-3-dev:i386 \ libgtk-vnc-2.0-dev:i386 \ @@ -127,7 +127,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libpixman-1-dev:i386 \ libpng-dev:i386 \ libpulse-dev:i386 \ - librbd-dev:i386 \ librdmacm-dev:i386 \ libsasl2-dev:i386 \ libsdl2-dev:i386 \ diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index 97ef64d934948..09de265c26f67 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch ppc64le debian-12 qemu +# $ lcitool dockerfile --layers all --cross-arch ppc64le debian-13 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:12-slim +FROM docker.io/library/debian:13-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -48,11 +48,12 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ + python3-tomli \ python3-venv \ python3-wheel \ python3-yaml \ rpm2cpio \ - rustc-web \ + rustc \ sed \ socat \ sparse \ @@ -85,7 +86,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ gcc-powerpc64le-linux-gnu \ libaio-dev:ppc64el \ - libasan6:ppc64el \ + libasan8:ppc64el \ libasound2-dev:ppc64el \ libattr1-dev:ppc64el \ libbpf-dev:ppc64el \ diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker index 79ec37616d169..e6c463eff7f75 100644 --- a/tests/docker/dockerfiles/debian-riscv64-cross.docker +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -1,6 +1,6 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch riscv64 debian-13 qemu-minimal +# $ lcitool dockerfile --layers all --cross-arch riscv64 debian-13 qemu # # https://gitlab.com/libvirt/libvirt-ci @@ -13,25 +13,57 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ + bsdextrautils \ + bzip2 \ ca-certificates \ ccache \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ findutils \ flex \ gcc \ + gcovr \ + gettext \ git \ + hostname \ + libclang-rt-dev \ libglib2.0-dev \ + llvm \ locales \ make \ + mtools \ + ncat \ ninja-build \ + openssh-client \ pkgconf \ python3 \ + python3-numpy \ + python3-opencv \ + python3-pillow \ python3-pip \ python3-setuptools \ + python3-sphinx \ + python3-sphinx-rtd-theme \ + python3-tomli \ python3-venv \ python3-wheel \ + python3-yaml \ + rpm2cpio \ + rustc \ sed \ - tar && \ + socat \ + sparse \ + swtpm \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + vulkan-tools \ + xorriso \ + zstd && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ @@ -56,11 +88,78 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ gcc-riscv64-linux-gnu \ + libaio-dev:riscv64 \ + libasan8:riscv64 \ + libasound2-dev:riscv64 \ + libattr1-dev:riscv64 \ + libbpf-dev:riscv64 \ + libbrlapi-dev:riscv64 \ + libbz2-dev:riscv64 \ libc6-dev:riscv64 \ + libcacard-dev:riscv64 \ + libcap-ng-dev:riscv64 \ + libcapstone-dev:riscv64 \ + libcbor-dev:riscv64 \ + libcmocka-dev:riscv64 \ + libcurl4-gnutls-dev:riscv64 \ + libdaxctl-dev:riscv64 \ + libdrm-dev:riscv64 \ + libepoxy-dev:riscv64 \ libfdt-dev:riscv64 \ libffi-dev:riscv64 \ + libfuse3-dev:riscv64 \ + libgbm-dev:riscv64 \ + libgcrypt20-dev:riscv64 \ libglib2.0-dev:riscv64 \ - libpixman-1-dev:riscv64 && \ + libglusterfs-dev:riscv64 \ + libgnutls28-dev:riscv64 \ + libgtk-3-dev:riscv64 \ + libgtk-vnc-2.0-dev:riscv64 \ + libibverbs-dev:riscv64 \ + libiscsi-dev:riscv64 \ + libjemalloc-dev:riscv64 \ + libjpeg62-turbo-dev:riscv64 \ + libjson-c-dev:riscv64 \ + liblttng-ust-dev:riscv64 \ + liblzo2-dev:riscv64 \ + libncursesw5-dev:riscv64 \ + libnfs-dev:riscv64 \ + libnuma-dev:riscv64 \ + libpam0g-dev:riscv64 \ + libpcre2-dev:riscv64 \ + libpipewire-0.3-dev:riscv64 \ + libpixman-1-dev:riscv64 \ + libpng-dev:riscv64 \ + libpulse-dev:riscv64 \ + librbd-dev:riscv64 \ + librdmacm-dev:riscv64 \ + libsasl2-dev:riscv64 \ + libsdl2-dev:riscv64 \ + libsdl2-image-dev:riscv64 \ + libseccomp-dev:riscv64 \ + libselinux1-dev:riscv64 \ + libslirp-dev:riscv64 \ + libsnappy-dev:riscv64 \ + libsndio-dev:riscv64 \ + libspice-protocol-dev:riscv64 \ + libspice-server-dev:riscv64 \ + libssh-dev:riscv64 \ + libstd-rust-dev:riscv64 \ + libsystemd-dev:riscv64 \ + libtasn1-6-dev:riscv64 \ + libubsan1:riscv64 \ + libudev-dev:riscv64 \ + liburing-dev:riscv64 \ + libusb-1.0-0-dev:riscv64 \ + libusbredirhost-dev:riscv64 \ + libvdeplug-dev:riscv64 \ + libvirglrenderer-dev:riscv64 \ + libvte-2.91-dev:riscv64 \ + libxdp-dev:riscv64 \ + libzstd-dev:riscv64 \ + nettle-dev:riscv64 \ + systemtap-sdt-dev:riscv64 \ + zlib1g-dev:riscv64 && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ mkdir -p /usr/local/share/meson/cross && \ @@ -82,6 +181,7 @@ endian = 'little'\n" > /usr/local/share/meson/cross/riscv64-linux-gnu && \ ENV ABI "riscv64-linux-gnu" ENV MESON_OPTS "--cross-file=riscv64-linux-gnu" +ENV RUST_TARGET "riscv64gc-unknown-linux-gnu" ENV QEMU_CONFIGURE_OPTS --cross-prefix=riscv64-linux-gnu- ENV DEF_TARGET_LIST riscv64-softmmu,riscv64-linux-user # As a final step configure the user (if env is defined) diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index 3afe91494dbc7..d7b2ca99ce3b2 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch s390x debian-12 qemu +# $ lcitool dockerfile --layers all --cross-arch s390x debian-13 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:12-slim +FROM docker.io/library/debian:13-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -48,11 +48,12 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ + python3-tomli \ python3-venv \ python3-wheel \ python3-yaml \ rpm2cpio \ - rustc-web \ + rustc \ sed \ socat \ sparse \ @@ -85,7 +86,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ gcc-s390x-linux-gnu \ libaio-dev:s390x \ - libasan6:s390x \ + libasan8:s390x \ libasound2-dev:s390x \ libattr1-dev:s390x \ libbpf-dev:s390x \ diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker index f68fcc83a9e1c..2696cf21672a7 100644 --- a/tests/docker/dockerfiles/debian.docker +++ b/tests/docker/dockerfiles/debian.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all debian-12 qemu +# $ lcitool dockerfile --layers all debian-13 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:12-slim +FROM docker.io/library/debian:13-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -32,7 +32,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libaio-dev \ - libasan6 \ + libasan8 \ libasound2-dev \ libattr1-dev \ libbpf-dev \ @@ -121,11 +121,12 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ + python3-tomli \ python3-venv \ python3-wheel \ python3-yaml \ rpm2cpio \ - rustc-web \ + rustc \ sed \ socat \ sparse \ diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 6f98a9127769b..5c62cdde4a04b 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -105,7 +105,7 @@ alpine_extras = [ # Netmap still needs to be manually built as it is yet to be packaged # into a distro. We also add cscope and gtags which are used in the CI # test -debian12_extras = [ +debian13_extras = [ "# netmap/cscope/global\n", "RUN DEBIAN_FRONTEND=noninteractive eatmydata \\\n", " apt install -y --no-install-recommends \\\n", @@ -175,8 +175,8 @@ try: generate_dockerfile("alpine", "alpine-321", trailer="".join(alpine_extras)) generate_dockerfile("centos9", "centos-stream-9") - generate_dockerfile("debian", "debian-12", - trailer="".join(debian12_extras)) + generate_dockerfile("debian", "debian-13", + trailer="".join(debian13_extras)) generate_dockerfile("fedora", "fedora-41") generate_dockerfile("opensuse-leap", "opensuse-leap-15") generate_dockerfile("ubuntu2204", "ubuntu-2204", @@ -191,30 +191,32 @@ try: # # Cross compiling builds # - generate_dockerfile("debian-amd64-cross", "debian-12", + generate_dockerfile("debian-amd64-cross", "debian-13", cross="x86_64", trailer=cross_build("x86_64-linux-gnu-", "x86_64-softmmu," "x86_64-linux-user," "i386-softmmu,i386-linux-user")) - generate_dockerfile("debian-arm64-cross", "debian-12", + generate_dockerfile("debian-arm64-cross", "debian-13", cross="aarch64", trailer=cross_build("aarch64-linux-gnu-", "aarch64-softmmu,aarch64-linux-user")) - generate_dockerfile("debian-armhf-cross", "debian-12", + generate_dockerfile("debian-armhf-cross", "debian-13", cross="armv7l", trailer=cross_build("arm-linux-gnueabihf-", "arm-softmmu,arm-linux-user")) - generate_dockerfile("debian-i686-cross", "debian-12", + generate_dockerfile("debian-i686-cross", "debian-13", cross="i686", trailer=cross_build("i686-linux-gnu-", "x86_64-softmmu," "x86_64-linux-user," "i386-softmmu,i386-linux-user")) + # mips no longer supported in debian-13 + # https://www.debian.org/releases/trixie/release-notes/issues.html#mips-architectures-removed generate_dockerfile("debian-mips64el-cross", "debian-12", cross="mips64el", trailer=cross_build("mips64el-linux-gnuabi64-", @@ -225,7 +227,7 @@ try: trailer=cross_build("mipsel-linux-gnu-", "mipsel-softmmu,mipsel-linux-user")) - generate_dockerfile("debian-ppc64el-cross", "debian-12", + generate_dockerfile("debian-ppc64el-cross", "debian-13", cross="ppc64le", trailer=cross_build("powerpc64le-linux-gnu-", "ppc64-softmmu,ppc64-linux-user")) @@ -233,12 +235,11 @@ try: # while not yet a release architecture the packages are still # build while part of testing generate_dockerfile("debian-riscv64-cross", "debian-13", - project="qemu-minimal", cross="riscv64", trailer=cross_build("riscv64-linux-gnu-", "riscv64-softmmu,riscv64-linux-user")) - generate_dockerfile("debian-s390x-cross", "debian-12", + generate_dockerfile("debian-s390x-cross", "debian-13", cross="s390x", trailer=cross_build("s390x-linux-gnu-", "s390x-softmmu,s390x-linux-user")) From 2086d47992e199c250fcc1dcbc1177590c83ccd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:14 +0400 Subject: [PATCH 1071/1794] tests/docker: add ENABLE_RUST environment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-ID: <20250924120426.2158655-18-marcandre.lureau@redhat.com> --- tests/docker/common.rc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/docker/common.rc b/tests/docker/common.rc index d202c0c666598..752f4f3aed9a6 100755 --- a/tests/docker/common.rc +++ b/tests/docker/common.rc @@ -21,6 +21,14 @@ else DEF_TARGET_LIST=${DEF_TARGET_LIST:-"x86_64-softmmu,aarch64-softmmu"} fi +enable_rust="" +if [ "$ENABLE_RUST" = "1" ]; then + enable_rust="--enable-rust" + if [ -n "$RUST_TARGET" ]; then + enable_rust="$enable_rust --rust-target-triple=$RUST_TARGET" + fi +fi + requires_binary() { found=0 @@ -46,6 +54,7 @@ configure_qemu() ${TARGET_LIST:+--target-list=${TARGET_LIST}} \ --prefix=$INSTALL_DIR \ $QEMU_CONFIGURE_OPTS $EXTRA_CONFIGURE_OPTS \ + $enable_rust \ $@" echo "Configure options:" echo $config_opts From e05b9e3d9b70842e10f49977fc3bbdc91587677e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:15 +0400 Subject: [PATCH 1072/1794] tests/lcitool: enable rust & refresh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable Rust on various distro images: alpine, centos, debian, fedora, opensuse. Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth Message-ID: <20250924120426.2158655-19-marcandre.lureau@redhat.com> --- tests/docker/dockerfiles/alpine.docker | 2 ++ tests/docker/dockerfiles/centos9.docker | 2 ++ tests/docker/dockerfiles/debian-amd64-cross.docker | 2 ++ tests/docker/dockerfiles/debian-arm64-cross.docker | 2 ++ tests/docker/dockerfiles/debian-armhf-cross.docker | 2 ++ tests/docker/dockerfiles/debian-i686-cross.docker | 2 ++ .../dockerfiles/debian-mips64el-cross.docker | 2 ++ .../docker/dockerfiles/debian-mipsel-cross.docker | 2 ++ .../docker/dockerfiles/debian-ppc64el-cross.docker | 2 ++ .../docker/dockerfiles/debian-riscv64-cross.docker | 2 ++ tests/docker/dockerfiles/debian-s390x-cross.docker | 2 ++ tests/docker/dockerfiles/debian.docker | 2 ++ .../docker/dockerfiles/fedora-rust-nightly.docker | 2 ++ tests/docker/dockerfiles/fedora.docker | 2 ++ tests/docker/dockerfiles/opensuse-leap.docker | 2 ++ tests/lcitool/refresh | 14 +++++++++++--- 16 files changed, 41 insertions(+), 3 deletions(-) diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index b50fbc3dba67f..52adf9ccbb712 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -138,3 +138,5 @@ ARG USER ARG UID RUN if [ "${USER}" ]; then \ id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi + +ENV ENABLE_RUST 1 diff --git a/tests/docker/dockerfiles/centos9.docker b/tests/docker/dockerfiles/centos9.docker index e7fc688ee9f4d..0674d778262f1 100644 --- a/tests/docker/dockerfiles/centos9.docker +++ b/tests/docker/dockerfiles/centos9.docker @@ -142,3 +142,5 @@ ARG USER ARG UID RUN if [ "${USER}" ]; then \ id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi + +ENV ENABLE_RUST 1 diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index 08621879dd40a..7f4674400df57 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -188,3 +188,5 @@ ARG USER ARG UID RUN if [ "${USER}" ]; then \ id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi + +ENV ENABLE_RUST 1 diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index 725cccbee1afe..c7cd54ee5caa3 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -187,3 +187,5 @@ ARG USER ARG UID RUN if [ "${USER}" ]; then \ id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi + +ENV ENABLE_RUST 1 diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index 50f7e0e986e42..627d41c6dee3c 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -184,3 +184,5 @@ ARG USER ARG UID RUN if [ "${USER}" ]; then \ id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi + +ENV ENABLE_RUST 1 diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index f53b77cb622f3..4e8b3a8293d24 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -184,3 +184,5 @@ ARG USER ARG UID RUN if [ "${USER}" ]; then \ id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi + +ENV ENABLE_RUST 1 diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index cca04a45947f3..6e88777f76d01 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -183,3 +183,5 @@ ARG USER ARG UID RUN if [ "${USER}" ]; then \ id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi + +ENV ENABLE_RUST 1 diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index 59c6f9224888f..5f4e3fa963620 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -183,3 +183,5 @@ ARG USER ARG UID RUN if [ "${USER}" ]; then \ id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi + +ENV ENABLE_RUST 1 diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index 09de265c26f67..dfa690616d1f9 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -186,3 +186,5 @@ ARG USER ARG UID RUN if [ "${USER}" ]; then \ id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi + +ENV ENABLE_RUST 1 diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker index e6c463eff7f75..09b2953f32ce0 100644 --- a/tests/docker/dockerfiles/debian-riscv64-cross.docker +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -189,3 +189,5 @@ ARG USER ARG UID RUN if [ "${USER}" ]; then \ id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi + +ENV ENABLE_RUST 1 diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index d7b2ca99ce3b2..09a78c15baf99 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -185,3 +185,5 @@ ARG USER ARG UID RUN if [ "${USER}" ]; then \ id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi + +ENV ENABLE_RUST 1 diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker index 2696cf21672a7..8dd893be4b0c4 100644 --- a/tests/docker/dockerfiles/debian.docker +++ b/tests/docker/dockerfiles/debian.docker @@ -175,3 +175,5 @@ ARG USER ARG UID RUN if [ "${USER}" ]; then \ id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi + +ENV ENABLE_RUST 1 diff --git a/tests/docker/dockerfiles/fedora-rust-nightly.docker b/tests/docker/dockerfiles/fedora-rust-nightly.docker index 167246f0f58a4..7d31c9f406062 100644 --- a/tests/docker/dockerfiles/fedora-rust-nightly.docker +++ b/tests/docker/dockerfiles/fedora-rust-nightly.docker @@ -185,3 +185,5 @@ ARG USER ARG UID RUN if [ "${USER}" ]; then \ id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi + +ENV ENABLE_RUST 1 diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 56a1ad8a182e8..891a740fcbc4d 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -162,3 +162,5 @@ ARG USER ARG UID RUN if [ "${USER}" ]; then \ id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi + +ENV ENABLE_RUST 1 diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index d71dbc30c70fe..75e1747780733 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -148,3 +148,5 @@ ARG USER ARG UID RUN if [ "${USER}" ]; then \ id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi + +ENV ENABLE_RUST 1 diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 5c62cdde4a04b..645959318a3ad 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -63,7 +63,8 @@ add_user_mapping = [ " id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi\n" ] -def generate_dockerfile(host, target, project="qemu", cross=None, trailer=None): +def generate_dockerfile(host, target, project="qemu", cross=None, trailer=None, + enable_rust=True): filename = Path(src_dir, "tests", "docker", "dockerfiles", host + ".docker") cmd = lcitool_cmd + ["dockerfile"] if cross is not None: @@ -75,6 +76,8 @@ def generate_dockerfile(host, target, project="qemu", cross=None, trailer=None): else: trailer = "\n".join(add_user_mapping) + if enable_rust: + trailer += "\nENV ENABLE_RUST 1\n" generate(filename, cmd, trailer) @@ -180,7 +183,9 @@ try: generate_dockerfile("fedora", "fedora-41") generate_dockerfile("opensuse-leap", "opensuse-leap-15") generate_dockerfile("ubuntu2204", "ubuntu-2204", - trailer="".join(ubuntu2204_rust_extras)) + trailer="".join(ubuntu2204_rust_extras), + # https://bugs.launchpad.net/ubuntu/+source/rustc-1.83/+bug/2120318 + enable_rust=False) # # Non-fatal Rust-enabled build @@ -248,7 +253,10 @@ try: project='qemu,qemu-win-installer', cross="mingw64", trailer=cross_build("x86_64-w64-mingw32-", - "x86_64-softmmu")) + "x86_64-softmmu"), + # linking with rust is buggy: + # https://github.com/mesonbuild/meson/pull/14991 + enable_rust=False) # # Cirrus packages lists for GitLab From 6890bec797e51efdccc7e082303d225e70c343f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:16 +0400 Subject: [PATCH 1073/1794] configure: set the meson executable suffix/ext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'rustfmt' target runs meson: it needs the correct path with extension on Windows. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-ID: <20250924120426.2158655-20-marcandre.lureau@redhat.com> --- configure | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/configure b/configure index bf964947b8a2a..78445cbb4b374 100755 --- a/configure +++ b/configure @@ -1000,7 +1000,19 @@ $mkvenv ensuregroup --dir "${source_path}/python/wheels" \ # We ignore PATH completely here: we want to use the venv's Meson # *exclusively*. -meson="$(cd pyvenv/bin; pwd)/meson" +# for msys2 +get_pwd() { + if pwd -W >/dev/null 2>&1; then + pwd -W + else + pwd + fi +} + +meson="$(cd pyvenv/bin; get_pwd)/meson" +if [ -f "$meson$EXESUF" ]; then + meson="$meson$EXESUF" +fi # Conditionally ensure Sphinx is installed. From 5314a5ba2b53709eeb16f23c80cb28e096c9589b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:17 +0400 Subject: [PATCH 1074/1794] tests/freebsd: enable Rust MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-ID: <20250924120426.2158655-21-marcandre.lureau@redhat.com> --- .gitlab-ci.d/cirrus.yml | 2 +- tests/vm/freebsd | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.d/cirrus.yml b/.gitlab-ci.d/cirrus.yml index 75b611418e7b8..13a0bf5bb9f95 100644 --- a/.gitlab-ci.d/cirrus.yml +++ b/.gitlab-ci.d/cirrus.yml @@ -42,7 +42,7 @@ x64-freebsd-14-build: CIRRUS_VM_RAM: 8G UPDATE_COMMAND: pkg update; pkg upgrade -y INSTALL_COMMAND: pkg install -y - CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblaze-softmmu,mips64el-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4eb-softmmu,xtensa-softmmu + CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblaze-softmmu,mips64el-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4eb-softmmu,xtensa-softmmu --enable-rust TEST_TARGETS: check aarch64-macos-build: diff --git a/tests/vm/freebsd b/tests/vm/freebsd index 2e96c9eba52d6..ea09b21fbc1dc 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -40,7 +40,9 @@ class FreeBSDVM(basevm.BaseVM): tar -xf /dev/vtbd1; cd ../build; ../src/configure --extra-ldflags=-L/usr/local/lib \ - --extra-cflags=-I/usr/local/include {configure_opts}; + --extra-cflags=-I/usr/local/include \ + --enable-rust \ + {configure_opts}; gmake --output-sync -j{jobs} {target} {verbose}; """ From d9703225f5bbec3f1dec08623106b10255c0474a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:18 +0400 Subject: [PATCH 1075/1794] meson: rust-bindgen limit allowlist-file to srcdir/include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gitlab CI restricts usage of directories for the build environment and cache. Msys64 is installed under project root ($srcdir/msys64). This confuses rust-bindgen allowlist-file which will generate bindings for all the system include headers under msys64/. blocklist-file is also too strict, as it prevents generating all the recursively dependent types coming from system includes. Instead, let's not use allowlist-file from the project root, Signed-off-by: Marc-André Lureau Message-ID: <20250924120426.2158655-22-marcandre.lureau@redhat.com> --- meson.build | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 14e626aa1ee05..0d0bf9ee463b0 100644 --- a/meson.build +++ b/meson.build @@ -4238,8 +4238,7 @@ if have_rust '--no-layout-tests', '--no-prepend-enum-name', '--allowlist-file', meson.project_source_root() + '/include/.*', - '--allowlist-file', meson.project_source_root() + '/.*', - '--allowlist-file', meson.project_build_root() + '/.*' + '--allowlist-file', meson.project_build_root() + '/.*', ] if not rustfmt.found() if bindgen.version().version_compare('<0.65.0') From 7a3fe60cb0ec2742371fd96a396de118800b213d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:22 +0400 Subject: [PATCH 1076/1794] build-sys: deprecate mips host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250924120426.2158655-26-marcandre.lureau@redhat.com> --- docs/about/build-platforms.rst | 2 -- docs/about/deprecated.rst | 9 +++------ meson.build | 8 ++++++++ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index 0160d3adb8312..798cb4631dfc1 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -44,8 +44,6 @@ Those hosts are officially supported, with various accelerators: - Accelerators * - Arm - hvf (64 bit only), kvm (64 bit only), tcg, xen - * - MIPS (64 bit little endian only) - - kvm, tcg * - PPC - kvm, tcg * - RISC-V diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index b8d60c1a90bac..67e527740c0e4 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -172,8 +172,8 @@ This argument has always been ignored. Host Architectures ------------------ -Big endian MIPS since 7.2; 32-bit little endian MIPS since 9.2 -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +Big endian MIPS since 7.2; 32-bit little endian MIPS since 9.2, MIPS since 11.0 +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' As Debian 10 ("Buster") moved into LTS the big endian 32 bit version of MIPS moved out of support making it hard to maintain our @@ -181,10 +181,7 @@ cross-compilation CI tests of the architecture. As we no longer have CI coverage support may bitrot away before the deprecation process completes. -Likewise, the little endian variant of 32 bit MIPS is not supported by -Debian 13 ("Trixie") and newer. - -64 bit little endian MIPS is still a supported host architecture. +Likewise, MIPS is not supported by Debian 13 ("Trixie") and newer. System emulation on 32-bit x86 hosts (since 8.0) '''''''''''''''''''''''''''''''''''''''''''''''' diff --git a/meson.build b/meson.build index 0d0bf9ee463b0..762c533a31c5c 100644 --- a/meson.build +++ b/meson.build @@ -5040,6 +5040,14 @@ elif host_long_bits < 64 message() message('Support for 32-bit CPU host architecture ' + cpu + ' is going') message('to be dropped in a future QEMU release.') +elif host_arch == 'mips' + message() + warning('DEPRECATED HOST CPU') + message() + message('Support for CPU host architecture ' + cpu + ' is going to be') + message('dropped as soon as the QEMU project stops supporting Debian 12') + message('("Bookworm"). Going forward, the QEMU project will not guarantee') + message('that QEMU will compile or work on this host CPU.') endif if not supported_oses.contains(host_os) From 582a39beea414c092dbd8c178f3eff3a718eee77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 16:04:23 +0400 Subject: [PATCH 1077/1794] build-sys: pass -fvisibility=default for wasm bindgen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise, no functions are generated: https://github.com/rust-lang/rust-bindgen/issues/2989 Signed-off-by: Marc-André Lureau Reviewed-by: Kohei Tokunaga Message-ID: <20250924120426.2158655-27-marcandre.lureau@redhat.com> --- meson.build | 4 ++++ rust/bql/meson.build | 1 + rust/chardev/meson.build | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/core/meson.build | 1 + rust/migration/meson.build | 3 ++- rust/qom/meson.build | 1 + rust/system/meson.build | 1 + rust/util/meson.build | 1 + 9 files changed, 13 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 762c533a31c5c..55c8202a4d8b1 100644 --- a/meson.build +++ b/meson.build @@ -4256,6 +4256,10 @@ if have_rust else bindgen_args_common += ['--merge-extern-blocks'] endif + bindgen_c_args = [] + if host_arch == 'wasm32' + bindgen_c_args += ['-fvisibility=default'] + endif subdir('rust') endif diff --git a/rust/bql/meson.build b/rust/bql/meson.build index 7214d9440893e..bc51c7f160be1 100644 --- a/rust/bql/meson.build +++ b/rust/bql/meson.build @@ -21,6 +21,7 @@ _bql_bindings_inc_rs = rust.bindgen( include_directories: bindings_incdir, bindgen_version: ['>=0.60.0'], args: bindgen_args_common, + c_args: bindgen_c_args, ) _bql_rs = static_library( diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build index 370895c111fd0..e7ce02b3bc23d 100644 --- a/rust/chardev/meson.build +++ b/rust/chardev/meson.build @@ -20,6 +20,7 @@ _chardev_bindings_inc_rs = rust.bindgen( include_directories: bindings_incdir, bindgen_version: ['>=0.60.0'], args: bindgen_args_common + _chardev_bindgen_args, + c_args: bindgen_c_args, ) _chardev_rs = static_library( diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index ffdc8af53f18d..a33f32906e912 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -12,6 +12,7 @@ _libpl011_bindings_inc_rs = rust.bindgen( include_directories: bindings_incdir, bindgen_version: ['>=0.60.0'], args: bindgen_args_common, + c_args: bindgen_c_args, ) _libpl011_rs = static_library( diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build index 81d8c77f9ad7a..e1ae95ed61e39 100644 --- a/rust/hw/core/meson.build +++ b/rust/hw/core/meson.build @@ -41,6 +41,7 @@ _hwcore_bindings_inc_rs = rust.bindgen( include_directories: bindings_incdir, bindgen_version: ['>=0.60.0'], args: bindgen_args_common + _hwcore_bindgen_args, + c_args: bindgen_c_args, ) _hwcore_rs = static_library( diff --git a/rust/migration/meson.build b/rust/migration/meson.build index 2a49bd1633e59..ddf5c2f51d504 100644 --- a/rust/migration/meson.build +++ b/rust/migration/meson.build @@ -22,7 +22,8 @@ _migration_bindings_inc_rs = rust.bindgen( include_directories: bindings_incdir, bindgen_version: ['>=0.60.0'], args: bindgen_args_common + _migration_bindgen_args, - ) + c_args: bindgen_c_args, +) _migration_rs = static_library( 'migration', diff --git a/rust/qom/meson.build b/rust/qom/meson.build index 21e12148da4f0..71fdac696c3c3 100644 --- a/rust/qom/meson.build +++ b/rust/qom/meson.build @@ -12,6 +12,7 @@ _qom_bindings_inc_rs = rust.bindgen( include_directories: bindings_incdir, bindgen_version: ['>=0.60.0'], args: bindgen_args_common, + c_args: bindgen_c_args, ) _qom_rs = static_library( diff --git a/rust/system/meson.build b/rust/system/meson.build index 3ec140de01877..0859f3974535e 100644 --- a/rust/system/meson.build +++ b/rust/system/meson.build @@ -20,6 +20,7 @@ _system_bindings_inc_rs = rust.bindgen( include_directories: bindings_incdir, bindgen_version: ['>=0.60.0'], args: bindgen_args_common + _system_bindgen_args, + c_args: bindgen_c_args, ) _system_rs = static_library( diff --git a/rust/util/meson.build b/rust/util/meson.build index 7ca69939ce5cc..094b43355aa2b 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -22,6 +22,7 @@ _util_bindings_inc_rs = rust.bindgen( include_directories: bindings_incdir, bindgen_version: ['>=0.60.0'], args: bindgen_args_common + _util_bindgen_args, + c_args: bindgen_c_args, ) _util_rs = static_library( From f3da25a5571573dfd8b5459b3b4458cd4de38db7 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 23 Sep 2025 11:09:58 +0200 Subject: [PATCH 1078/1794] ui/dbus: Consistent handling of texture mutex failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We report d3d_texture2d_acquire0() and d3d_texture2d_release0() failure as error, except in dbus_update_gl_cb(), where we report it as warning. Report it as error there as well. Cc: Marc-André Lureau Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-ID: <20250923091000.3180122-12-armbru@redhat.com> Reviewed-by: Akihiko Odaki --- ui/dbus-listener.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 09d7a319b10a8..52e041edb0f72 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -214,26 +214,31 @@ static void dbus_update_gl_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) { - g_autoptr(GError) err = NULL; + g_autoptr(GError) gerr = NULL; +#ifdef WIN32 + Error *err = NULL; +#endif DBusDisplayListener *ddl = user_data; bool success; #ifdef CONFIG_GBM success = qemu_dbus_display1_listener_call_update_dmabuf_finish( - ddl->proxy, res, &err); + ddl->proxy, res, &gerr); if (!success) { - error_report("Failed to call update: %s", err->message); + error_report("Failed to call update: %s", gerr->message); } #endif #ifdef WIN32 success = qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d_finish( - ddl->d3d11_proxy, res, &err); + ddl->d3d11_proxy, res, &gerr); if (!success) { - error_report("Failed to call update: %s", err->message); + error_report("Failed to call update: %s", gerr->message); } - d3d_texture2d_acquire0(ddl->d3d_texture, &error_warn); + if (!d3d_texture2d_acquire0(ddl->d3d_texture, &err)) { + error_report_err(err); + } #endif graphic_hw_gl_block(ddl->dcl.con, false); From e0c6c38738501ff4f79864ab2b10622433317711 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 23 Sep 2025 11:09:59 +0200 Subject: [PATCH 1079/1794] ivshmem-flat: Mark an instance of missing error handling FIXME ivshmem-flat's ivshmem_flat_add_vector() neglects to handle qemu_set_blocking() failure. It used to silently ignore errors there. Recent commit 6f607941b1c (treewide: use qemu_set_blocking instead of g_unix_set_fd_nonblocking) changed it to warn (without mentioning it the commit message, tsk, tsk, tsk). Note that ivshmem-pci's process_msg_connect() handles this error. Add a FIXME comment to mark the missing error handling. Cc: Gustavo Romero Signed-off-by: Markus Armbruster Message-ID: <20250923091000.3180122-13-armbru@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Akihiko Odaki --- hw/misc/ivshmem-flat.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/misc/ivshmem-flat.c b/hw/misc/ivshmem-flat.c index e83e6c6ee9a7c..27ee8c921830e 100644 --- a/hw/misc/ivshmem-flat.c +++ b/hw/misc/ivshmem-flat.c @@ -138,6 +138,8 @@ static void ivshmem_flat_remove_peer(IvshmemFTState *s, uint16_t peer_id) static void ivshmem_flat_add_vector(IvshmemFTState *s, IvshmemPeer *peer, int vector_fd) { + Error *err = NULL; + if (peer->vector_counter >= IVSHMEM_MAX_VECTOR_NUM) { trace_ivshmem_flat_add_vector_failure(peer->vector_counter, vector_fd, peer->id); @@ -154,8 +156,10 @@ static void ivshmem_flat_add_vector(IvshmemFTState *s, IvshmemPeer *peer, * peer. */ peer->vector[peer->vector_counter].id = peer->vector_counter; - /* WARNING: qemu_socket_set_nonblock() return code ignored */ - qemu_set_blocking(vector_fd, false, &error_warn); + if (!qemu_set_blocking(vector_fd, false, &err)) { + /* FIXME handle the error */ + warn_report_err(err); + } event_notifier_init_fd(&peer->vector[peer->vector_counter].event_notifier, vector_fd); From bcb536cabe108e71e2900cdd605f5b4e59ac3e1f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 23 Sep 2025 11:10:00 +0200 Subject: [PATCH 1080/1794] error: Kill @error_warn We added @error_warn some two years ago in commit 3ffef1a55ca (error: add global &error_warn destination). It has multiple issues: * error.h's big comment was not updated for it. * Function contracts were not updated for it. * ERRP_GUARD() is unaware of @error_warn, and fails to mask it from error_prepend() and such. These crash on @error_warn, as pointed out by Akihiko Odaki. All fixable. However, after more than two years, we had just of 15 uses, of which the last few patches removed seven as unclean or otherwise undesirable, adding back five elsewhere. I didn't look closely enough at the remaining seven to decide whether they are desirable or not. I don't think this feature earns its keep. Drop it. Thanks-to: Akihiko Odaki Signed-off-by: Markus Armbruster Reviewed-by: Vladimir Sementsov-Ogievskiy Message-ID: <20250923091000.3180122-14-armbru@redhat.com> Reviewed-by: Akihiko Odaki --- hw/display/virtio-gpu.c | 8 ++++++-- hw/net/virtio-net.c | 8 +++++++- include/qapi/error.h | 6 ------ include/system/os-win32.h | 5 ++++- io/channel-socket.c | 4 ++-- io/channel-watch.c | 6 +++--- tests/unit/test-error-report.c | 17 ----------------- ui/gtk.c | 6 +++++- util/aio-win32.c | 2 +- util/error.c | 5 +---- util/oslib-win32.c | 21 ++++++++++++++++++++- 11 files changed, 49 insertions(+), 39 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 0a1a625b0ea6c..de3590221308d 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -242,6 +242,7 @@ static uint32_t calc_image_hostmem(pixman_format_code_t pformat, static void virtio_gpu_resource_create_2d(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { + Error *err = NULL; pixman_format_code_t pformat; struct virtio_gpu_simple_resource *res; struct virtio_gpu_resource_create_2d c2d; @@ -293,7 +294,8 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, c2d.width, c2d.height, c2d.height ? res->hostmem / c2d.height : 0, - &error_warn)) { + &err)) { + warn_report_err(err); goto end; } } @@ -1282,6 +1284,7 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, const VMStateField *field) { VirtIOGPU *g = opaque; + Error *err = NULL; struct virtio_gpu_simple_resource *res; uint32_t resource_id, pformat; int i; @@ -1317,7 +1320,8 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, res->width, res->height, res->height ? res->hostmem / res->height : 0, - &error_warn)) { + &err)) { + warn_report_err(err); g_free(res); return -EINVAL; } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 6b5b5dace334a..7848e26278ada 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1289,6 +1289,8 @@ static bool virtio_net_load_ebpf_fds(VirtIONet *n, Error **errp) static bool virtio_net_load_ebpf(VirtIONet *n, Error **errp) { + Error *err = NULL; + if (!virtio_net_attach_ebpf_to_backend(n->nic, -1)) { return true; } @@ -1306,7 +1308,11 @@ static bool virtio_net_load_ebpf(VirtIONet *n, Error **errp) return virtio_net_load_ebpf_fds(n, errp); } - ebpf_rss_load(&n->ebpf_rss, &error_warn); + ebpf_rss_load(&n->ebpf_rss, &err); + /* Beware, ebpf_rss_load() can return false with @err unset */ + if (err) { + warn_report_err(err); + } return true; } diff --git a/include/qapi/error.h b/include/qapi/error.h index 41e3816380490..b16c6303f83ee 100644 --- a/include/qapi/error.h +++ b/include/qapi/error.h @@ -533,12 +533,6 @@ static inline void error_propagator_cleanup(ErrorPropagator *prop) G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(ErrorPropagator, error_propagator_cleanup); -/* - * Special error destination to warn on error. - * See error_setg() and error_propagate() for details. - */ -extern Error *error_warn; - /* * Special error destination to abort on error. * See error_setg() and error_propagate() for details. diff --git a/include/system/os-win32.h b/include/system/os-win32.h index 3aa6cee4c232c..22d72babdf407 100644 --- a/include/system/os-win32.h +++ b/include/system/os-win32.h @@ -168,11 +168,14 @@ static inline void qemu_funlockfile(FILE *f) #endif } -/* Helper for WSAEventSelect, to report errors */ +/* Helpers for WSAEventSelect() */ bool qemu_socket_select(int sockfd, WSAEVENT hEventObject, long lNetworkEvents, Error **errp); +void qemu_socket_select_nofail(int sockfd, WSAEVENT hEventObject, + long lNetworkEvents); bool qemu_socket_unselect(int sockfd, Error **errp); +void qemu_socket_unselect_nofail(int sockfd); /* We wrap all the sockets functions so that we can set errno based on * WSAGetLastError(), and use file-descriptors instead of SOCKET. diff --git a/io/channel-socket.c b/io/channel-socket.c index fdc767086753c..712b793eaf2cd 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -454,7 +454,7 @@ static void qio_channel_socket_finalize(Object *obj) } } #ifdef WIN32 - qemu_socket_unselect(ioc->fd, &error_warn); + qemu_socket_unselect_nofail(ioc->fd); #endif close(ioc->fd); ioc->fd = -1; @@ -929,7 +929,7 @@ qio_channel_socket_close(QIOChannel *ioc, if (sioc->fd != -1) { #ifdef WIN32 - qemu_socket_unselect(sioc->fd, &error_warn); + qemu_socket_unselect_nofail(sioc->fd); #endif if (qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_LISTEN)) { socket_listen_cleanup(sioc->fd, errp); diff --git a/io/channel-watch.c b/io/channel-watch.c index ec76bd1ec6a17..018648b36b4cf 100644 --- a/io/channel-watch.c +++ b/io/channel-watch.c @@ -281,9 +281,9 @@ GSource *qio_channel_create_socket_watch(QIOChannel *ioc, GSource *source; QIOChannelSocketSource *ssource; - qemu_socket_select(sockfd, ioc->event, - FD_READ | FD_ACCEPT | FD_CLOSE | - FD_CONNECT | FD_WRITE | FD_OOB, &error_warn); + qemu_socket_select_nofail(sockfd, ioc->event, + FD_READ | FD_ACCEPT | FD_CLOSE | + FD_CONNECT | FD_WRITE | FD_OOB); source = g_source_new(&qio_channel_socket_source_funcs, sizeof(QIOChannelSocketSource)); diff --git a/tests/unit/test-error-report.c b/tests/unit/test-error-report.c index 54319c86c927f..0cbde3c4cf5f4 100644 --- a/tests/unit/test-error-report.c +++ b/tests/unit/test-error-report.c @@ -104,22 +104,6 @@ test_error_report_timestamp(void) "); } -static void -test_error_warn(void) -{ - if (g_test_subprocess()) { - error_setg(&error_warn, "Testing &error_warn"); - return; - } - - g_test_trap_subprocess(NULL, 0, 0); - g_test_trap_assert_passed(); - g_test_trap_assert_stderr("\ -test-error-report: warning: Testing &error_warn*\ -"); -} - - int main(int argc, char *argv[]) { @@ -133,7 +117,6 @@ main(int argc, char *argv[]) g_test_add_func("/error-report/glog", test_error_report_glog); g_test_add_func("/error-report/once", test_error_report_once); g_test_add_func("/error-report/timestamp", test_error_report_timestamp); - g_test_add_func("/error-report/warn", test_error_warn); return g_test_run(); } diff --git a/ui/gtk.c b/ui/gtk.c index e91d093a49e5d..9a08cadc88ffb 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -1181,6 +1181,7 @@ static gboolean gd_touch_event(GtkWidget *widget, GdkEventTouch *touch, void *opaque) { VirtualConsole *vc = opaque; + Error *err = NULL; uint64_t num_slot = GPOINTER_TO_UINT(touch->sequence); int type = -1; @@ -1203,7 +1204,10 @@ static gboolean gd_touch_event(GtkWidget *widget, GdkEventTouch *touch, console_handle_touch_event(vc->gfx.dcl.con, touch_slots, num_slot, surface_width(vc->gfx.ds), surface_height(vc->gfx.ds), touch->x, - touch->y, type, &error_warn); + touch->y, type, &err); + if (err) { + warn_report_err(err); + } return TRUE; } diff --git a/util/aio-win32.c b/util/aio-win32.c index b125924433fa2..c6fbce64c2cb8 100644 --- a/util/aio-win32.c +++ b/util/aio-win32.c @@ -121,7 +121,7 @@ void aio_set_fd_handler(AioContext *ctx, QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, node, node); event = event_notifier_get_handle(&ctx->notifier); - qemu_socket_select(fd, event, bitmask, &error_warn); + qemu_socket_select_nofail(fd, event, bitmask); } if (old_node) { aio_remove_fd_handler(ctx, old_node); diff --git a/util/error.c b/util/error.c index daea2142f3012..0ae08225c095a 100644 --- a/util/error.c +++ b/util/error.c @@ -19,7 +19,6 @@ Error *error_abort; Error *error_fatal; -Error *error_warn; static void error_handle(Error **errp, Error *err) { @@ -41,9 +40,7 @@ static void error_handle(Error **errp, Error *err) error_report_err(err); exit(1); } - if (errp == &error_warn) { - warn_report_err(err); - } else if (errp && !*errp) { + if (errp && !*errp) { *errp = err; } else { error_free(err); diff --git a/util/oslib-win32.c b/util/oslib-win32.c index 6a2367c89defc..84bc65a76575b 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -182,7 +182,7 @@ bool qemu_set_blocking(int fd, bool block, Error **errp) unsigned long opt = block ? 0 : 1; if (block) { - qemu_socket_unselect(fd, &error_warn); + qemu_socket_unselect_nofail(fd); } if (ioctlsocket(fd, FIONBIO, &opt) != NO_ERROR) { @@ -311,6 +311,25 @@ bool qemu_socket_unselect(int sockfd, Error **errp) return qemu_socket_select(sockfd, NULL, 0, errp); } +void qemu_socket_select_nofail(int sockfd, WSAEVENT hEventObject, + long lNetworkEvents) +{ + Error *err = NULL; + + if (!qemu_socket_select(sockfd, hEventObject, lNetworkEvents, &err)) { + warn_report_err(err); + } +} + +void qemu_socket_unselect_nofail(int sockfd) +{ + Error *err = NULL; + + if (!qemu_socket_unselect(sockfd, &err)) { + warn_report_err(err); + } +} + int qemu_socketpair(int domain, int type, int protocol, int sv[2]) { struct sockaddr_un addr = { From 50873504dc3cc67ca06b4ebf307d084316e407a6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 29 Sep 2025 17:49:23 +0200 Subject: [PATCH 1081/1794] tracetool: fix usage of try_import() try_import returns a tuple of a boolean and the requested module or attribute. exists() functions return tracetool.try_import("tracetool.format." + name)[1] but they should return the boolean value instead. Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini Message-ID: <20250929154938.594389-2-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/backend/__init__.py | 2 +- scripts/tracetool/format/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/tracetool/backend/__init__.py b/scripts/tracetool/backend/__init__.py index 7bfcc86cc5366..6c6344deddb24 100644 --- a/scripts/tracetool/backend/__init__.py +++ b/scripts/tracetool/backend/__init__.py @@ -94,7 +94,7 @@ def exists(name): if name == "nop": return True name = name.replace("-", "_") - return tracetool.try_import("tracetool.backend." + name)[1] + return tracetool.try_import("tracetool.backend." + name)[0] class Wrapper: diff --git a/scripts/tracetool/format/__init__.py b/scripts/tracetool/format/__init__.py index 2dc46f3dd93e4..042fe7d103cd6 100644 --- a/scripts/tracetool/format/__init__.py +++ b/scripts/tracetool/format/__init__.py @@ -70,7 +70,7 @@ def exists(name): if len(name) == 0: return False name = name.replace("-", "_") - return tracetool.try_import("tracetool.format." + name)[1] + return tracetool.try_import("tracetool.format." + name)[0] def generate(events, format, backend, group): From 4c2f770f67e0b0d8b512defca4d81bd09936ab85 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 29 Sep 2025 17:49:24 +0200 Subject: [PATCH 1082/1794] tracetool: remove dead code Remove a bunch of dead code from tracetool. In particular, there are no tcg-exec events anymore and the sub-event functionality was only used for it. Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini Message-ID: <20250929154938.594389-3-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/__init__.py | 45 +++----------------- scripts/tracetool/format/h.py | 5 --- scripts/tracetool/format/log_stap.py | 2 - scripts/tracetool/format/simpletrace_stap.py | 2 - 4 files changed, 6 insertions(+), 48 deletions(-) diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 1d5238a084329..4ef1dc1fca3e8 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -15,7 +15,6 @@ import os import re import sys -import weakref from pathlib import PurePath import tracetool.backend @@ -122,10 +121,6 @@ def __init__(self, args): else: self._args.append(arg) - def copy(self): - """Create a new copy.""" - return Arguments(list(self._args)) - @staticmethod def build(arg_str): """Build and Arguments instance from an argument string. @@ -222,13 +217,12 @@ class Event(object): r"(?P\w+)" r"\((?P[^)]*)\)" r"\s*" - r"(?:(?:(?P\".+),)?\s*(?P\".+))?" + r"(?P\".+)?" r"\s*") _VALID_PROPS = set(["disable"]) - def __init__(self, name, props, fmt, args, lineno, filename, orig=None, - event_trans=None, event_exec=None): + def __init__(self, name, props, fmt, args, lineno, filename): """ Parameters ---------- @@ -236,20 +230,14 @@ def __init__(self, name, props, fmt, args, lineno, filename, orig=None, Event name. props : list of str Property names. - fmt : str, list of str - Event printing format string(s). + fmt : str + Event printing format string. args : Arguments Event arguments. lineno : int The line number in the input file. filename : str The path to the input file. - orig : Event or None - Original Event before transformation/generation. - event_trans : Event or None - Generated translation-time event ("tcg" property). - event_exec : Event or None - Generated execution-time event ("tcg" property). """ self.name = name @@ -258,29 +246,16 @@ def __init__(self, name, props, fmt, args, lineno, filename, orig=None, self.args = args self.lineno = int(lineno) self.filename = str(filename) - self.event_trans = event_trans - self.event_exec = event_exec if len(args) > 10: raise ValueError("Event '%s' has more than maximum permitted " "argument count" % name) - if orig is None: - self.original = weakref.ref(self) - else: - self.original = orig - unknown_props = set(self.properties) - self._VALID_PROPS if len(unknown_props) > 0: raise ValueError("Unknown properties: %s" % ", ".join(unknown_props)) - assert isinstance(self.fmt, str) or len(self.fmt) == 2 - def copy(self): - """Create a new copy.""" - return Event(self.name, list(self.properties), self.fmt, - self.args.copy(), self.lineno, self.filename, - self, self.event_trans, self.event_exec) @staticmethod def build(line_str, lineno, filename): @@ -302,8 +277,7 @@ def build(line_str, lineno, filename): name = groups["name"] props = groups["props"].split() fmt = groups["fmt"] - fmt_trans = groups["fmt_trans"] - if fmt.find("%m") != -1 or fmt_trans.find("%m") != -1: + if fmt.find("%m") != -1: raise ValueError("Event format '%m' is forbidden, pass the error " "as an explicit trace argument") if fmt.endswith(r'\n"'): @@ -312,29 +286,22 @@ def build(line_str, lineno, filename): if '\\n' in fmt: raise ValueError("Event format must not use new line character") - if len(fmt_trans) > 0: - fmt = [fmt_trans, fmt] args = Arguments.build(groups["args"]) return Event(name, props, fmt, args, lineno, posix_relpath(filename)) def __repr__(self): """Evaluable string representation for this object.""" - if isinstance(self.fmt, str): - fmt = self.fmt - else: - fmt = "%s, %s" % (self.fmt[0], self.fmt[1]) return "Event('%s %s(%s) %s')" % (" ".join(self.properties), self.name, self.args, - fmt) + self.fmt) # Star matching on PRI is dangerous as one might have multiple # arguments with that format, hence the non-greedy version of it. _FMT = re.compile(r"(%[\d\.]*\w+|%.*?PRI\S+)") def formats(self): """List conversion specifiers in the argument print format string.""" - assert not isinstance(self.fmt, list) return self._FMT.findall(self.fmt) QEMU_TRACE = "trace_%(name)s" diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py index b42a8268a81f2..bd9e0ca7f2362 100644 --- a/scripts/tracetool/format/h.py +++ b/scripts/tracetool/format/h.py @@ -40,11 +40,6 @@ def generate(events, backend, group): enabled = 0 else: enabled = 1 - if "tcg-exec" in e.properties: - # a single define for the two "sub-events" - out('#define TRACE_%(name)s_ENABLED %(enabled)d', - name=e.original.name.upper(), - enabled=enabled) out('#define TRACE_%s_ENABLED %d' % (e.name.upper(), enabled)) backend.generate_begin(events, group) diff --git a/scripts/tracetool/format/log_stap.py b/scripts/tracetool/format/log_stap.py index 710d62bffe422..5b1bbe907ff5b 100644 --- a/scripts/tracetool/format/log_stap.py +++ b/scripts/tracetool/format/log_stap.py @@ -18,8 +18,6 @@ from tracetool.backend.simple import is_string from tracetool.format.stap import stap_escape -def global_var_name(name): - return probeprefix().replace(".", "_") + "_" + name STATE_SKIP = 0 STATE_LITERAL = 1 diff --git a/scripts/tracetool/format/simpletrace_stap.py b/scripts/tracetool/format/simpletrace_stap.py index 72971133bf016..ac39a492d961c 100644 --- a/scripts/tracetool/format/simpletrace_stap.py +++ b/scripts/tracetool/format/simpletrace_stap.py @@ -17,8 +17,6 @@ from tracetool.backend.simple import is_string from tracetool.format.stap import stap_escape -def global_var_name(name): - return probeprefix().replace(".", "_") + "_" + name def generate(events, backend, group): out('/* This file is autogenerated by tracetool, do not edit. */', From 20b92da6db74490a6ffdf01f3ec8d99fcb7c2c18 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 29 Sep 2025 17:49:25 +0200 Subject: [PATCH 1083/1794] treewide: remove unnessary "coding" header The "-*- coding: utf-8 -*-" header was needed in Python 2, but in Python 3 UTF-8 is already the default encoding of source files. It is even less necessary in .css files that do not have UTF-8 sequences at all. Suggested-by: Manos Pitsidianakis Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini Message-ID: <20250929154938.594389-4-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- docs/conf.py | 2 -- docs/sphinx-static/theme_overrides.css | 3 +-- scripts/analyse-locks-simpletrace.py | 1 - scripts/modinfo-collect.py | 1 - scripts/modinfo-generate.py | 1 - scripts/oss-fuzz/minimize_qtest_trace.py | 1 - scripts/oss-fuzz/output_reproducer.py | 1 - scripts/oss-fuzz/reorder_fuzzer_qtest_trace.py | 1 - scripts/probe-gdb-support.py | 1 - scripts/qapi/error.py | 2 -- scripts/qapi/expr.py | 2 -- scripts/qapi/gen.py | 2 -- scripts/qapi/parser.py | 2 -- scripts/qapi/schema.py | 2 -- scripts/qemu-plugin-symbols.py | 1 - scripts/qemugdb/tcg.py | 2 -- scripts/qemugdb/timers.py | 1 - scripts/replay-dump.py | 1 - scripts/tracetool.py | 1 - scripts/tracetool/__init__.py | 2 -- scripts/tracetool/backend/__init__.py | 2 -- scripts/tracetool/backend/dtrace.py | 2 -- scripts/tracetool/backend/ftrace.py | 2 -- scripts/tracetool/backend/log.py | 2 -- scripts/tracetool/backend/simple.py | 2 -- scripts/tracetool/backend/syslog.py | 2 -- scripts/tracetool/backend/ust.py | 2 -- scripts/tracetool/format/__init__.py | 2 -- scripts/tracetool/format/c.py | 2 -- scripts/tracetool/format/d.py | 2 -- scripts/tracetool/format/h.py | 2 -- scripts/tracetool/format/log_stap.py | 2 -- scripts/tracetool/format/simpletrace_stap.py | 2 -- scripts/tracetool/format/stap.py | 2 -- scripts/tracetool/format/ust_events_c.py | 2 -- scripts/tracetool/format/ust_events_h.py | 2 -- 36 files changed, 1 insertion(+), 61 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index e09769e5f83d4..0c9ec74097fab 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # QEMU documentation build configuration file, created by # sphinx-quickstart on Thu Jan 31 16:40:14 2019. # diff --git a/docs/sphinx-static/theme_overrides.css b/docs/sphinx-static/theme_overrides.css index b225bf706f505..f312e9b57e4a0 100644 --- a/docs/sphinx-static/theme_overrides.css +++ b/docs/sphinx-static/theme_overrides.css @@ -1,5 +1,4 @@ -/* -*- coding: utf-8; mode: css -*- - * +/* * Sphinx HTML theme customization: read the doc * Based on Linux Documentation/sphinx-static/theme_overrides.css */ diff --git a/scripts/analyse-locks-simpletrace.py b/scripts/analyse-locks-simpletrace.py index d650dd7140867..bd04cd43c9429 100755 --- a/scripts/analyse-locks-simpletrace.py +++ b/scripts/analyse-locks-simpletrace.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # Analyse lock events and compute statistics # diff --git a/scripts/modinfo-collect.py b/scripts/modinfo-collect.py index 48bd92bd61808..6ebaea989db64 100644 --- a/scripts/modinfo-collect.py +++ b/scripts/modinfo-collect.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- import os import sys diff --git a/scripts/modinfo-generate.py b/scripts/modinfo-generate.py index b1538fcced77e..aaf23544c4622 100644 --- a/scripts/modinfo-generate.py +++ b/scripts/modinfo-generate.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- import os import sys diff --git a/scripts/oss-fuzz/minimize_qtest_trace.py b/scripts/oss-fuzz/minimize_qtest_trace.py index d1f3990c16a7c..414a6d91dd816 100755 --- a/scripts/oss-fuzz/minimize_qtest_trace.py +++ b/scripts/oss-fuzz/minimize_qtest_trace.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ This takes a crashing qtest trace and tries to remove superfluous operations diff --git a/scripts/oss-fuzz/output_reproducer.py b/scripts/oss-fuzz/output_reproducer.py index e8ef76b341381..0df96cf95878e 100755 --- a/scripts/oss-fuzz/output_reproducer.py +++ b/scripts/oss-fuzz/output_reproducer.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ Convert plain qtest traces to C or Bash reproducers diff --git a/scripts/oss-fuzz/reorder_fuzzer_qtest_trace.py b/scripts/oss-fuzz/reorder_fuzzer_qtest_trace.py index b154a25508f7e..8af0d5d9c4e04 100755 --- a/scripts/oss-fuzz/reorder_fuzzer_qtest_trace.py +++ b/scripts/oss-fuzz/reorder_fuzzer_qtest_trace.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ Use this to convert qtest log info from a generic fuzzer input into a qtest diff --git a/scripts/probe-gdb-support.py b/scripts/probe-gdb-support.py index 6bcadce15007f..43c7030287385 100644 --- a/scripts/probe-gdb-support.py +++ b/scripts/probe-gdb-support.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# coding: utf-8 # # Probe gdb for supported architectures. # diff --git a/scripts/qapi/error.py b/scripts/qapi/error.py index e35e4ddb26a01..f73bc553db6fd 100644 --- a/scripts/qapi/error.py +++ b/scripts/qapi/error.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # Copyright (c) 2017-2019 Red Hat Inc. # # Authors: diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index cae0a083591ab..f40b247f8b61b 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # Copyright IBM, Corp. 2011 # Copyright (c) 2013-2021 Red Hat Inc. # diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index d3c56d45c8925..0c9b8db3b0241 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # QAPI code generation # # Copyright (c) 2015-2019 Red Hat Inc. diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 2529edf81aa8e..9fbf80a541045 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # QAPI schema parser # # Copyright IBM, Corp. 2011 diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 3abddea35257e..8d88b40de2e19 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # QAPI schema internal representation # # Copyright (c) 2015-2019 Red Hat Inc. diff --git a/scripts/qemu-plugin-symbols.py b/scripts/qemu-plugin-symbols.py index e285ebb8f9ed3..69644979c19b2 100755 --- a/scripts/qemu-plugin-symbols.py +++ b/scripts/qemu-plugin-symbols.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # Extract QEMU Plugin API symbols from a header file # diff --git a/scripts/qemugdb/tcg.py b/scripts/qemugdb/tcg.py index 16c03c06a9437..22529c72775cd 100644 --- a/scripts/qemugdb/tcg.py +++ b/scripts/qemugdb/tcg.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # GDB debugging support, TCG status # # Copyright 2016 Linaro Ltd diff --git a/scripts/qemugdb/timers.py b/scripts/qemugdb/timers.py index 46537b27cf058..5714f92cc211e 100644 --- a/scripts/qemugdb/timers.py +++ b/scripts/qemugdb/timers.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # GDB debugging support # # Copyright 2017 Linaro Ltd diff --git a/scripts/replay-dump.py b/scripts/replay-dump.py index 4ce7ff51cc7b3..097636570dd89 100755 --- a/scripts/replay-dump.py +++ b/scripts/replay-dump.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # Dump the contents of a recorded execution stream # diff --git a/scripts/tracetool.py b/scripts/tracetool.py index 5de9ce96d3024..0fdc9cb94770b 100755 --- a/scripts/tracetool.py +++ b/scripts/tracetool.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ Command-line wrapper for the tracetool machinery. diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 4ef1dc1fca3e8..9dd8ec27e1544 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ Machinery for generating tracing-related intermediate files. """ diff --git a/scripts/tracetool/backend/__init__.py b/scripts/tracetool/backend/__init__.py index 6c6344deddb24..70401309196e6 100644 --- a/scripts/tracetool/backend/__init__.py +++ b/scripts/tracetool/backend/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ Backend management. diff --git a/scripts/tracetool/backend/dtrace.py b/scripts/tracetool/backend/dtrace.py index e17edc9b9d829..4835454193d1a 100644 --- a/scripts/tracetool/backend/dtrace.py +++ b/scripts/tracetool/backend/dtrace.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ DTrace/SystemTAP backend. """ diff --git a/scripts/tracetool/backend/ftrace.py b/scripts/tracetool/backend/ftrace.py index 5fa30ccc08e69..e6317ca4bfbd5 100644 --- a/scripts/tracetool/backend/ftrace.py +++ b/scripts/tracetool/backend/ftrace.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ Ftrace built-in backend. """ diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index eb50ceea34c09..9842522b18447 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ Stderr built-in backend. """ diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py index 7c84c06b20084..066e5e9f11f21 100644 --- a/scripts/tracetool/backend/simple.py +++ b/scripts/tracetool/backend/simple.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ Simple built-in backend. """ diff --git a/scripts/tracetool/backend/syslog.py b/scripts/tracetool/backend/syslog.py index 3f82e54aabfdf..74af038089906 100644 --- a/scripts/tracetool/backend/syslog.py +++ b/scripts/tracetool/backend/syslog.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ Syslog built-in backend. """ diff --git a/scripts/tracetool/backend/ust.py b/scripts/tracetool/backend/ust.py index c857516f21261..6cc651646dd69 100644 --- a/scripts/tracetool/backend/ust.py +++ b/scripts/tracetool/backend/ust.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ LTTng User Space Tracing backend. """ diff --git a/scripts/tracetool/format/__init__.py b/scripts/tracetool/format/__init__.py index 042fe7d103cd6..94a37bfce9fb5 100644 --- a/scripts/tracetool/format/__init__.py +++ b/scripts/tracetool/format/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ Format management. diff --git a/scripts/tracetool/format/c.py b/scripts/tracetool/format/c.py index e473fb6c6eb05..3c4398c237300 100644 --- a/scripts/tracetool/format/c.py +++ b/scripts/tracetool/format/c.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ trace/generated-tracers.c """ diff --git a/scripts/tracetool/format/d.py b/scripts/tracetool/format/d.py index a5e096e214b81..684598c18351d 100644 --- a/scripts/tracetool/format/d.py +++ b/scripts/tracetool/format/d.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ trace/generated-tracers.dtrace (DTrace only). """ diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py index bd9e0ca7f2362..2119753be10da 100644 --- a/scripts/tracetool/format/h.py +++ b/scripts/tracetool/format/h.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ trace/generated-tracers.h """ diff --git a/scripts/tracetool/format/log_stap.py b/scripts/tracetool/format/log_stap.py index 5b1bbe907ff5b..02f23bfb8d28e 100644 --- a/scripts/tracetool/format/log_stap.py +++ b/scripts/tracetool/format/log_stap.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ Generate .stp file that printfs log messages (DTrace with SystemTAP only). """ diff --git a/scripts/tracetool/format/simpletrace_stap.py b/scripts/tracetool/format/simpletrace_stap.py index ac39a492d961c..3c3584a9318f3 100644 --- a/scripts/tracetool/format/simpletrace_stap.py +++ b/scripts/tracetool/format/simpletrace_stap.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ Generate .stp file that outputs simpletrace binary traces (DTrace with SystemTAP only). """ diff --git a/scripts/tracetool/format/stap.py b/scripts/tracetool/format/stap.py index 4d77fbc11a9d9..04c7a35a25fe9 100644 --- a/scripts/tracetool/format/stap.py +++ b/scripts/tracetool/format/stap.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ Generate .stp file (DTrace with SystemTAP only). """ diff --git a/scripts/tracetool/format/ust_events_c.py b/scripts/tracetool/format/ust_events_c.py index 569754a30482e..ea5f0ae99fa51 100644 --- a/scripts/tracetool/format/ust_events_c.py +++ b/scripts/tracetool/format/ust_events_c.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ trace/generated-ust.c """ diff --git a/scripts/tracetool/format/ust_events_h.py b/scripts/tracetool/format/ust_events_h.py index 2a31fefeca1e2..242c9814fdc51 100644 --- a/scripts/tracetool/format/ust_events_h.py +++ b/scripts/tracetool/format/ust_events_h.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ trace/generated-ust-provider.h """ From 6f94ad27f0014c04a2aa403988acc22740a21196 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 29 Sep 2025 17:49:26 +0200 Subject: [PATCH 1084/1794] tracetool: add SPDX headers Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini Message-ID: <20250929154938.594389-5-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/__init__.py | 2 ++ scripts/tracetool/backend/__init__.py | 2 ++ scripts/tracetool/backend/dtrace.py | 2 ++ scripts/tracetool/backend/ftrace.py | 2 ++ scripts/tracetool/backend/log.py | 2 ++ scripts/tracetool/backend/simple.py | 2 ++ scripts/tracetool/backend/syslog.py | 2 ++ scripts/tracetool/backend/ust.py | 2 ++ scripts/tracetool/format/__init__.py | 2 ++ scripts/tracetool/format/c.py | 2 ++ scripts/tracetool/format/d.py | 2 ++ scripts/tracetool/format/h.py | 2 ++ scripts/tracetool/format/log_stap.py | 2 ++ scripts/tracetool/format/simpletrace_stap.py | 2 ++ scripts/tracetool/format/stap.py | 2 ++ scripts/tracetool/format/ust_events_c.py | 2 ++ scripts/tracetool/format/ust_events_h.py | 2 ++ 17 files changed, 34 insertions(+) diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 9dd8ec27e1544..c4fa0f74e6fc0 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ Machinery for generating tracing-related intermediate files. """ diff --git a/scripts/tracetool/backend/__init__.py b/scripts/tracetool/backend/__init__.py index 70401309196e6..bf91e443e99d0 100644 --- a/scripts/tracetool/backend/__init__.py +++ b/scripts/tracetool/backend/__init__.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ Backend management. diff --git a/scripts/tracetool/backend/dtrace.py b/scripts/tracetool/backend/dtrace.py index 4835454193d1a..b4af403025c84 100644 --- a/scripts/tracetool/backend/dtrace.py +++ b/scripts/tracetool/backend/dtrace.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ DTrace/SystemTAP backend. """ diff --git a/scripts/tracetool/backend/ftrace.py b/scripts/tracetool/backend/ftrace.py index e6317ca4bfbd5..a14aafcee62fc 100644 --- a/scripts/tracetool/backend/ftrace.py +++ b/scripts/tracetool/backend/ftrace.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ Ftrace built-in backend. """ diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index 9842522b18447..faacec46105e2 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ Stderr built-in backend. """ diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py index 066e5e9f11f21..97e40495ee9ed 100644 --- a/scripts/tracetool/backend/simple.py +++ b/scripts/tracetool/backend/simple.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ Simple built-in backend. """ diff --git a/scripts/tracetool/backend/syslog.py b/scripts/tracetool/backend/syslog.py index 74af038089906..78ee67136b8c3 100644 --- a/scripts/tracetool/backend/syslog.py +++ b/scripts/tracetool/backend/syslog.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ Syslog built-in backend. """ diff --git a/scripts/tracetool/backend/ust.py b/scripts/tracetool/backend/ust.py index 6cc651646dd69..3aa9bb1da2991 100644 --- a/scripts/tracetool/backend/ust.py +++ b/scripts/tracetool/backend/ust.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ LTTng User Space Tracing backend. """ diff --git a/scripts/tracetool/format/__init__.py b/scripts/tracetool/format/__init__.py index 94a37bfce9fb5..7b9d1b578265e 100644 --- a/scripts/tracetool/format/__init__.py +++ b/scripts/tracetool/format/__init__.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ Format management. diff --git a/scripts/tracetool/format/c.py b/scripts/tracetool/format/c.py index 3c4398c237300..50e03313cbf18 100644 --- a/scripts/tracetool/format/c.py +++ b/scripts/tracetool/format/c.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ trace/generated-tracers.c """ diff --git a/scripts/tracetool/format/d.py b/scripts/tracetool/format/d.py index 684598c18351d..e9e33dfe30a3a 100644 --- a/scripts/tracetool/format/d.py +++ b/scripts/tracetool/format/d.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ trace/generated-tracers.dtrace (DTrace only). """ diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py index 2119753be10da..be7f32e67b918 100644 --- a/scripts/tracetool/format/h.py +++ b/scripts/tracetool/format/h.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ trace/generated-tracers.h """ diff --git a/scripts/tracetool/format/log_stap.py b/scripts/tracetool/format/log_stap.py index 02f23bfb8d28e..259303a189df6 100644 --- a/scripts/tracetool/format/log_stap.py +++ b/scripts/tracetool/format/log_stap.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ Generate .stp file that printfs log messages (DTrace with SystemTAP only). """ diff --git a/scripts/tracetool/format/simpletrace_stap.py b/scripts/tracetool/format/simpletrace_stap.py index 3c3584a9318f3..c7bde97a8556d 100644 --- a/scripts/tracetool/format/simpletrace_stap.py +++ b/scripts/tracetool/format/simpletrace_stap.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ Generate .stp file that outputs simpletrace binary traces (DTrace with SystemTAP only). """ diff --git a/scripts/tracetool/format/stap.py b/scripts/tracetool/format/stap.py index 04c7a35a25fe9..285c9203ba707 100644 --- a/scripts/tracetool/format/stap.py +++ b/scripts/tracetool/format/stap.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ Generate .stp file (DTrace with SystemTAP only). """ diff --git a/scripts/tracetool/format/ust_events_c.py b/scripts/tracetool/format/ust_events_c.py index ea5f0ae99fa51..074226bfd3766 100644 --- a/scripts/tracetool/format/ust_events_c.py +++ b/scripts/tracetool/format/ust_events_c.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ trace/generated-ust.c """ diff --git a/scripts/tracetool/format/ust_events_h.py b/scripts/tracetool/format/ust_events_h.py index 242c9814fdc51..cee7970a40368 100644 --- a/scripts/tracetool/format/ust_events_h.py +++ b/scripts/tracetool/format/ust_events_h.py @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + """ trace/generated-ust-provider.h """ From 02d4a4a674d45d41c9afd1fd161c14eca81f7a86 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 29 Sep 2025 17:49:27 +0200 Subject: [PATCH 1085/1794] trace/ftrace: move snprintf+write from tracepoints to ftrace.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This simplifies the Python code and reduces the size of the tracepoints. Reviewed-by: Manos Pitsidianakis Reviewed-by: Zhao Liu Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini Reviewed-by: Daniel P. Berrangé Message-ID: <20250929154938.594389-6-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/backend/ftrace.py | 12 ++---------- tests/tracetool/ftrace.h | 28 ++++++---------------------- trace/ftrace.c | 15 +++++++++++++++ trace/ftrace.h | 1 + 4 files changed, 24 insertions(+), 32 deletions(-) diff --git a/scripts/tracetool/backend/ftrace.py b/scripts/tracetool/backend/ftrace.py index a14aafcee62fc..8c0ce3f23a0d7 100644 --- a/scripts/tracetool/backend/ftrace.py +++ b/scripts/tracetool/backend/ftrace.py @@ -28,18 +28,10 @@ def generate_h(event, group): if len(event.args) > 0: argnames = ", " + argnames - out(' {', - ' char ftrace_buf[MAX_TRACE_STRLEN];', - ' int unused __attribute__ ((unused));', - ' int trlen;', - ' if (trace_event_get_state(%(event_id)s)) {', + out(' if (trace_event_get_state(%(event_id)s)) {', '#line %(event_lineno)d "%(event_filename)s"', - ' trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN,', - ' "%(name)s " %(fmt)s "\\n" %(argnames)s);', + ' ftrace_write("%(name)s " %(fmt)s "\\n" %(argnames)s);', '#line %(out_next_lineno)d "%(out_filename)s"', - ' trlen = MIN(trlen, MAX_TRACE_STRLEN - 1);', - ' unused = write(trace_marker_fd, ftrace_buf, trlen);', - ' }', ' }', name=event.name, args=event.args, diff --git a/tests/tracetool/ftrace.h b/tests/tracetool/ftrace.h index fe22ea0f09ffd..1dfe42394136c 100644 --- a/tests/tracetool/ftrace.h +++ b/tests/tracetool/ftrace.h @@ -21,18 +21,10 @@ extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; static inline void trace_test_blah(void *context, const char *filename) { - { - char ftrace_buf[MAX_TRACE_STRLEN]; - int unused __attribute__ ((unused)); - int trlen; - if (trace_event_get_state(TRACE_TEST_BLAH)) { + if (trace_event_get_state(TRACE_TEST_BLAH)) { #line 4 "trace-events" - trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN, - "test_blah " "Blah context=%p filename=%s" "\n" , context, filename); -#line 33 "ftrace.h" - trlen = MIN(trlen, MAX_TRACE_STRLEN - 1); - unused = write(trace_marker_fd, ftrace_buf, trlen); - } + ftrace_write("test_blah " "Blah context=%p filename=%s" "\n" , context, filename); +#line 28 "ftrace.h" } } @@ -42,18 +34,10 @@ static inline void trace_test_blah(void *context, const char *filename) static inline void trace_test_wibble(void *context, int value) { - { - char ftrace_buf[MAX_TRACE_STRLEN]; - int unused __attribute__ ((unused)); - int trlen; - if (trace_event_get_state(TRACE_TEST_WIBBLE)) { + if (trace_event_get_state(TRACE_TEST_WIBBLE)) { #line 5 "trace-events" - trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN, - "test_wibble " "Wibble context=%p value=%d" "\n" , context, value); -#line 54 "ftrace.h" - trlen = MIN(trlen, MAX_TRACE_STRLEN - 1); - unused = write(trace_marker_fd, ftrace_buf, trlen); - } + ftrace_write("test_wibble " "Wibble context=%p value=%d" "\n" , context, value); +#line 41 "ftrace.h" } } #endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/trace/ftrace.c b/trace/ftrace.c index 9749543d9b242..6875faedb9c1d 100644 --- a/trace/ftrace.c +++ b/trace/ftrace.c @@ -38,6 +38,21 @@ static int find_mount(char *mount_point, const char *fstype) return ret; } +void ftrace_write(const char *fmt, ...) +{ + char ftrace_buf[MAX_TRACE_STRLEN]; + int unused __attribute__ ((unused)); + int trlen; + va_list ap; + + va_start(ap, fmt); + trlen = vsnprintf(ftrace_buf, MAX_TRACE_STRLEN, fmt, ap); + va_end(ap); + + trlen = MIN(trlen, MAX_TRACE_STRLEN - 1); + unused = write(trace_marker_fd, ftrace_buf, trlen); +} + bool ftrace_init(void) { char mount_point[PATH_MAX]; diff --git a/trace/ftrace.h b/trace/ftrace.h index cb5e35d2171d7..16c122816d178 100644 --- a/trace/ftrace.h +++ b/trace/ftrace.h @@ -8,5 +8,6 @@ extern int trace_marker_fd; bool ftrace_init(void); +G_GNUC_PRINTF(1, 2) void ftrace_write(const char *fmt, ...); #endif /* TRACE_FTRACE_H */ From 809caa0d40a853144c02456737ed1e47859b0308 Mon Sep 17 00:00:00 2001 From: Tanish Desai Date: Mon, 29 Sep 2025 17:49:28 +0200 Subject: [PATCH 1086/1794] tracetool: add CHECK_TRACE_EVENT_GET_STATE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new attribute CHECK_TRACE_EVENT_GET_STATE to the backends. When present and True, the code generated by the generate function is wrapped in a conditional that checks whether the event is enabled; this removes the need for repeating the same conditional in multiple backends. Signed-off-by: Tanish Desai Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini Reviewed-by: Daniel P. Berrangé Message-ID: <20250929154938.594389-7-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/backend/__init__.py | 39 ++++++++++++++++++--------- scripts/tracetool/format/h.py | 13 ++++++--- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/scripts/tracetool/backend/__init__.py b/scripts/tracetool/backend/__init__.py index bf91e443e99d0..9109a783c72f6 100644 --- a/scripts/tracetool/backend/__init__.py +++ b/scripts/tracetool/backend/__init__.py @@ -19,11 +19,15 @@ Backend attributes ------------------ -========= ==================================================================== -Attribute Description -========= ==================================================================== -PUBLIC If exists and is set to 'True', the backend is considered "public". -========= ==================================================================== +=========================== ==================================================== +Attribute Description +=========================== ==================================================== +PUBLIC If exists and is set to 'True', the backend is + considered "public". +CHECK_TRACE_EVENT_GET_STATE If exists and is set to 'True', the backend-specific + code inside the tracepoint is emitted within an + ``if trace_event_get_state()`` conditional. +=========================== ==================================================== Backend functions @@ -101,22 +105,33 @@ class Wrapper: def __init__(self, backends, format): self._backends = [backend.replace("-", "_") for backend in backends] self._format = format.replace("-", "_") + self.check_trace_event_get_state = False for backend in self._backends: assert exists(backend) assert tracetool.format.exists(self._format) + for backend in self.backend_modules(): + check_trace_event_get_state = getattr(backend, "CHECK_TRACE_EVENT_GET_STATE", False) + self.check_trace_event_get_state = self.check_trace_event_get_state or check_trace_event_get_state - def _run_function(self, name, *args, **kwargs): + def backend_modules(self): for backend in self._backends: - func = tracetool.try_import("tracetool.backend." + backend, - name % self._format, None)[1] - if func is not None: - func(*args, **kwargs) + module = tracetool.try_import("tracetool.backend." + backend)[1] + if module is not None: + yield module + + def _run_function(self, name, *args, check_trace_event_get_state=None, **kwargs): + for backend in self.backend_modules(): + func = getattr(backend, name % self._format, None) + if func is not None and \ + (check_trace_event_get_state is None or + check_trace_event_get_state == getattr(backend, 'CHECK_TRACE_EVENT_GET_STATE', False)): + func(*args, **kwargs) def generate_begin(self, events, group): self._run_function("generate_%s_begin", events, group) - def generate(self, event, group): - self._run_function("generate_%s", event, group) + def generate(self, event, group, check_trace_event_get_state=None): + self._run_function("generate_%s", event, group, check_trace_event_get_state=check_trace_event_get_state) def generate_backend_dstate(self, event, group): self._run_function("generate_%s_backend_dstate", event, group) diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py index be7f32e67b918..dd58713a15816 100644 --- a/scripts/tracetool/format/h.py +++ b/scripts/tracetool/format/h.py @@ -55,7 +55,6 @@ def generate(events, backend, group): out(' false)') - # tracer without checks out('', 'static inline void %(api)s(%(args)s)', '{', @@ -63,11 +62,17 @@ def generate(events, backend, group): args=e.args) if "disable" not in e.properties: - backend.generate(e, group) - + backend.generate(e, group, check_trace_event_get_state=False) + + if backend.check_trace_event_get_state: + event_id = 'TRACE_' + e.name.upper() + cond = "trace_event_get_state(%s)" % event_id + out(' if (%(cond)s) {', + cond=cond) + backend.generate(e, group, check_trace_event_get_state=True) + out(' }') out('}') - backend.generate_end(events, group) out('#endif /* TRACE_%s_GENERATED_TRACERS_H */' % group.upper()) From 494492c5c5a145e04f3ebc9eb40fd34ffbcc2061 Mon Sep 17 00:00:00 2001 From: Tanish Desai Date: Mon, 29 Sep 2025 17:49:29 +0200 Subject: [PATCH 1087/1794] tracetool/backend: remove redundant trace event checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use CHECK_TRACE_EVENT_GET_STATE in log, syslog, dtrace and simple backend, so that the "if (trace_event_get_state)" is created from common code and unified when multiple backends are active. When a single backend is active there is no code change (except for the log backend, as shown in tests/tracetool/log.h), but the code in the backends is simpler. Signed-off-by: Tanish Desai Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini Reviewed-by: Daniel P. Berrangé Message-ID: <20250929154938.594389-8-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/backend/ftrace.py | 6 ++---- scripts/tracetool/backend/log.py | 10 ++++------ scripts/tracetool/backend/simple.py | 8 ++------ scripts/tracetool/backend/syslog.py | 8 ++------ tests/tracetool/log.h | 16 ++++++++++------ 5 files changed, 20 insertions(+), 28 deletions(-) diff --git a/scripts/tracetool/backend/ftrace.py b/scripts/tracetool/backend/ftrace.py index 8c0ce3f23a0d7..7ddd5d11a66a9 100644 --- a/scripts/tracetool/backend/ftrace.py +++ b/scripts/tracetool/backend/ftrace.py @@ -16,6 +16,7 @@ PUBLIC = True +CHECK_TRACE_EVENT_GET_STATE = True def generate_h_begin(events, group): @@ -28,14 +29,11 @@ def generate_h(event, group): if len(event.args) > 0: argnames = ", " + argnames - out(' if (trace_event_get_state(%(event_id)s)) {', - '#line %(event_lineno)d "%(event_filename)s"', + out('#line %(event_lineno)d "%(event_filename)s"', ' ftrace_write("%(name)s " %(fmt)s "\\n" %(argnames)s);', '#line %(out_next_lineno)d "%(out_filename)s"', - ' }', name=event.name, args=event.args, - event_id="TRACE_" + event.name.upper(), event_lineno=event.lineno, event_filename=event.filename, fmt=event.fmt.rstrip("\n"), diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index faacec46105e2..d01d234289f18 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -16,6 +16,7 @@ PUBLIC = True +CHECK_TRACE_EVENT_GET_STATE = True def generate_h_begin(events, group): @@ -28,14 +29,11 @@ def generate_h(event, group): if len(event.args) > 0: argnames = ", " + argnames - cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) - - out(' if (%(cond)s && qemu_loglevel_mask(LOG_TRACE)) {', + out(' if (qemu_loglevel_mask(LOG_TRACE)) {', '#line %(event_lineno)d "%(event_filename)s"', - ' qemu_log("%(name)s " %(fmt)s "\\n"%(argnames)s);', + ' qemu_log("%(name)s " %(fmt)s "\\n"%(argnames)s);', '#line %(out_next_lineno)d "%(out_filename)s"', - ' }', - cond=cond, + ' }', event_lineno=event.lineno, event_filename=event.filename, name=event.name, diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py index 97e40495ee9ed..b5a6b7205a316 100644 --- a/scripts/tracetool/backend/simple.py +++ b/scripts/tracetool/backend/simple.py @@ -16,6 +16,7 @@ PUBLIC = True +CHECK_TRACE_EVENT_GET_STATE = True def is_string(arg): @@ -36,13 +37,8 @@ def generate_h_begin(events, group): def generate_h(event, group): - event_id = 'TRACE_' + event.name.upper() - cond = "trace_event_get_state(%s)" % event_id - out(' if (%(cond)s) {', - ' _simple_%(api)s(%(args)s);', - ' }', + out(' _simple_%(api)s(%(args)s);', api=event.api(), - cond=cond, args=", ".join(event.args.names())) diff --git a/scripts/tracetool/backend/syslog.py b/scripts/tracetool/backend/syslog.py index 78ee67136b8c3..177414d56a685 100644 --- a/scripts/tracetool/backend/syslog.py +++ b/scripts/tracetool/backend/syslog.py @@ -16,6 +16,7 @@ PUBLIC = True +CHECK_TRACE_EVENT_GET_STATE = True def generate_h_begin(events, group): @@ -28,14 +29,9 @@ def generate_h(event, group): if len(event.args) > 0: argnames = ", " + argnames - cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) - - out(' if (%(cond)s) {', - '#line %(event_lineno)d "%(event_filename)s"', + out('#line %(event_lineno)d "%(event_filename)s"', ' syslog(LOG_INFO, "%(name)s " %(fmt)s %(argnames)s);', '#line %(out_next_lineno)d "%(out_filename)s"', - ' }', - cond=cond, event_lineno=event.lineno, event_filename=event.filename, name=event.name, diff --git a/tests/tracetool/log.h b/tests/tracetool/log.h index edcc7f9d47cfe..c7795871f8514 100644 --- a/tests/tracetool/log.h +++ b/tests/tracetool/log.h @@ -21,10 +21,12 @@ extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; static inline void trace_test_blah(void *context, const char *filename) { - if (trace_event_get_state(TRACE_TEST_BLAH) && qemu_loglevel_mask(LOG_TRACE)) { + if (trace_event_get_state(TRACE_TEST_BLAH)) { + if (qemu_loglevel_mask(LOG_TRACE)) { #line 4 "trace-events" - qemu_log("test_blah " "Blah context=%p filename=%s" "\n", context, filename); -#line 28 "log.h" + qemu_log("test_blah " "Blah context=%p filename=%s" "\n", context, filename); +#line 29 "log.h" + } } } @@ -34,10 +36,12 @@ static inline void trace_test_blah(void *context, const char *filename) static inline void trace_test_wibble(void *context, int value) { - if (trace_event_get_state(TRACE_TEST_WIBBLE) && qemu_loglevel_mask(LOG_TRACE)) { + if (trace_event_get_state(TRACE_TEST_WIBBLE)) { + if (qemu_loglevel_mask(LOG_TRACE)) { #line 5 "trace-events" - qemu_log("test_wibble " "Wibble context=%p value=%d" "\n", context, value); -#line 41 "log.h" + qemu_log("test_wibble " "Wibble context=%p value=%d" "\n", context, value); +#line 44 "log.h" + } } } #endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ From e2e182bef73f24d885f7f2ca589d8ec004c24877 Mon Sep 17 00:00:00 2001 From: Tanish Desai Date: Mon, 29 Sep 2025 17:49:30 +0200 Subject: [PATCH 1088/1794] tracetool: Add Rust format support Generating .rs files makes it possible to support tracing in rust. This support comprises a new format, and common code that converts the C expressions in trace-events to Rust. In particular, types need to be converted, and PRI macros expanded. As of this commit no backend generates Rust code, but it is already possible to use tracetool to generate Rust sources; they are not functional but they compile and contain tracepoint functions. [Move Rust argument conversion from Event to Arguments; string support. - Paolo] Signed-off-by: Tanish Desai Signed-off-by: Paolo Bonzini Message-ID: <20250929154938.594389-9-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/__init__.py | 155 +++++++++++++++++++++++++++++++++ scripts/tracetool/format/rs.py | 64 ++++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 scripts/tracetool/format/rs.py diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index c4fa0f74e6fc0..74062d21a7ced 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -30,6 +30,49 @@ def error(*lines): error_write(*lines) sys.exit(1) +FMT_TOKEN = re.compile(r'''(?: + " ( (?: [^"\\] | \\[\\"abfnrt] | # a string literal + \\x[0-9a-fA-F][0-9a-fA-F]) *? ) " + | ( PRI [duixX] (?:8|16|32|64|PTR|MAX) ) # a PRIxxx macro + | \s+ # spaces (ignored) + )''', re.X) + +PRI_SIZE_MAP = { + '8': 'hh', + '16': 'h', + '32': '', + '64': 'll', + 'PTR': 't', + 'MAX': 'j', +} + +def expand_format_string(c_fmt, prefix=""): + def pri_macro_to_fmt(pri_macro): + assert pri_macro.startswith("PRI") + fmt_type = pri_macro[3] # 'd', 'i', 'u', or 'x' + fmt_size = pri_macro[4:] # '8', '16', '32', '64', 'PTR', 'MAX' + + size = PRI_SIZE_MAP.get(fmt_size, None) + if size is None: + raise Exception(f"unknown macro {pri_macro}") + return size + fmt_type + + result = prefix + pos = 0 + while pos < len(c_fmt): + m = FMT_TOKEN.match(c_fmt, pos) + if not m: + print("No match at position", pos, ":", repr(c_fmt[pos:]), file=sys.stderr) + raise Exception("syntax error in trace file") + if m[1]: + substr = m[1] + elif m[2]: + substr = pri_macro_to_fmt(m[2]) + else: + substr = "" + result += substr + pos = m.end() + return result out_lineno = 1 out_filename = '' @@ -89,6 +132,49 @@ def out(*lines, **kwargs): "ptrdiff_t", ] +C_TYPE_KEYWORDS = {"char", "int", "void", "short", "long", "signed", "unsigned"} + +C_TO_RUST_TYPE_MAP = { + "int": "std::ffi::c_int", + "long": "std::ffi::c_long", + "long long": "std::ffi::c_longlong", + "short": "std::ffi::c_short", + "char": "std::ffi::c_char", + "bool": "bool", + "unsigned": "std::ffi::c_uint", + # multiple keywords, keep them sorted + "long unsigned": "std::ffi::c_long", + "long long unsigned": "std::ffi::c_ulonglong", + "short unsigned": "std::ffi::c_ushort", + "char unsigned": "u8", + "int8_t": "i8", + "uint8_t": "u8", + "int16_t": "i16", + "uint16_t": "u16", + "int32_t": "i32", + "uint32_t": "u32", + "int64_t": "i64", + "uint64_t": "u64", + "void": "()", + "size_t": "usize", + "ssize_t": "isize", + "uintptr_t": "usize", + "ptrdiff_t": "isize", +} + +# Rust requires manual casting of <32-bit types when passing them to +# variable-argument functions. +RUST_VARARGS_SMALL_TYPES = { + "std::ffi::c_short", + "std::ffi::c_ushort", + "std::ffi::c_char", + "i8", + "u8", + "i16", + "u16", + "bool", +} + def validate_type(name): bits = name.split(" ") for bit in bits: @@ -104,6 +190,38 @@ def validate_type(name): "other complex pointer types should be " "declared as 'void *'" % name) +def c_type_to_rust(name): + ptr = False + const = False + name = name.rstrip() + if name[-1] == '*': + name = name[:-1].rstrip() + ptr = True + if name[-1] == '*': + # pointers to pointers are the same as void* + name = "void" + + bits = name.split() + if "const" in bits: + const = True + bits.remove("const") + if bits[0] in C_TYPE_KEYWORDS: + if "signed" in bits: + bits.remove("signed") + if len(bits) > 1 and "int" in bits: + bits.remove("int") + bits.sort() + name = ' '.join(bits) + else: + if len(bits) > 1: + raise ValueError("Invalid type '%s'." % name) + name = bits[0] + + ty = C_TO_RUST_TYPE_MAP[name.strip()] + if ptr: + ty = f'*{"const" if const else "mut"} {ty}' + return ty + class Arguments: """Event arguments description.""" @@ -192,6 +310,43 @@ def casted(self): """List of argument names casted to their type.""" return ["(%s)%s" % (type_, name) for type_, name in self._args] + def rust_decl_extern(self): + """Return a Rust argument list for an extern "C" function""" + return ", ".join((f"_{name}: {c_type_to_rust(type_)}" + for type_, name in self._args)) + + def rust_decl(self): + """Return a Rust argument list for a tracepoint function""" + def decl_type(type_): + if type_ == "const char *": + return "&std::ffi::CStr" + return c_type_to_rust(type_) + + return ", ".join((f"_{name}: {decl_type(type_)}" + for type_, name in self._args)) + + def rust_call_extern(self): + """Return a Rust argument list for a call to an extern "C" function""" + def rust_cast(name, type_): + if type_ == "const char *": + return f"_{name}.as_ptr()" + return f"_{name}" + + return ", ".join((rust_cast(name, type_) for type_, name in self._args)) + + def rust_call_varargs(self): + """Return a Rust argument list for a call to a C varargs function""" + def rust_cast(name, type_): + if type_ == "const char *": + return f"_{name}.as_ptr()" + + type_ = c_type_to_rust(type_) + if type_ in RUST_VARARGS_SMALL_TYPES: + return f"_{name} as std::ffi::c_int" + return f"_{name} /* as {type_} */" + + return ", ".join((rust_cast(name, type_) for type_, name in self._args)) + class Event(object): """Event description. diff --git a/scripts/tracetool/format/rs.py b/scripts/tracetool/format/rs.py new file mode 100644 index 0000000000000..32ac4e5977048 --- /dev/null +++ b/scripts/tracetool/format/rs.py @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +""" +trace-DIR.rs +""" + +__author__ = "Tanish Desai " +__copyright__ = "Copyright 2025, Tanish Desai " +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@redhat.com" + + +from tracetool import out + + +def generate(events, backend, group): + out('// SPDX-License-Identifier: GPL-2.0-or-later', + '// This file is @generated by tracetool, do not edit.', + '', + '#[allow(unused_imports)]', + 'use std::ffi::c_char;', + '#[allow(unused_imports)]', + 'use util::bindings;', + '', + '#[inline(always)]', + 'fn trace_event_state_is_enabled(dstate: u16) -> bool {', + ' (unsafe { trace_events_enabled_count }) != 0 && dstate != 0', + '}', + '', + 'extern "C" {', + ' static mut trace_events_enabled_count: u32;', + '}',) + + out('extern "C" {') + + for e in events: + out(' static mut %s: u16;' % e.api(e.QEMU_DSTATE)) + out('}') + + backend.generate_begin(events, group) + + for e in events: + out('', + '#[inline(always)]', + '#[allow(dead_code)]', + 'pub fn %(api)s(%(args)s)', + '{', + api=e.api(e.QEMU_TRACE), + args=e.args.rust_decl()) + + if "disable" not in e.properties: + backend.generate(e, group, check_trace_event_get_state=False) + if backend.check_trace_event_get_state: + event_id = 'TRACE_' + e.name.upper() + out(' if trace_event_state_is_enabled(unsafe { _%(event_id)s_DSTATE}) {', + event_id = event_id, + api=e.api()) + backend.generate(e, group, check_trace_event_get_state=True) + out(' }') + out('}') + + backend.generate_end(events, group) From 54140102d2e57bd2e34823e4537d2191cdcd5f08 Mon Sep 17 00:00:00 2001 From: Tanish Desai Date: Mon, 29 Sep 2025 17:49:31 +0200 Subject: [PATCH 1089/1794] rust: add trace crate The trace crate is a minimal container for dependencies of tracepoints (so that they do not have to be imported in all the crates that use tracepoints); it also contains a macro called "include_trace!" that is able to find the right include file from the trace/ directory. [Write commit message, add #[allow()]. - Paolo] Signed-off-by: Tanish Desai Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini Message-ID: <20250929154938.594389-10-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- rust/Cargo.lock | 4 ++++ rust/Cargo.toml | 1 + rust/meson.build | 2 +- rust/trace/Cargo.toml | 16 ++++++++++++++++ rust/trace/meson.build | 19 +++++++++++++++++++ rust/trace/src/lib.rs | 35 +++++++++++++++++++++++++++++++++++ trace/meson.build | 8 +++++++- 7 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 rust/trace/Cargo.toml create mode 100644 rust/trace/meson.build create mode 100644 rust/trace/src/lib.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 8315f98c46f6b..3428dbaf0b35c 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -258,6 +258,10 @@ dependencies = [ "util", ] +[[package]] +name = "trace" +version = "0.1.0" + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index d8183c614d491..f372d7dbf7022 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -11,6 +11,7 @@ members = [ "hw/core", "hw/char/pl011", "hw/timer/hpet", + "trace", "util", "tests", ] diff --git a/rust/meson.build b/rust/meson.build index b3ac3a7197045..695d5a62de901 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -34,7 +34,7 @@ subdir('system') subdir('chardev') subdir('hw/core') subdir('tests') - +subdir('trace') subdir('hw') cargo = find_program('cargo', required: false) diff --git a/rust/trace/Cargo.toml b/rust/trace/Cargo.toml new file mode 100644 index 0000000000000..13ac0b33d6fbe --- /dev/null +++ b/rust/trace/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "trace" +version = "0.1.0" +authors = ["Tanish Desai "] +description = "QEMU tracing infrastructure support" +resolver = "2" +publish = false + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lints] +workspace = true diff --git a/rust/trace/meson.build b/rust/trace/meson.build new file mode 100644 index 0000000000000..adca57e550753 --- /dev/null +++ b/rust/trace/meson.build @@ -0,0 +1,19 @@ +rust = import('rust') + +lib_rs = configure_file( + input: 'src/lib.rs', + output: 'lib.rs', + configuration: { + 'MESON_BUILD_ROOT': meson.project_build_root(), + }) + +_trace_rs = static_library( + 'trace', # Library name, + lib_rs, + trace_rs_targets, # List of generated `.rs` custom targets + override_options: ['rust_std=2021', 'build.rust_std=2021'], + dependencies: [libc_rs], + rust_abi: 'rust', +) + +trace_rs = declare_dependency(link_with: _trace_rs) diff --git a/rust/trace/src/lib.rs b/rust/trace/src/lib.rs new file mode 100644 index 0000000000000..0955461573df5 --- /dev/null +++ b/rust/trace/src/lib.rs @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +//! This crate provides macros that aid in using QEMU's tracepoint +//! functionality. + +#[macro_export] +/// Define the trace-points from the named directory (which should have slashes +/// replaced by underscore characters) as functions in a module called `trace`. +/// +/// ```ignore +/// ::trace::include_trace!("hw_char"); +/// // ... +/// trace::trace_pl011_read_fifo_rx_full(); +/// ``` +macro_rules! include_trace { + ($name:literal) => { + #[allow( + clippy::ptr_as_ptr, + clippy::cast_lossless, + clippy::used_underscore_binding + )] + mod trace { + #[cfg(not(MESON))] + include!(concat!( + env!("MESON_BUILD_ROOT"), + "/trace/trace-", + $name, + ".rs" + )); + + #[cfg(MESON)] + include!(concat!("@MESON_BUILD_ROOT@/trace/trace-", $name, ".rs")); + } + }; +} diff --git a/trace/meson.build b/trace/meson.build index 9c42a57a05337..d89a0db82a143 100644 --- a/trace/meson.build +++ b/trace/meson.build @@ -1,5 +1,5 @@ system_ss.add(files('control-target.c', 'trace-hmp-cmds.c')) - +trace_rs_targets = [] trace_events_files = [] foreach item : [ '.' ] + trace_events_subdirs + qapi_trace_events if item in qapi_trace_events @@ -24,6 +24,11 @@ foreach item : [ '.' ] + trace_events_subdirs + qapi_trace_events input: trace_events_file, command: [ tracetool, group, '--format=c', '@INPUT@', '@OUTPUT@' ], depend_files: tracetool_depends) + trace_rs = custom_target(fmt.format('trace', 'rs'), + output: fmt.format('trace', 'rs'), + input: trace_events_file, + command: [ tracetool, group, '--format=rs', '@INPUT@', '@OUTPUT@' ], + depend_files: tracetool_depends) if 'ust' in get_option('trace_backends') trace_ust_h = custom_target(fmt.format('trace-ust', 'h'), output: fmt.format('trace-ust', 'h'), @@ -34,6 +39,7 @@ foreach item : [ '.' ] + trace_events_subdirs + qapi_trace_events genh += trace_ust_h endif trace_ss.add(trace_h, trace_c) + trace_rs_targets += trace_rs if 'dtrace' in get_option('trace_backends') trace_dtrace = custom_target(fmt.format('trace-dtrace', 'dtrace'), output: fmt.format('trace-dtrace', 'dtrace'), From fe791b4004cbdc64bee6cb69ba4a98513879945d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 29 Sep 2025 17:49:32 +0200 Subject: [PATCH 1090/1794] rust: qdev: add minimal clock bindings Add the minimal support that is needed by pl011's event and tracepoint. Reviewed-by: Zhao Liu Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini Message-ID: <20250929154938.594389-11-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- rust/hw/core/src/qdev.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs index a4493dbf0119e..c3097a284d738 100644 --- a/rust/hw/core/src/qdev.rs +++ b/rust/hw/core/src/qdev.rs @@ -411,6 +411,39 @@ where impl DeviceMethods for R where R::Target: IsA {} +impl Clock { + pub const PERIOD_1SEC: u64 = bindings::CLOCK_PERIOD_1SEC; + + pub const fn period_from_ns(ns: u64) -> u64 { + ns * Self::PERIOD_1SEC / 1_000_000_000 + } + + pub const fn period_from_hz(hz: u64) -> u64 { + if hz == 0 { + 0 + } else { + Self::PERIOD_1SEC / hz + } + } + + pub const fn period_to_hz(period: u64) -> u64 { + if period == 0 { + 0 + } else { + Self::PERIOD_1SEC / period + } + } + + pub const fn period(&self) -> u64 { + // SAFETY: Clock is returned by init_clock_in with zero value for period + unsafe { &*self.0.as_ptr() }.period + } + + pub const fn hz(&self) -> u64 { + Self::period_to_hz(self.period()) + } +} + unsafe impl ObjectType for Clock { type Class = ObjectClass; const TYPE_NAME: &'static CStr = From 357f92697971b5a52bb38248405c40afcc355a1d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 29 Sep 2025 17:49:33 +0200 Subject: [PATCH 1091/1794] rust: pl011: add tracepoints Finally bring parity between C and Rust versions of the PL011 device model. Changing some types of the arguments makes for nicer Rust code; C does not care. :) Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini Message-ID: <20250929154938.594389-12-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- hw/char/trace-events | 14 ++++---- rust/Cargo.lock | 1 + rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 57 ++++++++++++++++++++++---------- 5 files changed, 50 insertions(+), 24 deletions(-) diff --git a/hw/char/trace-events b/hw/char/trace-events index 05a33036c1207..9e74be2c14f5d 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -58,15 +58,15 @@ imx_serial_write(const char *chrname, uint64_t addr, uint64_t value) "%s:[0x%03" imx_serial_put_data(const char *chrname, uint32_t value) "%s: 0x%" PRIx32 # pl011.c -pl011_irq_state(int level) "irq state %d" -pl011_read(uint32_t addr, uint32_t value, const char *regname) "addr 0x%03x value 0x%08x reg %s" -pl011_read_fifo(unsigned rx_fifo_used, size_t rx_fifo_depth) "RX FIFO read, used %u/%zu" -pl011_write(uint32_t addr, uint32_t value, const char *regname) "addr 0x%03x value 0x%08x reg %s" -pl011_can_receive(uint32_t lcr, unsigned rx_fifo_used, size_t rx_fifo_depth, unsigned rx_fifo_available) "LCR 0x%02x, RX FIFO used %u/%zu, can_receive %u chars" -pl011_fifo_rx_put(uint32_t c, unsigned read_count, size_t rx_fifo_depth) "RX FIFO push char [0x%02x] %d/%zu depth used" +pl011_irq_state(bool level) "irq state %d" +pl011_read(uint64_t addr, uint32_t value, const char *regname) "addr 0x%03" PRIx64 " value 0x%08x reg %s" +pl011_read_fifo(unsigned rx_fifo_used, unsigned rx_fifo_depth) "RX FIFO read, used %u/%u" +pl011_write(uint64_t addr, uint32_t value, const char *regname) "addr 0x%03" PRIx64 " value 0x%08x reg %s" +pl011_can_receive(uint32_t lcr, unsigned rx_fifo_used, unsigned rx_fifo_depth, unsigned rx_fifo_available) "LCR 0x%02x, RX FIFO used %u/%u, can_receive %u chars" +pl011_fifo_rx_put(uint32_t c, unsigned read_count, unsigned rx_fifo_depth) "RX FIFO push char [0x%02x] %d/%u depth used" pl011_fifo_rx_full(void) "RX FIFO now full, RXFF set" pl011_baudrate_change(unsigned int baudrate, uint64_t clock, uint32_t ibrd, uint32_t fbrd) "new baudrate %u (clk: %" PRIu64 "hz, ibrd: %" PRIu32 ", fbrd: %" PRIu32 ")" -pl011_receive(int size) "recv %d chars" +pl011_receive(size_t size) "recv %zd chars" # cmsdk-apb-uart.c cmsdk_apb_uart_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB UART read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 3428dbaf0b35c..f84a3dd07648d 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -159,6 +159,7 @@ dependencies = [ "migration", "qom", "system", + "trace", "util", ] diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index b2418abc4bfe9..dc41d0e499e16 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -24,6 +24,7 @@ qom = { path = "../../../qom" } chardev = { path = "../../../chardev" } system = { path = "../../../system" } hwcore = { path = "../../../hw/core" } +trace = { path = "../../../trace" } [lints] workspace = true diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index a33f32906e912..07b3da17e8319 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -40,6 +40,7 @@ _libpl011_rs = static_library( chardev_rs, system_rs, hwcore_rs, + trace_rs ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 1b4587d5f6071..8889d6e54fbd0 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -21,6 +21,8 @@ use util::{log::Log, log_mask_ln}; use crate::registers::{self, Interrupt, RegisterOffset}; +::trace::include_trace!("hw_char"); + // TODO: You must disable the UART before any of the control registers are // reprogrammed. When the UART is disabled in the middle of transmission or // reception, it completes the current character before stopping @@ -208,13 +210,7 @@ impl PL011Registers { (update, result) } - pub(self) fn write( - &mut self, - offset: RegisterOffset, - value: u32, - char_backend: &CharBackend, - ) -> bool { - // eprintln!("write offset {offset} value {value}"); + pub(self) fn write(&mut self, offset: RegisterOffset, value: u32, device: &PL011State) -> bool { use RegisterOffset::*; match offset { DR => return self.write_data_register(value), @@ -229,9 +225,11 @@ impl PL011Registers { } IBRD => { self.ibrd = value; + device.trace_baudrate_change(self.ibrd, self.fbrd); } FBRD => { self.fbrd = value; + device.trace_baudrate_change(self.ibrd, self.fbrd); } LCR_H => { let new_val: registers::LineControl = value.into(); @@ -242,7 +240,7 @@ impl PL011Registers { } let update = (self.line_control.send_break() != new_val.send_break()) && { let break_enable = new_val.send_break(); - let _ = char_backend.send_break(break_enable); + let _ = device.char_backend.send_break(break_enable); self.loopback_break(break_enable) }; self.line_control = new_val; @@ -279,12 +277,13 @@ impl PL011Registers { } fn read_data_register(&mut self, update: &mut bool) -> u32 { + let depth = self.fifo_depth(); self.flags.set_receive_fifo_full(false); let c = self.read_fifo[self.read_pos]; if self.read_count > 0 { self.read_count -= 1; - self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1); + self.read_pos = (self.read_pos + 1) & (depth - 1); } if self.read_count == 0 { self.flags.set_receive_fifo_empty(true); @@ -292,6 +291,7 @@ impl PL011Registers { if self.read_count + 1 == self.read_trigger { self.int_level &= !Interrupt::RX; } + trace::trace_pl011_read_fifo(self.read_count, depth); self.receive_status_error_clear.set_from_data(c); *update = true; u32::from(c) @@ -447,7 +447,9 @@ impl PL011Registers { self.read_fifo[slot] = value; self.read_count += 1; self.flags.set_receive_fifo_empty(false); + trace::trace_pl011_fifo_rx_put(value.into(), self.read_count, depth); if self.read_count == depth { + trace::trace_pl011_fifo_rx_full(); self.flags.set_receive_fifo_full(true); } @@ -516,8 +518,21 @@ impl PL011State { uninit_field_mut!(*this, clock).write(clock); } - const fn clock_update(&self, _event: ClockEvent) { - /* pl011_trace_baudrate_change(s); */ + pub fn trace_baudrate_change(&self, ibrd: u32, fbrd: u32) { + let divider = 4.0 / f64::from(ibrd * (FBRD_MASK + 1) + fbrd); + let hz = self.clock.hz(); + let rate = if ibrd == 0 { + 0 + } else { + ((hz as f64) * divider) as u32 + }; + trace::trace_pl011_baudrate_change(rate, hz, ibrd, fbrd); + } + + fn clock_update(&self, _event: ClockEvent) { + let regs = self.regs.borrow(); + let (ibrd, fbrd) = (regs.ibrd, regs.fbrd); + self.trace_baudrate_change(ibrd, fbrd) } pub fn clock_needed(&self) -> bool { @@ -543,6 +558,7 @@ impl PL011State { } Ok(field) => { let (update_irq, result) = self.regs.borrow_mut().read(field); + trace::trace_pl011_read(offset, result, c""); if update_irq { self.update(); self.char_backend.accept_input(); @@ -557,6 +573,7 @@ impl PL011State { if let Ok(field) = RegisterOffset::try_from(offset) { // qemu_chr_fe_write_all() calls into the can_receive // callback, so handle writes before entering PL011Registers. + trace::trace_pl011_write(offset, value as u32, c""); if field == RegisterOffset::DR { // ??? Check if transmitter is enabled. let ch: [u8; 1] = [value as u8]; @@ -565,10 +582,7 @@ impl PL011State { let _ = self.char_backend.write_all(&ch); } - update_irq = self - .regs - .borrow_mut() - .write(field, value as u32, &self.char_backend); + update_irq = self.regs.borrow_mut().write(field, value as u32, self); } else { log_mask_ln!( Log::GuestError, @@ -582,11 +596,19 @@ impl PL011State { fn can_receive(&self) -> u32 { let regs = self.regs.borrow(); - // trace_pl011_can_receive(s->lcr, s->read_count, r); - regs.fifo_depth() - regs.read_count + let fifo_available = regs.fifo_depth() - regs.read_count; + trace::trace_pl011_can_receive( + regs.line_control.into(), + regs.read_count, + regs.fifo_depth(), + fifo_available, + ); + fifo_available } fn receive(&self, buf: &[u8]) { + trace::trace_pl011_receive(buf.len()); + let mut regs = self.regs.borrow_mut(); if regs.loopback_enabled() { // In loopback mode, the RX input signal is internally disconnected @@ -635,6 +657,7 @@ impl PL011State { fn update(&self) { let regs = self.regs.borrow(); let flags = regs.int_level & regs.int_enabled; + trace::trace_pl011_irq_state(flags != 0); for (irq, i) in self.interrupts.iter().zip(IRQMASK) { irq.set(flags.any_set(i)); } From 42f73f264a9fc0b1d38d84e8e716e69e929d6697 Mon Sep 17 00:00:00 2001 From: Tanish Desai Date: Mon, 29 Sep 2025 17:49:34 +0200 Subject: [PATCH 1092/1794] tracetool/simple: add Rust support Signed-off-by: Tanish Desai Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini Message-ID: <20250929154938.594389-13-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/backend/simple.py | 7 +++++ tests/tracetool/simple.rs | 40 +++++++++++++++++++++++++++++ tests/tracetool/tracetool-test.py | 2 ++ 3 files changed, 49 insertions(+) create mode 100644 tests/tracetool/simple.rs diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py index b5a6b7205a316..b131e4fc19422 100644 --- a/scripts/tracetool/backend/simple.py +++ b/scripts/tracetool/backend/simple.py @@ -98,3 +98,10 @@ def generate_c(event, group): out(' trace_record_finish(&rec);', '}', '') + +def generate_rs(event, group): + out(' extern "C" { fn _simple_%(api)s(%(rust_args)s); }', + ' unsafe { _simple_%(api)s(%(args)s); }', + api=event.api(), + rust_args=event.args.rust_decl_extern(), + args=event.args.rust_call_extern()) diff --git a/tests/tracetool/simple.rs b/tests/tracetool/simple.rs new file mode 100644 index 0000000000000..9ee39495e387d --- /dev/null +++ b/tests/tracetool/simple.rs @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// This file is @generated by tracetool, do not edit. + +#[allow(unused_imports)] +use std::ffi::c_char; +#[allow(unused_imports)] +use util::bindings; + +#[inline(always)] +fn trace_event_state_is_enabled(dstate: u16) -> bool { + (unsafe { trace_events_enabled_count }) != 0 && dstate != 0 +} + +extern "C" { + static mut trace_events_enabled_count: u32; +} +extern "C" { + static mut _TRACE_TEST_BLAH_DSTATE: u16; + static mut _TRACE_TEST_WIBBLE_DSTATE: u16; +} + +#[inline(always)] +#[allow(dead_code)] +pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr) +{ + if trace_event_state_is_enabled(unsafe { _TRACE_TEST_BLAH_DSTATE}) { + extern "C" { fn _simple_trace_test_blah(_context: *mut (), _filename: *const std::ffi::c_char); } + unsafe { _simple_trace_test_blah(_context, _filename.as_ptr()); } + } +} + +#[inline(always)] +#[allow(dead_code)] +pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int) +{ + if trace_event_state_is_enabled(unsafe { _TRACE_TEST_WIBBLE_DSTATE}) { + extern "C" { fn _simple_trace_test_wibble(_context: *mut (), _value: std::ffi::c_int); } + unsafe { _simple_trace_test_wibble(_context, _value); } + } +} diff --git a/tests/tracetool/tracetool-test.py b/tests/tracetool/tracetool-test.py index 65430fdedc1d9..3e37890476d0a 100755 --- a/tests/tracetool/tracetool-test.py +++ b/tests/tracetool/tracetool-test.py @@ -14,6 +14,8 @@ def get_formats(backend): "c", "h", ] + if backend in {"simple"}: + formats += ["rs"] if backend == "dtrace": formats += [ "d", From 75871d88d3ddf310dd5ffb1d36c8476959692726 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 29 Sep 2025 17:49:35 +0200 Subject: [PATCH 1093/1794] log: change qemu_loglevel to unsigned MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bindgen makes the LOG_* constants unsigned, even if they are defined as (1 << 15): pub const LOG_TRACE: u32 = 32768; Make them unsigned in C as well through the BIT() macro, and also change the type of the variable that they are used with. Reviewed-by: Manos Pitsidianakis Reviewed-by: Zhao Liu Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini Message-ID: <20250929154938.594389-14-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- include/qemu/log-for-trace.h | 4 ++-- include/qemu/log.h | 44 ++++++++++++++++++------------------ rust/util/src/log.rs | 2 +- util/log.c | 2 +- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/include/qemu/log-for-trace.h b/include/qemu/log-for-trace.h index d47c9cd4462b7..f3a8791f1d4ec 100644 --- a/include/qemu/log-for-trace.h +++ b/include/qemu/log-for-trace.h @@ -19,9 +19,9 @@ #define QEMU_LOG_FOR_TRACE_H /* Private global variable, don't use */ -extern int qemu_loglevel; +extern unsigned qemu_loglevel; -#define LOG_TRACE (1 << 15) +#define LOG_TRACE (1u << 15) /* Returns true if a bit is set in the current loglevel mask */ static inline bool qemu_loglevel_mask(int mask) diff --git a/include/qemu/log.h b/include/qemu/log.h index aae72985f0dfc..7effba4da4cac 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -14,30 +14,30 @@ bool qemu_log_enabled(void); /* Returns true if qemu_log() will write somewhere other than stderr. */ bool qemu_log_separate(void); -#define CPU_LOG_TB_OUT_ASM (1 << 0) -#define CPU_LOG_TB_IN_ASM (1 << 1) -#define CPU_LOG_TB_OP (1 << 2) -#define CPU_LOG_TB_OP_OPT (1 << 3) -#define CPU_LOG_INT (1 << 4) -#define CPU_LOG_EXEC (1 << 5) -#define CPU_LOG_PCALL (1 << 6) -#define CPU_LOG_TB_CPU (1 << 8) -#define CPU_LOG_RESET (1 << 9) -#define LOG_UNIMP (1 << 10) -#define LOG_GUEST_ERROR (1 << 11) -#define CPU_LOG_MMU (1 << 12) -#define CPU_LOG_TB_NOCHAIN (1 << 13) -#define CPU_LOG_PAGE (1 << 14) +#define CPU_LOG_TB_OUT_ASM (1u << 0) +#define CPU_LOG_TB_IN_ASM (1u << 1) +#define CPU_LOG_TB_OP (1u << 2) +#define CPU_LOG_TB_OP_OPT (1u << 3) +#define CPU_LOG_INT (1u << 4) +#define CPU_LOG_EXEC (1u << 5) +#define CPU_LOG_PCALL (1u << 6) +#define CPU_LOG_TB_CPU (1u << 8) +#define CPU_LOG_RESET (1u << 9) +#define LOG_UNIMP (1u << 10) +#define LOG_GUEST_ERROR (1u << 11) +#define CPU_LOG_MMU (1u << 12) +#define CPU_LOG_TB_NOCHAIN (1u << 13) +#define CPU_LOG_PAGE (1u << 14) /* LOG_TRACE (1 << 15) is defined in log-for-trace.h */ -#define CPU_LOG_TB_OP_IND (1 << 16) -#define CPU_LOG_TB_FPU (1 << 17) -#define CPU_LOG_PLUGIN (1 << 18) +#define CPU_LOG_TB_OP_IND (1u << 16) +#define CPU_LOG_TB_FPU (1u << 17) +#define CPU_LOG_PLUGIN (1u << 18) /* LOG_STRACE is used for user-mode strace logging. */ -#define LOG_STRACE (1 << 19) -#define LOG_PER_THREAD (1 << 20) -#define CPU_LOG_TB_VPU (1 << 21) -#define LOG_TB_OP_PLUGIN (1 << 22) -#define LOG_INVALID_MEM (1 << 23) +#define LOG_STRACE (1u << 19) +#define LOG_PER_THREAD (1u << 20) +#define CPU_LOG_TB_VPU (1u << 21) +#define LOG_TB_OP_PLUGIN (1u << 22) +#define LOG_INVALID_MEM (1u << 23) /* Lock/unlock output. */ diff --git a/rust/util/src/log.rs b/rust/util/src/log.rs index af9a3e912348b..0a4bc4249a174 100644 --- a/rust/util/src/log.rs +++ b/rust/util/src/log.rs @@ -142,7 +142,7 @@ macro_rules! log_mask_ln { let _: $crate::log::Log = $mask; if unsafe { - ($crate::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0 + ($crate::bindings::qemu_loglevel & ($mask as std::os::raw::c_uint)) != 0 } { _ = $crate::log::LogGuard::log_fmt( format_args!("{}\n", format_args!($fmt $($args)*))); diff --git a/util/log.c b/util/log.c index abdcb6b31111f..41f78ce86b252 100644 --- a/util/log.c +++ b/util/log.c @@ -44,7 +44,7 @@ static FILE *global_file; static __thread FILE *thread_file; static __thread Notifier qemu_log_thread_cleanup_notifier; -int qemu_loglevel; +unsigned qemu_loglevel; static bool log_per_thread; static GArray *debug_regions; From c4e8d44bac5cecebba9bb7293b4256bd57e9f52e Mon Sep 17 00:00:00 2001 From: Tanish Desai Date: Mon, 29 Sep 2025 17:49:36 +0200 Subject: [PATCH 1094/1794] tracetool/log: add Rust support Signed-off-by: Tanish Desai Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini Message-ID: <20250929154938.594389-15-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/backend/log.py | 10 ++++++- tests/tracetool/log.rs | 44 +++++++++++++++++++++++++++++++ tests/tracetool/tracetool-test.py | 2 +- 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 tests/tracetool/log.rs diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index d01d234289f18..9e3e5046f5f66 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -12,7 +12,7 @@ __email__ = "stefanha@redhat.com" -from tracetool import out +from tracetool import out, expand_format_string PUBLIC = True @@ -44,3 +44,11 @@ def generate_h(event, group): def generate_h_backend_dstate(event, group): out(' trace_event_get_state_dynamic_by_id(%(event_id)s) || \\', event_id="TRACE_" + event.name.upper()) + +def generate_rs(event, group): + out(' let format_string = c"%(fmt)s\\n";', + ' if (unsafe { bindings::qemu_loglevel } & bindings::LOG_TRACE) != 0 {', + ' unsafe { bindings::qemu_log(format_string.as_ptr() as *const c_char, %(args)s);}', + ' }', + fmt=expand_format_string(event.fmt, event.name + " "), + args=event.args.rust_call_varargs()) diff --git a/tests/tracetool/log.rs b/tests/tracetool/log.rs new file mode 100644 index 0000000000000..c191895c8f87e --- /dev/null +++ b/tests/tracetool/log.rs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// This file is @generated by tracetool, do not edit. + +#[allow(unused_imports)] +use std::ffi::c_char; +#[allow(unused_imports)] +use util::bindings; + +#[inline(always)] +fn trace_event_state_is_enabled(dstate: u16) -> bool { + (unsafe { trace_events_enabled_count }) != 0 && dstate != 0 +} + +extern "C" { + static mut trace_events_enabled_count: u32; +} +extern "C" { + static mut _TRACE_TEST_BLAH_DSTATE: u16; + static mut _TRACE_TEST_WIBBLE_DSTATE: u16; +} + +#[inline(always)] +#[allow(dead_code)] +pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr) +{ + if trace_event_state_is_enabled(unsafe { _TRACE_TEST_BLAH_DSTATE}) { + let format_string = c"test_blah Blah context=%p filename=%s\n"; + if (unsafe { bindings::qemu_loglevel } & bindings::LOG_TRACE) != 0 { + unsafe { bindings::qemu_log(format_string.as_ptr() as *const c_char, _context /* as *mut () */, _filename.as_ptr());} + } + } +} + +#[inline(always)] +#[allow(dead_code)] +pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int) +{ + if trace_event_state_is_enabled(unsafe { _TRACE_TEST_WIBBLE_DSTATE}) { + let format_string = c"test_wibble Wibble context=%p value=%d\n"; + if (unsafe { bindings::qemu_loglevel } & bindings::LOG_TRACE) != 0 { + unsafe { bindings::qemu_log(format_string.as_ptr() as *const c_char, _context /* as *mut () */, _value /* as std::ffi::c_int */);} + } + } +} diff --git a/tests/tracetool/tracetool-test.py b/tests/tracetool/tracetool-test.py index 3e37890476d0a..f58f3b795e789 100755 --- a/tests/tracetool/tracetool-test.py +++ b/tests/tracetool/tracetool-test.py @@ -14,7 +14,7 @@ def get_formats(backend): "c", "h", ] - if backend in {"simple"}: + if backend in {"log", "simple"}: formats += ["rs"] if backend == "dtrace": formats += [ From 7dbee1274266b802456c7f07dc10184af2dc1ec0 Mon Sep 17 00:00:00 2001 From: Tanish Desai Date: Mon, 29 Sep 2025 17:49:37 +0200 Subject: [PATCH 1095/1794] tracetool/ftrace: add Rust support Signed-off-by: Tanish Desai Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini Message-ID: <20250929154938.594389-16-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/backend/ftrace.py | 8 +++++- tests/tracetool/ftrace.rs | 40 +++++++++++++++++++++++++++++ tests/tracetool/tracetool-test.py | 2 +- 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 tests/tracetool/ftrace.rs diff --git a/scripts/tracetool/backend/ftrace.py b/scripts/tracetool/backend/ftrace.py index 7ddd5d11a66a9..e03698a2edff8 100644 --- a/scripts/tracetool/backend/ftrace.py +++ b/scripts/tracetool/backend/ftrace.py @@ -12,7 +12,7 @@ __email__ = "stefanha@redhat.com" -from tracetool import out +from tracetool import out, expand_format_string PUBLIC = True @@ -43,3 +43,9 @@ def generate_h(event, group): def generate_h_backend_dstate(event, group): out(' trace_event_get_state_dynamic_by_id(%(event_id)s) || \\', event_id="TRACE_" + event.name.upper()) + +def generate_rs(event, group): + out(' let format_string = c"%(fmt)s";', + ' unsafe {bindings::ftrace_write(format_string.as_ptr() as *const c_char, %(args)s);}', + fmt=expand_format_string(event.fmt), + args=event.args.rust_call_varargs()) diff --git a/tests/tracetool/ftrace.rs b/tests/tracetool/ftrace.rs new file mode 100644 index 0000000000000..07b9259cf29f3 --- /dev/null +++ b/tests/tracetool/ftrace.rs @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// This file is @generated by tracetool, do not edit. + +#[allow(unused_imports)] +use std::ffi::c_char; +#[allow(unused_imports)] +use util::bindings; + +#[inline(always)] +fn trace_event_state_is_enabled(dstate: u16) -> bool { + (unsafe { trace_events_enabled_count }) != 0 && dstate != 0 +} + +extern "C" { + static mut trace_events_enabled_count: u32; +} +extern "C" { + static mut _TRACE_TEST_BLAH_DSTATE: u16; + static mut _TRACE_TEST_WIBBLE_DSTATE: u16; +} + +#[inline(always)] +#[allow(dead_code)] +pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr) +{ + if trace_event_state_is_enabled(unsafe { _TRACE_TEST_BLAH_DSTATE}) { + let format_string = c"Blah context=%p filename=%s"; + unsafe {bindings::ftrace_write(format_string.as_ptr() as *const c_char, _context /* as *mut () */, _filename.as_ptr());} + } +} + +#[inline(always)] +#[allow(dead_code)] +pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int) +{ + if trace_event_state_is_enabled(unsafe { _TRACE_TEST_WIBBLE_DSTATE}) { + let format_string = c"Wibble context=%p value=%d"; + unsafe {bindings::ftrace_write(format_string.as_ptr() as *const c_char, _context /* as *mut () */, _value /* as std::ffi::c_int */);} + } +} diff --git a/tests/tracetool/tracetool-test.py b/tests/tracetool/tracetool-test.py index f58f3b795e789..3341fb18f90c3 100755 --- a/tests/tracetool/tracetool-test.py +++ b/tests/tracetool/tracetool-test.py @@ -14,7 +14,7 @@ def get_formats(backend): "c", "h", ] - if backend in {"log", "simple"}: + if backend in {"ftrace", "log", "simple"}: formats += ["rs"] if backend == "dtrace": formats += [ From 1461752f0fa4bcd7e60d51fe47e3430f8a81cdd8 Mon Sep 17 00:00:00 2001 From: Tanish Desai Date: Mon, 29 Sep 2025 17:49:38 +0200 Subject: [PATCH 1096/1794] tracetool/syslog: add Rust support The syslog backend needs the syslog function from libc and the LOG_INFO enum value; they are re-exported as "::trace::syslog" and "::trace::LOG_INFO" so that device crates do not all have to add the libc dependency, but otherwise there is nothing special. Signed-off-by: Tanish Desai Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini Message-ID: <20250929154938.594389-17-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- rust/Cargo.lock | 3 +++ rust/trace/Cargo.toml | 3 +++ rust/trace/src/lib.rs | 4 +++ scripts/tracetool/backend/syslog.py | 7 ++++- tests/tracetool/syslog.rs | 40 +++++++++++++++++++++++++++++ tests/tracetool/tracetool-test.py | 2 +- 6 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 tests/tracetool/syslog.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index f84a3dd07648d..444ef516a70af 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -262,6 +262,9 @@ dependencies = [ [[package]] name = "trace" version = "0.1.0" +dependencies = [ + "libc", +] [[package]] name = "unicode-ident" diff --git a/rust/trace/Cargo.toml b/rust/trace/Cargo.toml index 13ac0b33d6fbe..fc81bce5803d3 100644 --- a/rust/trace/Cargo.toml +++ b/rust/trace/Cargo.toml @@ -12,5 +12,8 @@ license.workspace = true repository.workspace = true rust-version.workspace = true +[dependencies] +libc = { workspace = true } + [lints] workspace = true diff --git a/rust/trace/src/lib.rs b/rust/trace/src/lib.rs index 0955461573df5..e03bce43c4717 100644 --- a/rust/trace/src/lib.rs +++ b/rust/trace/src/lib.rs @@ -3,6 +3,10 @@ //! This crate provides macros that aid in using QEMU's tracepoint //! functionality. +#[doc(hidden)] +/// Re-exported item to avoid adding libc as a dependency everywhere. +pub use libc::{syslog, LOG_INFO}; + #[macro_export] /// Define the trace-points from the named directory (which should have slashes /// replaced by underscore characters) as functions in a module called `trace`. diff --git a/scripts/tracetool/backend/syslog.py b/scripts/tracetool/backend/syslog.py index 177414d56a685..12b826593db4b 100644 --- a/scripts/tracetool/backend/syslog.py +++ b/scripts/tracetool/backend/syslog.py @@ -12,7 +12,7 @@ __email__ = "stefanha@redhat.com" -from tracetool import out +from tracetool import out, expand_format_string PUBLIC = True @@ -38,6 +38,11 @@ def generate_h(event, group): fmt=event.fmt.rstrip("\n"), argnames=argnames) +def generate_rs(event, group): + out(' let format_string = c"%(fmt)s";', + ' unsafe {::trace::syslog(::trace::LOG_INFO, format_string.as_ptr() as *const c_char, %(args)s);}', + fmt=expand_format_string(event.fmt), + args=event.args.rust_call_varargs()) def generate_h_backend_dstate(event, group): out(' trace_event_get_state_dynamic_by_id(%(event_id)s) || \\', diff --git a/tests/tracetool/syslog.rs b/tests/tracetool/syslog.rs new file mode 100644 index 0000000000000..9d3675a0b5767 --- /dev/null +++ b/tests/tracetool/syslog.rs @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// This file is @generated by tracetool, do not edit. + +#[allow(unused_imports)] +use std::ffi::c_char; +#[allow(unused_imports)] +use util::bindings; + +#[inline(always)] +fn trace_event_state_is_enabled(dstate: u16) -> bool { + (unsafe { trace_events_enabled_count }) != 0 && dstate != 0 +} + +extern "C" { + static mut trace_events_enabled_count: u32; +} +extern "C" { + static mut _TRACE_TEST_BLAH_DSTATE: u16; + static mut _TRACE_TEST_WIBBLE_DSTATE: u16; +} + +#[inline(always)] +#[allow(dead_code)] +pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr) +{ + if trace_event_state_is_enabled(unsafe { _TRACE_TEST_BLAH_DSTATE}) { + let format_string = c"Blah context=%p filename=%s"; + unsafe {::trace::syslog(::trace::LOG_INFO, format_string.as_ptr() as *const c_char, _context /* as *mut () */, _filename.as_ptr());} + } +} + +#[inline(always)] +#[allow(dead_code)] +pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int) +{ + if trace_event_state_is_enabled(unsafe { _TRACE_TEST_WIBBLE_DSTATE}) { + let format_string = c"Wibble context=%p value=%d"; + unsafe {::trace::syslog(::trace::LOG_INFO, format_string.as_ptr() as *const c_char, _context /* as *mut () */, _value /* as std::ffi::c_int */);} + } +} diff --git a/tests/tracetool/tracetool-test.py b/tests/tracetool/tracetool-test.py index 3341fb18f90c3..786083ad7fb7a 100755 --- a/tests/tracetool/tracetool-test.py +++ b/tests/tracetool/tracetool-test.py @@ -14,7 +14,7 @@ def get_formats(backend): "c", "h", ] - if backend in {"ftrace", "log", "simple"}: + if backend in {"ftrace", "log", "simple", "syslog"}: formats += ["rs"] if backend == "dtrace": formats += [ From 030cd885563e9ca843a46ec7e65b657c93df5af7 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 30 Sep 2025 11:04:44 +0200 Subject: [PATCH 1097/1794] tests/qtest: Add missing checks for the availability of machines When QEMU has been compiled with "--without-default-devices", the machines might not be available in the binary. Let's properly check for the machines before running the tests to avoid that they are failing in this case. Signed-off-by: Thomas Huth Link: https://lore.kernel.org/qemu-devel/20250930090444.234431-1-thuth@redhat.com Signed-off-by: Fabiano Rosas --- tests/qtest/bios-tables-test.c | 2 +- tests/qtest/cpu-plug-test.c | 2 +- tests/qtest/riscv-csr-test.c | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 4fa8ac5096a6e..6b892ef23e1a7 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -2864,7 +2864,7 @@ int main(int argc, char *argv[]) test_acpi_riscv64_virt_tcg_acpi_spcr); } } else if (strcmp(arch, "loongarch64") == 0) { - if (has_tcg) { + if (has_tcg && qtest_has_machine("virt")) { qtest_add_func("acpi/virt", test_acpi_loongarch64_virt); qtest_add_func("acpi/virt/topology", test_acpi_loongarch64_virt_topology); diff --git a/tests/qtest/cpu-plug-test.c b/tests/qtest/cpu-plug-test.c index 44d704680b146..0aa4ccc5b6191 100644 --- a/tests/qtest/cpu-plug-test.c +++ b/tests/qtest/cpu-plug-test.c @@ -190,7 +190,7 @@ int main(int argc, char **argv) qtest_cb_for_every_machine(add_pseries_test_case, g_test_quick()); } else if (g_str_equal(arch, "s390x")) { qtest_cb_for_every_machine(add_s390x_test_case, g_test_quick()); - } else if (g_str_equal(arch, "loongarch64")) { + } else if (g_str_equal(arch, "loongarch64") && qtest_has_machine("virt")) { add_loongarch_test_case("virt"); } diff --git a/tests/qtest/riscv-csr-test.c b/tests/qtest/riscv-csr-test.c index ff5c29e6c6f29..bb1b0ffed3080 100644 --- a/tests/qtest/riscv-csr-test.c +++ b/tests/qtest/riscv-csr-test.c @@ -50,7 +50,9 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - qtest_add_func("/cpu/csr", run_test_csr); + if (qtest_has_machine("virt")) { + qtest_add_func("/cpu/csr", run_test_csr); + } return g_test_run(); } From bfa07595eb6652a3b2599bb2eb3a82f14f75497b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 30 Sep 2025 11:09:32 +0200 Subject: [PATCH 1098/1794] tests/qtest/migration: Fix cpr-tests in case the machine is not available When QEMU has been compiled with "--without-default-devices", the migration cpr-tests are currently failing since the first test leaves a socket file behind that avoids that the second test can be initialized correctly. Make sure that we delete the socket file in case that the migrate_start() failed due to the missing machine. Signed-off-by: Thomas Huth Link: https://lore.kernel.org/qemu-devel/20250930090932.235151-1-thuth@redhat.com Signed-off-by: Fabiano Rosas --- tests/qtest/migration/cpr-tests.c | 5 ++++- tests/qtest/migration/framework.c | 6 ++++-- tests/qtest/migration/framework.h | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c index 5e764a67876e0..c4ce60ff66bb3 100644 --- a/tests/qtest/migration/cpr-tests.c +++ b/tests/qtest/migration/cpr-tests.c @@ -97,7 +97,10 @@ static void test_mode_transfer_common(bool incoming_defer) .start_hook = test_mode_transfer_start, }; - test_precopy_common(&args); + if (test_precopy_common(&args) < 0) { + close(cpr_sockfd); + unlink(cpr_path); + } } static void test_mode_transfer(void) diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c index 407c9023c0511..a044b354658e5 100644 --- a/tests/qtest/migration/framework.c +++ b/tests/qtest/migration/framework.c @@ -736,7 +736,7 @@ void test_postcopy_recovery_common(MigrateCommon *args) migrate_postcopy_complete(from, to, args); } -void test_precopy_common(MigrateCommon *args) +int test_precopy_common(MigrateCommon *args) { QTestState *from, *to; void *data_hook = NULL; @@ -746,7 +746,7 @@ void test_precopy_common(MigrateCommon *args) g_assert(!args->cpr_channel || args->connect_channels); if (migrate_start(&from, &to, args->listen_uri, &args->start)) { - return; + return -1; } if (args->start_hook) { @@ -869,6 +869,8 @@ void test_precopy_common(MigrateCommon *args) } migrate_end(from, to, args->result == MIG_TEST_SUCCEED); + + return 0; } static void file_dirty_offset_region(void) diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 01e425e64e2d7..744040d53a14e 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -227,7 +227,7 @@ void migrate_end(QTestState *from, QTestState *to, bool test_dest); void test_postcopy_common(MigrateCommon *args); void test_postcopy_recovery_common(MigrateCommon *args); -void test_precopy_common(MigrateCommon *args); +int test_precopy_common(MigrateCommon *args); void test_file_common(MigrateCommon *args, bool stop_src); void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, QTestState *to, From e16d7a732124ccaf9960920a9b64f4cf95f8fdab Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Fri, 19 Sep 2025 06:58:30 -0700 Subject: [PATCH 1099/1794] tests/qtest: optimize qtest_get_machines qtest_get_machines returns the machines supported by the QEMU binary described by an environment variable and caches the result. If the next call to qtest_get_machines passes the same variable name, the cached result is returned, but if the name changes, the caching is defeated. To make caching more effective, remember the path of the QEMU binary instead. Different env vars, eg QTEST_QEMU_BINARY_SRC and QTEST_QEMU_BINARY_DST, usually resolve to the same path. Before the optimization, the test /x86_64/migration/precopy/unix/plain exec's QEMU and calls query-machines 3 times. After optimization, that only happens once. This does not significantly speed up the tests, but it reduces QTEST_LOG output, and launches fewer QEMU instances, making it easier to debug problems. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Message-ID: <87h5ymdzrf.fsf@pond.sub.org> Link: https://lore.kernel.org/qemu-devel/1758290310-349623-1-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- tests/qtest/libqtest.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 94526b7f9cc5f..f3d4e0829a30e 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -1630,7 +1630,8 @@ static void qtest_free_machine_list(struct MachInfo *machines) static struct MachInfo *qtest_get_machines(const char *var) { static struct MachInfo *machines; - static char *qemu_var; + static char *qemu_bin; + const char *new_qemu_bin; QDict *response, *minfo; QList *list; const QListEntry *p; @@ -1639,9 +1640,10 @@ static struct MachInfo *qtest_get_machines(const char *var) QTestState *qts; int idx; - if (g_strcmp0(qemu_var, var)) { - g_free(qemu_var); - qemu_var = g_strdup(var); + new_qemu_bin = qtest_qemu_binary(var); + if (g_strcmp0(qemu_bin, new_qemu_bin)) { + g_free(qemu_bin); + qemu_bin = g_strdup(new_qemu_bin); /* new qemu, clear the cache */ qtest_free_machine_list(machines); @@ -1654,7 +1656,7 @@ static struct MachInfo *qtest_get_machines(const char *var) silence_spawn_log = !g_test_verbose(); - qts = qtest_init_ext(qemu_var, "-machine none", NULL, true); + qts = qtest_init_ext(var, "-machine none", NULL, true); response = qtest_qmp(qts, "{ 'execute': 'query-machines' }"); g_assert(response); list = qdict_get_qlist(response, "return"); From 8e9f9981d6846515b2540439a80a0e16f93d4bd3 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:34:01 -0700 Subject: [PATCH 1100/1794] tests/qtest: export qtest_qemu_binary Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/1759332851-370353-10-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- tests/qtest/libqtest.c | 2 +- tests/qtest/libqtest.h | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index f3d4e0829a30e..6f76be12a47d7 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -357,7 +357,7 @@ void qtest_remove_abrt_handler(void *data) } } -static const char *qtest_qemu_binary(const char *var) +const char *qtest_qemu_binary(const char *var) { const char *qemu_bin; diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index fd27521a9c761..dc2cdd0b60c69 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -47,6 +47,15 @@ QTestState *qtest_initf(const char *fmt, ...) G_GNUC_PRINTF(1, 2); */ QTestState *qtest_vinitf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); +/** + * qtest_qemu_binary: + * @var: environment variable name + * + * Look up @var and return its value as the qemu binary path. + * If @var is NULL, look up the default var name. + */ +const char *qtest_qemu_binary(const char *var); + /** * qtest_init: * @extra_args: other arguments to pass to QEMU. CAUTION: these From b4a21e457409f972abca2818d78cdf22d59544f5 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:34:02 -0700 Subject: [PATCH 1101/1794] tests/qtest: qtest_qemu_args Define an accessor that returns all the arguments used to exec QEMU. Collect the arguments that were passed to qtest_spawn_qemu, plus the trace arguments that were composed inside qtest_spawn_qemu, and move them to a new function qtest_qemu_args. This will be needed to test the cpr-exec migration mode. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/1759332851-370353-11-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- tests/qtest/libqtest.c | 54 ++++++++++++++++++++++++------------------ tests/qtest/libqtest.h | 8 +++++++ 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 6f76be12a47d7..551bc8c1b83f5 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -409,20 +409,12 @@ static pid_t qtest_create_process(char *cmd) } #endif /* _WIN32 */ -static QTestState *G_GNUC_PRINTF(2, 3) qtest_spawn_qemu(const char *qemu_bin, - const char *fmt, ...) +static QTestState *qtest_spawn_qemu(const char *qemu_bin, const char *args) { - va_list ap; QTestState *s = g_new0(QTestState, 1); - const char *trace = g_getenv("QTEST_TRACE"); - g_autofree char *tracearg = trace ? - g_strdup_printf("-trace %s ", trace) : g_strdup(""); g_autoptr(GString) command = g_string_new(""); - va_start(ap, fmt); - g_string_append_printf(command, CMD_EXEC "%s %s", qemu_bin, tracearg); - g_string_append_vprintf(command, fmt, ap); - va_end(ap); + g_string_printf(command, CMD_EXEC "%s %s", qemu_bin, args); qtest_add_abrt_handler(kill_qemu_hook_func, s); @@ -466,6 +458,33 @@ static char *qtest_socket_path(const char *suffix) return g_strdup_printf("%s/qtest-%d.%s", g_get_tmp_dir(), getpid(), suffix); } +gchar *qtest_qemu_args(const char *extra_args) +{ + g_autofree gchar *socket_path = qtest_socket_path("sock"); + g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp"); + const char *trace = g_getenv("QTEST_TRACE"); + g_autofree char *tracearg = trace ? g_strdup_printf("-trace %s ", trace) : + g_strdup(""); + gchar *args = g_strdup_printf( + "%s" + "-qtest unix:%s " + "-qtest-log %s " + "-chardev socket,path=%s,id=char0 " + "-mon chardev=char0,mode=control " + "-display none " + "-audio none " + "%s" + " -accel qtest", + + tracearg, + socket_path, + getenv("QTEST_LOG") ? DEV_STDERR : DEV_NULL, + qmp_socket_path, + extra_args ?: ""); + + return args; +} + static QTestState *qtest_init_internal(const char *qemu_bin, const char *extra_args, bool do_connect) @@ -474,6 +493,7 @@ static QTestState *qtest_init_internal(const char *qemu_bin, int sock, qmpsock, i; g_autofree gchar *socket_path = qtest_socket_path("sock"); g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp"); + g_autofree gchar *args = qtest_qemu_args(extra_args); /* * It's possible that if an earlier test run crashed it might @@ -488,19 +508,7 @@ static QTestState *qtest_init_internal(const char *qemu_bin, sock = init_socket(socket_path); qmpsock = init_socket(qmp_socket_path); - s = qtest_spawn_qemu(qemu_bin, - "-qtest unix:%s " - "-qtest-log %s " - "-chardev socket,path=%s,id=char0 " - "-mon chardev=char0,mode=control " - "-display none " - "-audio none " - "%s" - " -accel qtest", - socket_path, - getenv("QTEST_LOG") ? DEV_STDERR : DEV_NULL, - qmp_socket_path, - extra_args ?: ""); + s = qtest_spawn_qemu(qemu_bin, args); qtest_client_set_rx_handler(s, qtest_client_socket_recv_line); qtest_client_set_tx_handler(s, qtest_client_socket_send); diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index dc2cdd0b60c69..7f8dd0a912f7e 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -56,6 +56,14 @@ QTestState *qtest_vinitf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); */ const char *qtest_qemu_binary(const char *var); +/** + * qtest_qemu_args: + * @extra_args: Other arguments to pass to QEMU. + * + * Return the command line used to start QEMU, sans binary. + */ +gchar *qtest_qemu_args(const char *extra_args); + /** * qtest_init: * @extra_args: other arguments to pass to QEMU. CAUTION: these From bf64633510b2a13268425a3eb78500bc88898bb8 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:34:03 -0700 Subject: [PATCH 1102/1794] tests/qtest: qtest_create_test_state Refactor qtest_spawn_qemu and create a subroutine to create a QTestState object, to be used in a subsequent patch. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/1759332851-370353-12-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- tests/qtest/libqtest.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 551bc8c1b83f5..3fa93172c16f0 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -409,22 +409,29 @@ static pid_t qtest_create_process(char *cmd) } #endif /* _WIN32 */ -static QTestState *qtest_spawn_qemu(const char *qemu_bin, const char *args) +static QTestState *qtest_create_test_state(int pid) { QTestState *s = g_new0(QTestState, 1); + + s->qemu_pid = pid; + qtest_add_abrt_handler(kill_qemu_hook_func, s); + return s; +} + +static QTestState *qtest_spawn_qemu(const char *qemu_bin, const char *args) +{ + int pid; g_autoptr(GString) command = g_string_new(""); g_string_printf(command, CMD_EXEC "%s %s", qemu_bin, args); - qtest_add_abrt_handler(kill_qemu_hook_func, s); - if (!silence_spawn_log) { g_test_message("starting QEMU: %s", command->str); } #ifndef _WIN32 - s->qemu_pid = fork(); - if (s->qemu_pid == 0) { + pid = fork(); + if (pid == 0) { #ifdef __linux__ /* * Although we register a ABRT handler to kill off QEMU @@ -447,10 +454,10 @@ static QTestState *qtest_spawn_qemu(const char *qemu_bin, const char *args) exit(1); } #else - s->qemu_pid = qtest_create_process(command->str); + pid = qtest_create_process(command->str); #endif /* _WIN32 */ - return s; + return qtest_create_test_state(pid); } static char *qtest_socket_path(const char *suffix) From e9fbd9e6b15a054f2b7eee757f6c9b11d4132044 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:34:04 -0700 Subject: [PATCH 1103/1794] tests/qtest: qtest_qemu_spawn_func Allow the qtest_qemu_spawn caller to pass the function to be called to perform the spawn. The opaque argument is needed by a new spawn function in a subsequent patch. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/1759332851-370353-13-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- tests/qtest/libqtest.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 3fa93172c16f0..d97144e1d5f4c 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -418,7 +418,8 @@ static QTestState *qtest_create_test_state(int pid) return s; } -static QTestState *qtest_spawn_qemu(const char *qemu_bin, const char *args) +static QTestState *qtest_spawn_qemu(const char *qemu_bin, const char *args, + void *opaque) { int pid; g_autoptr(GString) command = g_string_new(""); @@ -492,9 +493,15 @@ gchar *qtest_qemu_args(const char *extra_args) return args; } +typedef QTestState *(*qtest_qemu_spawn_func)(const char *qemu_bin, + const char *extra_args, + void *opaque); + static QTestState *qtest_init_internal(const char *qemu_bin, const char *extra_args, - bool do_connect) + bool do_connect, + qtest_qemu_spawn_func spawn, + void *opaque) { QTestState *s; int sock, qmpsock, i; @@ -515,7 +522,7 @@ static QTestState *qtest_init_internal(const char *qemu_bin, sock = init_socket(socket_path); qmpsock = init_socket(qmp_socket_path); - s = qtest_spawn_qemu(qemu_bin, args); + s = spawn(qemu_bin, args, opaque); qtest_client_set_rx_handler(s, qtest_client_socket_recv_line); qtest_client_set_tx_handler(s, qtest_client_socket_send); @@ -570,7 +577,8 @@ void qtest_connect(QTestState *s) QTestState *qtest_init_without_qmp_handshake(const char *extra_args) { - return qtest_init_internal(qtest_qemu_binary(NULL), extra_args, true); + return qtest_init_internal(qtest_qemu_binary(NULL), extra_args, true, + qtest_spawn_qemu, NULL); } void qtest_qmp_handshake(QTestState *s, QList *capabilities) @@ -593,7 +601,7 @@ QTestState *qtest_init_ext(const char *var, const char *extra_args, QList *capabilities, bool do_connect) { QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args, - do_connect); + do_connect, qtest_spawn_qemu, NULL); if (do_connect) { qtest_qmp_handshake(s, capabilities); From 5419f36e9eb3ec155cd3762a691c92fcfca48958 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:34:05 -0700 Subject: [PATCH 1104/1794] tests/qtest: qtest_init_after_exec Define a function to create a QTestState object representing the state of QEMU after old QEMU exec's new QEMU. This is needed for testing the cpr-exec migration mode. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/1759332851-370353-14-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- tests/qtest/libqtest.c | 19 +++++++++++++++++++ tests/qtest/libqtest.h | 8 ++++++++ 2 files changed, 27 insertions(+) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index d97144e1d5f4c..933d0858697c2 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -615,6 +615,25 @@ QTestState *qtest_init_ext(const char *var, const char *extra_args, return s; } +static QTestState *qtest_attach_qemu(const char *qemu_bin, + const char *extra_args, + void *opaque) +{ + int pid = *(int *)opaque; + return qtest_create_test_state(pid); +} + +QTestState *qtest_init_after_exec(QTestState *qts) +{ + void *opaque = (void *)&qts->qemu_pid; + QTestState *s; + + s = qtest_init_internal(NULL, NULL, true, qtest_attach_qemu, opaque); + qts->qemu_pid = -1; + qtest_qmp_handshake(s, NULL); + return s; +} + QTestState *qtest_init(const char *extra_args) { return qtest_init_ext(NULL, extra_args, NULL, true); diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index 7f8dd0a912f7e..9c118c89ca0bc 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -56,6 +56,14 @@ QTestState *qtest_vinitf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); */ const char *qtest_qemu_binary(const char *var); +/** + * qtest_init_after_exec: + * @qts: the previous QEMU state + * + * Return a test state representing new QEMU after @qts exec's it. + */ +QTestState *qtest_init_after_exec(QTestState *qts); + /** * qtest_qemu_args: * @extra_args: Other arguments to pass to QEMU. From 0a3aeedb39aee0d20a954650b2956cfbc0a7c566 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:34:06 -0700 Subject: [PATCH 1105/1794] migration-test: only_source option Add the only_source option, analogous to only_target. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/1759332851-370353-15-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- tests/qtest/migration/framework.c | 24 +++++++++++++++--------- tests/qtest/migration/framework.h | 2 ++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c index a044b354658e5..2c13fd168879c 100644 --- a/tests/qtest/migration/framework.c +++ b/tests/qtest/migration/framework.c @@ -234,7 +234,7 @@ static void migrate_start_set_capabilities(QTestState *from, QTestState *to, * to mimic as closer as that. */ migrate_set_capability(from, "events", true); - if (!args->defer_target_connect) { + if (!args->defer_target_connect && to) { migrate_set_capability(to, "events", true); } @@ -246,8 +246,10 @@ static void migrate_start_set_capabilities(QTestState *from, QTestState *to, if (args->caps[MIGRATION_CAPABILITY_MULTIFD]) { migrate_set_parameter_int(from, "multifd-channels", MULTIFD_TEST_CHANNELS); - migrate_set_parameter_int(to, "multifd-channels", - MULTIFD_TEST_CHANNELS); + if (to) { + migrate_set_parameter_int(to, "multifd-channels", + MULTIFD_TEST_CHANNELS); + } } return; @@ -410,11 +412,13 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, shmem_opts ? shmem_opts : "", args->opts_target ? args->opts_target : "", ignore_stderr); - *to = qtest_init_ext(QEMU_ENV_DST, cmd_target, capabilities, - !args->defer_target_connect); - qtest_qmp_set_event_callback(*to, - migrate_watch_for_events, - &dst_state); + if (!args->only_source) { + *to = qtest_init_ext(QEMU_ENV_DST, cmd_target, capabilities, + !args->defer_target_connect); + qtest_qmp_set_event_callback(*to, + migrate_watch_for_events, + &dst_state); + } /* * Remove shmem file immediately to avoid memory leak in test failed case. @@ -424,7 +428,9 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, unlink(shmem_path); } - migrate_start_set_capabilities(*from, *to, args); + migrate_start_set_capabilities(*from, + args->only_source ? NULL : *to, + args); return 0; } diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 744040d53a14e..19d552f78a1fd 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -103,6 +103,8 @@ typedef struct { */ bool hide_stderr; bool use_shmem; + /* only launch the source process */ + bool only_source; /* only launch the target process */ bool only_target; /* Use dirty ring if true; dirty logging otherwise */ From c454a8d254570eba3c38e2e0b4e9f3f6d9daff4f Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:34:07 -0700 Subject: [PATCH 1106/1794] migration-test: shm path accessor Define an accessor for the shm path. It will be referenced from multiple sites in a subsequent patch. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/1759332851-370353-16-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- tests/qtest/migration/framework.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c index 2c13fd168879c..d20938ccec5e2 100644 --- a/tests/qtest/migration/framework.c +++ b/tests/qtest/migration/framework.c @@ -255,6 +255,11 @@ static void migrate_start_set_capabilities(QTestState *from, QTestState *to, return; } +static char *test_shmem_path(void) +{ + return g_strdup_printf("/dev/shm/qemu-%d", getpid()); +} + int migrate_start(QTestState **from, QTestState **to, const char *uri, MigrateStart *args) { @@ -342,7 +347,7 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, } if (args->use_shmem) { - shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid()); + shmem_path = test_shmem_path(); shmem_opts = g_strdup_printf( "-object memory-backend-file,id=mem0,size=%s" ",mem-path=%s,share=on -numa node,memdev=mem0", From bdd739a42c121aefa35bcfd6583c03d95a44c18f Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:34:08 -0700 Subject: [PATCH 1107/1794] migration-test: misc exports Export misc definitions needed by the cpr-exec test. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/1759332851-370353-17-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- tests/qtest/migration/bootfile.c | 5 +++++ tests/qtest/migration/bootfile.h | 1 + tests/qtest/migration/framework.c | 5 +++++ tests/qtest/migration/framework.h | 1 + 4 files changed, 12 insertions(+) diff --git a/tests/qtest/migration/bootfile.c b/tests/qtest/migration/bootfile.c index fac059d11d74a..479c43231d717 100644 --- a/tests/qtest/migration/bootfile.c +++ b/tests/qtest/migration/bootfile.c @@ -68,3 +68,8 @@ char *bootfile_create(const char *arch, const char *dir, bool suspend_me) return bootpath; } + +char *bootfile_get(void) +{ + return bootpath; +} diff --git a/tests/qtest/migration/bootfile.h b/tests/qtest/migration/bootfile.h index 6d6a67386e1be..96e784b163403 100644 --- a/tests/qtest/migration/bootfile.h +++ b/tests/qtest/migration/bootfile.h @@ -35,5 +35,6 @@ void bootfile_delete(void); char *bootfile_create(const char *arch, const char *dir, bool suspend_me); +char *bootfile_get(void); #endif /* BOOTFILE_H */ diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c index d20938ccec5e2..89c85cd314a39 100644 --- a/tests/qtest/migration/framework.c +++ b/tests/qtest/migration/framework.c @@ -1007,6 +1007,11 @@ QTestMigrationState *get_src(void) return &src_state; } +QTestMigrationState *get_dst(void) +{ + return &dst_state; +} + MigrationTestEnv *migration_get_env(void) { static MigrationTestEnv *env; diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 19d552f78a1fd..cc0d09b6da231 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -237,6 +237,7 @@ void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, typedef struct QTestMigrationState QTestMigrationState; QTestMigrationState *get_src(void); +QTestMigrationState *get_dst(void); #ifdef CONFIG_GNUTLS void migration_test_add_tls(MigrationTestEnv *env); From aca1533cd84edbbb084d5e201463ae480eb3877c Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:34:09 -0700 Subject: [PATCH 1108/1794] migration-test: migrate_args Define the subroutine migrate_args to return the arguments that are used to exec the source or target qemu process. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/1759332851-370353-18-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- tests/qtest/migration/framework.c | 65 +++++++++++++++++++------------ tests/qtest/migration/framework.h | 2 + 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c index 89c85cd314a39..a9be9c2dbf8d1 100644 --- a/tests/qtest/migration/framework.c +++ b/tests/qtest/migration/framework.c @@ -260,13 +260,12 @@ static char *test_shmem_path(void) return g_strdup_printf("/dev/shm/qemu-%d", getpid()); } -int migrate_start(QTestState **from, QTestState **to, const char *uri, - MigrateStart *args) +int migrate_args(char **from, char **to, const char *uri, MigrateStart *args) { /* options for source and target */ g_autofree gchar *arch_opts = NULL; - g_autofree gchar *cmd_source = NULL; - g_autofree gchar *cmd_target = NULL; + gchar *cmd_source = NULL; + gchar *cmd_target = NULL; const gchar *ignore_stderr; g_autofree char *shmem_opts = NULL; g_autofree char *shmem_path = NULL; @@ -275,23 +274,10 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, const char *memory_size; const char *machine_alias, *machine_opts = ""; g_autofree char *machine = NULL; - const char *bootpath; - g_autoptr(QList) capabilities = migrate_start_get_qmp_capabilities(args); + const char *bootpath = bootfile_get(); g_autofree char *memory_backend = NULL; const char *events; - if (args->use_shmem) { - if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { - g_test_skip("/dev/shm is not supported"); - return -1; - } - } - - dst_state = (QTestMigrationState) { }; - src_state = (QTestMigrationState) { }; - bootpath = bootfile_create(arch, tmpfs, args->suspend_me); - src_state.suspend_me = args->suspend_me; - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { memory_size = "150M"; @@ -388,12 +374,6 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, shmem_opts ? shmem_opts : "", args->opts_source ? args->opts_source : "", ignore_stderr); - if (!args->only_target) { - *from = qtest_init_ext(QEMU_ENV_SRC, cmd_source, capabilities, true); - qtest_qmp_set_event_callback(*from, - migrate_watch_for_events, - &src_state); - } /* * If the monitor connection is deferred, enable events on the command line @@ -417,6 +397,42 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, shmem_opts ? shmem_opts : "", args->opts_target ? args->opts_target : "", ignore_stderr); + + *from = cmd_source; + *to = cmd_target; + return 0; +} + +int migrate_start(QTestState **from, QTestState **to, const char *uri, + MigrateStart *args) +{ + g_autofree gchar *cmd_source = NULL; + g_autofree gchar *cmd_target = NULL; + g_autoptr(QList) capabilities = migrate_start_get_qmp_capabilities(args); + + if (args->use_shmem) { + if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { + g_test_skip("/dev/shm is not supported"); + return -1; + } + } + + dst_state = (QTestMigrationState) { }; + src_state = (QTestMigrationState) { }; + bootfile_create(qtest_get_arch(), tmpfs, args->suspend_me); + src_state.suspend_me = args->suspend_me; + + if (migrate_args(&cmd_source, &cmd_target, uri, args)) { + return -1; + } + + if (!args->only_target) { + *from = qtest_init_ext(QEMU_ENV_SRC, cmd_source, capabilities, true); + qtest_qmp_set_event_callback(*from, + migrate_watch_for_events, + &src_state); + } + if (!args->only_source) { *to = qtest_init_ext(QEMU_ENV_DST, cmd_target, capabilities, !args->defer_target_connect); @@ -430,6 +446,7 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, * It's valid because QEMU has already opened this file */ if (args->use_shmem) { + g_autofree char *shmem_path = test_shmem_path(); unlink(shmem_path); } diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index cc0d09b6da231..9bb584a6bb1db 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -223,6 +223,8 @@ typedef struct { void wait_for_serial(const char *side); void migrate_prepare_for_dirty_mem(QTestState *from); void migrate_wait_for_dirty_mem(QTestState *from, QTestState *to); + +int migrate_args(char **from, char **to, const char *uri, MigrateStart *args); int migrate_start(QTestState **from, QTestState **to, const char *uri, MigrateStart *args); void migrate_end(QTestState *from, QTestState *to, bool test_dest); From ca5be51a51fc6b6402838310ae8d0162fc03ecef Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:34:10 -0700 Subject: [PATCH 1109/1794] migration-test: strv parameter Define migrate_set_parameter_strv. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/1759332851-370353-19-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- tests/qtest/migration/migration-qmp.c | 16 ++++++++++++++++ tests/qtest/migration/migration-qmp.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c index 66dd369ba7ec9..c803fcee9d36d 100644 --- a/tests/qtest/migration/migration-qmp.c +++ b/tests/qtest/migration/migration-qmp.c @@ -442,6 +442,22 @@ void migrate_set_parameter_str(QTestState *who, const char *parameter, migrate_check_parameter_str(who, parameter, value); } +void migrate_set_parameter_strv(QTestState *who, const char *parameter, + char **strv) +{ + g_autofree char *args = g_strjoinv("\",\"", strv); + g_autoptr(GString) value = g_string_new(""); + g_autofree char *command = NULL; + + g_string_printf(value, "\"%s\"", args); + + command = g_strdup_printf("{ 'execute': 'migrate-set-parameters'," + "'arguments': { %%s: [ %s ]}}", + value->str); + + qtest_qmp_assert_success(who, command, parameter); +} + static long long migrate_get_parameter_bool(QTestState *who, const char *parameter) { diff --git a/tests/qtest/migration/migration-qmp.h b/tests/qtest/migration/migration-qmp.h index faa8181d9164a..44482d250f352 100644 --- a/tests/qtest/migration/migration-qmp.h +++ b/tests/qtest/migration/migration-qmp.h @@ -34,6 +34,8 @@ void read_blocktime(QTestState *who); void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state); void migrate_set_parameter_str(QTestState *who, const char *parameter, const char *value); +void migrate_set_parameter_strv(QTestState *who, const char *parameter, + char **strv); void migrate_set_parameter_bool(QTestState *who, const char *parameter, int value); void migrate_ensure_non_converge(QTestState *who); From 4f7528295b3e6dfe1189f660fa7865ad972d82e7 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 4 Sep 2025 08:27:24 -0500 Subject: [PATCH 1110/1794] hw/riscv/riscv-iommu: Fix MSI table size limit The MSI table is not limited to 4k. The only constraint the table has is that its base address must be aligned to its size, ensuring no offsets of the table size will overrun when added to the base address (see "8.5. MSI page tables" of the AIA spec). Fixes: 0c54acb8243d ("hw/riscv: add RISC-V IOMMU base emulation") Signed-off-by: Andrew Jones Reviewed-by: Daniel Henrique Barboza Message-ID: <20250904132723.614507-2-ajones@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 96a7fbdefcf3b..155190d032dd9 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -558,6 +558,7 @@ static MemTxResult riscv_iommu_msi_write(RISCVIOMMUState *s, MemTxResult res; dma_addr_t addr; uint64_t intn; + size_t offset; uint32_t n190; uint64_t pte[2]; int fault_type = RISCV_IOMMU_FQ_TTYPE_UADDR_WR; @@ -565,16 +566,18 @@ static MemTxResult riscv_iommu_msi_write(RISCVIOMMUState *s, /* Interrupt File Number */ intn = riscv_iommu_pext_u64(PPN_DOWN(gpa), ctx->msi_addr_mask); - if (intn >= 256) { + offset = intn * sizeof(pte); + + /* fetch MSI PTE */ + addr = PPN_PHYS(get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_PPN)); + if (addr & offset) { /* Interrupt file number out of range */ res = MEMTX_ACCESS_ERROR; cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT; goto err; } - /* fetch MSI PTE */ - addr = PPN_PHYS(get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_PPN)); - addr = addr | (intn * sizeof(pte)); + addr |= offset; res = dma_memory_read(s->target_as, addr, &pte, sizeof(pte), MEMTXATTRS_UNSPECIFIED); if (res != MEMTX_OK) { From da14767b356c2342197708a997eeb0da053262a0 Mon Sep 17 00:00:00 2001 From: Andrea Bolognani Date: Wed, 10 Sep 2025 14:15:01 +0200 Subject: [PATCH 1111/1794] docs/interop/firmware: Add riscv64 to FirmwareArchitecture Descriptors using this value have been shipped for years by distros, so we just need to update the spec to match reality. Signed-off-by: Andrea Bolognani Reviewed-by: Kashyap Chamarthy Message-ID: <20250910121501.676219-1-abologna@redhat.com> Signed-off-by: Alistair Francis --- docs/interop/firmware.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json index 6bbe2cce0afef..ccbfaf828d62d 100644 --- a/docs/interop/firmware.json +++ b/docs/interop/firmware.json @@ -85,12 +85,14 @@ # # @loongarch64: 64-bit LoongArch. (since: 7.1) # +# @riscv64: 64-bit RISC-V. +# # @x86_64: 64-bit x86. # # Since: 3.0 ## { 'enum' : 'FirmwareArchitecture', - 'data' : [ 'aarch64', 'arm', 'i386', 'loongarch64', 'x86_64' ] } + 'data' : [ 'aarch64', 'arm', 'i386', 'loongarch64', 'riscv64', 'x86_64' ] } ## # @FirmwareTarget: From 696086ad9fb500573c7f710483215e079ee3cf33 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 3 Sep 2025 13:40:43 -0300 Subject: [PATCH 1112/1794] linux-user/syscall.c: sync RISC-V hwprobe with Linux It has been awhile since the last sync. Let's bring QEMU hwprobe support on par with Linux 6.17-rc4. A lot of new RISCV_HWPROBE_KEY_* entities are added but this patch is only adding support for ZICBOM_BLOCK_SIZE. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250903164043.2828336-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- linux-user/syscall.c | 89 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 1a5f2a03f9f9d..d78b2029fa17a 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -9023,6 +9023,29 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count) #define RISCV_HWPROBE_EXT_ZTSO (1ULL << 33) #define RISCV_HWPROBE_EXT_ZACAS (1ULL << 34) #define RISCV_HWPROBE_EXT_ZICOND (1ULL << 35) +#define RISCV_HWPROBE_EXT_ZIHINTPAUSE (1ULL << 36) +#define RISCV_HWPROBE_EXT_ZVE32X (1ULL << 37) +#define RISCV_HWPROBE_EXT_ZVE32F (1ULL << 38) +#define RISCV_HWPROBE_EXT_ZVE64X (1ULL << 39) +#define RISCV_HWPROBE_EXT_ZVE64F (1ULL << 40) +#define RISCV_HWPROBE_EXT_ZVE64D (1ULL << 41) +#define RISCV_HWPROBE_EXT_ZIMOP (1ULL << 42) +#define RISCV_HWPROBE_EXT_ZCA (1ULL << 43) +#define RISCV_HWPROBE_EXT_ZCB (1ULL << 44) +#define RISCV_HWPROBE_EXT_ZCD (1ULL << 45) +#define RISCV_HWPROBE_EXT_ZCF (1ULL << 46) +#define RISCV_HWPROBE_EXT_ZCMOP (1ULL << 47) +#define RISCV_HWPROBE_EXT_ZAWRS (1ULL << 48) +#define RISCV_HWPROBE_EXT_SUPM (1ULL << 49) +#define RISCV_HWPROBE_EXT_ZICNTR (1ULL << 50) +#define RISCV_HWPROBE_EXT_ZIHPM (1ULL << 51) +#define RISCV_HWPROBE_EXT_ZFBFMIN (1ULL << 52) +#define RISCV_HWPROBE_EXT_ZVFBFMIN (1ULL << 53) +#define RISCV_HWPROBE_EXT_ZVFBFWMA (1ULL << 54) +#define RISCV_HWPROBE_EXT_ZICBOM (1ULL << 55) +#define RISCV_HWPROBE_EXT_ZAAMO (1ULL << 56) +#define RISCV_HWPROBE_EXT_ZALRSC (1ULL << 57) +#define RISCV_HWPROBE_EXT_ZABHA (1ULL << 58) #define RISCV_HWPROBE_KEY_CPUPERF_0 5 #define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0) @@ -9033,6 +9056,22 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count) #define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0) #define RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE 6 +#define RISCV_HWPROBE_KEY_HIGHEST_VIRT_ADDRESS 7 +#define RISCV_HWPROBE_KEY_TIME_CSR_FREQ 8 +#define RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF 9 +#define RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN 0 +#define RISCV_HWPROBE_MISALIGNED_SCALAR_EMULATED 1 +#define RISCV_HWPROBE_MISALIGNED_SCALAR_SLOW 2 +#define RISCV_HWPROBE_MISALIGNED_SCALAR_FAST 3 +#define RISCV_HWPROBE_MISALIGNED_SCALAR_UNSUPPORTED 4 +#define RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF 10 +#define RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN 0 +#define RISCV_HWPROBE_MISALIGNED_VECTOR_SLOW 2 +#define RISCV_HWPROBE_MISALIGNED_VECTOR_FAST 3 +#define RISCV_HWPROBE_MISALIGNED_VECTOR_UNSUPPORTED 4 +#define RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0 11 +#define RISCV_HWPROBE_KEY_ZICBOM_BLOCK_SIZE 12 +#define RISCV_HWPROBE_KEY_VENDOR_EXT_SIFIVE_0 13 struct riscv_hwprobe { abi_llong key; @@ -9141,6 +9180,52 @@ static void risc_hwprobe_fill_pairs(CPURISCVState *env, RISCV_HWPROBE_EXT_ZACAS : 0; value |= cfg->ext_zicond ? RISCV_HWPROBE_EXT_ZICOND : 0; + value |= cfg->ext_zihintpause ? + RISCV_HWPROBE_EXT_ZIHINTPAUSE : 0; + value |= cfg->ext_zve32x ? + RISCV_HWPROBE_EXT_ZVE32X : 0; + value |= cfg->ext_zve32f ? + RISCV_HWPROBE_EXT_ZVE32F : 0; + value |= cfg->ext_zve64x ? + RISCV_HWPROBE_EXT_ZVE64X : 0; + value |= cfg->ext_zve64f ? + RISCV_HWPROBE_EXT_ZVE64F : 0; + value |= cfg->ext_zve64d ? + RISCV_HWPROBE_EXT_ZVE64D : 0; + value |= cfg->ext_zimop ? + RISCV_HWPROBE_EXT_ZIMOP : 0; + value |= cfg->ext_zca ? + RISCV_HWPROBE_EXT_ZCA : 0; + value |= cfg->ext_zcb ? + RISCV_HWPROBE_EXT_ZCB : 0; + value |= cfg->ext_zcd ? + RISCV_HWPROBE_EXT_ZCD : 0; + value |= cfg->ext_zcf ? + RISCV_HWPROBE_EXT_ZCF : 0; + value |= cfg->ext_zcmop ? + RISCV_HWPROBE_EXT_ZCMOP : 0; + value |= cfg->ext_zawrs ? + RISCV_HWPROBE_EXT_ZAWRS : 0; + value |= cfg->ext_supm ? + RISCV_HWPROBE_EXT_SUPM : 0; + value |= cfg->ext_zicntr ? + RISCV_HWPROBE_EXT_ZICNTR : 0; + value |= cfg->ext_zihpm ? + RISCV_HWPROBE_EXT_ZIHPM : 0; + value |= cfg->ext_zfbfmin ? + RISCV_HWPROBE_EXT_ZFBFMIN : 0; + value |= cfg->ext_zvfbfmin ? + RISCV_HWPROBE_EXT_ZVFBFMIN : 0; + value |= cfg->ext_zvfbfwma ? + RISCV_HWPROBE_EXT_ZVFBFWMA : 0; + value |= cfg->ext_zicbom ? + RISCV_HWPROBE_EXT_ZICBOM : 0; + value |= cfg->ext_zaamo ? + RISCV_HWPROBE_EXT_ZAAMO : 0; + value |= cfg->ext_zalrsc ? + RISCV_HWPROBE_EXT_ZALRSC : 0; + value |= cfg->ext_zabha ? + RISCV_HWPROBE_EXT_ZABHA : 0; __put_user(value, &pair->value); break; case RISCV_HWPROBE_KEY_CPUPERF_0: @@ -9150,6 +9235,10 @@ static void risc_hwprobe_fill_pairs(CPURISCVState *env, value = cfg->ext_zicboz ? cfg->cboz_blocksize : 0; __put_user(value, &pair->value); break; + case RISCV_HWPROBE_KEY_ZICBOM_BLOCK_SIZE: + value = cfg->ext_zicbom ? cfg->cbom_blocksize : 0; + __put_user(value, &pair->value); + break; default: __put_user(-1, &pair->key); break; From e06d209aa69d8ece901ada9620b37af4d87b713e Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 3 Jul 2025 10:08:15 -0300 Subject: [PATCH 1113/1794] target/riscv: implement MonitorDef HMP API The MonitorDef API is related to two HMP monitor commands: 'p' and 'x': (qemu) help p print|p /fmt expr -- print expression value (use $reg for CPU register access) (qemu) help x x /fmt addr -- virtual memory dump starting at 'addr' For x86, one of the few targets that implements it, it is possible to print the PC register value with $pc and use the PC value in the 'x' command as well. Those 2 commands are hooked into get_monitor_def(), called by exp_unary() in hmp.c. The function tries to fetch a reg value in two ways: by reading them directly via a target_monitor_defs array or using a target_get_monitor_def() helper. In RISC-V we have *A LOT* of registers and this number will keep getting bigger, so we're opting out of an array declaration. We're able to retrieve all regs but vregs because the API only fits an uint64_t and vregs have 'vlen' size that are bigger than that. With this patch we can do things such as: - print CSRs and use their val in expressions: (qemu) p $mstatus 0xa000000a0 (qemu) p $mstatus & 0xFF 0xa0 - dump the next 10 insn from virtual memory starting at x1 (ra): (qemu) x/10i $ra 0xffffffff80958aea: a9bff0ef jal ra,-1382 # 0xffffffff80958584 0xffffffff80958aee: 10016073 csrrsi zero,sstatus,2 0xffffffff80958af2: 60a2 ld ra,8(sp) 0xffffffff80958af4: 6402 ld s0,0(sp) 0xffffffff80958af6: 0141 addi sp,sp,16 0xffffffff80958af8: 8082 ret 0xffffffff80958afa: 10016073 csrrsi zero,sstatus,2 0xffffffff80958afe: 8082 ret 0xffffffff80958b00: 1141 addi sp,sp,-16 0xffffffff80958b02: e422 sd s0,8(sp) (qemu) Suggested-by: Dr. David Alan Gilbert Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250703130815.1592493-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 1 + target/riscv/riscv-qmp-cmds.c | 148 ++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 4a862da61582c..738e68fa6e25c 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -592,6 +592,7 @@ static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext) extern const char * const riscv_int_regnames[]; extern const char * const riscv_int_regnamesh[]; extern const char * const riscv_fpr_regnames[]; +extern const char * const riscv_rvv_regnames[]; const char *riscv_cpu_get_trap_name(target_ulong cause, bool async); int riscv_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, diff --git a/target/riscv/riscv-qmp-cmds.c b/target/riscv/riscv-qmp-cmds.c index 8a1856c50e0f6..b63de8dd457aa 100644 --- a/target/riscv/riscv-qmp-cmds.c +++ b/target/riscv/riscv-qmp-cmds.c @@ -31,6 +31,10 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/visitor.h" #include "qom/qom-qobject.h" +#include "qemu/ctype.h" +#include "qemu/qemu-print.h" +#include "monitor/hmp.h" +#include "monitor/hmp-target.h" #include "system/kvm.h" #include "system/tcg.h" #include "cpu-qom.h" @@ -240,3 +244,147 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, return expansion_info; } + +/* + * We have way too many potential CSRs and regs being added + * regularly to register them in a static array. + * + * Declare an empty array instead, making get_monitor_def() use + * the target_get_monitor_def() API directly. + */ +const MonitorDef monitor_defs[] = { { } }; +const MonitorDef *target_monitor_defs(void) +{ + return monitor_defs; +} + +static bool reg_is_ulong_integer(CPURISCVState *env, const char *name, + target_ulong *val, bool is_gprh) +{ + const char * const *reg_names; + target_ulong *vals; + + if (is_gprh) { + reg_names = riscv_int_regnamesh; + vals = env->gprh; + } else { + reg_names = riscv_int_regnames; + vals = env->gpr; + } + + for (int i = 0; i < 32; i++) { + g_autofree char *reg_name = g_strdup(reg_names[i]); + char *reg1 = strtok(reg_name, "/"); + char *reg2 = strtok(NULL, "/"); + + if (strcasecmp(reg1, name) == 0 || + (reg2 && strcasecmp(reg2, name) == 0)) { + *val = vals[i]; + return true; + } + } + + return false; +} + +static bool reg_is_u64_fpu(CPURISCVState *env, const char *name, uint64_t *val) +{ + if (qemu_tolower(name[0]) != 'f') { + return false; + } + + for (int i = 0; i < 32; i++) { + g_autofree char *reg_name = g_strdup(riscv_fpr_regnames[i]); + char *reg1 = strtok(reg_name, "/"); + char *reg2 = strtok(NULL, "/"); + + if (strcasecmp(reg1, name) == 0 || + (reg2 && strcasecmp(reg2, name) == 0)) { + *val = env->fpr[i]; + return true; + } + } + + return false; +} + +static bool reg_is_vreg(const char *name) +{ + if (qemu_tolower(name[0]) != 'v' || strlen(name) > 3) { + return false; + } + + for (int i = 0; i < 32; i++) { + if (strcasecmp(name, riscv_rvv_regnames[i]) == 0) { + return true; + } + } + + return false; +} + +int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval) +{ + CPURISCVState *env = &RISCV_CPU(cs)->env; + target_ulong val = 0; + uint64_t val64 = 0; + int i; + + if (reg_is_ulong_integer(env, name, &val, false) || + reg_is_ulong_integer(env, name, &val, true)) { + *pval = val; + return 0; + } + + if (reg_is_u64_fpu(env, name, &val64)) { + *pval = val64; + return 0; + } + + if (reg_is_vreg(name)) { + if (!riscv_has_ext(env, RVV)) { + return -EINVAL; + } + + qemu_printf("Unable to print the value of vector " + "vreg '%s' from this API\n", name); + + /* + * We're returning 0 because returning -EINVAL triggers + * an 'unknown register' message in exp_unary() later, + * which feels ankward after our own error message. + */ + *pval = 0; + return 0; + } + + for (i = 0; i < ARRAY_SIZE(csr_ops); i++) { + RISCVException res; + int csrno = i; + + /* + * Early skip when possible since we're going + * through a lot of NULL entries. + */ + if (csr_ops[csrno].predicate == NULL) { + continue; + } + + if (strcasecmp(csr_ops[csrno].name, name) != 0) { + continue; + } + + res = riscv_csrrw_debug(env, csrno, &val, 0, 0); + + /* + * Rely on the smode, hmode, etc, predicates within csr.c + * to do the filtering of the registers that are present. + */ + if (res == RISCV_EXCP_NONE) { + *pval = val; + return 0; + } + } + + return -EINVAL; +} From 7a9202eaeb2797fd122f9034c3739b48a4152636 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 22 Jul 2025 14:29:06 -0300 Subject: [PATCH 1114/1794] roms/opensbi: Update to v1.7 Update OpenSBI and the pre-built opensbi32 and opensbi64 images to version 1.7. It has been almost an year since we last updated OpenSBI (at the time, up to v1.5.1) and we're missing a lot of good stuff from both v1.6 and v1.7, including SBI 3.0 and RPMI 1.0. The changelog is too large and tedious to post in the commit msg so I encourage refering to [1] and [2] to see the new features we're adding into the QEMU roms. [1] https://github.com/riscv-software-src/opensbi/releases/tag/v1.6 [2] https://github.com/riscv-software-src/opensbi/releases/tag/v1.7 Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Alistair Francis --- .../opensbi-riscv32-generic-fw_dynamic.bin | Bin 268312 -> 268752 bytes .../opensbi-riscv64-generic-fw_dynamic.bin | Bin 272504 -> 273048 bytes roms/opensbi | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin index b2e740010b26835d28ba49a8041e7b28881b37d4..02be3a72a880b58afbc264177c6c145b0779e73f 100644 GIT binary patch literal 268752 zcmce<4OCNC)-Zf-Zf4L$$a^M7Z2e<6;DthMVMy0FcRG7$Q8b9-!_D@LZQtW?I=4SBg)7bXrV z-Jo~3w}X@AiNPij3QImLnxTYR3b(*s)HT(l8d??XQ24rpW19NE1dMx}o z#A9FhhTS2fMTk=q>JZUr6e8LkUP=E+6e7AG#%;uZl{D0)4Yw=lU&0vQ9}werADLkU zlR^7q(Jp4neZ;8t5zN%x&&=wj7n#6)&#cJEnapOG>Hqt;ufE6xJ^Pl2p?frpFXCrM zk7NAsGls2kEHmW?5u*}`n5iPs$Z@Vr1}H=$@ybkJFM8f&U66dQv_V1lc#f>Sa&idy zELK?&DP~cnxH64G`no?OvUxqyH!nrx#R%j#x_0z>&q`|@QEB}eepcdR6PuWfFvT#w zbwr-Xi?qfhJ2;>=z&-hoE1oErWPFz)s>D^sUM3k5Ro0Or*UxIg)OAFaEDsCjXSz`~ zVjU&PFNWP?bKDW3)t9>?He(}^=T6bqnC(L$qiDn~|C@|o>9l5krC}|};k8Vbi)Muo zm_jh#wDf}jdGM!tF5B41mjsMYmM1)=*<^n-%ZB8mCmm2Agv>AtlU|SX`_z>FA0$<& zuMDfyFB2(4@a*)B&zm?lgh~!eOC=DlpNwMVy;TGX;V6X5lE;WqH0O) z=+k4)OgR61vtNs zzx~nmfey!w2Y$Y@#x_3i3!XLhtmvHRf-Yk0Mb{n;p$KGK*AuMw)gW>*hxC&h5a~w| zB*PJUrh!09BA2`#FJBqAa@?BLYsjgTSU*)GCZ};?{j>%#8Awt3Kn+E{$Wi(i8(`;A z68&_IgnWsU=wE7(kU7(!M5HcKrzPg=<`dDnXd*@zLoCoOAYRwKPAt?dBo^rw5sP(;lVj1Wv{X#T zmiUNh6W2RS8evr)Yc*=-M_B7hA!Q?vq4_assVVV%J$|Os~KR!riNc z9j^WCeDCXeAL4w^x{>Ia`fj8n`R3jaaGRPxyxa3xC4rVv=;p>$5n9FxDMbi*A*Dn@ zUPviY$O|bY7V<($Q9@owDT$C5Qi>MxLP|-6ypU2dAuptqn~)b$YLt){Qp#P(3n}Fx z=NSApCc_CdkTF49OGEX5dqyfPc!t)OZ z_7PHlA)PIx8X=7r(j+0>CZwE@<_l@5kRB7#1|hv7q@6q`;p7d}kHbCIjUcVvR;&*9^Q1Q*&KRwnwfQ@M%&Q$!K_r~i_44E zy2eFZntAKDoJQ$`Ie+I&TW-tTt-V64&*-TtjyxDCvIV!q@3^PpY$(rTjlG$Jr|K<> zOsHhjSCxut7<| zxru5BupPen!6b`<3I?ofc@<;Cs+9f0dQ)SR(6fRLP)$^W1}rM-u56@5{ImU=2LWhS z=33j~E%Tf4%sj0T)Gv8jmh6?=Q1EhL4t~XIYX<$Z(9h(#S>x2!(ezJv78o-+4Mi8$ zVazOdX5b*;g6 z^*QR6vrcTw6048vxq7qp1?mUBPv6&|{~ufL$kD&G(`GO?`kH z!YhX%$1uZ;y{tO=hSxFI>Qf3^ZqEdO=jUn1oHJU2TldVdj-@*A)4KNnz7FT=qa$(r ziu8z~{`HO(Ty~Dt469+{I_3PTbjodqQ(6Am-^+HmdOh?}y(K}#aT*(nT^pDoyTGrb zCWfy?O|~6gx6X!YB+kB3OJrb%M-5+xnm=5}_YE7;i2=J4s8+7hSvNNK4?^Y|M6 z&`Wiktta?zc#en14%gs*KMYIBQd^IF2P=_@ocl|gQ%7mi%)@mXUL7&sVi@l|Crn{m zyoT#=p?n<4;_Zbd-rRAXgpCD`5W7 zq<{Tgg-rZQ6Q__MDXh-oCeBp{-fJ;8`D(!_R~f9+O<2QR6)T~_DumJ`KUnWbTkN`d zP&#kok_zrr*76L#mri(B9RcrGuq5?+R}0M)Sf6kk`r{TTo$D&~_qQEJVK(2e$x!-= ztCUi~eCNS#)7Zj#5?F$o<9bsHZ_sNx4FPXrm$RPKOxGI?@P&21vxT+14W+@ZQvaz? zf39tdJQzxYT&2`B=>L_S!ipya0=%K;UF$#npbqkLc18*~c?sq}yEyXc`!8J-| zofm2(1w;Lio%4CF+OXuHjtTtYXB!F+WTx-`nJ-O>1zyZBK~M9GZD{ffdR6^hQk8yJ z?;m+rkx+^brcF!ZM=ga>Q-grt`rtOU;9PGK>T*ey8KnZ8ni(+P>9$<7$yp<}WRo7& zHPE#NA<#aMh>GD%6s&P>$>t_u^Isi~-& zFKL)t2eOmC1Wuh%`W~JeIE;@Vj7sl!i*8l7I5AY&gO{aYt)}r_Li= zzvRm_M@ogK(!+a&C+e`j@RaoL@xoKXp$n(@7NxV4PU-A_9djAXT>xGY&tYJ%BDt(d zpbX7*m_Nw<`nx+$VL3|%bXSZ&hvl!noUX`gtS}d==jKnWNlOj)@~fAe2I}p}o#J{{ zgi=zzuA!W&E4t`L@v^%7NT7L~dVJ&2Fu%xoW~n-yNK3`fga$wTw)Mt;jBMTb`TtvM zRjuT7Of`~Lq$m4_-Q%^Sus`sSbf0D4>_s3CCa~^2w=Z6h2O15mOPKFU_$xmQa?lpG z1?0u2FR8e^mz@&9RR@LR6%0#Vj%&)}@pH>xRooGO757p=gT)1V^1AR@ct#`VgYzCa zirGGBWGR-od!t=-S8m;Rxv8b~{7==X<1(%WW+hKUvq*m<8I;3Jt$)k}U49HS5~`pl zp^(s<$P_vO_3zR(GJ)d9$Np8;Y7&bF$~fR%1QWRbG5Z`fwYeGWU1xr%@2a&Yu(Vxn z!Mnzg5U*G@0ab2)f-32O|7qW}Y0(LDDC`>r-PhBR*dV6%5;)pznqD_fHw$nrrz!`wH?z*?@gjwO7!EFI@MH@{3{C$0%r}C}l z>w|3GmlH5QRdMg!u$9~#YodGTiX7HjE++%T{dO&TvwIvDb>nnMaThYnK!Y~QK2S^u zK+5MpV;z;PS5AV|<3d`cA(|*uvtrsCu<_VD0cvLbq~Ci0Bt`!_8C_fqXz;T;E4dGd zf0uLNy^Z5FHhB*+wXFdRU|o-ksw_B8m`*<$+6FjKWK2RlRJM{57fg4`#r!Cn>rCdC z&{ebkUtrJ8c6Xn+O#k92bnS^XZ~Bvo33#L=3%i(dH)^f&-T_WK+#56f;bU7#suw>? zYk&Cw8Iaz;@S`R z8GI(dOW$|^JH2_24lsgt0JQBhpmrnjXWAn!CLTk$L@rQ>YTdbWknZVu;Vc%s`08| zD0$wy4Xlxp&l}H~QSEoYdDpk+PtLLSmCq-cddJGM<{wF9%ABg!muHGDoij^-Mlz!2&B;7& zAhfU_fJ!Ku9c7W^s`KcS0dptR<}|OFlmRF=zSgY#g+$EMU*<6-O5)X~5pwjx;ldWU`%~Ys0+P;A|XLvlsrx z))+0@;$M48$H`kFgTD`PVcfN9IIo~mYewRk$Clw0ekF(B;BxTCg(7LYdScCr8fo1M z&6zq`BQ9tD)%_a@wR%Zj-ND;2)v8+MZ4xN$V2FN-46#{Lh~h{UP+OXr>P!!_emrGJ z2|bVj8)uloV+8hvM=afkxQ!+Qotj*tg>}GC?v4~iV_3dgR=ny;TbfyO<=1oJd!b+A zd(qw>(aT|Nu&cJo0$N_^SafYyp4IF7wA3A2{VuCo(KyLfX3BmCqx!-T#OX9g(`~_9 z{sx@Q0osZ~Kh+>+Q>9``d*UORnwVeto{~_E?j9;?HiIT-%8-=u8l9xHk`srAa5TY6 zMvEjgEB>0G)Mu;MiBhpDV;;xrOP%{{!NG4IjrEJ$9jC$6HRU0UD;{EkyzPwdn-0eB zmIEZ!hvZZk86dERQ-gtpWI&{YWW)eVy`5WRjVK&YWmdvS|JX%2hWnbCskrJ-nA#{p zVWtR=tmB#CdefsmMEvq)_Ced{wME9;KBYB^Py^UvCSOcTJ*f@UZ;Q7UKXTM;+^bYb z${jUH*4{>k-G?1-5%K$@e8TtN6)(H@jf-YTe-hPlQ5nPZ!D-p|`p_IME%kHlw^ex8 z^H_~(S=21KipEu>A2MRyW3MI#PR+R!W;854zq|?eZZb4_A2}1l?bU$pIB2VG83*{b zo7{Z=tZ?(4IpcP*`bynb+J?GCTp7rT^)an6c&?(A_oG!Dk3GJZjq*Zu%Ga-GH-C+L zZg@Q0<6#Bgqa1pa&(vH~^chj83`LH&!rw4OpNx<-hSEqHvgtO0oXDAz3puef=ZkYo zfhwFZrrxBRxaFKJbsdd+E>@R5z%XIhM#A;#R=*C_^;ERz>he~}jhObOm(6ZiqGwUO z6g(?trhGBTsOkrqsh0DdSQC`eEKI}@osV&8dJ!an!{1${*RV?JUxz9V7w zc|tFqXj}aK*!6L(aSKnJqhN(cZjL5Awsjz;;dcteBaqBed!s( z`Z4uniDhg2CJXRVxw02LmQ?Axurq$268~uxPn!YvE}R3~>#P%?|7aQodi{m{uvcnQ z`h%9)p|&q)GuxyVi6;tq)P~qGSEaRdjlO2X-m!|za!2?EwqHW0jAeC=0=3h3CgqH# z0>Q4T^&kUi4?@#XQ&{s*IYFU%mT^rc*PC3P%Js!l6_wQ)#mU$4c| zgV134p#x7~aSgMNA_z@dD&8NVH1G|&Z-{-+i4GY+R}@*GgdOTdu3d)P=%NU1((hcH z3%7H?y;L~So0WWv;!^5Jz z!ku&V$gg6tnn|bKU;9)8>@~t;s%K1%eO3S2oyW3ymp`)~wN<52Sx5s;P z>1D}{R3zZ!Tyh|q#s00=+eG&cu?E^(b-f~eDHqee=xSoi8_jc!N3(;nxoBH;Nd$ZO z;OGYdXeOh2IB05n7;eulGv^z!Xp^LV@KixzgsJZ#5t7h{sBD&^<%JQIX0Q`9_6_y# zTFMpI=4foysWG!V*$Fa%-Wuyc4_;dYmgO{T%O6b6rDqZvewT?>-CuzBoS$>ADY%Cp zDkz^|oyNB1@E>sQI*0SNEiLnK?gq}iV%yU58b7Oms)oK4i^*_#Xsg!xR^MXKZoJxeq|PAYUmv@j{A>e%`$XXV1HEx6t1U85r1Ze& z!JK_uNdz6PXOYsN)W|2Mv7xA`05n5)rb6PO zS4F#1Q6b7M6u*n{yX~%v6wYo=NMQWgcziahTuhdaLLr%J2BN-&Wo@9%*sssDs2%(G#1lA%5HjMjs7cN3BRGB_oPpXgzHX3Cob zjOs4~jGy?4=vMEegOA^+f2y#dq!GeMfwa&Z`@?dw{iIvYy-)- znmX>rX?|DG9|6xEVAE+;Su4+T($&4I*j?hWYz{THsinEO^*nFm_^JAx8{r!<<#Zw^ z<n?WNSFwyoQ#_!=kN@6I`gzA78VuhNew6e-=0U&$*J)GSBaPD8J`E1+)$pe2hQ`v|0>h2W0PuOjD$10#tmY~Nf z671nA*>9VD3~sau=<`H=VYp(O9r#u`=38TE@51nqaR*SyMw%$BAp`24B+F@`x5gc^ z##hwi>)z%>X59kz%D)Jp&Veho5mYu{%T4vfGqPSSf2eOvcfWlHpXc!z{ZaG=;hZm7 zcz?|s-0=lTuxkq9@OxJdQ+-X+ULOTj?qCN>-Sh#G11!}i!n0A(^j4`_UmEF7X+x;b z!$N?+E3|z>n|Cq$Zn^6ga8>-AH@>^+8*-|Fv`VCdd^^KtcXehM6LjlwC3P}P1p0J+ ze?@luwb!aDGu|YaeNlvh{&wh|*7|ezeVs-@dCatJ3EL#d(8ICsQkS6Y&zSe#+~`$h z>-o;eYC%petTcb_TvgbO16T%*uj*ZO_t^KVzCHHss%e(qRg(k?ENe|2XMRi~{ed5o zfcB-B&JG!2m1Z3(BrBu{cC>`emPvl7Z)(9-wx6nNk^*P*x@7atpPZtWxf4|U57P3kDT@SEbBw^nCERQl7+oae5<&E zIE;O~CU(mlBjZ~?a5Vi?#@vi1ZYhUpkWt+lu=UtZfOY?|bul%dcuq!O%Yb6lDB{>f zlUYX8=poLj+jI?{B29h$p1SV6V$(Gvf@h@b&V%P@czzY0jqp4Tnfk{g)0g8>^PV5} zn9Ng9tGQimPp}ewkMyt}Y$`nguggjB&Gtn;3p1t=7sNsxx=&>Wn$VXwF2*|w%_ShL-(tlZv@Zk+C_1z!EwG%Edx&w)ZM%ib1|VE?kf zIgs^f6YKr!2MQNoUYxsN^De!A`oP_f*wJ01aetf}+czN9mwOaKwD&HsFCQcee)cBrkp%|rYgb3%;5|fE`}nZ!My`^g!drY>;gbZ6F@V%eL;K zZ|S1IBNqj>)t-w#J-cFG9&2A`Np_~~QqsGRs)V$ur z*Ot#_<<%?&2f6a_4jHOa24W0qM zb4(Wk4m|f%q}xyOwff`o{~OD3jO`nznV?xu?64NGuT-S7=l|(fv(c`6=LXQvV{<>y zi}AL~zD8dvgLR&QDh=sG2=xJ&Yya%QSYSP$c;o*2 zdKq?Xpysqd_;-Z%F*r6$`_ZN{O{p?a2#yfSki@m)tdk` z0+#2M$1Bn&9d}}jdV-EG=#QIxN|0HkPTL<>v9AB9JMbt`b(S=c^;Pgto)a-$-B9(T$Z zWCVZIOdcxjeMM3{0?9MT+8;`VvU}{qhvJc^u6<_U{`Sf?Gvcy8KW8#L{Lo}jhPBeY zg_rbS-V6ol095PS57vcY+Xc2;D4m)Seb*vJsl>%`30~(!X5ws$k78RdIg_gF-j8lu zEPZ6^B@4t^!ATE%(BQeNA5_5kP_ey_oJn8Xl>}#-%@%CwifLJ2F9AE3*(r^>y90L; z(RqznCY|6En)mR5a%B5 z&Kim(`WM}SH=KuEy%WW7H#pnPs#Cfskl=%Ql^g26t`7jszwdWM9?i-VX#Lcku=?i_ z5jCv@Y_4FxIW1{luygSlZAjmbh>}!p`wy~ucRC6eSiLL`F3L5M_0wYTAEMyhardLK z^($h*(FMC-2O4u)==@&S0Gkb2;I!tcuLM&JPd!Pk!**yS1y6tz)AXfZc>_R zfks_qQC)>OT@7|fh-?ALc%>YDpMcNYxTHD~Vi}Mf@*9JXRP(JG&q6F-N*}^h?nbN3 zCWDx;FW*y_M_d2i?SLH6zu1R}(naOgU9Q_pTc7K5F#E*rOuE<|!|$?N6HcE^%o*&R z2r^crgO8jH$ESZ4LN5QikKi^Z1!Ovm%xnJopcScOP z$JLB$Slz(;KUyOroPHk@g?#W@D5xaH))M^2Cg;e>0b%#X)tuUsS-r$!OcLHth8^R8 zc(PSWSPuog@wl{Z$yxB9$ZxLTp+0+z@jZw&5{G|7d+emfSC)-Spc-*2XFQ|B>q1}# zZ!7ozQQ3*@emGvS1fr?x9o#v*5)HKZ5v=vzuETf?=Ec54K;tde80r`E6_CMP(&u2e zlDBNVaO{HCLwl*p43O(f8$RbD)F^E&TjH^&vewtvjx+LSZY-*QCfb%8~r~iw#)Zo9m{6@gZ z9h0+(MpJhvn%ID$$I@h?#?-A9@(Qy7?$gi+ml>XxZAaF%^Z+iSiHQyWr{1o8- zYuHBkQq4q-qECsYU0k7&MVXd260GjDXnYj=@oACikpZ2owMbIIN?>|K6UX2g5%Iee zj{=bCz3oHeYfT#rC@=fdI#ZW~h-;V_4twPN}j6fNQzlJ_tfiNL+9IyI7M$;_Am$EBWOxXDxjY^Z_vQ@M1V|kJh}=_gU$? zqLI*c5ymRK*L?kW@8tXsnUVttB0GP^^e#KVX_>&@V0{1=%&2;?k5jPeW2tDOQfKfx z#J`hKdE0F*hhNxqML)HE@PR7q9y9eyu)bW1n#`GXOO9b}Lj{pBNP0~3U<#Urqbqto zAhcd*u|E)`^7b-_<|(Qp%MDKJ?e56APR}^c2l0NZ$UKxjGjmc6mV`UkGJcm~?dxIf z_d9I4)m|{FgPfY~01J>~iQvIC{F#@j#p;viLf&gfDG97+mzXT0gEz&qbQi*Vpw;QI zCCjzJ*71!F#r9XwYt|z_usW%zmQ!U!aXC?Nvc(iO1XhX{NWsVEQNxtbGnjp};PvHu zVM-c0=UV0TRIbhHD#Vcmh5-s$LAzy$R= ztP_t7LXXq&YVrONGC&)=Cf@pd-y_~Tf}!X2Dy&uU-s7`Nv83bYLLLB1uT(152S|f~ z1`OSxPtD^FrSGN|HAC!-{ixCDsVfN{3ujC_P?2316~xJ+zCXV@T+!A@nEq-RVzbVG zAL<$cs%5YSlM-KZfTbEy(R{0qatP#r6l8$7f7iVsP7+R8_Y@531I`2!YW9aVcRb@$ zMhs7gp&xW7fW}kmrk{Eyn9Y{DkwKZkEIrx{!>Wr4$>>H>CuSYEe`((F$ z9HSaH^IQumcr!qdRx^LKFVM_X8 z-dns=;&Gers~}3Rr)n4e`(d$8TdWr4V(hD0m!6kgUDOOK@Dp%Vw~Dzqvb-|C6gcV^ zgLu!Enf2J_zDp+6ml}fuGvGvM2kZZ0eYr81eXCcTf1pqemP4o9NRIV=pM>@b3HaZk zo(f|2ye=~PX^7o`AFL=-L4!`v2z-@M^#$V|A$E6xv#(3DjT+rgZxV%=Rb^~E5L9cY4?)SO>>d3kGMi@r3Sr?x@xc1^{(P`6aaqzT5D z@eA0ukBJT*2-&VBSknfgy^*KD4lUMKa7}^!pk=H-&a>N(iSSrFZ8ZtBb^E(QEtS3X zn|3YMo2*64pG^9sy)nKD`@Kc0hNb5H1z2uk8pEp=b7s-$rS(qPW+eTo(V#O+XJ#p6 zJ+y7h79aNQgCbRv%dWOlt%P_W1;xE%YblvdmdZv8b`>dj_nrfrO3J}O7Z#5K<}tSM z7;{}PKnqQ}=^z4~#a*Etd__><28w-x%wQO)jVa0#<|BICikaT`V@(yQ^+8zU4-jp$z&N5c7D96j zv;$r*=QSo?G6zU1(v>hHxQBZyi(cJeOHKW=R|9`$#3$H}*xqlxS~jS*L$uRysSDq@ z1h*#r{eQr<2`@=Ajtb;yaCt3~V-6i?%v>Fmbui^1PPxGz*U^-xLT~tN-ctKY) z*x9~*NtUcqJex=ltK1$r#iz9|H(0Jgb2Lq!;09o5D4ofci>)+#+%9Wmdz`CLaBw$Kzu zGJaSpp%-gF*SZE6J8QvogJ*;(2h%}CI*wkprQZB6elA=KPYETjCJ7p1cMtnvzdNRc ziQy>;u$nu}nNJLN{Nh4bksMtf*1M{%l6Cn=7;txUTlx%s_}cy_i^JIh3)0{n`H@8mJ96S}2R;!mQJOL$huFJaQt(hoacwwD zpHQ^K_AksUNz%Ph~V2*wJnBM1vw@BKkavqrGT0vk_T~ z+Z;ewT8JwjlM|z~gMGx~rH%)%Kgd!eNd}HWpnbf`&ot@p`hVgblS&h3o%8E?;+{3? z4d9a|+Bn4C9B|8`e*&5JlR3@sRoLyu){K~p>l}_nseo98*gs?JM=bDronL|VG0VAD z=NjmNa~9Px2O>#I<~7RtaC-mj1Gwz&H%ysOHZZ-i6L-Mcew%15+<1G5#RRn)zQ%o={f^lmNiG3Rb+3i5 zonvW_voE)fY5Qf*3a}vl%$MFvJZRbddXeQyqEV>Vo9JJKe@%v@lXzXBPRgUCq*MZf4*! zri`-8^a!@#GWz2IppvZ1QlOHF{SHHkD5^+XF9c`=E5Gjh4&9c z%-7r544DLLWBLP;;#Tm-UgZ{L!kQF1PXxI=EA&u@(wZ_Lp6!3Np_AQMnvzf-B$lLo zKDNbI(mi3rI|o~5=W%%Nd=**qpdX^q_h`ROuoiVayjS&ABtHUPbD@7YX}S8h{;#$H zEJ<;HnyTJ}Z5De%(r>tslLtNJ&xZhS)n4XfSXL*!=j6~^a@Q`rz~im4fcg zydi5+tYKfDX&3Y+1d5FNU}Ujf;BdLr{#p%t1eT` zlwf;mmWUJQ$#5LmD2Pcoh`eOhI}bsN z;$T7utn`I*3R&;aydovoQo-K4DrCoPWQ&bmqdWonM&R44sEz!ahLx3cAi=)XC&9R2 zg8Ek2Vtdx$NQULL=Ia1=)=%50=$H(hhTZfD!s`^m zX^Embuv$&Q_3KlkYkuJ>`OUb3%11`PKbbSs*Eh#nvkbGV!@?%;qUI4ny20; zycWDaKyn&etN`cESy78@vB9zYvt}WtFa7@dg#iB&+ybJDovkK<)i)=b{kU7q;|s~h zt*kJtP|MD(STMd(8b55aZeO)?ZRqJ61D4aD-zcnky{=HLkaYdil=0}7t)D11Dp6rw z%Qt(z!!YysUkMuF-VL`uI@YAT3RqQTY?yZ1+y+pTmnRWgrzb|tx;KjUBv9aaD|5KU zS8$CaGX?B2*v1|!j|6H5OH4Xgfr@Kz?^shX_rZD8(4A_21Zl>Gv_ds_dWcRjj`PNv zD7KQ4Qx7;TLsmEGAV2QLH77t4w8Pjy)uiKKazblJ zq+%+?z2FgsGd;+;b>1c_-9GJmY^|U|H^UbO((Ql?fZFt4DC!@p*a$nGy$<*&PEeR> z!Wu^p+G2C(K5Y-TMAPk?F7dlikoa(Zhnt!p)g@OCrNeu$4quvGVVHE6gqe?vH;ttU zwnZY2)L?%En|n(#E*$Q-I>%$*yy}j}99IM14u zT1s&{EmCZ!^HfK7Vbx5d8+86 zrO{9D2s^5<2gl!OYjSDkD!5m|uT^;fUaJb@39w=WWhsp5`0NuKEG-UWKYBO@Zb86m z%qVGJWujp%xLaci)tGv%g{K`Y+6Q1sW}m<6y+?~VNdW8|MIYGnCedRGS2;Nm+OfeLeR1}Jc~FDCE$bNeM9{p z;o6rMn;uC-d8c`g6z;Ko8T=I&E`wQ#ix=Z{1TO{}K)Ohb#p?I)nuy>|$od%9tq_1z zez3~)-lM)}=P<2xb`{vSu?Ere;XXX?Z6|xmCG2@JAV78kJh780_&UPd#bK1-k2*t}9)%({ zuS1a#tl$N(ushwW4sho8#8biwMch+4v4E$LV#3J&^F)d zf-j$RIWpr46RgRdj@-2hLnL{*J-1_AZQ*ytD>~H1bHxja8Z+b4%z{sBs5lPuTQ;6X z#bJ5bdu;%Tb2cMk=ww^UH0>lJYTa} zgz@{rt5&Yu>Wr7bx28lt%{WR3s3bBo(}|c zyV@C@?TB+sCOH0|wn`FOjkM-~CCAA-GS?nTw{7uV9caX}ez~mzZUjSqEpefY1K!m0 zT@JqzSlh!|z&5Z-x}bglH`hTnWxJrm+5~@pl-Dn(eUG&;UH9Yz#oD}cg=*`h&Sz|K zMGeK_#zvEpBG?-=STV7MG04bkPf319Abw81W>O9A1KS(LH4_^c&`7cV##=gE5M8Ol zW!Rp0*Yf3sLk7D~P(Hi)n7CBK{!}V9ZJUFx!&H$4k&@{9Dw#HG_e?%7!tLIePu{qye-TPk zTi#^1Z1Pxsef{MPt#7w9XEmjlzOMWktO)Ms+ey&AS!=O3K3)6()cph41wgwY!5#sz ztD}$y)`rd9Zu;_b;Ew>gG`3Fy+Pa(7`;$Tc*UdkHzM1vYRsDWAi@q~mW>QWAt8%gW zD)fR|8?!&ZgK0T)Eu3}-AR_Z+eR;MhFvnB?e5qaH4m#r#!1cij?ros#)EVO&^iTrN zhhzT|{cUTa-|F3~MQgSt+ENd{gSKS1~1@h%Qwzhdx35 z7Uo}{pzkeA*(V6^zQvzG|0GlPPf>^Z!Z*e0Z`;VyY!Qw&jkiEsw4=(^_tr#Xm8s7M zecR1PJ>ZBpeDet70j#rYj~el=u72K2TgJ`Jz_c8;A-n;9y9Lvo=cx*VYy@5MR7nEw z@>VMz%|S2yAYXOwV^h~yCR|In{VT5h?=PO^+R(p)it<*Qdgmav zpg}BO6&vBz92-S|H+KJ^6m*kYgCY!t(=zC-zW1?rop*wwKtfpG5MaR)IV|{nfW@ho z^ZXY^c1mWK4H)d@Ujt_+$vym8IC}TlyVy>Gqn{vZZUVL<=*u?jk=D$wVWvag8Rp6Z-(}3MEek%B`kFt2LAa-yGc8w&xqT!AL4L8n8E#INw%SwrEx%Fm0krJ@&gS{L`7$6+3%gmXLGdCtc0 zDQ|#o5cEzazF9`9NOV5~z6&~kS_yc9B|({(-zt276GT&vVl5>(l-c=&O^3J@-#WyO zmdbf8!;rqG&Xm6a@jTc697IPu&lyZ0tb%d_sxX@hB?P_~g*iXl zA{FgvEBt1Unw*+RQW8@aoD0fm@_Zw6h!*Xs8>ijW2Qeczha9|h>g)#$SPwQeghN@C zo%ig}A7s{P&MNDi`2T>!m~na^sJ}6)Hyv~H-6&{3;Pit`E&uCF7nG$?lj{7d?eJ%J zNT%MwE}%fq1YDGZrQqfA_wkxp$5FSOICh@MJ5S=CO!->$X-*b(iGp~%?XNnc%uEsu zL4nOUu7QzuLWD)VBeTxy0@zvWPcC8-gvEDQWje($wmsX#%|>0w1+x#`Q3=3II8mrmcAiecbF?a5l7E> zb-h$lbPm7wsCBhZ8Ed8c93qeJLkL~3c2;v9vy zIE{oWr;e-9X~&nak1c4p_T~|_aql{?rH$-g)*M(6XuwcV^glfN9M~3kD&IP;%W2~p zSrbEIJcqx2#2R{$9=u=_Wq&fP*^Gn**E-TjXeqjq?nL_2G_nt{8+y^~1MKKdbi#m3 z6o$SbYtiu$wlqE5qRiQap)^?_;uucJ{kwQ*0}Y*)dGKY93r{0QL+A!H{&aWhBhlqi!cpU!hu4~-teM@;dM?`d?K)Gn7-xY6@I?uVb!!0pQpoy= zp7<3ENhLSIVO^+>1)$zpDg(=T``=4lEL>^N6TxLHIk`!+l`g3GWts z9#SiH)|aObwSpd{I|p|Mu2vwTp(0C>oo-JL zvzn$1;eEpUk+JTV$cYxH{%fxJNAW*KEZa=h6hQopM&+v{^ta&cd=>onD%9?CoL~t#_pu$i*7*Vk4VMor)81&E-ZML(&3H? zj-v|3H=CMi_6$ARM4yw*n<375|Z#CAy*C%4l^!`G)p%#fXaIB3VUG=R6kA5!>zVP)YT)#CDm-?;l zxA5=nahEI+tL$+pRd?ewRl_~n{iTQ5c(mzGUcZo$5LtRr5yk@$|V_ zj9)#(;zAr6oOQ4#0X$1^VyVc=To#l~s>Fkpvndi{G6zE-{>r$V`b@}&ZF?irqD-8GHYYi*Z>m%poSBCb&_nS`Tiup0{h4RpyY4Dfr2A;#9!&Mxx;*~-4XLTx z@fVVFFMB7;ulblDE-_iSGw-@tAi!nq{kk}z^J^8?1RMtHiSTXI|EazU8s|Is!h>rZ z6fWy@wk!GVmmG%qR71x|-s9p^8^0OmQ#?F|>q^Y4g38-F`&KXM{o0wE*4NH!^mr{m zI^V{n@Rg1Bm5uz$bzMDS*1>(kopmjBghqF|I1%jHW-Kf4+Nhe^mrd4S&&P>$+fC2Q z&ORrXUw*X#_L&2|eV1B-y?+j$&Z6`GiG4*)T*P&SI|WW_9v|1sk6PZ+8o9}$bp9_D z8=LU>%)zeaYnIkX1=uIH3e?tkfotqY$ggu@?N_)R`DLMzIEV7IRCY8qWre0l%l_0o zWyeMa|f^vGhFIW`Hlh(5POmZ!1UfniG5+YxQSN zOQor=kG@VnFw<|%%c|U1gn+Y7G#R`Rn@La9Y4gs9Q2p@TU#D%edgap{ba7#?k*#@Vv^yH9Y-;jvB`onxibZ(21FF~r(?Ufs^BK9MY z1UOzbiyjSnZPM+lA@n2oe?I6jhs@SHZH~=VxV`?00nOGhfzZeF%h1mk(9a)o_0R{Y zii9ZHdS_o38gWlo>U3Hv>1-?Ck7Z1Sv!9$Q9_zyx&}dG!1kRf11H^!)fy19<3WhgTnV{|Kf@d#C?E0#S5YoKR7tTcHox?UzLI}@m}F0-73Udg zWuv9%G3|w9btAynOqrGycZzsDOq0uL7Qt)}v+HAYTGtxgkLE2Bn6ii{947&K!uWrh6c9R)wfjlhTZ>h|3HGKcnDcKr&opcZ@pMz_zk6CD$18u)M ziAUkIBV!RJU-`wSLgO<(jM(rBbv+t>tZ`W9#D|e6y8A#v8A*)ZH z!#iR{1bKi$9pQ7#wPyC+b`eAhiLHeaJ5?eB8Tp&{2;Pg}{gWChhzpnCxCHpZ@+J-Y)s< zi{aC?>okJT?lEUTZlvRHV4CCm_wcA*Zp1^w@qW;2qO2%O;5PXL(xiEs2(&Lo* z(5t`yU+o(9R_QpE*;2(257+5U znYFBll4H2QKJpHu!qJ9nuVE~(k(3*z8)jPhpIDI$ZaT{(R8sNUJb>;ph7Rf!M&Q1g z1LbcD^H0Ka66XKrr}tP78Td10k`-$wpBuhYufry570;z=1_tSqo2pL z<%)*kBcI6tPWrh%$T&%VMN^Tk7zjnC_mwCae=TjxVeqn!KMQ9*sp*;-zCF?R9scG7 zv;QIQIrf#$iN3(@bORqT{QqrQw!2%J9N@c7uL4SdHX#XZ@CB63-Ovs^hpsj)Gq--P z4~Ry|xuM?raDDQhM*>9oymiw(l|}m!(`(|1N4HJNo+nL)p6r zL{;Yh4m0DR2sGkA zVxoyCZD~w6pDg7fh?i7yQ>@jZ#*wVG!OMssFyGhvIWvOQ{=VPeAG|p8oacEz*YkdE z?@I{AeCF5Cu#4ujkOltmXvQYds)0qd3uG1FO!3xXX20!*ie&O?Tz5lxhO{ep8r5Dt?ZP(Q(WyH4{(tkH5X zBsV%_e%{VjLzs1%#`x1GmDM`>V>S<7i!|Jx$^LLvdAfsSHS*{Nj@$E)KRl^4z8WD5 z+pwOKN)WwcKZTU$ukwd97FR(M9Q+P4TktyZM}N5a-GXWj!XKfTl8c#eJN&)z)mdy! zCXMSDHoNq{WKtOo!=QQEns|SbXlzY@u5WH@0-IO%Az`4Wwyu;o0Drwi< za;{bVQj1h{&V*E^RvA)dw#uYb4{ThM=1l2ngmUHPk0&a+tRVO$6D;Z3s{fQT~^#wqAQQihQ-2!?%5_A zoSRvV4O6ALP;Yb3iHIAw12T@dSyHIO{}(jWKbA;2wU!(;`BRilky4~11@{-j&TQ^n z9KDrN9DY|yVSd*~|Ibov;|9>5WZ0?T&n5K6ezC~cKSfe(*F#e5vI3NMBFZ8wnbR9v zz0p_iLMgT|QHtHL9{2lyH0-RGmCWspz0)eiUTl?ONB&WY?ON-elQ|BwyWT*155YL$&q?LpNh53eKf=oyZ}^84TR%jKExeBQ4#zlA zos-JVV*mJ-6ubN_DRz3e6g%=&wD$neR&_~z$zrcrA;nHwA;mThkYelq2kjl&CG{iA zeerT1y}P6sWy?S>7%Sj_^Yy;+oJ4toP#%q;yk5O>Fl$Se%ZrPoTox~qXha4{DO~BO zZ$Bm%6%jGrsTPi+d`O+331{hc`qGszB3i!u1EjQ(&m(_T)u&j2WP%OY^?|+viqQL@ocZuRUhaugeD@seEtmn$HlGsMai|xvpcqXu7B%F8;*hmFBUg zVF&;8_?t<>K|`5JLwoUxY-u+>*hTYSlu_D`TaAtC$+Qw?r^2*nkitE4FZm=G$o{^( zJh7Lb2ed1q>h@-nguqvijWt)A4P^>VVcjQ>$#*IR+?sKdOb1M3EV3g0(mM&X_ee2H z_JjSiMQ|sDj;dV8oFPP zDs+cr&I6BwcKN%Ujw|O}|1yBvX%<5wty?r+#EQbA$^CgYyNzf15pBD#o|;?vMSs#}V4#X%MjPaMh&F`d|P^yuLVXnYtoWG9r)P<3M!x2(&< zxQ|R%PFj~oa{fCKZAYv3R6Q(uzc8NLjM=bV<;ny1x3mU@F7B@iT?Fk}^j&|IC0)#G z9ngRAFqLR%y-RD(z$tF4Lckl$G6Tcte|GD@PZ#GRy=Z705V}|c3+Sx_M3W;(L(gkk zg9a}aH?|Jco*u;wvy2d|12VYTCP-`E3e~UPC_YsxE5MCh_ATr6>|mSC>)l~%U9Ac; zK#pwiVpwU})}em1RM7J&QW~Uqq*M`=Le>S(F#Cd$*jb|O)mW)_Qu{+%IGI`>tK!az z?DNi3_15L%A)yeO>|EE{)>~IivU5{wo1mBIK9{n3qjO$qCwl6Z_Vip%o6(@M+miU% zh4$v8_@N}jqUQBSu|w!kQ{GW2y}AFkW+PBD=QE|(3o@xzwAc36Y;knon*GfYikc0B zCEI$oUwq(Hq@yo2J|$E?!lBW}r)YqDif6yI`3#JfVO^KRo@Wk+~||{Ta8CZ zwz189I4UK1zyH*jbTLV%$fy~+hQ^iWVBb+?z253AP-xS~!v{(vKWwqJy-|hLuj;7n zr97)LMz(I6C~pq3EZ%Fw%1fP#5-yjCs&<6nS{37H zEM>Na+_M-XGw2@rmDd_1u5J!_h@LI2AyR!K7sDTeN>wA~w1#Mxd(cL_9;)=ThNut& z+aZs9L=8Eew!F_C+WDF#r7-Z(Q2kMptVn#Kl*6hPeT-dTM2A{Bla{_b+!xd6>OwDd z>qgNC<8*8dgA;J|)HpPgiTQ=&K46XoOKtNnchN$2*il-`rJA*uB95X#;=Mw(fC_{vik5=gj^# zQ*An=yYCZl34AMzvq@4#T21z?34Y$T2D~jYKx5Fub{VYM5`SCRkG?QLbNSbtEjkCB zjY&r_yVbaDFC!_W=jYRH$kiw54y5<@Y@-xHoULz~lUt=YS8m^jY1=*Bmc^o?oy*dP zx`;#LM1q%Yz9%c~={q>-xBqO!Cg5(sD3sv@<|B&T_iS2vm)?e*!KvSWr{92g6~F(^ zRRo$R{qG)q&D6Z@^p1ZW{qn@gQ^(I7`{u~EjXyM8Jor=XE8k@f?s$D*LC11=q*9G@ zfE#waIm+a@xlmIqlL|&bsvPob`!GTx{@C&U*7t zoc4)kJP(i8zIj-M^QR(fYBN6(7EG+G9^njQVKt;Uor_(!mz$g$`;SIg+z#fnt5!ij z+QmI>ev^5 z;%qFVmi0Yu()~Awq>#esqyXgbj#Amtdj=ytXX@k8gYfh)iVfWqwg=Q~sym)7x#_buz%W7nuu zoz`uetd)0ht^pH2^FC$&;VR#`L09^3j_9P{?fA_5u-*AM_xDZKPX=(VyX_a=;5s*1 zWAEndPAg~q1pg5N>UV?cvu)?mFPs0c#hQn5qGv#{$ML?m$G)na^-els{bSQctM=i$ z8GXA`s#EJ4M_hM%Uc`kzQCWcqU`^04WQEX zY#{I8=*`LWK7F|T!VCOAwoLrM`!IUpAINLC-E@`z=G~4}MBiFDgtO-5U87vKZLwc? zozs{*59Lnd9s zVy<#ctm-VkV%gX=9U1Vraz(35yt1uuiPB%)^Il)FXNxa6w)?bWc^LAGtBGj-E_-TCsd+T?du-COm9h zA8FfWD>~S*9Hla=uA}rfw%Lwm?_2k>#f~=5nziO2Kg*u8i|1#2i_`3vdHC46-){aN zUYA*pHD`V2r>#Oir}kd2)jW(#*?hD_ly@l)l~>1 zj}Y*at+LSh+z@Zaa;w>VWt&Z2q2kioV1VBEmkJi<*@n{=*FiBjAdr4H;9Jlb+qMm=)zy$ zW*cc6S(uVrGHuW_b$paX+jE1@dc-m<7aGD_PpGnkWK-XJ*(R>?b?OIs3)^Ene|O`VJt>@u0N{a;~o6Z23`-dq(2Ko=Sz$dIhz(LcJj$qv8y>nM+%en8j+;6|1=p=>W?u=-g`Nn{czT zfu+8oyKh&gUAO>=TkN6imgff+9GEk9?t62Cp6p3}CFR?c*)tBz7(4U5nL)EUpZ*(W z$>_i!S^8(u>LA&y&!QDUvJ89LlJV-Lb22&03sxm8SFPC?7tN_<-JeVO((GbMnr+l= zhi!))_`^y6+1ip?-A-4hZilDS-LmSRFF7VY+xFeWH`~4=9_grD)$O{A<_AY`W;bz3 znjZ#&%R9EM>aGJnv@4$A{Bs0&)+(AOh+`rCW@#STxP`b1=9*RZ3m15zr3LSFRIl1h zw20pmCm}vd{2tbDIL#vGi#x8zDvffZUYL1wO>F8S?*7j|vUGepap$Vd#O9F^XGBb0>^hen#udS@i| ze$%hszxAv4PyOos!@qiex9hHf(hRnZW{sOX=8WB5%owiWRFcju_8(-t_J3&xdxG=L zU=ODcnz}{&LojF5Yu`-4j2*^{1JszKGdTO8q5Swt?Gw>F&F`~|45QWDxPAXmYl^bo zHO1Z>eRPEhr-5}#?+K~9Ha4Pe>1eDdzB`1qz!a<~R&ds5AA)uGRh-p}-_2+6+#XLW za{&_A+HcSB4jJsxFH7R&@cLyR;N*wAS7)?eP38ED!`8-Xotz^LbJm+}n`xJ5U2{|Q zx8*M_^I(;2>$cw<$ZM4wyC>TcH)iegW;-F`oX z-dEaaH~Mv6*3XV|FD!K0x(^hI#p;>%;lJ=$?fuj|#cG?tiHB5^MT_ju6VoszmSId_ zKY`O`mBWEC@xJ#~Mqb8xN^3P(yB+`Q{r5gwrPaEos2g+UXGDKr5zT|zew(`O&THJH zP1gS%#7*4i-DLmqP1vh=x0_~B$EV)6V*h$KKj~eorI@HZwXaw^33DOn#J>D84;rve z?Vx%7QZjE{yQ!N|`OJH_{l`D?u>s3@qVL${{cOICr301Nmxu6`#Z+U*PEcPAt_W&G z$A0M&`d}cB@P{c3ZHHK~izo8bhL7g4c>r_5pt_C~Y$mv2wIt*Bwqo*-0=~EmGeVaz zLz+44nhRWP_|N>HI=lR)oW=KOo0oBvo>97vUp)P?HYZ|^>A>8Q2wtni{IYH1^XAQK zb)~7iE+UHm!D=@*%g3(kTvL&=&u9-Ug3V9a5I^_B?5R;b$mU0nG-nRwC);98zFCrm z>9%V+e>GEL*7D7i{n$(?G>;NBizA~srA%I7>5Mh%rJ4Swz25~kx0=Ce2Tgm%lr;-_ zV!Y;&{?kY7M9u@F6#=juF^qe6W7I~G`$e%YSKGb}?bs||WQz^B7#C}~06zQ>o1?YO z7&n2fTIU7+&yOE#DF34K z!&YN|=Z4Ka#QDvb7fYt?*tDZq%rb`xfso0k^+K0jd4bcW{TX)GXkOPYdmHn5BhBlB zRX+%UJ4$-2&gT}ec|8dfNKzrynASEXtGF~;9Uyyde>4{$OWsd8_}|mB3UO1723iFy ziOe#Tt;0N*qj?4MTwa;(pa1MUMIMk^PB#4C=eM-B$!s>mshCR9Rh$={sggbPDa)fv zn#)q}n9IImbJ=O%T(-M|=Ig%b0m)TWKsP)@A&P^3 zIIY{ZVO^%;)=iT_lMrfVq3|;S%L>wJWChjxD*E^e>rM0;R&(zwIr&G@`->I>Xd@rQ z!8#Q0g}{<&hL4mSYuStiKRXL$Uu@q02V`(-(^#sX|CoKONrsFwZ6+`ws zX`in3+zjr!|F1ONPJdZSujKZwv76MlDd@PWJ1F!`?N58?((mRy`p&!)?~t1Jr&V4Y zvxZ9TP{VGXzvSiW82LOkr^Q)iM!2R`o;I4B3hRIVnv}$kPZHO+%I49%9v7B`6II-( z(n_y{`In?Jp3^^?R8)&wV4O8OO_J<5#h`c85+D2G_pJ|wOR^B@&-V?71Ro?0;4|{M zUI{*mOQE_C%b%gRQjFGT3Xg4g+<4Rhdp94#S6o$(xbTLIei>Epoy7TjZS_VVP#XZzG_wika20Xs2ZXzh$FVXw!MH&4PRN^w1EafoCWQ?)k zB>0(9=&Twbn~}m~aV0Bc(jCFn)%6Te53UYjSr+|Esm9*W!M_FBoK6Mu~4 z=cIdMx-~9}gS@hFXxeP@h3C^jmb}tYycP4lM!AmM*1_bHU~BR({%3U~%5Sg4)p*PVJH6e6T& z>HGFHg-Ij8IQe$3Ha0$A>?Dh!{_?;VwW^mM?;?$mJ9;o1qh3Ml%FI~&xM2|ojfvq ziw-g+ZmxE;9KG!3z6@aho0>xDp6E_Xb6Ta5lTryK*GR9mB}Pdl_SaRCJhQQKUMNPs zO>I%)Oo6i%(*3is79o3<+#@~#{V=9?D@monxAsQd4=R;4^nT9k&Z$CA1#C0GTQ>_g zRYR^{x#G0DY4;jl|NZR~OJDh|?xv=P`6E`9Yx14n(VhJw#Wv={2H1FinaQ`vr99#? zxW_Vz8&v!xv^e#LUxp5Lct2BuNCj-x zhuker6QXw1RT!Z;hj9j<;}hx3x$S`+kZDqMrTFTWXs5WLM=Fevsgs9ikYrNBXvjiV zOY|B(MyfqA1EIqeL0ZS8n@akluwf4QLFiZ{i&LRh7SN^&FG1SCb;EXmQvEHYYN<)C zFz-N`!k@*KNzf51jUQvS+U|jNsaBcLMQ=1GI(_|DmSVIm2XERnQ3oBkK=iE4-*<`IDSmz`N`o+1NbZvQE^!Biw?Uq& zzDu0j#=)2Mpjh)ma;w?GRjLL;k_0pE6nJ*1L5mRW2ik|aTbA5kVV3gLzlU(*hr#V* z+53AI4`cjswh^T+kg^u}(}Bl+?5J=x_7~EScVO`69J!OUr2e*EWm` z=u8HkKP8W<$L$sIATv*Qoz9%EZj$8~SfuOGHqVW-L1GQ^8$AK4v5+{64J+Y}Mi z@g_#0^Ej^vFn{B1o})oov54FstrUq5Z#PdTOE=B+n86`#e-}Z*RaVaqc1@>=8TMLuB*5xV?7$xu8_V|nyVz~bp8_ENk3af9EdoW z4jhWU4cv*%_3?z&zBUPRx792oj<>=ieLax^bV8?52+dVVT<$HyCfFyOLR>bJa%scN7BhKuvS#Hu;vnY5JVtu*Hm?Q>x6!6kC z$RlI*3>k;Hu)b${on$qDTD>4i9PHuN%yWml7Uplo-d2zw66ET%FYuQChyN`kn7Z&S z4>caV79G*0wY76%nu@y?UHUdcL$cgiNee(si+9PJmDKHk$7F%Z@RzYvg4fPd zS|eh9eS3@-Tf|C~!BRx88^A3~0=zGvl^xq8fPb23MUUE0c?T1NDDMHz*}GCgEY zhGwKK4GCX4QKjIPhNP{e6n$xV(w@FO=aosCDRWwe4R%4go9SdyA4_9D{j58GO9Mvz zdVlTx^`zg@SJ^5^Uu7%LzQSD>5BvD;;oh%MswYNd+m_71m#HpTAH3>lr7sYcSj^@r zTKMTX_eD{YR8AT#{!vz89&tQ=@x&+&wkjy4?G0MBCv{h(w0FY&&tELfPkY;e{II8#@kvDoad4%=s!xX5uZDZj-(uo?A%JL0 zD*$&*L}cv?NTo6=5O+xS?NkCNxNG7@IHGY^Xl`uEJ{UNwFe|rV(4!LtUYR3k1-)#a zd0*k++^?p|o1?i{rH89i&dVD_RyS>)9)C%Q4!)bW8K)lAUWac(dcLo{2C|=TL+VFH z>rdUbi#1eBc!GG_uQU1%)@RiF#F1coC}!9(t~y?RKgRVBcWckKFK z;?k!XaB>5!zL;@Gv}@XLS>L=FEWWAb@!Y|l|3}Nm76tLK89TYqhFIfi$QpS#qtdSW zh1(4KhHYGI)gkUuo}g;*ik?H<-a=ibz|kB_vnolCPJi^ZuSY*BA8q8xA0q4@-B%Au z07wHwppGLUuQ>{Go3I7}uZU6Ew+zr(l#f-wLP-K-YqDWEqQ1*GJAE4LaVkUPs4r@i zi5i_oja-~=wTCNAgx`6Xgo5mLpPD{Y4KFTCj?btJH)3rq=m)qX^6!T|k)>ioIa;r6 zoOr5SVO0qypgj=}>!~tH%FMjAk94m}$V87dA%=#@|1t^R#pS&*{3FGmTUn2kTY9KF zVowz6zlT~Of?*89IEHf=E@ZfbVJ5?ThNTSmFsxzN#IS{78$<46q8!37f?*89IEHf= zE@ZfbVJ5?ThNTSmFsxzN#IS{78$<4YS;-m3FpOh3hv7noOBiM{%x752a1X;8hD{7x z7`8FwK4m3m7{M@xVI0Fb3>Pw7!Z4FzKEqOmdl=R*Y+~5Lu#I8FgN#1Iwx#qO!=B?9 z&SAKa;Sz?K4D%V5GTg(khG7%K7KUvMxmZS@VFbe%hH(t%FkHxR3Byc=`3y@L?qOKN zu!&&{!#0N8Bu1ZM1j87HaSZ1$T*z<02S=P+Eza0$aqhWQLj8SY_N!?1~A3q$Tfc=Gta^3KMU zy_^x8^-kW|ND|QHhTa&z=AEux67MYA?~Z7BY-zqgd@etd;dq96hVvMvGt6LUWLU_s zjA0eS28O2@x)^#G3dG^^!x%;~9M4eCa2~^Sh8YZv3=0{SF|15u!>;=!_y313_T15;&Az43?mtiXQ*d5k6}8)42DLAg$&CWRxxZ~c$%S$ zp@(7E>qI}2;dq96hVvMvGt6LUWLU_sjA0eS28O2@x)^#G3QJkZ8AdW3&rr{B9>a8o z84QgK3mKL%tYX-}@H9ggL*YTjJLRIQToE6dQlot`l2iKZK|_O3&`g3qoHIBjEBqt( zoCRDqn06`F#&$2g>(0d!n z&uLd!c=%@tw$6>@G)a{?_TqbZ$jwdC1_%#}YcDFHjW!1MHkcke^rqS#I04UE(3#5a z#{O1jbs2ekVI;pR*X|k5#~vwz^m2VSymsYYgrteR(1V-tJ#p6NF zmD-o;zOUo&(8MHL=48b|SE}T!?@olhG9~ont(`eM-FZQhYm!yb7OY*XdU!%kC49Tu zH_YW>Ij`o@I{R01d2M?(|9#SYBc#G*kUc*TAXpN05yhOYbQdUn<2%Rj>qq*nmLvV= z@Oh;0$h1iwuXTAiSLhTB+GX&JXELf*ezP*o9^THa%q>s`2(zlK(A(wpe{**%~WME5OnoQL>NB>w-z_1B}fg~$zQl4UY@>k7HEh7`O1Z*HEXGXj5(6em2LPI82UeOth` z2q!M4#9H_PSli4M%@EUF+}Ro8vU09unzOjM2O9ZEDOoFev_YJ=M=gDGT~tm?*ak_r_aI*uC#7#n`l2MiU8^%?PtG?z zh7%*YV`{hn{mXy)b(c8xxY{|%fyv^ldu0nAHJ+5}sI>Ri@gmi6i9;sU@$A$Au+;WM zt@_~My58E-6Io@X)}lI7O~cVzTIHgThECP_yk^Qr>lJ^BuUI3(8^H#&-dXOe8KOri zC(4aI@ZP9ik*3@JqpU!sE4``C!aea#rUxtWg>#R0e21Hfo zn;|K?r{mUrry8Dn4K4IJ;AtMd& zzDH?R&ZFFQ+jq)FBuTkD-%xeXsajRFRrRCy^1qxjN;|Jq+ZNOLS?kuV%;JjgF7E~O zW%h;Ahe}u53KaY$$hdk7j-7kt@-1k+v0HKJ4v*T%e;P%1j0Knd<4v79Gu6GP+CnjU zPpkZIX(U1y|2>>pGM$6IBT{--wgooPbSSg-N<^JvVhA+!Qx5FcgGF5YcWJ-L#rgA>p9Pgc~v`ce)XbZv{Eh~ zohT1{=4%6P_f=s@IFas}VAKt}-lZFjHXWnr)(r&}<||})Gv+B3&Zh40n<_^Nu@u zTvh&9x&ByGc7Lp4Qmi9YGv^PQU*M17FXL$G?Yt_>?U6$*(ABsIrg>a81}mXTKk;*9PTF0qjg~w zqV3ra4Y-RqjF2y@YZq@;(Ys5Ip{aDH{S|JO4$#^7KPgUF!_Y^IPevK7#zffQBFd6< z1kOwu?MJJPN$2Wteyx8A_Gy){%#5$VaYtR|vHPZF(8B!0Uss;Z_cZfVuGyQtR0qqc zu-JxE&ea~p{RZuu(9dXb$hICCVubCbg)viMzk)klBi-Fd`R69BnCC3^^bDF0`>L`z zsdc6y(35X^;_GU8qyZLaakmu`@_>Q7u;RUYVNFJc^k!r|W;vX?!Xl_Ur0qV#=mgxx zgYOUzlN)v%mdSUlkN>7P1;`ExVhK(TTaT8()8^c z*bUg@Ho(5QzPM2;VWf!B@vgt0wkC<+J3>~xCXKo1svJR@ABY1z2oEu<&2#6^oEI`* zJ>JErt6v<}7Z=my9H>9H1l?e@JQ^3?b3+w>Gj zS$6r$?Vs!M)tPZ?>U7BV*Y;_>rd1V`AIj5xuFl{7?5l%yK=5L4ugNAVHLVk2vE@U5 zoW3-s?4TaFlZHB#^!|2xu;Hl4@}(T!E$ zZUbB&QZV2mm~|ChTa%d8=by`?q_0F}3=rURm(9>aQ$>Sn6f`n#;cjFk?nXu_Zb9}8 zG`{L|HwAkQqwDEbq*FdRyHg&OfYwlSR+z>2)PIocFkhz5ov%s4dTA82u%iatqWMub z47rYZ0W%=YOqlcYA2_G|o!J_cNfWtKj4zjIF=MY>JH{4frr9q*HNiGunvfVfa0mD0 z`T+-Nwmt;Ec;Ey-l3sEozF@YhDSft!*ZV`*80MC&>UOxfrnmi}?uEhMT&Y&4ib`Q1 z)soe16zVyW)$a;=U#eeJHtLqpDHf>iIsiWH{wf`_jc8Hb|0yY~F+OygO8Vj^syNM7!#yw z{A&^E_i(fSH!Q}pxgf97A3}HM@X6rk#vu{0a^gRc^*wXbLWtHjf6O^)f!MD=lEmQ; z2~5+P$iKKd^O4)DA1RKTS<|Lnx35HJ{b~BngP4d z;S9`xj6e0xfRIUO^tX{a|Jj45s|P)EdsfUJ{_SZ;-``Vy{Ic3Nua!QM{XOPHcd0wC zmj35MdkNnX=-Ktvdm+_0JS*Kne0GA#onKx58fK$DD@18Mf>WJWxA=31HveT>Ay(e* z8|5@yRm5^oB@fD1dBH^<{r(-@3UoSHWgT@0x@(t%D2v zwe<68@zEw(x3bX0CzZrwcL6)%1%ka@#Sg6wRfY=A#OB}Fu>IW>@GAA+Q>;t^H(ugU zTTMbw*1Np^o>EbDT7eTN8Og6@{F`KBYFB3^75@-#s7)f-tKuKFszw;_#1;QgpbEv) zM6Tedu$J_5_Sq&E`fFiPX$+Sk8zjgqxt`M-rde?!mz%dTjpTtd^7QQ#Z)_p0*LZ!y z?Nz9_d@uZCrE)pUD!VpA*|Rdok~`v;?nJS5FZbb2q9=ClRSun&6&9+x{BQDqQM;N) z`DzXENw6M*@{Le!HQ)*53sn{1X(FC9DvQCmmUNva+a}#EBl(~2P9sT*?nG)?(Yu#B z-yW*U3Zr~pbFeb9bAyi4L#NT1SnhhNK{F&TzP>iLFrE3V>b0zBTY=rSMRSnk3(3n% z*}=1qYmPw<1GE1-%+C_79!nlKo~$}Tr!M!T*aND#5v3Hr3;3r9HLN$@_8 zlwUh|eOdMMILFMbHXbR8hm^(jR@u<_g4M`+T%zX2^t~WVqrIot(@At@HBxlLsd&K?1!#| zY$Jr~l+@5SLKee`i;+|=Sy-6;?PKRszj?XIbZGur_;1On$bQfWuP&Cc zx!TSl@C3ZuV7KgwgKy;^@zIztYx5A!xK`VblNBEX%4g@CG0ivqWQ;Pd9D=jlE6%|I zJ@O>@R)J;hXU9=| zdx7J5c%*sdMAppkx6%56|H-lTM{gK6$HxjKI640AbVZ33?eA>2d3B}PJhi$mqMIYH zFjr^Y56{~gZdUtqj@dbnI-WTr(b9gMBGr01YyiUN%!yoM%7bfdZ()S5{W;qji1X>d zmSFKlC$$zvM-f_UG2gefTD{%jlndV~M{8+*?jGhW?%zdLyeZca)qJwucwc<1@m`d$ z`q-Fa=ZgDKx;Ji@uC{voYa@@R9^^vj*Zy!$c{1u)$`P%+O^K7W8@e&uZ(xOZ<3P@X zYbUG|`@I-oUH+z5d|D1E8;zyFm+OMBcUs%W zz*FeOZ}?Geq5GZJvZtRea3ad?P;A+!z<@Xypp? zit}JoFGM_|971&Gtrx?U$W5ZFRbIG-Q_`sHc0;719Vg@Msb;dG(HZx0!Z!);rUcAb zv>>H0CAmLFI;AGNy;uH4Wu4vlSi`s9Ui{{#v%j3#>MJ)^(>E~EZfFCp-?Ew)(<#l3 zC{tGAeB%$Wp&GNWB6n;qJyRI$USIyk^26F+){R*^cAXg3pTo?4!@(ncyM3L5x2_A6 zzaRXr*BaRA6`?n`DCg;F?VDaFQ{1EN7{iHS8nw1Pg&W)$pClSC z+=YE|-u1h%-oCNOg!VCYdEd|L>e-h(CgsLcXBH$rz4z&H4ypxFva0sT`qqZ($$Dbc z^&9{e47r4Xy5*jzTmJ%9BqnKIw1I$)+=;GyVy}+l6F|u=|iBN(v7e=4@}Q z!^W))KfmegyseE=>8NarOlKBURZ-4%Wm8=2pKFLSp`1;3lrwgnDW1w2+pi|hU*6;@ zf4*l-_-^Gm-S}s1lDPJ|jODLcWRgm79(Fg;Pv+$~=l)NRRrQk>BT7!bF|6y_z28?| zG&fXJ3_mrkX*o~z*iue(G@#SLYO=D5c4BAh{FbUt96DA1O^q&UduOG2Z;q%IMt)=w zvxNag@gl@8gz4e`1V!u{+xE-KyJ}Dubg@L>|Fs_30ln@S^P}fhd5=< zomE*j)Aj+L*8W^2hD7(19}4H5KB~o8aJ)8?Y=-H$-3*EE^;PvA7=iq}Yk~8RsaTOa)%2=Tp{84%<^mkr6*8|%!x5)E` zR+TB!1`UIUWrre1_!jH5XF!qsUJCtJILMEs(EmyNH{<_)Sk;Wi|IO9%Ln>HoIb}Lw zga`1pDTPJ_(U2z%Qm1zIJIEv)0I!tel0P1Z*o-1#uVj@DB~Hn z%ac#<&&hWLaAV9_i4vvZpA`gDY#>_ms;TA3gE;YBd83MR4p;q@mGE>mrJ$71wn)7Z z|Gk5LSD4^kIIzC+1hfINZ_PKpAE@ZWSSa*U+stAW1c*xI0E#Q+w#q8ywbfxr9L8yb zGX>TXnHR&Y>koBUJw3r+J|g~5eV33lao}ChPv*3dC#?ufcBazL_Ym~=;;2) zYgLCkw9DIrMW;I`HtizptiEaSZVbItI>U|>lvc{q`_HYu8Xnn?F&HAcfKH zaVsM2#(OZI;GPHP6yVk`J>-3R7gttm^HR9K=T2B7<^(CV=4Bn@ z8>zGt*7*BFH;TNLeYaIUHoNnuoQe&v=L$jN_P?I1Rp%@FJHi*y?(5<@{eJpQv?w*z zllo;ooj2Uljk*8|)tlvGakelfyR%}$mCv-fDcp0_M61?+Cvs2L$&$4HCfmyU?g-yk zZSR2v0)e!!cK@*QYyZmK@4u>M&;u>rY6Es}kPL6z{hj|^*dT8ziZpYvn&@cxn@wdQfgXvr7?RWE z0fy-Ly+%e^JdTH6Q=TBrz}_-N_8W9~!C~yydC1-JGiyTX=hnMhc2`-I2^{7J?5EkD zr)|nUe@O*3-tYC7l)3wFRxTrk!jgIa$&H^xXr= z+%0(@(Vj|eS*-g{;iR*pL);X+Tip4|(C;p}pm;4&m#=y_jt z{q_jU`+Visy|EhZg2k{mUOVp}bN>7XA1^pwr0-6o-J<9s&#Ca8!ESP?G*mk2PHuby zjWQZpxYU3N247n( zdybb(FF1zztA{kwR@*Qx;6FrjxhNq(HK|xP>d0+r1xeSDVpjS}V{C)JFq{edwJD~; zl04;qiFw$Ao=n?!ZOkrq_4ryk>y>;rGta@vv=6iNkT*qGqulsMUf7u(Cz}2!XL@mM zQ?CE*YZ@a&19pvl<5AjIO*YaVjmAXQ9d_j+|bi0T3QbA#pN_@{Z(fQk%C^o9nhk!_r!Bt5bWRx6bvpdiBr$ zZ>>%i&q?3GlXm&`slSzchP%pT@D`p%B}+=LfL<@}`!dUq6*o>URL9e|apyM@Sxdbq zN!~z9Q$sQ#IC;kT{&t-24|hU#hojQo8ypG4ZHwB(2S}$; zBn_n7hCiRhB1!vK;`_=IIAOp!aXre9+ZEKs9k*nRR?W#I{+rz+zPeM!>SVs|HBLWJ zXt(Wz#8X>bdfq;p$P04u#&u4-?%~8PXhREq-#WE|7e3Aoovm9_mw!kaL-ggv`|d9O z0uA~o&U$qqSA5X=!$|C<6S-Y(Se_4BGi_XL?jtK7G0rBZ76}#p4AL7O-MR|XP-ByNm z;Cb)dsU%zHT;l2(n)tH0mr|L^S1tjPUOhx<>a5L zK(!ke@K~`NZAB+kgtT=XUcb^1fx8zMj0fIf(sH;jXS_Kyk}|d z+jY!uZIs&Qo%z((l_sf2gqzd~zILSZ;$QsjxVfrN`*@@nk(bdv|M|FThpG3S>zec~ z{nimjZ>aIAkER>v<(G)&;QgW?+(qMT_L*l+&`7ge8n}=5Y->(mX zRnSDk{WwV~>CcUvKXq2+EGm2DlUHXGE-|NXaTOplqqB~9cu z@1Y9QxFf$9e=_sat?FA|aji|(G)=sDOjZ;Z3o3~=@%4+c*s#yJ2Mbq*iEn?vi7%IP zqM%mDqo;G?a~Bn2zl*XjFfKZ-h=FRE;oj+-^U}lZ{`MX@|DE}=r=Fil?Or*nw-)4W z-RW_4O(x`@(wo(X4HTl~AN8HDpGVHJf8Okhbf2eQ??f+%p5Env%o8)DhAo^OZ z{pi~sZWX`(Ab{GH`T@H2DYif3`(5`x3hmk>8tu9xYVyo;(I)>;#mzU`X3>uLZ*Pmr z0M7O_J>UNwd}yq;LR&jW^#il^gPmtnb$0BN#gIcC%)M;-#VD1s!D&lG3X|zgQK{i< zsqFdRkiQ%BrHGY4= zTB#j{z0TFDixhinl~mI*ioa!Wwe(AW2=#eyghTq}YCw73yS!got@?HZ57EqLd7An9 zTs=xY`%ndZahw1LnQ}-PmjcG{?su`~FN9=IvVY}B^FDDr@`p&4t(nItxey^{2@$mZ zGBm2jiqm^(m(UMZU4a<61KqL@>=&%pc;~~I|9|Zo%pN3-#-JHFH1;XZ!6}*k*6-AeJtd{ z@zwACSA|)d^MzWxxGNy_)chuz$!t%d9BY3*D9o@;!c$I4efR>@Juq;LVMkz)wmFSf zZm?*oy#Z^oXAW`3oLxt?h0&aS%?CJ1f!)EdW8CIL^3j1@-A0jD%ktro8@kWxNgD@D z)gJVC?Kc*21L`Pd)}gwMy3&pOW4PnCn<}_jxZ#HHq~J?3x<6oR57>%$^;Ct~d2QEC z;-fUrEkc_IS5;(>xJ2XW)Jz&(Q=i0X0h1+^m#+(Q^3mO}9H#bv{pla$yb?3p$xN)8 zyL8*{3)VX*)g{a-y(ud&qXqg_DGg?6BnW%3zUh|ln2DMNauXZB?Q~#IF`%xgPOEDD zz-n&)MOTWp-`2$uJ*hms6p5ZERhnhXR{O?QnY-oLo0V9nc;*)S?CO)`h+PAJze~o4 z9z9GamUI%i=$n_%zH(;A(TZGa;0^EIwGXZnW`(0iaPAc8TZe_Ne9GRwnHS;R+iL62 z*BZnkJT>D9-%@+9#Og= zX`E_nD{9Ns=mT9~GhE!pEa! zga$3X!GDc7;z%$O(R08!7OTFG1>MLCkp3Y# zo=-a6nXjHVK!#r7|s9&B3>>?RT%y_s#z-ZXCty*)12W?yHMJBz+f1LTq=njd~A z-;o_;*r5m(hneKMe}03w3BkILI`O|gNLSHG=PU1x^6*K&onM}xIf<_FPB@Q0>hP!3U0qj(}Rys1$XgBI>-*5Ii7d%=>8vc)O zKIY#AueOab3CBUHN21a&#WxQub+=5}M<+Ej2Rz93yT&BOcV+Eo_{0Z6m)`io)a_qU z7|jzIsi(2pBMI-6vzc6oUfMw=-6%PD?w}}Fj*R-os<`2`FW<;v#)z1GW@vncrF)(D zj$&k}{;{u~IT1dexI@m#kJh@EoP?#v3J!kZ{@`v}m^Q{T_&6-=!{^{58_A~!?HH)l z{GLBBYYvj`z9WyPo`OdzRYi`d5C+uhrO<|BR2E%n+GTiSREjG2U~ZDe&d-MpxC02M zw-1#6h_wc1-58Et|Mso}8?m0_a+F5ZnT=u4u(`#7zpSCy@^TAjC`;z(J;gq-F-KXP zJE@Rj_V>jk>CJwekgQc^%KdSU+Rkhwf7e8dmB$e6!dzgGQwb93dpEr)hsOkKILT7v zz^Wg4+hZ}ymVb{~lOH@a*}Y7r&L4ACaDSUi`N3pjBv7N$c1;n|&b8 zlU;>e(KM1>A37Ch%FgRObw-KXxW0gVUpH0&tH)UG|6Uq})V9)0$i z_Jy^J~u!YA&Sh}VzMS$UUFKB3Yb!@VYZs-xQ9 zmlR*ve`>BoD{`PjD}5Hmb3q37T~c`20AC)nvEI+mc#=|I{tV|1@EfOXcErKf_>g9q z|CGW2Uw4}!^^x^6JdZ7zVz_;tAT+4m`MY!dzx`6kB$aty4^LtVn%lYzS$azAWhbq4 zn)IMKR(a-j4uQ40Fc+sA0J{<+aY_>Od z1fQwZpBb9#IX>B#QqcPGPC1^%)Jrl$^94@cyd9RXkJ`k8(2vNIH)nI};slKyI$sA0 zWsn?o{#4PWHR3#;JciXw*$3XkPSvGTOp+FnPBJab3LV9SRWgSq=}Pr;$2*R!U>-ra zL9|HDO@yiwpPY$QoRHKlT3b+_?4O5^PGE6rHpc~JtmcMT^!{lsxK;0Qj`}Z4ZEl~ zP-s9|b(tL~snq_4GxaDf)tGMnV^uA2-2*%FSgWJvx{Y6<7F)LYrJgI9{EA(5LpIxZ z041MtIlVeZbpVp&@QA}@I81mpp!~Z04sHc2t*`v=X@!W|jN9j%#w2l_hr?ZkiW8RZ ziH?zM?a}CCA*InV-H?Lz+?JVwRs^%+iCu<3Hcd)ur8@!tci^W79SQZ)J>}zi>9LT~ z@^QbWS2~Wy?`3>D{~xp>enTr_>3^l=`F&bBsOP1nOSbi?`$@! z(W>uv%HyN4{SF%2{|k-W-=T3_vdP5lQBbY5v3l^H2z#tU#%R8Zz<2DG;xC}vUgny$E|}_^U^GNq=t)vO!}^qvrpJYVSSkW!mGA> z*gA3PnmADHu7%X=l>dafusZ5qFs)KpaADJJk;uoG{$h9(&EOYbwBf(iwCJU9>^88= zddY_WI7fc@&<0(Ez|X9m(=hkQ6UP^Pxu&KV{)RmA^Wm>0RhY!*+F`*}F(^zWSxnU& zgE2p(U;IzfKwC2nUq5VlYuioo%?F3MA*GzxD${!4a|9>xm}AwDdrObx3UZ8f+H^g) zI#Y8}dwK%iDtW;oy|dm>$~;#|-m-UG`{vu-zWKHV_p?klM&cm;@lv^F&eRF(X~(O5 zJc<+Fkn>{C4b&MvciD+nB+>=j{3B<>cml za4jk(#`QH6by68wB&EHA4Sm#UkDAA;GEZtYa<}SNAduasiQ#mDMQv(1AOFOx(8G*xk@3k|3frm3c4lG(4xTm8;lk}{>QiYQQe=+(!R z;vPEJLGNg`KJM`Gu)Bz-@p@?Zq0WU&bCvpN!Q+-j$ooG+cc!4aXZTHOJ!(0>4)5#} zXgS6Q8~azF?vUO!X@Y2lmC`i-@H2n-tLQwcfgylYe05D{^9A z{)rO{`vsCz!HdoNv*JxS}jsWU2P zQ0o%!qSlQ%^?bd*ChiX_)-GN6`Ww_{-(8UJni0g$q*I4a5B`uXY6CfAg4pv%xg6!! zu0x5JXTX}aQEogq7HVXJ(E}|jXoVi!d=MPG@=jX9cXaAK1#5Mp^nH7z!>Bl2y_Dnb(h8Xh z_nt@c!@hJ!xc~Q|+q1rJd@zkDe5LWf{SuRY(IiPH@lo3*7cPh&YlPe<{PRj-i?)&# zaKZP>v_&+va?+c`Qb-s~0B=<;S`59ebN-R#=MUCqPrMDgF~g3iAG_Cngq!vWg*mDb zf{-*BGYm;-4LJV3=6> zCw4P+R%mOB{~+ccbOE(B|1=@lt_`OAm4V;;JP$Ji5})t-dg096d+xdSo_p`P=bn4+ zIf#qyFm6}~tAnTim9%>!GhgvM8h2>S`L4)j0$l?XoE(@{K-nZ}+ow!RfuwScMtjdyl7BQHA z5Hws!;Y(U>Er)lcHzrkDzEiE;l&zW}t*YWH-)kDEREzaejIg3zv8hq;asPh49j^m6 zWv`z>DV#xqN$g^-Ax-^Q=6o?ut6>y2YawInGO#Int*Cme@C^2+iv(ZBF%?MpO$#{N z-fe}|j>F?G;PhQM4Yo&^@x}A5O{(0qlvyj+R$%bm_QY4Y2E0)^EoKO5d|<8tpWFC{ z%6P^nF{{X6$zRV1%{)`4jzoO&O-n1_y}y3urlpNDAjQM0%z1pjI%89I8$R3d*)`LJ z*)m}Te4_FSxyR)fH4P~3gOd2WiobRrxkZyN2={Xno7e4-;u)JlizQs> zQMc2-#hPt+HB0+DK5|l(UW{Qw2J)LzgJkK42~a&YtpPD(VK zNF$DS$tO2c% zRGz3HpPXT4Q0DQEuI;Z1Sb+85wV+5D>;?NAmGp|!ruv969#=6#Ula-^FJ8JsK9N74 zULEj~H|fFoS*B^QhQb4KTK#t<$?8J7PJ+$~$I^L?gZL4r(k{59+{MWpdkM2bz>UQbdi3s}B5mD3TO7CyoYt2X*DG23HynYJVn-0i$X z%qH%?Hf1MDa8iC$I`Snf{)sbg2Q&Y*$;Xo$G_Ua5MxTngmV6Fxi~t|BcATY!W25wz zZdgd?dNEVDy<72}q3^yNyi#}1mcDB^Gg+%Ze?B`;!=<0BTqk_j%xnz0sv%rY>C3zr zR=6{PeZl{I9^ZZF`y2i{>3bx7f7?HUx72kG(CN;()jyNtjvuFSjD@`5ZtZkeYvsO1 zK91N1?p*kv8|;gmyY{i$1z!JU>;9AxQa@!j`mCLqyC6M=aEg5HaZkD1(|v}OyBaMJTe;Apkd{bA0Cc1cvx<6=EfrIIHN$C)@R zw>aQ-{&Q}e6%$6oxpr8oNWbAYlZNH4z5jOp%Wj;>zN6uMeOM`G{BWF`hvf#1y`BG- z8)xL0(QqaYD^;Nxj#C_#dsTfqKi!R!QICc*dswMt)o`47!*YuixAP0!I4juEa2C5u zfm8o+IL3XRyxsgCZs#8aK6^pwA4bD<*aM%(|9&{GW1c)K|JLpNPYKt~(ziy#RpEi( z!~b?Tu3ArC4*%QR`Ns)YQR#0+!`0}4FXUezxni1!=D&73{{rF5Eq!e?d{;g2IsE?) zZ(FM;&%(cQJO6va^2`iM;VLM7X*68Sf6JUawa=c% zzc?Hh=gG73Yj5X!{Woy!EL}SqE*}s49)9(3T>hTC9Deof{E39CsC3n6xPmILve9rwdf<2SONQgpd-C@1 zi@@&)*RIkT<|u+mtQ(}GF z@8)L@$Cc;F+rvK&UPicfl|DWiu3`_om49qFu6>@oLVgDL7~#q-oiQ4&!yfn?{vKK2 zm?zJ|KLS2RxZW!b8x2>52mW1t`tWwudh&MjA>d<#tDtn$y}!`|pT}e8AG+%|d-AM2 z_WTQkYiH@y(b{#@1HXsAXSZ+l3|Tp(QU zl_reVuB#sScll+*>EG(f+s!WpA0u1^rAtS{)$W1MyH_6Q@Z?!Z9_S`qJ4crXn7J-K zChXyH>Kr=9I8R;<$pc<c+c;E~9`NMJfd-C4pqrt-nSN`boK#&LCLh``S zb_IL#@_5Js(+OAh=(4~J5BzTa$>F$WdGhw~zXT5>T)RqtIa(hhJ@D3hWdXe>uaIPc z1%xYibXj1j2R?^n0Xp#y?Puc9e8>aO5x%0*@X^|~)&pP2KQg?ZNuIoS`7rP?!j(U| zJn*^)-g2)zu-TKBNAkd1ge!Y=c_7&Xznh;ryj`LvZx0^?K1R59l?IL0$7~P0^2=OSErBms~XZ2OSaLE6IG(`Ou( zwS}pD;`nGbNy|p>^P`WdA4gzfocMdYwCcN*3@6Zz-WNuB^N#CI-JjnD+$netc z=p2|W3KzYY**mnb>YY-oV0Tr*ZY;?mX1%J+kaocmBw-t?oS3s~TCh-JL(OY==7!^ZYy?mX1{`pB{~-1#HR&T{9W-v1q0 zHqxCxvaH^nhkDnIEW6a5KeBA1I}i1~II`?ocmBw-N$xzp`6J8bx${u(l96SL-T5QS?sMm%-UTDe9(L!CEPKqIhkECa zEL-8uA6d56orikojx5{g&L3H}*`0@apB!2CsylyV*;aR+kj&2*S+?DsKeB9xJ5NaG z!|pA+DR}J+*ZR%5-V`a+@OW?AR522cd2AS5LL+1TSvcI2)6F70(*xUXE=bf17n>EC zKMTuz?ZPP(frrm;ibSjuW_t~MijClo@JV{t4ZWQYY>G7Ng}*nC;Dnok8~xmHTlt`- zNR57bjPUDbemLja6kOr%Io_QDp9Ht6Sv3OA$Boa(KQsupyjgKCoR6DALJoEYsW+St zdQ-(KeukdEcVkT+xnHBV6|Q{q^``lj>l2u!nvqz1?tuycR1qXJYMUgZSy#;16cPle z6|mk&?K8Jsnoqu_E=iVBm!8dpH`$_RbKv(Ka@$?WhVAa8T9@ZN@&-fin~UbhTN7Z1 zk#hq7<3=~9F2O)IyYM9U2%-!)n-&$-q|{m3)T*Ky@??9534|3}p?%}kGa0u-U;iz9 z$Km_PnslFhpukgt2g5yW*I}cV!X+D(p&6l?eJ|(!%q%7{vVbcf zxWiJKg3=}dE}h{1ETyqh*}vT`c#GinQ(7`ZX}kY&yWnMld!N!GrL;+adye3WC@n}z zyLc3RBDexd^O4Jb1{x9EPD=BV(jEldbb{MKX)3wwr?|%^xO7TWNNFn%z1_P@5u<-7|{-#BHrId~BpY&s74hxopy-ka9TxmU^9>LvdT0|pE)cxcRs7G+^ zO^ZZVngi4$xF0AD*0jko?8YBJ!NWq%!RylKL{j;Ut)Spxq43~UDJ@7!y9x>t+$AZ^ zM@st#C`fQEQks{P)&dF=+&L*tC8eFktW~7gECn3a`398-xva`U&P|!ZVrW=aS!agtf8>JtsYoi5OBiZ3EBF*{W~q+`Od8@E%dl& zv#=(+$a+Pt@eXRdk^vi8sYc1?N~o&ew_#HVGgIsTCIdfrU2!`$`S3Gg&jp_7hZxd= zsgub$9WQQTf@b)CwJAFx2sT>NU{^z*(_3MEa~0kasW0+Z&BZeolu_JEI{K{>r1mPU9zcfL#^s%>dy3Y+PCWQ6GgW8ZkZuc6Ku9K&kI z$FKrlK2CX%V~n5ZnX=RHA$0oV%evz6tZr&IYuWMRpS0up@#eP~Z?0=u!YTZo_f<|u zImP(RjD8gAVAd=N!v0zoRW z>&6(C8tU>Q_z6*C>X+6lLyd}}stZ+q)w*eaR}{rgIY@2Pz2C~hCl0F%v!T{c@!H`5 z5pOU1;UoSB8fUUVcSZ0{k={MoCr@-Pit&jdZ%WjH?8-$|DP z?**vKt_U=2F0((t_Y~FY_WQ$orct3qes<-h%I(OvDH7_75DCP!D#A^8lza!fW+>F5*Y+R}{pajJ4-~-=AIA z2rIe;Y_u4j-&ki1NoNbHo2rYdbdPj1j_fn%Ty;wOr6RpLTD(vlSgi}{R$zWh(Z$J< zZdcyPI%Q}&tkV~;)CxeLJhl3h(=H4J(M>2_0NksqF`lPf>BHBKBRlK366-Ey{_4dR zz7{XX&odVcMKPb{+IOGFJou!6%DFu6$)glf#drnAsq_K6+#jpOUzu>$uQ@EOwpzCzufpKtDR42( zZZVeTT43CmQ#nSVvs1CuV6nzBB7fAX+Yg-%e1-Wujd!7JV|8Ga&%(enRIB~R&OYUI z18WS%3J&pNvMXIP-ZS5^nz3rS8NY?A4XnP@s+-mgdBtyGUY0Hd-=5V4O(ZUX)vreD z)JoTkjy6?^`f{JRhpP~od|sYowSJ;r_-C6UAu2)*+qdi2;jf}=p1<8imoZT?0N5Uh&CmH(EGR1&5o09)AyjL=Tt5`c3`~Mt}JUhv;y5y0kwIq*~;r>IZcoCTG zC=N3T$sa0+ms%yyCBaz%{^ZT1wb@a;=SNy+Gz%ic8u*6tN5meQgOb;<8pJTLs4L@@ya0T{rvg*BkUZSL-Z-Z4D|#zU zwLY3|dGpvn;Td7vW>$U^d}1(~RX#%&hU6p8<*^2qpv9jYC`q+DKkB7+?8FNLveZ7h z5E0r2TSRF`?)7@^hn4i$l`DT+mO5*3R zj%|WxlwSC?W0hfXtn%RotcjXFijGHCqBSgJ1t-SQdBSuKJ_}g`d|!0C<`<2POxdJc zLth2>)()=ec5}UJ{OFTXb^8wdNU$`gXhwZeNm^3JjEx^ixgQ?**Bi9~6Ul1!r3>d_;qQf)qB*?% zUu}pf;^JK)C#(`}KD@rHhHTZBSB1gL>Fvn&+%$(xD|GeSgjT)-sMt=aIM zBYE;oK{HE9?<;c76gGCFm#I&E7CyTE1&Qr}4h8ieJ`66Izbbfk z60n=$g|ugdpzUC`<_?b62e*SGZ^3&0Q@nq_;u_81L^*fgdx+XpcCq{ky!#LNntV6N z9+=2GR#@g^;+NZxW*$l{&3H1y5wvFj64q6NHvU4Gx$tBOc?CRt#)Q}^=u1xQGE)n! zs6zTJt=1nx$6M9XdRngyco;9@Q)7f=)i`*X0kw=D#hqQ%$i?n2gnuVk-@{u|^7B{T zz7{W%4;V-Lum4iAjcl9yKMC8eE6HVbe@o2+;S^)Y8sN>ZCI zEfG0d1--wrES@t#XV&`+NInWyvwPCc9Ru zyN&-Dz!5JGejBeha^OkoQ+Xet^+PLHUMc1yv>HlM51HrYv9X0Tp5=%iH-y^+2g`W& z{TA~RQD{H0QHc;S!c1?^ul!YG5LO+)s)%Fp#$bjr)J&fXIJ{GpX7P;=_ZJEY-rn?nHgs%GY`p1f@Zmi-UTDyj{1&tJCCu7s zELq8uXT5nxxJnnLM1Q%5;yOq1qXR$1or~YH@}pwi`*AFd>cF=$r9=1ZgKyzu)!UaY zga6eYc+wEYbf_$I{23w98~z)MmTtA8PZ%YNbAmDQ>EH>MyaT)_y*7&% zgL|mXxbnAG+Kaq6t&*@p=zAit@idu#uQjqtd zaC1rOhHCAKX1trLS1-97ZiWB(-oTh>@(WIJ9PfT? z)F*O_i2Hk?wLv{z`FN-C(@N4+Jt_HLju*`a&XKW4SY*ZH|Gs6#Eca`c{Z;gy1s;Dic#-Y%s7c%P zID`F4+oiB}DIj4PKGb&A;9YbPUYUnrUJujU1#93hj79^Vh}Q1;OoownLQNvTUTIJ+omx&g0D&tsN9QP_-+AkAzJMSn? zi;kttiyLk#Lsd>ebD#I#b=M|bIG%gbXfQJZf*oKaoDuwarm-e3tLV59FHw)?H`UOm z>-TKyh2u&CB1doiPLWp~0Lp6Bq3;I9e6Urepx2X)^QROS#R%zbOq0Q0RM#&WKG=Fk zVMPy&`&w6mA7vk2QIaOhO4py;e63*VFY0A1_4x@g1r<#dsa5l3G3O`#4Rft36`5F66_${o)|Ko`!$BOd~uSEiPvlro^rJ zB0GApz1jXJmcXCEcX;IlPbk@qPY{$6WBx_C^nz%=ioxq;NDr5e8>PP9Q3RJOAI3?l zcs45p++Z5Yln+y682DX@U8(3GAOD;g@1|TB+hYkEVZAo2O=2v#Rw|`GQJ%XrrT1gR zwur3MNcu6^pFsem7p>Z`RHhKWRLjHrb*f|S+UI<0J*NR+=pU3f7l(-2v^0h&Y!UT~ z3r<|Bb1bchd80?B!oG!$?4>VzO1dOs-&5n6NH#EP0i$T6J9_NWXR^Q`ZK||Xa`!nr~Lp<ZWgc~9QiX(zIFFm!JD4aO|!>Pqs;fj^yZQL^c%#? zM~uk65;5R9)*in<32AGKH(1okm3B=>@8)tDDrnga>C8oK!ajr8(%|wxV{hWXJ3{t` zGDJmMBd0Pr)%ndd*PiFvBmeL6LCc4izZf#Ke8@RwB6WG{XAjEKp(F?kT&hKx?N;zW;O@*eo zrvGAAPW>&N>wF1acV#o)nLLyNwWK;7%+mW@y>s!VK_1u@7=xFjh|^9yrxIS~C60wOwKBchwTlif zqrNk=4DQwb=@kP{CrYm~jWstuOo?oQ%;`;WI)bwhqp&-V&e5Tq@`mR+cD{^BB zs{UFzQ{pEDF=Ffs$Rm%05nLG)F{hQW%$~s5xbu+$bDl>`rM2TPSon?s`4p=Dr;HIC zT`6N+V<3E#3aJk9?Q+xw)dAoET24O~(GI_n=W>WB&*pbdQJdvcQcA4-mD=97-JDsb zQfO-+yur&fg6=TLMWxXw6#A_Pp5jz_#mKbLZtfU=FMY_XhFnYiWv}X#QPjL{~|V)m-jU44r{w8k@pf4HBi!9H9JjM=L; zTGb5s&7zS)Pxy}WGA4^@Jv?mf2R4GBTuPr9gPWV4DQcZhm)q;RYc9yNum8I|p1Cos zA&1vxUq{(UHaBLHUYlOW=zI<`dwF9%@5Ts^?V<_ktxUr`=?z0l55+_{&+w(Mw7XyG z2c;Pggnp^ZE*orZ>jQ{2e`K`Q)_)!tBhOF!Np-(GrXy#kj8*+t*Id?iAa>E;)y$Se z*Q^{qy6a2RtHo0fp7`07IOe>HR^rAzo|U-XMl~12(CU@HrNlYU)F@qG&WQBgfPHY#roOqmM>?i8SMfl-X_fiWxc@ zGMzMrN{nfndrV!Ni&myIHhT~3CrFuk>-VvSvl6^Ki&}ZKzhLCJkmP9GU-!OTs{Z`| z^g|h{em}y$85S)iR)IgSFvkj-^GtfoP`;_mlV==ZjOL^&O{+HH)oR_w%#Jb(h(>*;>7n0w;GI#$yIqR+DA<&cKnjMgf#zBG} z7oHcO#HZ4pYxRO;oc_JVe*yGsOP;{#WRSxscDE@HctO$!HA2S0lkNAA_=VIug?&$p z)5U9uBY_(!dj0|bIPH6Wa?M89>Z37Fbo1mvnLLwZZ^ZxE9so+iXUz7Z=rPg$hKPUz z3-k?KKWa^XBZ2LymO7D0{C*rdL;_IbCrH`dP{Q}`~dB_lBo}eW@+*jQLiET zT~5*CblyJsvOoTxcb-aIX-UyPNS~Jm_rc4!uld*Teb8~qbAq9kKv#uGNOB9FzvzkX zE}!q(HoI0Ajr89C7k`w_um^rKv{9-+BOQ^1U>LdEHdV&BR6n+oFCPS~kR{GSG~eB)4Q} z{ZDfW?uJtTxYP&2`ygtTasOQX_BCAJmPD77O068;pDl^aWuBJF?Q^a90MJ)yNUOFI zo$CXtXP%MD8#XeL3U^&7f>_^=Ll4Ak$_{wkre+wj*a*NUV$b$(3Rp2?DE&71te8P~ z5$W$s`ut!_*WYNZ(~6Ss8eN#>w$T6b>LSS;u0F}1U?gnCrsnoLf+iQ~LNIR7O45jN z(g@tsKWIe+MJpeG^CNUU|1H%W)KxlIz90v5CJdmw8^h4{ZlUtZR@LBK{6s1r-gOxF zd7sK_M;_hL#$QM*R@l_Vkx^wXh~e!MRunUouP-~b;z;?a6)%*_qv09@t8|VP|KwQO z_#ZAF8Rg5@(JcYvwI~V^~Tn&V)cvOLWuJa9FDgf#wF*12|W=bVoTe7DUzM=b1A25_t5twM<~ejNR8=bG;C+ zD2}EHs`H93#aQvxaMz4{dq^#B41RyRY!mKP$garid1LSr=znVZbwR2DTA$zr7mUF* z{l2kdacd3vXpfOjPViu;?ONP(JC6_b)N*i3_J&zCCHWe8?QO zUCXO_!$q?`F+9wyY3N+KE}?Ygx<&C~>`Dz*Q_h0ebc=d^FJdmm((UOIaWUXFIQQyh z%C=6vd>WMgx?~&lh2v0n?;!mj(x!E>mc*_or}?wIJW*GCm&xGZ)9k$N{Xa2@4g0@7 z*JM0!ACrRcZ<%cyilHg&6?iK~B=s)0ZBo7Rz(VYjvTtaDLzJJ}clg~4IKyWqwC8SY zIy6>2nP`tLJh%IaaQOz=HenaD=Av=mLh_Q`Ew`lBAGQAd^59Hp^+P+FLc8f?s*92cPGIC%E#1Vr7K8t?K2__MJZf^NWq zqet2c=)QO7U_3r~zEj-4hRUeQz-Nw~kv*fKO&YW*BV3AsH>)@`QrP;qa!Tw8kvuOs zg0g%? zF*Vhm^0I9M{Z4UP;LNNH)bFi!t;Y+`mWY$;=fZm8Fa7X5ixt%vl^gYQJuQH}m20(b zP5$^)@Toxkv9EGh>^pHmuc^iL4jn);%rFDw>rwy9ioWJeuJL*&-Y6PT2S zFGcvk&v)U`uA9JQS9|G2*kIl1pTg-vzUz0az0!^KB-`K=B6qo`4CwECT zlrxuBq?~chM9;mYq%i~(#qFgy7~wmfiOJx-y`X*RrTZMZnbE3McXiVo{7h$?#*zOs68bJGhQ8o@~Zx^&aOgI?_=z`X>IxRcu@jZNxkpxZ3mq8X)jOuy3$k5XubxZs%u)FP=h)DpB% zH|8a@1ho#WedxVg7(8}OzeE1F1|So*kv^X+q+PTSRvcDnt-(yCUO64UzSDBx3EwsW z_JIctOch_bHDbl540)f=QQQ4H`U9hE)_O@d;PIeHQLwL73pbj*1+mndxTn?v1ud(c z$g!3Jp0z;XYPzg_R>^9g^I|O*ybvj*G9@ada%Gud(1U4zAsV8kAIfwMZ=!e< z=v7VMu^$DCSBUTsA7bn5P*m}@>+5hg)v1W)pUI5)T?alRUOvv)nmZJ>mQF=PVuvDP zdADLxy>6bXh1G}))^qD zYL5jlB_eqHlKo%PPH?WNcK%7U?RT4m~8;XQ6DZg_f)q3MjTmvdt4m7FD+2HTE@ zqHJjaQ5kCO_?o_6EA)7MvXlfEwMfrs>A4xvtdJbSpz@da&ZT80?!h$ax7l?|g@x9%V>sk8=9_9^-UX zkMhyfZe#d-tOg@v)WtBCozoa?UO4mnyf4iWI}~h0#*0jJ78|Dw9gkDi(~MxW`$VfR zX4x*ksjzjuiAaN4jMl1V7iZZ_bCp8U`EkqA&Tak4l=AeLn4;)60&L=!idz#hvutTy zQIj#_@P^&dbnrjVNtE=rb z7y4~!??q;)=N9OKyl58${Z)5CpI&3g{JYWc^!`j%bC&M?9~fP6FL0uI3B# zkXdll)S2Fy3T?Pj)z|vD^C8g4usz!NiGsOx|E&rAd55eLeNs+VRT}MnE5Mt^W0#iB zYe54{^A{)5Xy)AdoH(9KbBr?-Y`!3qfTz0noojuPm)BeWG#WU_)&7h!b{1od4Q8U% z-(vL6X1&kPQIBhE&eE#S{|KMbsyXVP9tBtN7521w84Y^ei6ttOv6~f2Lz3cKbrs)? zGE-6VVl{GsDG?Yq12gE%YxC0BM68ji>N5DGT$u?=haV0|Ue#$Y20ROR7Vs?KSsB|@ z_1Ub8S)4)G)TJ;QL_p*L;us+I4TI1d>~D2-uf)0cg~TmoYZ6~Ao0YhxEGcoZ`&P$j z$U}QX#O^+Z_UyyBe~Z@I+SDwy_mEYnF{?6G=)A57&lg@U1%*H@ zJ*c%{GpLrNcu`%&EzTm061MgAlICM>9?^ni6!~OHqe$`s$r16#8J}w_j+z6jd}Hb6 z80++>;M$NAfl+h#0VZm__F@`q0gWyBX-ve1zcPD`w{~dN>h_+jhz%E!(!(^bpjZR@ z43^Y*SZ0PQkKZho*7@k_QKb48l=jct)2 zCjXFhHdA%0Kv!G{Yszco`$b>eNJ9hg{R_>0QA)!q)@X%g_orjg1c1{|@ z)<>8UqP{5KozZ$ZM@8Q=C7lPI!fAeGs!5KxnU&+9dsVdNpRl2s*a2OQXAQgoNsTBx zTAAm+hgHGPlzBpmc($yGA`nsJ!pF>E8Nz7}y6jm@t;3?J>04qVE{Yoc;eUHtO`mW| zrI6zmnd5^OiVK&(E4f77Zv{=FQcxyw!b6*tumw<&9TH+-1obd-oE5S&Ba5%8t6Vl_ zf(CJ-0ObrID4{{Epc=N6N%dS%DJ*HJOrc#+Y7{btE#-2}Df6jiQ4*Y-lUOEyIukaO z-%42NrUg+9H+4j{Tf#zchR_G86e0aej#zr+~gEiisBr ztJ@hwnuI>A#tA!s_IWo;M0*0)4-N02<)|Jwi1IEky^@|P^)WHr{8)y==gI+#25}ZN zPeg?j3co949xTS$*q7Zx&%g9_gh9;{}SWjU=i2b7~!=BhGJr)J%1VM1D5)~84)N*wsUb& ztQv3tf6VY%;7!4Uyoq?>PEO)3-AGvLGR>ktgk`MO`zZ%wBkF7g0#!H_5twK?gL)RdRST1`7z4lj{|ZV_JSAH ziCI&@CDm*hKd$kaEQ_h-N4obG#+~QO_@5rNm_mF7zw={+i<}Cj70Oya1xhQ7b<@B( zqq(?2&S~QgM}ilxD^m#nto8a5_Qff%2zvhgXBq^BM!{w35Tik!DX#O&8;OESgBK|u z;oLhs$6n3#E9>X8%Gv~0dHlDmvF;_}=%|bMxmx1q_JQl&|5tkwv+V7yy<@ZN|6hIQ zlP0k|+;psLaiV>araKNhligp_y+MkKxBo=455>a!^<~rP*IF>Hy`|fp%z#AN8-~#> zx54&jG3VV<^>u|DDK9e~Yw4~WpM2oGeFniByUw(lyS@h4>y5E1TKk;|m9JPbyxZs8 zH^7Wtv~Q8`VxeBeF9V+;JJ&1!D9K#Y5U3|Cuxz4Tq9qNoQ5w^n8S&-x~m$j@nf_H_6?pkl;_)7@h-JMn3$&&8dZEgV%@#C`rPX1HG;|; z^xegzEcQgA`{`G&iAUE2V^-P6?Yrwtkm5b{{@Ud5nV0$u}vwYtN@aHZ`$N z=Fwc&=9?I8K^kM*tY@|7O>As_#9Py`J{M;tW!g4}uzR=C&eOUBJJIht$2VZFA&Dd9 zE77}!)_(iss>F_Kt6b}XcEIJLe6p7LpB$%wZ0^a@lk7Z|@gif4&tsGe_c6wW#~7L8 zJbFWj<-AT4|7tJK12w(CeOP5(0!G1`wfJXX-pO+gBhk8<)dp-}u&A zRQ`-TK+|`&JaNbxxG2SBqqw^iACv5|52YU6HhUY*L&rD5e2!m!Zb=KBJH>e6gbFi) z&K=U6%EGwym83?r9bkk7YL(rxVgNG)R#}w${Ew>;Ctw4-o!9i)>()DMnk3fY^K|PA zw~X}~W|j0Z>ephP{piY_*xBUhV$qUq^%R;V&FX&2!9G()r7UVjwepq{CG9?4-M26s z_|tNv^AhYDI?~3GCeG*48l_|r0zPRXO26}yJaWX($~pl0oqQdPzEYYJm|v*9bXU39ATUOE+Sdq#!m6)iW^2IjNq)s*5X% z+?-Rk^|GiX+phL^DrTONYQnf<*I}{i4DJ=Or2CWsr*{Qt)_+93d)w~%)lp@fi*swG z*$mkZylAiS)g8pQpON@B;=R1w#mwY9eU0MeHzc$_FFQ$gC>T{pDX>Ya2ul;$k`aNb_KNDk9;TT2OK_* zuRsr{5|(-;Y0uQ!_`Jx^#1MG&muw{4uY8Sq^c({o;oMxNSn1mNUyOfJ)PqV(K)2;5 z96nj~B^eJl*fe>O0j;DD*Pp>`Jpemk{6gHE)62x*d5}RAA*K@Xw26gvBi7FkC8=X8 zF)#LoNpjSrc`d17Y3Jgp{V8Q9()N{~$XHyiSP7fr6)y~%E8(I%Pd?d8yHuDocI7uc zbh~F>4Vxny-&0~-XzXbY$m2fwTTfZy2gD6X9x5@*y$P{jmL})ja-eNh+;t z0Y2hMDzxAEsK3N~sti%7aRDN8{3lu0j!In8wcmQUB-z5tGx0Gq_zUKu!&+j-*s6qu z8lEiHSi`B6DF&fV1xv*m=a-O>1?3Heka|Y}*yErQZkmKsA1W{oP8WpT_eTGhW_$LX zenE8~_MLw+!u(LBJliPxMD)TXq8v+N%L?IQ8}r5rvRM7n+*9scd87Q+%8TXAD;p%q zmsToCSr$mYY^*G2J%7jlni4Zbh~qG79Rp-f410t2D}#I`T8ipskLRRwHA$a~6Nv{9 zJ!Q==#gOe-AzW`$k=B)7K_O;zI`&gNr(k29iSgp3gC!EjoOcPa>|-S=BR(?TH2oE? zt)AujNy?>J=A!70gi}&`skO^$hDpxMygNedN6;{rm>)ifd6Z*Q}ijW8xt*-YPR!{#9ba&Sr`iX_jMs9yUH);~ZNXp_#>&q^86P>w?*t2Stt0H;uJ% zg^}Qx%)mc6t|Zs~yTbn3LEfN%&9FRDPe`wYl9~7m6z`S4Of?-%Ej459_Ti-vbEOrl zw+}1OUE`jQW>w-o!C8_TRw%q2%%&6zqDJMMgc_Bu8n1juaZEzezRb1%rm$b&yqVe+ zx}aXX>vZitbjp$=tJ-<*ey5ass?5%MlkkVnJNpyXn@W1k{q9duyQ;pB8Z+ z=a}*OuUxl)E)Ga7FENLk1Dfd`uyr%;?|l)u>+r)36t#-DV2H^vV{IT_oYmrfP=XRS zN&7~{M!emraUwp-Jd z1d(x=8D3!-FMwj;4Sg~VO)nE)oVR;RCED9J)*!soHD*<#*%hUs$j$z_OAiAyJYb<2W9c~k}O{SIq?RZOc39D z;8@9^>}CIo>vaSb$62hYkmFNPHkE14ig*)clc96H^AcN|<$v=R2D`PbTN#|%r)`-O z*&=@NpHF?`dc78w@vsyloMbG86nFd_v<$8uyP!6&YCfj*j;wSZ+q~+=7qlu}E0SWp zThEiNgJC>wFwGXxio3vZ~QdYj64VGf3NqN7B}H&M`=| z9oR>-70z%Jd#cz)TcIAmgRVwA0luFKs)u)4TFk7)Y6g|f5|!7pmWyTvbnb~`oy%=) zbJ$vk?L78Ow#^%uFvE-x%o(uysyg zZP3b7jH=C9mU@a*MRr$3l?hRc^b?~3^+`3JY1+ObUS+?JU2EwrTs6IkV1 z^%#?>s~2n4SLZncAiV}nT%5CBc)KeIIuXT3^+togLacu7ncW7#(e{vEA|%}ZIsZ^X z;ueFj{`^A|R|?;?Jp|3J5-@83bF(e*@hHG-I3M`iD8MA05B#NG__i%j-ccDJsyoFn zF)pxLvkvbW+F64$kS=GOm5zl;R_5!C0>@ zh)KHA6w~qblO|!)d1kh0(PH~|wf)%!`?b^kLf3VqkQal`Opm1rn*!L+(uGZbXJ#8M zT-QJb=W1!LLCY^-!t>#CC(^H~DTY($aQ2_BL+ZEZ)ZYzU%o_e$?fY6zlJB^^obN_) ziQH<6)x^g3T1y989vJtN`@5?bu%0hI&jv2$)b@2kG&) z!L2I+=sV)Z@zxgfZhsxe>gQT@L7^;-{v1|5laSmsvmACHa4rz+7kKE4loq~SDLmMsN?3g4gcxgsmkMxIYZ-X% zbw*6G<$))Gs}i>bH)geJk#+8H`s0+RSsg>~A9KF){V_0RirQxAW;(J`{%QA@ZB^vk zAL_$q$>M~#ADvB>)G;UH9-!d)~BxfMX<`>ECcv94eE)i}-%}y>o+l(Iuihf5?jS^iIMJ z`|!)pI4!Wc=_#kw*K(xPLkBKJ^-$`yGMafMW}3gQ(j9o+>l)#QYyB2mtci@M&fEHB zegt0;nZawVn;Ff^fpJyV&#c7dva7*;3$OQ2j62Gese_o$^1jTY^xX!E?(ABtWMe}# zp*1AWIKG7}v;v=>ZS{-MT0^l9%(gZzG5Q-6rB`t^fs^lSXx^N7&CP`Zr1e*Sn%YA%ovbW|S`-cD zgb`-@|S&xD?vi)!%QODZaUljklyrW+I)+t=wU``R_(D4kX#Qpci+r33g zGOIa5cDyAZlsP&G5y7`IAB^wpFIs}V;dYy3&or{lX%r<7ZO)ESPUjT@=jClhyDeE~ zUN;^X4-31GtNo4I$!NFoX~r@R?OtMjxl9qnC@rw7=H6nW(=7g*7@NpN1;+cu)L9P= zj%zNpJfO8dF#h)ik16x56Qibh?UF{kpqemq<8sE?U+eFkxI{E?)nnpm3~hWj?hQHz z!X-FASow)+`=+k`)l1CVQoag|pM&}FM!;uQ&QRdCC>I#ck7kB^mWM}xi=evZ;lI$0 z8BRo^+UL@uoNY&{1CE1(PJdb0z6gEv(KaVC-kdh=B?V$+b8M_}0`?EJuUe?)nXr6& zp?ctGo-EyRA0hhox9N><*A^@i4Vp8#3#g^Dr52{d`c)%UZU0?Y|D{Fg3(VWjww+u+ z>+dE%hxcI|uTRbj+Yx+?>ehN4{FM=YL0|=`)4*u`}~*%FFB`ZzjB@5=rr$vyC%KA-BNxop<_jon!P1g7!VKXp{BrN9o++96Yn^TymPU zS|vEYmw%?GI~AwdNT=MhXO^9edL_P*?hLdzwZOMeT$%R(^Hi~;{hQtkl6{^d9Gu4YK4C=XAEh(S)$;SuOh_64eTMyd@k!1Er>{NZll=b!Y=jwV+^ZfgzmEYY39&L{jm(XcUaQGU!HB2dl`~K^gAbh zmP%B6{s6QdV_?%7QsbOr5Ukh7Ek7sGw~n+J*Z)Y~e~cvtw5$BXw`-N44Lnq6yT(8k zW(M`<%i&inARV_U>F@xE$fVDugQk4@#jJ?8E=XE^t?B&0X6VoBy|m34IOE|jKZA+O zdUc2PLI&)+E`Y}8MB9dXCM7mNuJ=tk+uLqvSkMDDLa^X_yGO05oug)wBR1T?d}0*N zL_ut0M7=GqgSily8;NlW2!Ny(JMJ@UhWg8VXn)qev+q(Qs~6RmDm*#|nzJ;scdpu; z*dfWx6b~EL%$RG1xKr@Fxs+yFzuY4Je_^Rdc7gqltCMpz4MYjr$pT}{TT88h)if&d zc*LEGI!)YBwg9Ew^N`tEi+LuB`=DjZ0L?Q=Ba62+QeXFP!1!JrXc3|)4c?|sI2x}& z%SN~7gLQ9^G%8o0KU0!MRt@oDp@_L1aR9LP)m)!TrXbC*EZ4fY6s5SNRGJrL zQ<2PA$|f+{^BwHPEchz8pF9EyxBp46Nof3Qk&Pk z@HOchuD)Y@qjLE6!Ks&rEH!N#fccNrh$##ob(>@2Q&Rh)G>XNh< z(?Z}~C0=Z{8xJgE_KuNm%JZR_q1(gj6WEBi7GQ1J;5XIQHHZCX$JTdomcJ=%-^CdZ zkT&Pum1r1TiGa<-xa`47l;_WicyYgIZ&7u7_?cS^vj|$=1eyUf6Ue&|!KBQWZ^mA9 z=gJxrPpJ-xH@eF1Ty+Cpf@Hl;N5VIf-=NwuvYg!i_1BFItR;Uq4*m8v(&a%v_g@yS zVmx+`Zg}46gr;oCi3>;>6J{RFe*$rE@93s}G!7b_@JhX$D*m$NoNns0ySN#Ig(ZAx z6u<`9k#^{Q1JN!QWKE&UC`~|HX zkC)B8RAz8&4UVgyw88eS7u+)FPVdd?b4?{?HvWZEmrqyz&-D|QZTGd8jTvAHiyb~| zCzYiAfRch${Um<*K3J&?uIX`HJ^eLju&hXgHJ@$50j$V&b&FLHVeg0NtbBJ9gH=v3pY*yXTEznOfMohloFj_T^){$zr`_5j0sc{X*f(=X&YNNyXZ+ zk(I{axN&lNQV2Zte3!rK#2{Q!Fn-%(SIV~S{I6aSV;7UHXviunPn>@L$69{ALrHVok=4AKGN;D zdghETxT&fa)=#I~Lcrm=VN0K~J`~?zOWHGzN)C=yIF-V9ofH(@R4M}<&NppA&qc}n z*AjsBlZd|wX-q@bl)+oevss&Q$F2(*im5J4#x(vo^htq(m@cRZGIE~jg|?0J&Z2=@t)IZnu9c^Pkli$@ujD+cX#W;dd9hS0)C3z z=B!;2vx*trn2*MNjoTraGIc>j7e@i*e1IJ5G2*iOmQm{Xvan2h6nCivw7Y!z+mmh! zZ^RsN!G`@g$X6{ij zJwQ2st#(|6H$qd?db$ZDOTZ8@^Mny&BrNY!6h*t{d~+sQ4BpzY*Vv33LgH0VH-%*i zOE_Lo{b`I;Z{s_AOU!t;)oI~Wupe=(9jFM)eE23=yXIpptXnh-{kY49Cduh6)z_{b(xY)7FM9Kl;n zmhN+DWr_FbO4>K#FF;1( zQk~e{ytSJ7&VbGVSY^ZtA)<4Y?vclxy08SNE_@np={62f{X#A0Wd#f@u70oORcH@p zBlO|0c%0XEc zd9*{m5rh2-ZgMb-ne16xZh?b3?b(I_Ic4cD-nb~;@;C>_s}*pKS8!jagiHl~Sk~tgi>3J`mc# z8m1Iis)aH2OfIy<_ILM7=k|2NNrT5yI7Lt2Nv@N<;N5XIop(M$~^tJRm_yk5&P6_!3@~15kGxd(w_cUo5NV6mBPh{PY z2hvoAIWV=hj3BlolCJGWxxjIc$?J*k=8_p1=l-+KL~2de5ws?Xo3W&Y>{7|PL(X5< z7$TCVNRsgIQmk!5C1~GQR#sACNh>RXhZx1jv6=Kbxlg!r-MjLYune+$OIb*kZ!^jM zv~Q-jaQ?2BF~l1>(s;%gn!#wVm{`lNu@Ak6KiGz8Ehfg|51uj+wqj%n2N^FSsg;e` zB(S#DX4cmIG8?h34l>`%un?PK+-rFSxOhUkU)bP>0SbXM(~gI;OX=v7&pLY5l14>FCGJOe}h3&0%E z1$P~Ur=&|XA9Q;U&`o9_m4uD0S7lO(X_<+vrP&2@U?+r^-8fQ6woaHWK{GP>lK8HSP(n zxBo?&37&cJ;v%|frCIUfB357dxt0s6Jo`E6=(X_6T4|GCXT8#x?1j~HzfjHHzrRZR z=>+DpRPCGzlI@oOm9>zfp0Mh|>}?j`zH>xtTEs*(OoTrzPzq9-WkMQrDvM_M z6eHay8*u)z6hVVnB&_T65`;pvuq@l#=_m9*!YL1q!O2*=LLL*Gu^HKdl&JSjCY_at zBG}s?$x%^H)D3l5(Q5A zwhn(t`irw{U261Icc`=9zWZb!yZA_X{`}2rUM=I}_La+T_f8y7_KEnumJ#B;rk|^U zy+6lI-+$tCT}#!WRNb`idP_5on(@NmI-b2vXqXv?t2QE9UeFY5cBx6q)}3;Zi)eV; zikG~PfRs9T0p$LBKc6)Vu=YNu^ZLEMzuzAWGiyDc=W~BP&*!-gcEEP&V!mr}=E#sF z=?|Gh$YXN$71!cxufR(;S_q;uCZSoTcU7>N!#J(O4R>&k_-8)HI#R8M53Z+4x>p^P zD8eopew{^4lG&?o_&nJj`27mRI;xG2h>s-8?*3!4Y-X)}4Q3EMBTItv zb3x15o0BV2--C=97QeG=yN8(K4e^l_PvfPPW&xZTb`<`=q2*M8YJRj2b)QPjX~^b% z)t8Zz_xl!|J&=1c-I2;qf^;>z2e)UX&;#(i{VK|BwqnmxD97lwTw{u}-`6eD&z?Oc@89`mrBB+F zlbg=n&yYTO)IG;?CM@T>GhFCO&+wPlhI*_E%Xz7Si(Hpu)AD-~^qHUIcKR|e_xS22 zM47}r)$~;~=bRzjmhGGO%mUBQwB?u--wk#g5lvp*3jUa5UclQ|dO#b^8_1hwL-+v; zw8ym8x>#+7el#Cc}I6j@G3nbNcknaCUB`8c&JI8|OK;SwdKdR+H#GP&t;gumdd8{>UHBD74l+61VnP>|apcp4 zJKEO4JmGP#)2XhL&)y_l(zIHYw;PY_^`PL1N_*ULc0-YWrheNyyh2mPyh)T~s`>N` zqD7rg?Ot&OmviUNp#P0|G&c70bv)SzpK?W&6OR+eDLl*EALbv`UfGxQd?NMO-QS6h z6&C%I1cyy!?ASjJizTdSd)26l0j?+(|s_u*X$K4^_u153HH4^=tl&pGVRYsXy< zYbd@qwokImwO)aYYCdtz`%dFj8JbC-6yuFf8Il;uQlHZk=!BtDg}6epc}82>+-(VQ zo!oV5Kta$^NTk?mF+{korPjjcmhsi|#)3z=w)1EIN?B?rTLa3Ffn!|P?%q)#4dDdY zqehnHQ8ay$A}b5qt4|s<@Psf`b1$b$Jv~+O3i|J7o_wds zK2)}NQzYReDV3ipPvl>9Q7)hfSvG*ewB>q?AM9EI}*Z~E~4z1+^_A=Vty|w z{VE<#{YpKHe$_nQuldhlKjc1Fu9j~bP1TzJd;JM4KUhYqL(!BCw`FW68O6MD;LcB; z_!>7%$b4zt;5NWc0&O7H?@r8Dn-o*4>0RD2&hn|lcIq#87>#qM_mm>(C9bEANhFV+ zCwXMnMZf)!MS3oTJRROSh#Y}f8;|q>MUSM7Vt^w7+NzVO4)S!sDH$TI)C`l`y8RW5 z|i*sbCzY#g9 zyLW(WO;LRpXA zYx`q2{f+ecIx#@Ny{4hTTUuY;-(>X1lQ{zWy}G);+BlT6izC#McLQfKjzWv7TkH+z zdfG~LVs?weX)4ij#}k~Z=flFm z%IV{@O|e()8coyE9e$r1G(;vzI*^fW zO%#8Ejyx1bvP=@=H-|8aF4{SFZ9f{62gqyNowf0St4OL8toVahIX>i0NV9b9C zwjZzwa%FqauhGqzCCE7K*6ev?D|6v+#y;Gnvr6DGr+Gw~P5O?(Q~0{ca#%UW3blGr z1|k48Wo$&*l=tpue1ZQ-e`+6?xy9RQTJAD!p9t9vsMFecjp{6K*x1>p)+55q9u{Bb zr?tsrLf4jZ6H`|e7Ucc9t+d|55sNddU}5gB-!6?!&s#e~fL3zJfYG%LL3qG!^wX{ooo^Zbc;(%wC8!&cFU7($jwFU? z11L4~m`sD`p>G+TGGiX|MkV^?rpZ;uL7(+OXIfnOc|Q|X$C=&&O3^*Gyl;5;e7d1B zc`)7EdRrN1G0;>4$-1_-JZ*_A5$4>uZgk>muj;_{R;~7#-->HUBjn&6=fxFQ{)!uf zN19Qq63RL#m-{wAJjSmmR|qIqUcB*}Tz!Zq=bvjv?x%2AgVFpD=Y@P1ca+gHl#N;v zeLj4lEIWMtjN(fbKVH7s`i>_}(>GCwV8bYB)pptoi?-l45G80ahHgF~=*ug#OY-y1 zEkk}M+^GMvTdVGkA)tsFa9Rc~-r%34R>-$lgKzcMEYksw&8T|Jep5&K@bn7SQwU7IO)pkmvml5&3kNm zPRpIP$Sv2d$LWxtR}MNnWa>p|*P5YoZJ0LHeldi@&1?9S=F#bN=i(!Y5B_Fc|8P(3 zK*kBv-NcS@#%-&f_~2OgtxHPr(>o_lZB=T>lBFd1D@x;y1B%sjTe3Q_Xup@SymjFE|F%avIN9^r`HS2ZFK4gBR%oU83n%EF zPIvM_GeKj)Lu;dmgGOeHu!05BP# zqj8Y9Ja?+~GXBrXJv~Ei%hK5GEc$Uu9<{0td2iwhK8=%k_2~0tqfdN#&_#GVCcD@6 zHrk2sA4qoPZ{Cb^nc14Ze#Sdm`i~}`NflF1W2_;ef~Rc-)^a-TCb(;p2kl7gJd#2x zUbNsV^4s$uZ-(EH!#tZ=NxTYf*SB1j1j%&t{&+}(I&`|TK@ld`I}(<)no7HI&SWcg zd{Il4!h3x|IA2{GMLchd^huM}W}0!5q=c{@i6&c+Uu&SLRZ5G{o_!AU4GXme+^eec z709L-F5hm>@TL|;Lgh$E&sDzfOxTjAeBYltXog)ASFqmwmcBoe=euOK1*;}WOeahtPiBksv_W=Uk_ z^EW2|>Yg;U_P25WJ6mKNStQ{o8872_E6%qVI*NOsXt#2oGDikk*z?->b6S1?R%Ae>klgZj!UXpI!f_^FI&u06#YvTuGONxxsr%&~sRCyH0rRAnS;^gBe2-XUtL*2EjX z|H0dvH(s$k*Ik-DtDC{KFUb@P|A;mHxKNaEQ)b4$m#0EXfptUDn_S)tI-dC%zdvRQ zPG;k!A0}$tcWGMxTvTdK-8?`~WYDM5{&LKZwLfUFyZt}Ea+c=B+X%b8dfmBTP9Ji! z7EwWhy2HB7SCBj_U}xt%Tm7(CxUb;sSJ8H5O(I&Xv-WGJHd7WYyQLrZTz6;Wth!Az zdc4^-{{0D)CLWpy{;;+!=KU#Grc9cCX!?~2zMwY0oFl=gPsR*oey`E*#0+h_q`?e* z*x*GobfX+|w6#T}&$_KSxYrh6@JD^fceSI_nG`2-WRG_4nX#Q`7fEsG>>}UWoca>< zm`*Q0TFZ$GRL%+eTFhqs*WYvZayHF)e?oe0=S*rPc_h*oYMjqrK^IB-vM0eU ze}SJIEbz#FXK#q@wd?4iTL!b8C^x>73}$aB@@O0(i3#I4*i30>fX6<`Xv*y0ws$uC zKr)9x%ufojV3;gh<}>K2@!k! zeLL$%32E-d{wAD}6jg`L#Pl6qYv&{7 zQizn%M|cwVO;+kunv^E?rBZ~SVlwIje!$&`@*KbpX+2JzC;nNxBnw4-k?Vpj6TT;) z-X^YpSQRubL`!Cd$^EUueVu+|JaXPHrZSL_ENch-y8n2s~j57}w zc7ORjKf@Aa4!v#V;Z+4Iz0FtruS?w$MK#(ac*4SJS_6L|!)$eqZG0pxST5SF{kVgN z`-a)Y0bGvt<8Xbz$NU?Jg*av+Z;)>to$@Vt#SuTN%qe)@P2%l;Hg>1>5{z`~W8V2e z>!Z;r;kjcbF3&xbkC)bpy`%(fFXEElJ^51N%PD(o3J(P@OlSq5;l$Z2=}7xUd{hE#RrQ&O(FpHR zCjFQ9-%UE#cdaAYdO|a1YB|l++%uFp3Ud`--Gg*D^G}oR#*rK!#ZOS?3jTxJ{`Fij z`_KGcxy;9neA@C|-;y;}5$reM^QMB`T=6gc2Qkla&C~~^bjVQ$%~Ua+3^GH$NqnHLTU-Hgg^EN~ zHA{KJPq+>(%hET?^62xPTLS(6cH~_*|L2jP{{xMD&-nj4Bi}Q#bL3$?^4Q2fz4JGW z{O(P6?bb8@AC5fTZIwKNI}=)CVO^0wIhW!;In2BfHj4_;5Et&!4lR))cCnsgT*Jv^(a$mt- zFq$L#&8dh?irqR<(8wpyTxLNj(+%ZpQipWse7igSyqNXw=_xtpk>TW9K56|odC6gZ zkb=TP=O@>QJa9=}u{x&-u+QTosr_$dt_g$nvo+%^$xJIp$))C@)LSc+nx`{fdF}){ z1;1hbDr9#nN3Q{ooY)%6M`DzBKl^vIR~2;h^&&_Mpka{hXd$`zsUsqQ#mGw~qDtE2 zr2_olU{yPttZD~EOY6Mgvp^Df^7r~ITFw|POXLSdcYQ|C7hnY+S5Ver|Be~0&8t_W z7>aa5z|*(tl&2)m4>~Q!xtVc<>r4E7d;~1i=~k2fEd0zLz)A`?9WPi)jmRqvNg~dK z_!6?Ip*7R#4G_g({8HMq7iOlX0HA9kFQ1 ztkj)0v?R=7h9qQ8oEKJG1haf1%dg#=!7C@=4v83U;4EOhr^-mD6(anD-mb`HYZE(4 zZr}XV^z7QCF(Y1wg(y$uu9#U#l8rIBn0zB3 z$r4!v$mO($eT_=VRg+BFQNN*NklEMh?o+;8XKgg>-S|2ujoB5vrFy|Z~&Uo~JzG_HO+Vk3`#Jc5)R)%@q^#9%O8j1TXjg+~dk` zE1;Ef#hk%bO60e0yzdfpZ8&;@)A@fIgU+5jZ{3V=YgtqW^X(i}3{2NDWf#1QbH+QF*nT2GF(k?CTwQSw3o_8vP>3p_hC(r(B zVI$%~wA^s>&GY5bF(dRLHs>Uvp7i|~>maNv_!JR;wl9Nv#O&m87bFWK_ifOVoY z;mbAQmlR>Hc8oYZRo;mm6d}gvb=Z>ikoTHl19tuHyf|25bf;4ocGveIiwPIH(+Q=+ zXftcSYiWC5yCZ))@l8~jINW5r`p)+Qm*|K4 zKB*rT|D---^^{sryulMKYhU4{q)0^_$#))Ixo#_T{q1w*NC1! z!gm2Z53!MzOphJp$-pv6FP@amR%qDCW$^}wjn?WM*vP_8wiI@rt!rto+7t zB=TGB(1Hz-ISmWAx@O6+DcH0ll9O!R`<}MWKwPY$nSv;2rTE)@laZsy`FZbllL6Yk z`t5ClGLV0Y%f7X8%))zse**T;RiH07@xM|Z^sPU}#$P{Vi+_Byc_RE_Z&KgXLvh25 z>c+XkR{aSh0BIib&izM73iim&XlsSVJ?Dt8LV42D5-~$JWm<;_7b}Th1g&B8I`Tc- z6%TuOA!a}&G7$brHxY4Y{7!ma|yuaz0g1)9<;r{XC zO#ub{)Pv*unXqcqL-~HDp#@K>L*kz_g@iw;{x|-~*=Ezf75IWeY^U*kxoJOIk>o}DJQ7e!~(kOV@&n4olNaSf|W~Gro8lG??;qy}P zZs0VzEQD_H!(53C>g-Hw$_fls#vImi3>M zozz)2?ltKCRWu3*b7cN@dx}goDkr!C-nwY-7E?gua%Zn2>GEEJb0jq$RJg~Xi0U6= zO+PWyU_Oyyffm)GU5A_u)^p%l8aVg}Y&_R?X-u!fykX|yb%)P)#9JVKQ_inIzHf3Q zHi|kLbaYaQyj#q*;FfEjJtzO#eh-wrc5(%MZr)3uD+=&=a@Q_UK{N4OhTnlEWxB2B zEyO)!jReGQ7sX-(4~FkCK7JWCw9!_UV%i3-3bF7mMFg3T;uet*=KSJ?cA1hWu@sck zOO8uLUaubv4MRJ2CXmPL`*x;@uFUqJT2eoQCfW+zR&hCLqQ5!jmqAe*7TgXjJ&@7c zD5ckcVi-AZmww1#*yY1ce%Kns){AIeIo8)`9tUf^79F_g$dv~y2jB(U-YoNnvLuA) zKl#P48?QO6E=61r7pyGY=L5;%=zY9#Vct5Ujq-bGrOGPR^uuc=D|ztL=d3Dqg{WGZ z_L>~?WwL%%H7qrj=A}eB`<-iAH`!N|^QWqJSP@;tgJq9fIqipOtNw8251~s1{=KxQ z)L2$_&E!>u)RqmCDaRpi*q*m;vRro6v^9TNf|#1}TYC3x9@=1$^XMTSiAs%IO_w$x z0~&7#%zJN%esH{xe$Z;4pHAt6e0?}CKj`Ik-rt-z1$)CU!{*WnoEmqw`>x$RZ2Ew2 zI8$KJeaOy+(V3L+0Jn}2yZ;2a+pyzTO+ynNe2dXcsEdz`TuB+)`N(v`>B|*ABJ*CA z-765jR2SPp`R<1#IxNx#aqyLMjnkwj>z-t?q=13G7cbm6TlJ5-C*aLns}IoJj~@@- z9#)i^d&qoHi>Uhd^usi@r~wu$5Ana~0kGkJ#KWAIV|bLhpOJ!fkdiX9aVAPapP{K$ z2SwEGM=lkb-HirB8eP?X8M6L=!CpfI#l8>U+q-)+)jKsf z6^rVSY3Ab48r-7DhvA_bdJ_Gx+gM)__e^Nno(%n<+bX=R)emWbv`|y052D`*_w)gm zRFDbReyL*6_W?Qhl1e}LHe~Y*vQh9vjL{DpJ=9-oxVhk-+A2e=bIsDipM6}Jqbt3H z_tPoTki@H@AM@tY(TmXT54bzW^Z~!;^+A8(^{SaX+H@bM!hP!Bdi2lP`}4YZP@c;D%( zz3sP@JQs_n=z|iF9scf06KdMH1K8@pZ`DJi7>Aj-KiZ5Z4Jq1z$V2_z374EwL& z9D1|sJG(ZlU}B!bvM|rCjVqW3&uk_Bp0+|Cu&wRE;IMl^h?-5Yw_TZ^9Aot7cD4p* zN#>@b9rWi#qCZ;>yXnui0mmp_Mano&tiBR6%jVMtuDBKZVXuEBW)5*-C`NK)M{GI+ zqb3{1s#fvLVvH;b^+8Jg`oU}3P~Txzd$nBqp+cqh;Ag1zjc)2^-OwACnKBtV0U&$ECS^XogoJ1 z4Wm41HA+8c!&)aa9VJHW{mTad>G<3JtCI4Hn)keG)g+d^v}Y`q`1`e+7= zxezR5%!fC|j{4W(VVz_3pkng}x3vvXMxJg|BLX1x zjQS*>bDR%*H>Y+8y>nd~-SJ-O2Vah8c}AU3+mq}ZJ0qb`T|!4)K}V@J`ShDdK0ld= zbpD2*qT2je{xflq+}dg_>%-QnNY?7X4UwnOW@=G~&7aIuTy$k#-TC$@?3%U|qqC*D z>9^P$_i)Ki! zCS8$BYLChML)0R|z~uX|LV?G$6C6GK(z;^U-0ZN1Q_gB;6HV9*;hRVrfVQyF&nj_cwyzB=0DWsg1cVJ$nkjBeD$1UqI}d&?-NLkzTRxTf-HB=U za4MsP&0eB@M-As**H3@~Ho10xa+TiPpWe9l{&YwJM_<7IGW^v|TY4}9bB4H3PasaJ zwVDexXNQqRZ|ANs}k6My~sjv3JzQhpqMnVvDo}?-q!Uehv3!Pm*R`dw%*UIkLg_DmU-?9dcUjH0M#h zsE^lYF5uaBv9>2-e`q!^9_A~f{NFBeUK$>@2((ALNXm){txH ziP)j>k&x{T<|91~t-Ub5ZH+B6EONbpm)-U5LT;?=%7gOLV)s+?SFV6v+spSV`99=4;vb`@^h|D_*ye7RMsAnV6aVt%z0F2~ z3*DQIm_V6fx~08qb%LBRmY&P>N`e1ZwaBB9?IqV{ZDpg8S=GX79R#~2)-GC!l-HbA zBhiKg#O@tV84B|Aig3RwQ>46dbXDwlCNX2QVNkkW7ChQ%Z$k4W{aks z0zERLR$um<(u%&QJj>mC;BWF;3|xck2*_)ew+-H z?{r!p4<=oV3F9WVvVAA|Xq|l}svH&3;8;`y>CJcq+#{@lAperkwh z<-=nB;(eX4>l!}h_!HN^knziJ*=`N<`OmKHk@1}TXdvRb$j>J-OfqcD(t-CtyI?g* z{S88oM@7nSc7HG+_t$u0{sR$?YCEAP0{Wd+SJ=tLl^u6hwyFiXGgf-|Y3Zg|rOBvo zgI9D(n_oj)JE)g71OM&T1vNCE`jEY+GwA=0;?sV_3kUD+M*8))^c*%(QCRE0gNMrOXepQyb=hY_PP z4(p(3IihMdaDJs|xfr}55wR`hY(5GWcpel}@3!yq!_8PHRQ}oJgGh#}>JEMy_ra=4 z_IZ|l7PHU2>~lN&OlO~F_PLaO&SRgk?9;$LUu2(w?6Wuf6y)dM6aGN<*_(X|`21(B zE?xJpTP=Jkx7xhe<@5B_4+3imci=Vld6IqRv(Me^Gn0L;VxMvBb20mz!#-o!XBhh& z!9D}nrw{w&VVCPrj;OVCU4V$44qKF!ftM8*9pB} z(GU9$xVDkU{#7+76Et@}nNU(v@8#-ZNg^6zK2@R*vO90Iym5RyAb}wyzcW zf=^Kg>IMxpuD?-W3OCy88+xw4F;IkcV|@d+{)Vr(8sA3Zo2O_qnUlyaU~~rf@wL^6 zKH@F*PRT*U+5X`DS9WA*q_{JdD;1|X{;TqV#|w;7b*c72a6pSL!;h1uiZ3V`@W@L8 zxn&T&d1oRY&5y5p^E7FSYx8aiUXr*}Z7ThoEASfqg>zm=yX4nRrDR(ppMdyivgV&^ zUOR&#s&su*H{)`yx&*vLDRhb@&?%NGIz?GF`((EnYmdVs$`y8PBK$^-LP)^^?5Nrs zKe#OoGK(`wi&*- zKCkYtfaND)<_y4WfR_jI^T3zxDO2fp9Q^eHD0`0i7}0V>WGR#!IAMl8yve}S&dvF6i?B`N=vdGu1O#u&?!qvcwePSxs-6On#1oLZni=RrX zFnfvazFmU99&otAGJB#olY|~ZcfqSbBYhC!XF~p=x*z3RN3j+?Ue$ifk!bIxX+i{t z+w!>ih>#;E(YWpQb|lF-BZWz3Scrykp=^A--11Pl|7U6Bu6aO*gSI9?9*SPBP2yN4 zqd$@?Qwv#U67s!!)XKJaRNsZPD;7vY8HV{BjUeOzO|Gyx8q(2r87X+pQic`sk}Uu> z!eXD6#K|^&oj9smI&lPW&bNdsjzq<8g#0+V`lBeTPo`DHLtL4&n@PVy7&6czyTuah z?)R6j5ix&8Je4aG^EzidtBK9{h~3MOE9=VJdA=UsLc*YFd}-yVgI!m&;{WnoPS>Y= zPUDQa{av3ZaCI}zeyf}VplU62b}Wk*Y;=Ce-sW*09s6;n=riex-B#C5x2@MN&90bx zhH^nXpX^v4A5kz~cXn`?n}YlvJM&9>!wb0QZEM+@VJa0Ntt@J`3kl6BNkQg~hhe|) zHxq1y;K$H>)H2CBdfoEA(g%&)A&d0E@AxrR>Em-AW{orB!)9QPdK~l9n;yrUl9X}` zu$x|Jm3yRE$>zvpOlZ-4+#*7o*C0NE?XjR~(Q80~ z10Zc36hXB0OJru~S%-+A@S?RIw?w-=APuA3bLi{xS@iQ#WD%5WRh}EXk^Q7*S?u(@ zAa{xFBjpl-z0saqX1CpD9P*I6>D)}wOU|%E9-j`x`wJg$pE5K|XMRZ37-QLVpb2G&+yZMxA)+NE331gB)&J_3;7nVjPd|oy%;~eRb+)_y%3*T-+k2w^ zYs+H??C8Y&7V;)+Ytg5A(y7wY`Fv4}PH1Fj0rp^RS~G7T-Qpe=H;m2=k|4|d7J}X5 z(Tef7xGc83!w*KjGm6jpnQ_AtisKW-nZ+)j&0i?|iL&n8eDn(BZc9<#nT;Qi-AF?& z*E%iqY&Iv@WN%d^+gXV1Wn}jG4w-#!w(Uu9jaU%}n#NA2c%$@2CdPKoi1?xhL38HR zrL8Z^ITy_9>ijz5C(~UA-{CT2IDz zovZ`hD8mtl7_@isTS@&2+m~iqq^eXVfULy_-`xl^9cSj@c;B6colX zZ?@yg1pGG?_~`_H2KaAac2B6$*S5 z!5;zs3Q#2mzpN|%Rd%E>0sb=zyn*10f&cV_U>^p*zbpO}yI&pQKdHcn6MP}?pL`I^ zG5A-y;!m{u2|Vy0SKwbEc;r+O5GA?xZ?pqi_jhT(6)T?y{-X-~D1!eU_>W@kGx)}D zyTB)4?GyeZ3jB)%zaRKZu=*MNvaa~!vHA&rkpll5!S4h9BCLM~f3OpOz^iRo|AfC# zfq#bJbAUe|>z~2D(uqId)n@z0LYQ-dcu0XCO7OdZ{}9$cgCEru|0BQ$IM;~ZEAW8? z{|)dT!1`zKjfXn=8}Mo!;C+DqZwmZCf_DJ_-?07}{PKNW;O_(81NiqT@B;`w8~DG& z`e*P5yW)2NUIYAl6nKAv-v<19u>KkR{;v3M16~FEyA}A}1iuCNcVhiB_)%T)*8rXe z{;w5yUxMER{9j}JGx*l;y0pK_{xSCN8quM^dlUQ@z`q0QpTRHx-V@LAK6mo;EPy?G z?>mXtzT?V#>P&JzZ&>m@4~Wg{oScw_RmvdV?o!LutIn~)Hl+rl&{xx)Ucy$a&~Isl ze*1lAi=MvBX!9{KQ-LSieDyP@O31_tW$-U{!Tt0_Mx76fn-qAW&R5fbe*;!1gReW# z1^+oWeg0g5XY@H4_&>)AW$^d*bb&v^DD+`*odQo3`l=21*J6b-_$6KOpJEjHn7BrP zCklPl0{m;RLK*ztuK15L3Vm4oOo1l~ebof~tFS^D{EJ=jA7d2yn3$@-6NSDS2mGm6 zp$vXe?bTJCJ!mgwG`dhsR!R_!-qq^#6cVvU-6ir;I!jzFVpO_JT&a{GDxKDZ`CW-s z>MpSvnKg-zz!@BnSHS3WzGzWO5S?Ds;1mVevBtPdtgv>L*nEgl>Qb@ec1)Cd@^;N`YQtJ}mv}Y4v&36}BWf)bi!Dm4h+6l& zi|EHqSjp}ZJ=;1F)9)VuFOSqakOFX@YD7JK1tXJwFihZJ5 z@)90mFStvbXy`1_jx%52kz1zY%qNn1F^?_)>orX8`UcGtts5?68R_vWPn5&CVJ zOCYM9c3twIQ~#Q~MB2U15_vfH19|C4=ebXGd(jp6xZ>PD=`OM8ZfA+jIQd`Xr4yYe zKT+<n|VC=np7={*05e!EUd?EGKlF5$Y~S>oySDB&Z0(a8Z2 z1wV0IQV|Cb=Pq&LN@t1obd>OrKI`NGh=%Vv2D=CF0E^uvcKz5{;_7E8p+VrT4laPG zc-j$oV}T2p<1UeQzOzJL8cL|7Rh@hQ(eXval1O|&jJw35vz;Y2C!+)ppUw_WfGGLo zLaB#v9Q(svV)E(E5-V&(%cV`?56UVbTK+7>3j{AP!d>Fo6GY8V9K`y>i67v8dZJt~ z=xHa{KKni9kDc~`(3172UkUoYsP@D$qVS6jVEy3S=alvy)rm24C?ga8zb&nG&Pp8C?$;ckq(2>adS{IWfOyt6WYVcs(M3Mn@mQ?|iF zoo)88Yh&hoV;W#eNfMGI?O7Fmzby?o3yK@nHE@PQJTK(#p-)m;? z@9~D>j+>QhPS4fKO-ZTCVX=z_#4uhtCK*;#mA}g1K)RNdg?4Ksx9V_mhg}uvVv#H6 zD~ty6I&2sle?q~xZrYKtstlzT-Vl$d02v=a8kLnrr{fDBAR;SeYT0;LDRFZA8RMmC zzG*$g18ZAjmroT8pLUdoJu0iI8mA@Iv~DbB|K5O(vcoM7^SJR-jBiY{rKKVUI+Fi% zDQhyGFv@gP5@DZ<@`Xm>+ zH;b!CAH6ozQWBOU?&WmT`XM&tBiJ?f<#P6wz|z?-e0WAWV#njRP!*PA6~jYc+Q%KW zgj!0&5J_PTMG->QUBnKeJbrQxL**_@dLemS7ObXmc>s$IvVRy*j9WS79H~`9)=Ojg zM${C;%-HqGsT#qMwf55tC29`c2+7B$9D2D>ac`JAg2zZ$sj%n`w(9ue?x*yF1eG$A zI5nQ=Kk<+yzO-+Yq!pj&YmlB2eR!s&B8xip&|*%vY#Ar{d-mxYH6RJTkbQ&&(*5c_ zWNY_&(N5^kq*TGDSNOVcX?;~MzyHGR-s@en8rsi!ho=@wmKUHseL=0?s!<)X$oje9 ziVbJnn!0nB&L9)3nDH)jREg2v8%vIii%xgNZQ3H;{fd*nU&*8_Y5l`(rG)bUa$9Ve z0Ex!`X>3W3SY4Sh4H}idiZ3TeT#8sX?nqb;qRdKOXT5T?dy!ZEoQI;dME2v0&s+j92KnDE!q1|&Io z@hpqm^wH*xh=@vMDfSCx!Ft>Mdui;!4m~Srj!aqe7t_xD*SnkaN;=W_UN5$< zB-Ty_I?D;c|>80*+ zwdYj7uB(%tsaBOU&&guu<)rAICxnkPbk5A=Jcrfu&bkO%_wxK0p6Ywbm3enJNo3?v zmc5shw$plE&ZuP9Cy#57GcYQq(gm~Poqkg>KH8FxGVmXIyN?88JfiV=6vi7S6s{}0 zRCu;?yjhvx8L&@fBd?vYZp@`Iq0s2~A4g=p$mGJ4g?9LB2$9md5*6&hc-_YhVsEFi zzvLQ%7AvKeba+syR!v{B^-R|3xV}eEp8lcYsHNigaUF73>jQ2!#T#K2*d#>i2NyNP zMca`8?xd)s>pZ9 zloiG0%&Xw`Sz9klw~YdBM0cINJlhBZ>9y+Wm2a&T#krtSx7UUBA3J62*|fT58Q0A@ zPH#_GI~sYpwi<(e&@^6tw{3R#yVLWKpIn9UAIE&7w|eq&ueKw8+@w?cVuGfuouZvy zA2mO^B6=R^8|`>V)ZRo!7$arRE6ejPmS?u~)r~IthIme|y4Uh(b7b;ipTqQ2pL*{1 z%Gz+Rsdg8)E1PCY*1yVStN(r7Sjj(^lQERO+-s~ewWNvmyHbyFlu}RdC>d(O@4D2J zcnVo)sjz(*LsXEbk?Jk_P;|Tp*F;Zh9WXNo!H*CrM;DvMR~1;R>~TG zSkw??;Jgv$alS_4p2(IG)}_$QYuZu_ALtP;eUJwC8vF&k)8<-yV!!|O54ac~I*AMz zRpL{q|A-?!Z2e)1`NO*{6=>aKkA%QZ)+b=pK)FqU#Y1h(B2~5tOn_A$tRPT>Yw@$+ z^@d#_d_5$rP4(DdH+{WBwvqzbNvfROm#POG#^fuRwkoGq=dPnZF7Hh?MWlJ4mT*3l z(+s)F$Z7+23$X6aeCsR3a?_}T6DXo^+l93KhoMnugD=ihrj?&V{n78LehQw4h@U4g zYel&=^KQ|+>7Q-r?^@NiN~Z5h&SX2Hm9#S6Dmq_V5>&`}(5^zfxx-|wWG;}Z>OC$%0%M#XY{69w=eu=pcP)xHK*ZQ%i=_g7%|$B zz?YN!z0>1e$?Gx0LbhQHE-r1aXEFRz!t(pgs-&lWAJO;(Wyo)LpzK1J@Q*QJ4oiPJk*hN%S1`}O`E zRN1$;f;)6WS+F3AAREJw*0JQbRWPOZeh5{|ugH+q>LsfK5(oKmJAoQRfq-s*kHVD_Jy81bTP?I`j}rgtUADY8bjNeERT-d2regCgnKBt}Z6r&f zHt)zUMd(=qcK+~`x<$5~O8ezJ&?e9Y zX4}bjuje(gQFg^m-Uj>531r{t2f79x;0pR4;4>07p2#S%t;J;U97d zfZFG=zFYk()y5CoXjkhkRTt%zY1qeZ8LLu%Ac)6Xpo}4{G@@pKjG8G=Woj#)8!0;a z*ezCmUEvl{07W3^PO7Ipu2`yJzAi$)Pj>lkdS^Z?x1o3sqjX!VPrFw2DEcK$9fgRq zl%s-Zi%ePQ9JbrOIw9G-`oNEugY4qTNfwIdN26IbJS5S-jQI|NvQXA%CD$|deXHMm zMmyeQ)>@0(U9f%Z&LXe1{%<0$bz;)qk}mL3`;;g!Q!uH`Q zUR-B9I6dO8b>gzTF5}t^pS5j>P*x<{_Ocd7zJ`o?bS|}oE~w*{l!pINiS~Wr*oLvX zbEpTt#P+pT4&GA8b*$8Z*!Ed9+-vU0=entY>0^aA8o|hs&qY{M=n;EOvMLnKkm*{L z`qxemo6ZrGM?Px*!oBj$Z3pDt9sdjxmP6!IjsE|-O{fnM<65UJ%w0I++k-SyOJ}U^ zuutTd7|KN#!^JF!DqZygk=mCin$SiSpyph(TMs_fR_Zxo88VCqVJ-7sIT z8@u!zt=uDz<+l7a^_AhPv;R3fXf^WMdV(95`c!fRYs$`M9NK6z?FpMvdXNn}W8O_| zFwbtANu-xI9H2bwn%zXQJ=a8|_g$ox*PcGsL&x5pc0UPryrlW{&~SUbt{JI|^QAWjEX zO6(Td$?3dO@M57Xy^!;i>`4F(U%cWfyk#c}9AyJUo*0{;N9$Yt%U0&!kBT=a)J3*W zB|e8JSP;BypFZX=`xo4gYRsEtt{b?p@qUyYeh>wDvc(yT-gu5Or0sV-S59L`o^r%b zQA{XsYHWv81yT#H?iqksy<{DPsM_RVkH~ej0x6r$78CQkMCP9$#B)IQALN#$v7|g6 z)HC9dXSJ#tlxoM4bM|QfdXEbp{GNeAPxXVfX3oPjZS11(3 z{{}3p{xz25@c+5|ssd`|<87q&?Sy=bN~`hxK)Q@+OFSa&_9S@f;a0I6(PAGj$?V4a zXn87SKWE_wY4>CliU-mQOdI}(dQ3s66M3kWI)`Vtz3c*u+a2!uugBj&on+~>Ra-D` zpo!T!A~|!#2IrG*r|2`Wb!JV4iYM>bJeX zt3%*9fxTn*8^v8ZqDO8JJv`K+kv}0m64_7lj{E>xqG|L%EO4<#3%b*iRC#|s%p56ohmWPY!DG~kK{M(QkN;8_IHr_p59!lj&(;QaD^9v4WT(G@ zKUc?i+>ys)(Id0b`J_ORq}?NBxh=mrQJ*t1le35y4$63}Zam^??>za5(No4;>LujL zPqNR;PmEVaxu1xqJ&V@{A7Xp&R#q6H$T(C$I3g`^#AMb%!p)n)ED79y!IUT z+z#ZROFqY`v&A-NKRHXb&MdYO&H44tey%_+UG-e@7%Bd&$|roMbzmkiBqWJ~=SVy`vGoQ;jTg<&|8EsarBf3sJ_os35E{1CJ!*bG#Ias3ZKpY>zL%VA7i-qn| zQ}Z=cN(Y)f8o9S%860x6MYigF*rHlzXjS8^gKr}9I#MHk&R41AksDfH1)tZ1)}g7S z4WSyB<;Gr7YLc@`k>A|*j{B9)THl_F$PCYCq=?eZYL)Hyl0IJAMD6qGJJpiu1AK6}6TD}eU>n^8FWDx204($d;cqbFrEMR_ zNw@cTnYORwq+4gbB$bDUY5NgQx{>QOAzN?H$i%qHMQ_K2hG8-bX|oz~YV-Yn#FHY3Wi`fyt`@?)3e4mU$-)Z&_x#>yAu zE|0D0*h&02z!-58yQ_?;GM=zM?1&#tc9>a(uV0#a(NZ$$N}LCxZo>*%ra6_oUEoC1 z&`1s*tlvXB*#9V~X+mp=jgQPigzJJ3#7TKljkTZ#-(NRnra=-KRLEJEc+^d`id$VH z+W)SNBFo78EJ|;Xc`dV%ge0jY9=z2FyCZ=x)I0mxatJQe2XqEir)FV7;UXLe})8r>*fYG#W#FJ-J9ctVa99C+HG~-&2N^tS&Aqg#}AlNM;s}rAqtXXl z(8AwUr61y{)(w^~Jxxb(vwqK(kUZ!CZQ|vpmqM7XQ0Rtc4QM^9r0;eBR178qC ze+>8vT&*V`51wt3MJJSLKh1C?cvtEK8}FL)2_i*#)@Y-`_mUjy8+;Uta&{)?dA z$dPyPG$J7^+T^er8X>KUSy3Ef4X+EDVHXz{?9Z#4HX6PY^ednszJtzO3P)UPZUgfD z=m%-xOQ7r_-VR4=* z*xcT~urim9$Ob`D;2|R8cthlF4ChkB`zNCe;Ofp7jMrHGlL4)9~?}dPmU%WL5&Fx3WD^ zl;$`5P91!Vm*(PscDH&{_V{|Av1`UkUKKB?hl!xlBI0EIFjBp167(ma+F$nI=JqTU zLl?b;Y-oBebWuB}&PuA+eW2rRSVv`|d|gh%BbDUU_I#-DNM&D%IGEZj;83efC;D)v zLMDxv$)HR|k$I?PIa=wB$Nu$Ax0`S3LQ zvx_Ki)7+WqTRUf5*Wiyh2VG~~nQ`tKo9QIa0I84oWaoU&zxeL?_bW`_SljQ7kB_o@ zpoc5{)%K)g53;`CqeD}A@v~7&+X2%s%yBm6*irP*l>Gyz{yO2IU2DZ$E9a+lyS|qx zs2eZcs_&%^npm$6oLFZH^seteCOOS7%3IG^6T6|LZ4=Cb%NRw53~>J~L77?@oDCPtP1P zi!yE%H94)~aXA-OWE5q|+J{V(bTU^&$iL~`K9(_yjkDF$Yhi;LyC?%5Z}2UD4>^&& z{@JM8yK!DAQE%^+cb767=JuCt_r|9j^o)8fmyQ2+k_|H%aijoE?{UzS@x0*WoZD0- zw^3=ElErK8%zaU>#Zn7tm9%a7MftxYX1mJl>AcohbRk#i!_wGi^5pg!=E8g$(hc(n z*R|o!rLhXcP^MuesqC8YEepUYG5wFu`ceA@KMDDQg5V*vGeL^0QjME#8#mk%xsv1? z6oUgaJ3~MOWJps=?-~3IpwB|!UtpEK4|3M>bzuXAb%)h>d;YL|{}<45IRlg&7l5c< zMd8JK%W=wu7m@`2gyE?C7&k><*N8=XU?UWHx=7a4lmv$)R+wj)A-A~`V)HIIZI+>h zrLGar<^)C4Xi>H^C8HTQfhM_RR-jyNMK&ym`u_K~%cby9!mdX|UD)EQr4Jg0b>azf z<7wa8I_m>_Aidm<_D$yBc+1s8k0SSBeyUvd>GE`=+d&~MuYD??3lg;^&SHRV{#xI^ppj-s}s7WX`u^*di^*MI%6 zjpkzWQHNQL%znrYT_8=+hPW0VDnb@DM_c5}KnetS50- zU9u)0(bXPO);OJKK*5W9r6;vPWCh{(BCRH2rp#LzDz5v<99UJ7%ymVk__2HVf&|z{ z^w1BR;jJIu)LlR505X&x?V%64*h4?$rye>Xx;ygLl^{mu6G9KW7xhYrqNj+E)a;5$ z`-gs*qld$`q}Vnt+Io~EgH4euE3E5b5k~J%Af~K0q;(O^;eXQaY=j48Vo&MT0&j{n zQeuYKm1%wu2d_HB7F$klT`{h6QJH>`#4QGWKqCm1JzKTyxin6W1pmv@IBnl(%M6Tf zLbF{ED8jiD;(Q+s0uO zd(a5TJp-2>lXfA*HUJTUIv@g?d^#b(wZ|+vhx(roY84p$pe7$@<$`8)c3Hb~b#+rZ zMhi5fS;h$-J_h-~ZBBpTHmF0heVQLkDq)_9X17G{uw1OjJ)3470`aq zkq}sJ8iMS*Xk(W3dSCnbAzb828DBc#J2Bw;4`FV4@PdBu0UuYu=DYeJhmU`;Iw;Y{ zZcgA_i$6($)P>XHn0Hs9oE%r`gcUqfJ~m^`?DC9lr^&>k_Kpt)Xtct+{1W?{x5{Ay$(SHz0$+tH+JxO_9O;T%k}bOWUf=sTL70714=QfT{WlmhSwf$WqQ^fShDp12=lD$ym+Z&=v~i zsX0|za8k(TdAp>=WE6qbPo<7D0&W_o9y?NHG6btk<38qYh-%{k)G#Y$j0edfiEF0p z$y)`gZ(<5bBnOEvr6WQ-;zLl4G+T4<4Yc%B z+8}Uw1qSIsokm?6p)!?BQK^eRL=>@iRCC2bZERXp+91RW4l_!8>lMm zQRs0)oGXT|+#Z%weGYkl{rKnC8>b*TI)IK-BOL(2%dBtc+;_nD?AQyg*||Cfyhd1^$1;A5=NLuuaoAB>TuuT z);;Nz|5dj6^G*#F&S7U9|Cz>AmH;n-JcVTRqn41Txstz2j>HYkppjNbZ{Cq$8uudB zN+3T5y`XtuvmoqDEKAdQT99k<}Y4bkst0=Jj|-|01o zarqs%?QLA4_J(jaWvh59<#GzLt6D_Twi`cXF3LB4z&An}$5|=Uj zPq28X4PCkvQDT>3eNxOvKyjhV7D7TjpN~o{PYazNgg5$aN0DEsNo6W7LcM2D??cpk zvrE08%`M0>Y>wu$(lH(v?Nbpk(GPJF{SuL#`(wgBBasXJuXmvz$*DH-`YiOKq!-qS zMx`zttTL6ppxUZEl{!X@O}jz&hRh~ar^Zgnqe&i+^4g@h-7LaK!XDd8h(Y-D<=1LV zwEm7v!5S>$)JM?D;~%2M@2J#=AL8bL)?%c`c+fcC(9+(ab+J_l)s70!shW>FPrI)+&0+>8X^lo3du<)>$CuKztu$x-OVU`Mr_d_R$}?EuSJEIimfP!Z(|8Oy5bR zl`|_fXTdq{=~adfl9p_z-I@b*HIRgTB%jzJPuXR$&z&JItxHtwKoB<&@Vg z@YRoL{}g9L>)Qo6Vr+PPH2eVi(%Mne+9}4`DcMH&g;ObIDZrm{AlB74@J zN?V#zmbT#}%{$?qc#!l-UFneaN*uQ{k=EYJNe=9tx3>Ii3n!8Xz77ry`k|Otiyc<| zux%}BXngjc@D^yT*v+M!?xW)Nd!NIzpJi^LC>rg>;Eq{_4euH|;>+qV@3CmBjjfP^ z`84+nS!-WIeAx@wLBag_!}@@~bKk%oX=7gNG|97$Ti$32OlbX9pyw&MM=f$Y!1cjX ze8sWy4%?jr38dY93E4i1+OjOL^10`*uFn6Zr3#HNTHY39fktcbq*R(0Mb|6avOGRc zG)lvfLwsN2%d|(~5ALw~vwVxPS2oe4(JYb&jnO2`IK?q*-$NtxOgZRbtKFj*a>;Ei zp<)nXu_Jz=Hpsl>^Lw9{V5DwB*4>L@gXB1@v2|)D+uqi~;y+9U=hMeZJE}KHRclhz zLn7Fin~;ByHQAts5*TR^*o`K z;GYv*b2gD>OUr;fnI3U{5xJ1XEFpa&Mm7$X2h4Jw?L3rYeStQF^=}*QD;UMb%R23g z+>+0O%+G0|>#Mz*)mi58ah!3IjU;H@^GW+KnpDTLO}`vT)tf}yk7JIRbxBNnn)I6B z1wLy)9WvnIUeQXmPhM#@h9pXT>O7Fc6;_w@6tfv~!m_lt^J7PwskHN4RLkcgqlm<# z9L>;Kqq(dM`?AvZAoC%!-D2Zl=S7kBYui76rjf>EB(mF0ve*-h+`7Ze4sFh%nyJWP zfx736{@D$oiZ&n5IDa@Ea>*C-CdLNh{;xew{1W0#B|J}3TSx%DLrkYn;JKRs%des~ zhXojF^X_(XIb|}e&Ob4U!xs*GU z>w~;Q8Qnl}bE-4IbtIAf4!D>c>vUdff2fgK z_iJ*jv5-Fv<)#0u*UUw3Le;FgsYHHa~<$Q5J^wDQRi-4$sdIvNMUtb`e2P0erzV{|? zDt!nZy$h$Zvyqeg5+B(+FUTB?SpMLdS34}vP}aN_rXvB@MV3#xEBFIXLG)1f2OBT* z5~1@;aK~Y13Fumg;=g}wEY8nn`3jY@M)72C)r^iIS(ALE9x zy~Ya0)oF?1zKl)_J_Vm#F8j>~Zm+7<$JQ8SkEe8$IFP{`JIl~mk)1Q=vkaMMPmA_5 zM)*3-G)#d#STHADuj(!=X!Tp>C;guWujnJM7x}#mX^PiRu_Rduv0W&ooH5L!2G8w@ zA?MYBHZCaL74!aW>DpH+r`Kka=DcvRK7?w&9q0g*SC>y6(|Mi4v}k;t!?bAk>l{Yz z(n44LV|VKuC7~Z=@Gq+F}6KqS2bP zKR*22=ObgkE6-0&t41%&!FRpT^PL@hfZmmAEds4E)1FmkEgJ+C@V@XLHevlw;6Hr$ zsxihBU(CNiYZ(6UiuQ!tX`PZb=a}+-;hB_I4*u|s5$~F&J-w9PjN=>MQ)$%oi;dU) zX$iizx(;)XjVq>IKS<0$uDH5o_nKzRdYabn9$Gj3g%M*V})Y=%b`V_yal)LKyW`JhwAH9mc3rhX7e(f~xrF|oGK%$a}H^8@? znb-c($R>O*bl!b0{d7Ox8!Nk#v{h&n_W2t8?sP0nV&VISZ_sNq@l=L1?QR!en4$KC z*8Y|5zT;K#l)5@+)r(6ft)}u!Z#1o#VWe=b@ckuE z9gOO^_#o;171+JrPgukMxg=^!^rOO}UE+9E;ecfaqZo^wwRR2t`=n&Y_xhSgJU(`F z=a`q79nCmfSCELlUcB=mi|3FVJ%vv3e|Fb@-acEuX4<_+J&A6MD>2iRG$tx$&d(>F z3;Nl_X>_i0*qO6u@@SOynBH;l?!CR=rZ3nwK7JkA+s2C*zqb`*#~n*WEYOdxe~xzj zI2v#jX*aB?+N7!X-U+N`z$)hh9rbHYx>gH+K!mU1@CTxWB2Q2+@c`6|wm1eS_U{sU zS5kjHbmM13@cXdw98fx1>RJr%9Vaso;&gu3Y6c%}YAr#@%GufFp~Z9w=@#yu<}sUwU6GU8vTo({BT#-8XfrLI?U(cX=54H+zrjXtjpEh z#KZU!U;X83E}2RrICc`8SK2efXRA_tRa$Xo8icy8Xz5@tWPI@csAh+<*68qfT>7bkCF&ypzm! z+~23Sa1D-qkb zO#7IPcTP*O#w^^slX~efH!e@Yehs{l{h8-4 z)}{npuikEK#n{Hs>fo*psK!bUVQ)nm#|-hgCWlwO{=jwe-93lmqLpFQhE7_EG#uYv-?@ zUg2N%;ex-kwca)0s^G2-x%eI7p{AmiTy2E8>|Yc69$Z(SKOZBNF~8f8`=Rq<`>5jk zU&nhkF`8fYFK1GI^U|HG`~K)+(YrHd+=`Kn@K0O}`yD$gu zsqv>bDL?O{@zR|}!%w*AdduYjUM*5{gv?w@hZ=Wo1lrupz^8*&r-eA(9#y3nsB zcUK|XFL-{{91~)GZWz{xwdCS$^aOgx`ti7NE9RJz^LG_4{PJH0Hx9m8d4AQ%yw!cK zZ)(YXWbnTXK8!2i{DzRJ|gbrFP4mY5N}WM+;6_{^8<}*aRtjKVJ_P(a~&Sj3)Pn7k!AAay@`_5&(varr>__o#$Q~F)pko)t`?M-+S z`}O>O*hOo;prCjAde{hZcYi%BlUy^_^^sd}o>m&-zAyfZnZAbc7|L|zV@qx*!n@TS z%Q3UmidlynHHtc`5gci+WzFKYAJ^>ep{jS-@`_*njo<&Y!mA zHRNA^xFz2-?UAIxO^&2}<~8|=Nna*?__L$9fP+Ws(heVf^U(6Q zC#=p{fp+7sPsBzWn|!>^T}p&t4-+@$6Oi z%96pm)|M~C%+eo5&c3j=obsLb%G&bCY!AL(5uZ;vmbYx0CR2ok z(0atDWU(|KJ4fDqdd2($!lLX%|MmHJX6eOk*sx(E@Xl5F=i{SRE!@-i_EOAkE;S7s zaiduC{MN&HuEw=X@vEWOD~b0%Pu6`}|JK8M9>#p{H;rotDLdD$?v387ZR=XBWmd>{ zKpAHK(pKOt*a{QP^wAqf>?8WPx5@J`@|?ID?>)D!8+0A+d*ZLg59&=d%9#CGP! z%LDzY+vru=6=w8pOl(d#qr9@aXit}?k$Jy(;`)!4;Ee^(FPzuv(WdKnP1QzqNndU_ zGHl~;Q+@N1=A*mUAEIX#->6GGu?ST^LVEwO3+?|U=Nj~~pO!Z_uh$LxqPMJ@|Fe{Q z+EqqcK9tnhiuXGBWs~REy`xYK`|{!LYK|VdaqUgY`>S`X`|@XaUNJX_cm^OesZYAk z7d7ztpT4F1@hA0{(d;9v8YRvC^|{--(aiSXNP}jde64BrG3twA)>PZ~Raez+w_HxO zWoK}geto5?twW24?{W9-GUs+>dy7QqO}(`_w~{>L-lA!U!8qsE^WLKCuyZc)wYhnz z(;VNtqom(@>G`$q?@7=5$-XtyD)JnmcM^`>dUfo(TIq$$9(?Sz#uu(r;@{JX&)g+q z#`&jOI2GY;yROG@TALaVsh6;IN%XrH``%U?=49)wry~EP0#X5~fK)&#@K-2cwOW-7 zb&@6MiWF^7ULiNM^TU$#YJ?nC6hRv8EIJz zCvIxFfnrA@_+QYuWI0#FJDEZVQ%VT`C^SVoh7NFuKl0IIwIRC{+%{&hWi(mQ!D0%E z|22Q{zu<_N|8PFeaTz2MY3m8icep*l9`zMALC+=6hrN}TKJW3lLOoh&`KYR?3SYuR zU zic+GdHOuaE={JPkNnBuKHd9^pa3thX4R&2@rstKGTk-=De>i3Zn+V<0SUg1~MV12B z0*@n}VwZTz#f|A;MK+dPONKWxeNl(_@&kS~;B^_}>=NJfvYE#7l({Z8B{OGV)~3`| z83=?sn01M#%pD0k12ukwRacvcidMmCFzK3~{F0J-cfuHzr_Ila(tG|a3E~=j?B`sYso|jbcGhUoN=FsYCNh6xm;*|mH4$gLiVu3ZOpKUdrF6> zaTJdS2YNJ0DGD=6qlKFAJ6s{v6Y$4`q^TlVQtvuCsVDqz)m#& zg|$If2$A`5MV;+*8$ELBD+;+gu~fUlVUNGsGP9^G&r(rbIIFKRg$kmmZ$$-q7~Mr& zqRz!_3bfWoH-M|B7q=-1c@}iM#I81?z)>b*e_d_pbVmWY+LX*J>Do>RAr1L~&9so+ zAMx5lp725~&k;+dCrn~?w99ikQ7}}?6r2>-)+w%;(@SPriu_dpW4^r+OC}0;_QYBg zn0R~zuF6QYr99-RuEzbQ0LZ`75@rvi9gDGHa&5iU#d2mJVMl%nL@{T4rpfE&dQt)DM|Si(447yNDoc6GNEZ190IsBl9m&Q0`OI-{VO*gJr zWXP_no@zfvL*W22+Qfi9{gP^G1Y_MKuE#aGz7{Ps#*9~)|? z^mLI(jVJ65M8X!Tqalk^^edu1>gi~%g@yuE9^C(`h}TO75x=IK)8Y-_8qiCIF?x#x z!#L;hLT%xvhC>lYnCiS{9pXEsC_ayhj$B-*H(+;O>bx4Jo6{)*t#9t+iL^yzRrZKi zzqRos6)mFfPdWn;O((RtonsA$;2Cl`Jt3DvAFLv&j`ssLbYIZR8i%opFTB9+g{{wF zM+4bYm>-II_>D#~-vLFubOMG4szB@DM3P{#t5DcFfbN@K@ol!e52I3V)USZw4j;W5)v7N*M8k5lsImy=a=5e5GpW7^OpJc6aW`fws8%%|zuy(gQX~GH zY>(ABRzcKO)c=qRy-V1$K(qsN9eRDGGC6u?MX|-@cLbbND>IxFx5wqPS2^c9yp{Ka zgA4rbdjp}G+J%d%>6|fqep#s{V_2>5EAr#Mdz=>ik;Z~Ssl`-c1`2)5W)@u?_sL|#tpg?0$tF}i7QN{u zZDx`#nvF4hskn<7**Q+?jtwzb^|nZviQHv4ihxpJ4p4!-T9mRevpL^=m6=x3y{}Tn z?rh!F`reM(lr2q>0{S~^JP4=7dhhdWUAURQLxbBaJwA$s(OD8!!0k3wzT?CRb$gKLyZ9r;(@6q zarX*iirXKs_*@v}EEJgy(dd`!u~rAWIZqXzS`2XyDGu#kTLOOXLOn2=DZgHAOXpP3 z&S@CJ}r zNE;(oMyjg7wHqjIhoWM70$nj&7cP=q>a1Pp0l`&ApepFA0tF9?zI&0!;SYxtWs%1n z^ud48y&;4lKh3aKbaEJM(m9W#GO&nU7^@3{l|GCMR3{DV;QAaYIt9^Jfop?Zk(R+< zbvUZXc7feV9Ur+Ax9an2>3bYi9&eD+s=^IKKAn|bWCkV_KV_A!gWdSGk>rJ?rIRe> zZW^PZtAJLxH+n-YEG<7qyMLD`N5XchHs6Qw7!9@%=EoUyn&RS9sAqPEgC0q^Q?*ou z0zNK*lqa&)``ADb-8Fg*loPTJx$Mp{HF)Ns*`3%36k$Rh)v+MUf$=ttYH&TCDvSc+ z9$^eRoLP>Wv$L(jh-?ggcHwEos0?FeI;X|g(K|ji33`1hniWBwt)a0&j2Sx|kIjLtJtWk< z$31Z@1SM07quTC4WhM%qLpR&AZ>r87Ph(ZL z{ob%gq-MNtRgoLV<4$GAV(0rw@d`UHia>1^{ja6&SI17c5o1Gksk$l{Z>d?%8Y}V> z%OgF@c(ZC%JlD8%&swj)Hmf$Mierk>I(Bq+_Gl5uq;Qza0xGY^O4FGrKmo_SbWNhn z#r=8w>r*`#>!OLq0EvdXN>F>~P*6A{wORTRjeUL4n}%?DT=t;X<4|JHTdGyGRZf?a z)AAs0hln#?7X1zvg~E-Xw6r{tN48!E=zy+hG#MDmyhw}cTKsvhsPuLo(-`dZN}$%@ zx;oYi;bj<~Q>QEpO>~KO$+vu;Q zuKD{TKC8z`8lb|VQ6!1@`2pO$*UQ{zW^%ezCfa%r8Lv zBp9Vbu7gGwiZUm!bXL)u42SA&dhePY@uq(29aYrN$~A;}ut} zCv5ccdHhyX6pzzjQ)9o^1?f8kq-XVbPB7=;i&#a$LZ-%0O`}O{t2U$+`RwF1j7E%B zhs*0#m0?`a`B3Ok9>9Ru?qAq(;HlEATa~9a5~Lo_NA=r=#1>>Yb2Do39BB2~aiyA0 zqD^-MF%^R`u-)#@x)-k`LJQF|+?z9wno&g!1l`qvEXRVe6SK1`I~4s($#|iOHf9rjKQ2}8zr-<3$nY2-J~oK^q2)k5u}1t}mtA$E^w^;~ zT~GYk4zCxT7N`B@>@~$4t06HO?@qrWiX|wdNarCnn*@4%6BwOlYv}3#~pC z{+)ejP8K~Bnh$!HfgswkDB|d;aBQgm<8!R;8ehO~^*JKl&0SiN6^hD55n+ZfK(BNt zPR!9oMBji1bvmYcjc7vRO>6u(ma2Ba77>+R@PAAVEA;|*O~Q$Ar~W{+bF7n=12hPt z9rUC3f(B^zJa&4{#jrSxz9V*A>8Xw#X!+8$r~X76CZliiyS!SvHOyBTsO3hQD0P-p zKlSknh9eP|4U?)gA%G45@4#)McZMI88RLC*qie8IBr$*0iwR^gqfBwqkxk4hOt$zu zK6K`|^!X7?@uSnkb0^Ix(&>1-!5q|^Kp<3!KE;a5p*TEC~! z0BTqOZ)7mPKTs2fop-_r1mHw`JXg*2;3atIQ;hVxow?XJq)?6fXEZx)!4*K2}Mb` z7aCQ!N1p|nfxW;X;7#Bda2z-Zd9B>g(?rD$q2KoWl0%^bx zfZ@O>ARCwfOa`U^(}C~u-pRD3{;~|nG9b%bbfLh=_U@5Q~XaLp%8-QN|n}KJ6ZNLto8Q2RP0^S6U0mp%pz-Pc&pcQBX zc3>W1$85v}Gy{8qL%^HBG2l3G68H=_3$y}lK*AlEdjR?aslX7x0*nB%fE-{Va0^fX z%m7M&JAk=BCEy190HrPQ-Dwc!d$0&@9k3jz2Ob6115W@Ofv15jz>C1kz^lN1;4p9$ zco#SUd<>ihz5>nz?SOd><{y9*U?7kV3fp)KoD2}ECwC`Rss(JYk3mgL81daj6 zfs?>zz*(RbXaf>3r;rTv2U39{fCU% zAPdL=CIYtr1;7lT1h@m33seGbzz>9hML->}9H<8#1=a&k02_g)0ZLoqyVGC`--8$7 zz6`ty><115M}c>N6Trv7Y2Yj1JkSo9i!lEHqyPhfbYLiu35*8D1CxMUU@A}ylmZpN z-GCFo?m(MZ#BIZZI;=6HV_N*phvFLz*kMZh&BZ!B@y!Jre;on#rMF`a1K10k2Cgkd z{VBoR0{Ck1HNYld3(x`tfjZ*ln4bU^Vg9u@FdUcwQ~;ZR{d5jUW}p)A1N~8NM*tIn zsXzsA5AZS0wE-a)^kpnCaPl*rqvQMdyx(BA9J~RTdkI|byfN(Ch-Ys%aT)!FU~Qcq zhT=BH-^O8$e#FIEO0h3mTb^k+*1x-@t=H6S#JXl$mr1*cZP;UBGwiAo`?axt*2bHh z3~}lE36vEmLkWKa;KRk2jpqT_EC(nbv;)Y7Zvxq3yL?qD<{i&z8+mQwV+DFR)u)Ax zFBjLs=>Aw99r3wr5X+;|{tim3(>Z-Hc!zVmfu@t6ST(Nu(LNevqhp*RvRHB*&uP1U zXoGl|)`z#gg1Xm?x&e3rI(`}00xZR`w!qt_ZKcKSS7|}F;ZLk}&DB=A>YSFn(neO^ zqG}VnOl{hxxG4Vq3pfY-8E6IQkNoKGBJdZW4fqCV2k1{va`k(tOF$E_on7)Xg3gKE zj^aj!@MSm~K6_Uc%XyVEY}58eiSlf#T*#a2kqDim)$Fv5h{`Q?#zRPK{J}XYll0e1 zbFhy>`y?5b@`^R*Vllm5uwg@6V4xf=spj?FwyFsBebAkuJYfm6qV4UlvCP^!dYD%L z$=@^K59GwuX!Kp+C~z1k`IguZ`&WUNffs=-z|+7+;0a(o@F-9ZEC=dy@LMz0_HMAX-iv8*QVj8)uH{G22-pOKfhj-=aJ(WKeFj(rOa}%6C+9??TY)-Y z7LX2nc1JY29as*O14Dtcv!l_SKs|6LFbcR>9*rIZ9tZ9LCIS6{d9x(GOARiTp?m_z zfXzTHFdaw(PL<-`0xN+EAQL!$do;QqSP!^?i9j-NtOVhKT3|Ym2AsML;enMv1&|3` z#Qi=5JOOxt`UvVF;0Eqw1mV^Kb-;3f_H}5x8L-m<+jD74hSri$n>*hY!p0VI!?eK# zf7E{H?UwLhKk_GB>0hsJWqqZwN;|25R6r^q6_5%@1*8H}f&a-0+o)?otDj!B0wTrcycM(vpV%hMB-jqHDNjiPL3UIKoW z`Q2-w6>~Fq8}lE4QyLUcKe%S*Q`c$7Fi$%zF6n$6{U{31$-%Ua#tF71M{PB z*D!B`yPo;*-ezS3^J4HO=3(%S%r}5{$el%HpxLz!p33I5s4 z=Wa!vWquGg70lC~SCo0oAAn6A^IPD*lKGwBo0&ffzJ>X-;76E;UqoGIJ_Gzb^X0!q zrl2F`Z5#Y8%qMO~onpQjHq)7>{vZ4n1oLv(lrvAyFe^^xX}agU&H(}@b%2^2H(JZIrv8Ad%-s|e*p16!+b0F zHs-&C&35L8;r}x8cJSTIj}A>x4l!>5Kg#?hcnkBh;3t{4fuCk>9+se-W8M$Ejd?1# zxv!>G8hAhE!@*OTXM?9Rp9nsjc`kSs^XcH@nU{c1W zJDJymdzr5R4>R8YzL@z&@TJVRfY&qM4!(wYGx&Pur>??1VxIE@lhS118x4Fj^Get} zWANW%;M)v*yMe!K;LQfU+ralTAM!hsa>(F+#K4al_%Q=-G4K-xe$v2C8Te@fKWpIU z4E(%-w;8x{l~y)LE3<(o8+bqF&%!3f;Gb&XX$GFoJQJ>kxfd=SQ=2dzZWi+|(6`bt z`PU9lP$n{OwxNtMUkU$QgMR_@_3)q0d>ibGnLh*n66TxXU(S3#{GH6nbsIS4Pvik^ zkom=-CZ(2n3&O5sz6EZ*?hm(t`FXf&n5Xtbf5$u%^4IPtzHf-pciC4faVg3PZ=$QO-VKYy+ z0ViF^e=*!J^PxAG#4-81VMBTmUk|r|c@Q>qO#Zd7X<}XnzL|MF_!j0(;LXer!QIV# zFXEwNif0>a+L$Yu(C2E+e>`|5^ARIWN;dO5z{{C`242T}$UmBtmCR>>Kf`=K_%`O} zz>hKCoMlo@FmC}j_t(;$Jldr6W4;4CoB5<{lQNOHAABD3Bj8Tv4P#A8n0X!CTIS{7 zGb?mV*IN&FDf5(fOyZdQk3xqA_TK<^BlB%=DLmQCgImFz!qPGMpGH_G^B~+><}2YY zW}Yw}S}|_}U&{Q*IFpi^qQygQnt`V?&w^`VJ{|6G=1#bDj`A=OZZ`9`G5)7x;g8?Z zrZB|I;TAKWRf7A-yaF~97uf{ix|wgg-6W35-wm5u_OAo4V_pxwl6ez&6Z3cHn3Rpo zH{M}V_A}1`Kf=5P?osAx*O(PLrnFAMJ;8i8o?qyg{Hgss&D=RJK{?Bu+Rt+a|MSeL z{cJP%D@cRl*^hiCGd}@NX_NnOxarL6?ly^I@;?Wg@$4T?H7gUDZwH^u+;W{+$z^T^ zFJQhKd^+G_b-N`afqd=vcX znEa0*tyJbsxL;|^Pr*Ok;BR4m9{$4({+Y~^tr({n{Ii*-!GFBLeh^;eq;YITmt#m!L4OJx(W4~c^d4uFdv0@wlQyh z(xlKcv(N9`CV}2AinaoduXEQ$!p38hXTmr>Y4!4;348%jnKMh-dl&iz=9S*YBjEMS`++wwe-@nDBMN&2?tbRy;ZMipPvatLr-+}1dy;wIgD8j0lgFa|qMag} z1qV!uh506MYS+krBHV1|=Mk2U$=`yoZst4S?`3`joZ3FJSqgVC^W4|bS2CZAu+&zP zO%vSp1{*pie=lr~FyHouNjb{A5?n!hN;a#(&CGMbshuVNbhxR^?>vk-H0GNTXEF1W zurFcW4>og|JK+*2o*-N|^ZGxayfHtAc_#u3^6IJ(IG5dFs2U zN6ag7aJ|g$`3l#|d<}Lq)cANR+bI<{O3B^yY*}#*Tr^4;WJR2?@lf4CQ8uRh3 zxbMu{kO#6Oo5^s;Gw*#7b&z@TIJCXY)4{1fBl~%9E10LYqYN{j44Yc!#o&vXXMKZf zXTBDEBlFEBjDeX?NI;!segy6|=4atCE>gq3>f(VKbSNn`Ph>O#Vp) zqyka_sen{KDj*e*3P=T{0#X5~fK)&#AQg}bNCl(=kP1izqyka_sen{KDj*e*3P=T{0#X5~fK)&#AQg}bNCl(=kP1izqyka_sen{KDj*e*3P=T{0#X5~fK)&#AQg}bNCl(=kP1izqyka_sen{KDj*e*3P=T{0#X5~fK)&#AQg}b zNCl(=kP1izqyka_sen{KDj*e*3P=T{0#X5~ zfK)&#AQg}bNCl(=kP1izqyka_sen{KDj*e* z3P=T{0#X5~fK)&#AQg}bNCl(=kP1izqyka_ zsen{KDj*e*3P=T{0#X5~fK)&#AQg}bNCl(= zkP1izqyka_sen{KDj*e*3P=T{0#X5~fK)&#AQg}bNCl(=kP1izqyka_sen{KDj*e*3P=T{0#X5~fK)&#AQg}bNCl(=kP1izqyka_sen{KDj*e*3P=T{0#X5~fK)&#AQg}bNCl(< zQUR%eR6r^q6_5%@1*8H}0jYpgKq?>=kP1izqyka_sen{KDj*e*3P=T{0#X5~fK)&# zAQg}bNCl(=kP1izqyka_sen{KDj*e*3P=T{ z0#X5~fK)&#AQg}bNCl((8D&RP_I7Vx5yBHQ%5;_@rWv2I^B^R|M*D+%zo zut`yr$%u#YEC2rIDnRA5tgO&hG;2m-{uPzhXf#&*>f+o2Pj#UhcKNJoL=C$9PHWhu zhGVAP5eh@;mlsdz?sT_)Ns0;o6lI#r?+SSw-P&AkT6-L{WSsmS{Odua9HfwHA#^kR?{5jbkt8=V17`)PJqHucKOZ;!~7xwsE z6i?yIqH;n{afYh=q5$U4np#Te$<7n>h*L!+D$2XslbKR!S4FyLniWD%e{R($PR%Tv zt`K_qQ$tz>EH5i9BlKv83QOlo%4U}lda~0?&g_{b3ZbVzXQjhW=Vs+kDIoM@=LiNh z{Ypym^MRi1s%ol4LsC9<&QwBAc3xMt-LX(Ro7|HT{1>2g3WS6~AGG{^dNOrZhJ{sW z`IG`ePj;$y+mynxGJ&4#e8F0gyB}4Q%p~+=hw}WlF48?E^*8Yk1p=3D=>N)8OqWoh z^C`VO*OTg{6L!)3bo-~rqleeS>Gp{nsyDky?CRx9FFbwO|0;I-v3oVU{n`B|V$2Kz0YQo5t>7c89Pl9jQQ13g~6Mr}MhM!|rtX9WGnM9=dG3uC{wr zuPby#wjqq!R69m24o{VyR!^?#w%;4}Tv4=&>Z$T9xU%djUQgIndu7oobo~=^Zp`i} zk4FDmufNtx0Vo0EiEg;+@_F0=HSF9>&r{n)PfXM{>VCRCwUav6&rzF6VF<=^I;XZ$Z!bZ+{iA$R ze4YO;cTIY_#%oGPBm_!d&!e6fx+b0L=jfXBc=U60O*+@l!RtHu)BC3G61RKoR*z5p zZ#*e=7wg}bUnuDFSoOi=WwI7_yX?*^$IaQq*rWjrwpg7RVjG zeBTx`2Kl!3#;`^%>OjW{7~|G_$;GCe=jcuIfn70=tj l1TjuqiHBbr*V0(=FVScKPVxn~tyfwJ5FmRn<)rK1{{yCI`5XWM literal 268312 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

wwml%M*b}o)<;FFa8GJ!EEB$N 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

q-{;fXW6 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 N{TCXp7)iGI{{a2V*^U4J literal 272504 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 Date: Fri, 12 Sep 2025 00:06:43 +0800 Subject: [PATCH 1115/1794] hw/char: sifive_uart: Raise IRQ according to the Tx/Rx watermark thresholds Currently, the SiFive UART raises an IRQ whenever: 1. ie.txwm is enabled. 2. ie.rxwm is enabled and the Rx FIFO is not empty. It does not check the watermark thresholds set by software. However, since commit [1] changed the SiFive UART character printing from synchronous to asynchronous, Tx overflows may occur, causing characters to be dropped when running Linux because: 1. The Linux SiFive UART driver sets the transmit watermark level to 1 [2], meaning a transmit watermark interrupt is raised whenever a character is enqueued into the Tx FIFO. 2. Upon receiving a transmit watermark interrupt, the Linux driver transfers up to a full Tx FIFO's worth of characters from the Linux serial transmit buffer [3], without checking the txdata.full flag before transferring multiple characters [4]. To fix this issue, we must honor the Tx/Rx watermark thresholds and raise interrupts only when the Tx threshold is exceeded or the Rx threshold is undercut. [1] 53c1557b230986ab6320a58e1b2c26216ecd86d5 [2] https://github.com/torvalds/linux/blob/master/drivers/tty/serial/sifive.c#L1039 [3] https://github.com/torvalds/linux/blob/master/drivers/tty/serial/sifive.c#L538 [4] https://github.com/torvalds/linux/blob/master/drivers/tty/serial/sifive.c#L291 Signed-off-by: Frank Chang Signed-off-by: Emmanuel Blot Reviewed-by: Alistair Francis Message-ID: <20250911160647.5710-2-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- hw/char/sifive_uart.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c index 9bc697a67b520..138c31fcabf4c 100644 --- a/hw/char/sifive_uart.c +++ b/hw/char/sifive_uart.c @@ -35,16 +35,17 @@ */ /* Returns the state of the IP (interrupt pending) register */ -static uint64_t sifive_uart_ip(SiFiveUARTState *s) +static uint32_t sifive_uart_ip(SiFiveUARTState *s) { - uint64_t ret = 0; + uint32_t ret = 0; - uint64_t txcnt = SIFIVE_UART_GET_TXCNT(s->txctrl); - uint64_t rxcnt = SIFIVE_UART_GET_RXCNT(s->rxctrl); + uint32_t txcnt = SIFIVE_UART_GET_TXCNT(s->txctrl); + uint32_t rxcnt = SIFIVE_UART_GET_RXCNT(s->rxctrl); - if (txcnt != 0) { + if (fifo8_num_used(&s->tx_fifo) < txcnt) { ret |= SIFIVE_UART_IP_TXWM; } + if (s->rx_fifo_len > rxcnt) { ret |= SIFIVE_UART_IP_RXWM; } @@ -55,15 +56,14 @@ static uint64_t sifive_uart_ip(SiFiveUARTState *s) static void sifive_uart_update_irq(SiFiveUARTState *s) { int cond = 0; - if ((s->ie & SIFIVE_UART_IE_TXWM) || - ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len)) { + uint32_t ip = sifive_uart_ip(s); + + if (((ip & SIFIVE_UART_IP_TXWM) && (s->ie & SIFIVE_UART_IE_TXWM)) || + ((ip & SIFIVE_UART_IP_RXWM) && (s->ie & SIFIVE_UART_IE_RXWM))) { cond = 1; } - if (cond) { - qemu_irq_raise(s->irq); - } else { - qemu_irq_lower(s->irq); - } + + qemu_set_irq(s->irq, cond); } static gboolean sifive_uart_xmit(void *do_not_use, GIOCondition cond, From a090ecd880a1b211e92c41029d1a90edf6686a48 Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Fri, 12 Sep 2025 00:06:44 +0800 Subject: [PATCH 1116/1794] hw/char: sifive_uart: Avoid pushing Tx FIFO when size is zero There's no need to call fifo8_push_all() when size is zero. Signed-off-by: Frank Chang Reviewed-by: Alistair Francis Message-ID: <20250911160647.5710-3-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- hw/char/sifive_uart.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c index 138c31fcabf4c..401f869680dc8 100644 --- a/hw/char/sifive_uart.c +++ b/hw/char/sifive_uart.c @@ -122,7 +122,9 @@ static void sifive_uart_write_tx_fifo(SiFiveUARTState *s, const uint8_t *buf, qemu_log_mask(LOG_GUEST_ERROR, "sifive_uart: TX FIFO overflow"); } - fifo8_push_all(&s->tx_fifo, buf, size); + if (size > 0) { + fifo8_push_all(&s->tx_fifo, buf, size); + } if (fifo8_is_full(&s->tx_fifo)) { s->txfifo |= SIFIVE_UART_TXFIFO_FULL; From 9100b9e67fbd2d60b53a6c5620c4ca2e6e43a0db Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Fri, 12 Sep 2025 00:06:45 +0800 Subject: [PATCH 1117/1794] hw/char: sifive_uart: Remove outdated comment about Tx FIFO Since Tx FIFO is now implemented using "qemu/fifo8.h", remove the comment that no longer reflects the current implementation. Signed-off-by: Frank Chang Reviewed-by: Alistair Francis Message-ID: <20250911160647.5710-4-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- hw/char/sifive_uart.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c index 401f869680dc8..baef0bd9c284f 100644 --- a/hw/char/sifive_uart.c +++ b/hw/char/sifive_uart.c @@ -28,12 +28,6 @@ #define TX_INTERRUPT_TRIGGER_DELAY_NS 100 -/* - * Not yet implemented: - * - * Transmit FIFO using "qemu/fifo8.h" - */ - /* Returns the state of the IP (interrupt pending) register */ static uint32_t sifive_uart_ip(SiFiveUARTState *s) { From e04886254f0d88881ea14533c438859537ed1dfb Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Fri, 12 Sep 2025 00:06:46 +0800 Subject: [PATCH 1118/1794] hw/char: sifive_uart: Add newline to error message Adds a missing newline character to the error message. Signed-off-by: Frank Chang Reviewed-by: Alistair Francis Message-ID: <20250911160647.5710-5-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- hw/char/sifive_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c index baef0bd9c284f..e7357d585a1ae 100644 --- a/hw/char/sifive_uart.c +++ b/hw/char/sifive_uart.c @@ -113,7 +113,7 @@ static void sifive_uart_write_tx_fifo(SiFiveUARTState *s, const uint8_t *buf, if (size > fifo8_num_free(&s->tx_fifo)) { size = fifo8_num_free(&s->tx_fifo); - qemu_log_mask(LOG_GUEST_ERROR, "sifive_uart: TX FIFO overflow"); + qemu_log_mask(LOG_GUEST_ERROR, "sifive_uart: TX FIFO overflow.\n"); } if (size > 0) { From 63181b069be342c49b2bb95344654d3667250f46 Mon Sep 17 00:00:00 2001 From: TANG Tiancheng Date: Thu, 11 Sep 2025 17:56:13 +0800 Subject: [PATCH 1119/1794] hw/intc: Save time_delta in RISC-V mtimer VMState In QEMU's RISC-V ACLINT timer model, 'mtime' is not stored directly as a state variable. It is computed on demand as: mtime = rtc_r + time_delta where: - 'rtc_r' is the current VM virtual time (in ticks) obtained via cpu_riscv_read_rtc_raw() from QEMU_CLOCK_VIRTUAL. - 'time_delta' is an offset applied when the guest writes a new 'mtime' value via riscv_aclint_mtimer_write(): time_delta = value - rtc_r Under this design, 'rtc_r' is assumed to be monotonically increasing during VM execution. Even if the guest writes an 'mtime' value smaller than the current one (making 'time_delta' negative in signed arithmetic, or underflow in unsigned arithmetic), the computed 'mtime' remains correct because 'rtc_r_new > rtc_r_old': mtime_new = rtc_r_new + (value - rtc_r_old) However, this monotonicity assumption breaks on snapshot load. Before restoring a snapshot, QEMU resets the guest, which calls riscv_aclint_mtimer_reset_enter() to set 'mtime' to 0 and recompute 'time_delta' as: time_delta = 0 - rtc_r_reset Here, the time_delta differs from the value that was present when the snapshot was saved. As a result, subsequent reads produce a fixed offset from the true mtime. This can be observed with the 'date' command inside the guest: after loading a snapshot, the reported time appears "frozen" at the save point, and only resumes correctly after the guest has run long enough to compensate for the erroneous offset. The fix is to treat 'time_delta' as part of the device's migratable state and save/restore it via vmstate. This preserves the correct relation between 'rtc_r' and 'mtime' across snapshot save/load, ensuring 'mtime' continues incrementing from the precise saved value after restore. Reviewed-by: LIU Zhiwei Reviewed-by: Daniel Henrique Barboza Signed-off-by: TANG Tiancheng Reviewed-by: Alistair Francis Message-ID: <20250911-timers-v3-1-60508f640050@linux.alibaba.com> Signed-off-by: Alistair Francis --- hw/intc/riscv_aclint.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c index 4623cfa029365..318a9c8248432 100644 --- a/hw/intc/riscv_aclint.c +++ b/hw/intc/riscv_aclint.c @@ -323,9 +323,10 @@ static void riscv_aclint_mtimer_reset_enter(Object *obj, ResetType type) static const VMStateDescription vmstate_riscv_mtimer = { .name = "riscv_mtimer", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (const VMStateField[]) { + VMSTATE_UINT64(time_delta, RISCVAclintMTimerState), VMSTATE_VARRAY_UINT32(timecmp, RISCVAclintMTimerState, num_harts, 0, vmstate_info_uint64, uint64_t), From 1d9a832b58be63e53ef0d2342c271a34ecb349db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 26 Sep 2025 10:54:23 +0200 Subject: [PATCH 1120/1794] vfio: Remove workaround for kernel DMA unmap overflow bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A kernel bug was introduced in Linux v4.15 via commit 71a7d3d78e3c ("vfio/type1: Check for address space wrap-around on unmap"), which added a test for address space wrap-around in the vfio DMA unmap path. Unfortunately, due to an integer overflow, the kernel would incorrectly detect an unmap of the last page in the 64-bit address space as a wrap-around, causing the unmap to fail with -EINVAL. A QEMU workaround was introduced in commit 567d7d3e6be5 ("vfio/common: Work around kernel overflow bug in DMA unmap") to retry the unmap, excluding the final page of the range. The kernel bug was then fixed in Linux v5.0 via commit 58fec830fc19 ("vfio/type1: Fix dma_unmap wrap-around check"). Since the oldest supported LTS kernel is now v5.4, kernels affected by this bug are considered deprecated, and the workaround is no longer necessary. This change reverts 567d7d3e6be5, removing the workaround. Link: https://bugzilla.redhat.com/show_bug.cgi?id=1662291 Reviewed-by: Alex Williamson Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/20250926085423.375547-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/container-legacy.c | 20 +------------------- hw/vfio/trace-events | 1 - 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/hw/vfio/container-legacy.c b/hw/vfio/container-legacy.c index c0f87f774a008..25a15ea8674c1 100644 --- a/hw/vfio/container-legacy.c +++ b/hw/vfio/container-legacy.c @@ -147,25 +147,7 @@ static int vfio_legacy_dma_unmap_one(const VFIOContainer *bcontainer, need_dirty_sync = true; } - while (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) { - /* - * The type1 backend has an off-by-one bug in the kernel (71a7d3d78e3c - * v4.15) where an overflow in its wrap-around check prevents us from - * unmapping the last page of the address space. Test for the error - * condition and re-try the unmap excluding the last page. The - * expectation is that we've never mapped the last page anyway and this - * unmap request comes via vIOMMU support which also makes it unlikely - * that this page is used. This bug was introduced well after type1 v2 - * support was introduced, so we shouldn't need to test for v1. A fix - * is queued for kernel v5.0 so this workaround can be removed once - * affected kernels are sufficiently deprecated. - */ - if (errno == EINVAL && unmap.size && !(unmap.iova + unmap.size) && - container->iommu_type == VFIO_TYPE1v2_IOMMU) { - trace_vfio_legacy_dma_unmap_overflow_workaround(); - unmap.size -= 1ULL << ctz64(bcontainer->pgsizes); - continue; - } + if (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) { return -errno; } diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index e3d571f8c845d..7496e1b64b5de 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -112,7 +112,6 @@ vfio_container_disconnect(int fd) "close container->fd=%d" vfio_group_put(int fd) "close group->fd=%d" vfio_device_get(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u" vfio_device_put(int fd) "close vdev->fd=%d" -vfio_legacy_dma_unmap_overflow_workaround(void) "" # region.c vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)" From 70a7e33ddb7f2ca7caacf286222bd80fd330c454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 30 Sep 2025 14:35:25 +0200 Subject: [PATCH 1121/1794] system/iommufd: Use uint64_t type for IOVA mapping size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'ram_addr_t' type is described as: a QEMU internal address space that maps guest RAM physical addresses into an intermediate address space that can map to host virtual address spaces. This doesn't represent well an IOVA mapping size. Simply use the uint64_t type. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250930123528.42878-2-philmd@linaro.org Signed-off-by: Cédric Le Goater --- backends/iommufd.c | 6 +++--- include/system/iommufd.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backends/iommufd.c b/backends/iommufd.c index 2a33c7ab0bcdc..fdfb7c9d67197 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -197,7 +197,7 @@ void iommufd_backend_free_id(IOMMUFDBackend *be, uint32_t id) } int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, - ram_addr_t size, void *vaddr, bool readonly) + uint64_t size, void *vaddr, bool readonly) { int ret, fd = be->fd; struct iommu_ioas_map map = { @@ -230,7 +230,7 @@ int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, } int iommufd_backend_map_file_dma(IOMMUFDBackend *be, uint32_t ioas_id, - hwaddr iova, ram_addr_t size, + hwaddr iova, uint64_t size, int mfd, unsigned long start, bool readonly) { int ret, fd = be->fd; @@ -268,7 +268,7 @@ int iommufd_backend_map_file_dma(IOMMUFDBackend *be, uint32_t ioas_id, } int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, - hwaddr iova, ram_addr_t size) + hwaddr iova, uint64_t size) { int ret, fd = be->fd; struct iommu_ioas_unmap unmap = { diff --git a/include/system/iommufd.h b/include/system/iommufd.h index c9c72ffc4509d..a659f36a20fdc 100644 --- a/include/system/iommufd.h +++ b/include/system/iommufd.h @@ -45,12 +45,12 @@ bool iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id, Error **errp); void iommufd_backend_free_id(IOMMUFDBackend *be, uint32_t id); int iommufd_backend_map_file_dma(IOMMUFDBackend *be, uint32_t ioas_id, - hwaddr iova, ram_addr_t size, int fd, + hwaddr iova, uint64_t size, int fd, unsigned long start, bool readonly); int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, - ram_addr_t size, void *vaddr, bool readonly); + uint64_t size, void *vaddr, bool readonly); int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, - hwaddr iova, ram_addr_t size); + hwaddr iova, uint64_t size); bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid, uint32_t *type, void *data, uint32_t len, uint64_t *caps, Error **errp); From 5764a715277afc4d6076fbf2bae1697dbd2fa182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 30 Sep 2025 14:35:26 +0200 Subject: [PATCH 1122/1794] hw/vfio: Reorder vfio_container_query_dirty_bitmap() trace format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the trace-events comments after the changes from commit dcce51b1938 ("hw/vfio/container-base.c: rename file to container.c") and commit a3bcae62b6a ("hw/vfio/container.c: rename file to container-legacy.c"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250930123528.42878-3-philmd@linaro.org Signed-off-by: Cédric Le Goater --- hw/vfio/trace-events | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 7496e1b64b5de..b1b470cc295ec 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -104,10 +104,10 @@ vfio_device_dirty_tracking_update(uint64_t start, uint64_t end, uint64_t min, ui vfio_device_dirty_tracking_start(int nr_ranges, uint64_t min32, uint64_t max32, uint64_t min64, uint64_t max64, uint64_t minpci, uint64_t maxpci) "nr_ranges %d 32:[0x%"PRIx64" - 0x%"PRIx64"], 64:[0x%"PRIx64" - 0x%"PRIx64"], pci64:[0x%"PRIx64" - 0x%"PRIx64"]" vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64 -# container-base.c +# container.c vfio_container_query_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64 -# container.c +# container-legacy.c vfio_container_disconnect(int fd) "close container->fd=%d" vfio_group_put(int fd) "close group->fd=%d" vfio_device_get(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u" From 0ca70d3bf722a94c53f254670e6a642e77aa077c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 30 Sep 2025 14:35:27 +0200 Subject: [PATCH 1123/1794] hw/vfio: Avoid ram_addr_t in vfio_container_query_dirty_bitmap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'ram_addr_t' type is described as: a QEMU internal address space that maps guest RAM physical addresses into an intermediate address space that can map to host virtual address spaces. vfio_container_query_dirty_bitmap() doesn't expect such QEMU intermediate address, but a guest physical addresses. Use the appropriate 'hwaddr' type, rename as @translated_addr for clarity. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250930123528.42878-4-philmd@linaro.org Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 11 ++++++----- hw/vfio/listener.c | 18 +++++++++--------- hw/vfio/trace-events | 2 +- include/hw/vfio/vfio-container.h | 3 ++- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 250b20f424522..9d69439371402 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -246,7 +246,7 @@ static int vfio_container_devices_query_dirty_bitmap( int vfio_container_query_dirty_bitmap(const VFIOContainer *bcontainer, uint64_t iova, uint64_t size, - ram_addr_t ram_addr, Error **errp) + hwaddr translated_addr, Error **errp) { bool all_device_dirty_tracking = vfio_container_devices_dirty_tracking_is_supported(bcontainer); @@ -255,7 +255,7 @@ int vfio_container_query_dirty_bitmap(const VFIOContainer *bcontainer, int ret; if (!bcontainer->dirty_pages_supported && !all_device_dirty_tracking) { - cpu_physical_memory_set_dirty_range(ram_addr, size, + cpu_physical_memory_set_dirty_range(translated_addr, size, tcg_enabled() ? DIRTY_CLIENTS_ALL : DIRTY_CLIENTS_NOCODE); return 0; @@ -280,11 +280,12 @@ int vfio_container_query_dirty_bitmap(const VFIOContainer *bcontainer, goto out; } - dirty_pages = cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap, ram_addr, + dirty_pages = cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap, + translated_addr, vbmap.pages); - trace_vfio_container_query_dirty_bitmap(iova, size, vbmap.size, ram_addr, - dirty_pages); + trace_vfio_container_query_dirty_bitmap(iova, size, vbmap.size, + translated_addr, dirty_pages); out: g_free(vbmap.bitmap); diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index 3b6f17f0c3aa7..a2c19a3cec1a9 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -1059,7 +1059,7 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) VFIOGuestIOMMU *giommu = gdn->giommu; VFIOContainer *bcontainer = giommu->bcontainer; hwaddr iova = iotlb->iova + giommu->iommu_offset; - ram_addr_t translated_addr; + hwaddr translated_addr; Error *local_err = NULL; int ret = -EINVAL; MemoryRegion *mr; @@ -1108,8 +1108,8 @@ static int vfio_ram_discard_query_dirty_bitmap(MemoryRegionSection *section, { const hwaddr size = int128_get64(section->size); const hwaddr iova = section->offset_within_address_space; - const ram_addr_t ram_addr = memory_region_get_ram_addr(section->mr) + - section->offset_within_region; + const hwaddr translated_addr = memory_region_get_ram_addr(section->mr) + + section->offset_within_region; VFIORamDiscardListener *vrdl = opaque; Error *local_err = NULL; int ret; @@ -1118,8 +1118,8 @@ static int vfio_ram_discard_query_dirty_bitmap(MemoryRegionSection *section, * Sync the whole mapped region (spanning multiple individual mappings) * in one go. */ - ret = vfio_container_query_dirty_bitmap(vrdl->bcontainer, iova, size, ram_addr, - &local_err); + ret = vfio_container_query_dirty_bitmap(vrdl->bcontainer, iova, size, + translated_addr, &local_err); if (ret) { error_report_err(local_err); } @@ -1183,7 +1183,7 @@ static int vfio_sync_iommu_dirty_bitmap(VFIOContainer *bcontainer, static int vfio_sync_dirty_bitmap(VFIOContainer *bcontainer, MemoryRegionSection *section, Error **errp) { - ram_addr_t ram_addr; + hwaddr translated_addr; if (memory_region_is_iommu(section->mr)) { return vfio_sync_iommu_dirty_bitmap(bcontainer, section); @@ -1198,12 +1198,12 @@ static int vfio_sync_dirty_bitmap(VFIOContainer *bcontainer, return ret; } - ram_addr = memory_region_get_ram_addr(section->mr) + - section->offset_within_region; + translated_addr = memory_region_get_ram_addr(section->mr) + + section->offset_within_region; return vfio_container_query_dirty_bitmap(bcontainer, REAL_HOST_PAGE_ALIGN(section->offset_within_address_space), - int128_get64(section->size), ram_addr, errp); + int128_get64(section->size), translated_addr, errp); } static void vfio_listener_log_sync(MemoryListener *listener, diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index b1b470cc295ec..1e895448cd9b4 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -105,7 +105,7 @@ vfio_device_dirty_tracking_start(int nr_ranges, uint64_t min32, uint64_t max32, vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64 # container.c -vfio_container_query_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64 +vfio_container_query_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t translated_addr, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" gpa=0x%"PRIx64" dirty_pages=%"PRIu64 # container-legacy.c vfio_container_disconnect(int fd) "close container->fd=%d" diff --git a/include/hw/vfio/vfio-container.h b/include/hw/vfio/vfio-container.h index b8fb2b8b5d72b..093c360f0eef5 100644 --- a/include/hw/vfio/vfio-container.h +++ b/include/hw/vfio/vfio-container.h @@ -98,7 +98,8 @@ bool vfio_container_dirty_tracking_is_started( bool vfio_container_devices_dirty_tracking_is_supported( const VFIOContainer *bcontainer); int vfio_container_query_dirty_bitmap(const VFIOContainer *bcontainer, - uint64_t iova, uint64_t size, ram_addr_t ram_addr, Error **errp); + uint64_t iova, uint64_t size, + hwaddr translated_addr, Error **errp); GList *vfio_container_get_iova_ranges(const VFIOContainer *bcontainer); From f0b52aa08ab0868c18d881381a8fda4b59b37517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 30 Sep 2025 14:35:28 +0200 Subject: [PATCH 1124/1794] hw/vfio: Use uint64_t for IOVA mapping size in vfio_container_dma_*map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'ram_addr_t' type is described as: a QEMU internal address space that maps guest RAM physical addresses into an intermediate address space that can map to host virtual address spaces. This doesn't represent well an IOVA mapping size. Simply use the uint64_t type. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250930123528.42878-5-philmd@linaro.org Signed-off-by: Cédric Le Goater --- hw/vfio-user/container.c | 4 ++-- hw/vfio/container-legacy.c | 8 ++++---- hw/vfio/container.c | 4 ++-- hw/vfio/cpr-legacy.c | 2 +- hw/vfio/iommufd.c | 6 +++--- include/hw/vfio/vfio-container.h | 10 +++++----- include/hw/vfio/vfio-cpr.h | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/hw/vfio-user/container.c b/hw/vfio-user/container.c index 411eb7b28b72a..e45192fef6531 100644 --- a/hw/vfio-user/container.c +++ b/hw/vfio-user/container.c @@ -39,7 +39,7 @@ static void vfio_user_listener_commit(VFIOContainer *bcontainer) } static int vfio_user_dma_unmap(const VFIOContainer *bcontainer, - hwaddr iova, ram_addr_t size, + hwaddr iova, uint64_t size, IOMMUTLBEntry *iotlb, bool unmap_all) { VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); @@ -81,7 +81,7 @@ static int vfio_user_dma_unmap(const VFIOContainer *bcontainer, } static int vfio_user_dma_map(const VFIOContainer *bcontainer, hwaddr iova, - ram_addr_t size, void *vaddr, bool readonly, + uint64_t size, void *vaddr, bool readonly, MemoryRegion *mrp) { VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); diff --git a/hw/vfio/container-legacy.c b/hw/vfio/container-legacy.c index 25a15ea8674c1..34352dd31fc9b 100644 --- a/hw/vfio/container-legacy.c +++ b/hw/vfio/container-legacy.c @@ -69,7 +69,7 @@ static int vfio_ram_block_discard_disable(VFIOLegacyContainer *container, } static int vfio_dma_unmap_bitmap(const VFIOLegacyContainer *container, - hwaddr iova, ram_addr_t size, + hwaddr iova, uint64_t size, IOMMUTLBEntry *iotlb) { const VFIOContainer *bcontainer = VFIO_IOMMU(container); @@ -122,7 +122,7 @@ static int vfio_dma_unmap_bitmap(const VFIOLegacyContainer *container, } static int vfio_legacy_dma_unmap_one(const VFIOContainer *bcontainer, - hwaddr iova, ram_addr_t size, + hwaddr iova, uint64_t size, IOMMUTLBEntry *iotlb) { const VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); @@ -167,7 +167,7 @@ static int vfio_legacy_dma_unmap_one(const VFIOContainer *bcontainer, * DMA - Mapping and unmapping for the "type1" IOMMU interface used on x86 */ static int vfio_legacy_dma_unmap(const VFIOContainer *bcontainer, - hwaddr iova, ram_addr_t size, + hwaddr iova, uint64_t size, IOMMUTLBEntry *iotlb, bool unmap_all) { int ret; @@ -192,7 +192,7 @@ static int vfio_legacy_dma_unmap(const VFIOContainer *bcontainer, } static int vfio_legacy_dma_map(const VFIOContainer *bcontainer, hwaddr iova, - ram_addr_t size, void *vaddr, bool readonly, + uint64_t size, void *vaddr, bool readonly, MemoryRegion *mr) { const VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 9d69439371402..41de343924614 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -74,7 +74,7 @@ void vfio_address_space_insert(VFIOAddressSpace *space, } int vfio_container_dma_map(VFIOContainer *bcontainer, - hwaddr iova, ram_addr_t size, + hwaddr iova, uint64_t size, void *vaddr, bool readonly, MemoryRegion *mr) { VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); @@ -93,7 +93,7 @@ int vfio_container_dma_map(VFIOContainer *bcontainer, } int vfio_container_dma_unmap(VFIOContainer *bcontainer, - hwaddr iova, ram_addr_t size, + hwaddr iova, uint64_t size, IOMMUTLBEntry *iotlb, bool unmap_all) { VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index bbf7a0d35f0ba..3a1d126556e11 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -39,7 +39,7 @@ static bool vfio_dma_unmap_vaddr_all(VFIOLegacyContainer *container, * The incoming state is cleared thereafter. */ static int vfio_legacy_cpr_dma_map(const VFIOContainer *bcontainer, - hwaddr iova, ram_addr_t size, void *vaddr, + hwaddr iova, uint64_t size, void *vaddr, bool readonly, MemoryRegion *mr) { const VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index f0ffe23591965..68470d552eccc 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -35,7 +35,7 @@ TYPE_HOST_IOMMU_DEVICE_IOMMUFD "-vfio" static int iommufd_cdev_map(const VFIOContainer *bcontainer, hwaddr iova, - ram_addr_t size, void *vaddr, bool readonly, + uint64_t size, void *vaddr, bool readonly, MemoryRegion *mr) { const VFIOIOMMUFDContainer *container = VFIO_IOMMU_IOMMUFD(bcontainer); @@ -46,7 +46,7 @@ static int iommufd_cdev_map(const VFIOContainer *bcontainer, hwaddr iova, } static int iommufd_cdev_map_file(const VFIOContainer *bcontainer, - hwaddr iova, ram_addr_t size, + hwaddr iova, uint64_t size, int fd, unsigned long start, bool readonly) { const VFIOIOMMUFDContainer *container = VFIO_IOMMU_IOMMUFD(bcontainer); @@ -57,7 +57,7 @@ static int iommufd_cdev_map_file(const VFIOContainer *bcontainer, } static int iommufd_cdev_unmap(const VFIOContainer *bcontainer, - hwaddr iova, ram_addr_t size, + hwaddr iova, uint64_t size, IOMMUTLBEntry *iotlb, bool unmap_all) { const VFIOIOMMUFDContainer *container = VFIO_IOMMU_IOMMUFD(bcontainer); diff --git a/include/hw/vfio/vfio-container.h b/include/hw/vfio/vfio-container.h index 093c360f0eef5..c4b58d664b7e7 100644 --- a/include/hw/vfio/vfio-container.h +++ b/include/hw/vfio/vfio-container.h @@ -81,10 +81,10 @@ void vfio_address_space_insert(VFIOAddressSpace *space, VFIOContainer *bcontainer); int vfio_container_dma_map(VFIOContainer *bcontainer, - hwaddr iova, ram_addr_t size, + hwaddr iova, uint64_t size, void *vaddr, bool readonly, MemoryRegion *mr); int vfio_container_dma_unmap(VFIOContainer *bcontainer, - hwaddr iova, ram_addr_t size, + hwaddr iova, uint64_t size, IOMMUTLBEntry *iotlb, bool unmap_all); bool vfio_container_add_section_window(VFIOContainer *bcontainer, MemoryRegionSection *section, @@ -167,7 +167,7 @@ struct VFIOIOMMUClass { * Returns 0 to indicate success and -errno otherwise. */ int (*dma_map)(const VFIOContainer *bcontainer, - hwaddr iova, ram_addr_t size, + hwaddr iova, uint64_t size, void *vaddr, bool readonly, MemoryRegion *mr); /** * @dma_map_file @@ -182,7 +182,7 @@ struct VFIOIOMMUClass { * @readonly: map read only if true */ int (*dma_map_file)(const VFIOContainer *bcontainer, - hwaddr iova, ram_addr_t size, + hwaddr iova, uint64_t size, int fd, unsigned long start, bool readonly); /** * @dma_unmap @@ -198,7 +198,7 @@ struct VFIOIOMMUClass { * Returns 0 to indicate success and -errno otherwise. */ int (*dma_unmap)(const VFIOContainer *bcontainer, - hwaddr iova, ram_addr_t size, + hwaddr iova, uint64_t size, IOMMUTLBEntry *iotlb, bool unmap_all); diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index 26ee0c4fe15ac..81f4e24e229ef 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -21,7 +21,7 @@ struct VFIOIOMMUFDContainer; struct IOMMUFDBackend; typedef int (*dma_map_fn)(const struct VFIOContainer *bcontainer, - hwaddr iova, ram_addr_t size, void *vaddr, + hwaddr iova, uint64_t size, void *vaddr, bool readonly, MemoryRegion *mr); typedef struct VFIOContainerCPR { From 30478e2ff7839a692c3894ae5f6c28e780f0bc6d Mon Sep 17 00:00:00 2001 From: TANG Tiancheng Date: Thu, 11 Sep 2025 17:56:14 +0800 Subject: [PATCH 1125/1794] migration: Add support for a variable-length array of UINT32 pointers Add support for defining a vmstate field which is a variable-length array of pointers, and use this to define a VMSTATE_TIMER_PTR_VARRAY() which allows a variable-length array of QEMUTimer* to be used by devices. Message-id: 20250909-timers-v1-0-7ee18a9d8f4b@linux.alibaba.com Reviewed-by: LIU Zhiwei Reviewed-by: Peter Xu Signed-off-by: TANG Tiancheng Reviewed-by: Alistair Francis Message-ID: <20250911-timers-v3-2-60508f640050@linux.alibaba.com> Signed-off-by: Alistair Francis --- include/migration/vmstate.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 1ff7bd9ac425b..1cfddf31b5409 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -522,6 +522,16 @@ extern const VMStateInfo vmstate_info_qlist; .offset = vmstate_offset_array(_s, _f, _type*, _n), \ } +#define VMSTATE_VARRAY_OF_POINTER_UINT32(_field, _state, _field_num, _version, _info, _type) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .num_offset = vmstate_offset_value(_state, _field_num, uint32_t), \ + .info = &(_info), \ + .size = sizeof(_type), \ + .flags = VMS_VARRAY_UINT32 | VMS_ARRAY_OF_POINTER | VMS_POINTER, \ + .offset = vmstate_offset_pointer(_state, _field, _type), \ +} + #define VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, _num, _version, _vmsd, _type) { \ .name = (stringify(_field)), \ .version_id = (_version), \ From 09f89ccc9763a20c0cf9030661af2c04647c1eec Mon Sep 17 00:00:00 2001 From: TANG Tiancheng Date: Thu, 11 Sep 2025 17:56:15 +0800 Subject: [PATCH 1126/1794] hw/intc: Save timers array in RISC-V mtimer VMState The current 'timecmp' field in vmstate_riscv_mtimer is insufficient to keep timers functional after migration. If an mtimer's entry in 'mtimer->timers' is active at the time the snapshot is taken, it means riscv_aclint_mtimer_write_timecmp() has written to 'mtimecmp' and scheduled a timer into QEMU's main loop 'timer_list'. During snapshot save, these active timers must also be migrated; otherwise, after snapshot load there is no mechanism to restore 'mtimer->timers' back into the 'timer_list', and any pending timer events would be lost. QEMU's migration framework commonly uses VMSTATE_TIMER_xxx macros to save and restore 'QEMUTimer' variables. However, 'timers' is a pointer array with variable length, and vmstate.h did not previously provide a helper macro for such type. This commit adds a new macro, 'VMSTATE_TIMER_PTR_VARRAY', to handle saving and restoring a variable-length array of 'QEMUTimer *'. We then use this macro to migrate the 'mtimer->timers' array, ensuring that timer events remain scheduled correctly after snapshot load. Reviewed-by: LIU Zhiwei Signed-off-by: TANG Tiancheng Reviewed-by: Alistair Francis Message-ID: <20250911-timers-v3-3-60508f640050@linux.alibaba.com> Signed-off-by: Alistair Francis --- hw/intc/riscv_aclint.c | 6 ++++-- include/hw/intc/riscv_aclint.h | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c index 318a9c8248432..9f4c36e965e2a 100644 --- a/hw/intc/riscv_aclint.c +++ b/hw/intc/riscv_aclint.c @@ -323,13 +323,15 @@ static void riscv_aclint_mtimer_reset_enter(Object *obj, ResetType type) static const VMStateDescription vmstate_riscv_mtimer = { .name = "riscv_mtimer", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .fields = (const VMStateField[]) { VMSTATE_UINT64(time_delta, RISCVAclintMTimerState), VMSTATE_VARRAY_UINT32(timecmp, RISCVAclintMTimerState, num_harts, 0, vmstate_info_uint64, uint64_t), + VMSTATE_TIMER_PTR_VARRAY(timers, RISCVAclintMTimerState, + num_harts), VMSTATE_END_OF_LIST() } }; diff --git a/include/hw/intc/riscv_aclint.h b/include/hw/intc/riscv_aclint.h index 693415eb6defe..4b7406eec005a 100644 --- a/include/hw/intc/riscv_aclint.h +++ b/include/hw/intc/riscv_aclint.h @@ -80,4 +80,8 @@ enum { RISCV_ACLINT_SWI_SIZE = 0x4000 }; +#define VMSTATE_TIMER_PTR_VARRAY(_f, _s, _f_n) \ +VMSTATE_VARRAY_OF_POINTER_UINT32(_f, _s, _f_n, 0, vmstate_info_timer, \ + QEMUTimer *) + #endif From b0daaa172a1cd7e8bc8320bfd6612edbebef157f Mon Sep 17 00:00:00 2001 From: TANG Tiancheng Date: Thu, 11 Sep 2025 17:56:16 +0800 Subject: [PATCH 1127/1794] target/riscv: Save stimer and vstimer in CPU vmstate vmstate_riscv_cpu was missing env.stimer and env.vstimer. Without migrating these QEMUTimer fields, active S/VS-mode timer events are lost after snapshot or migration. Add VMSTATE_TIMER_PTR() entries to save and restore them. Reviewed-by: LIU Zhiwei Reviewed-by: Daniel Henrique Barboza Signed-off-by: TANG Tiancheng Reviewed-by: Alistair Francis Message-ID: <20250911-timers-v3-4-60508f640050@linux.alibaba.com> Signed-off-by: Alistair Francis --- target/riscv/machine.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 1600ec44f0b75..51e0567ed30cb 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -400,6 +400,30 @@ static const VMStateDescription vmstate_ssp = { } }; +static bool sstc_timer_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + + if (!cpu->cfg.ext_sstc) { + return false; + } + + return env->stimer != NULL || env->vstimer != NULL; +} + +static const VMStateDescription vmstate_sstc = { + .name = "cpu/timer", + .version_id = 1, + .minimum_version_id = 1, + .needed = sstc_timer_needed, + .fields = (const VMStateField[]) { + VMSTATE_TIMER_PTR(env.stimer, RISCVCPU), + VMSTATE_TIMER_PTR(env.vstimer, RISCVCPU), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_riscv_cpu = { .name = "cpu", .version_id = 10, @@ -476,6 +500,7 @@ const VMStateDescription vmstate_riscv_cpu = { &vmstate_elp, &vmstate_ssp, &vmstate_ctr, + &vmstate_sstc, NULL } }; From c69fc80035b708a8b997cbab0d393e2702364a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 24 Sep 2025 18:45:15 +0200 Subject: [PATCH 1128/1794] target/riscv/kvm: Use riscv_cpu_is_32bit() when handling SBI_DBCN reg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the existing riscv_cpu_is_32bit() helper to check for 32-bit CPU. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Andrew Jones Message-ID: <20250924164515.51782-1-philmd@linaro.org> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 5c19062c19b60..187c2c9501e35 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1588,7 +1588,7 @@ static void kvm_riscv_handle_sbi_dbcn(CPUState *cs, struct kvm_run *run) * Handle the case where a 32 bit CPU is running in a * 64 bit addressing env. */ - if (riscv_cpu_mxl(&cpu->env) == MXL_RV32) { + if (riscv_cpu_is_32bit(cpu)) { addr |= (uint64_t)run->riscv_sbi.args[2] << 32; } From cebaf7434b4af059caca053ee1ec7ed8df91c2a7 Mon Sep 17 00:00:00 2001 From: stove Date: Wed, 27 Aug 2025 13:36:17 -0700 Subject: [PATCH 1129/1794] target/riscv: use riscv_csrr in riscv_csr_read Commit 38c83e8d3a33 ("target/riscv: raise an exception when CSRRS/CSRRC writes a read-only CSR") changed the behavior of riscv_csrrw, which would formerly be treated as read-only if the write mask were set to 0. Fixes an exception being raised when accessing read-only vector CSRs like vtype. Fixes: 38c83e8d3a33 ("target/riscv: raise an exception when CSRRS/CSRRC writes a read-only CSR") Signed-off-by: stove Reviewed-by: Daniel Henrique Barboza Message-ID: <20250827203617.79947-1-stove@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 738e68fa6e25c..2c2266415ec87 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -874,7 +874,7 @@ static inline void riscv_csr_write(CPURISCVState *env, int csrno, static inline target_ulong riscv_csr_read(CPURISCVState *env, int csrno) { target_ulong val = 0; - riscv_csrrw(env, csrno, &val, 0, 0, 0); + riscv_csrr(env, csrno, &val); return val; } From ec139c3dd00599e3e71b28c30b8207f6f15207c7 Mon Sep 17 00:00:00 2001 From: Xuemei Liu Date: Wed, 24 Sep 2025 13:18:03 +0800 Subject: [PATCH 1130/1794] qemu/osdep: align memory allocations to 2M on RISC-V Similar to other architectures (e.g., x86_64, aarch64), utilizing THP on RISC-V KVM requires 2MiB-aligned memory blocks. Signed-off-by: Xuemei Liu Reviewed-by: David Hildenbrand Message-ID: <20250924131803656Yqt9ZJKfevWkInaGppFdE@zte.com.cn> Signed-off-by: Alistair Francis --- include/qemu/osdep.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 1b38cb7e452ac..6de6c0c4e5c07 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -561,7 +561,7 @@ int madvise(char *, size_t, int); #if defined(__linux__) && \ (defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) \ - || defined(__powerpc64__)) + || defined(__powerpc64__) || defined(__riscv)) /* Use 2 MiB alignment so transparent hugepages can be used by KVM. Valgrind does not support alignments larger than 1 MiB, therefore we need special code which handles running on Valgrind. */ From a86d3352ab70f33f5feabbf9bad9450d3c19d0bf Mon Sep 17 00:00:00 2001 From: Vladimir Isaev Date: Fri, 15 Aug 2025 17:06:33 +0300 Subject: [PATCH 1131/1794] target/riscv: do not use translator_ldl in opcode_at opcode_at is used only in semihosting checks to match opcodes with expected pattern. This is not a translator and if we got following assert if page is not in TLB: qemu-system-riscv64: ../accel/tcg/translator.c:363: record_save: Assertion `offset == db->record_start + db->record_len' failed. Fixes: 1f9c4462334f ("target/riscv: Use translator_ld* for everything") Signed-off-by: Vladimir Isaev Reviewed-by: Richard Henderson Message-ID: <20250815140633.86920-1-vladimir.isaev@syntacore.com> [ Changes by AF: - Fixup header includes after rebase ] Signed-off-by: Alistair Francis --- target/riscv/translate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 9ddef2d6e2a91..6fc06c71f5136 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -24,6 +24,7 @@ #include "exec/helper-gen.h" #include "exec/target_page.h" #include "exec/translator.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/translation-block.h" #include "exec/log.h" #include "semihosting/semihost.h" @@ -1166,7 +1167,7 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) CPUState *cpu = ctx->cs; CPURISCVState *env = cpu_env(cpu); - return translator_ldl(env, &ctx->base, pc); + return cpu_ldl_code(env, pc); } #define SS_MMU_INDEX(ctx) (ctx->mem_idx | MMU_IDX_SS_WRITE) From c851052a77fd79300708df2070297b5428b4be8d Mon Sep 17 00:00:00 2001 From: Jim Shu Date: Wed, 24 Sep 2025 15:48:16 +0800 Subject: [PATCH 1132/1794] target/riscv: Fix the mepc when sspopchk triggers the exception When sspopchk is in the middle of TB and triggers the SW check exception, it should update PC from gen_update_pc(). If not, RISC-V mepc CSR will get wrong PC address which is still at the start of TB. Signed-off-by: Jim Shu Reviewed-by: Alistair Francis Message-ID: <20250924074818.230010-2-jim.shu@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvzicfiss.c.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/insn_trans/trans_rvzicfiss.c.inc b/target/riscv/insn_trans/trans_rvzicfiss.c.inc index b0096adcd0e50..45686af4d6317 100644 --- a/target/riscv/insn_trans/trans_rvzicfiss.c.inc +++ b/target/riscv/insn_trans/trans_rvzicfiss.c.inc @@ -40,6 +40,7 @@ static bool trans_sspopchk(DisasContext *ctx, arg_sspopchk *a) tcg_gen_brcond_tl(TCG_COND_EQ, data, rs1, skip); tcg_gen_st_tl(tcg_constant_tl(RISCV_EXCP_SW_CHECK_BCFI_TVAL), tcg_env, offsetof(CPURISCVState, sw_check_code)); + gen_update_pc(ctx, 0); gen_helper_raise_exception(tcg_env, tcg_constant_i32(RISCV_EXCP_SW_CHECK)); gen_set_label(skip); From 84c1605b7606d810ded4c1c3a2717f158dc89e3f Mon Sep 17 00:00:00 2001 From: Jim Shu Date: Wed, 24 Sep 2025 15:48:17 +0800 Subject: [PATCH 1133/1794] target/riscv: Fix SSP CSR error handling in VU/VS mode In VU/VS mode, accessing $ssp CSR will trigger the virtual instruction exception instead of illegal instruction exception if SSE is disabled via xenvcfg CSRs. This is from RISC-V CFI v1.0 spec ch2.2.4. Shadow Stack Pointer Signed-off-by: Jim Shu Reviewed-by: Alistair Francis Message-ID: <20250924074818.230010-3-jim.shu@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 3c8989f522e8f..ea36eccb3dc9d 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -203,6 +203,8 @@ static RISCVException cfi_ss(CPURISCVState *env, int csrno) #if !defined(CONFIG_USER_ONLY) if (env->debugger) { return RISCV_EXCP_NONE; + } else if (env->virt_enabled) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } #endif return RISCV_EXCP_ILLEGAL_INST; From 0b16c7b6a854d461cdfd418769b51d58e43dd92a Mon Sep 17 00:00:00 2001 From: Jim Shu Date: Wed, 24 Sep 2025 15:48:18 +0800 Subject: [PATCH 1134/1794] target/riscv: Fix ssamoswap error handling Follow the RISC-V CFI v1.0 spec [1] to fix the exception type when ssamoswap is disabled by xSSE. [1] RISC-V CFI spec v1.0, ch2.7 Atomic Swap from a Shadow Stack Location Signed-off-by: Jim Shu Reviewed-by: Alistair Francis Message-ID: <20250924074818.230010-4-jim.shu@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/helper.h | 5 ++ target/riscv/insn_trans/trans_rvzicfiss.c.inc | 8 +++ target/riscv/op_helper.c | 49 +++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/target/riscv/helper.h b/target/riscv/helper.h index f712b1c368ece..c82bacdc397a0 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -1284,3 +1284,8 @@ DEF_HELPER_4(vgmul_vv, void, ptr, ptr, env, i32) DEF_HELPER_5(vsm4k_vi, void, ptr, ptr, i32, env, i32) DEF_HELPER_4(vsm4r_vv, void, ptr, ptr, env, i32) DEF_HELPER_4(vsm4r_vs, void, ptr, ptr, env, i32) + +/* CFI (zicfiss) helpers */ +#ifndef CONFIG_USER_ONLY +DEF_HELPER_1(ssamoswap_disabled, void, env) +#endif diff --git a/target/riscv/insn_trans/trans_rvzicfiss.c.inc b/target/riscv/insn_trans/trans_rvzicfiss.c.inc index 45686af4d6317..f4a1c12ca0bcc 100644 --- a/target/riscv/insn_trans/trans_rvzicfiss.c.inc +++ b/target/riscv/insn_trans/trans_rvzicfiss.c.inc @@ -91,7 +91,11 @@ static bool trans_ssamoswap_w(DisasContext *ctx, arg_amoswap_w *a) } if (!ctx->bcfi_enabled) { +#ifndef CONFIG_USER_ONLY + gen_helper_ssamoswap_disabled(tcg_env); +#else return false; +#endif } TCGv dest = dest_gpr(ctx, a->rd); @@ -116,7 +120,11 @@ static bool trans_ssamoswap_d(DisasContext *ctx, arg_amoswap_w *a) } if (!ctx->bcfi_enabled) { +#ifndef CONFIG_USER_ONLY + gen_helper_ssamoswap_disabled(tcg_env); +#else return false; +#endif } TCGv dest = dest_gpr(ctx, a->rd); diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 110292e84dad2..8382aa94cb208 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -717,4 +717,53 @@ target_ulong helper_hyp_hlvx_wu(CPURISCVState *env, target_ulong addr) return cpu_ldl_code_mmu(env, addr, oi, ra); } +void helper_ssamoswap_disabled(CPURISCVState *env) +{ + int exception = RISCV_EXCP_ILLEGAL_INST; + + /* + * Here we follow the RISC-V CFI spec [1] to implement the exception type + * of ssamoswap* instruction. + * + * [1] RISC-V CFI spec v1.0, ch2.7 Atomic Swap from a Shadow Stack Location + * + * Note: We have already checked some conditions in trans_* functions: + * 1. The effective priv mode is not M-mode. + * 2. The xSSE specific to the effictive priv mode is disabled. + */ + if (!get_field(env->menvcfg, MENVCFG_SSE)) { + /* + * Disabled M-mode SSE always trigger illegal instruction when + * current priv mode is not M-mode. + */ + exception = RISCV_EXCP_ILLEGAL_INST; + goto done; + } + + if (!riscv_has_ext(env, RVS)) { + /* S-mode is not implemented */ + exception = RISCV_EXCP_ILLEGAL_INST; + goto done; + } else if (env->virt_enabled) { + /* + * VU/VS-mode with disabled xSSE will trigger the virtual instruction + * exception. + */ + exception = RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + goto done; + } else { + /* + * U-mode with disabled S-mode SSE will trigger the illegal instruction + * exception. + * + * Note: S-mode is already handled in the disabled M-mode SSE case. + */ + exception = RISCV_EXCP_ILLEGAL_INST; + goto done; + } + +done: + riscv_raise_exception(env, exception, GETPC()); +} + #endif /* !CONFIG_USER_ONLY */ From ae4a37f57818e47e212272821a5a86ad54620eb8 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Tue, 23 Sep 2025 17:07:28 +0800 Subject: [PATCH 1135/1794] target/riscv: rvv: Replace checking V by checking Zve32x The Zve32x extension will be applied by the V and Zve* extensions. Therefore we can replace the original V checking with Zve32x checking for both the V and Zve* extensions. Signed-off-by: Max Chou Reviewed-by: Alistair Francis Message-ID: <20250923090729.1887406-2-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 +- target/riscv/csr.c | 3 ++- target/riscv/machine.c | 3 ++- target/riscv/riscv-qmp-cmds.c | 2 +- target/riscv/tcg/tcg-cpu.c | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index d055ddf462311..a877018ab0c29 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -604,7 +604,7 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) } } } - if (riscv_has_ext(env, RVV) && (flags & CPU_DUMP_VPU)) { + if (riscv_cpu_cfg(env)->ext_zve32x && (flags & CPU_DUMP_VPU)) { static const int dump_rvv_csrs[] = { CSR_VSTART, CSR_VXSAT, diff --git a/target/riscv/csr.c b/target/riscv/csr.c index ea36eccb3dc9d..5c91658c3dc41 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -2005,7 +2005,8 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, if (riscv_has_ext(env, RVF)) { mask |= MSTATUS_FS; } - if (riscv_has_ext(env, RVV)) { + + if (riscv_cpu_cfg(env)->ext_zve32x) { mask |= MSTATUS_VS; } diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 51e0567ed30cb..18d790af0d073 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -131,7 +131,8 @@ static bool vector_needed(void *opaque) RISCVCPU *cpu = opaque; CPURISCVState *env = &cpu->env; - return riscv_has_ext(env, RVV); + return kvm_enabled() ? riscv_has_ext(env, RVV) : + riscv_cpu_cfg(env)->ext_zve32x; } static const VMStateDescription vmstate_vector = { diff --git a/target/riscv/riscv-qmp-cmds.c b/target/riscv/riscv-qmp-cmds.c index b63de8dd457aa..c499f9b9a7d6b 100644 --- a/target/riscv/riscv-qmp-cmds.c +++ b/target/riscv/riscv-qmp-cmds.c @@ -342,7 +342,7 @@ int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval) } if (reg_is_vreg(name)) { - if (!riscv_has_ext(env, RVV)) { + if (!riscv_cpu_cfg(env)->ext_zve32x) { return -EINVAL; } diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 143ab079d49bf..b3b7f14503a65 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -661,7 +661,7 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } - if (riscv_has_ext(env, RVV)) { + if (cpu->cfg.ext_zve32x) { riscv_cpu_validate_v(env, &cpu->cfg, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); From be50ff3a73859ebbbdc0e6f704793062b1743d93 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Tue, 23 Sep 2025 17:07:29 +0800 Subject: [PATCH 1136/1794] target/riscv: rvv: Modify minimum VLEN according to enabled vector extensions According to the RISC-V unprivileged specification, the VLEN should be greater or equal to the ELEN. This commit modifies the minimum VLEN based on the vector extensions and introduces a check rule for VLEN and ELEN. Extension Minimum VLEN * V 128 * Zve64[d|f|x] 64 * Zve32[f|x] 32 Signed-off-by: Max Chou Reviewed-by: Alistair Francis Message-ID: <20250923090729.1887406-3-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/tcg-cpu.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index b3b7f14503a65..1150bd14697cc 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -417,12 +417,21 @@ static void riscv_cpu_validate_misa_priv(CPURISCVState *env, Error **errp) static void riscv_cpu_validate_v(CPURISCVState *env, RISCVCPUConfig *cfg, Error **errp) { + uint32_t min_vlen; uint32_t vlen = cfg->vlenb << 3; - if (vlen > RV_VLEN_MAX || vlen < 128) { + if (riscv_has_ext(env, RVV)) { + min_vlen = 128; + } else if (cfg->ext_zve64x) { + min_vlen = 64; + } else if (cfg->ext_zve32x) { + min_vlen = 32; + } + + if (vlen > RV_VLEN_MAX || vlen < min_vlen) { error_setg(errp, "Vector extension implementation only supports VLEN " - "in the range [128, %d]", RV_VLEN_MAX); + "in the range [%d, %d]", min_vlen, RV_VLEN_MAX); return; } @@ -432,6 +441,12 @@ static void riscv_cpu_validate_v(CPURISCVState *env, RISCVCPUConfig *cfg, "in the range [8, 64]"); return; } + + if (vlen < cfg->elen) { + error_setg(errp, "Vector extension implementation requires VLEN " + "to be greater than or equal to ELEN"); + return; + } } static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu) From 81d1885dcc4424fec6761120f6e251eb3408fb8e Mon Sep 17 00:00:00 2001 From: Max Chou Date: Fri, 24 Jan 2025 15:33:22 +0800 Subject: [PATCH 1137/1794] target/riscv: rvv: Fix vslide1[up|down].vx unexpected result when XLEN=32 and SEW=64 When XLEN is 32 and SEW is 64, the original implementation of vslide1up.vx and vslide1down.vx helper functions fills the 32-bit value of rs1 into the first element of the destination vector register (rd), which is a 64-bit element. This commit attempted to resolve the issue by extending the rs1 value to 64 bits during the TCG translation phase to ensure that the helper functions won't lost the higer 32 bits. Signed-off-by: Max Chou Acked-by: Alistair Francis Message-ID: <20250124073325.2467664-1-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/helper.h | 16 ++++---- target/riscv/insn_trans/trans_rvv.c.inc | 50 ++++++++++++++++++++++++- target/riscv/vector_helper.c | 20 +++++----- 3 files changed, 66 insertions(+), 20 deletions(-) diff --git a/target/riscv/helper.h b/target/riscv/helper.h index c82bacdc397a0..b785456ee08d1 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -1101,14 +1101,14 @@ DEF_HELPER_6(vslidedown_vx_b, void, ptr, ptr, tl, ptr, env, i32) DEF_HELPER_6(vslidedown_vx_h, void, ptr, ptr, tl, ptr, env, i32) DEF_HELPER_6(vslidedown_vx_w, void, ptr, ptr, tl, ptr, env, i32) DEF_HELPER_6(vslidedown_vx_d, void, ptr, ptr, tl, ptr, env, i32) -DEF_HELPER_6(vslide1up_vx_b, void, ptr, ptr, tl, ptr, env, i32) -DEF_HELPER_6(vslide1up_vx_h, void, ptr, ptr, tl, ptr, env, i32) -DEF_HELPER_6(vslide1up_vx_w, void, ptr, ptr, tl, ptr, env, i32) -DEF_HELPER_6(vslide1up_vx_d, void, ptr, ptr, tl, ptr, env, i32) -DEF_HELPER_6(vslide1down_vx_b, void, ptr, ptr, tl, ptr, env, i32) -DEF_HELPER_6(vslide1down_vx_h, void, ptr, ptr, tl, ptr, env, i32) -DEF_HELPER_6(vslide1down_vx_w, void, ptr, ptr, tl, ptr, env, i32) -DEF_HELPER_6(vslide1down_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslide1up_vx_b, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vslide1up_vx_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vslide1up_vx_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vslide1up_vx_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vslide1down_vx_b, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vslide1down_vx_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vslide1down_vx_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vslide1down_vx_d, void, ptr, ptr, i64, ptr, env, i32) DEF_HELPER_6(vfslide1up_vf_h, void, ptr, ptr, i64, ptr, env, i32) DEF_HELPER_6(vfslide1up_vf_w, void, ptr, ptr, i64, ptr, env, i32) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 71f98fb350b68..f4b5460340ec0 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -3561,7 +3561,6 @@ static bool slideup_check(DisasContext *s, arg_rmrr *a) } GEN_OPIVX_TRANS(vslideup_vx, slideup_check) -GEN_OPIVX_TRANS(vslide1up_vx, slideup_check) GEN_OPIVI_TRANS(vslideup_vi, IMM_ZX, vslideup_vx, slideup_check) static bool slidedown_check(DisasContext *s, arg_rmrr *a) @@ -3572,9 +3571,56 @@ static bool slidedown_check(DisasContext *s, arg_rmrr *a) } GEN_OPIVX_TRANS(vslidedown_vx, slidedown_check) -GEN_OPIVX_TRANS(vslide1down_vx, slidedown_check) GEN_OPIVI_TRANS(vslidedown_vi, IMM_ZX, vslidedown_vx, slidedown_check) +typedef void gen_helper_vslide1_vx(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv_ptr, + TCGv_env, TCGv_i32); + +#define GEN_OPIVX_VSLIDE1_TRANS(NAME, CHECK) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (CHECK(s, a)) { \ + static gen_helper_vslide1_vx * const fns[4] = { \ + gen_helper_##NAME##_b, gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, gen_helper_##NAME##_d, \ + }; \ + \ + TCGv_ptr dest, src2, mask; \ + TCGv_i64 src1; \ + TCGv_i32 desc; \ + uint32_t data = 0; \ + \ + dest = tcg_temp_new_ptr(); \ + mask = tcg_temp_new_ptr(); \ + src2 = tcg_temp_new_ptr(); \ + src1 = tcg_temp_new_i64(); \ + \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, \ + s->cfg_ptr->vlenb, data)); \ + \ + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, a->rd)); \ + tcg_gen_addi_ptr(src2, tcg_env, vreg_ofs(s, a->rs2)); \ + tcg_gen_addi_ptr(mask, tcg_env, vreg_ofs(s, 0)); \ + tcg_gen_ext_tl_i64(src1, get_gpr(s, a->rs1, EXT_SIGN)); \ + \ + fns[s->sew](dest, mask, src1, src2, tcg_env, desc); \ + \ + tcg_gen_movi_tl(cpu_vstart, 0); \ + finalize_rvv_inst(s); \ + \ + return true; \ + } \ + return false; \ +} + +GEN_OPIVX_VSLIDE1_TRANS(vslide1up_vx, slideup_check) +GEN_OPIVX_VSLIDE1_TRANS(vslide1down_vx, slidedown_check) + /* Vector Floating-Point Slide Instructions */ static bool fslideup_check(DisasContext *s, arg_rmrr *a) { diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 7c67d67a13f6c..41ea223106794 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -5198,11 +5198,11 @@ GEN_VEXT_VSLIE1UP(16, H2) GEN_VEXT_VSLIE1UP(32, H4) GEN_VEXT_VSLIE1UP(64, H8) -#define GEN_VEXT_VSLIDE1UP_VX(NAME, BITWIDTH) \ -void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ - CPURISCVState *env, uint32_t desc) \ -{ \ - vslide1up_##BITWIDTH(vd, v0, s1, vs2, env, desc); \ +#define GEN_VEXT_VSLIDE1UP_VX(NAME, BITWIDTH) \ +void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + vslide1up_##BITWIDTH(vd, v0, s1, vs2, env, desc); \ } /* vslide1up.vx vd, vs2, rs1, vm # vd[0]=x[rs1], vd[i+1] = vs2[i] */ @@ -5249,11 +5249,11 @@ GEN_VEXT_VSLIDE1DOWN(16, H2) GEN_VEXT_VSLIDE1DOWN(32, H4) GEN_VEXT_VSLIDE1DOWN(64, H8) -#define GEN_VEXT_VSLIDE1DOWN_VX(NAME, BITWIDTH) \ -void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ - CPURISCVState *env, uint32_t desc) \ -{ \ - vslide1down_##BITWIDTH(vd, v0, s1, vs2, env, desc); \ +#define GEN_VEXT_VSLIDE1DOWN_VX(NAME, BITWIDTH) \ +void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + vslide1down_##BITWIDTH(vd, v0, s1, vs2, env, desc); \ } /* vslide1down.vx vd, vs2, rs1, vm # vd[i] = vs2[i+1], vd[vl-1]=x[rs1] */ From 15abfced803929f935bb59a0e1b02558bd8325c4 Mon Sep 17 00:00:00 2001 From: "Guo Ren (Alibaba DAMO Academy)" Date: Sat, 13 Sep 2025 00:12:33 -0400 Subject: [PATCH 1138/1794] hw/riscv/riscv-iommu: Fixup PDT Nested Walk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current implementation is wrong when iohgatp != bare. The RISC-V IOMMU specification has defined that the PDT is based on GPA, not SPA. So this patch fixes the problem, making PDT walk correctly when the G-stage table walk is enabled. Fixes: 0c54acb8243d ("hw/riscv: add RISC-V IOMMU base emulation") Cc: qemu-stable@nongnu.org Cc: Sebastien Boeuf Cc: Tomasz Jeznach Reviewed-by: Weiwei Li Reviewed-by: Nutty Liu Signed-off-by: Guo Ren (Alibaba DAMO Academy) Tested-by: Chen Pei Tested-by: Fangyu Yu  Message-ID: <20250913041233.972870-1-guoren@kernel.org> [ Changes by AF: - Add braces to if statements ] Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu.c | 143 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 2 deletions(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 155190d032dd9..b33c7fe3259ef 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -869,6 +869,145 @@ static bool riscv_iommu_validate_process_ctx(RISCVIOMMUState *s, return true; } +/** + * pdt_memory_read: PDT wrapper of dma_memory_read. + * + * @s: IOMMU Device State + * @ctx: Device Translation Context with devid and pasid set + * @addr: address within that address space + * @buf: buffer with the data transferred + * @len: length of the data transferred + * @attrs: memory transaction attributes + */ +static MemTxResult pdt_memory_read(RISCVIOMMUState *s, + RISCVIOMMUContext *ctx, + dma_addr_t addr, + void *buf, dma_addr_t len, + MemTxAttrs attrs) +{ + uint64_t gatp_mode, pte; + struct { + unsigned char step; + unsigned char levels; + unsigned char ptidxbits; + unsigned char ptesize; + } sc; + MemTxResult ret; + dma_addr_t base = addr; + + /* G stages translation mode */ + gatp_mode = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD); + if (gatp_mode == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) { + goto out; + } + + /* G stages translation tables root pointer */ + base = PPN_PHYS(get_field(ctx->gatp, RISCV_IOMMU_ATP_PPN_FIELD)); + + /* Start at step 0 */ + sc.step = 0; + + if (s->fctl & RISCV_IOMMU_FCTL_GXL) { + /* 32bit mode for GXL == 1 */ + switch (gatp_mode) { + case RISCV_IOMMU_DC_IOHGATP_MODE_SV32X4: + if (!(s->cap & RISCV_IOMMU_CAP_SV32X4)) { + return MEMTX_ACCESS_ERROR; + } + sc.levels = 2; + sc.ptidxbits = 10; + sc.ptesize = 4; + break; + default: + return MEMTX_ACCESS_ERROR; + } + } else { + /* 64bit mode for GXL == 0 */ + switch (gatp_mode) { + case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4: + if (!(s->cap & RISCV_IOMMU_CAP_SV39X4)) { + return MEMTX_ACCESS_ERROR; + } + sc.levels = 3; + sc.ptidxbits = 9; + sc.ptesize = 8; + break; + case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4: + if (!(s->cap & RISCV_IOMMU_CAP_SV48X4)) { + return MEMTX_ACCESS_ERROR; + } + sc.levels = 4; + sc.ptidxbits = 9; + sc.ptesize = 8; + break; + case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4: + if (!(s->cap & RISCV_IOMMU_CAP_SV57X4)) { + return MEMTX_ACCESS_ERROR; + } + sc.levels = 5; + sc.ptidxbits = 9; + sc.ptesize = 8; + break; + default: + return MEMTX_ACCESS_ERROR; + } + } + + do { + const unsigned va_bits = (sc.step ? 0 : 2) + sc.ptidxbits; + const unsigned va_skip = TARGET_PAGE_BITS + sc.ptidxbits * + (sc.levels - 1 - sc.step); + const unsigned idx = (addr >> va_skip) & ((1 << va_bits) - 1); + const dma_addr_t pte_addr = base + idx * sc.ptesize; + + /* Address range check before first level lookup */ + if (!sc.step) { + const uint64_t va_mask = (1ULL << (va_skip + va_bits)) - 1; + if ((addr & va_mask) != addr) { + return MEMTX_ACCESS_ERROR; + } + } + + /* Read page table entry */ + if (sc.ptesize == 4) { + uint32_t pte32 = 0; + ret = ldl_le_dma(s->target_as, pte_addr, &pte32, attrs); + pte = pte32; + } else { + ret = ldq_le_dma(s->target_as, pte_addr, &pte, attrs); + } + if (ret != MEMTX_OK) { + return ret; + } + + sc.step++; + hwaddr ppn = pte >> PTE_PPN_SHIFT; + + if (!(pte & PTE_V)) { + return MEMTX_ACCESS_ERROR; /* Invalid PTE */ + } else if (!(pte & (PTE_R | PTE_W | PTE_X))) { + base = PPN_PHYS(ppn); /* Inner PTE, continue walking */ + } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) { + return MEMTX_ACCESS_ERROR; /* Reserved leaf PTE flags: PTE_W */ + } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) { + return MEMTX_ACCESS_ERROR; /* Reserved leaf PTE flags: PTE_W + PTE_X */ + } else if (ppn & ((1ULL << (va_skip - TARGET_PAGE_BITS)) - 1)) { + return MEMTX_ACCESS_ERROR; /* Misaligned PPN */ + } else { + /* Leaf PTE, translation completed. */ + base = PPN_PHYS(ppn) | (addr & ((1ULL << va_skip) - 1)); + break; + } + + if (sc.step == sc.levels) { + return MEMTX_ACCESS_ERROR; /* Can't find leaf PTE */ + } + } while (1); + +out: + return dma_memory_read(s->target_as, base, buf, len, attrs); +} + /* * RISC-V IOMMU Device Context Loopkup - Device Directory Tree Walk * @@ -1041,7 +1180,7 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx) */ const int split = depth * 9 + 8; addr |= ((ctx->process_id >> split) << 3) & ~TARGET_PAGE_MASK; - if (dma_memory_read(s->target_as, addr, &de, sizeof(de), + if (pdt_memory_read(s, ctx, addr, &de, sizeof(de), MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT; } @@ -1056,7 +1195,7 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx) /* Leaf entry in PDT */ addr |= (ctx->process_id << 4) & ~TARGET_PAGE_MASK; - if (dma_memory_read(s->target_as, addr, &dc.ta, sizeof(uint64_t) * 2, + if (pdt_memory_read(s, ctx, addr, &dc.ta, sizeof(uint64_t) * 2, MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT; } From b25133d38fe693589cf695b85968caa0724bfafd Mon Sep 17 00:00:00 2001 From: vhaudiquet Date: Mon, 29 Sep 2025 13:55:43 +0200 Subject: [PATCH 1139/1794] target/riscv: Fix endianness swap on compressed instructions Three instructions were not using the endianness swap flag, which resulted in a bug on big-endian architectures. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3131 Buglink: https://bugs.launchpad.net/ubuntu/+source/qemu/+bug/2123828 Fixes: e0a3054f18e ("target/riscv: add support for Zcb extension") Signed-off-by: Valentin Haudiquet Cc: qemu-stable@nongnu.org Reviewed-by: Anton Johansson Reviewed-by: Daniel Henrique Barboza Reviewed-by: Heinrich Schuchardt Reviewed-by: Richard Henderson Message-ID: <20250929115543.1648157-1-valentin.haudiquet@canonical.com> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvzce.c.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvzce.c.inc b/target/riscv/insn_trans/trans_rvzce.c.inc index c77c2b927b0cf..dd15af0f54b96 100644 --- a/target/riscv/insn_trans/trans_rvzce.c.inc +++ b/target/riscv/insn_trans/trans_rvzce.c.inc @@ -88,13 +88,13 @@ static bool trans_c_lbu(DisasContext *ctx, arg_c_lbu *a) static bool trans_c_lhu(DisasContext *ctx, arg_c_lhu *a) { REQUIRE_ZCB(ctx); - return gen_load(ctx, a, MO_UW); + return gen_load(ctx, a, MO_TEUW); } static bool trans_c_lh(DisasContext *ctx, arg_c_lh *a) { REQUIRE_ZCB(ctx); - return gen_load(ctx, a, MO_SW); + return gen_load(ctx, a, MO_TESW); } static bool trans_c_sb(DisasContext *ctx, arg_c_sb *a) @@ -106,7 +106,7 @@ static bool trans_c_sb(DisasContext *ctx, arg_c_sb *a) static bool trans_c_sh(DisasContext *ctx, arg_c_sh *a) { REQUIRE_ZCB(ctx); - return gen_store(ctx, a, MO_UW); + return gen_store(ctx, a, MO_TEUW); } #define X_S0 8 From ad2a0aa2824b1dac9f61bac33980e866e9a88856 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Thu, 14 Aug 2025 09:44:50 +0930 Subject: [PATCH 1140/1794] docs: riscv-iommu: Update status of kernel support The iommu Linux kernel support is now upstream. VFIO is still downstream at this stage. Reviewed-by: Daniel Henrique Barboza Signed-off-by: Joel Stanley Message-ID: <20250814001452.504510-1-joel@jms.id.au> Signed-off-by: Alistair Francis --- docs/specs/riscv-iommu.rst | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/docs/specs/riscv-iommu.rst b/docs/specs/riscv-iommu.rst index 991d376fdc24a..571a6a6cc9634 100644 --- a/docs/specs/riscv-iommu.rst +++ b/docs/specs/riscv-iommu.rst @@ -30,15 +30,15 @@ This will add a RISC-V IOMMU PCI device in the board following any additional PCI parameters (like PCI bus address). The behavior of the RISC-V IOMMU is defined by the spec but its operation is OS dependent. -As of this writing the existing Linux kernel support `linux-v8`_, not yet merged, -does not have support for features like VFIO passthrough. The IOMMU emulation -was tested using a public Ventana Micro Systems kernel repository in -`ventana-linux`_. This kernel is based on `linux-v8`_ with additional patches that -enable features like KVM VFIO passthrough with irqbypass. Until the kernel support -is feature complete feel free to use the kernel available in the Ventana Micro Systems -mirror. - -The current Linux kernel support will use the IOMMU device to create IOMMU groups +Linux kernel iommu support was merged in v6.13. QEMU IOMMU emulation can be +used with mainline kernels for simple IOMMU PCIe support. + +As of v6.17, it does not have support for features like VFIO passthrough. +There is a `VFIO`_ RFC series that is not yet merged. The public Ventana Micro +Systems kernel repository in `ventana-linux`_ can be used for testing the VFIO +functions. + +The v6.13+ Linux kernel support uses the IOMMU device to create IOMMU groups with any eligible cards available in the system, regardless of factors such as the order in which the devices are added in the command line. @@ -49,7 +49,7 @@ IOMMU kernel driver behaves: $ qemu-system-riscv64 \ -M virt,aia=aplic-imsic,aia-guests=5 \ - -device riscv-iommu-pci,addr=1.0,vendor-id=0x1efd,device-id=0xedf1 \ + -device riscv-iommu-pci,addr=1.0 \ -device e1000e,netdev=net1 -netdev user,id=net1,net=192.168.0.0/24 \ -device e1000e,netdev=net2 -netdev user,id=net2,net=192.168.200.0/24 \ (...) @@ -58,21 +58,11 @@ IOMMU kernel driver behaves: -M virt,aia=aplic-imsic,aia-guests=5 \ -device e1000e,netdev=net1 -netdev user,id=net1,net=192.168.0.0/24 \ -device e1000e,netdev=net2 -netdev user,id=net2,net=192.168.200.0/24 \ - -device riscv-iommu-pci,addr=1.0,vendor-id=0x1efd,device-id=0xedf1 \ + -device riscv-iommu-pci,addr=3.0 \ (...) Both will create iommu groups for the two e1000e cards. -Another thing to notice on `linux-v8`_ and `ventana-linux`_ is that the kernel driver -considers an IOMMU identified as a Rivos device, i.e. it uses Rivos vendor ID. To -use the riscv-iommu-pci device with the existing kernel support we need to emulate -a Rivos PCI IOMMU by setting 'vendor-id' and 'device-id': - -.. code-block:: bash - - $ qemu-system-riscv64 -M virt \ - -device riscv-iommu-pci,vendor-id=0x1efd,device-id=0xedf1 (...) - Several options are available to control the capabilities of the device, namely: - "bus": the bus that the IOMMU device uses @@ -84,6 +74,7 @@ Several options are available to control the capabilities of the device, namely: - "g-stage": enable g-stage support - "hpm-counters": number of hardware performance counters available. Maximum value is 31. Default value is 31. Use 0 (zero) to disable HPM support +- "vendor-id"/"device-id": pci device ID. Defaults to 1b36:0014 (Redhat) riscv-iommu-sys device ---------------------- @@ -111,6 +102,6 @@ riscv-iommu options: .. _iommu1.0.0: https://github.com/riscv-non-isa/riscv-iommu/releases/download/v1.0.0/riscv-iommu.pdf -.. _linux-v8: https://lore.kernel.org/linux-riscv/cover.1718388908.git.tjeznach@rivosinc.com/ +.. _VFIO: https://lore.kernel.org/linux-riscv/20241114161845.502027-17-ajones@ventanamicro.com/ .. _ventana-linux: https://github.com/ventanamicro/linux/tree/dev-upstream From faccaa34f0cb75b93ff1f4bcdd2f40f23f6aa10f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 30 Sep 2025 13:59:20 +0200 Subject: [PATCH 1141/1794] subprojects: Remove version number from .gitignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Get rid of all the version numbers, and use wildcard matches instead, because peopl will repeatedly forgot to change these versions. Suggested-by: Manos Pitsidianakis Suggested-by: Daniel P. Berrangé Suggested-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- subprojects/.gitignore | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/subprojects/.gitignore b/subprojects/.gitignore index 58a29f0120441..0b5d96340944c 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -6,21 +6,21 @@ /keycodemapdb /libvfio-user /slirp -/anyhow-1.0.98 -/arbitrary-int-1.2.7 -/attrs-0.2.9 -/bilge-0.2.0 -/bilge-impl-0.2.0 -/either-1.12.0 -/foreign-0.3.1 -/itertools-0.11.0 -/libc-0.2.162 -/proc-macro-error-1.0.4 -/proc-macro-error-attr-1.0.4 -/proc-macro2-1.0.95 -/quote-1.0.36 -/syn-2.0.66 -/unicode-ident-1.0.12 +/anyhow-* +/arbitrary-int-* +/attrs-* +/bilge-* +/bilge-impl-* +/either-* +/foreign-* +/itertools-* +/libc-* +/proc-macro-error-* +/proc-macro-error-attr-* +/proc-macro* +/quote-* +/syn-* +/unicode-ident-* # Workaround for Meson v1.9.0 https://github.com/mesonbuild/meson/issues/14948 /.wraplock From 4c18783a88726c3fc39cea9117b720adb82e557f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 1 Oct 2025 08:52:32 +0200 Subject: [PATCH 1142/1794] subprojects: add glib-sys-rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- rust/meson.build | 2 ++ scripts/archive-source.sh | 1 + subprojects/.gitignore | 1 + subprojects/glib-sys-0.21-rs.wrap | 7 ++++ .../packagefiles/glib-sys-0.21-rs/meson.build | 33 +++++++++++++++++++ 5 files changed, 44 insertions(+) create mode 100644 subprojects/glib-sys-0.21-rs.wrap create mode 100644 subprojects/packagefiles/glib-sys-0.21-rs/meson.build diff --git a/rust/meson.build b/rust/meson.build index 695d5a62de901..6ba075c8c7162 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -2,12 +2,14 @@ subproject('anyhow-1-rs', required: true) subproject('bilge-0.2-rs', required: true) subproject('bilge-impl-0.2-rs', required: true) subproject('foreign-0.3-rs', required: true) +subproject('glib-sys-0.21-rs', required: true) subproject('libc-0.2-rs', required: true) anyhow_rs = dependency('anyhow-1-rs') bilge_rs = dependency('bilge-0.2-rs') bilge_impl_rs = dependency('bilge-impl-0.2-rs') foreign_rs = dependency('foreign-0.3-rs') +glib_sys_rs = dependency('glib-sys-0.21-rs') libc_rs = dependency('libc-0.2-rs') subproject('proc-macro2-1-rs', required: true) diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index a725dd923dc81..8f97b19a088fe 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -36,6 +36,7 @@ subprojects=( bilge-impl-0.2-rs either-1-rs foreign-0.3-rs + glib-sys-0.21-rs itertools-0.11-rs keycodemapdb libc-0.2-rs diff --git a/subprojects/.gitignore b/subprojects/.gitignore index 0b5d96340944c..c00c847837244 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -13,6 +13,7 @@ /bilge-impl-* /either-* /foreign-* +/glib-sys-* /itertools-* /libc-* /proc-macro-error-* diff --git a/subprojects/glib-sys-0.21-rs.wrap b/subprojects/glib-sys-0.21-rs.wrap new file mode 100644 index 0000000000000..313ced731ac86 --- /dev/null +++ b/subprojects/glib-sys-0.21-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = glib-sys-0.21.2 +source_url = https://crates.io/api/v1/crates/glib-sys/0.21.2/download +source_filename = glib-sys-0.21.2.tar.gz +source_hash = d09d3d0fddf7239521674e57b0465dfbd844632fec54f059f7f56112e3f927e1 +#method = cargo +patch_directory = glib-sys-0.21-rs diff --git a/subprojects/packagefiles/glib-sys-0.21-rs/meson.build b/subprojects/packagefiles/glib-sys-0.21-rs/meson.build new file mode 100644 index 0000000000000..8c5483311ed98 --- /dev/null +++ b/subprojects/packagefiles/glib-sys-0.21-rs/meson.build @@ -0,0 +1,33 @@ +project('glib-sys-0.21-rs', 'rust', + meson_version: '>=1.5.0', + version: '0.21.2', + license: 'MIT', + default_options: []) + +subproject('libc-0.2-rs', required: true) +libc_rs = dependency('libc-0.2-rs') + +_glib_sys_rs = static_library( + 'glib_sys', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + rust_args: [ + '--cap-lints', 'allow', + '--cfg', 'feature="v2_66"', + '--cfg', 'feature="v2_64"', + '--cfg', 'feature="v2_62"', + '--cfg', 'feature="v2_60"', + '--cfg', 'feature="v2_58"', + ], + # should also link with glib; don't bother doing it here since all + # QEMU targets have it + dependencies: [libc_rs], +) + +glib_sys_dep = declare_dependency( + link_with: _glib_sys_rs, +) + +meson.override_dependency('glib-sys-0.21-rs', glib_sys_dep) From 9c40c1ff970d7ec3d4456aeb65245d66e1226118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 24 Sep 2025 17:56:38 +0400 Subject: [PATCH 1143/1794] rust: use glib-sys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't generate FFI for glib, rely on glib-sys crate. Signed-off-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- include/hw/core/cpu.h | 2 +- meson.build | 1 + rust/Cargo.lock | 180 +++++++++++++++++++++++++++++ rust/Cargo.toml | 1 + rust/bql/Cargo.toml | 1 + rust/bql/meson.build | 1 + rust/bql/src/bindings.rs | 4 + rust/chardev/Cargo.toml | 1 + rust/chardev/meson.build | 2 +- rust/chardev/src/bindings.rs | 4 + rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/bindings.rs | 5 + rust/hw/core/Cargo.toml | 1 + rust/hw/core/meson.build | 2 +- rust/hw/core/src/bindings.rs | 3 + rust/migration/Cargo.toml | 1 + rust/migration/meson.build | 2 +- rust/migration/src/bindings.rs | 1 + rust/qom/Cargo.toml | 1 + rust/qom/meson.build | 2 +- rust/qom/src/bindings.rs | 2 + rust/system/Cargo.toml | 1 + rust/system/meson.build | 2 +- rust/system/src/bindings.rs | 4 + rust/util/Cargo.toml | 1 + rust/util/meson.build | 2 +- rust/util/src/bindings.rs | 2 + 28 files changed, 224 insertions(+), 7 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index c9f40c25392e5..4196293ba1ca6 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -169,7 +169,7 @@ struct CPUClass { vaddr (*gdb_adjust_breakpoint)(CPUState *cpu, vaddr addr); const char *gdb_core_xml_file; - const gchar * (*gdb_arch_name)(CPUState *cpu); + const char * (*gdb_arch_name)(CPUState *cpu); const char * (*gdb_get_core_xml_file)(CPUState *cpu); void (*disas_set_info)(CPUState *cpu, disassemble_info *info); diff --git a/meson.build b/meson.build index 55c8202a4d8b1..62766c0f19cc9 100644 --- a/meson.build +++ b/meson.build @@ -4239,6 +4239,7 @@ if have_rust '--no-prepend-enum-name', '--allowlist-file', meson.project_source_root() + '/include/.*', '--allowlist-file', meson.project_build_root() + '/.*', + '--blocklist-file', glib_pc.get_variable('includedir') + '/glib-2.0/.*', ] if not rustfmt.found() if bindgen.version().version_compare('<0.65.0') diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 444ef516a70af..110851334905c 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -58,15 +58,27 @@ dependencies = [ name = "bql" version = "0.1.0" dependencies = [ + "glib-sys", "migration", ] +[[package]] +name = "cfg-expr" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a2c5f3bf25ec225351aa1c8e230d04d880d3bd89dea133537dafad4ae291e5c" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "chardev" version = "0.1.0" dependencies = [ "bql", "common", + "glib-sys", "migration", "qom", "util", @@ -86,6 +98,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "foreign" version = "0.3.1" @@ -95,6 +113,28 @@ dependencies = [ "libc", ] +[[package]] +name = "glib-sys" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d09d3d0fddf7239521674e57b0465dfbd844632fec54f059f7f56112e3f927e1" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hpet" version = "0.1.0" @@ -115,6 +155,7 @@ dependencies = [ "bql", "chardev", "common", + "glib-sys", "migration", "qemu_macros", "qom", @@ -122,6 +163,16 @@ dependencies = [ "util", ] +[[package]] +name = "indexmap" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "itertools" version = "0.11.0" @@ -137,14 +188,27 @@ version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + [[package]] name = "migration" version = "0.1.0" dependencies = [ "common", + "glib-sys", "util", ] +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "pl011" version = "0.1.0" @@ -155,6 +219,7 @@ dependencies = [ "bql", "chardev", "common", + "glib-sys", "hwcore", "migration", "qom", @@ -211,6 +276,7 @@ version = "0.1.0" dependencies = [ "bql", "common", + "glib-sys", "migration", "qemu_macros", "util", @@ -225,6 +291,50 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "serde" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + [[package]] name = "syn" version = "2.0.104" @@ -241,10 +351,30 @@ name = "system" version = "0.1.0" dependencies = [ "common", + "glib-sys", "qom", "util", ] +[[package]] +name = "system-deps" +version = "7.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4be53aa0cba896d2dc615bd42bbc130acdcffa239e0a2d965ea5b3b2a86ffdb" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" + [[package]] name = "tests" version = "0.1.0" @@ -259,6 +389,40 @@ dependencies = [ "util", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "trace" version = "0.1.0" @@ -279,11 +443,27 @@ dependencies = [ "anyhow", "common", "foreign", + "glib-sys", "libc", ] +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +dependencies = [ + "memchr", +] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index f372d7dbf7022..783e626802c92 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -29,6 +29,7 @@ authors = ["The QEMU Project Developers "] anyhow = "~1.0" foreign = "~0.3.1" libc = "0.2.162" +glib-sys = { version = "0.21.2", features = ["v2_66"] } [workspace.lints.rust] unexpected_cfgs = { level = "deny", check-cfg = ['cfg(MESON)'] } diff --git a/rust/bql/Cargo.toml b/rust/bql/Cargo.toml index 1041bd4ea93d8..d5177e5f8e2a9 100644 --- a/rust/bql/Cargo.toml +++ b/rust/bql/Cargo.toml @@ -14,6 +14,7 @@ rust-version.workspace = true [dependencies] migration = { path = "../migration" } +glib-sys.workspace = true [features] default = ["debug_cell"] diff --git a/rust/bql/meson.build b/rust/bql/meson.build index bc51c7f160be1..22d7c9b8776f1 100644 --- a/rust/bql/meson.build +++ b/rust/bql/meson.build @@ -38,6 +38,7 @@ _bql_rs = static_library( rust_abi: 'rust', rust_args: _bql_cfg, link_with: [_migration_rs], + dependencies: [glib_sys_rs], ) bql_rs = declare_dependency(link_with: [_bql_rs], diff --git a/rust/bql/src/bindings.rs b/rust/bql/src/bindings.rs index 9ffff12cded80..8c70f3a87ce18 100644 --- a/rust/bql/src/bindings.rs +++ b/rust/bql/src/bindings.rs @@ -18,6 +18,10 @@ clippy::too_many_arguments )] +use glib_sys::{ + guint, GArray, GHashTable, GHashTableIter, GList, GPollFD, GPtrArray, GQueue, GSList, GSource, +}; + #[cfg(MESON)] include!("bindings.inc.rs"); diff --git a/rust/chardev/Cargo.toml b/rust/chardev/Cargo.toml index 3e77972546eb6..f105189dccbb3 100644 --- a/rust/chardev/Cargo.toml +++ b/rust/chardev/Cargo.toml @@ -13,6 +13,7 @@ repository.workspace = true rust-version.workspace = true [dependencies] +glib-sys = { workspace = true } common = { path = "../common" } bql = { path = "../bql" } migration = { path = "../migration" } diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build index e7ce02b3bc23d..d365d8dd0f456 100644 --- a/rust/chardev/meson.build +++ b/rust/chardev/meson.build @@ -36,7 +36,7 @@ _chardev_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], - dependencies: [common_rs, qemu_macros], + dependencies: [glib_sys_rs, common_rs, qemu_macros], ) chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [chardev, qemuutil]) diff --git a/rust/chardev/src/bindings.rs b/rust/chardev/src/bindings.rs index 2d98026d62752..c95dc89c56dcb 100644 --- a/rust/chardev/src/bindings.rs +++ b/rust/chardev/src/bindings.rs @@ -19,6 +19,10 @@ )] use common::Zeroable; +use glib_sys::{ + gboolean, guint, GArray, GHashTable, GHashTableIter, GIOCondition, GList, GMainContext, + GPollFD, GPtrArray, GQueue, GSList, GSource, GSourceFunc, +}; #[cfg(MESON)] include!("bindings.inc.rs"); diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index dc41d0e499e16..5b319455ee30a 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -13,6 +13,7 @@ repository.workspace = true rust-version.workspace = true [dependencies] +glib-sys.workspace = true bilge = { version = "0.2.0" } bilge-impl = { version = "0.2.0" } bits = { path = "../../../bits" } diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index 07b3da17e8319..33b91f21911c5 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -33,6 +33,7 @@ _libpl011_rs = static_library( bilge_impl_rs, bits_rs, common_rs, + glib_sys_rs, util_rs, migration_rs, bql_rs, diff --git a/rust/hw/char/pl011/src/bindings.rs b/rust/hw/char/pl011/src/bindings.rs index bd5ea840cb2b1..52a76d0de5cba 100644 --- a/rust/hw/char/pl011/src/bindings.rs +++ b/rust/hw/char/pl011/src/bindings.rs @@ -20,6 +20,11 @@ //! `bindgen`-generated declarations. +use glib_sys::{ + gboolean, guint, GArray, GByteArray, GHashTable, GHashTableIter, GIOCondition, GList, + GMainContext, GPollFD, GPtrArray, GQueue, GSList, GSource, GSourceFunc, GString, +}; + #[cfg(MESON)] include!("bindings.inc.rs"); diff --git a/rust/hw/core/Cargo.toml b/rust/hw/core/Cargo.toml index 9a9aa517082ed..ecfb56471843d 100644 --- a/rust/hw/core/Cargo.toml +++ b/rust/hw/core/Cargo.toml @@ -13,6 +13,7 @@ repository.workspace = true rust-version.workspace = true [dependencies] +glib-sys.workspace = true qemu_macros = { path = "../../qemu-macros" } common = { path = "../../common" } bql = { path = "../../bql" } diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build index e1ae95ed61e39..1560dd20c6b22 100644 --- a/rust/hw/core/meson.build +++ b/rust/hw/core/meson.build @@ -59,7 +59,7 @@ _hwcore_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', link_with: [_bql_rs, _chardev_rs, _migration_rs, _qom_rs, _system_rs, _util_rs], - dependencies: [qemu_macros, common_rs], + dependencies: [glib_sys_rs, qemu_macros, common_rs], ) hwcore_rs = declare_dependency(link_with: [_hwcore_rs], diff --git a/rust/hw/core/src/bindings.rs b/rust/hw/core/src/bindings.rs index 919c02b56aea6..65b9aae75360e 100644 --- a/rust/hw/core/src/bindings.rs +++ b/rust/hw/core/src/bindings.rs @@ -20,6 +20,9 @@ use chardev::bindings::Chardev; use common::Zeroable; +use glib_sys::{ + GArray, GByteArray, GHashTable, GHashTableIter, GList, GPtrArray, GQueue, GSList, GString, +}; use migration::bindings::VMStateDescription; use qom::bindings::ObjectClass; use system::bindings::MemoryRegion; diff --git a/rust/migration/Cargo.toml b/rust/migration/Cargo.toml index 708bfaaa68256..94504f3625c92 100644 --- a/rust/migration/Cargo.toml +++ b/rust/migration/Cargo.toml @@ -15,6 +15,7 @@ rust-version.workspace = true [dependencies] common = { path = "../common" } util = { path = "../util" } +glib-sys.workspace = true [lints] workspace = true diff --git a/rust/migration/meson.build b/rust/migration/meson.build index ddf5c2f51d504..18be65c92cf00 100644 --- a/rust/migration/meson.build +++ b/rust/migration/meson.build @@ -38,7 +38,7 @@ _migration_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', link_with: [_util_rs], - dependencies: [common_rs], + dependencies: [common_rs, glib_sys_rs], ) migration_rs = declare_dependency(link_with: [_migration_rs], diff --git a/rust/migration/src/bindings.rs b/rust/migration/src/bindings.rs index 8ce13a9000ef2..24503eb69bd68 100644 --- a/rust/migration/src/bindings.rs +++ b/rust/migration/src/bindings.rs @@ -19,6 +19,7 @@ )] use common::Zeroable; +use glib_sys::{GHashTable, GHashTableIter, GList, GPtrArray, GQueue, GSList}; #[cfg(MESON)] include!("bindings.inc.rs"); diff --git a/rust/qom/Cargo.toml b/rust/qom/Cargo.toml index 060ad2ec349da..4be3c2541b612 100644 --- a/rust/qom/Cargo.toml +++ b/rust/qom/Cargo.toml @@ -18,6 +18,7 @@ bql = { path = "../bql" } migration = { path = "../migration" } qemu_macros = { path = "../qemu-macros" } util = { path = "../util" } +glib-sys.workspace = true [lints] workspace = true diff --git a/rust/qom/meson.build b/rust/qom/meson.build index 71fdac696c3c3..e50f41858d618 100644 --- a/rust/qom/meson.build +++ b/rust/qom/meson.build @@ -29,7 +29,7 @@ _qom_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', link_with: [_bql_rs, _migration_rs], - dependencies: [common_rs, qemu_macros], + dependencies: [common_rs, glib_sys_rs, qemu_macros], ) qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_macros, qom]) diff --git a/rust/qom/src/bindings.rs b/rust/qom/src/bindings.rs index 9ffff12cded80..91de42f242672 100644 --- a/rust/qom/src/bindings.rs +++ b/rust/qom/src/bindings.rs @@ -18,6 +18,8 @@ clippy::too_many_arguments )] +use glib_sys::{GHashTable, GHashTableIter, GList, GPtrArray, GQueue, GSList}; + #[cfg(MESON)] include!("bindings.inc.rs"); diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml index 7fd369b9e3239..186ea00bfff9c 100644 --- a/rust/system/Cargo.toml +++ b/rust/system/Cargo.toml @@ -16,6 +16,7 @@ rust-version.workspace = true common = { path = "../common" } qom = { path = "../qom" } util = { path = "../util" } +glib-sys.workspace = true [lints] workspace = true diff --git a/rust/system/meson.build b/rust/system/meson.build index 0859f3974535e..73d61991146ac 100644 --- a/rust/system/meson.build +++ b/rust/system/meson.build @@ -36,7 +36,7 @@ _system_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], - dependencies: [common_rs, qemu_macros], + dependencies: [glib_sys_rs, common_rs, qemu_macros], ) system_rs = declare_dependency(link_with: [_system_rs], diff --git a/rust/system/src/bindings.rs b/rust/system/src/bindings.rs index 43edd98807adb..6cbb588de3d9c 100644 --- a/rust/system/src/bindings.rs +++ b/rust/system/src/bindings.rs @@ -19,6 +19,10 @@ )] use common::Zeroable; +use glib_sys::{ + guint, GArray, GByteArray, GHashTable, GHashTableIter, GList, GPollFD, GPtrArray, GQueue, + GSList, GString, +}; #[cfg(MESON)] include!("bindings.inc.rs"); diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml index 1f6767ed9d102..85f914365450d 100644 --- a/rust/util/Cargo.toml +++ b/rust/util/Cargo.toml @@ -15,6 +15,7 @@ rust-version.workspace = true [dependencies] anyhow = { workspace = true } foreign = { workspace = true } +glib-sys = { workspace = true } libc = { workspace = true } common = { path = "../common" } diff --git a/rust/util/meson.build b/rust/util/meson.build index 094b43355aa2b..b0b75e93ff655 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -40,7 +40,7 @@ _util_rs = static_library( ), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', - dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qom, qemuutil], + dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs, qom, qemuutil], ) util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom]) diff --git a/rust/util/src/bindings.rs b/rust/util/src/bindings.rs index 9ffff12cded80..c277a295add3d 100644 --- a/rust/util/src/bindings.rs +++ b/rust/util/src/bindings.rs @@ -18,6 +18,8 @@ clippy::too_many_arguments )] +use glib_sys::{guint, GList, GPollFD, GQueue, GSList, GString}; + #[cfg(MESON)] include!("bindings.inc.rs"); From 73b42fc58d035cb2fcfe90083d6b33aeb4fa1b2a Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:18 +0530 Subject: [PATCH 1144/1794] migration: push Error **errp into vmstate_subsection_load() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that vmstate_subsection_load() must report an error in errp, in case of failure. The errors are temporarily reported using error_report_err(). This is removed in the subsequent patches in this series, when we are actually able to propagate the error to the calling function using errp. Reviewed-by: Marc-André Lureau Reviewed-by: Fabiano Rosas Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-1-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/vmstate.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/migration/vmstate.c b/migration/vmstate.c index 5feaa3244d259..08f2b562e3a5e 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -25,7 +25,7 @@ static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, JSONWriter *vmdesc, Error **errp); static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque); + void *opaque, Error **errp); /* Whether this field should exist for either save or load the VM? */ static bool @@ -136,6 +136,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, { const VMStateField *field = vmsd->fields; int ret = 0; + Error *local_err = NULL; trace_vmstate_load_state(vmsd->name, version_id); if (version_id > vmsd->version_id) { @@ -225,9 +226,10 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, field++; } assert(field->flags == VMS_END); - ret = vmstate_subsection_load(f, vmsd, opaque); + ret = vmstate_subsection_load(f, vmsd, opaque, &local_err); if (ret != 0) { qemu_file_set_error(f, ret); + error_report_err(local_err); return ret; } if (vmsd->post_load) { @@ -566,7 +568,7 @@ vmstate_get_subsection(const VMStateDescription * const *sub, } static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque) + void *opaque, Error **errp) { trace_vmstate_subsection_load(vmsd->name); @@ -598,6 +600,8 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr); if (sub_vmsd == NULL) { trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(lookup)"); + error_setg(errp, "VM subsection '%s' in '%s' does not exist", + idstr, vmsd->name); return -ENOENT; } qemu_file_skip(f, 1); /* subsection */ @@ -608,6 +612,9 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, ret = vmstate_load_state(f, sub_vmsd, opaque, version_id); if (ret) { trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(child)"); + error_setg(errp, + "Loading VM subsection '%s' in '%s' failed: %d", + idstr, vmsd->name, ret); return ret; } } From c632ffbd74a497e88bbb4e4d55a357055eae6f47 Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:19 +0530 Subject: [PATCH 1145/1794] migration: push Error **errp into vmstate_load_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that vmstate_load_state() must report an error in errp, in case of failure. The errors are temporarily reported using error_report_err(). This is removed in the subsequent patches in this series, when we are actually able to propagate the error to the calling function using errp. Whereas, if we want the function to exit on error, then error_fatal is passed. Reviewed-by: Marc-André Lureau Reviewed-by: Fabiano Rosas Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-2-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- hw/display/virtio-gpu.c | 2 +- hw/pci/pci.c | 3 +- hw/s390x/virtio-ccw.c | 2 +- hw/scsi/spapr_vscsi.c | 4 ++- hw/vfio/pci.c | 5 ++- hw/virtio/virtio-mmio.c | 3 +- hw/virtio/virtio-pci.c | 2 +- hw/virtio/virtio.c | 7 +++-- include/migration/vmstate.h | 2 +- migration/cpr.c | 3 +- migration/savevm.c | 8 +++-- migration/vmstate-types.c | 28 ++++++++++------- migration/vmstate.c | 61 +++++++++++++++++++++++------------ tests/unit/test-vmstate.c | 63 +++++++++++++++++++++++++++++++------ ui/vdagent.c | 5 ++- 15 files changed, 143 insertions(+), 55 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index de3590221308d..e61585aa611fb 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -1347,7 +1347,7 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, } /* load & apply scanout state */ - vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1); + vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1, &error_fatal); return 0; } diff --git a/hw/pci/pci.c b/hw/pci/pci.c index c3df9d6656bf8..17715ca1b34c9 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -934,7 +934,8 @@ void pci_device_save(PCIDevice *s, QEMUFile *f) int pci_device_load(PCIDevice *s, QEMUFile *f) { int ret; - ret = vmstate_load_state(f, &vmstate_pci_device, s, s->version_id); + ret = vmstate_load_state(f, &vmstate_pci_device, s, s->version_id, + &error_fatal); /* Restore the interrupt status bit. */ pci_update_irq_status(s); return ret; diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index d2f85b39f30f7..6a9641a03d5d3 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1136,7 +1136,7 @@ static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f) static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - return vmstate_load_state(f, &vmstate_virtio_ccw_dev, dev, 1); + return vmstate_load_state(f, &vmstate_virtio_ccw_dev, dev, 1, &error_fatal); } static void virtio_ccw_pre_plugged(DeviceState *d, Error **errp) diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index 20f70fb2729de..da173f4867639 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -642,15 +642,17 @@ static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq) VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(bus->qbus.parent); vscsi_req *req; int rc; + Error *local_err = NULL; assert(sreq->tag < VSCSI_REQ_LIMIT); req = &s->reqs[sreq->tag]; assert(!req->active); memset(req, 0, sizeof(*req)); - rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1); + rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1, &local_err); if (rc) { fprintf(stderr, "VSCSI: failed loading request tag#%u\n", sreq->tag); + error_report_err(local_err); return NULL; } assert(req->active); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 5b022da19ef86..a5df4685d4981 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2831,13 +2831,16 @@ static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f) PCIDevice *pdev = PCI_DEVICE(vdev); pcibus_t old_addr[PCI_NUM_REGIONS - 1]; int bar, ret; + Error *local_err = NULL; for (bar = 0; bar < PCI_ROM_SLOT; bar++) { old_addr[bar] = pdev->io_regions[bar].addr; } - ret = vmstate_load_state(f, &vmstate_vfio_pci_config, vdev, 1); + ret = vmstate_load_state(f, &vmstate_vfio_pci_config, vdev, 1, + &local_err); if (ret) { + error_report_err(local_err); return ret; } diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 532c67107ba1d..0a688909fc606 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -34,6 +34,7 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "trace.h" +#include "qapi/error.h" static bool virtio_mmio_ioeventfd_enabled(DeviceState *d) { @@ -619,7 +620,7 @@ static int virtio_mmio_load_extra_state(DeviceState *opaque, QEMUFile *f) { VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); - return vmstate_load_state(f, &vmstate_virtio_mmio, proxy, 1); + return vmstate_load_state(f, &vmstate_virtio_mmio, proxy, 1, &error_fatal); } static bool virtio_mmio_has_extra_state(DeviceState *opaque) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 767216d795998..b04faa1e5c91b 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -161,7 +161,7 @@ static int virtio_pci_load_extra_state(DeviceState *d, QEMUFile *f) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - return vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1); + return vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1, &error_fatal); } static void virtio_pci_save_queue(DeviceState *d, int n, QEMUFile *f) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 9a81ad912e013..018803c80d131 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -3235,6 +3235,7 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); + Error *local_err = NULL; /* * We poison the endianness to ensure it does not get used before @@ -3327,15 +3328,17 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) } if (vdc->vmsd) { - ret = vmstate_load_state(f, vdc->vmsd, vdev, version_id); + ret = vmstate_load_state(f, vdc->vmsd, vdev, version_id, &local_err); if (ret) { + error_report_err(local_err); return ret; } } /* Subsections */ - ret = vmstate_load_state(f, &vmstate_virtio, vdev, 1); + ret = vmstate_load_state(f, &vmstate_virtio, vdev, 1, &local_err); if (ret) { + error_report_err(local_err); return ret; } diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 1ff7bd9ac425b..056781b1c21e7 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -1196,7 +1196,7 @@ extern const VMStateInfo vmstate_info_qlist; } int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque, int version_id); + void *opaque, int version_id, Error **errp); int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, JSONWriter *vmdesc); int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription *vmsd, diff --git a/migration/cpr.c b/migration/cpr.c index 9848a21ea6ed2..6b0e19651a46c 100644 --- a/migration/cpr.c +++ b/migration/cpr.c @@ -234,9 +234,8 @@ int cpr_state_load(MigrationChannel *channel, Error **errp) return -ENOTSUP; } - ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1); + ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1, errp); if (ret) { - error_setg(errp, "vmstate_load_state error %d", ret); qemu_fclose(f); return ret; } diff --git a/migration/savevm.c b/migration/savevm.c index abe0547f9b3f5..55c99e0902dce 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -969,7 +969,8 @@ static int vmstate_load(QEMUFile *f, SaveStateEntry *se) if (!se->vmsd) { /* Old style */ return se->ops->load_state(f, se->opaque, se->load_version_id); } - return vmstate_load_state(f, se->vmsd, se->opaque, se->load_version_id); + return vmstate_load_state(f, se->vmsd, se->opaque, se->load_version_id, + &error_fatal); } static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se, @@ -2817,6 +2818,7 @@ static int qemu_loadvm_state_header(QEMUFile *f) { unsigned int v; int ret; + Error *local_err = NULL; v = qemu_get_be32(f); if (v != QEMU_VM_FILE_MAGIC) { @@ -2839,9 +2841,11 @@ static int qemu_loadvm_state_header(QEMUFile *f) error_report("Configuration section missing"); return -EINVAL; } - ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0); + ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0, + &local_err); if (ret) { + error_report_err(local_err); return ret; } } diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c index 741a588b7e18c..c5cfd861e3aa5 100644 --- a/migration/vmstate-types.c +++ b/migration/vmstate-types.c @@ -19,6 +19,7 @@ #include "qemu/error-report.h" #include "qemu/queue.h" #include "trace.h" +#include "qapi/error.h" /* bool */ @@ -543,13 +544,17 @@ static int get_tmp(QEMUFile *f, void *pv, size_t size, const VMStateField *field) { int ret; + Error *local_err = NULL; const VMStateDescription *vmsd = field->vmsd; int version_id = field->version_id; void *tmp = g_malloc(size); /* Writes the parent field which is at the start of the tmp */ *(void **)tmp = pv; - ret = vmstate_load_state(f, vmsd, tmp, version_id); + ret = vmstate_load_state(f, vmsd, tmp, version_id, &local_err); + if (ret < 0) { + error_report_err(local_err); + } g_free(tmp); return ret; } @@ -626,6 +631,7 @@ static int get_qtailq(QEMUFile *f, void *pv, size_t unused_size, const VMStateField *field) { int ret = 0; + Error *local_err = NULL; const VMStateDescription *vmsd = field->vmsd; /* size of a QTAILQ element */ size_t size = field->size; @@ -649,8 +655,9 @@ static int get_qtailq(QEMUFile *f, void *pv, size_t unused_size, while (qemu_get_byte(f)) { elm = g_malloc(size); - ret = vmstate_load_state(f, vmsd, elm, version_id); + ret = vmstate_load_state(f, vmsd, elm, version_id, &local_err); if (ret) { + error_report_err(local_err); return ret; } QTAILQ_RAW_INSERT_TAIL(pv, elm, entry_offset); @@ -772,6 +779,7 @@ static int get_gtree(QEMUFile *f, void *pv, size_t unused_size, GTree *tree = *pval; void *key, *val; int ret = 0; + Error *local_err = NULL; /* in case of direct key, the key vmsd can be {}, ie. check fields */ if (!direct_key && version_id > key_vmsd->version_id) { @@ -803,18 +811,16 @@ static int get_gtree(QEMUFile *f, void *pv, size_t unused_size, key = (void *)(uintptr_t)qemu_get_be64(f); } else { key = g_malloc0(key_size); - ret = vmstate_load_state(f, key_vmsd, key, version_id); + ret = vmstate_load_state(f, key_vmsd, key, version_id, &local_err); if (ret) { - error_report("%s : failed to load %s (%d)", - field->name, key_vmsd->name, ret); + error_report_err(local_err); goto key_error; } } val = g_malloc0(val_size); - ret = vmstate_load_state(f, val_vmsd, val, version_id); + ret = vmstate_load_state(f, val_vmsd, val, version_id, &local_err); if (ret) { - error_report("%s : failed to load %s (%d)", - field->name, val_vmsd->name, ret); + error_report_err(local_err); goto val_error; } g_tree_insert(tree, key, val); @@ -872,6 +878,7 @@ static int get_qlist(QEMUFile *f, void *pv, size_t unused_size, const VMStateField *field) { int ret = 0; + Error *local_err = NULL; const VMStateDescription *vmsd = field->vmsd; /* size of a QLIST element */ size_t size = field->size; @@ -892,10 +899,9 @@ static int get_qlist(QEMUFile *f, void *pv, size_t unused_size, while (qemu_get_byte(f)) { elm = g_malloc(size); - ret = vmstate_load_state(f, vmsd, elm, version_id); + ret = vmstate_load_state(f, vmsd, elm, version_id, &local_err); if (ret) { - error_report("%s: failed to load %s (%d)", field->name, - vmsd->name, ret); + error_report_err(local_err); g_free(elm); return ret; } diff --git a/migration/vmstate.c b/migration/vmstate.c index 08f2b562e3a5e..8d1e9eb62bb9a 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -132,30 +132,33 @@ static void vmstate_handle_alloc(void *ptr, const VMStateField *field, } int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque, int version_id) + void *opaque, int version_id, Error **errp) { const VMStateField *field = vmsd->fields; int ret = 0; - Error *local_err = NULL; trace_vmstate_load_state(vmsd->name, version_id); if (version_id > vmsd->version_id) { - error_report("%s: incoming version_id %d is too new " - "for local version_id %d", - vmsd->name, version_id, vmsd->version_id); + error_setg(errp, "%s: incoming version_id %d is too new " + "for local version_id %d", + vmsd->name, version_id, vmsd->version_id); trace_vmstate_load_state_end(vmsd->name, "too new", -EINVAL); return -EINVAL; } if (version_id < vmsd->minimum_version_id) { - error_report("%s: incoming version_id %d is too old " - "for local minimum version_id %d", - vmsd->name, version_id, vmsd->minimum_version_id); + error_setg(errp, "%s: incoming version_id %d is too old " + "for local minimum version_id %d", + vmsd->name, version_id, vmsd->minimum_version_id); trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL); return -EINVAL; } if (vmsd->pre_load) { ret = vmsd->pre_load(opaque); if (ret) { + error_setg(errp, "pre load hook failed for: '%s', " + "version_id: %d, minimum version_id: %d, ret: %d", + vmsd->name, vmsd->version_id, vmsd->minimum_version_id, + ret); return ret; } } @@ -193,13 +196,21 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, if (inner_field->flags & VMS_STRUCT) { ret = vmstate_load_state(f, inner_field->vmsd, curr_elem, - inner_field->vmsd->version_id); + inner_field->vmsd->version_id, + errp); } else if (inner_field->flags & VMS_VSTRUCT) { ret = vmstate_load_state(f, inner_field->vmsd, curr_elem, - inner_field->struct_version_id); + inner_field->struct_version_id, + errp); } else { ret = inner_field->info->get(f, curr_elem, size, inner_field); + if (ret < 0) { + error_setg(errp, + "Failed to load element of type %s for %s: " + "%d", inner_field->info->name, + inner_field->name, ret); + } } /* If we used a fake temp field.. free it now */ @@ -209,31 +220,40 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, if (ret >= 0) { ret = qemu_file_get_error(f); + if (ret < 0) { + error_setg(errp, + "Failed to load %s state: stream error: %d", + vmsd->name, ret); + } } if (ret < 0) { qemu_file_set_error(f, ret); - error_report("Failed to load %s:%s", vmsd->name, - field->name); trace_vmstate_load_field_error(field->name, ret); return ret; } } } else if (field->flags & VMS_MUST_EXIST) { - error_report("Input validation failed: %s/%s", - vmsd->name, field->name); + error_setg(errp, "Input validation failed: %s/%s version_id: %d", + vmsd->name, field->name, vmsd->version_id); return -1; } field++; } assert(field->flags == VMS_END); - ret = vmstate_subsection_load(f, vmsd, opaque, &local_err); + ret = vmstate_subsection_load(f, vmsd, opaque, errp); if (ret != 0) { qemu_file_set_error(f, ret); - error_report_err(local_err); return ret; } if (vmsd->post_load) { ret = vmsd->post_load(opaque, version_id); + if (ret < 0) { + error_setg(errp, + "post load hook failed for: %s, version_id: %d, " + "minimum_version: %d, ret: %d", + vmsd->name, vmsd->version_id, vmsd->minimum_version_id, + ret); + } } trace_vmstate_load_state_end(vmsd->name, "end", ret); return ret; @@ -570,6 +590,7 @@ vmstate_get_subsection(const VMStateDescription * const *sub, static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, Error **errp) { + ERRP_GUARD(); trace_vmstate_subsection_load(vmsd->name); while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) { @@ -609,12 +630,12 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, qemu_file_skip(f, len); /* idstr */ version_id = qemu_get_be32(f); - ret = vmstate_load_state(f, sub_vmsd, opaque, version_id); + ret = vmstate_load_state(f, sub_vmsd, opaque, version_id, errp); if (ret) { trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(child)"); - error_setg(errp, - "Loading VM subsection '%s' in '%s' failed: %d", - idstr, vmsd->name, ret); + error_prepend(errp, + "Loading VM subsection '%s' in '%s' failed: %d: ", + idstr, vmsd->name, ret); return ret; } } diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c index 63f28f26f4569..4ff0ab632f7e0 100644 --- a/tests/unit/test-vmstate.c +++ b/tests/unit/test-vmstate.c @@ -30,6 +30,7 @@ #include "../migration/savevm.h" #include "qemu/module.h" #include "io/channel-file.h" +#include "qapi/error.h" static int temp_fd; @@ -108,14 +109,16 @@ static int load_vmstate_one(const VMStateDescription *desc, void *obj, { QEMUFile *f; int ret; + Error *local_err = NULL; f = open_test_file(true); qemu_put_buffer(f, wire, size); qemu_fclose(f); f = open_test_file(false); - ret = vmstate_load_state(f, desc, obj, version); + ret = vmstate_load_state(f, desc, obj, version, &local_err); if (ret) { + error_report_err(local_err); g_assert(qemu_file_get_error(f)); } else{ g_assert(!qemu_file_get_error(f)); @@ -355,6 +358,8 @@ static const VMStateDescription vmstate_versioned = { static void test_load_v1(void) { + Error *local_err = NULL; + int ret; uint8_t buf[] = { 0, 0, 0, 10, /* a */ 0, 0, 0, 30, /* c */ @@ -365,7 +370,10 @@ static void test_load_v1(void) QEMUFile *loading = open_test_file(false); TestStruct obj = { .b = 200, .e = 500, .f = 600 }; - vmstate_load_state(loading, &vmstate_versioned, &obj, 1); + ret = vmstate_load_state(loading, &vmstate_versioned, &obj, 1, &local_err); + if (ret < 0) { + error_report_err(local_err); + } g_assert(!qemu_file_get_error(loading)); g_assert_cmpint(obj.a, ==, 10); g_assert_cmpint(obj.b, ==, 200); @@ -378,6 +386,8 @@ static void test_load_v1(void) static void test_load_v2(void) { + Error *local_err = NULL; + int ret; uint8_t buf[] = { 0, 0, 0, 10, /* a */ 0, 0, 0, 20, /* b */ @@ -391,7 +401,10 @@ static void test_load_v2(void) QEMUFile *loading = open_test_file(false); TestStruct obj; - vmstate_load_state(loading, &vmstate_versioned, &obj, 2); + ret = vmstate_load_state(loading, &vmstate_versioned, &obj, 2, &local_err); + if (ret < 0) { + error_report_err(local_err); + } g_assert_cmpint(obj.a, ==, 10); g_assert_cmpint(obj.b, ==, 20); g_assert_cmpint(obj.c, ==, 30); @@ -467,6 +480,8 @@ static void test_save_skip(void) static void test_load_noskip(void) { + Error *local_err = NULL; + int ret; uint8_t buf[] = { 0, 0, 0, 10, /* a */ 0, 0, 0, 20, /* b */ @@ -480,7 +495,10 @@ static void test_load_noskip(void) QEMUFile *loading = open_test_file(false); TestStruct obj = { .skip_c_e = false }; - vmstate_load_state(loading, &vmstate_skipping, &obj, 2); + ret = vmstate_load_state(loading, &vmstate_skipping, &obj, 2, &local_err); + if (ret < 0) { + error_report_err(local_err); + } g_assert(!qemu_file_get_error(loading)); g_assert_cmpint(obj.a, ==, 10); g_assert_cmpint(obj.b, ==, 20); @@ -493,6 +511,8 @@ static void test_load_noskip(void) static void test_load_skip(void) { + Error *local_err = NULL; + int ret; uint8_t buf[] = { 0, 0, 0, 10, /* a */ 0, 0, 0, 20, /* b */ @@ -504,7 +524,10 @@ static void test_load_skip(void) QEMUFile *loading = open_test_file(false); TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 }; - vmstate_load_state(loading, &vmstate_skipping, &obj, 2); + ret = vmstate_load_state(loading, &vmstate_skipping, &obj, 2, &local_err); + if (ret < 0) { + error_report_err(local_err); + } g_assert(!qemu_file_get_error(loading)); g_assert_cmpint(obj.a, ==, 10); g_assert_cmpint(obj.b, ==, 20); @@ -744,6 +767,8 @@ static void test_save_q(void) static void test_load_q(void) { + int ret; + Error *local_err = NULL; TestQtailq obj_q = { .i16 = -512, .i32 = 70000, @@ -773,7 +798,10 @@ static void test_load_q(void) TestQtailq tgt; QTAILQ_INIT(&tgt.q); - vmstate_load_state(fload, &vmstate_q, &tgt, 1); + ret = vmstate_load_state(fload, &vmstate_q, &tgt, 1, &local_err); + if (ret < 0) { + error_report_err(local_err); + } char eof = qemu_get_byte(fload); g_assert(!qemu_file_get_error(fload)); g_assert_cmpint(tgt.i16, ==, obj_q.i16); @@ -1115,6 +1143,8 @@ static void diff_iommu(TestGTreeIOMMU *iommu1, TestGTreeIOMMU *iommu2) static void test_gtree_load_domain(void) { + Error *local_err = NULL; + int ret; TestGTreeDomain *dest_domain = g_new0(TestGTreeDomain, 1); TestGTreeDomain *orig_domain = create_first_domain(); QEMUFile *fload, *fsave; @@ -1127,7 +1157,11 @@ static void test_gtree_load_domain(void) fload = open_test_file(false); - vmstate_load_state(fload, &vmstate_domain, dest_domain, 1); + ret = vmstate_load_state(fload, &vmstate_domain, dest_domain, 1, + &local_err); + if (ret < 0) { + error_report_err(local_err); + } eof = qemu_get_byte(fload); g_assert(!qemu_file_get_error(fload)); g_assert_cmpint(orig_domain->id, ==, dest_domain->id); @@ -1230,6 +1264,8 @@ static void test_gtree_save_iommu(void) static void test_gtree_load_iommu(void) { + Error *local_err = NULL; + int ret; TestGTreeIOMMU *dest_iommu = g_new0(TestGTreeIOMMU, 1); TestGTreeIOMMU *orig_iommu = create_iommu(); QEMUFile *fsave, *fload; @@ -1241,7 +1277,10 @@ static void test_gtree_load_iommu(void) qemu_fclose(fsave); fload = open_test_file(false); - vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1); + ret = vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1, &local_err); + if (ret < 0) { + error_report_err(local_err); + } eof = qemu_get_byte(fload); g_assert(!qemu_file_get_error(fload)); g_assert_cmpint(orig_iommu->id, ==, dest_iommu->id); @@ -1363,6 +1402,8 @@ static void test_save_qlist(void) static void test_load_qlist(void) { + Error *local_err = NULL; + int ret; QEMUFile *fsave, *fload; TestQListContainer *orig_container = alloc_container(); TestQListContainer *dest_container = g_new0(TestQListContainer, 1); @@ -1376,7 +1417,11 @@ static void test_load_qlist(void) qemu_fclose(fsave); fload = open_test_file(false); - vmstate_load_state(fload, &vmstate_container, dest_container, 1); + ret = vmstate_load_state(fload, &vmstate_container, dest_container, 1, + &local_err); + if (ret < 0) { + error_report_err(local_err); + } eof = qemu_get_byte(fload); g_assert(!qemu_file_get_error(fload)); g_assert_cmpint(eof, ==, QEMU_VM_EOF); diff --git a/ui/vdagent.c b/ui/vdagent.c index c0746fe5b168f..bc3c77f01332a 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -1001,6 +1001,7 @@ static int get_cbinfo(QEMUFile *f, void *pv, size_t size, VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(pv); struct CBInfoArray cbinfo = {}; int i, ret; + Error *local_err = NULL; if (!have_clipboard(vd)) { return 0; @@ -1008,8 +1009,10 @@ static int get_cbinfo(QEMUFile *f, void *pv, size_t size, vdagent_clipboard_peer_register(vd); - ret = vmstate_load_state(f, &vmstate_cbinfo_array, &cbinfo, 0); + ret = vmstate_load_state(f, &vmstate_cbinfo_array, &cbinfo, 0, + &local_err); if (ret) { + error_report_err(local_err); return ret; } From 71cf63a471a5814fa2c29c74e268838851b14d01 Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:20 +0530 Subject: [PATCH 1146/1794] migration: push Error **errp into qemu_loadvm_state_header() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that qemu_loadvm_state_header() must report an error in errp, in case of failure. Reviewed-by: Fabiano Rosas Reviewed-by: Marc-André Lureau Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-3-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/savevm.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index 55c99e0902dce..8ac3d338148fc 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2814,38 +2814,43 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type) return 0; } -static int qemu_loadvm_state_header(QEMUFile *f) +static int qemu_loadvm_state_header(QEMUFile *f, Error **errp) { unsigned int v; int ret; - Error *local_err = NULL; v = qemu_get_be32(f); if (v != QEMU_VM_FILE_MAGIC) { - error_report("Not a migration stream"); + error_setg(errp, "Not a migration stream, magic: %x != %x", + v, QEMU_VM_FILE_MAGIC); return -EINVAL; } v = qemu_get_be32(f); if (v == QEMU_VM_FILE_VERSION_COMPAT) { - error_report("SaveVM v2 format is obsolete and don't work anymore"); + error_setg(errp, + "SaveVM v2 format is obsolete and no longer supported"); + return -ENOTSUP; } if (v != QEMU_VM_FILE_VERSION) { - error_report("Unsupported migration stream version"); + error_setg(errp, "Unsupported migration stream version, " + "file version %x != %x", + v, QEMU_VM_FILE_VERSION); return -ENOTSUP; } if (migrate_get_current()->send_configuration) { - if (qemu_get_byte(f) != QEMU_VM_CONFIGURATION) { - error_report("Configuration section missing"); + v = qemu_get_byte(f); + if (v != QEMU_VM_CONFIGURATION) { + error_setg(errp, "Configuration section missing, %x != %x", + v, QEMU_VM_CONFIGURATION); return -EINVAL; } - ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0, - &local_err); + ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0, + errp); if (ret) { - error_report_err(local_err); return ret; } } @@ -3121,8 +3126,9 @@ int qemu_loadvm_state(QEMUFile *f) qemu_loadvm_thread_pool_create(mis); - ret = qemu_loadvm_state_header(f); + ret = qemu_loadvm_state_header(f, &local_err); if (ret) { + error_report_err(local_err); return ret; } From e97da6b583d7cf7d5289cbe9faf280bd453d6391 Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:21 +0530 Subject: [PATCH 1147/1794] migration: push Error **errp into vmstate_load() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that vmstate_load() must report an error in errp, in case of failure. The errors are temporarily reported using error_report_err(). This is removed in the subsequent patches in this series when we are actually able to propagate the error to the calling function. Reviewed-by: Marc-André Lureau Reviewed-by: Fabiano Rosas Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-4-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/savevm.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index 8ac3d338148fc..fffea57cd9628 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -963,14 +963,20 @@ void vmstate_unregister(VMStateIf *obj, const VMStateDescription *vmsd, } } -static int vmstate_load(QEMUFile *f, SaveStateEntry *se) +static int vmstate_load(QEMUFile *f, SaveStateEntry *se, Error **errp) { + int ret; trace_vmstate_load(se->idstr, se->vmsd ? se->vmsd->name : "(old)"); if (!se->vmsd) { /* Old style */ - return se->ops->load_state(f, se->opaque, se->load_version_id); + ret = se->ops->load_state(f, se->opaque, se->load_version_id); + if (ret < 0) { + error_setg(errp, "Failed to load vmstate version_id: %d, ret: %d", + se->load_version_id, ret); + } + return ret; } return vmstate_load_state(f, se->vmsd, se->opaque, se->load_version_id, - &error_fatal); + errp); } static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se, @@ -2692,6 +2698,7 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type) SaveStateEntry *se; char idstr[256]; int ret; + Error *local_err = NULL; /* Read section start */ section_id = qemu_get_be32(f); @@ -2741,10 +2748,11 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type) start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME); } - ret = vmstate_load(f, se); + ret = vmstate_load(f, se, &local_err); if (ret < 0) { error_report("error while loading state for instance 0x%"PRIx32" of" " device '%s'", instance_id, idstr); + error_report_err(local_err); return ret; } @@ -2769,6 +2777,7 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type) uint32_t section_id; SaveStateEntry *se; int ret; + Error *local_err = NULL; section_id = qemu_get_be32(f); @@ -2794,10 +2803,9 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type) start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME); } - ret = vmstate_load(f, se); + ret = vmstate_load(f, se, &local_err); if (ret < 0) { - error_report("error while loading state section id %d(%s)", - section_id, se->idstr); + error_report_err(local_err); return ret; } From 5fb0cfbb3576d5f545fef5746a08719ce8ab50ea Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:22 +0530 Subject: [PATCH 1148/1794] migration: push Error **errp into loadvm_process_command() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that loadvm_process_command() must report an error in errp, in case of failure. The errors are temporarily reported using error_report_err(). This is removed in the subsequent patches in this series when we are actually able to propagate the error to the calling function. Reviewed-by: Marc-André Lureau Reviewed-by: Fabiano Rosas Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-5-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/savevm.c | 86 +++++++++++++++++++++++++++++++++------------- 1 file changed, 63 insertions(+), 23 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index fffea57cd9628..d1ed2e1cde549 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2546,32 +2546,37 @@ static int loadvm_postcopy_handle_switchover_start(void) * LOADVM_QUIT All good, but exit the loop * <0 Error */ -static int loadvm_process_command(QEMUFile *f) +static int loadvm_process_command(QEMUFile *f, Error **errp) { MigrationIncomingState *mis = migration_incoming_get_current(); uint16_t cmd; uint16_t len; uint32_t tmp32; + int ret; cmd = qemu_get_be16(f); len = qemu_get_be16(f); /* Check validity before continue processing of cmds */ - if (qemu_file_get_error(f)) { - return qemu_file_get_error(f); + ret = qemu_file_get_error(f); + if (ret) { + error_setg(errp, + "Failed to load VM process command: stream error: %d", + ret); + return ret; } if (cmd >= MIG_CMD_MAX || cmd == MIG_CMD_INVALID) { - error_report("MIG_CMD 0x%x unknown (len 0x%x)", cmd, len); + error_setg(errp, "MIG_CMD 0x%x unknown (len 0x%x)", cmd, len); return -EINVAL; } trace_loadvm_process_command(mig_cmd_args[cmd].name, len); if (mig_cmd_args[cmd].len != -1 && mig_cmd_args[cmd].len != len) { - error_report("%s received with bad length - expecting %zu, got %d", - mig_cmd_args[cmd].name, - (size_t)mig_cmd_args[cmd].len, len); + error_setg(errp, "%s received with bad length - expecting %zu, got %d", + mig_cmd_args[cmd].name, + (size_t)mig_cmd_args[cmd].len, len); return -ERANGE; } @@ -2584,7 +2589,7 @@ static int loadvm_process_command(QEMUFile *f) } mis->to_src_file = qemu_file_get_return_path(f); if (!mis->to_src_file) { - error_report("CMD_OPEN_RETURN_PATH failed"); + error_setg(errp, "CMD_OPEN_RETURN_PATH failed"); return -1; } @@ -2594,11 +2599,10 @@ static int loadvm_process_command(QEMUFile *f) * been created. */ if (migrate_switchover_ack() && !mis->switchover_ack_pending_num) { - int ret = migrate_send_rp_switchover_ack(mis); + ret = migrate_send_rp_switchover_ack(mis); if (ret) { - error_report( - "Could not send switchover ack RP MSG, err %d (%s)", ret, - strerror(-ret)); + error_setg_errno(errp, -ret, + "Could not send switchover ack RP MSG"); return ret; } } @@ -2608,39 +2612,71 @@ static int loadvm_process_command(QEMUFile *f) tmp32 = qemu_get_be32(f); trace_loadvm_process_command_ping(tmp32); if (!mis->to_src_file) { - error_report("CMD_PING (0x%x) received with no return path", - tmp32); + error_setg(errp, "CMD_PING (0x%x) received with no return path", + tmp32); return -1; } migrate_send_rp_pong(mis, tmp32); break; case MIG_CMD_PACKAGED: - return loadvm_handle_cmd_packaged(mis); + ret = loadvm_handle_cmd_packaged(mis); + if (ret < 0) { + error_setg(errp, "Failed to load device state command: %d", ret); + } + return ret; case MIG_CMD_POSTCOPY_ADVISE: - return loadvm_postcopy_handle_advise(mis, len); + ret = loadvm_postcopy_handle_advise(mis, len); + if (ret < 0) { + error_setg(errp, "Failed to load device state command: %d", ret); + } + return ret; case MIG_CMD_POSTCOPY_LISTEN: - return loadvm_postcopy_handle_listen(mis); + ret = loadvm_postcopy_handle_listen(mis); + if (ret < 0) { + error_setg(errp, "Failed to load device state command: %d", ret); + } + return ret; case MIG_CMD_POSTCOPY_RUN: - return loadvm_postcopy_handle_run(mis); + ret = loadvm_postcopy_handle_run(mis); + if (ret < 0) { + error_setg(errp, "Failed to load device state command: %d", ret); + } + return ret; case MIG_CMD_POSTCOPY_RAM_DISCARD: - return loadvm_postcopy_ram_handle_discard(mis, len); + ret = loadvm_postcopy_ram_handle_discard(mis, len); + if (ret < 0) { + error_setg(errp, "Failed to load device state command: %d", ret); + } + return ret; case MIG_CMD_POSTCOPY_RESUME: return loadvm_postcopy_handle_resume(mis); case MIG_CMD_RECV_BITMAP: - return loadvm_handle_recv_bitmap(mis, len); + ret = loadvm_handle_recv_bitmap(mis, len); + if (ret < 0) { + error_setg(errp, "Failed to load device state command: %d", ret); + } + return ret; case MIG_CMD_ENABLE_COLO: - return loadvm_process_enable_colo(mis); + ret = loadvm_process_enable_colo(mis); + if (ret < 0) { + error_setg(errp, "Failed to load device state command: %d", ret); + } + return ret; case MIG_CMD_SWITCHOVER_START: - return loadvm_postcopy_handle_switchover_start(); + ret = loadvm_postcopy_handle_switchover_start(); + if (ret < 0) { + error_setg(errp, "Failed to load device state command: %d", ret); + } + return ret; } return 0; @@ -3049,6 +3085,7 @@ int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) { uint8_t section_type; int ret = 0; + Error *local_err = NULL; retry: while (true) { @@ -3076,7 +3113,10 @@ int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) } break; case QEMU_VM_COMMAND: - ret = loadvm_process_command(f); + ret = loadvm_process_command(f, &local_err); + if (ret < 0) { + error_report_err(local_err); + } trace_qemu_loadvm_state_section_command(ret); if ((ret < 0) || (ret == LOADVM_QUIT)) { goto out; From 829f0d238dd402d60738464a4cac81574b74f431 Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:23 +0530 Subject: [PATCH 1149/1794] migration: push Error **errp into loadvm_handle_cmd_packaged() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that loadvm_handle_cmd_packaged() must report an error in errp, in case of failure. Reviewed-by: Fabiano Rosas Reviewed-by: Daniel P. Berrangé Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-6-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/savevm.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index d1ed2e1cde549..5e54651652e95 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2405,7 +2405,7 @@ static int loadvm_postcopy_handle_resume(MigrationIncomingState *mis) * Returns: Negative values on error * */ -static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis) +static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis, Error **errp) { int ret; size_t length; @@ -2415,7 +2415,7 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis) trace_loadvm_handle_cmd_packaged(length); if (length > MAX_VM_CMD_PACKAGED_SIZE) { - error_report("Unreasonably large packaged state: %zu", length); + error_setg(errp, "Unreasonably large packaged state: %zu", length); return -1; } @@ -2426,8 +2426,8 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis) length); if (ret != length) { object_unref(OBJECT(bioc)); - error_report("CMD_PACKAGED: Buffer receive fail ret=%d length=%zu", - ret, length); + error_setg(errp, "CMD_PACKAGED: Buffer receive fail ret=%d length=%zu", + ret, length); return (ret < 0) ? ret : -EAGAIN; } bioc->usage += length; @@ -2457,6 +2457,9 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis) } while (1); ret = qemu_loadvm_state_main(packf, mis); + if (ret < 0) { + error_setg(errp, "VM state load failed: %d", ret); + } trace_loadvm_handle_cmd_packaged_main(ret); qemu_fclose(packf); object_unref(OBJECT(bioc)); @@ -2620,11 +2623,7 @@ static int loadvm_process_command(QEMUFile *f, Error **errp) break; case MIG_CMD_PACKAGED: - ret = loadvm_handle_cmd_packaged(mis); - if (ret < 0) { - error_setg(errp, "Failed to load device state command: %d", ret); - } - return ret; + return loadvm_handle_cmd_packaged(mis, errp); case MIG_CMD_POSTCOPY_ADVISE: ret = loadvm_postcopy_handle_advise(mis, len); From 9535435795983caa31070b8b642539b5a658dc2f Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:24 +0530 Subject: [PATCH 1150/1794] migration: push Error **errp into qemu_loadvm_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that qemu_loadvm_state() must report an error in errp, in case of failure. When postcopy live migration runs, the device states are loaded by both the qemu coroutine process_incoming_migration_co() and the postcopy_ram_listen_thread(). Therefore, it is important that the coroutine also reports the error in case of failure, with error_report_err(). Otherwise, the source qemu will not display any errors before going into the postcopy pause state. Suggested-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Reviewed-by: Fabiano Rosas Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-7-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/migration.c | 14 ++++++++++++-- migration/savevm.c | 30 ++++++++++++++++++------------ migration/savevm.h | 2 +- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index e1ac4d73c2bb0..cba2a39355505 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -881,7 +881,7 @@ process_incoming_migration_co(void *opaque) MIGRATION_STATUS_ACTIVE); mis->loadvm_co = qemu_coroutine_self(); - ret = qemu_loadvm_state(mis->from_src_file); + ret = qemu_loadvm_state(mis->from_src_file, &local_err); mis->loadvm_co = NULL; trace_vmstate_downtime_checkpoint("dst-precopy-loadvm-completed"); @@ -908,7 +908,8 @@ process_incoming_migration_co(void *opaque) } if (ret < 0) { - error_setg(&local_err, "load of migration failed: %s", strerror(-ret)); + error_prepend(&local_err, "load of migration failed: %s: ", + strerror(-ret)); goto fail; } @@ -935,6 +936,15 @@ process_incoming_migration_co(void *opaque) } exit(EXIT_FAILURE); + } else { + /* + * Report the error here in case that QEMU abruptly exits + * when postcopy is enabled. + */ + WITH_QEMU_LOCK_GUARD(&s->error_mutex) { + error_report_err(s->error); + s->error = NULL; + } } out: /* Pairs with the refcount taken in qmp_migrate_incoming() */ diff --git a/migration/savevm.c b/migration/savevm.c index 5e54651652e95..88116ed278ae0 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -3159,28 +3159,24 @@ int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) return ret; } -int qemu_loadvm_state(QEMUFile *f) +int qemu_loadvm_state(QEMUFile *f, Error **errp) { MigrationState *s = migrate_get_current(); MigrationIncomingState *mis = migration_incoming_get_current(); - Error *local_err = NULL; int ret; - if (qemu_savevm_state_blocked(&local_err)) { - error_report_err(local_err); + if (qemu_savevm_state_blocked(errp)) { return -EINVAL; } qemu_loadvm_thread_pool_create(mis); - ret = qemu_loadvm_state_header(f, &local_err); + ret = qemu_loadvm_state_header(f, errp); if (ret) { - error_report_err(local_err); return ret; } - if (qemu_loadvm_state_setup(f, &local_err) != 0) { - error_report_err(local_err); + if (qemu_loadvm_state_setup(f, errp) != 0) { return -EINVAL; } @@ -3191,6 +3187,9 @@ int qemu_loadvm_state(QEMUFile *f) cpu_synchronize_all_pre_loadvm(); ret = qemu_loadvm_state_main(f, mis); + if (ret < 0) { + error_setg(errp, "Load VM state failed: %d", ret); + } qemu_event_set(&mis->main_thread_load_event); trace_qemu_loadvm_state_post_main(ret); @@ -3208,8 +3207,15 @@ int qemu_loadvm_state(QEMUFile *f) if (migrate_has_error(migrate_get_current()) || !qemu_loadvm_thread_pool_wait(s, mis)) { ret = -EINVAL; + error_setg(errp, + "Error while loading vmstate"); } else { ret = qemu_file_get_error(f); + if (ret < 0) { + error_setg(errp, + "Error while loading vmstate: stream error: %d", + ret); + } } } /* @@ -3474,6 +3480,7 @@ void qmp_xen_save_devices_state(const char *filename, bool has_live, bool live, void qmp_xen_load_devices_state(const char *filename, Error **errp) { + ERRP_GUARD(); QEMUFile *f; QIOChannelFile *ioc; int ret; @@ -3495,10 +3502,10 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp) f = qemu_file_new_input(QIO_CHANNEL(ioc)); object_unref(OBJECT(ioc)); - ret = qemu_loadvm_state(f); + ret = qemu_loadvm_state(f, errp); qemu_fclose(f); if (ret < 0) { - error_setg(errp, "loading Xen device state failed"); + error_prepend(errp, "loading Xen device state failed: "); } migration_incoming_state_destroy(); } @@ -3569,13 +3576,12 @@ bool load_snapshot(const char *name, const char *vmstate, ret = -EINVAL; goto err_drain; } - ret = qemu_loadvm_state(f); + ret = qemu_loadvm_state(f, errp); migration_incoming_state_destroy(); bdrv_drain_all_end(); if (ret < 0) { - error_setg(errp, "Error %d while loading VM state", ret); return false; } diff --git a/migration/savevm.h b/migration/savevm.h index 2d5e9c716686f..b80770b7461a6 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -64,7 +64,7 @@ void qemu_savevm_send_colo_enable(QEMUFile *f); void qemu_savevm_live_state(QEMUFile *f); int qemu_save_device_state(QEMUFile *f); -int qemu_loadvm_state(QEMUFile *f); +int qemu_loadvm_state(QEMUFile *f, Error **errp); void qemu_loadvm_state_cleanup(MigrationIncomingState *mis); int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis); int qemu_load_device_state(QEMUFile *f); From 84279d5dc1afb115c673007d65bae575da04e115 Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:25 +0530 Subject: [PATCH 1151/1794] migration: push Error **errp into qemu_load_device_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that qemu_load_device_state() must report an error in errp, in case of failure. Reviewed-by: Marc-André Lureau Reviewed-by: Fabiano Rosas Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-8-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/colo.c | 3 +-- migration/savevm.c | 4 ++-- migration/savevm.h | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/migration/colo.c b/migration/colo.c index cf4d71d9edfa2..a426ec5b60b8e 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -729,9 +729,8 @@ static void colo_incoming_process_checkpoint(MigrationIncomingState *mis, bql_lock(); vmstate_loading = true; colo_flush_ram_cache(); - ret = qemu_load_device_state(fb); + ret = qemu_load_device_state(fb, errp); if (ret < 0) { - error_setg(errp, "COLO: load device state failed"); vmstate_loading = false; bql_unlock(); return; diff --git a/migration/savevm.c b/migration/savevm.c index 88116ed278ae0..9e30718995ec9 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -3264,7 +3264,7 @@ int qemu_loadvm_state(QEMUFile *f, Error **errp) return ret; } -int qemu_load_device_state(QEMUFile *f) +int qemu_load_device_state(QEMUFile *f, Error **errp) { MigrationIncomingState *mis = migration_incoming_get_current(); int ret; @@ -3272,7 +3272,7 @@ int qemu_load_device_state(QEMUFile *f) /* Load QEMU_VM_SECTION_FULL section */ ret = qemu_loadvm_state_main(f, mis); if (ret < 0) { - error_report("Failed to load device state: %d", ret); + error_setg(errp, "Failed to load device state: %d", ret); return ret; } diff --git a/migration/savevm.h b/migration/savevm.h index b80770b7461a6..b12681839f0b1 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -67,7 +67,7 @@ int qemu_save_device_state(QEMUFile *f); int qemu_loadvm_state(QEMUFile *f, Error **errp); void qemu_loadvm_state_cleanup(MigrationIncomingState *mis); int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis); -int qemu_load_device_state(QEMUFile *f); +int qemu_load_device_state(QEMUFile *f, Error **errp); int qemu_loadvm_approve_switchover(void); int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, bool in_postcopy); From deecb4e7133c573a85558e29dff61ae428a9cfe8 Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:26 +0530 Subject: [PATCH 1152/1794] migration: push Error **errp into qemu_loadvm_state_main() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that qemu_loadvm_state_main() must report an error in errp, in case of failure. Set errp explicitly if it is NULL in case of failure in the out section. This will be removed in the subsequent patch when all of the calls are converted to passing errp. The error message in the default case of qemu_loadvm_state_main() has the word "savevm". This is removed because it can confuse the user while reading destination side error logs. Reviewed-by: Daniel P. Berrangé Reviewed-by: Fabiano Rosas Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-9-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/colo.c | 3 +-- migration/savevm.c | 36 +++++++++++++++++------------------- migration/savevm.h | 3 ++- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/migration/colo.c b/migration/colo.c index a426ec5b60b8e..ad50a3abc9cfa 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -686,11 +686,10 @@ static void colo_incoming_process_checkpoint(MigrationIncomingState *mis, bql_lock(); cpu_synchronize_all_states(); - ret = qemu_loadvm_state_main(mis->from_src_file, mis); + ret = qemu_loadvm_state_main(mis->from_src_file, mis, errp); bql_unlock(); if (ret < 0) { - error_setg(errp, "Load VM's live state (ram) error"); return; } diff --git a/migration/savevm.c b/migration/savevm.c index 9e30718995ec9..f1338f5cf6d98 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2105,7 +2105,7 @@ static void *postcopy_ram_listen_thread(void *opaque) qemu_file_set_blocking(f, true, &error_fatal); /* TODO: sanity check that only postcopiable data will be loaded here */ - load_res = qemu_loadvm_state_main(f, mis); + load_res = qemu_loadvm_state_main(f, mis, &error_fatal); /* * This is tricky, but, mis->from_src_file can change after it @@ -2456,10 +2456,7 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis, Error **errp) qemu_coroutine_yield(); } while (1); - ret = qemu_loadvm_state_main(packf, mis); - if (ret < 0) { - error_setg(errp, "VM state load failed: %d", ret); - } + ret = qemu_loadvm_state_main(packf, mis, errp); trace_loadvm_handle_cmd_packaged_main(ret); qemu_fclose(packf); object_unref(OBJECT(bioc)); @@ -3080,18 +3077,22 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis) return true; } -int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) +int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis, + Error **errp) { + ERRP_GUARD(); uint8_t section_type; int ret = 0; - Error *local_err = NULL; retry: while (true) { section_type = qemu_get_byte(f); - ret = qemu_file_get_error_obj_any(f, mis->postcopy_qemufile_dst, NULL); + ret = qemu_file_get_error_obj_any(f, mis->postcopy_qemufile_dst, errp); if (ret) { + error_prepend(errp, + "Failed to load section ID: stream error: %d: ", + ret); break; } @@ -3112,10 +3113,7 @@ int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) } break; case QEMU_VM_COMMAND: - ret = loadvm_process_command(f, &local_err); - if (ret < 0) { - error_report_err(local_err); - } + ret = loadvm_process_command(f, errp); trace_qemu_loadvm_state_section_command(ret); if ((ret < 0) || (ret == LOADVM_QUIT)) { goto out; @@ -3125,7 +3123,7 @@ int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) /* This is the end of migration */ goto out; default: - error_report("Unknown savevm section type %d", section_type); + error_setg(errp, "Unknown section type %d", section_type); ret = -EINVAL; goto out; } @@ -3133,6 +3131,9 @@ int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) out: if (ret < 0) { + if (*errp == NULL) { + error_setg(errp, "Loading VM state failed: %d", ret); + } qemu_file_set_error(f, ret); /* Cancel bitmaps incoming regardless of recovery */ @@ -3153,6 +3154,7 @@ int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) migrate_postcopy_ram() && postcopy_pause_incoming(mis)) { /* Reset f to point to the newly created channel */ f = mis->from_src_file; + error_free_or_abort(errp); goto retry; } } @@ -3186,10 +3188,7 @@ int qemu_loadvm_state(QEMUFile *f, Error **errp) cpu_synchronize_all_pre_loadvm(); - ret = qemu_loadvm_state_main(f, mis); - if (ret < 0) { - error_setg(errp, "Load VM state failed: %d", ret); - } + ret = qemu_loadvm_state_main(f, mis, errp); qemu_event_set(&mis->main_thread_load_event); trace_qemu_loadvm_state_post_main(ret); @@ -3270,9 +3269,8 @@ int qemu_load_device_state(QEMUFile *f, Error **errp) int ret; /* Load QEMU_VM_SECTION_FULL section */ - ret = qemu_loadvm_state_main(f, mis); + ret = qemu_loadvm_state_main(f, mis, errp); if (ret < 0) { - error_setg(errp, "Failed to load device state: %d", ret); return ret; } diff --git a/migration/savevm.h b/migration/savevm.h index b12681839f0b1..c337e3e3d111a 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -66,7 +66,8 @@ int qemu_save_device_state(QEMUFile *f); int qemu_loadvm_state(QEMUFile *f, Error **errp); void qemu_loadvm_state_cleanup(MigrationIncomingState *mis); -int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis); +int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis, + Error **errp); int qemu_load_device_state(QEMUFile *f, Error **errp); int qemu_loadvm_approve_switchover(void); int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, From 4493752653141b480d6f4c4df4b25b71a8753b98 Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:27 +0530 Subject: [PATCH 1153/1794] migration: push Error **errp into qemu_loadvm_section_start_full() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that qemu_loadvm_section_start_full() must report an error in errp, in case of failure. Reviewed-by: Marc-André Lureau Reviewed-by: Fabiano Rosas Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-10-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/savevm.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index f1338f5cf6d98..83d8fb8f4144f 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2722,21 +2722,21 @@ static bool check_section_footer(QEMUFile *f, SaveStateEntry *se) } static int -qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type) +qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type, Error **errp) { + ERRP_GUARD(); bool trace_downtime = (type == QEMU_VM_SECTION_FULL); uint32_t instance_id, version_id, section_id; int64_t start_ts, end_ts; SaveStateEntry *se; char idstr[256]; int ret; - Error *local_err = NULL; /* Read section start */ section_id = qemu_get_be32(f); if (!qemu_get_counted_string(f, idstr)) { - error_report("Unable to read ID string for section %u", - section_id); + error_setg(errp, "Unable to read ID string for section %u", + section_id); return -EINVAL; } instance_id = qemu_get_be32(f); @@ -2744,8 +2744,7 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type) ret = qemu_file_get_error(f); if (ret) { - error_report("%s: Failed to read instance/version ID: %d", - __func__, ret); + error_setg(errp, "Failed to read instance/version ID: %d", ret); return ret; } @@ -2754,17 +2753,17 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type) /* Find savevm section */ se = find_se(idstr, instance_id); if (se == NULL) { - error_report("Unknown savevm section or instance '%s' %"PRIu32". " - "Make sure that your current VM setup matches your " - "saved VM setup, including any hotplugged devices", - idstr, instance_id); + error_setg(errp, "Unknown section or instance '%s' %"PRIu32". " + "Make sure that your current VM setup matches your " + "saved VM setup, including any hotplugged devices", + idstr, instance_id); return -EINVAL; } /* Validate version */ if (version_id > se->version_id) { - error_report("savevm: unsupported version %d for '%s' v%d", - version_id, idstr, se->version_id); + error_setg(errp, "unsupported version %d for '%s' v%d", + version_id, idstr, se->version_id); return -EINVAL; } se->load_version_id = version_id; @@ -2772,7 +2771,7 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type) /* Validate if it is a device's state */ if (xen_enabled() && se->is_ram) { - error_report("loadvm: %s RAM loading not allowed on Xen", idstr); + error_setg(errp, "loadvm: %s RAM loading not allowed on Xen", idstr); return -EINVAL; } @@ -2780,11 +2779,11 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type) start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME); } - ret = vmstate_load(f, se, &local_err); + ret = vmstate_load(f, se, errp); if (ret < 0) { - error_report("error while loading state for instance 0x%"PRIx32" of" - " device '%s'", instance_id, idstr); - error_report_err(local_err); + error_prepend(errp, + "error while loading state for instance 0x%"PRIx32" of" + " device '%s': ", instance_id, idstr); return ret; } @@ -2795,6 +2794,8 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type) } if (!check_section_footer(f, se)) { + error_setg(errp, "Section footer error, section_id: %d", + section_id); return -EINVAL; } @@ -3100,7 +3101,7 @@ int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis, switch (section_type) { case QEMU_VM_SECTION_START: case QEMU_VM_SECTION_FULL: - ret = qemu_loadvm_section_start_full(f, section_type); + ret = qemu_loadvm_section_start_full(f, section_type, errp); if (ret < 0) { goto out; } From 8b6dad124ba757ba96a3b4456223e53632371bc8 Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:28 +0530 Subject: [PATCH 1154/1794] migration: push Error **errp into qemu_loadvm_section_part_end() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that qemu_loadvm_section_part_end() must report an error in errp, in case of failure. This patch also removes the setting of errp when errp is NULL in the out section as it is no longer required in the series. Reviewed-by: Marc-André Lureau Reviewed-by: Fabiano Rosas Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-11-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/savevm.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index 83d8fb8f4144f..c1ae36b50a5d6 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2803,21 +2803,19 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type, Error **errp) } static int -qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type) +qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type, Error **errp) { bool trace_downtime = (type == QEMU_VM_SECTION_END); int64_t start_ts, end_ts; uint32_t section_id; SaveStateEntry *se; int ret; - Error *local_err = NULL; section_id = qemu_get_be32(f); ret = qemu_file_get_error(f); if (ret) { - error_report("%s: Failed to read section ID: %d", - __func__, ret); + error_setg(errp, "Failed to read section ID: %d", ret); return ret; } @@ -2828,7 +2826,7 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type) } } if (se == NULL) { - error_report("Unknown savevm section %d", section_id); + error_setg(errp, "Unknown section %d", section_id); return -EINVAL; } @@ -2836,9 +2834,8 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type) start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME); } - ret = vmstate_load(f, se, &local_err); + ret = vmstate_load(f, se, errp); if (ret < 0) { - error_report_err(local_err); return ret; } @@ -2849,6 +2846,8 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type) } if (!check_section_footer(f, se)) { + error_setg(errp, "Section footer error, section_id: %d", + section_id); return -EINVAL; } @@ -3108,7 +3107,7 @@ int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis, break; case QEMU_VM_SECTION_PART: case QEMU_VM_SECTION_END: - ret = qemu_loadvm_section_part_end(f, section_type); + ret = qemu_loadvm_section_part_end(f, section_type, errp); if (ret < 0) { goto out; } @@ -3132,9 +3131,6 @@ int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis, out: if (ret < 0) { - if (*errp == NULL) { - error_setg(errp, "Loading VM state failed: %d", ret); - } qemu_file_set_error(f, ret); /* Cancel bitmaps incoming regardless of recovery */ From 3f9d6e77b0471c844d5050cc71db0863eb4d1b64 Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:29 +0530 Subject: [PATCH 1155/1794] migration: Update qemu_file_get_return_path() docs and remove dead checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation of qemu_file_get_return_path() states that it can return NULL on failure. However, a review of the current implementation reveals that it is guaranteed that it will always succeed and will never return NULL. As a result, the NULL checks post calling the function become redundant. This commit updates the documentation for the function and removes all NULL checks throughout the migration code. Reviewed-by: Daniel P. Berrangé Reviewed-by: Marc-André Lureau Reviewed-by: Fabiano Rosas Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-12-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/colo.c | 4 ---- migration/migration.c | 12 ++---------- migration/qemu-file.c | 1 - migration/savevm.c | 4 ---- 4 files changed, 2 insertions(+), 19 deletions(-) diff --git a/migration/colo.c b/migration/colo.c index ad50a3abc9cfa..db783f6fa7750 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -847,10 +847,6 @@ static void *colo_process_incoming_thread(void *opaque) failover_init_state(); mis->to_src_file = qemu_file_get_return_path(mis->from_src_file); - if (!mis->to_src_file) { - error_report("COLO incoming thread: Open QEMUFile to_src_file failed"); - goto out; - } /* * Note: the communication between Primary side and Secondary side * should be sequential, we set the fd to unblocked in migration incoming diff --git a/migration/migration.c b/migration/migration.c index cba2a39355505..ce17dcc1c0ab4 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2656,12 +2656,9 @@ static void *source_return_path_thread(void *opaque) return NULL; } -static int open_return_path_on_source(MigrationState *ms) +static void 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) { - return -1; - } trace_open_return_path_on_source(); @@ -2670,8 +2667,6 @@ static int open_return_path_on_source(MigrationState *ms) ms->rp_state.rp_thread_created = true; trace_open_return_path_on_source_continue(); - - return 0; } /* Return true if error detected, or false otherwise */ @@ -4022,10 +4017,7 @@ void migration_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)) { - error_setg(&local_err, "Unable to open return-path for postcopy"); - goto fail; - } + open_return_path_on_source(s); } /* diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 0f4280df21f63..0ee0f48a3ecb1 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -125,7 +125,6 @@ static QEMUFile *qemu_file_new_impl(QIOChannel *ioc, bool is_writable) /* * Result: QEMUFile* for a 'return path' for comms in the opposite direction - * NULL if not available */ QEMUFile *qemu_file_get_return_path(QEMUFile *f) { diff --git a/migration/savevm.c b/migration/savevm.c index c1ae36b50a5d6..eb2a905f32eab 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2588,10 +2588,6 @@ static int loadvm_process_command(QEMUFile *f, Error **errp) return 0; } mis->to_src_file = qemu_file_get_return_path(f); - if (!mis->to_src_file) { - error_setg(errp, "CMD_OPEN_RETURN_PATH failed"); - return -1; - } /* * Switchover ack is enabled but no device uses it, so send an ACK to From 3cd7a260e54a350e4cf0b49f69dfd61baebfa8fb Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:30 +0530 Subject: [PATCH 1156/1794] migration: make loadvm_postcopy_handle_resume() void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. Use warn_report() instead of error_report(); it ensures that a resume command received while the migration is not in postcopy recover state is not fatal. It only informs that the command received is unusual, and therefore we should not set errp with the error string. Reviewed-by: Daniel P. Berrangé Reviewed-by: Fabiano Rosas Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-13-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/savevm.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index eb2a905f32eab..d145e7b1e5594 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2334,12 +2334,12 @@ static void migrate_send_rp_req_pages_pending(MigrationIncomingState *mis) } } -static int loadvm_postcopy_handle_resume(MigrationIncomingState *mis) +static void loadvm_postcopy_handle_resume(MigrationIncomingState *mis) { if (mis->state != MIGRATION_STATUS_POSTCOPY_RECOVER) { - error_report("%s: illegal resume received", __func__); + warn_report("%s: illegal resume received", __func__); /* Don't fail the load, only for this. */ - return 0; + return; } /* @@ -2391,8 +2391,6 @@ static int loadvm_postcopy_handle_resume(MigrationIncomingState *mis) /* Kick the fast ram load thread too */ qemu_sem_post(&mis->postcopy_pause_sem_fast_load); } - - return 0; } /** @@ -2647,7 +2645,8 @@ static int loadvm_process_command(QEMUFile *f, Error **errp) return ret; case MIG_CMD_POSTCOPY_RESUME: - return loadvm_postcopy_handle_resume(mis); + loadvm_postcopy_handle_resume(mis); + return 0; case MIG_CMD_RECV_BITMAP: ret = loadvm_handle_recv_bitmap(mis, len); From 44cdbaa98e8ccafcda3326d7307f393f06538558 Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:31 +0530 Subject: [PATCH 1157/1794] migration: push Error **errp into ram_postcopy_incoming_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that ram_postcopy_incoming_init() must report an error in errp, in case of failure. Reviewed-by: Fabiano Rosas Reviewed-by: Marc-André Lureau Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-14-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/postcopy-ram.c | 9 ++++++--- migration/postcopy-ram.h | 2 +- migration/ram.c | 4 ++-- migration/ram.h | 2 +- migration/savevm.c | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 01721723434d5..5471efb4f0135 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -681,6 +681,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis, Error **errp) */ static int init_range(RAMBlock *rb, void *opaque) { + Error **errp = opaque; const char *block_name = qemu_ram_get_idstr(rb); void *host_addr = qemu_ram_get_host_addr(rb); ram_addr_t offset = qemu_ram_get_offset(rb); @@ -701,6 +702,8 @@ static int init_range(RAMBlock *rb, void *opaque) * (Precopy will just overwrite this data, so doesn't need the discard) */ if (ram_discard_range(block_name, 0, length)) { + error_setg(errp, "failed to discard RAM block %s len=%zu", + block_name, length); return -1; } @@ -749,9 +752,9 @@ static int cleanup_range(RAMBlock *rb, void *opaque) * postcopy later; must be called prior to any precopy. * called from arch_init's similarly named ram_postcopy_incoming_init */ -int postcopy_ram_incoming_init(MigrationIncomingState *mis) +int postcopy_ram_incoming_init(MigrationIncomingState *mis, Error **errp) { - if (foreach_not_ignored_block(init_range, NULL)) { + if (foreach_not_ignored_block(init_range, errp)) { return -1; } @@ -1703,7 +1706,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis, Error **errp) return false; } -int postcopy_ram_incoming_init(MigrationIncomingState *mis) +int postcopy_ram_incoming_init(MigrationIncomingState *mis, Error **errp) { error_report("postcopy_ram_incoming_init: No OS support"); return -1; diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h index 3852141d7e37a..ca19433b24689 100644 --- a/migration/postcopy-ram.h +++ b/migration/postcopy-ram.h @@ -30,7 +30,7 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis); * postcopy later; must be called prior to any precopy. * called from ram.c's similarly named ram_postcopy_incoming_init */ -int postcopy_ram_incoming_init(MigrationIncomingState *mis); +int postcopy_ram_incoming_init(MigrationIncomingState *mis, Error **errp); /* * At the end of a migration where postcopy_ram_incoming_init was called. diff --git a/migration/ram.c b/migration/ram.c index 7208bc114fb5c..6a0dcc04f4365 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3716,9 +3716,9 @@ static int ram_load_cleanup(void *opaque) * postcopy-ram. postcopy-ram's similarly names * postcopy_ram_incoming_init does the work. */ -int ram_postcopy_incoming_init(MigrationIncomingState *mis) +int ram_postcopy_incoming_init(MigrationIncomingState *mis, Error **errp) { - return postcopy_ram_incoming_init(mis); + return postcopy_ram_incoming_init(mis, errp); } /** diff --git a/migration/ram.h b/migration/ram.h index 921c39a2c5c45..275709a99187f 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -86,7 +86,7 @@ void ram_postcopy_migrated_memory_release(MigrationState *ms); void ram_postcopy_send_discard_bitmap(MigrationState *ms); /* For incoming postcopy discard */ int ram_discard_range(const char *block_name, uint64_t start, size_t length); -int ram_postcopy_incoming_init(MigrationIncomingState *mis); +int ram_postcopy_incoming_init(MigrationIncomingState *mis, Error **errp); int ram_load_postcopy(QEMUFile *f, int channel); void ram_handle_zero(void *host, uint64_t size); diff --git a/migration/savevm.c b/migration/savevm.c index d145e7b1e5594..338d1a9756a99 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1989,7 +1989,7 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis, return -1; } - if (ram_postcopy_incoming_init(mis)) { + if (ram_postcopy_incoming_init(mis, NULL) < 0) { return -1; } From 34a94fe28da7d2e54e02a44e3bce35f1695ee013 Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:32 +0530 Subject: [PATCH 1158/1794] migration: push Error **errp into loadvm_postcopy_handle_advise() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that loadvm_postcopy_handle_advise() must report an error in errp, in case of failure. Reviewed-by: Fabiano Rosas Reviewed-by: Daniel P. Berrangé Reviewed-by: Marc-André Lureau Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-15-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/savevm.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index 338d1a9756a99..38e22b435b6b4 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1912,39 +1912,39 @@ enum LoadVMExitCodes { * quickly. */ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis, - uint16_t len) + uint16_t len, Error **errp) { PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_ADVISE); uint64_t remote_pagesize_summary, local_pagesize_summary, remote_tps; size_t page_size = qemu_target_page_size(); - Error *local_err = NULL; trace_loadvm_postcopy_handle_advise(); if (ps != POSTCOPY_INCOMING_NONE) { - error_report("CMD_POSTCOPY_ADVISE in wrong postcopy state (%d)", ps); + error_setg(errp, "CMD_POSTCOPY_ADVISE in wrong postcopy state (%d)", + ps); return -1; } switch (len) { case 0: if (migrate_postcopy_ram()) { - error_report("RAM postcopy is enabled but have 0 byte advise"); + error_setg(errp, "RAM postcopy is enabled but have 0 byte advise"); return -EINVAL; } return 0; case 8 + 8: if (!migrate_postcopy_ram()) { - error_report("RAM postcopy is disabled but have 16 byte advise"); + error_setg(errp, + "RAM postcopy is disabled but have 16 byte advise"); return -EINVAL; } break; default: - error_report("CMD_POSTCOPY_ADVISE invalid length (%d)", len); + error_setg(errp, "CMD_POSTCOPY_ADVISE invalid length (%d)", len); return -EINVAL; } - if (!postcopy_ram_supported_by_host(mis, &local_err)) { - error_report_err(local_err); + if (!postcopy_ram_supported_by_host(mis, errp)) { postcopy_state_set(POSTCOPY_INCOMING_NONE); return -1; } @@ -1967,9 +1967,10 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis, * also fails when passed to an older qemu that doesn't * do huge pages. */ - error_report("Postcopy needs matching RAM page sizes (s=%" PRIx64 - " d=%" PRIx64 ")", - remote_pagesize_summary, local_pagesize_summary); + error_setg(errp, + "Postcopy needs matching RAM page sizes " + "(s=%" PRIx64 " d=%" PRIx64 ")", + remote_pagesize_summary, local_pagesize_summary); return -1; } @@ -1979,17 +1980,18 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis, * Again, some differences could be dealt with, but for now keep it * simple. */ - error_report("Postcopy needs matching target page sizes (s=%d d=%zd)", - (int)remote_tps, page_size); + error_setg(errp, + "Postcopy needs matching target page sizes (s=%d d=%zd)", + (int)remote_tps, page_size); return -1; } - if (postcopy_notify(POSTCOPY_NOTIFY_INBOUND_ADVISE, &local_err)) { - error_report_err(local_err); + if (postcopy_notify(POSTCOPY_NOTIFY_INBOUND_ADVISE, errp)) { return -1; } - if (ram_postcopy_incoming_init(mis, NULL) < 0) { + if (ram_postcopy_incoming_init(mis, errp) < 0) { + error_prepend(errp, "Postcopy RAM incoming init failed: "); return -1; } @@ -2617,11 +2619,7 @@ static int loadvm_process_command(QEMUFile *f, Error **errp) return loadvm_handle_cmd_packaged(mis, errp); case MIG_CMD_POSTCOPY_ADVISE: - ret = loadvm_postcopy_handle_advise(mis, len); - if (ret < 0) { - error_setg(errp, "Failed to load device state command: %d", ret); - } - return ret; + return loadvm_postcopy_handle_advise(mis, len, errp); case MIG_CMD_POSTCOPY_LISTEN: ret = loadvm_postcopy_handle_listen(mis); From 208e5227669ec8de02bd253626566fd72eb3090c Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:33 +0530 Subject: [PATCH 1159/1794] migration: push Error **errp into loadvm_postcopy_handle_listen() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that loadvm_postcopy_handle_listen() must report an error in errp, in case of failure. Reviewed-by: Fabiano Rosas Reviewed-by: Daniel P. Berrangé Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-16-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/savevm.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index 38e22b435b6b4..ce88f56498cf8 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2181,15 +2181,16 @@ static void *postcopy_ram_listen_thread(void *opaque) } /* After this message we must be able to immediately receive postcopy data */ -static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) +static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis, + Error **errp) { PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_LISTENING); - Error *local_err = NULL; trace_loadvm_postcopy_handle_listen("enter"); if (ps != POSTCOPY_INCOMING_ADVISE && ps != POSTCOPY_INCOMING_DISCARD) { - error_report("CMD_POSTCOPY_LISTEN in wrong postcopy state (%d)", ps); + error_setg(errp, + "CMD_POSTCOPY_LISTEN in wrong postcopy state (%d)", ps); return -1; } if (ps == POSTCOPY_INCOMING_ADVISE) { @@ -2212,14 +2213,14 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) if (migrate_postcopy_ram()) { if (postcopy_ram_incoming_setup(mis)) { postcopy_ram_incoming_cleanup(mis); + error_setg(errp, "Failed to setup incoming postcopy RAM blocks"); return -1; } } trace_loadvm_postcopy_handle_listen("after uffd"); - if (postcopy_notify(POSTCOPY_NOTIFY_INBOUND_LISTEN, &local_err)) { - error_report_err(local_err); + if (postcopy_notify(POSTCOPY_NOTIFY_INBOUND_LISTEN, errp)) { return -1; } @@ -2622,11 +2623,7 @@ static int loadvm_process_command(QEMUFile *f, Error **errp) return loadvm_postcopy_handle_advise(mis, len, errp); case MIG_CMD_POSTCOPY_LISTEN: - ret = loadvm_postcopy_handle_listen(mis); - if (ret < 0) { - error_setg(errp, "Failed to load device state command: %d", ret); - } - return ret; + return loadvm_postcopy_handle_listen(mis, errp); case MIG_CMD_POSTCOPY_RUN: ret = loadvm_postcopy_handle_run(mis); From ff6ae44e00aec1769629919e30efb3bbd3f991cc Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:34 +0530 Subject: [PATCH 1160/1794] migration: push Error **errp into loadvm_postcopy_handle_run() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that loadvm_postcopy_handle_run() must report an error in errp, in case of failure. Reviewed-by: Fabiano Rosas Reviewed-by: Daniel P. Berrangé Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-17-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/savevm.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index ce88f56498cf8..f7947160fd5af 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2273,13 +2273,13 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) } /* After all discards we can start running and asking for pages */ -static int loadvm_postcopy_handle_run(MigrationIncomingState *mis) +static int loadvm_postcopy_handle_run(MigrationIncomingState *mis, Error **errp) { PostcopyState ps = postcopy_state_get(); trace_loadvm_postcopy_handle_run(); if (ps != POSTCOPY_INCOMING_LISTENING) { - error_report("CMD_POSTCOPY_RUN in wrong postcopy state (%d)", ps); + error_setg(errp, "CMD_POSTCOPY_RUN in wrong postcopy state (%d)", ps); return -1; } @@ -2626,11 +2626,7 @@ static int loadvm_process_command(QEMUFile *f, Error **errp) return loadvm_postcopy_handle_listen(mis, errp); case MIG_CMD_POSTCOPY_RUN: - ret = loadvm_postcopy_handle_run(mis); - if (ret < 0) { - error_setg(errp, "Failed to load device state command: %d", ret); - } - return ret; + return loadvm_postcopy_handle_run(mis, errp); case MIG_CMD_POSTCOPY_RAM_DISCARD: ret = loadvm_postcopy_ram_handle_discard(mis, len); From fd487d7fc78e168f530096f63a3159d7ebde4e5d Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:35 +0530 Subject: [PATCH 1161/1794] migration: push Error **errp into loadvm_postcopy_ram_handle_discard() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that loadvm_postcopy_ram_handle_discard() must report an error in errp, in case of failure. Reviewed-by: Fabiano Rosas Reviewed-by: Marc-André Lureau Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-18-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/savevm.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index f7947160fd5af..b80da04b47ff0 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2004,7 +2004,7 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis, * There can be 0..many of these messages, each encoding multiple pages. */ static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis, - uint16_t len) + uint16_t len, Error **errp) { int tmp; char ramid[256]; @@ -2017,6 +2017,7 @@ static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis, /* 1st discard */ tmp = postcopy_ram_prepare_discard(mis); if (tmp) { + error_setg(errp, "Failed to prepare for RAM discard: %d", tmp); return tmp; } break; @@ -2026,8 +2027,9 @@ static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis, break; default: - error_report("CMD_POSTCOPY_RAM_DISCARD in wrong postcopy state (%d)", - ps); + error_setg(errp, + "CMD_POSTCOPY_RAM_DISCARD in wrong postcopy state (%d)", + ps); return -1; } /* We're expecting a @@ -2036,29 +2038,30 @@ static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis, * then at least 1 16 byte chunk */ if (len < (1 + 1 + 1 + 1 + 2 * 8)) { - error_report("CMD_POSTCOPY_RAM_DISCARD invalid length (%d)", len); + error_setg(errp, "CMD_POSTCOPY_RAM_DISCARD invalid length (%d)", len); return -1; } tmp = qemu_get_byte(mis->from_src_file); if (tmp != postcopy_ram_discard_version) { - error_report("CMD_POSTCOPY_RAM_DISCARD invalid version (%d)", tmp); + error_setg(errp, "CMD_POSTCOPY_RAM_DISCARD invalid version (%d)", tmp); return -1; } if (!qemu_get_counted_string(mis->from_src_file, ramid)) { - error_report("CMD_POSTCOPY_RAM_DISCARD Failed to read RAMBlock ID"); + error_setg(errp, + "CMD_POSTCOPY_RAM_DISCARD Failed to read RAMBlock ID"); return -1; } tmp = qemu_get_byte(mis->from_src_file); if (tmp != 0) { - error_report("CMD_POSTCOPY_RAM_DISCARD missing nil (%d)", tmp); + error_setg(errp, "CMD_POSTCOPY_RAM_DISCARD missing nil (%d)", tmp); return -1; } len -= 3 + strlen(ramid); if (len % 16) { - error_report("CMD_POSTCOPY_RAM_DISCARD invalid length (%d)", len); + error_setg(errp, "CMD_POSTCOPY_RAM_DISCARD invalid length (%d)", len); return -1; } trace_loadvm_postcopy_ram_handle_discard_header(ramid, len); @@ -2070,6 +2073,7 @@ static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis, len -= 16; int ret = ram_discard_range(ramid, start_addr, block_length); if (ret) { + error_setg(errp, "Failed to discard RAM range %s: %d", ramid, ret); return ret; } } @@ -2629,11 +2633,7 @@ static int loadvm_process_command(QEMUFile *f, Error **errp) return loadvm_postcopy_handle_run(mis, errp); case MIG_CMD_POSTCOPY_RAM_DISCARD: - ret = loadvm_postcopy_ram_handle_discard(mis, len); - if (ret < 0) { - error_setg(errp, "Failed to load device state command: %d", ret); - } - return ret; + return loadvm_postcopy_ram_handle_discard(mis, len, errp); case MIG_CMD_POSTCOPY_RESUME: loadvm_postcopy_handle_resume(mis); From 97c2fad858b521afef1d0aad7b6bb880fbe0fb05 Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:36 +0530 Subject: [PATCH 1162/1794] migration: push Error **errp into loadvm_handle_recv_bitmap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that loadvm_handle_recv_bitmap() must report an error in errp, in case of failure. Reviewed-by: Daniel P. Berrangé Reviewed-by: Fabiano Rosas Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-19-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/savevm.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index b80da04b47ff0..2e8776768fcc7 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2476,32 +2476,35 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis, Error **errp) * len (1 byte) + ramblock_name (<255 bytes) */ static int loadvm_handle_recv_bitmap(MigrationIncomingState *mis, - uint16_t len) + uint16_t len, Error **errp) { QEMUFile *file = mis->from_src_file; RAMBlock *rb; char block_name[256]; size_t cnt; + int ret; cnt = qemu_get_counted_string(file, block_name); if (!cnt) { - error_report("%s: failed to read block name", __func__); + error_setg(errp, "failed to read block name"); return -EINVAL; } /* Validate before using the data */ - if (qemu_file_get_error(file)) { - return qemu_file_get_error(file); + ret = qemu_file_get_error(file); + if (ret < 0) { + error_setg(errp, "loadvm failed: stream error: %d", ret); + return ret; } if (len != cnt + 1) { - error_report("%s: invalid payload length (%d)", __func__, len); + error_setg(errp, "invalid payload length (%d)", len); return -EINVAL; } rb = qemu_ram_block_by_name(block_name); if (!rb) { - error_report("%s: block '%s' not found", __func__, block_name); + error_setg(errp, "block '%s' not found", block_name); return -EINVAL; } @@ -2640,11 +2643,7 @@ static int loadvm_process_command(QEMUFile *f, Error **errp) return 0; case MIG_CMD_RECV_BITMAP: - ret = loadvm_handle_recv_bitmap(mis, len); - if (ret < 0) { - error_setg(errp, "Failed to load device state command: %d", ret); - } - return ret; + return loadvm_handle_recv_bitmap(mis, len, errp); case MIG_CMD_ENABLE_COLO: ret = loadvm_process_enable_colo(mis); From d9d7c8d81398bb45d59cbdf1b4c2ed30f56c733d Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:37 +0530 Subject: [PATCH 1163/1794] migration: Return -1 on memory allocation failure in ram.c The function colo_init_ram_cache() currently returns -errno if qemu_anon_ram_alloc() fails. However, the subsequent cleanup loop that calls qemu_anon_ram_free() could potentially alter the value of errno. This would cause the function to return a value that does not accurately represent the original allocation failure. This commit changes the return value to -1 on memory allocation failure. This ensures that the return value is consistent and is not affected by any errno changes that may occur during the free process. Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-20-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/ram.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/migration/ram.c b/migration/ram.c index 6a0dcc04f4365..163265a57f26f 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3575,6 +3575,8 @@ static void colo_init_ram_state(void) * colo cache: this is for secondary VM, we cache the whole * memory of the secondary VM, it is need to hold the global lock * to call this helper. + * + * Returns zero to indicate success or -1 on error. */ int colo_init_ram_cache(void) { @@ -3594,7 +3596,7 @@ int colo_init_ram_cache(void) block->colo_cache = NULL; } } - return -errno; + return -1; } if (!machine_dump_guest_core(current_machine)) { qemu_madvise(block->colo_cache, block->used_length, From d865e4aabd4bec6d9807c3189eac8956b38b6b8d Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:38 +0530 Subject: [PATCH 1164/1794] migration: push Error **errp into loadvm_process_enable_colo() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that loadvm_process_enable_colo() must report an error in errp, in case of failure. Reviewed-by: Fabiano Rosas Reviewed-by: Marc-André Lureau Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-21-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- include/migration/colo.h | 2 +- migration/migration.c | 12 ++++++------ migration/ram.c | 8 ++++---- migration/ram.h | 2 +- migration/savevm.c | 26 ++++++++++++++------------ 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/include/migration/colo.h b/include/migration/colo.h index 43222ef5ae6ad..d4fe422e4d335 100644 --- a/include/migration/colo.h +++ b/include/migration/colo.h @@ -25,7 +25,7 @@ void migrate_start_colo_process(MigrationState *s); bool migration_in_colo_state(void); /* loadvm */ -int migration_incoming_enable_colo(void); +int migration_incoming_enable_colo(Error **errp); void migration_incoming_disable_colo(void); bool migration_incoming_colo_enabled(void); bool migration_incoming_in_colo_state(void); diff --git a/migration/migration.c b/migration/migration.c index ce17dcc1c0ab4..2f55f2784b49c 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -623,22 +623,22 @@ void migration_incoming_disable_colo(void) migration_colo_enabled = false; } -int migration_incoming_enable_colo(void) +int migration_incoming_enable_colo(Error **errp) { #ifndef CONFIG_REPLICATION - error_report("ENABLE_COLO command come in migration stream, but the " - "replication module is not built in"); + error_setg(errp, "ENABLE_COLO command come in migration stream, but the " + "replication module is not built in"); return -ENOTSUP; #endif if (!migrate_colo()) { - error_report("ENABLE_COLO command come in migration stream, but x-colo " - "capability is not set"); + error_setg(errp, "ENABLE_COLO command come in migration stream" + ", but x-colo capability is not set"); return -EINVAL; } if (ram_block_discard_disable(true)) { - error_report("COLO: cannot disable RAM discard"); + error_setg(errp, "COLO: cannot disable RAM discard"); return -EBUSY; } migration_colo_enabled = true; diff --git a/migration/ram.c b/migration/ram.c index 163265a57f26f..a8e8d2cc67903 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3578,7 +3578,7 @@ static void colo_init_ram_state(void) * * Returns zero to indicate success or -1 on error. */ -int colo_init_ram_cache(void) +int colo_init_ram_cache(Error **errp) { RAMBlock *block; @@ -3587,9 +3587,9 @@ int colo_init_ram_cache(void) block->colo_cache = qemu_anon_ram_alloc(block->used_length, NULL, false, false); if (!block->colo_cache) { - error_report("%s: Can't alloc memory for COLO cache of block %s," - "size 0x" RAM_ADDR_FMT, __func__, block->idstr, - block->used_length); + error_setg(errp, "Can't alloc memory for COLO cache of " + "block %s, size 0x" RAM_ADDR_FMT, + block->idstr, block->used_length); RAMBLOCK_FOREACH_NOT_IGNORED(block) { if (block->colo_cache) { qemu_anon_ram_free(block->colo_cache, block->used_length); diff --git a/migration/ram.h b/migration/ram.h index 275709a99187f..24cd0bf585762 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -109,7 +109,7 @@ void ramblock_set_file_bmap_atomic(RAMBlock *block, ram_addr_t offset, bool set); /* ram cache */ -int colo_init_ram_cache(void); +int colo_init_ram_cache(Error **errp); void colo_flush_ram_cache(void); void colo_release_ram_cache(void); void colo_incoming_start_dirty_log(void); diff --git a/migration/savevm.c b/migration/savevm.c index 2e8776768fcc7..8937496d9f667 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2515,15 +2515,21 @@ static int loadvm_handle_recv_bitmap(MigrationIncomingState *mis, return 0; } -static int loadvm_process_enable_colo(MigrationIncomingState *mis) +static int loadvm_process_enable_colo(MigrationIncomingState *mis, + Error **errp) { - int ret = migration_incoming_enable_colo(); + ERRP_GUARD(); + int ret; - if (!ret) { - ret = colo_init_ram_cache(); - if (ret) { - migration_incoming_disable_colo(); - } + ret = migration_incoming_enable_colo(errp); + if (ret < 0) { + return ret; + } + + ret = colo_init_ram_cache(errp); + if (ret) { + error_prepend(errp, "failed to init colo RAM cache: %d: ", ret); + migration_incoming_disable_colo(); } return ret; } @@ -2646,11 +2652,7 @@ static int loadvm_process_command(QEMUFile *f, Error **errp) return loadvm_handle_recv_bitmap(mis, len, errp); case MIG_CMD_ENABLE_COLO: - ret = loadvm_process_enable_colo(mis); - if (ret < 0) { - error_setg(errp, "Failed to load device state command: %d", ret); - } - return ret; + return loadvm_process_enable_colo(mis, errp); case MIG_CMD_SWITCHOVER_START: ret = loadvm_postcopy_handle_switchover_start(); From aa77746602cdf7e29d588d100e27f34bd6e46226 Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:39 +0530 Subject: [PATCH 1165/1794] migration: push Error **errp into loadvm_postcopy_handle_switchover_start() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that loadvm_postcopy_handle_switchover_start() must report an error in errp, in case of failure. Reviewed-by: Fabiano Rosas Reviewed-by: Daniel P. Berrangé Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-22-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/savevm.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index 8937496d9f667..34b7a28d38dcc 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2534,7 +2534,7 @@ static int loadvm_process_enable_colo(MigrationIncomingState *mis, return ret; } -static int loadvm_postcopy_handle_switchover_start(void) +static int loadvm_postcopy_handle_switchover_start(Error **errp) { SaveStateEntry *se; @@ -2547,6 +2547,7 @@ static int loadvm_postcopy_handle_switchover_start(void) ret = se->ops->switchover_start(se->opaque); if (ret < 0) { + error_setg(errp, "Switchover start failed: %d", ret); return ret; } } @@ -2655,11 +2656,7 @@ static int loadvm_process_command(QEMUFile *f, Error **errp) return loadvm_process_enable_colo(mis, errp); case MIG_CMD_SWITCHOVER_START: - ret = loadvm_postcopy_handle_switchover_start(); - if (ret < 0) { - error_setg(errp, "Failed to load device state command: %d", ret); - } - return ret; + return loadvm_postcopy_handle_switchover_start(errp); } return 0; From 94272d9b45bbf17b8bb143b39fd3060aeef7dc3d Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:40 +0530 Subject: [PATCH 1166/1794] migration: Capture error in postcopy_ram_listen_thread() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. postcopy_ram_listen_thread() calls qemu_loadvm_state_main() to load the vm, and in case of a failure, it should set the error in the migration object. Reviewed-by: Marc-André Lureau Reviewed-by: Fabiano Rosas Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-23-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- migration/savevm.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index 34b7a28d38dcc..996673b679801 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2095,6 +2095,7 @@ static void *postcopy_ram_listen_thread(void *opaque) QEMUFile *f = mis->from_src_file; int load_res; MigrationState *migr = migrate_get_current(); + Error *local_err = NULL; object_ref(OBJECT(migr)); @@ -2111,7 +2112,7 @@ static void *postcopy_ram_listen_thread(void *opaque) qemu_file_set_blocking(f, true, &error_fatal); /* TODO: sanity check that only postcopiable data will be loaded here */ - load_res = qemu_loadvm_state_main(f, mis, &error_fatal); + load_res = qemu_loadvm_state_main(f, mis, &local_err); /* * This is tricky, but, mis->from_src_file can change after it @@ -2137,7 +2138,10 @@ static void *postcopy_ram_listen_thread(void *opaque) __func__, load_res); load_res = 0; /* prevent further exit() */ } else { - error_report("%s: loadvm failed: %d", __func__, load_res); + error_prepend(&local_err, + "loadvm failed during postcopy: %d: ", load_res); + migrate_set_error(migr, local_err); + error_report_err(local_err); migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, MIGRATION_STATUS_FAILED); } From 6f9fc6f5012344292f7014e079e5225b8988383d Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:41 +0530 Subject: [PATCH 1167/1794] migration: Remove error variant of vmstate_save_state() function This commit removes the redundant vmstate_save_state_with_err() function. Previously, commit 969298f9d7 introduced vmstate_save_state_with_err() to handle error propagation, while vmstate_save_state() existed for non-error scenarios. This is because there were code paths where vmstate_save_state_v() (called internally by vmstate_save_state) did not explicitly set errors on failure. This change unifies error handling by - updating vmstate_save_state() to accept an Error **errp argument. - vmstate_save_state_v() ensures errors are set directly within the errp object, eliminating the need for two separate functions. All calls to vmstate_save_state_with_err() are replaced with vmstate_save_state(). This simplifies the API and improves code maintainability. vmstate_save_state() that only calls vmstate_save_state_v(), by inference, also has errors set in errp in case of failure. The errors are reported using error_report_err(). If we want the function to exit on error, then &error_fatal is passed. Reviewed-by: Fabiano Rosas Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-24-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- hw/display/virtio-gpu.c | 3 ++- hw/pci/pci.c | 2 +- hw/s390x/virtio-ccw.c | 2 +- hw/scsi/spapr_vscsi.c | 2 +- hw/vfio/pci.c | 4 ++-- hw/virtio/virtio-mmio.c | 2 +- hw/virtio/virtio-pci.c | 2 +- hw/virtio/virtio.c | 6 ++++-- include/migration/vmstate.h | 2 -- migration/cpr.c | 3 +-- migration/savevm.c | 11 ++++++++--- migration/vmstate-types.c | 25 ++++++++++++++++++------- migration/vmstate.c | 10 ++-------- tests/unit/test-vmstate.c | 20 +++++++++++++++++--- ui/vdagent.c | 3 ++- 15 files changed, 61 insertions(+), 36 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index e61585aa611fb..3a555125be60a 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -1248,7 +1248,8 @@ static int virtio_gpu_save(QEMUFile *f, void *opaque, size_t size, } qemu_put_be32(f, 0); /* end of list */ - return vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL); + return vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL, + &error_fatal); } static bool virtio_gpu_load_restore_mapping(VirtIOGPU *g, diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 17715ca1b34c9..5e2c3c6fc2d33 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -926,7 +926,7 @@ void pci_device_save(PCIDevice *s, QEMUFile *f) * This makes us compatible with old devices * which never set or clear this bit. */ s->config[PCI_STATUS] &= ~PCI_STATUS_INTERRUPT; - vmstate_save_state(f, &vmstate_pci_device, s, NULL); + vmstate_save_state(f, &vmstate_pci_device, s, NULL, &error_fatal); /* Restore the interrupt status bit. */ pci_update_irq_status(s); } diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 6a9641a03d5d3..4cb1ced001ae2 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1130,7 +1130,7 @@ static int virtio_ccw_load_queue(DeviceState *d, int n, QEMUFile *f) static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL); + vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL, &error_fatal); } static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index da173f4867639..f0a7dd2b882a1 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -630,7 +630,7 @@ static void vscsi_save_request(QEMUFile *f, SCSIRequest *sreq) vscsi_req *req = sreq->hba_private; assert(req->active); - vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL); + vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL, &error_fatal); trace_spapr_vscsi_save_request(req->qtag, req->cur_desc_num, req->cur_desc_offset); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index a5df4685d4981..06b06afc2b43d 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2821,8 +2821,8 @@ static int vfio_pci_save_config(VFIODevice *vbasedev, QEMUFile *f, Error **errp) { VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); - return vmstate_save_state_with_err(f, &vmstate_vfio_pci_config, vdev, NULL, - errp); + return vmstate_save_state(f, &vmstate_vfio_pci_config, vdev, NULL, + errp); } static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f) diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 0a688909fc606..fb58c36452730 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -613,7 +613,7 @@ static void virtio_mmio_save_extra_state(DeviceState *opaque, QEMUFile *f) { VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); - vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL); + vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL, &error_fatal); } static int virtio_mmio_load_extra_state(DeviceState *opaque, QEMUFile *f) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index b04faa1e5c91b..d2595fbd55151 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -154,7 +154,7 @@ static void virtio_pci_save_extra_state(DeviceState *d, QEMUFile *f) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL); + vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL, &error_fatal); } static int virtio_pci_load_extra_state(DeviceState *d, QEMUFile *f) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 018803c80d131..0a68f1b6f1ddb 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2992,6 +2992,7 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f) VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); uint32_t guest_features_lo = (vdev->guest_features & 0xffffffff); int i; + Error *local_err = NULL; if (k->save_config) { k->save_config(qbus->parent, f); @@ -3035,14 +3036,15 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f) } if (vdc->vmsd) { - int ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL); + int ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL, &local_err); if (ret) { + error_report_err(local_err); return ret; } } /* Subsections */ - return vmstate_save_state(f, &vmstate_virtio, vdev, NULL); + return vmstate_save_state(f, &vmstate_virtio, vdev, NULL, &error_fatal); } /* A wrapper for use as a VMState .put function */ diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 056781b1c21e7..5fe9bbf39058d 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -1198,8 +1198,6 @@ extern const VMStateInfo vmstate_info_qlist; int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id, Error **errp); int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque, JSONWriter *vmdesc); -int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, JSONWriter *vmdesc, Error **errp); int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, JSONWriter *vmdesc, diff --git a/migration/cpr.c b/migration/cpr.c index 6b0e19651a46c..e0b47df22279f 100644 --- a/migration/cpr.c +++ b/migration/cpr.c @@ -183,9 +183,8 @@ int cpr_state_save(MigrationChannel *channel, Error **errp) qemu_put_be32(f, QEMU_CPR_FILE_MAGIC); qemu_put_be32(f, QEMU_CPR_FILE_VERSION); - ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0); + ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0, errp); if (ret) { - error_setg(errp, "vmstate_save_state error %d", ret); qemu_fclose(f); return ret; } diff --git a/migration/savevm.c b/migration/savevm.c index 996673b679801..7b35ec4dd007c 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1056,8 +1056,8 @@ static int vmstate_save(QEMUFile *f, SaveStateEntry *se, JSONWriter *vmdesc, if (!se->vmsd) { vmstate_save_old_style(f, se, vmdesc); } else { - ret = vmstate_save_state_with_err(f, se->vmsd, se->opaque, vmdesc, - errp); + ret = vmstate_save_state(f, se->vmsd, se->opaque, vmdesc, + errp); if (ret) { return ret; } @@ -1285,6 +1285,7 @@ void qemu_savevm_state_header(QEMUFile *f) { MigrationState *s = migrate_get_current(); JSONWriter *vmdesc = s->vmdesc; + Error *local_err = NULL; trace_savevm_state_header(); qemu_put_be32(f, QEMU_VM_FILE_MAGIC); @@ -1303,7 +1304,11 @@ void qemu_savevm_state_header(QEMUFile *f) json_writer_start_object(vmdesc, "configuration"); } - vmstate_save_state(f, &vmstate_configuration, &savevm_state, vmdesc); + vmstate_save_state(f, &vmstate_configuration, &savevm_state, + vmdesc, &local_err); + if (local_err) { + error_report_err(local_err); + } if (vmdesc) { json_writer_end_object(vmdesc); diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c index c5cfd861e3aa5..a1cd7a95fa014 100644 --- a/migration/vmstate-types.c +++ b/migration/vmstate-types.c @@ -565,10 +565,14 @@ static int put_tmp(QEMUFile *f, void *pv, size_t size, const VMStateDescription *vmsd = field->vmsd; void *tmp = g_malloc(size); int ret; + Error *local_err = NULL; /* Writes the parent field which is at the start of the tmp */ *(void **)tmp = pv; - ret = vmstate_save_state(f, vmsd, tmp, vmdesc); + ret = vmstate_save_state(f, vmsd, tmp, vmdesc, &local_err); + if (ret) { + error_report_err(local_err); + } g_free(tmp); return ret; @@ -676,13 +680,15 @@ static int put_qtailq(QEMUFile *f, void *pv, size_t unused_size, size_t entry_offset = field->start; void *elm; int ret; + Error *local_err = NULL; trace_put_qtailq(vmsd->name, vmsd->version_id); QTAILQ_RAW_FOREACH(elm, pv, entry_offset) { qemu_put_byte(f, true); - ret = vmstate_save_state(f, vmsd, elm, vmdesc); + ret = vmstate_save_state(f, vmsd, elm, vmdesc, &local_err); if (ret) { + error_report_err(local_err); return ret; } } @@ -711,6 +717,7 @@ static gboolean put_gtree_elem(gpointer key, gpointer value, gpointer data) struct put_gtree_data *capsule = (struct put_gtree_data *)data; QEMUFile *f = capsule->f; int ret; + Error *local_err = NULL; qemu_put_byte(f, true); @@ -718,16 +725,20 @@ static gboolean put_gtree_elem(gpointer key, gpointer value, gpointer data) if (!capsule->key_vmsd) { qemu_put_be64(f, (uint64_t)(uintptr_t)(key)); /* direct key */ } else { - ret = vmstate_save_state(f, capsule->key_vmsd, key, capsule->vmdesc); + ret = vmstate_save_state(f, capsule->key_vmsd, key, capsule->vmdesc, + &local_err); if (ret) { + error_report_err(local_err); capsule->ret = ret; return true; } } /* put the data */ - ret = vmstate_save_state(f, capsule->val_vmsd, value, capsule->vmdesc); + ret = vmstate_save_state(f, capsule->val_vmsd, value, capsule->vmdesc, + &local_err); if (ret) { + error_report_err(local_err); capsule->ret = ret; return true; } @@ -857,14 +868,14 @@ static int put_qlist(QEMUFile *f, void *pv, size_t unused_size, size_t entry_offset = field->start; void *elm; int ret; + Error *local_err = NULL; trace_put_qlist(field->name, vmsd->name, vmsd->version_id); QLIST_RAW_FOREACH(elm, pv, entry_offset) { qemu_put_byte(f, true); - ret = vmstate_save_state(f, vmsd, elm, vmdesc); + ret = vmstate_save_state(f, vmsd, elm, vmdesc, &local_err); if (ret) { - error_report("%s: failed to save %s (%d)", field->name, - vmsd->name, ret); + error_report_err(local_err); return ret; } } diff --git a/migration/vmstate.c b/migration/vmstate.c index 8d1e9eb62bb9a..ad8e5b71ae2ce 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -406,12 +406,6 @@ bool vmstate_section_needed(const VMStateDescription *vmsd, void *opaque) int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque, JSONWriter *vmdesc_id) -{ - return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id, vmsd->version_id, NULL); -} - -int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, JSONWriter *vmdesc_id, Error **errp) { return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id, vmsd->version_id, errp); @@ -512,7 +506,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, if (inner_field->flags & VMS_STRUCT) { ret = vmstate_save_state(f, inner_field->vmsd, - curr_elem, vmdesc_loop); + curr_elem, vmdesc_loop, errp); } else if (inner_field->flags & VMS_VSTRUCT) { ret = vmstate_save_state_v(f, inner_field->vmsd, curr_elem, vmdesc_loop, @@ -674,7 +668,7 @@ static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, qemu_put_byte(f, len); qemu_put_buffer(f, (uint8_t *)vmsdsub->name, len); qemu_put_be32(f, vmsdsub->version_id); - ret = vmstate_save_state_with_err(f, vmsdsub, opaque, vmdesc, errp); + ret = vmstate_save_state(f, vmsdsub, opaque, vmdesc, errp); if (ret) { return ret; } diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c index 4ff0ab632f7e0..cadbab3c5e260 100644 --- a/tests/unit/test-vmstate.c +++ b/tests/unit/test-vmstate.c @@ -67,9 +67,13 @@ static QEMUFile *open_test_file(bool write) static void save_vmstate(const VMStateDescription *desc, void *obj) { QEMUFile *f = open_test_file(true); + Error *local_err = NULL; /* Save file with vmstate */ - int ret = vmstate_save_state(f, desc, obj, NULL); + int ret = vmstate_save_state(f, desc, obj, NULL, &local_err); + if (ret) { + error_report_err(local_err); + } g_assert(!ret); qemu_put_byte(f, QEMU_VM_EOF); g_assert(!qemu_file_get_error(f)); @@ -438,10 +442,15 @@ static const VMStateDescription vmstate_skipping = { static void test_save_noskip(void) { + Error *local_err = NULL; QEMUFile *fsave = open_test_file(true); TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, .skip_c_e = false }; - int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL); + int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL, + &local_err); + if (ret) { + error_report_err(local_err); + } g_assert(!ret); g_assert(!qemu_file_get_error(fsave)); @@ -460,10 +469,15 @@ static void test_save_noskip(void) static void test_save_skip(void) { + Error *local_err = NULL; QEMUFile *fsave = open_test_file(true); TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, .skip_c_e = true }; - int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL); + int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL, + &local_err); + if (ret) { + error_report_err(local_err); + } g_assert(!ret); g_assert(!qemu_file_get_error(fsave)); diff --git a/ui/vdagent.c b/ui/vdagent.c index bc3c77f01332a..ddb91e75c64a2 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -992,7 +992,8 @@ static int put_cbinfo(QEMUFile *f, void *pv, size_t size, } } - return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc); + return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc, + &error_fatal); } static int get_cbinfo(QEMUFile *f, void *pv, size_t size, From 40de712a89d8fe26af398dd23c24e94834ca442b Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:43 +0530 Subject: [PATCH 1168/1794] migration: Add error-parameterized function variants in VMSD struct - We need to have good error reporting in the callbacks in VMStateDescription struct. Specifically pre_save, pre_load and post_load callbacks. - It is not possible to change these functions everywhere in one patch, therefore, we introduce a duplicate set of callbacks with Error object passed to them. - So, in this commit, we implement 'errp' variants of these callbacks, introducing an explicit Error object parameter. - This is a functional step towards transitioning the entire codebase to the new error-parameterized functions. - Deliberately called in mutual exclusion from their counterparts, to prevent conflicts during the transition. - New impls should preferentally use 'errp' variants of these methods, and existing impls incrementally converted. The variants without 'errp' are intended to be removed once all usage is converted. Reviewed-by: Fabiano Rosas Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-26-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- docs/devel/migration/main.rst | 19 +++++++++++++++++++ include/migration/vmstate.h | 14 ++++++++++++++ migration/vmstate.c | 31 ++++++++++++++++++++++++++++--- 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/docs/devel/migration/main.rst b/docs/devel/migration/main.rst index 6493c1d2bca48..1afe7b9689bdf 100644 --- a/docs/devel/migration/main.rst +++ b/docs/devel/migration/main.rst @@ -444,6 +444,25 @@ The functions to do that are inside a vmstate definition, and are called: This function is called after we save the state of one device (even upon failure, unless the call to pre_save returned an error). +Following are the errp variants of these functions. + +- ``int (*pre_load_errp)(void *opaque, Error **errp);`` + + This function is called before we load the state of one device. + +- ``int (*post_load_errp)(void *opaque, int version_id, Error **errp);`` + + This function is called after we load the state of one device. + +- ``int (*pre_save_errp)(void *opaque, Error **errp);`` + + This function is called before we save the state of one device. + +New impls should preferentally use 'errp' variants of these +methods and existing impls incrementally converted. +The variants without 'errp' are intended to be removed +once all usage is converted. + Example: You can look at hpet.c, that uses the first three functions to massage the state that is transferred. diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 5fe9bbf39058d..5567fd78d0404 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -200,14 +200,28 @@ struct VMStateDescription { * exclusive. For this reason, also early_setup VMSDs are migrated in a * QEMU_VM_SECTION_FULL section, while save_setup() data is migrated in * a QEMU_VM_SECTION_START section. + * + * There are duplicate impls of the post/pre save/load hooks. + * New impls should preferentally use 'errp' variants of these + * methods and existing impls incrementally converted. + * The variants without 'errp' are intended to be removed + * once all usage is converted. + * + * For the errp variants, + * Returns: 0 on success, + * <0 on error where -value is an error number from errno.h */ + bool early_setup; int version_id; int minimum_version_id; MigrationPriority priority; int (*pre_load)(void *opaque); + int (*pre_load_errp)(void *opaque, Error **errp); int (*post_load)(void *opaque, int version_id); + int (*post_load_errp)(void *opaque, int version_id, Error **errp); int (*pre_save)(void *opaque); + int (*pre_save_errp)(void *opaque, Error **errp); int (*post_save)(void *opaque); bool (*needed)(void *opaque); bool (*dev_unplug_pending)(void *opaque); diff --git a/migration/vmstate.c b/migration/vmstate.c index ad8e5b71ae2ce..81eadde553dd2 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -134,6 +134,7 @@ static void vmstate_handle_alloc(void *ptr, const VMStateField *field, int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id, Error **errp) { + ERRP_GUARD(); const VMStateField *field = vmsd->fields; int ret = 0; @@ -152,7 +153,16 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL); return -EINVAL; } - if (vmsd->pre_load) { + if (vmsd->pre_load_errp) { + ret = vmsd->pre_load_errp(opaque, errp); + if (ret < 0) { + error_prepend(errp, "pre load hook failed for: '%s', " + "version_id: %d, minimum version_id: %d, " + "ret: %d: ", vmsd->name, vmsd->version_id, + vmsd->minimum_version_id, ret); + return ret; + } + } else if (vmsd->pre_load) { ret = vmsd->pre_load(opaque); if (ret) { error_setg(errp, "pre load hook failed for: '%s', " @@ -245,7 +255,14 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, qemu_file_set_error(f, ret); return ret; } - if (vmsd->post_load) { + if (vmsd->post_load_errp) { + ret = vmsd->post_load_errp(opaque, version_id, errp); + if (ret < 0) { + error_prepend(errp, "post load hook failed for: %s, version_id: " + "%d, minimum_version: %d, ret: %d: ", vmsd->name, + vmsd->version_id, vmsd->minimum_version_id, ret); + } + } else if (vmsd->post_load) { ret = vmsd->post_load(opaque, version_id); if (ret < 0) { error_setg(errp, @@ -414,12 +431,20 @@ int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, JSONWriter *vmdesc, int version_id, Error **errp) { + ERRP_GUARD(); int ret = 0; const VMStateField *field = vmsd->fields; trace_vmstate_save_state_top(vmsd->name); - if (vmsd->pre_save) { + if (vmsd->pre_save_errp) { + ret = vmsd->pre_save_errp(opaque, errp); + trace_vmstate_save_state_pre_save_res(vmsd->name, ret); + if (ret < 0) { + error_prepend(errp, "pre-save for %s failed, ret: %d: ", + vmsd->name, ret); + } + } else if (vmsd->pre_save) { ret = vmsd->pre_save(opaque); trace_vmstate_save_state_pre_save_res(vmsd->name, ret); if (ret) { From 42e556fa3f7acfd0d0bc834dd0b002eb24d20eea Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Thu, 18 Sep 2025 20:53:44 +0530 Subject: [PATCH 1169/1794] backends/tpm: Propagate vTPM error on migration failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - When migration of a VM with encrypted vTPM fails on the destination host, (e.g., due to a mismatch in secret values), the error message displayed on the source host is generic and unhelpful. - For example, a typical error looks like this: "operation failed: job 'migration out' failed: Sibling indicated error 1. operation failed: job 'migration in' failed: load of migration failed: Input/output error" - Such generic errors are logged using error_report(), which prints to the console/monitor but does not make the detailed error accessible via the QMP query-migrate command. - This change, along with the set of changes of passing errp Error object to the VM state loading functions, help in addressing the issue. We use the post_load_errp hook of VMStateDescription to propagate errors by setting Error **errp objects in case of failure in the TPM backend. - It can then be retrieved using QMP command: {"execute" : "query-migrate"} Buglink: https://issues.redhat.com/browse/RHEL-82826 Reviewed-by: Stefan Berger Reviewed-by: Daniel P. Berrangé Reviewed-by: Fabiano Rosas Signed-off-by: Arun Menon Tested-by: Fabiano Rosas Reviewed-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-27-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu --- backends/tpm/tpm_emulator.c | 40 +++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c index 4a234ab2c0b19..dacfca5ab7eb0 100644 --- a/backends/tpm/tpm_emulator.c +++ b/backends/tpm/tpm_emulator.c @@ -819,7 +819,8 @@ static int tpm_emulator_get_state_blobs(TPMEmulator *tpm_emu) static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu, uint32_t type, TPMSizedBuffer *tsb, - uint32_t flags) + uint32_t flags, + Error **errp) { ssize_t n; ptm_setstate pss; @@ -838,17 +839,18 @@ static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu, /* write the header only */ if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss, offsetof(ptm_setstate, u.req.data), 0, 0) < 0) { - error_report("tpm-emulator: could not set state blob type %d : %s", - type, strerror(errno)); + error_setg_errno(errp, errno, + "tpm-emulator: could not set state blob type %d", + type); return -1; } /* now the body */ n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, tsb->size); if (n != tsb->size) { - error_report("tpm-emulator: Writing the stateblob (type %d) " - "failed; could not write %u bytes, but only %zd", - type, tsb->size, n); + error_setg(errp, "tpm-emulator: Writing the stateblob (type %d) " + "failed; could not write %u bytes, but only %zd", + type, tsb->size, n); return -1; } @@ -856,17 +858,17 @@ static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu, n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, (uint8_t *)&pss, sizeof(pss.u.resp)); if (n != sizeof(pss.u.resp)) { - error_report("tpm-emulator: Reading response from writing stateblob " - "(type %d) failed; expected %zu bytes, got %zd", type, - sizeof(pss.u.resp), n); + error_setg(errp, "tpm-emulator: Reading response from writing " + "stateblob (type %d) failed; expected %zu bytes, " + "got %zd", type, sizeof(pss.u.resp), n); return -1; } tpm_result = be32_to_cpu(pss.u.resp.tpm_result); if (tpm_result != 0) { - error_report("tpm-emulator: Setting the stateblob (type %d) failed " - "with a TPM error 0x%x %s", type, tpm_result, - tpm_emulator_strerror(tpm_result)); + error_setg(errp, "tpm-emulator: Setting the stateblob (type %d) " + "failed with a TPM error 0x%x %s", type, tpm_result, + tpm_emulator_strerror(tpm_result)); return -1; } @@ -880,7 +882,7 @@ static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu, * * Returns a negative errno code in case of error. */ -static int tpm_emulator_set_state_blobs(TPMBackend *tb) +static int tpm_emulator_set_state_blobs(TPMBackend *tb, Error **errp) { TPMEmulator *tpm_emu = TPM_EMULATOR(tb); TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; @@ -894,13 +896,13 @@ static int tpm_emulator_set_state_blobs(TPMBackend *tb) if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, &state_blobs->permanent, - state_blobs->permanent_flags) < 0 || + state_blobs->permanent_flags, errp) < 0 || tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, &state_blobs->volatil, - state_blobs->volatil_flags) < 0 || + state_blobs->volatil_flags, errp) < 0 || tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, &state_blobs->savestate, - state_blobs->savestate_flags) < 0) { + state_blobs->savestate_flags, errp) < 0) { return -EIO; } @@ -948,12 +950,12 @@ static void tpm_emulator_vm_state_change(void *opaque, bool running, * * Returns negative errno codes in case of error. */ -static int tpm_emulator_post_load(void *opaque, int version_id) +static int tpm_emulator_post_load(void *opaque, int version_id, Error **errp) { TPMBackend *tb = opaque; int ret; - ret = tpm_emulator_set_state_blobs(tb); + ret = tpm_emulator_set_state_blobs(tb, errp); if (ret < 0) { return ret; } @@ -969,7 +971,7 @@ static const VMStateDescription vmstate_tpm_emulator = { .name = "tpm-emulator", .version_id = 0, .pre_save = tpm_emulator_pre_save, - .post_load = tpm_emulator_post_load, + .post_load_errp = tpm_emulator_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator), VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator), From 7e0c22d585581b8083ffdeb332ea497218665daf Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 18 Sep 2025 16:39:36 -0400 Subject: [PATCH 1170/1794] io/crypto: Move tls premature termination handling into QIO layer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QCryptoTLSSession allows TLS premature termination in two cases, one of the case is when the channel shutdown() is invoked on READ side. It's possible the shutdown() happened after the read thread blocked at gnutls_record_recv(). In this case, we should allow the premature termination to happen. The problem is by the time qcrypto_tls_session_read() was invoked, tioc->shutdown may not have been set, so this may instead be treated as an error if there is concurrent shutdown() calls. To allow the flag to reflect the latest status of tioc->shutdown, move the check upper into the QIOChannel level, so as to read the flag only after QEMU gets an GNUTLS_E_PREMATURE_TERMINATION. When at it, introduce qio_channel_tls_allow_premature_termination() helper to make the condition checks easier to read. When doing so, change the qatomic_load_acquire() to qatomic_read(): here we don't need any ordering of memory accesses, but reading a flag. qatomic_read() would suffice because it guarantees fetching from memory. Nothing else we should need to order on memory access. This patch will fix a qemu qtest warning when running the preempt tls test, reporting premature termination: QTEST_QEMU_BINARY=./qemu-system-x86_64 ./tests/qtest/migration-test --full -r /x86_64/migration/postcopy/preempt/tls/psk ... qemu-kvm: Cannot read from TLS channel: The TLS connection was non-properly terminated. ... In this specific case, the error was set by postcopy_preempt_thread, which normally will be concurrently shutdown()ed by the main thread. Reviewed-by: Daniel P. Berrangé Reviewed-by: Juraj Marcin Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250918203937.200833-2-peterx@redhat.com Signed-off-by: Peter Xu --- crypto/tlssession.c | 7 ++----- include/crypto/tlssession.h | 10 +++------- io/channel-tls.c | 21 +++++++++++++++++++-- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/crypto/tlssession.c b/crypto/tlssession.c index 86d407a14296e..ac38c2121dbd3 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -552,7 +552,6 @@ ssize_t qcrypto_tls_session_read(QCryptoTLSSession *session, char *buf, size_t len, - bool gracefulTermination, Error **errp) { ssize_t ret; @@ -570,9 +569,8 @@ qcrypto_tls_session_read(QCryptoTLSSession *session, if (ret < 0) { if (ret == GNUTLS_E_AGAIN) { return QCRYPTO_TLS_SESSION_ERR_BLOCK; - } else if ((ret == GNUTLS_E_PREMATURE_TERMINATION) && - gracefulTermination){ - return 0; + } else if (ret == GNUTLS_E_PREMATURE_TERMINATION) { + return QCRYPTO_TLS_SESSION_PREMATURE_TERMINATION; } else { if (session->rerr) { error_propagate(errp, session->rerr); @@ -789,7 +787,6 @@ ssize_t qcrypto_tls_session_read(QCryptoTLSSession *sess, char *buf, size_t len, - bool gracefulTermination, Error **errp) { error_setg(errp, "TLS requires GNUTLS support"); diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h index 2f62ce2d67a3f..2e9fe11cf6e20 100644 --- a/include/crypto/tlssession.h +++ b/include/crypto/tlssession.h @@ -110,6 +110,7 @@ typedef struct QCryptoTLSSession QCryptoTLSSession; #define QCRYPTO_TLS_SESSION_ERR_BLOCK -2 +#define QCRYPTO_TLS_SESSION_PREMATURE_TERMINATION -3 /** * qcrypto_tls_session_new: @@ -259,7 +260,6 @@ ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess, * @sess: the TLS session object * @buf: to fill with plain text received * @len: the length of @buf - * @gracefulTermination: treat premature termination as graceful EOF * @errp: pointer to hold returned error object * * Receive up to @len bytes of data from the remote peer @@ -267,22 +267,18 @@ ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess, * qcrypto_tls_session_set_callbacks(), decrypt it and * store it in @buf. * - * If @gracefulTermination is true, then a premature termination - * of the TLS session will be treated as indicating EOF, as - * opposed to an error. - * * It is an error to call this before * qcrypto_tls_session_handshake() returns * QCRYPTO_TLS_HANDSHAKE_COMPLETE * * Returns: the number of bytes received, * or QCRYPTO_TLS_SESSION_ERR_BLOCK if the receive would block, - * or -1 on error. + * or QCRYPTO_TLS_SESSION_PREMATURE_TERMINATION if a premature termination + * is detected, or -1 on error. */ ssize_t qcrypto_tls_session_read(QCryptoTLSSession *sess, char *buf, size_t len, - bool gracefulTermination, Error **errp); /** diff --git a/io/channel-tls.c b/io/channel-tls.c index 7135896f791f0..1fbed4be0c670 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -346,6 +346,19 @@ static void qio_channel_tls_finalize(Object *obj) qcrypto_tls_session_free(ioc->session); } +static bool +qio_channel_tls_allow_premature_termination(QIOChannelTLS *tioc, int flags) +{ + if (flags & QIO_CHANNEL_READ_FLAG_RELAXED_EOF) { + return true; + } + + if (qatomic_read(&tioc->shutdown) & QIO_CHANNEL_SHUTDOWN_READ) { + return true; + } + + return false; +} static ssize_t qio_channel_tls_readv(QIOChannel *ioc, const struct iovec *iov, @@ -364,8 +377,6 @@ static ssize_t qio_channel_tls_readv(QIOChannel *ioc, tioc->session, iov[i].iov_base, iov[i].iov_len, - flags & QIO_CHANNEL_READ_FLAG_RELAXED_EOF || - qatomic_load_acquire(&tioc->shutdown) & QIO_CHANNEL_SHUTDOWN_READ, errp); if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { if (got) { @@ -373,6 +384,12 @@ static ssize_t qio_channel_tls_readv(QIOChannel *ioc, } else { return QIO_CHANNEL_ERR_BLOCK; } + } else if (ret == QCRYPTO_TLS_SESSION_PREMATURE_TERMINATION) { + if (qio_channel_tls_allow_premature_termination(tioc, flags)) { + ret = 0; + } else { + return -1; + } } else if (ret < 0) { return -1; } From dc487044d5c09d32dd19c8e85e76396fbbc9dde1 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 18 Sep 2025 16:39:37 -0400 Subject: [PATCH 1171/1794] migration: Make migration_has_failed() work even for CANCELLING No issue I hit, the change is only from code observation when I am looking at a TLS premature termination issue. We set CANCELLED very late, it means migration_has_failed() may not work correctly if it's invoked before updating CANCELLING to CANCELLED. Allow that state will make migration_has_failed() working as expected even if it's invoked slightly earlier. One current user is the multifd code for the TLS graceful termination, where it's before updating to CANCELLED. Reviewed-by: Juraj Marcin Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250918203937.200833-3-peterx@redhat.com Signed-off-by: Peter Xu --- migration/migration.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index 2f55f2784b49c..3ff85098d5f83 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1712,7 +1712,8 @@ int migration_call_notifiers(MigrationState *s, MigrationEventType type, bool migration_has_failed(MigrationState *s) { - return (s->state == MIGRATION_STATUS_CANCELLED || + return (s->state == MIGRATION_STATUS_CANCELLING || + s->state == MIGRATION_STATUS_CANCELLED || s->state == MIGRATION_STATUS_FAILED); } From 2aae7171224b6a305f2bf228754b59f0f99d255a Mon Sep 17 00:00:00 2001 From: Bin Guo Date: Mon, 29 Sep 2025 10:12:13 +0800 Subject: [PATCH 1172/1794] migration: HMP: Adjust the order of output fields Adjust the positions of 'tls-authz' and 'max-postcopy-bandwidth' in the fields output by the 'info migrate_parameters' command so that related fields are next to each other. For clarity only, no functional changes. Sample output after this commit: (qemu) info migrate_parameters ... max-cpu-throttle: 99 tls-creds: '' tls-hostname: '' tls-authz: '' max-bandwidth: 134217728 bytes/second avail-switchover-bandwidth: 0 bytes/second max-postcopy-bandwidth: 0 bytes/second downtime-limit: 300 ms ... Cc: Dr. David Alan Gilbert Signed-off-by: Bin Guo Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20250929021213.28369-1-guobin@linux.alibaba.com [peterx: move postcopy-bw before avail-switchover-bw] Signed-off-by: Peter Xu --- migration/migration-hmp-cmds.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 0fc21f06473df..814221b260f2f 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -353,6 +353,10 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: '%s'\n", MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME), params->tls_hostname); + assert(params->tls_authz); + monitor_printf(mon, "%s: '%s'\n", + MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ), + params->tls_authz); assert(params->has_max_bandwidth); monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n", MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH), @@ -361,6 +365,10 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n", MigrationParameter_str(MIGRATION_PARAMETER_AVAIL_SWITCHOVER_BANDWIDTH), params->avail_switchover_bandwidth); + assert(params->has_max_postcopy_bandwidth); + monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n", + MigrationParameter_str(MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH), + params->max_postcopy_bandwidth); assert(params->has_downtime_limit); monitor_printf(mon, "%s: %" PRIu64 " ms\n", MigrationParameter_str(MIGRATION_PARAMETER_DOWNTIME_LIMIT), @@ -383,12 +391,6 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %" PRIu64 " bytes\n", MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE), params->xbzrle_cache_size); - monitor_printf(mon, "%s: %" PRIu64 "\n", - MigrationParameter_str(MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH), - params->max_postcopy_bandwidth); - monitor_printf(mon, "%s: '%s'\n", - MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ), - params->tls_authz); if (params->has_block_bitmap_mapping) { const BitmapMigrationNodeAliasList *bmnal; From 82f038d5961a8c8f896b499e31c638266e0291e9 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 25 Sep 2025 16:16:01 -0400 Subject: [PATCH 1173/1794] migration/multifd/tls: Cleanup BYE message processing on sender side This patch is a trivial cleanup to the BYE messages on the multifd sender side. It could also be a fix, but since we do not have a solid clue, taking this as a cleanup only. One trivial concern is, migration_tls_channel_end() might be unsafe to be invoked in the migration thread if migration is not successful, because when failed / cancelled we do not know whether the multifd sender threads can be writting to the channels, while GnuTLS library (when it's a TLS channel) logically doesn't support concurrent writes. When at it, cleanup on a few things. What changed: - Introduce a helper to do graceful shutdowns with rich comment, hiding the details - Only send bye() iff migration succeeded, skip if it failed / cancelled - Detect TLS channel using channel type rather than thread created flags - Move the loop into the existing one that will close the channels, but do graceful shutdowns before channel shutdowns - local_err seems to have been leaked if set, fix it along the way Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250925201601.290546-1-peterx@redhat.com Signed-off-by: Peter Xu --- migration/multifd.c | 65 ++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/migration/multifd.c b/migration/multifd.c index b2557788555a7..98873cee74f3c 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -439,6 +439,39 @@ static void multifd_send_set_error(Error *err) } } +/* + * Gracefully shutdown IOChannels. Only needed for successful migrations on + * top of TLS channels. Otherwise it is same to qio_channel_shutdown(). + * + * A successful migration also guarantees multifd sender threads are + * properly flushed and halted. It is only safe to send BYE in the + * migration thread here when we know there's no other thread writting to + * the channel, because GnuTLS doesn't support concurrent writers. + */ +static void migration_ioc_shutdown_gracefully(QIOChannel *ioc) +{ + g_autoptr(Error) local_err = NULL; + + if (!migration_has_failed(migrate_get_current()) && + object_dynamic_cast((Object *)ioc, TYPE_QIO_CHANNEL_TLS)) { + + /* + * The destination expects the TLS session to always be properly + * terminated. This helps to detect a premature termination in the + * middle of the stream. Note that older QEMUs always break the + * connection on the source and the destination always sees + * GNUTLS_E_PREMATURE_TERMINATION. + */ + migration_tls_channel_end(ioc, &local_err); + if (local_err) { + warn_report("Failed to gracefully terminate TLS connection: %s", + error_get_pretty(local_err)); + } + } + + qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); +} + static void multifd_send_terminate_threads(void) { int i; @@ -460,7 +493,7 @@ static void multifd_send_terminate_threads(void) qemu_sem_post(&p->sem); if (p->c) { - qio_channel_shutdown(p->c, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); + migration_ioc_shutdown_gracefully(p->c); } } @@ -547,36 +580,6 @@ void multifd_send_shutdown(void) return; } - for (i = 0; i < migrate_multifd_channels(); i++) { - MultiFDSendParams *p = &multifd_send_state->params[i]; - - /* thread_created implies the TLS handshake has succeeded */ - if (p->tls_thread_created && p->thread_created) { - Error *local_err = NULL; - /* - * The destination expects the TLS session to always be - * properly terminated. This helps to detect a premature - * termination in the middle of the stream. Note that - * older QEMUs always break the connection on the source - * and the destination always sees - * GNUTLS_E_PREMATURE_TERMINATION. - */ - migration_tls_channel_end(p->c, &local_err); - - /* - * The above can return an error in case the migration has - * already failed. If the migration succeeded, errors are - * not expected but there's no need to kill the source. - */ - if (local_err && !migration_has_failed(migrate_get_current())) { - warn_report( - "multifd_send_%d: Failed to terminate TLS connection: %s", - p->id, error_get_pretty(local_err)); - break; - } - } - } - multifd_send_terminate_threads(); for (i = 0; i < migrate_multifd_channels(); i++) { From 725a9e5f7885a3c0d0cd82022d6eb5a758ac9d47 Mon Sep 17 00:00:00 2001 From: Juraj Marcin Date: Tue, 26 Aug 2025 13:51:40 +0200 Subject: [PATCH 1174/1794] migration: Fix state transition in postcopy_start() error handling Commit 48814111366b ("migration: Always set DEVICE state") introduced DEVICE state to postcopy, which moved the actual state transition that leads to POSTCOPY_ACTIVE. However, the error handling part of the postcopy_start() function still expects the state POSTCOPY_ACTIVE, but depending on where an error happens, now the state can be either ACTIVE, DEVICE or CANCELLING, but never POSTCOPY_ACTIVE, as this transition now happens just before a successful return from the function. Instead, accept any state except CANCELLING when transitioning to FAILED state. Cc: qemu-stable@nongnu.org Fixes: 48814111366b ("migration: Always set DEVICE state") Signed-off-by: Juraj Marcin Reviewed-by: Peter Xu Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250826115145.871272-1-jmarcin@redhat.com Signed-off-by: Peter Xu --- migration/migration.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 3ff85098d5f83..edb8ff0d4619e 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2878,8 +2878,9 @@ static int postcopy_start(MigrationState *ms, Error **errp) fail_closefb: qemu_fclose(fb); fail: - migrate_set_state(&ms->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, - MIGRATION_STATUS_FAILED); + if (ms->state != MIGRATION_STATUS_CANCELLING) { + migrate_set_state(&ms->state, ms->state, MIGRATION_STATUS_FAILED); + } migration_block_activate(NULL); migration_call_notifiers(ms, MIG_EVENT_PRECOPY_FAILED, NULL); bql_unlock(); From d943cef76090b5255e68ba38ce6ddf20537b07bc Mon Sep 17 00:00:00 2001 From: Yanfei Xu Date: Mon, 18 Aug 2025 21:11:27 +0800 Subject: [PATCH 1175/1794] migration: ensure APIC is loaded prior to VFIO PCI devices The load procedure of VFIO PCI devices involves setting up IRT for each VFIO PCI devices. This requires determining whether an interrupt is single-destination interrupt to decide between Posted Interrupt(PI) or remapping mode for the IRTE. However, determining this may require accessing the VM's APIC registers. For example: ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irqs) ... kvm_arch_irq_bypass_add_producer kvm_x86_call(pi_update_irte) vmx_pi_update_irte kvm_intr_is_single_vcpu If the LAPIC has not been loaded yet, interrupts will use remapping mode. To prevent the fallback of interrupt mode, keep APIC is always loaded prior to VFIO PCI devices. Signed-off-by: Yicong Shen Signed-off-by: Yanfei Xu Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20250818131127.1021648-1-yanfei.xu@bytedance.com Signed-off-by: Peter Xu --- hw/intc/apic_common.c | 1 + include/migration/vmstate.h | 1 + 2 files changed, 2 insertions(+) diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 37a7a7019d33c..394fe020134f4 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -379,6 +379,7 @@ static const VMStateDescription vmstate_apic_common = { .pre_load = apic_pre_load, .pre_save = apic_dispatch_pre_save, .post_load = apic_dispatch_post_load, + .priority = MIG_PRI_APIC, .fields = (const VMStateField[]) { VMSTATE_UINT32(apicbase, APICCommonState), VMSTATE_UINT8(id, APICCommonState), diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 5567fd78d0404..6f5a9fed68bde 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -163,6 +163,7 @@ typedef enum { MIG_PRI_IOMMU, /* Must happen before PCI devices */ MIG_PRI_PCI_BUS, /* Must happen before IOMMU */ MIG_PRI_VIRTIO_MEM, /* Must happen before IOMMU */ + MIG_PRI_APIC, /* Must happen before PCI devices */ MIG_PRI_GICV3_ITS, /* Must happen before PCI devices */ MIG_PRI_GICV3, /* Must happen before the ITS */ MIG_PRI_MAX, From 9e7bfda4909cc688dd0327e17985019f08a78d5d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 29 Sep 2025 15:42:26 +0100 Subject: [PATCH 1176/1794] include/system/memory.h: Clarify address_space_destroy() behaviour address_space_destroy() doesn't actually immediately destroy the AS; it queues it to be destroyed via RCU. This means you can't g_free() the memory the AS struct is in until that has happened. Clarify this in the documentation. Signed-off-by: Peter Maydell Reviewed-by: David Hildenbrand Link: https://lore.kernel.org/r/20250929144228.1994037-2-peter.maydell@linaro.org Signed-off-by: Peter Xu --- include/system/memory.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/include/system/memory.h b/include/system/memory.h index aa85fc27a1023..827e2c5aa44ae 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -2727,9 +2727,14 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name); /** * address_space_destroy: destroy an address space * - * Releases all resources associated with an address space. After an address space - * is destroyed, its root memory region (given by address_space_init()) may be destroyed - * as well. + * Releases all resources associated with an address space. After an + * address space is destroyed, the reference the AddressSpace had to + * its root memory region is dropped, which may result in the + * destruction of that memory region as well. + * + * Note that destruction of the AddressSpace is done via RCU; + * it is therefore not valid to free the memory the AddressSpace + * struct is in until after that RCU callback has completed. * * @as: address space to be destroyed */ From 041600e23f2fe2a9c252c9a8b26c7d147bedf982 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 29 Sep 2025 15:42:27 +0100 Subject: [PATCH 1177/1794] memory: New AS helper to serialize destroy+free If an AddressSpace has been created in its own allocated memory, cleaning it up requires first destroying the AS and then freeing the memory. Doing this doesn't work: address_space_destroy(as); g_free_rcu(as, rcu); because both address_space_destroy() and g_free_rcu() try to use the same 'rcu' node in the AddressSpace struct and the address_space_destroy hook gets overwritten. Provide a new address_space_destroy_free() function which will destroy the AS and then free the memory it uses, all in one RCU callback. (CC to stable because the next commit needs this function.) Cc: qemu-stable@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Reviewed-by: David Hildenbrand Link: https://lore.kernel.org/r/20250929144228.1994037-3-peter.maydell@linaro.org Signed-off-by: Peter Xu --- include/system/memory.h | 13 +++++++++++++ system/memory.c | 20 +++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/include/system/memory.h b/include/system/memory.h index 827e2c5aa44ae..08daf0fc59e1d 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -2735,11 +2735,24 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name); * Note that destruction of the AddressSpace is done via RCU; * it is therefore not valid to free the memory the AddressSpace * struct is in until after that RCU callback has completed. + * If you want to g_free() the AddressSpace after destruction you + * can do that with address_space_destroy_free(). * * @as: address space to be destroyed */ void address_space_destroy(AddressSpace *as); +/** + * address_space_destroy_free: destroy an address space and free it + * + * This does the same thing as address_space_destroy(), and then also + * frees (via g_free()) the AddressSpace itself once the destruction + * is complete. + * + * @as: address space to be destroyed + */ +void address_space_destroy_free(AddressSpace *as); + /** * address_space_remove_listeners: unregister all listeners of an address space * diff --git a/system/memory.c b/system/memory.c index cf8cad6961156..fe8b28a096b65 100644 --- a/system/memory.c +++ b/system/memory.c @@ -3278,7 +3278,14 @@ static void do_address_space_destroy(AddressSpace *as) memory_region_unref(as->root); } -void address_space_destroy(AddressSpace *as) +static void do_address_space_destroy_free(AddressSpace *as) +{ + do_address_space_destroy(as); + g_free(as); +} + +/* Detach address space from global view, notify all listeners */ +static void address_space_detach(AddressSpace *as) { MemoryRegion *root = as->root; @@ -3293,9 +3300,20 @@ void address_space_destroy(AddressSpace *as) * values to expire before freeing the data. */ as->root = root; +} + +void address_space_destroy(AddressSpace *as) +{ + address_space_detach(as); call_rcu(as, do_address_space_destroy, rcu); } +void address_space_destroy_free(AddressSpace *as) +{ + address_space_detach(as); + call_rcu(as, do_address_space_destroy_free, rcu); +} + static const char *memory_region_type(MemoryRegion *mr) { if (mr->alias) { From 300a87c502c4ba7ffc7720d8f3583e3d1a68348a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 29 Sep 2025 15:42:28 +0100 Subject: [PATCH 1178/1794] physmem: Destroy all CPU AddressSpaces on unrealize When we unrealize a CPU object (which happens on vCPU hot-unplug), we should destroy all the AddressSpace objects we created via calls to cpu_address_space_init() when the CPU was realized. Commit 24bec42f3d6eae added a function to do this for a specific AddressSpace, but did not add any places where the function was called. Since we always want to destroy all the AddressSpaces on unrealize, regardless of the target architecture, we don't need to try to keep track of how many are still undestroyed, or make the target architecture code manually call a destroy function for each AS it created. Instead we can adjust the function to always completely destroy the whole cpu->ases array, and arrange for it to be called during CPU unrealize as part of the common code. Without this fix, AddressSanitizer will report a leak like this from a run where we hot-plugged and then hot-unplugged an x86 KVM vCPU: Direct leak of 416 byte(s) in 1 object(s) allocated from: #0 0x5b638565053d in calloc (/data_nvme1n1/linaro/qemu-from-laptop/qemu/build/x86-tgts-asan/qemu-system-x86_64+0x1ee153d) (BuildId: c1cd6022b195142106e1bffeca23498c2b752bca) #1 0x7c28083f77b1 in g_malloc0 (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x637b1) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x5b6386999c7c in cpu_address_space_init /data_nvme1n1/linaro/qemu-from-laptop/qemu/build/x86-tgts-asan/../../system/physmem.c:797:25 #3 0x5b638727f049 in kvm_cpu_realizefn /data_nvme1n1/linaro/qemu-from-laptop/qemu/build/x86-tgts-asan/../../target/i386/kvm/kvm-cpu.c:102:5 #4 0x5b6385745f40 in accel_cpu_common_realize /data_nvme1n1/linaro/qemu-from-laptop/qemu/build/x86-tgts-asan/../../accel/accel-common.c:101:13 #5 0x5b638568fe3c in cpu_exec_realizefn /data_nvme1n1/linaro/qemu-from-laptop/qemu/build/x86-tgts-asan/../../hw/core/cpu-common.c:232:10 #6 0x5b63874a2cd5 in x86_cpu_realizefn /data_nvme1n1/linaro/qemu-from-laptop/qemu/build/x86-tgts-asan/../../target/i386/cpu.c:9321:5 #7 0x5b6387a0469a in device_set_realized /data_nvme1n1/linaro/qemu-from-laptop/qemu/build/x86-tgts-asan/../../hw/core/qdev.c:494:13 #8 0x5b6387a27d9e in property_set_bool /data_nvme1n1/linaro/qemu-from-laptop/qemu/build/x86-tgts-asan/../../qom/object.c:2375:5 #9 0x5b6387a2090b in object_property_set /data_nvme1n1/linaro/qemu-from-laptop/qemu/build/x86-tgts-asan/../../qom/object.c:1450:5 #10 0x5b6387a35b05 in object_property_set_qobject /data_nvme1n1/linaro/qemu-from-laptop/qemu/build/x86-tgts-asan/../../qom/qom-qobject.c:28:10 #11 0x5b6387a21739 in object_property_set_bool /data_nvme1n1/linaro/qemu-from-laptop/qemu/build/x86-tgts-asan/../../qom/object.c:1520:15 #12 0x5b63879fe510 in qdev_realize /data_nvme1n1/linaro/qemu-from-laptop/qemu/build/x86-tgts-asan/../../hw/core/qdev.c:276:12 Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2517 Signed-off-by: Peter Maydell Reviewed-by: David Hildenbrand Link: https://lore.kernel.org/r/20250929144228.1994037-4-peter.maydell@linaro.org Signed-off-by: Peter Xu --- hw/core/cpu-common.c | 1 + include/exec/cpu-common.h | 10 +++++----- include/hw/core/cpu.h | 1 - stubs/cpu-destroy-address-spaces.c | 15 ++++++++++++++ stubs/meson.build | 1 + system/physmem.c | 32 ++++++++++++++---------------- 6 files changed, 37 insertions(+), 23 deletions(-) create mode 100644 stubs/cpu-destroy-address-spaces.c diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 41a339903ca79..8c306c89e4530 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -294,6 +294,7 @@ void cpu_exec_unrealizefn(CPUState *cpu) * accel_cpu_common_unrealize, which may free fields using call_rcu. */ accel_cpu_common_unrealize(cpu); + cpu_destroy_address_spaces(cpu); } static void cpu_common_initfn(Object *obj) diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index f373781ae071b..b96ac49844aff 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -123,13 +123,13 @@ size_t qemu_ram_pagesize_largest(void); void cpu_address_space_init(CPUState *cpu, int asidx, const char *prefix, MemoryRegion *mr); /** - * cpu_address_space_destroy: - * @cpu: CPU for which address space needs to be destroyed - * @asidx: integer index of this address space + * cpu_destroy_address_spaces: + * @cpu: CPU for which address spaces need to be destroyed * - * Note that with KVM only one address space is supported. + * Destroy all address spaces associated with this CPU; this + * is called as part of unrealizing the CPU. */ -void cpu_address_space_destroy(CPUState *cpu, int asidx); +void cpu_destroy_address_spaces(CPUState *cpu); void cpu_physical_memory_rw(hwaddr addr, void *buf, hwaddr len, bool is_write); diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index c9f40c25392e5..0fcbc923f3848 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -515,7 +515,6 @@ struct CPUState { QSIMPLEQ_HEAD(, qemu_work_item) work_list; struct CPUAddressSpace *cpu_ases; - int cpu_ases_count; int num_ases; AddressSpace *as; MemoryRegion *memory; diff --git a/stubs/cpu-destroy-address-spaces.c b/stubs/cpu-destroy-address-spaces.c new file mode 100644 index 0000000000000..dc6813f5bd128 --- /dev/null +++ b/stubs/cpu-destroy-address-spaces.c @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "exec/cpu-common.h" + +/* + * user-mode CPUs never create address spaces with + * cpu_address_space_init(), so the cleanup function doesn't + * need to do anything. We need this stub because cpu-common.c + * is built-once so it can't #ifndef CONFIG_USER around the + * call; the real function is in physmem.c which is system-only. + */ +void cpu_destroy_address_spaces(CPUState *cpu) +{ +} diff --git a/stubs/meson.build b/stubs/meson.build index cef046e6854dd..5d577467bfddf 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -55,6 +55,7 @@ endif if have_user # Symbols that are used by hw/core. stub_ss.add(files('cpu-synchronize-state.c')) + stub_ss.add(files('cpu-destroy-address-spaces.c')) # Stubs for QAPI events. Those can always be included in the build, but # they are not built at all for --disable-system builds. diff --git a/system/physmem.c b/system/physmem.c index ae8ecd50ea1bd..dbb2a4e017542 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -795,7 +795,6 @@ void cpu_address_space_init(CPUState *cpu, int asidx, if (!cpu->cpu_ases) { cpu->cpu_ases = g_new0(CPUAddressSpace, cpu->num_ases); - cpu->cpu_ases_count = cpu->num_ases; } newas = &cpu->cpu_ases[asidx]; @@ -809,30 +808,29 @@ void cpu_address_space_init(CPUState *cpu, int asidx, } } -void cpu_address_space_destroy(CPUState *cpu, int asidx) +void cpu_destroy_address_spaces(CPUState *cpu) { CPUAddressSpace *cpuas; + int asidx; assert(cpu->cpu_ases); - assert(asidx >= 0 && asidx < cpu->num_ases); - cpuas = &cpu->cpu_ases[asidx]; - if (tcg_enabled()) { - memory_listener_unregister(&cpuas->tcg_as_listener); - } + /* convenience alias just points to some cpu_ases[n] */ + cpu->as = NULL; - address_space_destroy(cpuas->as); - g_free_rcu(cpuas->as, rcu); - - if (asidx == 0) { - /* reset the convenience alias for address space 0 */ - cpu->as = NULL; + for (asidx = 0; asidx < cpu->num_ases; asidx++) { + cpuas = &cpu->cpu_ases[asidx]; + if (!cpuas->as) { + /* This index was never initialized; no deinit needed */ + continue; + } + if (tcg_enabled()) { + memory_listener_unregister(&cpuas->tcg_as_listener); + } + g_clear_pointer(&cpuas->as, address_space_destroy_free); } - if (--cpu->cpu_ases_count == 0) { - g_free(cpu->cpu_ases); - cpu->cpu_ases = NULL; - } + g_clear_pointer(&cpu->cpu_ases, g_free); } AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx) From a5bc1ccca9596ecbf57b05bed10bd39e8854e475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 1 Aug 2025 18:02:11 +0100 Subject: [PATCH 1179/1794] migration: simplify error reporting after channel read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code handling the return value of qio_channel_read proceses len == 0 (EOF) separately from len < 1 (error), but in both cases ends up calling qemu_file_set_error_obj() with -EIO as the errno. This logic can be merged into one codepath to simplify it. Signed-off-by: Daniel P. Berrangé Reviewed-by: Peter Xu Reviewed-by: Prasad Pandit Link: https://lore.kernel.org/r/20250801170212.54409-2-berrange@redhat.com Signed-off-by: Peter Xu --- migration/qemu-file.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 0ee0f48a3ecb1..2d4ce174a5ff6 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -348,17 +348,13 @@ static ssize_t coroutine_mixed_fn qemu_fill_buffer(QEMUFile *f) } else { qio_channel_wait(f->ioc, G_IO_IN); } - } else if (len < 0) { - len = -EIO; } } while (len == QIO_CHANNEL_ERR_BLOCK); if (len > 0) { f->buf_size += len; - } else if (len == 0) { - qemu_file_set_error_obj(f, -EIO, local_error); } else { - qemu_file_set_error_obj(f, len, local_error); + qemu_file_set_error_obj(f, -EIO, local_error); } for (int i = 0; i < nfd; i++) { From dc79c7d5e15be05b23f24fab12f0d5e2bf831514 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:33:53 -0700 Subject: [PATCH 1180/1794] migration: multi-mode notifier Allow a notifier to be added for multiple migration modes. To allow a notifier to appear on multiple per-node lists, use a generic list type. We can no longer use NotifierWithReturnList, because it shoe horns the notifier onto a single list. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/1759332851-370353-2-git-send-email-steven.sistare@oracle.com Signed-off-by: Peter Xu --- include/migration/misc.h | 12 ++++++++ migration/migration.c | 60 +++++++++++++++++++++++++++++++--------- 2 files changed, 59 insertions(+), 13 deletions(-) diff --git a/include/migration/misc.h b/include/migration/misc.h index a261f99d89051..592b93021eb42 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -95,7 +95,19 @@ void migration_add_notifier(NotifierWithReturn *notify, void migration_add_notifier_mode(NotifierWithReturn *notify, MigrationNotifyFunc func, MigMode mode); +/* + * Same as migration_add_notifier, but applies to all @mode in the argument + * list. The list is terminated by -1 or MIG_MODE_ALL. For the latter, + * the notifier is added for all modes. + */ +void migration_add_notifier_modes(NotifierWithReturn *notify, + MigrationNotifyFunc func, MigMode mode, ...); + +/* + * Remove a notifier from all modes. + */ void migration_remove_notifier(NotifierWithReturn *notify); + void migration_file_set_error(int ret, Error *err); /* True if incoming migration entered POSTCOPY_INCOMING_DISCARD */ diff --git a/migration/migration.c b/migration/migration.c index edb8ff0d4619e..a399735f023f6 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -74,11 +74,7 @@ #define INMIGRATE_DEFAULT_EXIT_ON_ERROR true -static NotifierWithReturnList migration_state_notifiers[] = { - NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_NORMAL), - NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_CPR_REBOOT), - NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_CPR_TRANSFER), -}; +static GSList *migration_state_notifiers[MIG_MODE__MAX]; /* Messages sent on the return path from destination to source */ enum mig_rp_message_type { @@ -1675,23 +1671,51 @@ void migration_cancel(void) } } +static int get_modes(MigMode mode, va_list ap); + +static void add_notifiers(NotifierWithReturn *notify, int modes) +{ + for (MigMode mode = 0; mode < MIG_MODE__MAX; mode++) { + if (modes & BIT(mode)) { + migration_state_notifiers[mode] = + g_slist_prepend(migration_state_notifiers[mode], notify); + } + } +} + +void migration_add_notifier_modes(NotifierWithReturn *notify, + MigrationNotifyFunc func, MigMode mode, ...) +{ + int modes; + va_list ap; + + va_start(ap, mode); + modes = get_modes(mode, ap); + va_end(ap); + + notify->notify = (NotifierWithReturnFunc)func; + add_notifiers(notify, modes); +} + void migration_add_notifier_mode(NotifierWithReturn *notify, MigrationNotifyFunc func, MigMode mode) { - notify->notify = (NotifierWithReturnFunc)func; - notifier_with_return_list_add(&migration_state_notifiers[mode], notify); + migration_add_notifier_modes(notify, func, mode, -1); } void migration_add_notifier(NotifierWithReturn *notify, MigrationNotifyFunc func) { - migration_add_notifier_mode(notify, func, MIG_MODE_NORMAL); + migration_add_notifier_modes(notify, func, MIG_MODE_NORMAL, -1); } void migration_remove_notifier(NotifierWithReturn *notify) { if (notify->notify) { - notifier_with_return_remove(notify); + for (MigMode mode = 0; mode < MIG_MODE__MAX; mode++) { + migration_blockers[mode] = + g_slist_remove(migration_state_notifiers[mode], notify); + } notify->notify = NULL; } } @@ -1701,13 +1725,23 @@ int migration_call_notifiers(MigrationState *s, MigrationEventType type, { MigMode mode = s->parameters.mode; MigrationEvent e; + NotifierWithReturn *notifier; + GSList *elem, *next; int ret; e.type = type; - ret = notifier_with_return_list_notify(&migration_state_notifiers[mode], - &e, errp); - assert(!ret || type == MIG_EVENT_PRECOPY_SETUP); - return ret; + + for (elem = migration_state_notifiers[mode]; elem; elem = next) { + next = elem->next; + notifier = (NotifierWithReturn *)elem->data; + ret = notifier->notify(notifier, &e, errp); + if (ret) { + assert(type == MIG_EVENT_PRECOPY_SETUP); + return ret; + } + } + + return 0; } bool migration_has_failed(MigrationState *s) From a9f9eee58bc5cfacc1aa5cb7a138b5a8c12a493c Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:33:54 -0700 Subject: [PATCH 1181/1794] migration: add cpr_walk_fd Add a helper to walk all CPR fd's and run a callback for each. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1759332851-370353-3-git-send-email-steven.sistare@oracle.com Signed-off-by: Peter Xu --- include/migration/cpr.h | 3 +++ migration/cpr.c | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/include/migration/cpr.h b/include/migration/cpr.h index 3fc19a74efdcf..2b074d7a65469 100644 --- a/include/migration/cpr.h +++ b/include/migration/cpr.h @@ -34,6 +34,9 @@ void cpr_resave_fd(const char *name, int id, int fd); int cpr_open_fd(const char *path, int flags, const char *name, int id, Error **errp); +typedef bool (*cpr_walk_fd_cb)(int fd); +bool cpr_walk_fd(cpr_walk_fd_cb cb); + MigMode cpr_get_incoming_mode(void); void cpr_set_incoming_mode(MigMode mode); bool cpr_is_incoming(void); diff --git a/migration/cpr.c b/migration/cpr.c index e0b47df22279f..6feda78f1bd24 100644 --- a/migration/cpr.c +++ b/migration/cpr.c @@ -122,6 +122,19 @@ int cpr_open_fd(const char *path, int flags, const char *name, int id, return fd; } +bool cpr_walk_fd(cpr_walk_fd_cb cb) +{ + CprFd *elem; + + QLIST_FOREACH(elem, &cpr_state.fds, next) { + g_assert(elem->fd >= 0); + if (!cb(elem->fd)) { + return false; + } + } + return true; +} + /*************************************************************************/ static const VMStateDescription vmstate_cpr_state = { .name = CPR_STATE, From fe72a8073ed63cf0fc138d1f4450da2e6e5b5271 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:33:55 -0700 Subject: [PATCH 1182/1794] oslib: qemu_clear_cloexec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define qemu_clear_cloexec, analogous to qemu_set_cloexec. Signed-off-by: Steve Sistare Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Marc-André Lureau Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/1759332851-370353-4-git-send-email-steven.sistare@oracle.com Signed-off-by: Peter Xu --- include/qemu/osdep.h | 9 +++++++++ util/oslib-posix.c | 9 +++++++++ util/oslib-win32.c | 4 ++++ 3 files changed, 22 insertions(+) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 1b38cb7e452ac..ed3e511a8ef52 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -689,6 +689,15 @@ ssize_t qemu_write_full(int fd, const void *buf, size_t count) void qemu_set_cloexec(int fd); bool qemu_set_blocking(int fd, bool block, Error **errp); +/* + * Clear FD_CLOEXEC for a descriptor. + * + * The caller must guarantee that no other fork+exec's occur before the + * exec that is intended to inherit this descriptor, eg by suspending CPUs + * and blocking monitor commands. + */ +void qemu_clear_cloexec(int fd); + /* Return a dynamically allocated directory path that is appropriate for storing * local state. * diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 14cf94ac03417..3c14b726659f4 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -305,6 +305,15 @@ int qemu_socketpair(int domain, int type, int protocol, int sv[2]) return ret; } +void qemu_clear_cloexec(int fd) +{ + int f; + f = fcntl(fd, F_GETFD); + assert(f != -1); + f = fcntl(fd, F_SETFD, f & ~FD_CLOEXEC); + assert(f != -1); +} + char * qemu_get_local_state_dir(void) { diff --git a/util/oslib-win32.c b/util/oslib-win32.c index 84bc65a76575b..839b8a4170e44 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -219,6 +219,10 @@ void qemu_set_cloexec(int fd) { } +void qemu_clear_cloexec(int fd) +{ +} + int qemu_get_thread_id(void) { return GetCurrentThreadId(); From f57ff59f1e14f8162efda41725d1c013ed76b7d7 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:33:56 -0700 Subject: [PATCH 1183/1794] migration: cpr-exec-command parameter Create the cpr-exec-command migration parameter, defined as a list of strings. It will be used for cpr-exec migration mode in a subsequent patch, and contains forward references to cpr-exec mode in the qapi doc. No functional change, except that cpr-exec-command is shown by the 'info migrate' command. Signed-off-by: Steve Sistare Acked-by: Markus Armbruster Link: https://lore.kernel.org/r/1759332851-370353-5-git-send-email-steven.sistare@oracle.com Signed-off-by: Peter Xu --- hmp-commands.hx | 2 +- migration/migration-hmp-cmds.c | 30 ++++++++++++++++++++++++++++++ migration/options.c | 14 ++++++++++++++ qapi/migration.json | 21 ++++++++++++++++++--- 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index d0e4f35a30afb..3cace8f1f7bff 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1009,7 +1009,7 @@ ERST { .name = "migrate_set_parameter", - .args_type = "parameter:s,value:s", + .args_type = "parameter:s,value:S", .params = "parameter value", .help = "Set the parameter for migration", .cmd = hmp_migrate_set_parameter, diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 814221b260f2f..847d18faaacd0 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -306,6 +306,18 @@ void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict) qapi_free_MigrationCapabilityStatusList(caps); } +static void monitor_print_cpr_exec_command(Monitor *mon, strList *args) +{ + monitor_printf(mon, "%s:", + MigrationParameter_str(MIGRATION_PARAMETER_CPR_EXEC_COMMAND)); + + while (args) { + monitor_printf(mon, " %s", args->value); + args = args->next; + } + monitor_printf(mon, "\n"); +} + void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) { MigrationParameters *params; @@ -437,6 +449,9 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) MIGRATION_PARAMETER_DIRECT_IO), params->direct_io ? "on" : "off"); } + + assert(params->has_cpr_exec_command); + monitor_print_cpr_exec_command(mon, params->cpr_exec_command); } qapi_free_MigrationParameters(params); @@ -718,6 +733,21 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_direct_io = true; visit_type_bool(v, param, &p->direct_io, &err); break; + case MIGRATION_PARAMETER_CPR_EXEC_COMMAND: { + g_autofree char **strv = NULL; + g_autoptr(GError) gerr = NULL; + strList **tail = &p->cpr_exec_command; + + if (!g_shell_parse_argv(valuestr, NULL, &strv, &gerr)) { + error_setg(&err, "%s", gerr->message); + break; + } + for (int i = 0; strv[i]; i++) { + QAPI_LIST_APPEND(tail, strv[i]); + } + p->has_cpr_exec_command = true; + break; + } default: g_assert_not_reached(); } diff --git a/migration/options.c b/migration/options.c index 4e923a2e072a7..51831127754ae 100644 --- a/migration/options.c +++ b/migration/options.c @@ -959,6 +959,9 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->zero_page_detection = s->parameters.zero_page_detection; params->has_direct_io = true; params->direct_io = s->parameters.direct_io; + params->has_cpr_exec_command = true; + params->cpr_exec_command = QAPI_CLONE(strList, + s->parameters.cpr_exec_command); return params; } @@ -993,6 +996,7 @@ void migrate_params_init(MigrationParameters *params) params->has_mode = true; params->has_zero_page_detection = true; params->has_direct_io = true; + params->has_cpr_exec_command = true; } /* @@ -1297,6 +1301,10 @@ static void migrate_params_test_apply(MigrateSetParameters *params, if (params->has_direct_io) { dest->direct_io = params->direct_io; } + + if (params->has_cpr_exec_command) { + dest->cpr_exec_command = params->cpr_exec_command; + } } static void migrate_params_apply(MigrateSetParameters *params, Error **errp) @@ -1429,6 +1437,12 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) if (params->has_direct_io) { s->parameters.direct_io = params->direct_io; } + + if (params->has_cpr_exec_command) { + qapi_free_strList(s->parameters.cpr_exec_command); + s->parameters.cpr_exec_command = + QAPI_CLONE(strList, params->cpr_exec_command); + } } void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp) diff --git a/qapi/migration.json b/qapi/migration.json index 2387c21e9c188..2be8fa1d1603a 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -924,6 +924,10 @@ # only has effect if the @mapped-ram capability is enabled. # (Since 9.1) # +# @cpr-exec-command: Command to start the new QEMU process when @mode +# is @cpr-exec. The first list element is the program's filename, +# the remainder its arguments. (Since 10.2) +# # Features: # # @unstable: Members @x-checkpoint-delay and @@ -950,7 +954,8 @@ 'vcpu-dirty-limit', 'mode', 'zero-page-detection', - 'direct-io'] } + 'direct-io', + 'cpr-exec-command'] } ## # @MigrateSetParameters: @@ -1105,6 +1110,10 @@ # only has effect if the @mapped-ram capability is enabled. # (Since 9.1) # +# @cpr-exec-command: Command to start the new QEMU process when @mode +# is @cpr-exec. The first list element is the program's filename, +# the remainder its arguments. (Since 10.2) +# # Features: # # @unstable: Members @x-checkpoint-delay and @@ -1146,7 +1155,8 @@ '*vcpu-dirty-limit': 'uint64', '*mode': 'MigMode', '*zero-page-detection': 'ZeroPageDetection', - '*direct-io': 'bool' } } + '*direct-io': 'bool', + '*cpr-exec-command': [ 'str' ]} } ## # @migrate-set-parameters: @@ -1315,6 +1325,10 @@ # only has effect if the @mapped-ram capability is enabled. # (Since 9.1) # +# @cpr-exec-command: Command to start the new QEMU process when @mode +# is @cpr-exec. The first list element is the program's filename, +# the remainder its arguments. (Since 10.2) +# # Features: # # @unstable: Members @x-checkpoint-delay and @@ -1353,7 +1367,8 @@ '*vcpu-dirty-limit': 'uint64', '*mode': 'MigMode', '*zero-page-detection': 'ZeroPageDetection', - '*direct-io': 'bool' } } + '*direct-io': 'bool', + '*cpr-exec-command': [ 'str' ]} } ## # @query-migrate-parameters: From efc65873131dac48d2f7620adfcd683834acc94b Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:33:57 -0700 Subject: [PATCH 1184/1794] migration: cpr-exec save and load To preserve CPR state across exec, create a QEMUFile based on a memfd, and keep the memfd open across exec. Save the value of the memfd in an environment variable so post-exec QEMU can find it. These new functions are called in a subsequent patch. Signed-off-by: Steve Sistare Link: https://lore.kernel.org/r/1759332851-370353-6-git-send-email-steven.sistare@oracle.com [peterx: fix build for Windows] Signed-off-by: Peter Xu --- include/migration/cpr.h | 5 +++ migration/cpr-exec.c | 99 +++++++++++++++++++++++++++++++++++++++++ migration/meson.build | 1 + 3 files changed, 105 insertions(+) create mode 100644 migration/cpr-exec.c diff --git a/include/migration/cpr.h b/include/migration/cpr.h index 2b074d7a65469..b84389ff04a9f 100644 --- a/include/migration/cpr.h +++ b/include/migration/cpr.h @@ -53,4 +53,9 @@ int cpr_get_fd_param(const char *name, const char *fdname, int index, QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp); QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp); +QEMUFile *cpr_exec_output(Error **errp); +QEMUFile *cpr_exec_input(Error **errp); +void cpr_exec_persist_state(QEMUFile *f); +bool cpr_exec_has_state(void); +void cpr_exec_unpersist_state(void); #endif diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c new file mode 100644 index 0000000000000..81d84425e1b7c --- /dev/null +++ b/migration/cpr-exec.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021-2025 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qemu/memfd.h" +#include "qapi/error.h" +#include "io/channel-file.h" +#include "io/channel-socket.h" +#include "migration/cpr.h" +#include "migration/qemu-file.h" +#include "migration/misc.h" +#include "migration/vmstate.h" +#include "system/runstate.h" + +#define CPR_EXEC_STATE_NAME "QEMU_CPR_EXEC_STATE" + +static QEMUFile *qemu_file_new_fd_input(int fd, const char *name) +{ + g_autoptr(QIOChannelFile) fioc = qio_channel_file_new_fd(fd); + QIOChannel *ioc = QIO_CHANNEL(fioc); + qio_channel_set_name(ioc, name); + return qemu_file_new_input(ioc); +} + +static QEMUFile *qemu_file_new_fd_output(int fd, const char *name) +{ + g_autoptr(QIOChannelFile) fioc = qio_channel_file_new_fd(fd); + QIOChannel *ioc = QIO_CHANNEL(fioc); + qio_channel_set_name(ioc, name); + return qemu_file_new_output(ioc); +} + +void cpr_exec_persist_state(QEMUFile *f) +{ + QIOChannelFile *fioc = QIO_CHANNEL_FILE(qemu_file_get_ioc(f)); + int mfd = dup(fioc->fd); + char val[16]; + + /* Remember mfd in environment for post-exec load */ + qemu_clear_cloexec(mfd); + snprintf(val, sizeof(val), "%d", mfd); + g_setenv(CPR_EXEC_STATE_NAME, val, 1); +} + +static int cpr_exec_find_state(void) +{ + const char *val = g_getenv(CPR_EXEC_STATE_NAME); + int mfd; + + assert(val); + g_unsetenv(CPR_EXEC_STATE_NAME); + assert(!qemu_strtoi(val, NULL, 10, &mfd)); + return mfd; +} + +bool cpr_exec_has_state(void) +{ + return g_getenv(CPR_EXEC_STATE_NAME) != NULL; +} + +void cpr_exec_unpersist_state(void) +{ + int mfd; + const char *val = g_getenv(CPR_EXEC_STATE_NAME); + + g_unsetenv(CPR_EXEC_STATE_NAME); + assert(val); + assert(!qemu_strtoi(val, NULL, 10, &mfd)); + close(mfd); +} + +QEMUFile *cpr_exec_output(Error **errp) +{ + int mfd; + +#ifdef CONFIG_LINUX + mfd = qemu_memfd_create(CPR_EXEC_STATE_NAME, 0, false, 0, 0, errp); +#else + mfd = -1; +#endif + + if (mfd < 0) { + return NULL; + } + + return qemu_file_new_fd_output(mfd, CPR_EXEC_STATE_NAME); +} + +QEMUFile *cpr_exec_input(Error **errp) +{ + int mfd = cpr_exec_find_state(); + + lseek(mfd, 0, SEEK_SET); + return qemu_file_new_fd_input(mfd, CPR_EXEC_STATE_NAME); +} diff --git a/migration/meson.build b/migration/meson.build index 0f71544a8254c..16909d54c5110 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -16,6 +16,7 @@ system_ss.add(files( 'channel-block.c', 'cpr.c', 'cpr-transfer.c', + 'cpr-exec.c', 'cpu-throttle.c', 'dirtyrate.c', 'exec.c', From a3eae205c601dd491d2e83fa9ff0d15fce7236b5 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:33:58 -0700 Subject: [PATCH 1185/1794] migration: cpr-exec mode Add the cpr-exec migration mode. Usage: qemu-system-$arch -machine aux-ram-share=on ... migrate_set_parameter mode cpr-exec migrate_set_parameter cpr-exec-command \ ... -incoming \ migrate -d The migrate command stops the VM, saves state to uri-1, directly exec's a new version of QEMU on the same host, replacing the original process while retaining its PID, and loads state from uri-1. Guest RAM is preserved in place, albeit with new virtual addresses. The new QEMU process is started by exec'ing the command specified by the @cpr-exec-command parameter. The first word of the command is the binary, and the remaining words are its arguments. The command may be a direct invocation of new QEMU, or may be a non-QEMU command that exec's the new QEMU binary. This mode creates a second migration channel that is not visible to the user. At the start of migration, old QEMU saves CPR state to the second channel, and at the end of migration, it tells the main loop to call cpr_exec. New QEMU loads CPR state early, before objects are created. Because old QEMU terminates when new QEMU starts, one cannot stream data between the two, so uri-1 must be a type, such as a file, that accepts all data before old QEMU exits. Otherwise, old QEMU may quietly block writing to the channel. Memory-backend objects must have the share=on attribute, but memory-backend-epc is not supported. The VM must be started with the '-machine aux-ram-share=on' option, which allows anonymous memory to be transferred in place to the new process. The memfds are kept open across exec by clearing the close-on-exec flag, their values are saved in CPR state, and they are mmap'd in new QEMU. Signed-off-by: Steve Sistare Acked-by: Markus Armbruster Link: https://lore.kernel.org/r/1759332851-370353-7-git-send-email-steven.sistare@oracle.com Signed-off-by: Peter Xu --- include/migration/cpr.h | 2 + migration/cpr-exec.c | 95 +++++++++++++++++++++++++++++++++++++++ migration/cpr.c | 23 +++++++++- migration/migration.c | 10 ++++- migration/ram.c | 1 + migration/trace-events | 1 + migration/vmstate-types.c | 8 ++++ qapi/migration.json | 25 ++++++++++- system/vl.c | 4 +- 9 files changed, 164 insertions(+), 5 deletions(-) diff --git a/include/migration/cpr.h b/include/migration/cpr.h index b84389ff04a9f..a412d6663c3df 100644 --- a/include/migration/cpr.h +++ b/include/migration/cpr.h @@ -53,9 +53,11 @@ int cpr_get_fd_param(const char *name, const char *fdname, int index, QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp); QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp); +void cpr_exec_init(void); QEMUFile *cpr_exec_output(Error **errp); QEMUFile *cpr_exec_input(Error **errp); void cpr_exec_persist_state(QEMUFile *f); bool cpr_exec_has_state(void); void cpr_exec_unpersist_state(void); +void cpr_exec_unpreserve_fds(void); #endif diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c index 81d84425e1b7c..d57714bc5da89 100644 --- a/migration/cpr-exec.c +++ b/migration/cpr-exec.c @@ -6,15 +6,21 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qemu/error-report.h" #include "qemu/memfd.h" #include "qapi/error.h" +#include "qapi/type-helpers.h" #include "io/channel-file.h" #include "io/channel-socket.h" +#include "block/block-global-state.h" +#include "qemu/main-loop.h" #include "migration/cpr.h" #include "migration/qemu-file.h" +#include "migration/migration.h" #include "migration/misc.h" #include "migration/vmstate.h" #include "system/runstate.h" +#include "trace.h" #define CPR_EXEC_STATE_NAME "QEMU_CPR_EXEC_STATE" @@ -97,3 +103,92 @@ QEMUFile *cpr_exec_input(Error **errp) lseek(mfd, 0, SEEK_SET); return qemu_file_new_fd_input(mfd, CPR_EXEC_STATE_NAME); } + +static bool preserve_fd(int fd) +{ + qemu_clear_cloexec(fd); + return true; +} + +static bool unpreserve_fd(int fd) +{ + qemu_set_cloexec(fd); + return true; +} + +static void cpr_exec_preserve_fds(void) +{ + cpr_walk_fd(preserve_fd); +} + +void cpr_exec_unpreserve_fds(void) +{ + cpr_walk_fd(unpreserve_fd); +} + +static void cpr_exec_cb(void *opaque) +{ + MigrationState *s = migrate_get_current(); + char **argv = strv_from_str_list(s->parameters.cpr_exec_command); + Error *err = NULL; + + /* + * Clear the close-on-exec flag for all preserved fd's. We cannot do so + * earlier because they should not persist across miscellaneous fork and + * exec calls that are performed during normal operation. + */ + cpr_exec_preserve_fds(); + + trace_cpr_exec(); + execvp(argv[0], argv); + + /* + * exec should only fail if argv[0] is bogus, or has a permissions problem, + * or the system is very short on resources. + */ + g_strfreev(argv); + cpr_exec_unpreserve_fds(); + + error_setg_errno(&err, errno, "execvp %s failed", argv[0]); + error_report_err(error_copy(err)); + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); + migrate_set_error(s, err); + + /* Note, we can go from state COMPLETED to FAILED */ + migration_call_notifiers(s, MIG_EVENT_PRECOPY_FAILED, NULL); + + err = NULL; + if (!migration_block_activate(&err)) { + /* error was already reported */ + error_free(err); + return; + } + + if (runstate_is_live(s->vm_old_state)) { + vm_start(); + } +} + +static int cpr_exec_notifier(NotifierWithReturn *notifier, MigrationEvent *e, + Error **errp) +{ + MigrationState *s = migrate_get_current(); + + if (e->type == MIG_EVENT_PRECOPY_DONE) { + QEMUBH *cpr_exec_bh = qemu_bh_new(cpr_exec_cb, NULL); + assert(s->state == MIGRATION_STATUS_COMPLETED); + qemu_bh_schedule(cpr_exec_bh); + qemu_notify_event(); + } else if (e->type == MIG_EVENT_PRECOPY_FAILED) { + cpr_exec_unpersist_state(); + } + return 0; +} + +void cpr_exec_init(void) +{ + static NotifierWithReturn exec_notifier; + + migration_add_notifier_mode(&exec_notifier, cpr_exec_notifier, + MIG_MODE_CPR_EXEC); +} diff --git a/migration/cpr.c b/migration/cpr.c index 6feda78f1bd24..22dbac7c728c3 100644 --- a/migration/cpr.c +++ b/migration/cpr.c @@ -6,6 +6,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/vfio/vfio-device.h" @@ -186,6 +187,8 @@ int cpr_state_save(MigrationChannel *channel, Error **errp) if (mode == MIG_MODE_CPR_TRANSFER) { g_assert(channel); f = cpr_transfer_output(channel, errp); + } else if (mode == MIG_MODE_CPR_EXEC) { + f = cpr_exec_output(errp); } else { return 0; } @@ -202,6 +205,10 @@ int cpr_state_save(MigrationChannel *channel, Error **errp) return ret; } + if (migrate_mode() == MIG_MODE_CPR_EXEC) { + cpr_exec_persist_state(f); + } + /* * Close the socket only partially so we can later detect when the other * end closes by getting a HUP event. @@ -220,7 +227,13 @@ int cpr_state_load(MigrationChannel *channel, Error **errp) QEMUFile *f; MigMode mode = 0; - if (channel) { + if (cpr_exec_has_state()) { + mode = MIG_MODE_CPR_EXEC; + f = cpr_exec_input(errp); + if (channel) { + warn_report("ignoring cpr channel for migration mode cpr-exec"); + } + } else if (channel) { mode = MIG_MODE_CPR_TRANSFER; cpr_set_incoming_mode(mode); f = cpr_transfer_input(channel, errp); @@ -232,6 +245,7 @@ int cpr_state_load(MigrationChannel *channel, Error **errp) } trace_cpr_state_load(MigMode_str(mode)); + cpr_set_incoming_mode(mode); v = qemu_get_be32(f); if (v != QEMU_CPR_FILE_MAGIC) { @@ -252,6 +266,11 @@ int cpr_state_load(MigrationChannel *channel, Error **errp) return ret; } + if (migrate_mode() == MIG_MODE_CPR_EXEC) { + /* Set cloexec to prevent fd leaks from fork until the next cpr-exec */ + cpr_exec_unpreserve_fds(); + } + /* * Let the caller decide when to close the socket (and generate a HUP event * for the sending side). @@ -272,7 +291,7 @@ void cpr_state_close(void) bool cpr_incoming_needed(void *opaque) { MigMode mode = migrate_mode(); - return mode == MIG_MODE_CPR_TRANSFER; + return mode == MIG_MODE_CPR_TRANSFER || mode == MIG_MODE_CPR_EXEC; } /* diff --git a/migration/migration.c b/migration/migration.c index a399735f023f6..a63b46bbef90f 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -333,6 +333,7 @@ void migration_object_init(void) ram_mig_init(); dirty_bitmap_mig_init(); + cpr_exec_init(); /* Initialize cpu throttle timers */ cpu_throttle_init(); @@ -1807,7 +1808,8 @@ bool migrate_mode_is_cpr(MigrationState *s) { MigMode mode = s->parameters.mode; return mode == MIG_MODE_CPR_REBOOT || - mode == MIG_MODE_CPR_TRANSFER; + mode == MIG_MODE_CPR_TRANSFER || + mode == MIG_MODE_CPR_EXEC; } int migrate_init(MigrationState *s, Error **errp) @@ -2156,6 +2158,12 @@ static bool migrate_prepare(MigrationState *s, bool resume, Error **errp) return false; } + if (migrate_mode() == MIG_MODE_CPR_EXEC && + !s->parameters.has_cpr_exec_command) { + error_setg(errp, "cpr-exec mode requires setting cpr-exec-command"); + return false; + } + if (migration_is_blocked(errp)) { return false; } diff --git a/migration/ram.c b/migration/ram.c index a8e8d2cc67903..9aac89638aebb 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -228,6 +228,7 @@ bool migrate_ram_is_ignored(RAMBlock *block) MigMode mode = migrate_mode(); return !qemu_ram_is_migratable(block) || mode == MIG_MODE_CPR_TRANSFER || + mode == MIG_MODE_CPR_EXEC || (migrate_ignore_shared() && qemu_ram_is_shared(block) && qemu_ram_is_named_file(block)); } diff --git a/migration/trace-events b/migration/trace-events index 706db97def9c5..e8edd1fbbadf4 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -354,6 +354,7 @@ cpr_state_save(const char *mode) "%s mode" cpr_state_load(const char *mode) "%s mode" cpr_transfer_input(const char *path) "%s" cpr_transfer_output(const char *path) "%s" +cpr_exec(void) "" # block-dirty-bitmap.c send_bitmap_header_enter(void) "" diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c index a1cd7a95fa014..4b01dc19c277c 100644 --- a/migration/vmstate-types.c +++ b/migration/vmstate-types.c @@ -322,6 +322,10 @@ static int get_fd(QEMUFile *f, void *pv, size_t size, const VMStateField *field) { int32_t *v = pv; + if (migrate_mode() == MIG_MODE_CPR_EXEC) { + qemu_get_sbe32s(f, v); + return 0; + } *v = qemu_file_get_fd(f); return 0; } @@ -330,6 +334,10 @@ static int put_fd(QEMUFile *f, void *pv, size_t size, const VMStateField *field, JSONWriter *vmdesc) { int32_t *v = pv; + if (migrate_mode() == MIG_MODE_CPR_EXEC) { + qemu_put_sbe32s(f, v); + return 0; + } return qemu_file_put_fd(f, *v); } diff --git a/qapi/migration.json b/qapi/migration.json index 2be8fa1d1603a..be0f3fcc12a9c 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -694,9 +694,32 @@ # until you issue the `migrate-incoming` command. # # (since 10.0) +# +# @cpr-exec: The migrate command stops the VM, saves state to the +# migration channel, directly exec's a new version of QEMU on the +# same host, replacing the original process while retaining its +# PID, and loads state from the channel. Guest RAM is preserved +# in place. Devices and their pinned pages are also preserved for +# VFIO and IOMMUFD. +# +# Old QEMU starts new QEMU by exec'ing the command specified by +# the @cpr-exec-command parameter. The command may be a direct +# invocation of new QEMU, or may be a wrapper that exec's the new +# QEMU binary. +# +# Because old QEMU terminates when new QEMU starts, one cannot +# stream data between the two, so the channel must be a type, +# such as a file, that accepts all data before old QEMU exits. +# Otherwise, old QEMU may quietly block writing to the channel. +# +# Memory-backend objects must have the share=on attribute, but +# memory-backend-epc is not supported. The VM must be started +# with the '-machine aux-ram-share=on' option. +# +# (since 10.2) ## { 'enum': 'MigMode', - 'data': [ 'normal', 'cpr-reboot', 'cpr-transfer' ] } + 'data': [ 'normal', 'cpr-reboot', 'cpr-transfer', 'cpr-exec' ] } ## # @ZeroPageDetection: diff --git a/system/vl.c b/system/vl.c index 00f36947257a5..646239e4a69d1 100644 --- a/system/vl.c +++ b/system/vl.c @@ -3837,6 +3837,8 @@ void qemu_init(int argc, char **argv) } qemu_init_displays(); accel_setup_post(current_machine); - os_setup_post(); + if (migrate_mode() != MIG_MODE_CPR_EXEC) { + os_setup_post(); + } resume_mux_open(); } From 2f9dc012ec31856573009ee922855ba4ab5183a9 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:33:59 -0700 Subject: [PATCH 1186/1794] migration: cpr-exec docs Update developer documentation for cpr-exec mode. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/1759332851-370353-8-git-send-email-steven.sistare@oracle.com Signed-off-by: Peter Xu --- docs/devel/migration/CPR.rst | 112 ++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/docs/devel/migration/CPR.rst b/docs/devel/migration/CPR.rst index 0a0fd4f6dc319..b6178568a854b 100644 --- a/docs/devel/migration/CPR.rst +++ b/docs/devel/migration/CPR.rst @@ -5,7 +5,7 @@ CPR is the umbrella name for a set of migration modes in which the VM is migrated to a new QEMU instance on the same host. It is intended for use when the goal is to update host software components that run the VM, such as QEMU or even the host kernel. At this time, -the cpr-reboot and cpr-transfer modes are available. +the cpr-reboot, cpr-transfer, and cpr-exec modes are available. Because QEMU is restarted on the same host, with access to the same local devices, CPR is allowed in certain cases where normal migration @@ -324,3 +324,113 @@ descriptors from old to new QEMU. In the future, descriptors for vhost, and char devices could be transferred, preserving those devices and their kernel state without interruption, even if they do not explicitly support live migration. + +cpr-exec mode +------------- + +In this mode, QEMU stops the VM, writes VM state to the migration +URI, and directly exec's a new version of QEMU on the same host, +replacing the original process while retaining its PID. Guest RAM is +preserved in place, albeit with new virtual addresses. The user +completes the migration by specifying the ``-incoming`` option, and +by issuing the ``migrate-incoming`` command if necessary; see details +below. + +This mode supports VFIO/IOMMUFD devices by preserving device +descriptors and hence kernel state across the exec, even for devices +that do not support live migration. + +Because the old and new QEMU instances are not active concurrently, +the URI cannot be a type that streams data from one instance to the +other. + +This mode does not require a channel of type ``cpr``. The information +that is passed over that channel for cpr-transfer mode is instead +serialized to a memfd, the number of the fd is saved in the +QEMU_CPR_EXEC_STATE environment variable during the exec of new QEMU. +and new QEMU mmaps the memfd. + +Usage +^^^^^ + +Arguments for the new QEMU process are taken from the +@cpr-exec-command parameter. The first argument should be the +path of a new QEMU binary, or a prefix command that exec's the +new QEMU binary, and the arguments should include the ''-incoming'' +option. + +Memory backend objects must have the ``share=on`` attribute. +The VM must be started with the ``-machine aux-ram-share=on`` option. + +Outgoing: + * Set the migration mode parameter to ``cpr-exec``. + * Set the ``cpr-exec-command`` parameter. + * Issue the ``migrate`` command. It is recommended that the URI be + a ``file`` type, but one can use other types such as ``exec``, + provided the command captures all the data from the outgoing side, + and provides all the data to the incoming side. + +Incoming: + * You do not need to explicitly start new QEMU. It is started as + a side effect of the migrate command above. + * If the VM was running when the outgoing ``migrate`` command was + issued, then QEMU automatically resumes VM execution. + +Example 1: incoming URI +^^^^^^^^^^^^^^^^^^^^^^^ + +In these examples, we simply restart the same version of QEMU, but in +a real scenario one would set a new QEMU binary path in +cpr-exec-command. + +:: + + # qemu-kvm -monitor stdio + -object memory-backend-memfd,id=ram0,size=4G + -machine memory-backend=ram0 + -machine aux-ram-share=on + ... + + QEMU 10.2.50 monitor - type 'help' for more information + (qemu) info status + VM status: running + (qemu) migrate_set_parameter mode cpr-exec + (qemu) migrate_set_parameter cpr-exec-command qemu-kvm ... -incoming file:vm.state + (qemu) migrate -d file:vm.state + (qemu) QEMU 10.2.50 monitor - type 'help' for more information + (qemu) info status + VM status: running + +Example 2: incoming defer +^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + # qemu-kvm -monitor stdio + -object memory-backend-memfd,id=ram0,size=4G + -machine memory-backend=ram0 + -machine aux-ram-share=on + ... + + QEMU 10.2.50 monitor - type 'help' for more information + (qemu) info status + VM status: running + (qemu) migrate_set_parameter mode cpr-exec + (qemu) migrate_set_parameter cpr-exec-command qemu-kvm ... -incoming defer + (qemu) migrate -d file:vm.state + (qemu) QEMU 10.2.50 monitor - type 'help' for more information + (qemu) info status + status: paused (inmigrate) + (qemu) migrate_incoming file:vm.state + (qemu) info status + VM status: running + +Caveats +^^^^^^^ + +cpr-exec mode may not be used with postcopy, background-snapshot, +or COLO. + +cpr-exec mode requires permission to use the exec system call, which +is denied by certain sandbox options, such as spawn. + +The guest pause time increases for large guest RAM backed by small pages. From ee1ca09fc1e6362ab94e92c25600ae51413d8ee4 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Mon, 30 Sep 2024 09:31:52 -0700 Subject: [PATCH 1187/1794] vfio: cpr-exec mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All blockers and notifiers for cpr-transfer mode also apply to cpr-exec. Signed-off-by: Steve Sistare Acked-by: Cédric Le Goater Link: https://lore.kernel.org/r/30750362-d4a1-4392-8dd6-016624d01be1@oracle.com Signed-off-by: Peter Xu --- hw/vfio/container-legacy.c | 3 ++- hw/vfio/cpr-iommufd.c | 3 ++- hw/vfio/cpr-legacy.c | 9 +++++---- hw/vfio/cpr.c | 13 +++++++------ 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/hw/vfio/container-legacy.c b/hw/vfio/container-legacy.c index c0f87f774a008..c0540f2bdce80 100644 --- a/hw/vfio/container-legacy.c +++ b/hw/vfio/container-legacy.c @@ -990,7 +990,8 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, error_setg(&vbasedev->cpr.mdev_blocker, "CPR does not support vfio mdev %s", vbasedev->name); if (migrate_add_blocker_modes(&vbasedev->cpr.mdev_blocker, errp, - MIG_MODE_CPR_TRANSFER, -1) < 0) { + MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC, + -1) < 0) { goto hiod_unref_exit; } } diff --git a/hw/vfio/cpr-iommufd.c b/hw/vfio/cpr-iommufd.c index 1d70c87996d0d..8a4d65de5e96e 100644 --- a/hw/vfio/cpr-iommufd.c +++ b/hw/vfio/cpr-iommufd.c @@ -159,7 +159,8 @@ bool vfio_iommufd_cpr_register_iommufd(IOMMUFDBackend *be, Error **errp) if (!vfio_cpr_supported(be, cpr_blocker)) { return migrate_add_blocker_modes(cpr_blocker, errp, - MIG_MODE_CPR_TRANSFER, -1) == 0; + MIG_MODE_CPR_TRANSFER, + MIG_MODE_CPR_EXEC, -1) == 0; } vmstate_register(NULL, -1, &iommufd_cpr_vmstate, be); diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index bbf7a0d35f0ba..1a16cb118858e 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -179,16 +179,17 @@ bool vfio_legacy_cpr_register_container(VFIOLegacyContainer *container, if (!vfio_cpr_supported(container, cpr_blocker)) { return migrate_add_blocker_modes(cpr_blocker, errp, - MIG_MODE_CPR_TRANSFER, -1) == 0; + MIG_MODE_CPR_TRANSFER, + MIG_MODE_CPR_EXEC, -1) == 0; } vfio_cpr_add_kvm_notifier(); vmstate_register(NULL, -1, &vfio_container_vmstate, container); - migration_add_notifier_mode(&container->cpr.transfer_notifier, - vfio_cpr_fail_notifier, - MIG_MODE_CPR_TRANSFER); + migration_add_notifier_modes(&container->cpr.transfer_notifier, + vfio_cpr_fail_notifier, + MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC, -1); return true; } diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index 2c71fc1e8ef7b..db462aabcbfe9 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -195,9 +195,10 @@ static int vfio_cpr_kvm_close_notifier(NotifierWithReturn *notifier, void vfio_cpr_add_kvm_notifier(void) { if (!kvm_close_notifier.notify) { - migration_add_notifier_mode(&kvm_close_notifier, - vfio_cpr_kvm_close_notifier, - MIG_MODE_CPR_TRANSFER); + migration_add_notifier_modes(&kvm_close_notifier, + vfio_cpr_kvm_close_notifier, + MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC, + -1); } } @@ -282,9 +283,9 @@ static int vfio_cpr_pci_notifier(NotifierWithReturn *notifier, void vfio_cpr_pci_register_device(VFIOPCIDevice *vdev) { - migration_add_notifier_mode(&vdev->cpr.transfer_notifier, - vfio_cpr_pci_notifier, - MIG_MODE_CPR_TRANSFER); + migration_add_notifier_modes(&vdev->cpr.transfer_notifier, + vfio_cpr_pci_notifier, + MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC, -1); } void vfio_cpr_pci_unregister_device(VFIOPCIDevice *vdev) From 27cffe16354816d57710d2d4357f16139405c749 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 1 Oct 2025 08:34:11 -0700 Subject: [PATCH 1188/1794] migration-test: test cpr-exec Add a test for the cpr-exec migration mode. Signed-off-by: Steve Sistare Link: https://lore.kernel.org/r/1759332851-370353-20-git-send-email-steven.sistare@oracle.com Signed-off-by: Peter Xu --- tests/qtest/migration/cpr-tests.c | 133 ++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c index c4ce60ff66bb3..9388ad64be4c3 100644 --- a/tests/qtest/migration/cpr-tests.c +++ b/tests/qtest/migration/cpr-tests.c @@ -113,6 +113,138 @@ static void test_mode_transfer_defer(void) test_mode_transfer_common(true); } +static void set_cpr_exec_args(QTestState *who, MigrateCommon *args) +{ + g_autofree char *qtest_from_args = NULL; + g_autofree char *from_args = NULL; + g_autofree char *to_args = NULL; + g_autofree char *exec_args = NULL; + g_auto(GStrv) argv = NULL; + char *from_str, *src, *dst; + int ret; + + /* + * hide_stderr appends "2>/dev/null" to the command line, but cpr-exec + * passes the command-line words to execv, not to the shell, so suppress it + * here. fd 2 was already bound in the source VM, and execv preserves it. + */ + g_assert(args->start.hide_stderr == false); + + ret = migrate_args(&from_args, &to_args, args->listen_uri, &args->start); + g_assert(!ret); + qtest_from_args = qtest_qemu_args(from_args); + + /* + * The generated args may have been formatted using "%s %s" with empty + * strings, which can produce consecutive spaces, which g_strsplit would + * convert into empty strings. Ditto for leading and trailing space. + * De-dup spaces to avoid that. + */ + + from_str = src = dst = g_strstrip(qtest_from_args); + do { + if (*src != ' ' || src[-1] != ' ') { + *dst++ = *src; + } + } while (*src++); + + exec_args = g_strconcat(qtest_qemu_binary(migration_get_env()->qemu_dst), + " -incoming defer ", from_str, NULL); + argv = g_strsplit(exec_args, " ", -1); + migrate_set_parameter_strv(who, "cpr-exec-command", argv); +} + +static void wait_for_migration_event(QTestState *who, const char *waitfor) +{ + QDict *rsp, *data; + char *status; + bool done = false; + + while (!done) { + rsp = qtest_qmp_eventwait_ref(who, "MIGRATION"); + g_assert(qdict_haskey(rsp, "data")); + data = qdict_get_qdict(rsp, "data"); + g_assert(qdict_haskey(data, "status")); + status = g_strdup(qdict_get_str(data, "status")); + g_assert(strcmp(status, "failed")); + done = !strcmp(status, waitfor); + qobject_unref(rsp); + } +} + +static void test_cpr_exec(MigrateCommon *args) +{ + QTestState *from, *to; + void *data_hook = NULL; + g_autofree char *connect_uri = g_strdup(args->connect_uri); + g_autofree char *filename = g_strdup_printf("%s/%s", tmpfs, + FILE_TEST_FILENAME); + + if (migrate_start(&from, NULL, args->listen_uri, &args->start)) { + return; + } + + /* Source and dest never run concurrently */ + g_assert_false(args->live); + + if (args->start_hook) { + data_hook = args->start_hook(from, NULL); + } + + wait_for_serial("src_serial"); + set_cpr_exec_args(from, args); + migrate_set_capability(from, "events", true); + migrate_qmp(from, NULL, connect_uri, NULL, "{}"); + wait_for_migration_event(from, "completed"); + + to = qtest_init_after_exec(from); + + qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," + " 'arguments': { " + " 'channels': [ { 'channel-type': 'main'," + " 'addr': { 'transport': 'file'," + " 'filename': %s," + " 'offset': 0 } } ] } }", + filename); + wait_for_migration_complete(to); + + wait_for_resume(to, get_dst()); + /* Device on target is still named src_serial because args do not change */ + wait_for_serial("src_serial"); + + if (args->end_hook) { + args->end_hook(from, to, data_hook); + } + + migrate_end(from, to, args->result == MIG_TEST_SUCCEED); +} + +static void *test_mode_exec_start(QTestState *from, QTestState *to) +{ + assert(!to); + migrate_set_parameter_str(from, "mode", "cpr-exec"); + return NULL; +} + +static void test_mode_exec(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + g_autofree char *listen_uri = g_strdup_printf("defer"); + + MigrateCommon args = { + .start.only_source = true, + .start.opts_source = "-machine aux-ram-share=on -nodefaults", + .start.memory_backend = "-object memory-backend-memfd,id=pc.ram,size=%s" + " -machine memory-backend=pc.ram", + .connect_uri = uri, + .listen_uri = listen_uri, + .start_hook = test_mode_exec_start, + }; + + test_cpr_exec(&args); +} + void migration_test_add_cpr(MigrationTestEnv *env) { tmpfs = env->tmpfs; @@ -135,5 +267,6 @@ void migration_test_add_cpr(MigrationTestEnv *env) migration_test_add("/migration/mode/transfer", test_mode_transfer); migration_test_add("/migration/mode/transfer/defer", test_mode_transfer_defer); + migration_test_add("/migration/mode/exec", test_mode_exec); } } From e5fd02d8253abdc25c0eb145765734890c256b71 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 22 Sep 2025 16:18:15 +0200 Subject: [PATCH 1189/1794] net: bundle all offloads in a single struct The set_offload() argument list is already pretty long and we are going to introduce soon a bunch of additional offloads. Replace the offload arguments with a single struct and update all the relevant call-sites. No functional changes intended. Signed-off-by: Paolo Abeni Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Message-ID: Signed-off-by: Michael S. Tsirkin --- hw/net/e1000e_core.c | 5 +++-- hw/net/igb_core.c | 5 +++-- hw/net/virtio-net.c | 19 +++++++++++-------- hw/net/vmxnet3.c | 13 +++++-------- include/net/net.h | 15 ++++++++++++--- net/net.c | 5 ++--- net/netmap.c | 3 +-- net/tap-bsd.c | 3 +-- net/tap-linux.c | 21 ++++++++++++--------- net/tap-solaris.c | 4 ++-- net/tap-stub.c | 3 +-- net/tap.c | 8 ++++---- net/tap_int.h | 4 ++-- 13 files changed, 59 insertions(+), 49 deletions(-) diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index 06657bb3ac5ca..8fef598b4988c 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -2822,8 +2822,9 @@ e1000e_update_rx_offloads(E1000ECore *core) trace_e1000e_rx_set_cso(cso_state); if (core->has_vnet) { - qemu_set_offload(qemu_get_queue(core->owner_nic)->peer, - cso_state, 0, 0, 0, 0, 0, 0); + NetOffloads ol = { .csum = cso_state }; + + qemu_set_offload(qemu_get_queue(core->owner_nic)->peer, &ol); } } diff --git a/hw/net/igb_core.c b/hw/net/igb_core.c index 39e3ce1c8febb..45d8fd795b844 100644 --- a/hw/net/igb_core.c +++ b/hw/net/igb_core.c @@ -3058,8 +3058,9 @@ igb_update_rx_offloads(IGBCore *core) trace_e1000e_rx_set_cso(cso_state); if (core->has_vnet) { - qemu_set_offload(qemu_get_queue(core->owner_nic)->peer, - cso_state, 0, 0, 0, 0, 0, 0); + NetOffloads ol = {.csum = cso_state }; + + qemu_set_offload(qemu_get_queue(core->owner_nic)->peer, &ol); } } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 7848e26278ada..129bebd82e22b 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -773,14 +773,17 @@ static uint64_t virtio_net_bad_features(VirtIODevice *vdev) static void virtio_net_apply_guest_offloads(VirtIONet *n) { - qemu_set_offload(qemu_get_queue(n->nic)->peer, - !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_CSUM)), - !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO4)), - !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO6)), - !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_ECN)), - !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO)), - !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_USO4)), - !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_USO6))); + NetOffloads ol = { + .csum = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_CSUM)), + .tso4 = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO4)), + .tso6 = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO6)), + .ecn = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_ECN)), + .ufo = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO)), + .uso4 = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_USO4)), + .uso6 = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_USO6)), + }; + + qemu_set_offload(qemu_get_queue(n->nic)->peer, &ol); } static uint64_t virtio_net_guest_offloads_by_features(uint64_t features) diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index af73aa8ef285d..03732375a76d4 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -1322,14 +1322,11 @@ static void vmxnet3_update_features(VMXNET3State *s) s->lro_supported, rxcso_supported, s->rx_vlan_stripping); if (s->peer_has_vhdr) { - qemu_set_offload(qemu_get_queue(s->nic)->peer, - rxcso_supported, - s->lro_supported, - s->lro_supported, - 0, - 0, - 0, - 0); + NetOffloads ol = { .csum = rxcso_supported, + .tso4 = s->lro_supported, + .tso6 = s->lro_supported }; + + qemu_set_offload(qemu_get_queue(s->nic)->peer, &ol); } } diff --git a/include/net/net.h b/include/net/net.h index 84ee18e0f9727..48ba333d0276f 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -35,6 +35,16 @@ typedef struct NICConf { int32_t bootindex; } NICConf; +typedef struct NetOffloads { + bool csum; + bool tso4; + bool tso6; + bool ecn; + bool ufo; + bool uso4; + bool uso6; +} NetOffloads; + #define DEFINE_NIC_PROPERTIES(_state, _conf) \ DEFINE_PROP_MACADDR("mac", _state, _conf.macaddr), \ DEFINE_PROP_NETDEV("netdev", _state, _conf.peers) @@ -57,7 +67,7 @@ typedef bool (HasUfo)(NetClientState *); typedef bool (HasUso)(NetClientState *); typedef bool (HasVnetHdr)(NetClientState *); typedef bool (HasVnetHdrLen)(NetClientState *, int); -typedef void (SetOffload)(NetClientState *, int, int, int, int, int, int, int); +typedef void (SetOffload)(NetClientState *, const NetOffloads *); typedef int (GetVnetHdrLen)(NetClientState *); typedef void (SetVnetHdrLen)(NetClientState *, int); typedef bool (GetVnetHashSupportedTypes)(NetClientState *, uint32_t *); @@ -189,8 +199,7 @@ bool qemu_has_ufo(NetClientState *nc); bool qemu_has_uso(NetClientState *nc); bool qemu_has_vnet_hdr(NetClientState *nc); bool qemu_has_vnet_hdr_len(NetClientState *nc, int len); -void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6, - int ecn, int ufo, int uso4, int uso6); +void qemu_set_offload(NetClientState *nc, const NetOffloads *ol); int qemu_get_vnet_hdr_len(NetClientState *nc); void qemu_set_vnet_hdr_len(NetClientState *nc, int len); bool qemu_get_vnet_hash_supported_types(NetClientState *nc, uint32_t *types); diff --git a/net/net.c b/net/net.c index da275db86ecf7..63872b6855609 100644 --- a/net/net.c +++ b/net/net.c @@ -540,14 +540,13 @@ bool qemu_has_vnet_hdr_len(NetClientState *nc, int len) return nc->info->has_vnet_hdr_len(nc, len); } -void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6, - int ecn, int ufo, int uso4, int uso6) +void qemu_set_offload(NetClientState *nc, const NetOffloads *ol) { if (!nc || !nc->info->set_offload) { return; } - nc->info->set_offload(nc, csum, tso4, tso6, ecn, ufo, uso4, uso6); + nc->info->set_offload(nc, ol); } int qemu_get_vnet_hdr_len(NetClientState *nc) diff --git a/net/netmap.c b/net/netmap.c index 297510e19088e..6cd8f2bdc5f30 100644 --- a/net/netmap.c +++ b/net/netmap.c @@ -366,8 +366,7 @@ static void netmap_set_vnet_hdr_len(NetClientState *nc, int len) } } -static void netmap_set_offload(NetClientState *nc, int csum, int tso4, int tso6, - int ecn, int ufo, int uso4, int uso6) +static void netmap_set_offload(NetClientState *nc, const NetOffloads *ol) { NetmapState *s = DO_UPCAST(NetmapState, nc, nc); diff --git a/net/tap-bsd.c b/net/tap-bsd.c index 3f98d0ea82e68..38eb28cb5878c 100644 --- a/net/tap-bsd.c +++ b/net/tap-bsd.c @@ -239,8 +239,7 @@ int tap_fd_set_vnet_be(int fd, int is_be) return -EINVAL; } -void tap_fd_set_offload(int fd, int csum, int tso4, - int tso6, int ecn, int ufo, int uso4, int uso6) +void tap_fd_set_offload(int fd, const NetOffloads *ol) { } diff --git a/net/tap-linux.c b/net/tap-linux.c index e832810665640..79a9dd0da01de 100644 --- a/net/tap-linux.c +++ b/net/tap-linux.c @@ -244,8 +244,7 @@ int tap_fd_set_vnet_be(int fd, int is_be) abort(); } -void tap_fd_set_offload(int fd, int csum, int tso4, - int tso6, int ecn, int ufo, int uso4, int uso6) +void tap_fd_set_offload(int fd, const NetOffloads *ol) { unsigned int offload = 0; @@ -254,20 +253,24 @@ void tap_fd_set_offload(int fd, int csum, int tso4, return; } - if (csum) { + if (ol->csum) { offload |= TUN_F_CSUM; - if (tso4) + if (ol->tso4) { offload |= TUN_F_TSO4; - if (tso6) + } + if (ol->tso6) { offload |= TUN_F_TSO6; - if ((tso4 || tso6) && ecn) + } + if ((ol->tso4 || ol->tso6) && ol->ecn) { offload |= TUN_F_TSO_ECN; - if (ufo) + } + if (ol->ufo) { offload |= TUN_F_UFO; - if (uso4) { + } + if (ol->uso4) { offload |= TUN_F_USO4; } - if (uso6) { + if (ol->uso6) { offload |= TUN_F_USO6; } } diff --git a/net/tap-solaris.c b/net/tap-solaris.c index af2ebb16f513c..ef8e0feeb0f71 100644 --- a/net/tap-solaris.c +++ b/net/tap-solaris.c @@ -27,6 +27,7 @@ #include "tap_int.h" #include "qemu/ctype.h" #include "qemu/cutils.h" +#include "net/net.h" #include #include @@ -240,8 +241,7 @@ int tap_fd_set_vnet_be(int fd, int is_be) return -EINVAL; } -void tap_fd_set_offload(int fd, int csum, int tso4, - int tso6, int ecn, int ufo, int uso4, int uso6) +void tap_fd_set_offload(int fd, const NetOffloads *ol) { } diff --git a/net/tap-stub.c b/net/tap-stub.c index 38673434cbd60..67d14ad4d5ddf 100644 --- a/net/tap-stub.c +++ b/net/tap-stub.c @@ -66,8 +66,7 @@ int tap_fd_set_vnet_be(int fd, int is_be) return -EINVAL; } -void tap_fd_set_offload(int fd, int csum, int tso4, - int tso6, int ecn, int ufo, int uso4, int uso6) +void tap_fd_set_offload(int fd, const NetOffloads *ol) { } diff --git a/net/tap.c b/net/tap.c index f37133e301647..df232837446d7 100644 --- a/net/tap.c +++ b/net/tap.c @@ -285,15 +285,14 @@ static int tap_set_vnet_be(NetClientState *nc, bool is_be) return tap_fd_set_vnet_be(s->fd, is_be); } -static void tap_set_offload(NetClientState *nc, int csum, int tso4, - int tso6, int ecn, int ufo, int uso4, int uso6) +static void tap_set_offload(NetClientState *nc, const NetOffloads *ol) { TAPState *s = DO_UPCAST(TAPState, nc, nc); if (s->fd < 0) { return; } - tap_fd_set_offload(s->fd, csum, tso4, tso6, ecn, ufo, uso4, uso6); + tap_fd_set_offload(s->fd, ol); } static void tap_exit_notify(Notifier *notifier, void *data) @@ -391,6 +390,7 @@ static TAPState *net_tap_fd_init(NetClientState *peer, int fd, int vnet_hdr) { + NetOffloads ol = {}; NetClientState *nc; TAPState *s; @@ -404,7 +404,7 @@ static TAPState *net_tap_fd_init(NetClientState *peer, s->has_ufo = tap_probe_has_ufo(s->fd); s->has_uso = tap_probe_has_uso(s->fd); s->enabled = true; - tap_set_offload(&s->nc, 0, 0, 0, 0, 0, 0, 0); + tap_set_offload(&s->nc, &ol); /* * Make sure host header length is set correctly in tap: * it might have been modified by another instance of qemu. diff --git a/net/tap_int.h b/net/tap_int.h index 8857ff299d224..f8bbe1cb0c6d3 100644 --- a/net/tap_int.h +++ b/net/tap_int.h @@ -27,6 +27,7 @@ #define NET_TAP_INT_H #include "qapi/qapi-types-net.h" +#include "net/net.h" int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required, int mq_required, Error **errp); @@ -37,8 +38,7 @@ void tap_set_sndbuf(int fd, const NetdevTapOptions *tap, Error **errp); int tap_probe_vnet_hdr(int fd, Error **errp); int tap_probe_has_ufo(int fd); int tap_probe_has_uso(int fd); -void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo, - int uso4, int uso6); +void tap_fd_set_offload(int fd, const NetOffloads *ol); void tap_fd_set_vnet_hdr_len(int fd, int len); int tap_fd_set_vnet_le(int fd, int vnet_is_le); int tap_fd_set_vnet_be(int fd, int vnet_is_be); From c3d9dcd87f0d228ea3ac5a42076da829cff401f0 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 22 Sep 2025 16:18:16 +0200 Subject: [PATCH 1190/1794] linux-headers: deal with counted_by annotation Such annotation is present into the kernel uAPI headers since v6.7, and will be used soon by the vhost_type.h. Deal with it just stripping it. Reviewed-by: Akihiko Odaki Acked-by: Jason Wang Acked-by: Stefano Garzarella Signed-off-by: Paolo Abeni Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Message-ID: Signed-off-by: Michael S. Tsirkin --- scripts/update-linux-headers.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 717c379f9e095..64c0d7c4eb5fc 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -90,6 +90,7 @@ cp_portable() { -e 's/]*\)>/"standard-headers\/linux\/\1"/' \ -e "$arch_cmd" \ -e 's/__bitwise//' \ + -e 's/__counted_by(\w*)//' \ -e 's/__attribute__((packed))/QEMU_PACKED/' \ -e 's/__inline__/inline/' \ -e 's/__BITS_PER_LONG/HOST_LONG_BITS/' \ From 8de6cd5452eb9c58c0d105dbc9718bd0e83cc70f Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 22 Sep 2025 16:18:17 +0200 Subject: [PATCH 1191/1794] linux-headers: Update to Linux v6.17-rc1 Update headers to include the virtio GSO over UDP tunnel features Reviewed-by: Akihiko Odaki Acked-by: Jason Wang Signed-off-by: Paolo Abeni Tested-by: Lei Yang Acked-by: Stefano Garzarella Reviewed-by: Michael S. Tsirkin Message-ID: <0b1f3c011f90583ab52aa4fef04df6db35cc4a69.1758549625.git.pabeni@redhat.com> Signed-off-by: Michael S. Tsirkin --- include/standard-headers/drm/drm_fourcc.h | 56 ++++++- include/standard-headers/linux/ethtool.h | 4 +- .../linux/input-event-codes.h | 8 + include/standard-headers/linux/input.h | 1 + include/standard-headers/linux/pci_regs.h | 9 + include/standard-headers/linux/vhost_types.h | 5 + include/standard-headers/linux/virtio_net.h | 33 ++++ linux-headers/LICENSES/preferred/GPL-2.0 | 10 +- linux-headers/asm-arm64/unistd_64.h | 2 + linux-headers/asm-generic/unistd.h | 8 +- linux-headers/asm-loongarch/unistd_64.h | 2 + linux-headers/asm-mips/unistd_n32.h | 2 + linux-headers/asm-mips/unistd_n64.h | 2 + linux-headers/asm-mips/unistd_o32.h | 2 + linux-headers/asm-powerpc/kvm.h | 13 -- linux-headers/asm-powerpc/unistd_32.h | 2 + linux-headers/asm-powerpc/unistd_64.h | 2 + linux-headers/asm-riscv/kvm.h | 1 + linux-headers/asm-riscv/unistd_32.h | 2 + linux-headers/asm-riscv/unistd_64.h | 2 + linux-headers/asm-s390/unistd_32.h | 2 + linux-headers/asm-s390/unistd_64.h | 2 + linux-headers/asm-x86/unistd_32.h | 2 + linux-headers/asm-x86/unistd_64.h | 2 + linux-headers/asm-x86/unistd_x32.h | 2 + linux-headers/linux/iommufd.h | 154 +++++++++++++++++- linux-headers/linux/kvm.h | 2 + linux-headers/linux/vfio.h | 12 +- linux-headers/linux/vhost.h | 35 ++++ 29 files changed, 352 insertions(+), 27 deletions(-) diff --git a/include/standard-headers/drm/drm_fourcc.h b/include/standard-headers/drm/drm_fourcc.h index c8309d378bff7..cef077dfb35a6 100644 --- a/include/standard-headers/drm/drm_fourcc.h +++ b/include/standard-headers/drm/drm_fourcc.h @@ -209,6 +209,10 @@ extern "C" { #define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */ #define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */ +/* 48 bpp RGB */ +#define DRM_FORMAT_RGB161616 fourcc_code('R', 'G', '4', '8') /* [47:0] R:G:B 16:16:16 little endian */ +#define DRM_FORMAT_BGR161616 fourcc_code('B', 'G', '4', '8') /* [47:0] B:G:R 16:16:16 little endian */ + /* 64 bpp RGB */ #define DRM_FORMAT_XRGB16161616 fourcc_code('X', 'R', '4', '8') /* [63:0] x:R:G:B 16:16:16:16 little endian */ #define DRM_FORMAT_XBGR16161616 fourcc_code('X', 'B', '4', '8') /* [63:0] x:B:G:R 16:16:16:16 little endian */ @@ -217,7 +221,7 @@ extern "C" { #define DRM_FORMAT_ABGR16161616 fourcc_code('A', 'B', '4', '8') /* [63:0] A:B:G:R 16:16:16:16 little endian */ /* - * Floating point 64bpp RGB + * Half-Floating point - 16b/component * IEEE 754-2008 binary16 half-precision float * [15:0] sign:exponent:mantissa 1:5:10 */ @@ -227,6 +231,20 @@ extern "C" { #define DRM_FORMAT_ARGB16161616F fourcc_code('A', 'R', '4', 'H') /* [63:0] A:R:G:B 16:16:16:16 little endian */ #define DRM_FORMAT_ABGR16161616F fourcc_code('A', 'B', '4', 'H') /* [63:0] A:B:G:R 16:16:16:16 little endian */ +#define DRM_FORMAT_R16F fourcc_code('R', ' ', ' ', 'H') /* [15:0] R 16 little endian */ +#define DRM_FORMAT_GR1616F fourcc_code('G', 'R', ' ', 'H') /* [31:0] G:R 16:16 little endian */ +#define DRM_FORMAT_BGR161616F fourcc_code('B', 'G', 'R', 'H') /* [47:0] B:G:R 16:16:16 little endian */ + +/* + * Floating point - 32b/component + * IEEE 754-2008 binary32 float + * [31:0] sign:exponent:mantissa 1:8:23 + */ +#define DRM_FORMAT_R32F fourcc_code('R', ' ', ' ', 'F') /* [31:0] R 32 little endian */ +#define DRM_FORMAT_GR3232F fourcc_code('G', 'R', ' ', 'F') /* [63:0] R:G 32:32 little endian */ +#define DRM_FORMAT_BGR323232F fourcc_code('B', 'G', 'R', 'F') /* [95:0] R:G:B 32:32:32 little endian */ +#define DRM_FORMAT_ABGR32323232F fourcc_code('A', 'B', '8', 'F') /* [127:0] R:G:B:A 32:32:32:32 little endian */ + /* * RGBA format with 10-bit components packed in 64-bit per pixel, with 6 bits * of unused padding per component: @@ -376,6 +394,42 @@ extern "C" { */ #define DRM_FORMAT_Q401 fourcc_code('Q', '4', '0', '1') +/* + * 3 plane YCbCr LSB aligned + * In order to use these formats in a similar fashion to MSB aligned ones + * implementation can multiply the values by 2^6=64. For that reason the padding + * must only contain zeros. + * index 0 = Y plane, [15:0] z:Y [6:10] little endian + * index 1 = Cr plane, [15:0] z:Cr [6:10] little endian + * index 2 = Cb plane, [15:0] z:Cb [6:10] little endian + */ +#define DRM_FORMAT_S010 fourcc_code('S', '0', '1', '0') /* 2x2 subsampled Cb (1) and Cr (2) planes 10 bits per channel */ +#define DRM_FORMAT_S210 fourcc_code('S', '2', '1', '0') /* 2x1 subsampled Cb (1) and Cr (2) planes 10 bits per channel */ +#define DRM_FORMAT_S410 fourcc_code('S', '4', '1', '0') /* non-subsampled Cb (1) and Cr (2) planes 10 bits per channel */ + +/* + * 3 plane YCbCr LSB aligned + * In order to use these formats in a similar fashion to MSB aligned ones + * implementation can multiply the values by 2^4=16. For that reason the padding + * must only contain zeros. + * index 0 = Y plane, [15:0] z:Y [4:12] little endian + * index 1 = Cr plane, [15:0] z:Cr [4:12] little endian + * index 2 = Cb plane, [15:0] z:Cb [4:12] little endian + */ +#define DRM_FORMAT_S012 fourcc_code('S', '0', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes 12 bits per channel */ +#define DRM_FORMAT_S212 fourcc_code('S', '2', '1', '2') /* 2x1 subsampled Cb (1) and Cr (2) planes 12 bits per channel */ +#define DRM_FORMAT_S412 fourcc_code('S', '4', '1', '2') /* non-subsampled Cb (1) and Cr (2) planes 12 bits per channel */ + +/* + * 3 plane YCbCr + * index 0 = Y plane, [15:0] Y little endian + * index 1 = Cr plane, [15:0] Cr little endian + * index 2 = Cb plane, [15:0] Cb little endian + */ +#define DRM_FORMAT_S016 fourcc_code('S', '0', '1', '6') /* 2x2 subsampled Cb (1) and Cr (2) planes 16 bits per channel */ +#define DRM_FORMAT_S216 fourcc_code('S', '2', '1', '6') /* 2x1 subsampled Cb (1) and Cr (2) planes 16 bits per channel */ +#define DRM_FORMAT_S416 fourcc_code('S', '4', '1', '6') /* non-subsampled Cb (1) and Cr (2) planes 16 bits per channel */ + /* * 3 plane YCbCr * index 0: Y plane, [7:0] Y diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h index cef0d207a6279..eb803140283d2 100644 --- a/include/standard-headers/linux/ethtool.h +++ b/include/standard-headers/linux/ethtool.h @@ -2314,7 +2314,7 @@ enum { IPV6_USER_FLOW = 0x0e, /* spec only (usr_ip6_spec; nfc only) */ IPV4_FLOW = 0x10, /* hash only */ IPV6_FLOW = 0x11, /* hash only */ - ETHER_FLOW = 0x12, /* spec only (ether_spec) */ + ETHER_FLOW = 0x12, /* hash or spec (ether_spec) */ /* Used for GTP-U IPv4 and IPv6. * The format of GTP packets only includes @@ -2371,7 +2371,7 @@ enum { /* Flag to enable RSS spreading of traffic matching rule (nfc only) */ #define FLOW_RSS 0x20000000 -/* L3-L4 network traffic flow hash options */ +/* L2-L4 network traffic flow hash options */ #define RXH_L2DA (1 << 1) #define RXH_VLAN (1 << 2) #define RXH_L3_PROTO (1 << 3) diff --git a/include/standard-headers/linux/input-event-codes.h b/include/standard-headers/linux/input-event-codes.h index a82ff795e068b..00dc9caac923a 100644 --- a/include/standard-headers/linux/input-event-codes.h +++ b/include/standard-headers/linux/input-event-codes.h @@ -601,6 +601,11 @@ #define BTN_DPAD_LEFT 0x222 #define BTN_DPAD_RIGHT 0x223 +#define BTN_GRIPL 0x224 +#define BTN_GRIPR 0x225 +#define BTN_GRIPL2 0x226 +#define BTN_GRIPR2 0x227 + #define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ #define KEY_ROTATE_LOCK_TOGGLE 0x231 /* Display rotation lock */ #define KEY_REFRESH_RATE_TOGGLE 0x232 /* Display refresh rate toggle */ @@ -765,6 +770,9 @@ #define KEY_KBD_LCD_MENU4 0x2bb #define KEY_KBD_LCD_MENU5 0x2bc +/* Performance Boost key (Alienware)/G-Mode key (Dell) */ +#define KEY_PERFORMANCE 0x2bd + #define BTN_TRIGGER_HAPPY 0x2c0 #define BTN_TRIGGER_HAPPY1 0x2c0 #define BTN_TRIGGER_HAPPY2 0x2c1 diff --git a/include/standard-headers/linux/input.h b/include/standard-headers/linux/input.h index 942ea6aaa9773..d4512c20b543f 100644 --- a/include/standard-headers/linux/input.h +++ b/include/standard-headers/linux/input.h @@ -272,6 +272,7 @@ struct input_mask { #define BUS_CEC 0x1E #define BUS_INTEL_ISHTP 0x1F #define BUS_AMD_SFH 0x20 +#define BUS_SDW 0x21 /* * MT_TOOL types diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h index a3a3e942dedff..f5b17745de607 100644 --- a/include/standard-headers/linux/pci_regs.h +++ b/include/standard-headers/linux/pci_regs.h @@ -745,6 +745,7 @@ #define PCI_EXT_CAP_ID_L1SS 0x1E /* L1 PM Substates */ #define PCI_EXT_CAP_ID_PTM 0x1F /* Precision Time Measurement */ #define PCI_EXT_CAP_ID_DVSEC 0x23 /* Designated Vendor-Specific */ +#define PCI_EXT_CAP_ID_VF_REBAR 0x24 /* VF Resizable BAR */ #define PCI_EXT_CAP_ID_DLF 0x25 /* Data Link Feature */ #define PCI_EXT_CAP_ID_PL_16GT 0x26 /* Physical Layer 16.0 GT/s */ #define PCI_EXT_CAP_ID_NPEM 0x29 /* Native PCIe Enclosure Management */ @@ -1141,6 +1142,14 @@ #define PCI_DVSEC_HEADER2 0x8 /* Designated Vendor-Specific Header2 */ #define PCI_DVSEC_HEADER2_ID(x) ((x) & 0xffff) +/* VF Resizable BARs, same layout as PCI_REBAR */ +#define PCI_VF_REBAR_CAP PCI_REBAR_CAP +#define PCI_VF_REBAR_CAP_SIZES PCI_REBAR_CAP_SIZES +#define PCI_VF_REBAR_CTRL PCI_REBAR_CTRL +#define PCI_VF_REBAR_CTRL_BAR_IDX PCI_REBAR_CTRL_BAR_IDX +#define PCI_VF_REBAR_CTRL_NBAR_MASK PCI_REBAR_CTRL_NBAR_MASK +#define PCI_VF_REBAR_CTRL_BAR_SIZE PCI_REBAR_CTRL_BAR_SIZE + /* Data Link Feature */ #define PCI_DLF_CAP 0x04 /* Capabilities Register */ #define PCI_DLF_EXCHANGE_ENABLE 0x80000000 /* Data Link Feature Exchange Enable */ diff --git a/include/standard-headers/linux/vhost_types.h b/include/standard-headers/linux/vhost_types.h index fd54044936fc5..79b53a931a8c3 100644 --- a/include/standard-headers/linux/vhost_types.h +++ b/include/standard-headers/linux/vhost_types.h @@ -110,6 +110,11 @@ struct vhost_msg_v2 { }; }; +struct vhost_features_array { + uint64_t count; /* number of entries present in features array */ + uint64_t features[] ; +}; + struct vhost_memory_region { uint64_t guest_phys_addr; uint64_t memory_size; /* bytes */ diff --git a/include/standard-headers/linux/virtio_net.h b/include/standard-headers/linux/virtio_net.h index 982e854f14e49..93abaae0b9079 100644 --- a/include/standard-headers/linux/virtio_net.h +++ b/include/standard-headers/linux/virtio_net.h @@ -70,6 +70,28 @@ * with the same MAC. */ #define VIRTIO_NET_F_SPEED_DUPLEX 63 /* Device set linkspeed and duplex */ +#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO 65 /* Driver can receive + * GSO-over-UDP-tunnel packets + */ +#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM 66 /* Driver handles + * GSO-over-UDP-tunnel + * packets with partial csum + * for the outer header + */ +#define VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO 67 /* Device can receive + * GSO-over-UDP-tunnel packets + */ +#define VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM 68 /* Device handles + * GSO-over-UDP-tunnel + * packets with partial csum + * for the outer header + */ + +/* Offloads bits corresponding to VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO{,_CSUM} + * features + */ +#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_MAPPED 46 +#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM_MAPPED 47 #ifndef VIRTIO_NET_NO_LEGACY #define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */ @@ -131,12 +153,17 @@ struct virtio_net_hdr_v1 { #define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 /* Use csum_start, csum_offset */ #define VIRTIO_NET_HDR_F_DATA_VALID 2 /* Csum is valid */ #define VIRTIO_NET_HDR_F_RSC_INFO 4 /* rsc info in csum_ fields */ +#define VIRTIO_NET_HDR_F_UDP_TUNNEL_CSUM 8 /* UDP tunnel csum offload */ uint8_t flags; #define VIRTIO_NET_HDR_GSO_NONE 0 /* Not a GSO frame */ #define VIRTIO_NET_HDR_GSO_TCPV4 1 /* GSO frame, IPv4 TCP (TSO) */ #define VIRTIO_NET_HDR_GSO_UDP 3 /* GSO frame, IPv4 UDP (UFO) */ #define VIRTIO_NET_HDR_GSO_TCPV6 4 /* GSO frame, IPv6 TCP */ #define VIRTIO_NET_HDR_GSO_UDP_L4 5 /* GSO frame, IPv4& IPv6 UDP (USO) */ +#define VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV4 0x20 /* UDPv4 tunnel present */ +#define VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6 0x40 /* UDPv6 tunnel present */ +#define VIRTIO_NET_HDR_GSO_UDP_TUNNEL (VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV4 | \ + VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6) #define VIRTIO_NET_HDR_GSO_ECN 0x80 /* TCP has ECN set */ uint8_t gso_type; __virtio16 hdr_len; /* Ethernet + IP + tcp/udp hdrs */ @@ -181,6 +208,12 @@ struct virtio_net_hdr_v1_hash { uint16_t padding; }; +struct virtio_net_hdr_v1_hash_tunnel { + struct virtio_net_hdr_v1_hash hash_hdr; + uint16_t outer_th_offset; + uint16_t inner_nh_offset; +}; + #ifndef VIRTIO_NET_NO_LEGACY /* This header comes first in the scatter-gather list. * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must diff --git a/linux-headers/LICENSES/preferred/GPL-2.0 b/linux-headers/LICENSES/preferred/GPL-2.0 index ff0812fd89cc4..ea8e93dc44bc4 100644 --- a/linux-headers/LICENSES/preferred/GPL-2.0 +++ b/linux-headers/LICENSES/preferred/GPL-2.0 @@ -20,8 +20,8 @@ License-Text: GNU GENERAL PUBLIC LICENSE Version 2, June 1991 - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -322,10 +322,8 @@ the "copyright" line and a pointer to where the full notice is found. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - + You should have received a copy of the GNU General Public License along + with this program; if not, see . Also add information on how to contact you by electronic and paper mail. diff --git a/linux-headers/asm-arm64/unistd_64.h b/linux-headers/asm-arm64/unistd_64.h index ee9aaebdf3252..4ae25c2b91685 100644 --- a/linux-headers/asm-arm64/unistd_64.h +++ b/linux-headers/asm-arm64/unistd_64.h @@ -324,6 +324,8 @@ #define __NR_listxattrat 465 #define __NR_removexattrat 466 #define __NR_open_tree_attr 467 +#define __NR_file_getattr 468 +#define __NR_file_setattr 469 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h index 2892a45023af6..04e0077fb4c97 100644 --- a/linux-headers/asm-generic/unistd.h +++ b/linux-headers/asm-generic/unistd.h @@ -852,8 +852,14 @@ __SYSCALL(__NR_removexattrat, sys_removexattrat) #define __NR_open_tree_attr 467 __SYSCALL(__NR_open_tree_attr, sys_open_tree_attr) +/* fs/inode.c */ +#define __NR_file_getattr 468 +__SYSCALL(__NR_file_getattr, sys_file_getattr) +#define __NR_file_setattr 469 +__SYSCALL(__NR_file_setattr, sys_file_setattr) + #undef __NR_syscalls -#define __NR_syscalls 468 +#define __NR_syscalls 470 /* * 32 bit systems traditionally used different diff --git a/linux-headers/asm-loongarch/unistd_64.h b/linux-headers/asm-loongarch/unistd_64.h index 50d22df8f7ce8..5033fc8f2fe9e 100644 --- a/linux-headers/asm-loongarch/unistd_64.h +++ b/linux-headers/asm-loongarch/unistd_64.h @@ -320,6 +320,8 @@ #define __NR_listxattrat 465 #define __NR_removexattrat 466 #define __NR_open_tree_attr 467 +#define __NR_file_getattr 468 +#define __NR_file_setattr 469 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-mips/unistd_n32.h b/linux-headers/asm-mips/unistd_n32.h index bdcc2f460baba..c99c10e5bf45d 100644 --- a/linux-headers/asm-mips/unistd_n32.h +++ b/linux-headers/asm-mips/unistd_n32.h @@ -396,5 +396,7 @@ #define __NR_listxattrat (__NR_Linux + 465) #define __NR_removexattrat (__NR_Linux + 466) #define __NR_open_tree_attr (__NR_Linux + 467) +#define __NR_file_getattr (__NR_Linux + 468) +#define __NR_file_setattr (__NR_Linux + 469) #endif /* _ASM_UNISTD_N32_H */ diff --git a/linux-headers/asm-mips/unistd_n64.h b/linux-headers/asm-mips/unistd_n64.h index 3b6b0193b6905..0d975bb185605 100644 --- a/linux-headers/asm-mips/unistd_n64.h +++ b/linux-headers/asm-mips/unistd_n64.h @@ -372,5 +372,7 @@ #define __NR_listxattrat (__NR_Linux + 465) #define __NR_removexattrat (__NR_Linux + 466) #define __NR_open_tree_attr (__NR_Linux + 467) +#define __NR_file_getattr (__NR_Linux + 468) +#define __NR_file_setattr (__NR_Linux + 469) #endif /* _ASM_UNISTD_N64_H */ diff --git a/linux-headers/asm-mips/unistd_o32.h b/linux-headers/asm-mips/unistd_o32.h index 4609a4b4d38b4..86ac0ac84b39d 100644 --- a/linux-headers/asm-mips/unistd_o32.h +++ b/linux-headers/asm-mips/unistd_o32.h @@ -442,5 +442,7 @@ #define __NR_listxattrat (__NR_Linux + 465) #define __NR_removexattrat (__NR_Linux + 466) #define __NR_open_tree_attr (__NR_Linux + 467) +#define __NR_file_getattr (__NR_Linux + 468) +#define __NR_file_setattr (__NR_Linux + 469) #endif /* _ASM_UNISTD_O32_H */ diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index eaeda001784eb..077c5437f5219 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -1,18 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * * Copyright IBM Corp. 2007 * * Authors: Hollis Blanchard diff --git a/linux-headers/asm-powerpc/unistd_32.h b/linux-headers/asm-powerpc/unistd_32.h index 5d38a427e0af7..d7a32c5e06903 100644 --- a/linux-headers/asm-powerpc/unistd_32.h +++ b/linux-headers/asm-powerpc/unistd_32.h @@ -449,6 +449,8 @@ #define __NR_listxattrat 465 #define __NR_removexattrat 466 #define __NR_open_tree_attr 467 +#define __NR_file_getattr 468 +#define __NR_file_setattr 469 #endif /* _ASM_UNISTD_32_H */ diff --git a/linux-headers/asm-powerpc/unistd_64.h b/linux-headers/asm-powerpc/unistd_64.h index 860a488e4d1a1..ff35c51fc6566 100644 --- a/linux-headers/asm-powerpc/unistd_64.h +++ b/linux-headers/asm-powerpc/unistd_64.h @@ -421,6 +421,8 @@ #define __NR_listxattrat 465 #define __NR_removexattrat 466 #define __NR_open_tree_attr 467 +#define __NR_file_getattr 468 +#define __NR_file_setattr 469 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-riscv/kvm.h b/linux-headers/asm-riscv/kvm.h index 5f59fd226cc57..ef27d4289da11 100644 --- a/linux-headers/asm-riscv/kvm.h +++ b/linux-headers/asm-riscv/kvm.h @@ -18,6 +18,7 @@ #define __KVM_HAVE_IRQ_LINE #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 +#define KVM_DIRTY_LOG_PAGE_OFFSET 64 #define KVM_INTERRUPT_SET -1U #define KVM_INTERRUPT_UNSET -2U diff --git a/linux-headers/asm-riscv/unistd_32.h b/linux-headers/asm-riscv/unistd_32.h index a5e769f1d9967..6083373e884ca 100644 --- a/linux-headers/asm-riscv/unistd_32.h +++ b/linux-headers/asm-riscv/unistd_32.h @@ -315,6 +315,8 @@ #define __NR_listxattrat 465 #define __NR_removexattrat 466 #define __NR_open_tree_attr 467 +#define __NR_file_getattr 468 +#define __NR_file_setattr 469 #endif /* _ASM_UNISTD_32_H */ diff --git a/linux-headers/asm-riscv/unistd_64.h b/linux-headers/asm-riscv/unistd_64.h index 8df4d64841c2a..f0c7585c60edf 100644 --- a/linux-headers/asm-riscv/unistd_64.h +++ b/linux-headers/asm-riscv/unistd_64.h @@ -325,6 +325,8 @@ #define __NR_listxattrat 465 #define __NR_removexattrat 466 #define __NR_open_tree_attr 467 +#define __NR_file_getattr 468 +#define __NR_file_setattr 469 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h index 85eedbd18e31f..37b8f6f3585df 100644 --- a/linux-headers/asm-s390/unistd_32.h +++ b/linux-headers/asm-s390/unistd_32.h @@ -440,5 +440,7 @@ #define __NR_listxattrat 465 #define __NR_removexattrat 466 #define __NR_open_tree_attr 467 +#define __NR_file_getattr 468 +#define __NR_file_setattr 469 #endif /* _ASM_S390_UNISTD_32_H */ diff --git a/linux-headers/asm-s390/unistd_64.h b/linux-headers/asm-s390/unistd_64.h index c03b1b9701024..0652ba63318b0 100644 --- a/linux-headers/asm-s390/unistd_64.h +++ b/linux-headers/asm-s390/unistd_64.h @@ -388,5 +388,7 @@ #define __NR_listxattrat 465 #define __NR_removexattrat 466 #define __NR_open_tree_attr 467 +#define __NR_file_getattr 468 +#define __NR_file_setattr 469 #endif /* _ASM_S390_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/unistd_32.h b/linux-headers/asm-x86/unistd_32.h index 491d6b4eb68ee..8f784a56347d8 100644 --- a/linux-headers/asm-x86/unistd_32.h +++ b/linux-headers/asm-x86/unistd_32.h @@ -458,6 +458,8 @@ #define __NR_listxattrat 465 #define __NR_removexattrat 466 #define __NR_open_tree_attr 467 +#define __NR_file_getattr 468 +#define __NR_file_setattr 469 #endif /* _ASM_UNISTD_32_H */ diff --git a/linux-headers/asm-x86/unistd_64.h b/linux-headers/asm-x86/unistd_64.h index 7cf88bf9bdac6..2f55bebb81cca 100644 --- a/linux-headers/asm-x86/unistd_64.h +++ b/linux-headers/asm-x86/unistd_64.h @@ -381,6 +381,8 @@ #define __NR_listxattrat 465 #define __NR_removexattrat 466 #define __NR_open_tree_attr 467 +#define __NR_file_getattr 468 +#define __NR_file_setattr 469 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/unistd_x32.h b/linux-headers/asm-x86/unistd_x32.h index 82959111e6a5e..8cc8673f15471 100644 --- a/linux-headers/asm-x86/unistd_x32.h +++ b/linux-headers/asm-x86/unistd_x32.h @@ -334,6 +334,8 @@ #define __NR_listxattrat (__X32_SYSCALL_BIT + 465) #define __NR_removexattrat (__X32_SYSCALL_BIT + 466) #define __NR_open_tree_attr (__X32_SYSCALL_BIT + 467) +#define __NR_file_getattr (__X32_SYSCALL_BIT + 468) +#define __NR_file_setattr (__X32_SYSCALL_BIT + 469) #define __NR_rt_sigaction (__X32_SYSCALL_BIT + 512) #define __NR_rt_sigreturn (__X32_SYSCALL_BIT + 513) #define __NR_ioctl (__X32_SYSCALL_BIT + 514) diff --git a/linux-headers/linux/iommufd.h b/linux-headers/linux/iommufd.h index cb0f7d6b4dec9..2105a039551e9 100644 --- a/linux-headers/linux/iommufd.h +++ b/linux-headers/linux/iommufd.h @@ -56,6 +56,7 @@ enum { IOMMUFD_CMD_VDEVICE_ALLOC = 0x91, IOMMUFD_CMD_IOAS_CHANGE_PROCESS = 0x92, IOMMUFD_CMD_VEVENTQ_ALLOC = 0x93, + IOMMUFD_CMD_HW_QUEUE_ALLOC = 0x94, }; /** @@ -590,17 +591,44 @@ struct iommu_hw_info_arm_smmuv3 { __u32 aidr; }; +/** + * struct iommu_hw_info_tegra241_cmdqv - NVIDIA Tegra241 CMDQV Hardware + * Information (IOMMU_HW_INFO_TYPE_TEGRA241_CMDQV) + * + * @flags: Must be 0 + * @version: Version number for the CMDQ-V HW for PARAM bits[03:00] + * @log2vcmdqs: Log2 of the total number of VCMDQs for PARAM bits[07:04] + * @log2vsids: Log2 of the total number of SID replacements for PARAM bits[15:12] + * @__reserved: Must be 0 + * + * VMM can use these fields directly in its emulated global PARAM register. Note + * that only one Virtual Interface (VINTF) should be exposed to a VM, i.e. PARAM + * bits[11:08] should be set to 0 for log2 of the total number of VINTFs. + */ +struct iommu_hw_info_tegra241_cmdqv { + __u32 flags; + __u8 version; + __u8 log2vcmdqs; + __u8 log2vsids; + __u8 __reserved; +}; + /** * enum iommu_hw_info_type - IOMMU Hardware Info Types - * @IOMMU_HW_INFO_TYPE_NONE: Used by the drivers that do not report hardware + * @IOMMU_HW_INFO_TYPE_NONE: Output by the drivers that do not report hardware * info + * @IOMMU_HW_INFO_TYPE_DEFAULT: Input to request for a default type * @IOMMU_HW_INFO_TYPE_INTEL_VTD: Intel VT-d iommu info type * @IOMMU_HW_INFO_TYPE_ARM_SMMUV3: ARM SMMUv3 iommu info type + * @IOMMU_HW_INFO_TYPE_TEGRA241_CMDQV: NVIDIA Tegra241 CMDQV (extension for ARM + * SMMUv3) info type */ enum iommu_hw_info_type { IOMMU_HW_INFO_TYPE_NONE = 0, + IOMMU_HW_INFO_TYPE_DEFAULT = 0, IOMMU_HW_INFO_TYPE_INTEL_VTD = 1, IOMMU_HW_INFO_TYPE_ARM_SMMUV3 = 2, + IOMMU_HW_INFO_TYPE_TEGRA241_CMDQV = 3, }; /** @@ -625,6 +653,15 @@ enum iommufd_hw_capabilities { IOMMU_HW_CAP_PCI_PASID_PRIV = 1 << 2, }; +/** + * enum iommufd_hw_info_flags - Flags for iommu_hw_info + * @IOMMU_HW_INFO_FLAG_INPUT_TYPE: If set, @in_data_type carries an input type + * for user space to request for a specific info + */ +enum iommufd_hw_info_flags { + IOMMU_HW_INFO_FLAG_INPUT_TYPE = 1 << 0, +}; + /** * struct iommu_hw_info - ioctl(IOMMU_GET_HW_INFO) * @size: sizeof(struct iommu_hw_info) @@ -634,6 +671,12 @@ enum iommufd_hw_capabilities { * data that kernel supports * @data_uptr: User pointer to a user-space buffer used by the kernel to fill * the iommu type specific hardware information data + * @in_data_type: This shares the same field with @out_data_type, making it be + * a bidirectional field. When IOMMU_HW_INFO_FLAG_INPUT_TYPE is + * set, an input type carried via this @in_data_type field will + * be valid, requesting for the info data to the given type. If + * IOMMU_HW_INFO_FLAG_INPUT_TYPE is unset, any input value will + * be seen as IOMMU_HW_INFO_TYPE_DEFAULT * @out_data_type: Output the iommu hardware info type as defined in the enum * iommu_hw_info_type. * @out_capabilities: Output the generic iommu capability info type as defined @@ -663,7 +706,10 @@ struct iommu_hw_info { __u32 dev_id; __u32 data_len; __aligned_u64 data_uptr; - __u32 out_data_type; + union { + __u32 in_data_type; + __u32 out_data_type; + }; __u8 out_max_pasid_log2; __u8 __reserved[3]; __aligned_u64 out_capabilities; @@ -951,10 +997,29 @@ struct iommu_fault_alloc { * enum iommu_viommu_type - Virtual IOMMU Type * @IOMMU_VIOMMU_TYPE_DEFAULT: Reserved for future use * @IOMMU_VIOMMU_TYPE_ARM_SMMUV3: ARM SMMUv3 driver specific type + * @IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV: NVIDIA Tegra241 CMDQV (extension for ARM + * SMMUv3) enabled ARM SMMUv3 type */ enum iommu_viommu_type { IOMMU_VIOMMU_TYPE_DEFAULT = 0, IOMMU_VIOMMU_TYPE_ARM_SMMUV3 = 1, + IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV = 2, +}; + +/** + * struct iommu_viommu_tegra241_cmdqv - NVIDIA Tegra241 CMDQV Virtual Interface + * (IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV) + * @out_vintf_mmap_offset: mmap offset argument for VINTF's page0 + * @out_vintf_mmap_length: mmap length argument for VINTF's page0 + * + * Both @out_vintf_mmap_offset and @out_vintf_mmap_length are reported by kernel + * for user space to mmap the VINTF page0 from the host physical address space + * to the guest physical address space so that a guest kernel can directly R/W + * access to the VINTF page0 in order to control its virtual command queues. + */ +struct iommu_viommu_tegra241_cmdqv { + __aligned_u64 out_vintf_mmap_offset; + __aligned_u64 out_vintf_mmap_length; }; /** @@ -965,6 +1030,9 @@ enum iommu_viommu_type { * @dev_id: The device's physical IOMMU will be used to back the virtual IOMMU * @hwpt_id: ID of a nesting parent HWPT to associate to * @out_viommu_id: Output virtual IOMMU ID for the allocated object + * @data_len: Length of the type specific data + * @__reserved: Must be 0 + * @data_uptr: User pointer to a driver-specific virtual IOMMU data * * Allocate a virtual IOMMU object, representing the underlying physical IOMMU's * virtualization support that is a security-isolated slice of the real IOMMU HW @@ -985,6 +1053,9 @@ struct iommu_viommu_alloc { __u32 dev_id; __u32 hwpt_id; __u32 out_viommu_id; + __u32 data_len; + __u32 __reserved; + __aligned_u64 data_uptr; }; #define IOMMU_VIOMMU_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VIOMMU_ALLOC) @@ -995,10 +1066,15 @@ struct iommu_viommu_alloc { * @dev_id: The physical device to allocate a virtual instance on the vIOMMU * @out_vdevice_id: Object handle for the vDevice. Pass to IOMMU_DESTORY * @virt_id: Virtual device ID per vIOMMU, e.g. vSID of ARM SMMUv3, vDeviceID - * of AMD IOMMU, and vRID of a nested Intel VT-d to a Context Table + * of AMD IOMMU, and vRID of Intel VT-d * * Allocate a virtual device instance (for a physical device) against a vIOMMU. * This instance holds the device's information (related to its vIOMMU) in a VM. + * User should use IOMMU_DESTROY to destroy the virtual device before + * destroying the physical device (by closing vfio_cdev fd). Otherwise the + * virtual device would be forcibly destroyed on physical device destruction, + * its vdevice_id would be permanently leaked (unremovable & unreusable) until + * iommu fd closed. */ struct iommu_vdevice_alloc { __u32 size; @@ -1075,10 +1151,12 @@ struct iommufd_vevent_header { * enum iommu_veventq_type - Virtual Event Queue Type * @IOMMU_VEVENTQ_TYPE_DEFAULT: Reserved for future use * @IOMMU_VEVENTQ_TYPE_ARM_SMMUV3: ARM SMMUv3 Virtual Event Queue + * @IOMMU_VEVENTQ_TYPE_TEGRA241_CMDQV: NVIDIA Tegra241 CMDQV Extension IRQ */ enum iommu_veventq_type { IOMMU_VEVENTQ_TYPE_DEFAULT = 0, IOMMU_VEVENTQ_TYPE_ARM_SMMUV3 = 1, + IOMMU_VEVENTQ_TYPE_TEGRA241_CMDQV = 2, }; /** @@ -1102,6 +1180,19 @@ struct iommu_vevent_arm_smmuv3 { __aligned_le64 evt[4]; }; +/** + * struct iommu_vevent_tegra241_cmdqv - Tegra241 CMDQV IRQ + * (IOMMU_VEVENTQ_TYPE_TEGRA241_CMDQV) + * @lvcmdq_err_map: 128-bit logical vcmdq error map, little-endian. + * (Refer to register LVCMDQ_ERR_MAPs per VINTF ) + * + * The 128-bit register value from HW exclusively reflect the error bits for a + * Virtual Interface represented by a vIOMMU object. Read and report directly. + */ +struct iommu_vevent_tegra241_cmdqv { + __aligned_le64 lvcmdq_err_map[2]; +}; + /** * struct iommu_veventq_alloc - ioctl(IOMMU_VEVENTQ_ALLOC) * @size: sizeof(struct iommu_veventq_alloc) @@ -1141,4 +1232,61 @@ struct iommu_veventq_alloc { __u32 __reserved; }; #define IOMMU_VEVENTQ_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VEVENTQ_ALLOC) + +/** + * enum iommu_hw_queue_type - HW Queue Type + * @IOMMU_HW_QUEUE_TYPE_DEFAULT: Reserved for future use + * @IOMMU_HW_QUEUE_TYPE_TEGRA241_CMDQV: NVIDIA Tegra241 CMDQV (extension for ARM + * SMMUv3) Virtual Command Queue (VCMDQ) + */ +enum iommu_hw_queue_type { + IOMMU_HW_QUEUE_TYPE_DEFAULT = 0, + /* + * TEGRA241_CMDQV requirements (otherwise, allocation will fail) + * - alloc starts from the lowest @index=0 in ascending order + * - destroy starts from the last allocated @index in descending order + * - @base_addr must be aligned to @length in bytes and mapped in IOAS + * - @length must be a power of 2, with a minimum 32 bytes and a maximum + * 2 ^ idr[1].CMDQS * 16 bytes (use GET_HW_INFO call to read idr[1] + * from struct iommu_hw_info_arm_smmuv3) + * - suggest to back the queue memory with contiguous physical pages or + * a single huge page with alignment of the queue size, and limit the + * emulated vSMMU's IDR1.CMDQS to log2(huge page size / 16 bytes) + */ + IOMMU_HW_QUEUE_TYPE_TEGRA241_CMDQV = 1, +}; + +/** + * struct iommu_hw_queue_alloc - ioctl(IOMMU_HW_QUEUE_ALLOC) + * @size: sizeof(struct iommu_hw_queue_alloc) + * @flags: Must be 0 + * @viommu_id: Virtual IOMMU ID to associate the HW queue with + * @type: One of enum iommu_hw_queue_type + * @index: The logical index to the HW queue per virtual IOMMU for a multi-queue + * model + * @out_hw_queue_id: The ID of the new HW queue + * @nesting_parent_iova: Base address of the queue memory in the guest physical + * address space + * @length: Length of the queue memory + * + * Allocate a HW queue object for a vIOMMU-specific HW-accelerated queue, which + * allows HW to access a guest queue memory described using @nesting_parent_iova + * and @length. + * + * A vIOMMU can allocate multiple queues, but it must use a different @index per + * type to separate each allocation, e.g:: + * + * Type1 HW queue0, Type1 HW queue1, Type2 HW queue0, ... + */ +struct iommu_hw_queue_alloc { + __u32 size; + __u32 flags; + __u32 viommu_id; + __u32 type; + __u32 index; + __u32 out_hw_queue_id; + __aligned_u64 nesting_parent_iova; + __aligned_u64 length; +}; +#define IOMMU_HW_QUEUE_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_HW_QUEUE_ALLOC) #endif diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 32c5885a3c200..be704965d8651 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -636,6 +636,7 @@ struct kvm_ioeventfd { #define KVM_X86_DISABLE_EXITS_HLT (1 << 1) #define KVM_X86_DISABLE_EXITS_PAUSE (1 << 2) #define KVM_X86_DISABLE_EXITS_CSTATE (1 << 3) +#define KVM_X86_DISABLE_EXITS_APERFMPERF (1 << 4) /* for KVM_ENABLE_CAP */ struct kvm_enable_cap { @@ -952,6 +953,7 @@ struct kvm_enable_cap { #define KVM_CAP_ARM_EL2 240 #define KVM_CAP_ARM_EL2_E2H0 241 #define KVM_CAP_RISCV_MP_STATE_RESET 242 +#define KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED 243 struct kvm_irq_routing_irqchip { __u32 irqchip; diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index 79bf8c0cc5e40..4d96d1fc12faf 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h @@ -905,10 +905,12 @@ struct vfio_device_feature { * VFIO_DEVICE_BIND_IOMMUFD - _IOR(VFIO_TYPE, VFIO_BASE + 18, * struct vfio_device_bind_iommufd) * @argsz: User filled size of this data. - * @flags: Must be 0. + * @flags: Must be 0 or a bit flags of VFIO_DEVICE_BIND_* * @iommufd: iommufd to bind. * @out_devid: The device id generated by this bind. devid is a handle for * this device/iommufd bond and can be used in IOMMUFD commands. + * @token_uuid_ptr: Valid if VFIO_DEVICE_BIND_FLAG_TOKEN. Points to a 16 byte + * UUID in the same format as VFIO_DEVICE_FEATURE_PCI_VF_TOKEN. * * Bind a vfio_device to the specified iommufd. * @@ -917,13 +919,21 @@ struct vfio_device_feature { * * Unbind is automatically conducted when device fd is closed. * + * A token is sometimes required to open the device, unless this is known to be + * needed VFIO_DEVICE_BIND_FLAG_TOKEN should not be set and token_uuid_ptr is + * ignored. The only case today is a PF/VF relationship where the VF bind must + * be provided the same token as VFIO_DEVICE_FEATURE_PCI_VF_TOKEN provided to + * the PF. + * * Return: 0 on success, -errno on failure. */ struct vfio_device_bind_iommufd { __u32 argsz; __u32 flags; +#define VFIO_DEVICE_BIND_FLAG_TOKEN (1 << 0) __s32 iommufd; __u32 out_devid; + __aligned_u64 token_uuid_ptr; }; #define VFIO_DEVICE_BIND_IOMMUFD _IO(VFIO_TYPE, VFIO_BASE + 18) diff --git a/linux-headers/linux/vhost.h b/linux-headers/linux/vhost.h index d4b3e2ae1314d..283348b64af9a 100644 --- a/linux-headers/linux/vhost.h +++ b/linux-headers/linux/vhost.h @@ -235,4 +235,39 @@ */ #define VHOST_VDPA_GET_VRING_SIZE _IOWR(VHOST_VIRTIO, 0x82, \ struct vhost_vring_state) + +/* Extended features manipulation */ +#define VHOST_GET_FEATURES_ARRAY _IOR(VHOST_VIRTIO, 0x83, \ + struct vhost_features_array) +#define VHOST_SET_FEATURES_ARRAY _IOW(VHOST_VIRTIO, 0x83, \ + struct vhost_features_array) + +/* fork_owner values for vhost */ +#define VHOST_FORK_OWNER_KTHREAD 0 +#define VHOST_FORK_OWNER_TASK 1 + +/** + * VHOST_SET_FORK_FROM_OWNER - Set the fork_owner flag for the vhost device, + * This ioctl must called before VHOST_SET_OWNER. + * Only available when CONFIG_VHOST_ENABLE_FORK_OWNER_CONTROL=y + * + * @param fork_owner: An 8-bit value that determines the vhost thread mode + * + * When fork_owner is set to VHOST_FORK_OWNER_TASK(default value): + * - Vhost will create vhost worker as tasks forked from the owner, + * inheriting all of the owner's attributes. + * + * When fork_owner is set to VHOST_FORK_OWNER_KTHREAD: + * - Vhost will create vhost workers as kernel threads. + */ +#define VHOST_SET_FORK_FROM_OWNER _IOW(VHOST_VIRTIO, 0x83, __u8) + +/** + * VHOST_GET_FORK_OWNER - Get the current fork_owner flag for the vhost device. + * Only available when CONFIG_VHOST_ENABLE_FORK_OWNER_CONTROL=y + * + * @return: An 8-bit value indicating the current thread mode. + */ +#define VHOST_GET_FORK_FROM_OWNER _IOR(VHOST_VIRTIO, 0x84, __u8) + #endif From b15a61fdae976eb1ca8f2deee6a63dc3407d7ec6 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 22 Sep 2025 16:18:18 +0200 Subject: [PATCH 1192/1794] virtio: introduce extended features type The virtio specifications allows for up to 128 bits for the device features. Soon we are going to use some of the 'extended' bits features (bit 64 and above) for the virtio net driver. Represent the virtio features bitmask with a fixed size array, and introduce a few helpers to help manipulate them. Most drivers will keep using only 64 bits features space: use union to allow them access the lower part of the extended space without any per driver change. Reviewed-by: Akihiko Odaki Acked-by: Jason Wang Acked-by: Stefano Garzarella Signed-off-by: Paolo Abeni Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Message-ID: <6a9bbb5eb33830f20afbcb7e64d300af4126dd98.1758549625.git.pabeni@redhat.com> Signed-off-by: Michael S. Tsirkin --- include/hw/virtio/virtio-features.h | 126 ++++++++++++++++++++++++++++ include/hw/virtio/virtio.h | 7 +- 2 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 include/hw/virtio/virtio-features.h diff --git a/include/hw/virtio/virtio-features.h b/include/hw/virtio/virtio-features.h new file mode 100644 index 0000000000000..e29b7fe48fcef --- /dev/null +++ b/include/hw/virtio/virtio-features.h @@ -0,0 +1,126 @@ +/* + * Virtio features helpers + * + * Copyright 2025 Red Hat, Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_VIRTIO_FEATURES_H +#define QEMU_VIRTIO_FEATURES_H + +#include "qemu/bitops.h" + +#define VIRTIO_FEATURES_FMT "%016"PRIx64"%016"PRIx64 +#define VIRTIO_FEATURES_PR(f) (f)[1], (f)[0] + +#define VIRTIO_FEATURES_MAX 128 +#define VIRTIO_FEATURES_BIT(b) BIT_ULL((b) % 64) +#define VIRTIO_FEATURES_U64(b) ((b) / 64) +#define VIRTIO_FEATURES_NU32S (VIRTIO_FEATURES_MAX / 32) +#define VIRTIO_FEATURES_NU64S (VIRTIO_FEATURES_MAX / 64) + +#define VIRTIO_DECLARE_FEATURES(name) \ + union { \ + uint64_t name; \ + uint64_t name##_ex[VIRTIO_FEATURES_NU64S]; \ + } + +#define VIRTIO_DEFINE_PROP_FEATURE(_name, _state, _field, _bit, _defval) \ + DEFINE_PROP_BIT64(_name, _state, _field[VIRTIO_FEATURES_U64(_bit)], \ + (_bit) % 64, _defval) + +static inline void virtio_features_clear(uint64_t *features) +{ + memset(features, 0, sizeof(features[0]) * VIRTIO_FEATURES_NU64S); +} + +static inline void virtio_features_from_u64(uint64_t *features, uint64_t from) +{ + virtio_features_clear(features); + features[0] = from; +} + +static inline bool virtio_has_feature_ex(const uint64_t *features, + unsigned int fbit) +{ + assert(fbit < VIRTIO_FEATURES_MAX); + return features[VIRTIO_FEATURES_U64(fbit)] & VIRTIO_FEATURES_BIT(fbit); +} + +static inline void virtio_add_feature_ex(uint64_t *features, + unsigned int fbit) +{ + assert(fbit < VIRTIO_FEATURES_MAX); + features[VIRTIO_FEATURES_U64(fbit)] |= VIRTIO_FEATURES_BIT(fbit); +} + +static inline void virtio_clear_feature_ex(uint64_t *features, + unsigned int fbit) +{ + assert(fbit < VIRTIO_FEATURES_MAX); + features[VIRTIO_FEATURES_U64(fbit)] &= ~VIRTIO_FEATURES_BIT(fbit); +} + +static inline bool virtio_features_equal(const uint64_t *f1, + const uint64_t *f2) +{ + return !memcmp(f1, f2, sizeof(uint64_t) * VIRTIO_FEATURES_NU64S); +} + +static inline bool virtio_features_use_ex(const uint64_t *features) +{ + int i; + + for (i = 1; i < VIRTIO_FEATURES_NU64S; ++i) { + if (features[i]) { + return true; + } + } + return false; +} + +static inline bool virtio_features_empty(const uint64_t *features) +{ + return !virtio_features_use_ex(features) && !features[0]; +} + +static inline void virtio_features_copy(uint64_t *to, const uint64_t *from) +{ + memcpy(to, from, sizeof(to[0]) * VIRTIO_FEATURES_NU64S); +} + +static inline bool virtio_features_andnot(uint64_t *to, const uint64_t *f1, + const uint64_t *f2) +{ + uint64_t diff = 0; + int i; + + for (i = 0; i < VIRTIO_FEATURES_NU64S; i++) { + to[i] = f1[i] & ~f2[i]; + diff |= to[i]; + } + return diff; +} + +static inline void virtio_features_and(uint64_t *to, const uint64_t *f1, + const uint64_t *f2) +{ + int i; + + for (i = 0; i < VIRTIO_FEATURES_NU64S; i++) { + to[i] = f1[i] & f2[i]; + } +} + +static inline void virtio_features_or(uint64_t *to, const uint64_t *f1, + const uint64_t *f2) +{ + int i; + + for (i = 0; i < VIRTIO_FEATURES_NU64S; i++) { + to[i] = f1[i] | f2[i]; + } +} + +#endif diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index c594764f23f40..39e4059a66d3e 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -16,6 +16,7 @@ #include "system/memory.h" #include "hw/qdev-core.h" +#include "hw/virtio/virtio-features.h" #include "net/net.h" #include "migration/vmstate.h" #include "qemu/event_notifier.h" @@ -121,9 +122,9 @@ struct VirtIODevice * backend (e.g. vhost) and could potentially be a subset of the * total feature set offered by QEMU. */ - uint64_t host_features; - uint64_t guest_features; - uint64_t backend_features; + VIRTIO_DECLARE_FEATURES(host_features); + VIRTIO_DECLARE_FEATURES(guest_features); + VIRTIO_DECLARE_FEATURES(backend_features); size_t config_len; void *config; From 0a49a97433279512a03f3d9f36164a46caf498c6 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 22 Sep 2025 16:18:19 +0200 Subject: [PATCH 1193/1794] virtio: serialize extended features state If the driver uses any of the extended features (i.e. 64 or above), store the extended features range (64-127 bits). At load time, let legacy features initialize the full features range and pass it to the set helper; sub-states loading will have filled-up the extended part as needed. This is one of the few spots that need explicitly to know and set in stone the extended features array size; add a build bug to prevent breaking the migration should such size change again in the future: more serialization plumbing will be needed. Reviewed-by: Akihiko Odaki Acked-by: Jason Wang Acked-by: Stefano Garzarella Signed-off-by: Paolo Abeni Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Message-ID: Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio.c | 88 ++++++++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 31 deletions(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 9a81ad912e013..bf53c211e5cc0 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2964,6 +2964,30 @@ static const VMStateDescription vmstate_virtio_disabled = { } }; +static bool virtio_128bit_features_needed(void *opaque) +{ + VirtIODevice *vdev = opaque; + + return virtio_features_use_ex(vdev->host_features_ex); +} + +static const VMStateDescription vmstate_virtio_128bit_features = { + .name = "virtio/128bit_features", + .version_id = 1, + .minimum_version_id = 1, + .needed = &virtio_128bit_features_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(guest_features_ex[1], VirtIODevice), + VMSTATE_END_OF_LIST() + } +}; + +/* + * Avoid silently breaking migration should the feature space increase + * even more in the (far away) future + */ +QEMU_BUILD_BUG_ON(VIRTIO_FEATURES_NU64S != 2); + static const VMStateDescription vmstate_virtio = { .name = "virtio", .version_id = 1, @@ -2973,6 +2997,7 @@ static const VMStateDescription vmstate_virtio = { }, .subsections = (const VMStateDescription * const []) { &vmstate_virtio_device_endian, + &vmstate_virtio_128bit_features, &vmstate_virtio_64bit_features, &vmstate_virtio_virtqueues, &vmstate_virtio_ringsize, @@ -3069,23 +3094,28 @@ const VMStateInfo virtio_vmstate_info = { .put = virtio_device_put, }; -static int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val) +static int virtio_set_features_nocheck(VirtIODevice *vdev, const uint64_t *val) { VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - bool bad = (val & ~(vdev->host_features)) != 0; + uint64_t tmp[VIRTIO_FEATURES_NU64S]; + bool bad; + + bad = virtio_features_andnot(tmp, val, vdev->host_features_ex); + virtio_features_and(tmp, val, vdev->host_features_ex); - val &= vdev->host_features; if (k->set_features) { - k->set_features(vdev, val); + bad = bad || virtio_features_use_ex(tmp); + k->set_features(vdev, tmp[0]); } - vdev->guest_features = val; + + virtio_features_copy(vdev->guest_features_ex, tmp); return bad ? -1 : 0; } typedef struct VirtioSetFeaturesNocheckData { Coroutine *co; VirtIODevice *vdev; - uint64_t val; + uint64_t val[VIRTIO_FEATURES_NU64S]; int ret; } VirtioSetFeaturesNocheckData; @@ -3098,14 +3128,15 @@ static void virtio_set_features_nocheck_bh(void *opaque) } static int coroutine_mixed_fn -virtio_set_features_nocheck_maybe_co(VirtIODevice *vdev, uint64_t val) +virtio_set_features_nocheck_maybe_co(VirtIODevice *vdev, + const uint64_t *val) { if (qemu_in_coroutine()) { VirtioSetFeaturesNocheckData data = { .co = qemu_coroutine_self(), .vdev = vdev, - .val = val, }; + virtio_features_copy(data.val, val); aio_bh_schedule_oneshot(qemu_get_current_aio_context(), virtio_set_features_nocheck_bh, &data); qemu_coroutine_yield(); @@ -3117,6 +3148,7 @@ virtio_set_features_nocheck_maybe_co(VirtIODevice *vdev, uint64_t val) int virtio_set_features(VirtIODevice *vdev, uint64_t val) { + uint64_t features[VIRTIO_FEATURES_NU64S]; int ret; /* * The driver must not attempt to set features after feature negotiation @@ -3132,7 +3164,8 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val) __func__, vdev->name); } - ret = virtio_set_features_nocheck(vdev, val); + virtio_features_from_u64(features, val); + ret = virtio_set_features_nocheck(vdev, features); if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { /* VIRTIO_RING_F_EVENT_IDX changes the size of the caches. */ int i; @@ -3155,6 +3188,7 @@ void virtio_reset(void *opaque) { VirtIODevice *vdev = opaque; VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + uint64_t features[VIRTIO_FEATURES_NU64S]; int i; virtio_set_status(vdev, 0); @@ -3181,7 +3215,8 @@ void virtio_reset(void *opaque) vdev->start_on_kick = false; vdev->started = false; vdev->broken = false; - virtio_set_features_nocheck(vdev, 0); + virtio_features_clear(features); + virtio_set_features_nocheck(vdev, features); vdev->queue_sel = 0; vdev->status = 0; vdev->disabled = false; @@ -3264,7 +3299,7 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) * Note: devices should always test host features in future - don't create * new dependencies like this. */ - vdev->guest_features = features; + virtio_features_from_u64(vdev->guest_features_ex, features); config_len = qemu_get_be32(f); @@ -3343,26 +3378,17 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) vdev->device_endian = virtio_default_endian(); } - if (virtio_64bit_features_needed(vdev)) { - /* - * Subsection load filled vdev->guest_features. Run them - * through virtio_set_features to sanity-check them against - * host_features. - */ - uint64_t features64 = vdev->guest_features; - if (virtio_set_features_nocheck_maybe_co(vdev, features64) < 0) { - error_report("Features 0x%" PRIx64 " unsupported. " - "Allowed features: 0x%" PRIx64, - features64, vdev->host_features); - return -1; - } - } else { - if (virtio_set_features_nocheck_maybe_co(vdev, features) < 0) { - error_report("Features 0x%x unsupported. " - "Allowed features: 0x%" PRIx64, - features, vdev->host_features); - return -1; - } + /* + * guest_features_ex is fully initialized with u32 features and upper + * bits have been filled as needed by the later load. + */ + if (virtio_set_features_nocheck_maybe_co(vdev, + vdev->guest_features_ex) < 0) { + error_report("Features 0x" VIRTIO_FEATURES_FMT " unsupported. " + "Allowed features: 0x" VIRTIO_FEATURES_FMT, + VIRTIO_FEATURES_PR(vdev->guest_features_ex), + VIRTIO_FEATURES_PR(vdev->host_features_ex)); + return -1; } if (!virtio_device_started(vdev, vdev->status) && From 64a6a336f42bc6305ab7589fd874cb4a3d403bd0 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 22 Sep 2025 16:18:20 +0200 Subject: [PATCH 1194/1794] virtio: add support for negotiating extended features The virtio specifications allows for a device features space up to 128 bits and more. Soon we are going to use some of the 'extended' bits features for the virtio net driver. Add support to allow extended features negotiation on a per devices basis. Devices willing to negotiated extended features need to implemented a new pair of features getter/setter, the core will conditionally use them instead of the basic one. Note that 'bad_features' don't need to be extended, as they are bound to the 64 bits limit. Reviewed-by: Akihiko Odaki Acked-by: Jason Wang Acked-by: Stefano Garzarella Signed-off-by: Paolo Abeni Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Message-ID: <9bb29d70adc3f2b8c7756d4e3cd076cffee87826.1758549625.git.pabeni@redhat.com> Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-bus.c | 11 ++++++++--- hw/virtio/virtio.c | 14 +++++++++++--- include/hw/virtio/virtio.h | 4 ++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index 11adfbf3abacd..cef944e01588f 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -62,9 +62,14 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) } /* Get the features of the plugged device. */ - assert(vdc->get_features != NULL); - vdev->host_features = vdc->get_features(vdev, vdev->host_features, - &local_err); + if (vdc->get_features_ex) { + vdc->get_features_ex(vdev, vdev->host_features_ex, &local_err); + } else { + assert(vdc->get_features != NULL); + virtio_features_from_u64(vdev->host_features_ex, + vdc->get_features(vdev, vdev->host_features, + &local_err)); + } if (local_err) { error_propagate(errp, local_err); return; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index bf53c211e5cc0..34f977a3c9e96 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -3103,7 +3103,9 @@ static int virtio_set_features_nocheck(VirtIODevice *vdev, const uint64_t *val) bad = virtio_features_andnot(tmp, val, vdev->host_features_ex); virtio_features_and(tmp, val, vdev->host_features_ex); - if (k->set_features) { + if (k->set_features_ex) { + k->set_features_ex(vdev, val); + } else if (k->set_features) { bad = bad || virtio_features_use_ex(tmp); k->set_features(vdev, tmp[0]); } @@ -3149,6 +3151,13 @@ virtio_set_features_nocheck_maybe_co(VirtIODevice *vdev, int virtio_set_features(VirtIODevice *vdev, uint64_t val) { uint64_t features[VIRTIO_FEATURES_NU64S]; + + virtio_features_from_u64(features, val); + return virtio_set_features_ex(vdev, features); +} + +int virtio_set_features_ex(VirtIODevice *vdev, const uint64_t *features) +{ int ret; /* * The driver must not attempt to set features after feature negotiation @@ -3158,13 +3167,12 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val) return -EINVAL; } - if (val & (1ull << VIRTIO_F_BAD_FEATURE)) { + if (features[0] & (1ull << VIRTIO_F_BAD_FEATURE)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: guest driver for %s has enabled UNUSED(30) feature bit!\n", __func__, vdev->name); } - virtio_features_from_u64(features, val); ret = virtio_set_features_nocheck(vdev, features); if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { /* VIRTIO_RING_F_EVENT_IDX changes the size of the caches. */ diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 39e4059a66d3e..2aeb021fb3f1e 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -178,6 +178,9 @@ struct VirtioDeviceClass { /* This is what a VirtioDevice must implement */ DeviceRealize realize; DeviceUnrealize unrealize; + void (*get_features_ex)(VirtIODevice *vdev, uint64_t *requested_features, + Error **errp); + void (*set_features_ex)(VirtIODevice *vdev, const uint64_t *val); uint64_t (*get_features)(VirtIODevice *vdev, uint64_t requested_features, Error **errp); @@ -373,6 +376,7 @@ void virtio_queue_reset(VirtIODevice *vdev, uint32_t queue_index); void virtio_queue_enable(VirtIODevice *vdev, uint32_t queue_index); void virtio_update_irq(VirtIODevice *vdev); int virtio_set_features(VirtIODevice *vdev, uint64_t val); +int virtio_set_features_ex(VirtIODevice *vdev, const uint64_t *val); /* Base devices. */ typedef struct VirtIOBlkConf VirtIOBlkConf; From 712c79d6d374e7abe94599de5ba2d155d5a79955 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 22 Sep 2025 16:18:21 +0200 Subject: [PATCH 1195/1794] virtio-pci: implement support for extended features Extend the features configuration space to 128 bits. If the virtio device supports any extended features, allow the common read/write operation to access all of it, otherwise keep exposing only the lower 64 bits. On migration, save the 128 bit version of the features only if the upper bits are non zero. Relay on reset to clear all the feature space before load. Reviewed-by: Akihiko Odaki Acked-by: Jason Wang Acked-by: Stefano Garzarella Signed-off-by: Paolo Abeni Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Message-ID: Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-pci.c | 76 ++++++++++++++++++++++++++++++---- include/hw/virtio/virtio-pci.h | 2 +- 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 767216d795998..01e4fecaf4f5d 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -109,6 +109,29 @@ static const VMStateDescription vmstate_virtio_pci_modern_queue_state = { } }; +static bool virtio_pci_modern_state_features128_needed(void *opaque) +{ + VirtIOPCIProxy *proxy = opaque; + uint32_t features = 0; + int i; + + for (i = 2; i < ARRAY_SIZE(proxy->guest_features); ++i) { + features |= proxy->guest_features[i]; + } + return features; +} + +static const VMStateDescription vmstate_virtio_pci_modern_state_features128 = { + .name = "virtio_pci/modern_state/features128", + .version_id = 1, + .minimum_version_id = 1, + .needed = &virtio_pci_modern_state_features128_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_SUB_ARRAY(guest_features, VirtIOPCIProxy, 2, 2), + VMSTATE_END_OF_LIST() + } +}; + static bool virtio_pci_modern_state_needed(void *opaque) { VirtIOPCIProxy *proxy = opaque; @@ -116,6 +139,12 @@ static bool virtio_pci_modern_state_needed(void *opaque) return virtio_pci_modern(proxy); } +/* + * Avoid silently breaking migration should the feature space increase + * even more in the (far away) future + */ +QEMU_BUILD_BUG_ON(VIRTIO_FEATURES_NU32S != 4); + static const VMStateDescription vmstate_virtio_pci_modern_state_sub = { .name = "virtio_pci/modern_state", .version_id = 1, @@ -124,11 +153,15 @@ static const VMStateDescription vmstate_virtio_pci_modern_state_sub = { .fields = (const VMStateField[]) { VMSTATE_UINT32(dfselect, VirtIOPCIProxy), VMSTATE_UINT32(gfselect, VirtIOPCIProxy), - VMSTATE_UINT32_ARRAY(guest_features, VirtIOPCIProxy, 2), + VMSTATE_UINT32_SUB_ARRAY(guest_features, VirtIOPCIProxy, 0, 2), VMSTATE_STRUCT_ARRAY(vqs, VirtIOPCIProxy, VIRTIO_QUEUE_MAX, 0, vmstate_virtio_pci_modern_queue_state, VirtIOPCIQueue), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_virtio_pci_modern_state_features128, + NULL } }; @@ -1477,6 +1510,19 @@ int virtio_pci_add_shm_cap(VirtIOPCIProxy *proxy, return virtio_pci_add_mem_cap(proxy, &cap.cap); } +static int virtio_pci_select_max(const VirtIODevice *vdev) +{ + int i; + + for (i = VIRTIO_FEATURES_NU64S - 1; i > 0; i--) { + if (vdev->host_features_ex[i]) { + return (i + 1) * 2; + } + } + + return 2; +} + static uint64_t virtio_pci_common_read(void *opaque, hwaddr addr, unsigned size) { @@ -1494,18 +1540,21 @@ static uint64_t virtio_pci_common_read(void *opaque, hwaddr addr, val = proxy->dfselect; break; case VIRTIO_PCI_COMMON_DF: - if (proxy->dfselect <= 1) { + if (proxy->dfselect < virtio_pci_select_max(vdev)) { VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); - val = (vdev->host_features & ~vdc->legacy_features) >> - (32 * proxy->dfselect); + val = vdev->host_features_ex[proxy->dfselect >> 1] >> + (32 * (proxy->dfselect & 1)); + if (proxy->dfselect <= 1) { + val &= (~vdc->legacy_features) >> (32 * proxy->dfselect); + } } break; case VIRTIO_PCI_COMMON_GFSELECT: val = proxy->gfselect; break; case VIRTIO_PCI_COMMON_GF: - if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features)) { + if (proxy->gfselect < virtio_pci_select_max(vdev)) { val = proxy->guest_features[proxy->gfselect]; } break; @@ -1588,11 +1637,18 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr, proxy->gfselect = val; break; case VIRTIO_PCI_COMMON_GF: - if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features)) { + if (proxy->gfselect < virtio_pci_select_max(vdev)) { + uint64_t features[VIRTIO_FEATURES_NU64S]; + int i; + proxy->guest_features[proxy->gfselect] = val; - virtio_set_features(vdev, - (((uint64_t)proxy->guest_features[1]) << 32) | - proxy->guest_features[0]); + virtio_features_clear(features); + for (i = 0; i < ARRAY_SIZE(proxy->guest_features); ++i) { + uint64_t cur = proxy->guest_features[i]; + + features[i >> 1] |= cur << ((i & 1) * 32); + } + virtio_set_features_ex(vdev, features); } break; case VIRTIO_PCI_COMMON_MSIX: @@ -2311,6 +2367,8 @@ static void virtio_pci_reset(DeviceState *qdev) virtio_bus_reset(bus); msix_unuse_all_vectors(&proxy->pci_dev); + memset(proxy->guest_features, 0, sizeof(proxy->guest_features)); + for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { proxy->vqs[i].enabled = 0; proxy->vqs[i].reset = 0; diff --git a/include/hw/virtio/virtio-pci.h b/include/hw/virtio/virtio-pci.h index eab5394898d99..639752977ee8a 100644 --- a/include/hw/virtio/virtio-pci.h +++ b/include/hw/virtio/virtio-pci.h @@ -158,7 +158,7 @@ struct VirtIOPCIProxy { uint32_t nvectors; uint32_t dfselect; uint32_t gfselect; - uint32_t guest_features[2]; + uint32_t guest_features[VIRTIO_FEATURES_NU32S]; VirtIOPCIQueue vqs[VIRTIO_QUEUE_MAX]; VirtIOIRQFD *vector_irqfd; From 9f979ef0e01a2dd47d167c482a9e2d1dcdff2d3f Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 22 Sep 2025 16:18:22 +0200 Subject: [PATCH 1196/1794] vhost: add support for negotiating extended features Similar to virtio infra, vhost core maintains the features status in the full extended format and allows the devices to implement extended version of the getter/setter. Note that 'protocol_features' are not extended: they are only used by vhost-user, and the latter device is not going to implement extended features soon. Reviewed-by: Akihiko Odaki Acked-by: Jason Wang Acked-by: Stefano Garzarella Signed-off-by: Paolo Abeni Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Message-ID: Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost.c | 68 ++++++++++++++++++++++--------- include/hw/virtio/vhost-backend.h | 6 +++ include/hw/virtio/vhost.h | 56 +++++++++++++++++++++---- 3 files changed, 103 insertions(+), 27 deletions(-) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 6557c58d12af2..5f485ad6cbc13 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -972,20 +972,34 @@ static int vhost_virtqueue_set_addr(struct vhost_dev *dev, static int vhost_dev_set_features(struct vhost_dev *dev, bool enable_log) { - uint64_t features = dev->acked_features; + uint64_t features[VIRTIO_FEATURES_NU64S]; int r; + + virtio_features_copy(features, dev->acked_features_ex); if (enable_log) { - features |= 0x1ULL << VHOST_F_LOG_ALL; + virtio_add_feature_ex(features, VHOST_F_LOG_ALL); } if (!vhost_dev_has_iommu(dev)) { - features &= ~(0x1ULL << VIRTIO_F_IOMMU_PLATFORM); + virtio_clear_feature_ex(features, VIRTIO_F_IOMMU_PLATFORM); } if (dev->vhost_ops->vhost_force_iommu) { if (dev->vhost_ops->vhost_force_iommu(dev) == true) { - features |= 0x1ULL << VIRTIO_F_IOMMU_PLATFORM; + virtio_add_feature_ex(features, VIRTIO_F_IOMMU_PLATFORM); } } - r = dev->vhost_ops->vhost_set_features(dev, features); + + if (virtio_features_use_ex(features) && + !dev->vhost_ops->vhost_set_features_ex) { + r = -EINVAL; + VHOST_OPS_DEBUG(r, "extended features without device support"); + goto out; + } + + if (dev->vhost_ops->vhost_set_features_ex) { + r = dev->vhost_ops->vhost_set_features_ex(dev, features); + } else { + r = dev->vhost_ops->vhost_set_features(dev, features[0]); + } if (r < 0) { VHOST_OPS_DEBUG(r, "vhost_set_features failed"); goto out; @@ -1508,12 +1522,27 @@ static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq) } } +static int vhost_dev_get_features(struct vhost_dev *hdev, + uint64_t *features) +{ + uint64_t features64; + int r; + + if (hdev->vhost_ops->vhost_get_features_ex) { + return hdev->vhost_ops->vhost_get_features_ex(hdev, features); + } + + r = hdev->vhost_ops->vhost_get_features(hdev, &features64); + virtio_features_from_u64(features, features64); + return r; +} + int vhost_dev_init(struct vhost_dev *hdev, void *opaque, VhostBackendType backend_type, uint32_t busyloop_timeout, Error **errp) { + uint64_t features[VIRTIO_FEATURES_NU64S]; unsigned int used, reserved, limit; - uint64_t features; int i, r, n_initialized_vqs = 0; hdev->vdev = NULL; @@ -1533,7 +1562,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, goto fail; } - r = hdev->vhost_ops->vhost_get_features(hdev, &features); + r = vhost_dev_get_features(hdev, features); if (r < 0) { error_setg_errno(errp, -r, "vhost_get_features failed"); goto fail; @@ -1571,7 +1600,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, } } - hdev->features = features; + virtio_features_copy(hdev->features_ex, features); hdev->memory_listener = (MemoryListener) { .name = "vhost", @@ -1594,7 +1623,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, }; if (hdev->migration_blocker == NULL) { - if (!(hdev->features & (0x1ULL << VHOST_F_LOG_ALL))) { + if (!virtio_has_feature_ex(hdev->features_ex, VHOST_F_LOG_ALL)) { error_setg(&hdev->migration_blocker, "Migration disabled: vhost lacks VHOST_F_LOG_ALL feature."); } else if (vhost_dev_log_is_shared(hdev) && !qemu_memfd_alloc_check()) { @@ -1859,28 +1888,27 @@ static void vhost_start_config_intr(struct vhost_dev *dev) } } -uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits, - uint64_t features) +void vhost_get_features_ex(struct vhost_dev *hdev, + const int *feature_bits, + uint64_t *features) { const int *bit = feature_bits; + while (*bit != VHOST_INVALID_FEATURE_BIT) { - uint64_t bit_mask = (1ULL << *bit); - if (!(hdev->features & bit_mask)) { - features &= ~bit_mask; + if (!virtio_has_feature_ex(hdev->features_ex, *bit)) { + virtio_clear_feature_ex(features, *bit); } bit++; } - return features; } -void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits, - uint64_t features) +void vhost_ack_features_ex(struct vhost_dev *hdev, const int *feature_bits, + const uint64_t *features) { const int *bit = feature_bits; while (*bit != VHOST_INVALID_FEATURE_BIT) { - uint64_t bit_mask = (1ULL << *bit); - if (features & bit_mask) { - hdev->acked_features |= bit_mask; + if (virtio_has_feature_ex(features, *bit)) { + virtio_add_feature_ex(hdev->acked_features_ex, *bit); } bit++; } diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index d6df209a2f0eb..ff94fa1734247 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -95,6 +95,10 @@ typedef int (*vhost_new_worker_op)(struct vhost_dev *dev, struct vhost_worker_state *worker); typedef int (*vhost_free_worker_op)(struct vhost_dev *dev, struct vhost_worker_state *worker); +typedef int (*vhost_set_features_ex_op)(struct vhost_dev *dev, + const uint64_t *features); +typedef int (*vhost_get_features_ex_op)(struct vhost_dev *dev, + uint64_t *features); typedef int (*vhost_set_features_op)(struct vhost_dev *dev, uint64_t features); typedef int (*vhost_get_features_op)(struct vhost_dev *dev, @@ -186,6 +190,8 @@ typedef struct VhostOps { vhost_free_worker_op vhost_free_worker; vhost_get_vring_worker_op vhost_get_vring_worker; vhost_attach_vring_worker_op vhost_attach_vring_worker; + vhost_set_features_ex_op vhost_set_features_ex; + vhost_get_features_ex_op vhost_get_features_ex; vhost_set_features_op vhost_set_features; vhost_get_features_op vhost_get_features; vhost_set_backend_cap_op vhost_set_backend_cap; diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 66be6afc88427..08bbb4dfe98af 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -107,9 +107,9 @@ struct vhost_dev { * future use should be discouraged and the variable retired as * its easy to confuse with the VirtIO backend_features. */ - uint64_t features; - uint64_t acked_features; - uint64_t backend_features; + VIRTIO_DECLARE_FEATURES(features); + VIRTIO_DECLARE_FEATURES(acked_features); + VIRTIO_DECLARE_FEATURES(backend_features); /** * @protocol_features: is the vhost-user only feature set by @@ -320,6 +320,20 @@ bool vhost_virtqueue_pending(struct vhost_dev *hdev, int n); void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, bool mask); +/** + * vhost_get_features_ex() - sanitize the extended features set + * @hdev: common vhost_dev structure + * @feature_bits: pointer to terminated table of feature bits + * @features: original features set, filtered out on return + * + * This is the extended variant of vhost_get_features(), supporting the + * the extended features set. Filter it with the intersection of what is + * supported by the vhost backend (hdev->features) and the supported + * feature_bits. + */ +void vhost_get_features_ex(struct vhost_dev *hdev, + const int *feature_bits, + uint64_t *features); /** * vhost_get_features() - return a sanitised set of feature bits * @hdev: common vhost_dev structure @@ -330,8 +344,28 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, * is supported by the vhost backend (hdev->features), the supported * feature_bits and the requested feature set. */ -uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits, - uint64_t features); +static inline uint64_t vhost_get_features(struct vhost_dev *hdev, + const int *feature_bits, + uint64_t features) +{ + uint64_t features_ex[VIRTIO_FEATURES_NU64S]; + + virtio_features_from_u64(features_ex, features); + vhost_get_features_ex(hdev, feature_bits, features_ex); + return features_ex[0]; +} + +/** + * vhost_ack_features_ex() - set vhost full set of acked_features + * @hdev: common vhost_dev structure + * @feature_bits: pointer to terminated table of feature bits + * @features: requested feature set + * + * This sets the internal hdev->acked_features to the intersection of + * the backends advertised features and the supported feature_bits. + */ +void vhost_ack_features_ex(struct vhost_dev *hdev, const int *feature_bits, + const uint64_t *features); /** * vhost_ack_features() - set vhost acked_features @@ -342,8 +376,16 @@ uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits, * This sets the internal hdev->acked_features to the intersection of * the backends advertised features and the supported feature_bits. */ -void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits, - uint64_t features); +static inline void vhost_ack_features(struct vhost_dev *hdev, + const int *feature_bits, + uint64_t features) +{ + uint64_t features_ex[VIRTIO_FEATURES_NU64S]; + + virtio_features_from_u64(features_ex, features); + vhost_ack_features_ex(hdev, feature_bits, features_ex); +} + unsigned int vhost_get_max_memslots(void); unsigned int vhost_get_free_memslots(void); From a76f5b795cab8ea0654e4813caa694470d7250a9 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 22 Sep 2025 16:18:23 +0200 Subject: [PATCH 1197/1794] qmp: update virtio features map to support extended features Extend the VirtioDeviceFeatures struct with an additional u64 to track unknown features in the 64-127 bit range and decode the full virtio features spaces for vhost and virtio devices. Also add entries for the soon-to-be-supported virtio net GSO over UDP features. Reviewed-by: Akihiko Odaki Acked-by: Jason Wang Acked-by: Markus Armbruster Acked-by: Stefano Garzarella Signed-off-by: Paolo Abeni Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Message-ID: Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-hmp-cmds.c | 3 +- hw/virtio/virtio-qmp.c | 91 +++++++++++++++++++++++++------------ hw/virtio/virtio-qmp.h | 3 +- qapi/virtio.json | 9 +++- 4 files changed, 74 insertions(+), 32 deletions(-) diff --git a/hw/virtio/virtio-hmp-cmds.c b/hw/virtio/virtio-hmp-cmds.c index 7d8677bcf0dc7..1daae482d3715 100644 --- a/hw/virtio/virtio-hmp-cmds.c +++ b/hw/virtio/virtio-hmp-cmds.c @@ -74,7 +74,8 @@ static void hmp_virtio_dump_features(Monitor *mon, } if (features->has_unknown_dev_features) { - monitor_printf(mon, " unknown-features(0x%016"PRIx64")\n", + monitor_printf(mon, " unknown-features(0x%016"PRIx64"%016"PRIx64")\n", + features->unknown_dev_features2, features->unknown_dev_features); } } diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c index 3b6377cf0d24c..b338344c6cca1 100644 --- a/hw/virtio/virtio-qmp.c +++ b/hw/virtio/virtio-qmp.c @@ -325,6 +325,20 @@ static const qmp_virtio_feature_map_t virtio_net_feature_map[] = { FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \ "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features " "negotiation supported"), + FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO, \ + "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Driver can receive GSO over " + "UDP tunnel packets"), + FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM, \ + "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Driver can receive GSO over " + "UDP tunnel packets requiring checksum offload for the outer " + "header"), + FEATURE_ENTRY(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO, \ + "VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO: Device can receive GSO over " + "UDP tunnel packets"), + FEATURE_ENTRY(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM, \ + "VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM: Device can receive GSO over " + "UDP tunnel packets requiring checksum offload for the outer " + "header"), { -1, "" } }; #endif @@ -510,6 +524,24 @@ static const qmp_virtio_feature_map_t virtio_gpio_feature_map[] = { list; \ }) +#define CONVERT_FEATURES_EX(type, map, bitmap) \ + ({ \ + type *list = NULL; \ + type *node; \ + for (i = 0; map[i].virtio_bit != -1; i++) { \ + bit = map[i].virtio_bit; \ + if (!virtio_has_feature_ex(bitmap, bit)) { \ + continue; \ + } \ + node = g_new0(type, 1); \ + node->value = g_strdup(map[i].feature_desc); \ + node->next = list; \ + list = node; \ + virtio_clear_feature_ex(bitmap, bit); \ + } \ + list; \ + }) + VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap) { VirtioDeviceStatus *status; @@ -545,109 +577,112 @@ VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap) return vhu_protocols; } -VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap) +VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, + const uint64_t *bmap) { + uint64_t bitmap[VIRTIO_FEATURES_NU64S]; VirtioDeviceFeatures *features; uint64_t bit; int i; + virtio_features_copy(bitmap, bmap); features = g_new0(VirtioDeviceFeatures, 1); features->has_dev_features = true; /* transport features */ - features->transports = CONVERT_FEATURES(strList, virtio_transport_map, 0, - bitmap); + features->transports = CONVERT_FEATURES_EX(strList, virtio_transport_map, + bitmap); /* device features */ switch (device_id) { #ifdef CONFIG_VIRTIO_SERIAL case VIRTIO_ID_CONSOLE: features->dev_features = - CONVERT_FEATURES(strList, virtio_serial_feature_map, 0, bitmap); + CONVERT_FEATURES_EX(strList, virtio_serial_feature_map, bitmap); break; #endif #ifdef CONFIG_VIRTIO_BLK case VIRTIO_ID_BLOCK: features->dev_features = - CONVERT_FEATURES(strList, virtio_blk_feature_map, 0, bitmap); + CONVERT_FEATURES_EX(strList, virtio_blk_feature_map, bitmap); break; #endif #ifdef CONFIG_VIRTIO_GPU case VIRTIO_ID_GPU: features->dev_features = - CONVERT_FEATURES(strList, virtio_gpu_feature_map, 0, bitmap); + CONVERT_FEATURES_EX(strList, virtio_gpu_feature_map, bitmap); break; #endif #ifdef CONFIG_VIRTIO_NET case VIRTIO_ID_NET: features->dev_features = - CONVERT_FEATURES(strList, virtio_net_feature_map, 0, bitmap); + CONVERT_FEATURES_EX(strList, virtio_net_feature_map, bitmap); break; #endif #ifdef CONFIG_VIRTIO_SCSI case VIRTIO_ID_SCSI: features->dev_features = - CONVERT_FEATURES(strList, virtio_scsi_feature_map, 0, bitmap); + CONVERT_FEATURES_EX(strList, virtio_scsi_feature_map, bitmap); break; #endif #ifdef CONFIG_VIRTIO_BALLOON case VIRTIO_ID_BALLOON: features->dev_features = - CONVERT_FEATURES(strList, virtio_balloon_feature_map, 0, bitmap); + CONVERT_FEATURES_EX(strList, virtio_balloon_feature_map, bitmap); break; #endif #ifdef CONFIG_VIRTIO_IOMMU case VIRTIO_ID_IOMMU: features->dev_features = - CONVERT_FEATURES(strList, virtio_iommu_feature_map, 0, bitmap); + CONVERT_FEATURES_EX(strList, virtio_iommu_feature_map, bitmap); break; #endif #ifdef CONFIG_VIRTIO_INPUT case VIRTIO_ID_INPUT: features->dev_features = - CONVERT_FEATURES(strList, virtio_input_feature_map, 0, bitmap); + CONVERT_FEATURES_EX(strList, virtio_input_feature_map, bitmap); break; #endif #ifdef CONFIG_VHOST_USER_FS case VIRTIO_ID_FS: features->dev_features = - CONVERT_FEATURES(strList, virtio_fs_feature_map, 0, bitmap); + CONVERT_FEATURES_EX(strList, virtio_fs_feature_map, bitmap); break; #endif #ifdef CONFIG_VHOST_VSOCK case VIRTIO_ID_VSOCK: features->dev_features = - CONVERT_FEATURES(strList, virtio_vsock_feature_map, 0, bitmap); + CONVERT_FEATURES_EX(strList, virtio_vsock_feature_map, bitmap); break; #endif #ifdef CONFIG_VIRTIO_CRYPTO case VIRTIO_ID_CRYPTO: features->dev_features = - CONVERT_FEATURES(strList, virtio_crypto_feature_map, 0, bitmap); + CONVERT_FEATURES_EX(strList, virtio_crypto_feature_map, bitmap); break; #endif #ifdef CONFIG_VIRTIO_MEM case VIRTIO_ID_MEM: features->dev_features = - CONVERT_FEATURES(strList, virtio_mem_feature_map, 0, bitmap); + CONVERT_FEATURES_EX(strList, virtio_mem_feature_map, bitmap); break; #endif #ifdef CONFIG_VIRTIO_I2C_ADAPTER case VIRTIO_ID_I2C_ADAPTER: features->dev_features = - CONVERT_FEATURES(strList, virtio_i2c_feature_map, 0, bitmap); + CONVERT_FEATURES_EX(strList, virtio_i2c_feature_map, bitmap); break; #endif #ifdef CONFIG_VIRTIO_RNG case VIRTIO_ID_RNG: features->dev_features = - CONVERT_FEATURES(strList, virtio_rng_feature_map, 0, bitmap); + CONVERT_FEATURES_EX(strList, virtio_rng_feature_map, bitmap); break; #endif #ifdef CONFIG_VHOST_USER_GPIO case VIRTIO_ID_GPIO: features->dev_features = - CONVERT_FEATURES(strList, virtio_gpio_feature_map, 0, bitmap); + CONVERT_FEATURES_EX(strList, virtio_gpio_feature_map, bitmap); break; #endif /* No features */ @@ -680,10 +715,9 @@ VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap) g_assert_not_reached(); } - features->has_unknown_dev_features = bitmap != 0; - if (features->has_unknown_dev_features) { - features->unknown_dev_features = bitmap; - } + features->has_unknown_dev_features = !virtio_features_empty(bitmap); + features->unknown_dev_features = bitmap[0]; + features->unknown_dev_features2 = bitmap[1]; return features; } @@ -743,11 +777,11 @@ VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp) status->device_id = vdev->device_id; status->vhost_started = vdev->vhost_started; status->guest_features = qmp_decode_features(vdev->device_id, - vdev->guest_features); + vdev->guest_features_ex); status->host_features = qmp_decode_features(vdev->device_id, - vdev->host_features); + vdev->host_features_ex); status->backend_features = qmp_decode_features(vdev->device_id, - vdev->backend_features); + vdev->backend_features_ex); switch (vdev->device_endian) { case VIRTIO_DEVICE_ENDIAN_LITTLE: @@ -785,11 +819,12 @@ VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp) status->vhost_dev->nvqs = hdev->nvqs; status->vhost_dev->vq_index = hdev->vq_index; status->vhost_dev->features = - qmp_decode_features(vdev->device_id, hdev->features); + qmp_decode_features(vdev->device_id, hdev->features_ex); status->vhost_dev->acked_features = - qmp_decode_features(vdev->device_id, hdev->acked_features); + qmp_decode_features(vdev->device_id, hdev->acked_features_ex); status->vhost_dev->backend_features = - qmp_decode_features(vdev->device_id, hdev->backend_features); + qmp_decode_features(vdev->device_id, hdev->backend_features_ex); + status->vhost_dev->protocol_features = qmp_decode_protocols(hdev->protocol_features); status->vhost_dev->max_queues = hdev->max_queues; diff --git a/hw/virtio/virtio-qmp.h b/hw/virtio/virtio-qmp.h index 245a446a5675f..e0a1e49035e4b 100644 --- a/hw/virtio/virtio-qmp.h +++ b/hw/virtio/virtio-qmp.h @@ -18,6 +18,7 @@ VirtIODevice *qmp_find_virtio_device(const char *path); VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap); VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap); -VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap); +VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, + const uint64_t *bitmap); #endif diff --git a/qapi/virtio.json b/qapi/virtio.json index 9d652fe4a8c5d..05295ab66559e 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -247,6 +247,7 @@ # }, # "host-features": { # "unknown-dev-features": 1073741824, +# "unknown-dev-features2": 0, # "dev-features": [], # "transports": [ # "VIRTIO_RING_F_EVENT_IDX: Used & avail. event fields enabled", @@ -490,14 +491,18 @@ # unique features) # # @unknown-dev-features: Virtio device features bitmap that have not -# been decoded +# been decoded (bits 0-63) +# +# @unknown-dev-features2: Virtio device features bitmap that have not +# been decoded (bits 64-127) (since 10.2) # # Since: 7.2 ## { 'struct': 'VirtioDeviceFeatures', 'data': { 'transports': [ 'str' ], '*dev-features': [ 'str' ], - '*unknown-dev-features': 'uint64' } } + '*unknown-dev-features': 'uint64', + '*unknown-dev-features2': 'uint64' } } ## # @VirtQueueStatus: From f412c1f57ab58fd595efee26264193759220ca6f Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 22 Sep 2025 16:18:24 +0200 Subject: [PATCH 1198/1794] vhost-backend: implement extended features support Leverage the kernel extended features manipulation ioctls(), if available, and fallback to old ops otherwise. Error out when setting extended features but kernel support is not available. Note that extended support for get/set backend features is not needed, as the only feature that can be changed belongs to the 64 bit range. Reviewed-by: Akihiko Odaki Acked-by: Jason Wang Acked-by: Stefano Garzarella Signed-off-by: Paolo Abeni Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Message-ID: <150daade3d59e77629276920e014ee8e5fc12121.1758549625.git.pabeni@redhat.com> Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-backend.c | 62 ++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index 833804dd40f2e..4367db0d9511e 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -20,6 +20,11 @@ #include #include +struct vhost_features { + uint64_t count; + uint64_t features[VIRTIO_FEATURES_NU64S]; +}; + static int vhost_kernel_call(struct vhost_dev *dev, unsigned long int request, void *arg) { @@ -182,12 +187,6 @@ static int vhost_kernel_get_vring_worker(struct vhost_dev *dev, return vhost_kernel_call(dev, VHOST_GET_VRING_WORKER, worker); } -static int vhost_kernel_set_features(struct vhost_dev *dev, - uint64_t features) -{ - return vhost_kernel_call(dev, VHOST_SET_FEATURES, &features); -} - static int vhost_kernel_set_backend_cap(struct vhost_dev *dev) { uint64_t features; @@ -210,10 +209,51 @@ static int vhost_kernel_set_backend_cap(struct vhost_dev *dev) return 0; } -static int vhost_kernel_get_features(struct vhost_dev *dev, - uint64_t *features) +static int vhost_kernel_set_features(struct vhost_dev *dev, + const uint64_t *features) { - return vhost_kernel_call(dev, VHOST_GET_FEATURES, features); + struct vhost_features farray; + bool extended_in_use; + int r; + + farray.count = VIRTIO_FEATURES_NU64S; + virtio_features_copy(farray.features, features); + extended_in_use = virtio_features_use_ex(farray.features); + + /* + * Can't check for ENOTTY: for unknown ioctls the kernel interprets + * the argument as a virtio queue id and most likely errors out validating + * such id, instead of reporting an unknown operation. + */ + r = vhost_kernel_call(dev, VHOST_SET_FEATURES_ARRAY, &farray); + if (!r) { + return 0; + } + + if (extended_in_use) { + error_report("Trying to set extended features without kernel support"); + return -EINVAL; + } + return vhost_kernel_call(dev, VHOST_SET_FEATURES, &farray.features[0]); +} + +static int vhost_kernel_get_features(struct vhost_dev *dev, uint64_t *features) +{ + struct vhost_features farray; + int r; + + farray.count = VIRTIO_FEATURES_NU64S; + r = vhost_kernel_call(dev, VHOST_GET_FEATURES_ARRAY, &farray); + if (r) { + memset(&farray, 0, sizeof(farray)); + r = vhost_kernel_call(dev, VHOST_GET_FEATURES, &farray.features[0]); + } + if (r) { + return r; + } + + virtio_features_copy(features, farray.features); + return 0; } static int vhost_kernel_set_owner(struct vhost_dev *dev) @@ -341,8 +381,8 @@ const VhostOps kernel_ops = { .vhost_attach_vring_worker = vhost_kernel_attach_vring_worker, .vhost_new_worker = vhost_kernel_new_worker, .vhost_free_worker = vhost_kernel_free_worker, - .vhost_set_features = vhost_kernel_set_features, - .vhost_get_features = vhost_kernel_get_features, + .vhost_set_features_ex = vhost_kernel_set_features, + .vhost_get_features_ex = vhost_kernel_get_features, .vhost_set_backend_cap = vhost_kernel_set_backend_cap, .vhost_set_owner = vhost_kernel_set_owner, .vhost_get_vq_index = vhost_kernel_get_vq_index, From d55ad8c9a9a5edd8152f13fc97879d66972f103e Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 22 Sep 2025 16:18:25 +0200 Subject: [PATCH 1199/1794] vhost-net: implement extended features support Provide extended version of the features manipulation helpers, and let the device initialization deal with the full features space, adjusting the relevant format strings accordingly. Reviewed-by: Akihiko Odaki Acked-by: Jason Wang Signed-off-by: Paolo Abeni Tested-by: Lei Yang Acked-by: Stefano Garzarella Reviewed-by: Michael S. Tsirkin Message-ID: <69c78c432e28e146a8874b2a7d00e9cbd111b1ba.1758549625.git.pabeni@redhat.com> Signed-off-by: Michael S. Tsirkin --- hw/net/vhost_net-stub.c | 8 +++----- hw/net/vhost_net.c | 45 +++++++++++++++++++++++------------------ include/net/vhost_net.h | 33 +++++++++++++++++++++++++++--- 3 files changed, 58 insertions(+), 28 deletions(-) diff --git a/hw/net/vhost_net-stub.c b/hw/net/vhost_net-stub.c index 7d49f82906a9b..0740d5a2ebe6a 100644 --- a/hw/net/vhost_net-stub.c +++ b/hw/net/vhost_net-stub.c @@ -46,9 +46,8 @@ void vhost_net_cleanup(struct vhost_net *net) { } -uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features) +void vhost_net_get_features_ex(struct vhost_net *net, uint64_t *features) { - return features; } int vhost_net_get_config(struct vhost_net *net, uint8_t *config, @@ -62,13 +61,12 @@ int vhost_net_set_config(struct vhost_net *net, const uint8_t *data, return 0; } -void vhost_net_ack_features(struct vhost_net *net, uint64_t features) +void vhost_net_ack_features_ex(struct vhost_net *net, const uint64_t *features) { } -uint64_t vhost_net_get_acked_features(VHostNetState *net) +void vhost_net_get_acked_features_ex(VHostNetState *net, uint64_t *features) { - return 0; } bool vhost_net_virtqueue_pending(VHostNetState *net, int idx) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 540492b37ddca..a8ee18a912661 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -35,10 +35,9 @@ #include "hw/virtio/virtio-bus.h" #include "linux-headers/linux/vhost.h" -uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features) +void vhost_net_get_features_ex(struct vhost_net *net, uint64_t *features) { - return vhost_get_features(&net->dev, net->feature_bits, - features); + vhost_get_features_ex(&net->dev, net->feature_bits, features); } int vhost_net_get_config(struct vhost_net *net, uint8_t *config, uint32_t config_len) @@ -51,10 +50,11 @@ int vhost_net_set_config(struct vhost_net *net, const uint8_t *data, return vhost_dev_set_config(&net->dev, data, offset, size, flags); } -void vhost_net_ack_features(struct vhost_net *net, uint64_t features) +void vhost_net_ack_features_ex(struct vhost_net *net, const uint64_t *features) { - net->dev.acked_features = net->dev.backend_features; - vhost_ack_features(&net->dev, net->feature_bits, features); + virtio_features_copy(net->dev.acked_features_ex, + net->dev.backend_features_ex); + vhost_ack_features_ex(&net->dev, net->feature_bits, features); } uint64_t vhost_net_get_max_queues(VHostNetState *net) @@ -62,9 +62,9 @@ uint64_t vhost_net_get_max_queues(VHostNetState *net) return net->dev.max_queues; } -uint64_t vhost_net_get_acked_features(VHostNetState *net) +void vhost_net_get_acked_features_ex(VHostNetState *net, uint64_t *features) { - return net->dev.acked_features; + virtio_features_copy(features, net->dev.acked_features_ex); } void vhost_net_save_acked_features(NetClientState *nc) @@ -234,7 +234,8 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) int r; bool backend_kernel = options->backend_type == VHOST_BACKEND_TYPE_KERNEL; struct vhost_net *net = g_new0(struct vhost_net, 1); - uint64_t features = 0; + uint64_t missing_features[VIRTIO_FEATURES_NU64S]; + uint64_t features[VIRTIO_FEATURES_NU64S]; Error *local_err = NULL; if (!options->net_backend) { @@ -247,6 +248,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) net->save_acked_features = options->save_acked_features; net->max_tx_queue_size = options->max_tx_queue_size; net->is_vhost_user = options->is_vhost_user; + virtio_features_clear(features); net->dev.max_queues = 1; net->dev.vqs = net->vqs; @@ -261,7 +263,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) net->backend = r; net->dev.protocol_features = 0; } else { - net->dev.backend_features = 0; + virtio_features_clear(net->dev.backend_features_ex); net->dev.protocol_features = 0; net->backend = -1; @@ -281,26 +283,29 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) sizeof(struct virtio_net_hdr_mrg_rxbuf))) { net->dev.features &= ~(1ULL << VIRTIO_NET_F_MRG_RXBUF); } - if (~net->dev.features & net->dev.backend_features) { - fprintf(stderr, "vhost lacks feature mask 0x%" PRIx64 - " for backend\n", - (uint64_t)(~net->dev.features & net->dev.backend_features)); + + if (virtio_features_andnot(missing_features, + net->dev.backend_features_ex, + net->dev.features_ex)) { + fprintf(stderr, "vhost lacks feature mask 0x" VIRTIO_FEATURES_FMT + " for backend\n", VIRTIO_FEATURES_PR(missing_features)); goto fail; } } /* Set sane init value. Override when guest acks. */ if (options->get_acked_features) { - features = options->get_acked_features(net->nc); - if (~net->dev.features & features) { - fprintf(stderr, "vhost lacks feature mask 0x%" PRIx64 - " for backend\n", - (uint64_t)(~net->dev.features & features)); + virtio_features_from_u64(features, + options->get_acked_features(net->nc)); + if (virtio_features_andnot(missing_features, features, + net->dev.features_ex)) { + fprintf(stderr, "vhost lacks feature mask 0x" VIRTIO_FEATURES_FMT + " for backend\n", VIRTIO_FEATURES_PR(missing_features)); goto fail; } } - vhost_net_ack_features(net, features); + vhost_net_ack_features_ex(net, features); return net; diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index 879781dad7b63..0225207491287 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -2,6 +2,7 @@ #define VHOST_NET_H #include "net/net.h" +#include "hw/virtio/virtio-features.h" #include "hw/virtio/vhost-backend.h" struct vhost_net; @@ -33,8 +34,26 @@ void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, void vhost_net_cleanup(VHostNetState *net); -uint64_t vhost_net_get_features(VHostNetState *net, uint64_t features); -void vhost_net_ack_features(VHostNetState *net, uint64_t features); +void vhost_net_get_features_ex(VHostNetState *net, uint64_t *features); +static inline uint64_t vhost_net_get_features(VHostNetState *net, + uint64_t features) +{ + uint64_t features_array[VIRTIO_FEATURES_NU64S]; + + virtio_features_from_u64(features_array, features); + vhost_net_get_features_ex(net, features_array); + return features_array[0]; +} + +void vhost_net_ack_features_ex(VHostNetState *net, const uint64_t *features); +static inline void vhost_net_ack_features(VHostNetState *net, + uint64_t features) +{ + uint64_t features_array[VIRTIO_FEATURES_NU64S]; + + virtio_features_from_u64(features_array, features); + vhost_net_ack_features_ex(net, features_array); +} int vhost_net_get_config(struct vhost_net *net, uint8_t *config, uint32_t config_len); @@ -51,7 +70,15 @@ VHostNetState *get_vhost_net(NetClientState *nc); int vhost_net_set_vring_enable(NetClientState *nc, int enable); -uint64_t vhost_net_get_acked_features(VHostNetState *net); +void vhost_net_get_acked_features_ex(VHostNetState *net, uint64_t *features); +static inline uint64_t vhost_net_get_acked_features(VHostNetState *net) +{ + uint64_t features[VIRTIO_FEATURES_NU64S]; + + vhost_net_get_acked_features_ex(net, features); + assert(!virtio_features_use_ex(features)); + return features[0]; +} int vhost_net_set_mtu(struct vhost_net *net, uint16_t mtu); From 3a7741c3bdc3537de4159418d712debbd22e4df6 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 22 Sep 2025 16:18:26 +0200 Subject: [PATCH 1200/1794] virtio-net: implement extended features support Use the extended types and helpers to manipulate the virtio_net features. Note that offloads are still 64bits wide, as per specification, and extended offloads will be mapped into such range. Reviewed-by: Akihiko Odaki Acked-by: Jason Wang Signed-off-by: Paolo Abeni Tested-by: Lei Yang Acked-by: Stefano Garzarella Reviewed-by: Michael S. Tsirkin Message-ID: Signed-off-by: Michael S. Tsirkin --- hw/net/virtio-net.c | 140 +++++++++++++++++++-------------- include/hw/virtio/virtio-net.h | 2 +- 2 files changed, 83 insertions(+), 59 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 129bebd82e22b..39f2a8c9879e2 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -90,6 +90,19 @@ VIRTIO_NET_RSS_HASH_TYPE_TCP_EX | \ VIRTIO_NET_RSS_HASH_TYPE_UDP_EX) +/* + * Features starting from VIRTIO_NET_FEATURES_MAP_MIN bit correspond + * to guest offloads in the VIRTIO_NET_OFFLOAD_MAP range + */ +#define VIRTIO_NET_OFFLOAD_MAP_MIN 46 +#define VIRTIO_NET_OFFLOAD_MAP_LENGTH 4 +#define VIRTIO_NET_OFFLOAD_MAP MAKE_64BIT_MASK( \ + VIRTIO_NET_OFFLOAD_MAP_MIN, \ + VIRTIO_NET_OFFLOAD_MAP_LENGTH) +#define VIRTIO_NET_FEATURES_MAP_MIN 65 +#define VIRTIO_NET_F2O_SHIFT (VIRTIO_NET_OFFLOAD_MAP_MIN - \ + VIRTIO_NET_FEATURES_MAP_MIN + 64) + static const VirtIOFeature feature_sizes[] = { {.flags = 1ULL << VIRTIO_NET_F_MAC, .end = endof(struct virtio_net_config, mac)}, @@ -786,7 +799,14 @@ static void virtio_net_apply_guest_offloads(VirtIONet *n) qemu_set_offload(qemu_get_queue(n->nic)->peer, &ol); } -static uint64_t virtio_net_guest_offloads_by_features(uint64_t features) +static uint64_t virtio_net_features_to_offload(const uint64_t *features) +{ + return (features[0] & ~VIRTIO_NET_OFFLOAD_MAP) | + ((features[1] << VIRTIO_NET_F2O_SHIFT) & VIRTIO_NET_OFFLOAD_MAP); +} + +static uint64_t +virtio_net_guest_offloads_by_features(const uint64_t *features) { static const uint64_t guest_offloads_mask = (1ULL << VIRTIO_NET_F_GUEST_CSUM) | @@ -797,13 +817,13 @@ static uint64_t virtio_net_guest_offloads_by_features(uint64_t features) (1ULL << VIRTIO_NET_F_GUEST_USO4) | (1ULL << VIRTIO_NET_F_GUEST_USO6); - return guest_offloads_mask & features; + return guest_offloads_mask & virtio_net_features_to_offload(features); } uint64_t virtio_net_supported_guest_offloads(const VirtIONet *n) { VirtIODevice *vdev = VIRTIO_DEVICE(n); - return virtio_net_guest_offloads_by_features(vdev->guest_features); + return virtio_net_guest_offloads_by_features(vdev->guest_features_ex); } typedef struct { @@ -882,34 +902,39 @@ static void failover_add_primary(VirtIONet *n, Error **errp) error_propagate(errp, err); } -static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) +static void virtio_net_set_features(VirtIODevice *vdev, + const uint64_t *in_features) { + uint64_t features[VIRTIO_FEATURES_NU64S]; VirtIONet *n = VIRTIO_NET(vdev); Error *err = NULL; int i; + virtio_features_copy(features, in_features); if (n->mtu_bypass_backend && !virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_MTU)) { - features &= ~(1ULL << VIRTIO_NET_F_MTU); + virtio_clear_feature_ex(features, VIRTIO_NET_F_MTU); } virtio_net_set_multiqueue(n, - virtio_has_feature(features, VIRTIO_NET_F_RSS) || - virtio_has_feature(features, VIRTIO_NET_F_MQ)); + virtio_has_feature_ex(features, + VIRTIO_NET_F_RSS) || + virtio_has_feature_ex(features, + VIRTIO_NET_F_MQ)); virtio_net_set_mrg_rx_bufs(n, - virtio_has_feature(features, + virtio_has_feature_ex(features, VIRTIO_NET_F_MRG_RXBUF), - virtio_has_feature(features, + virtio_has_feature_ex(features, VIRTIO_F_VERSION_1), - virtio_has_feature(features, + virtio_has_feature_ex(features, VIRTIO_NET_F_HASH_REPORT)); - n->rsc4_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) && - virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO4); - n->rsc6_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) && - virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO6); - n->rss_data.redirect = virtio_has_feature(features, VIRTIO_NET_F_RSS); + n->rsc4_enabled = virtio_has_feature_ex(features, VIRTIO_NET_F_RSC_EXT) && + virtio_has_feature_ex(features, VIRTIO_NET_F_GUEST_TSO4); + n->rsc6_enabled = virtio_has_feature_ex(features, VIRTIO_NET_F_RSC_EXT) && + virtio_has_feature_ex(features, VIRTIO_NET_F_GUEST_TSO6); + n->rss_data.redirect = virtio_has_feature_ex(features, VIRTIO_NET_F_RSS); if (n->has_vnet_hdr) { n->curr_guest_offloads = @@ -923,7 +948,7 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) if (!get_vhost_net(nc->peer)) { continue; } - vhost_net_ack_features(get_vhost_net(nc->peer), features); + vhost_net_ack_features_ex(get_vhost_net(nc->peer), features); /* * keep acked_features in NetVhostUserState up-to-date so it @@ -932,12 +957,14 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) vhost_net_save_acked_features(nc->peer); } - if (virtio_has_feature(vdev->guest_features ^ features, VIRTIO_NET_F_CTRL_VLAN)) { - bool vlan = virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN); + if (virtio_has_feature_ex(features, VIRTIO_NET_F_CTRL_VLAN) != + virtio_has_feature_ex(vdev->guest_features_ex, + VIRTIO_NET_F_CTRL_VLAN)) { + bool vlan = virtio_has_feature_ex(features, VIRTIO_NET_F_CTRL_VLAN); memset(n->vlans, vlan ? 0 : 0xff, MAX_VLAN >> 3); } - if (virtio_has_feature(features, VIRTIO_NET_F_STANDBY)) { + if (virtio_has_feature_ex(features, VIRTIO_NET_F_STANDBY)) { qapi_event_send_failover_negotiated(n->netclient_name); qatomic_set(&n->failover_primary_hidden, false); failover_add_primary(n, &err); @@ -1908,10 +1935,10 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, virtio_error(vdev, "virtio-net unexpected empty queue: " "i %zd mergeable %d offset %zd, size %zd, " "guest hdr len %zd, host hdr len %zd " - "guest features 0x%" PRIx64, + "guest features 0x" VIRTIO_FEATURES_FMT, i, n->mergeable_rx_bufs, offset, size, n->guest_hdr_len, n->host_hdr_len, - vdev->guest_features); + VIRTIO_FEATURES_PR(vdev->guest_features_ex)); } err = -1; goto err; @@ -3018,8 +3045,8 @@ static int virtio_net_pre_load_queues(VirtIODevice *vdev, uint32_t n) return 0; } -static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, - Error **errp) +static void virtio_net_get_features(VirtIODevice *vdev, uint64_t *features, + Error **errp) { VirtIONet *n = VIRTIO_NET(vdev); NetClientState *nc = qemu_get_queue(n->nic); @@ -3033,68 +3060,67 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, (supported_hash_types & peer_hash_types) == supported_hash_types; /* Firstly sync all virtio-net possible supported features */ - features |= n->host_features; + virtio_features_or(features, features, n->host_features_ex); - virtio_add_feature(&features, VIRTIO_NET_F_MAC); + virtio_add_feature_ex(features, VIRTIO_NET_F_MAC); if (!peer_has_vnet_hdr(n)) { - virtio_clear_feature(&features, VIRTIO_NET_F_CSUM); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO4); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO6); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_ECN); + virtio_clear_feature_ex(features, VIRTIO_NET_F_CSUM); + virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_TSO4); + virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_TSO6); + virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_ECN); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_CSUM); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO4); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN); + virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_CSUM); + virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_TSO4); + virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_TSO6); + virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_ECN); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_USO); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO4); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO6); + virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_USO); + virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO4); + virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO6); - virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT); + virtio_clear_feature_ex(features, VIRTIO_NET_F_HASH_REPORT); } if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) { - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_UFO); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO); + virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_UFO); + virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_UFO); } - if (!peer_has_uso(n)) { - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_USO); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO4); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO6); + virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_USO); + virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO4); + virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO6); } if (!get_vhost_net(nc->peer)) { if (!use_own_hash) { - virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT); - virtio_clear_feature(&features, VIRTIO_NET_F_RSS); - } else if (virtio_has_feature(features, VIRTIO_NET_F_RSS)) { + virtio_clear_feature_ex(features, VIRTIO_NET_F_HASH_REPORT); + virtio_clear_feature_ex(features, VIRTIO_NET_F_RSS); + } else if (virtio_has_feature_ex(features, VIRTIO_NET_F_RSS)) { virtio_net_load_ebpf(n, errp); } - return features; + return; } if (!use_peer_hash) { - virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT); + virtio_clear_feature_ex(features, VIRTIO_NET_F_HASH_REPORT); if (!use_own_hash || !virtio_net_attach_ebpf_to_backend(n->nic, -1)) { if (!virtio_net_load_ebpf(n, errp)) { - return features; + return; } - virtio_clear_feature(&features, VIRTIO_NET_F_RSS); + virtio_clear_feature_ex(features, VIRTIO_NET_F_RSS); } } - features = vhost_net_get_features(get_vhost_net(nc->peer), features); - vdev->backend_features = features; + vhost_net_get_features_ex(get_vhost_net(nc->peer), features); + virtio_features_copy(vdev->backend_features_ex, features); if (n->mtu_bypass_backend && (n->host_features & 1ULL << VIRTIO_NET_F_MTU)) { - features |= (1ULL << VIRTIO_NET_F_MTU); + virtio_add_feature_ex(features, VIRTIO_NET_F_MTU); } /* @@ -3109,10 +3135,8 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, * support it. */ if (!virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_CTRL_VQ)) { - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ANNOUNCE); + virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_ANNOUNCE); } - - return features; } static int virtio_net_post_load_device(void *opaque, int version_id) @@ -4244,8 +4268,8 @@ static void virtio_net_class_init(ObjectClass *klass, const void *data) vdc->unrealize = virtio_net_device_unrealize; vdc->get_config = virtio_net_get_config; vdc->set_config = virtio_net_set_config; - vdc->get_features = virtio_net_get_features; - vdc->set_features = virtio_net_set_features; + vdc->get_features_ex = virtio_net_get_features; + vdc->set_features_ex = virtio_net_set_features; vdc->bad_features = virtio_net_bad_features; vdc->reset = virtio_net_reset; vdc->queue_reset = virtio_net_queue_reset; diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index 73fdefc0dcb89..5b8ab7bda796c 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -182,7 +182,7 @@ struct VirtIONet { uint32_t has_vnet_hdr; size_t host_hdr_len; size_t guest_hdr_len; - uint64_t host_features; + VIRTIO_DECLARE_FEATURES(host_features); uint32_t rsc_timeout; uint8_t rsc4_enabled; uint8_t rsc6_enabled; From fffac046282c99801b62fa7fa1032cdc261bca6d Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 22 Sep 2025 16:18:27 +0200 Subject: [PATCH 1201/1794] net: implement tunnel probing Tap devices support GSO over UDP tunnel offload. Probe for such feature in a similar manner to other offloads. GSO over UDP tunnel needs to be enabled in addition to a "plain" offload (TSO or USO). No need to check separately for the outer header checksum offload: the kernel is going to support both of them or none. The new features are disabled by default to avoid compat issues, and could be enabled, after that hw_compat_10_1 will be added, together with the related compat entries. Reviewed-by: Akihiko Odaki Acked-by: Jason Wang Signed-off-by: Paolo Abeni Tested-by: Lei Yang Acked-by: Stefano Garzarella Reviewed-by: Michael S. Tsirkin Message-ID: Signed-off-by: Michael S. Tsirkin --- hw/net/virtio-net.c | 41 +++++++++++++++++++++++++++++++++++++++++ include/net/net.h | 3 +++ net/net.c | 9 +++++++++ net/tap-bsd.c | 5 +++++ net/tap-linux.c | 11 +++++++++++ net/tap-linux.h | 9 +++++++++ net/tap-solaris.c | 5 +++++ net/tap-stub.c | 5 +++++ net/tap.c | 11 +++++++++++ net/tap_int.h | 1 + 10 files changed, 100 insertions(+) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 39f2a8c9879e2..f8e2b4823ea48 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -649,6 +649,15 @@ static int peer_has_uso(VirtIONet *n) return qemu_has_uso(qemu_get_queue(n->nic)->peer); } +static bool peer_has_tunnel(VirtIONet *n) +{ + if (!peer_has_vnet_hdr(n)) { + return false; + } + + return qemu_has_tunnel(qemu_get_queue(n->nic)->peer); +} + static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, int version_1, int hash_report) { @@ -3079,6 +3088,13 @@ static void virtio_net_get_features(VirtIODevice *vdev, uint64_t *features, virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO4); virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO6); + virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO); + virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO); + virtio_clear_feature_ex(features, + VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM); + virtio_clear_feature_ex(features, + VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM); + virtio_clear_feature_ex(features, VIRTIO_NET_F_HASH_REPORT); } @@ -3092,6 +3108,15 @@ static void virtio_net_get_features(VirtIODevice *vdev, uint64_t *features, virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_USO6); } + if (!peer_has_tunnel(n)) { + virtio_clear_feature_ex(features, VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO); + virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO); + virtio_clear_feature_ex(features, + VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM); + virtio_clear_feature_ex(features, + VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM); + } + if (!get_vhost_net(nc->peer)) { if (!use_own_hash) { virtio_clear_feature_ex(features, VIRTIO_NET_F_HASH_REPORT); @@ -4254,6 +4279,22 @@ static const Property virtio_net_properties[] = { rss_data.specified_hash_types, VIRTIO_NET_HASH_REPORT_UDPv6_EX - 1, ON_OFF_AUTO_AUTO), + VIRTIO_DEFINE_PROP_FEATURE("host_tunnel", VirtIONet, + host_features_ex, + VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO, + false), + VIRTIO_DEFINE_PROP_FEATURE("host_tunnel_csum", VirtIONet, + host_features_ex, + VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM, + false), + VIRTIO_DEFINE_PROP_FEATURE("guest_tunnel", VirtIONet, + host_features_ex, + VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO, + false), + VIRTIO_DEFINE_PROP_FEATURE("guest_tunnel_csum", VirtIONet, + host_features_ex, + VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM, + false), }; static void virtio_net_class_init(ObjectClass *klass, const void *data) diff --git a/include/net/net.h b/include/net/net.h index 48ba333d0276f..9a9084690db46 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -65,6 +65,7 @@ typedef void (NetClientDestructor)(NetClientState *); typedef RxFilterInfo *(QueryRxFilter)(NetClientState *); typedef bool (HasUfo)(NetClientState *); typedef bool (HasUso)(NetClientState *); +typedef bool (HasTunnel)(NetClientState *); typedef bool (HasVnetHdr)(NetClientState *); typedef bool (HasVnetHdrLen)(NetClientState *, int); typedef void (SetOffload)(NetClientState *, const NetOffloads *); @@ -95,6 +96,7 @@ typedef struct NetClientInfo { NetPoll *poll; HasUfo *has_ufo; HasUso *has_uso; + HasTunnel *has_tunnel; HasVnetHdr *has_vnet_hdr; HasVnetHdrLen *has_vnet_hdr_len; SetOffload *set_offload; @@ -197,6 +199,7 @@ void qemu_set_info_str(NetClientState *nc, void qemu_format_nic_info_str(NetClientState *nc, uint8_t macaddr[6]); bool qemu_has_ufo(NetClientState *nc); bool qemu_has_uso(NetClientState *nc); +bool qemu_has_tunnel(NetClientState *nc); bool qemu_has_vnet_hdr(NetClientState *nc); bool qemu_has_vnet_hdr_len(NetClientState *nc, int len); void qemu_set_offload(NetClientState *nc, const NetOffloads *ol); diff --git a/net/net.c b/net/net.c index 63872b6855609..9536184a0cf4a 100644 --- a/net/net.c +++ b/net/net.c @@ -522,6 +522,15 @@ bool qemu_has_uso(NetClientState *nc) return nc->info->has_uso(nc); } +bool qemu_has_tunnel(NetClientState *nc) +{ + if (!nc || !nc->info->has_tunnel) { + return false; + } + + return nc->info->has_tunnel(nc); +} + bool qemu_has_vnet_hdr(NetClientState *nc) { if (!nc || !nc->info->has_vnet_hdr) { diff --git a/net/tap-bsd.c b/net/tap-bsd.c index 38eb28cb5878c..bbf84d1828d34 100644 --- a/net/tap-bsd.c +++ b/net/tap-bsd.c @@ -225,6 +225,11 @@ int tap_probe_has_uso(int fd) return 0; } +bool tap_probe_has_tunnel(int fd) +{ + return false; +} + void tap_fd_set_vnet_hdr_len(int fd, int len) { } diff --git a/net/tap-linux.c b/net/tap-linux.c index 79a9dd0da01de..98b0ae9602d09 100644 --- a/net/tap-linux.c +++ b/net/tap-linux.c @@ -201,6 +201,17 @@ int tap_probe_has_uso(int fd) return 1; } +bool tap_probe_has_tunnel(int fd) +{ + unsigned offload; + + offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_UDP_TUNNEL_GSO; + if (ioctl(fd, TUNSETOFFLOAD, offload) < 0) { + return false; + } + return true; +} + void tap_fd_set_vnet_hdr_len(int fd, int len) { if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) { diff --git a/net/tap-linux.h b/net/tap-linux.h index 9a58cecb7f479..8cd6b5874b988 100644 --- a/net/tap-linux.h +++ b/net/tap-linux.h @@ -53,4 +53,13 @@ #define TUN_F_USO4 0x20 /* I can handle USO for IPv4 packets */ #define TUN_F_USO6 0x40 /* I can handle USO for IPv6 packets */ +/* I can handle TSO/USO for UDP tunneled packets */ +#define TUN_F_UDP_TUNNEL_GSO 0x080 + +/* + * I can handle TSO/USO for UDP tunneled packets requiring csum offload for + * the outer header + */ +#define TUN_F_UDP_TUNNEL_GSO_CSUM 0x100 + #endif /* QEMU_TAP_LINUX_H */ diff --git a/net/tap-solaris.c b/net/tap-solaris.c index ef8e0feeb0f71..75397e6c5447b 100644 --- a/net/tap-solaris.c +++ b/net/tap-solaris.c @@ -227,6 +227,11 @@ int tap_probe_has_uso(int fd) return 0; } +bool tap_probe_has_tunnel(int fd) +{ + return false; +} + void tap_fd_set_vnet_hdr_len(int fd, int len) { } diff --git a/net/tap-stub.c b/net/tap-stub.c index 67d14ad4d5ddf..f7a5e0c1632e4 100644 --- a/net/tap-stub.c +++ b/net/tap-stub.c @@ -52,6 +52,11 @@ int tap_probe_has_uso(int fd) return 0; } +bool tap_probe_has_tunnel(int fd) +{ + return false; +} + void tap_fd_set_vnet_hdr_len(int fd, int len) { } diff --git a/net/tap.c b/net/tap.c index df232837446d7..5124372316834 100644 --- a/net/tap.c +++ b/net/tap.c @@ -76,6 +76,7 @@ typedef struct TAPState { bool using_vnet_hdr; bool has_ufo; bool has_uso; + bool has_tunnel; bool enabled; VHostNetState *vhost_net; unsigned host_vnet_hdr_len; @@ -246,6 +247,14 @@ static bool tap_has_uso(NetClientState *nc) return s->has_uso; } +static bool tap_has_tunnel(NetClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + + assert(nc->info->type == NET_CLIENT_DRIVER_TAP); + return s->has_tunnel; +} + static bool tap_has_vnet_hdr(NetClientState *nc) { TAPState *s = DO_UPCAST(TAPState, nc, nc); @@ -374,6 +383,7 @@ static NetClientInfo net_tap_info = { .cleanup = tap_cleanup, .has_ufo = tap_has_ufo, .has_uso = tap_has_uso, + .has_tunnel = tap_has_tunnel, .has_vnet_hdr = tap_has_vnet_hdr, .has_vnet_hdr_len = tap_has_vnet_hdr_len, .set_offload = tap_set_offload, @@ -403,6 +413,7 @@ static TAPState *net_tap_fd_init(NetClientState *peer, s->using_vnet_hdr = false; s->has_ufo = tap_probe_has_ufo(s->fd); s->has_uso = tap_probe_has_uso(s->fd); + s->has_tunnel = tap_probe_has_tunnel(s->fd); s->enabled = true; tap_set_offload(&s->nc, &ol); /* diff --git a/net/tap_int.h b/net/tap_int.h index f8bbe1cb0c6d3..b76a05044bc5a 100644 --- a/net/tap_int.h +++ b/net/tap_int.h @@ -38,6 +38,7 @@ void tap_set_sndbuf(int fd, const NetdevTapOptions *tap, Error **errp); int tap_probe_vnet_hdr(int fd, Error **errp); int tap_probe_has_ufo(int fd); int tap_probe_has_uso(int fd); +bool tap_probe_has_tunnel(int fd); void tap_fd_set_offload(int fd, const NetOffloads *ol); void tap_fd_set_vnet_hdr_len(int fd, int len); int tap_fd_set_vnet_le(int fd, int vnet_is_le); From a5289563ad74a2a37e8d2101d82935454c71fef4 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 22 Sep 2025 16:18:28 +0200 Subject: [PATCH 1202/1794] net: implement UDP tunnel features offloading When any host or guest GSO over UDP tunnel offload is enabled the virtio net header includes the additional tunnel-related fields, update the size accordingly. Push the GSO over UDP tunnel offloads all the way down to the tap device extending the newly introduced NetFeatures struct, and eventually enable the associated features. As per virtio specification, to convert features bit to offload bit, map the extended features into the reserved range. Finally, make the vhost backend aware of the exact header layout, to copy it correctly. The tunnel-related field are present if either the guest or the host negotiated any UDP tunnel related feature: add them to the kernel supported features list, to allow qemu transfer to the backend the needed information. Reviewed-by: Akihiko Odaki Acked-by: Jason Wang Signed-off-by: Paolo Abeni Tested-by: Lei Yang Acked-by: Stefano Garzarella Reviewed-by: Michael S. Tsirkin Message-ID: <093b4bc68368046bffbcab2202227632d6e4e83b.1758549625.git.pabeni@redhat.com> Signed-off-by: Michael S. Tsirkin --- hw/net/virtio-net.c | 34 ++++++++++++++++++++++++++-------- include/net/net.h | 2 ++ net/net.c | 3 ++- net/tap-linux.c | 6 ++++++ net/tap.c | 2 ++ 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index f8e2b4823ea48..33116712eb4ab 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -103,6 +103,12 @@ #define VIRTIO_NET_F2O_SHIFT (VIRTIO_NET_OFFLOAD_MAP_MIN - \ VIRTIO_NET_FEATURES_MAP_MIN + 64) +static bool virtio_has_tunnel_hdr(const uint64_t *features) +{ + return virtio_has_feature_ex(features, VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO) || + virtio_has_feature_ex(features, VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO); +} + static const VirtIOFeature feature_sizes[] = { {.flags = 1ULL << VIRTIO_NET_F_MAC, .end = endof(struct virtio_net_config, mac)}, @@ -659,7 +665,8 @@ static bool peer_has_tunnel(VirtIONet *n) } static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, - int version_1, int hash_report) + int version_1, int hash_report, + int tunnel) { int i; NetClientState *nc; @@ -667,9 +674,11 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, n->mergeable_rx_bufs = mergeable_rx_bufs; if (version_1) { - n->guest_hdr_len = hash_report ? - sizeof(struct virtio_net_hdr_v1_hash) : - sizeof(struct virtio_net_hdr_mrg_rxbuf); + n->guest_hdr_len = tunnel ? + sizeof(struct virtio_net_hdr_v1_hash_tunnel) : + (hash_report ? + sizeof(struct virtio_net_hdr_v1_hash) : + sizeof(struct virtio_net_hdr_mrg_rxbuf)); n->rss_data.populate_hash = !!hash_report; } else { n->guest_hdr_len = n->mergeable_rx_bufs ? @@ -803,6 +812,10 @@ static void virtio_net_apply_guest_offloads(VirtIONet *n) .ufo = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO)), .uso4 = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_USO4)), .uso6 = !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_USO6)), + .tnl = !!(n->curr_guest_offloads & + (1ULL << VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_MAPPED)), + .tnl_csum = !!(n->curr_guest_offloads & + (1ULL << VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM_MAPPED)), }; qemu_set_offload(qemu_get_queue(n->nic)->peer, &ol); @@ -824,7 +837,9 @@ virtio_net_guest_offloads_by_features(const uint64_t *features) (1ULL << VIRTIO_NET_F_GUEST_ECN) | (1ULL << VIRTIO_NET_F_GUEST_UFO) | (1ULL << VIRTIO_NET_F_GUEST_USO4) | - (1ULL << VIRTIO_NET_F_GUEST_USO6); + (1ULL << VIRTIO_NET_F_GUEST_USO6) | + (1ULL << VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_MAPPED) | + (1ULL << VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM_MAPPED); return guest_offloads_mask & virtio_net_features_to_offload(features); } @@ -937,7 +952,8 @@ static void virtio_net_set_features(VirtIODevice *vdev, virtio_has_feature_ex(features, VIRTIO_F_VERSION_1), virtio_has_feature_ex(features, - VIRTIO_NET_F_HASH_REPORT)); + VIRTIO_NET_F_HASH_REPORT), + virtio_has_tunnel_hdr(features)); n->rsc4_enabled = virtio_has_feature_ex(features, VIRTIO_NET_F_RSC_EXT) && virtio_has_feature_ex(features, VIRTIO_NET_F_GUEST_TSO4); @@ -3169,13 +3185,15 @@ static int virtio_net_post_load_device(void *opaque, int version_id) VirtIONet *n = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(n); int i, link_down; + bool has_tunnel_hdr = virtio_has_tunnel_hdr(vdev->guest_features_ex); trace_virtio_net_post_load_device(); virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs, virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1), virtio_vdev_has_feature(vdev, - VIRTIO_NET_F_HASH_REPORT)); + VIRTIO_NET_F_HASH_REPORT), + has_tunnel_hdr); /* MAC_TABLE_ENTRIES may be different from the saved image */ if (n->mac_table.in_use > MAC_TABLE_ENTRIES) { @@ -3995,7 +4013,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) n->vqs[0].tx_waiting = 0; n->tx_burst = n->net_conf.txburst; - virtio_net_set_mrg_rx_bufs(n, 0, 0, 0); + virtio_net_set_mrg_rx_bufs(n, 0, 0, 0, 0); n->promisc = 1; /* for compatibility */ n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); diff --git a/include/net/net.h b/include/net/net.h index 9a9084690db46..72b476ee1dc49 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -43,6 +43,8 @@ typedef struct NetOffloads { bool ufo; bool uso4; bool uso6; + bool tnl; + bool tnl_csum; } NetOffloads; #define DEFINE_NIC_PROPERTIES(_state, _conf) \ diff --git a/net/net.c b/net/net.c index 9536184a0cf4a..27e0d27807166 100644 --- a/net/net.c +++ b/net/net.c @@ -575,7 +575,8 @@ void qemu_set_vnet_hdr_len(NetClientState *nc, int len) assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) || len == sizeof(struct virtio_net_hdr) || - len == sizeof(struct virtio_net_hdr_v1_hash)); + len == sizeof(struct virtio_net_hdr_v1_hash) || + len == sizeof(struct virtio_net_hdr_v1_hash_tunnel)); nc->vnet_hdr_len = len; nc->info->set_vnet_hdr_len(nc, len); diff --git a/net/tap-linux.c b/net/tap-linux.c index 98b0ae9602d09..2a90b584678e9 100644 --- a/net/tap-linux.c +++ b/net/tap-linux.c @@ -284,6 +284,12 @@ void tap_fd_set_offload(int fd, const NetOffloads *ol) if (ol->uso6) { offload |= TUN_F_USO6; } + if (ol->tnl) { + offload |= TUN_F_UDP_TUNNEL_GSO; + } + if (ol->tnl_csum) { + offload |= TUN_F_UDP_TUNNEL_GSO_CSUM; + } } if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) { diff --git a/net/tap.c b/net/tap.c index 5124372316834..abe3b2d0369b4 100644 --- a/net/tap.c +++ b/net/tap.c @@ -62,6 +62,8 @@ static const int kernel_feature_bits[] = { VIRTIO_F_NOTIFICATION_DATA, VIRTIO_NET_F_RSC_EXT, VIRTIO_NET_F_HASH_REPORT, + VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO, + VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO, VHOST_INVALID_FEATURE_BIT }; From 6d65290d83919267a7775bd4877d923a505aae9a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:03:55 +0200 Subject: [PATCH 1203/1794] Revert "hw/acpi/ghes: Make ghes_record_cper_errors() static" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ghes_record_cper_errors() function was introduced to be used by other types of errors, as part of the error injection patch series. That's why it is not static. Make it non-static again to allow its usage outside ghes.c This reverts commit 611f3bdb20f7828b0813aa90d47d1275ef18329b. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Message-ID: <14f2a888cfbf922d5f2bf94d7612114f25107d59.1758610789.git.mchehab+huawei@kernel.org> Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 6 ++++-- include/hw/acpi/ghes.h | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index b85bb48195a0c..b709c177cdea9 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -390,8 +390,8 @@ static void get_hw_error_offsets(uint64_t ghes_addr, *read_ack_register_addr = ghes_addr + sizeof(uint64_t); } -static void ghes_record_cper_errors(const void *cper, size_t len, - uint16_t source_id, Error **errp) +void ghes_record_cper_errors(const void *cper, size_t len, + uint16_t source_id, Error **errp) { uint64_t cper_addr = 0, read_ack_register_addr = 0, read_ack_register; AcpiGedState *acpi_ged_state; @@ -440,6 +440,8 @@ static void ghes_record_cper_errors(const void *cper, size_t len, /* Write the generic error data entry into guest memory */ cpu_physical_memory_write(cper_addr, cper, len); + + return; } int acpi_ghes_memory_errors(uint16_t source_id, uint64_t physical_address) diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h index 578a582203cea..39619a2457cbb 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -75,6 +75,8 @@ void acpi_build_hest(GArray *table_data, GArray *hardware_errors, void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s, GArray *hardware_errors); int acpi_ghes_memory_errors(uint16_t source_id, uint64_t error_physical_addr); +void ghes_record_cper_errors(const void *cper, size_t len, + uint16_t source_id, Error **errp); /** * acpi_ghes_present: Report whether ACPI GHES table is present From 1547c5a5ff894eac9d3666ded3cbf80ce82a28e4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:03:56 +0200 Subject: [PATCH 1204/1794] acpi/ghes: Cleanup the code which gets ghes ged state Move the check logic into a common function and simplify the code which checks if GHES is enabled and was properly setup. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Message-ID: <2bbb1d3eb88b0a668114adef2f1c2a94deebba0e.1758610789.git.mchehab+huawei@kernel.org> Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes-stub.c | 7 ++++--- hw/acpi/ghes.c | 38 +++++++++++--------------------------- include/hw/acpi/ghes.h | 14 +++++++------- target/arm/kvm.c | 7 +++++-- 4 files changed, 27 insertions(+), 39 deletions(-) diff --git a/hw/acpi/ghes-stub.c b/hw/acpi/ghes-stub.c index 7cec1812dad95..40f660c246fe2 100644 --- a/hw/acpi/ghes-stub.c +++ b/hw/acpi/ghes-stub.c @@ -11,12 +11,13 @@ #include "qemu/osdep.h" #include "hw/acpi/ghes.h" -int acpi_ghes_memory_errors(uint16_t source_id, uint64_t physical_address) +int acpi_ghes_memory_errors(AcpiGhesState *ags, uint16_t source_id, + uint64_t physical_address) { return -1; } -bool acpi_ghes_present(void) +AcpiGhesState *acpi_ghes_get_state(void) { - return false; + return NULL; } diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index b709c177cdea9..84b891fd3dcf9 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -360,18 +360,12 @@ void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s, /* Create a read-write fw_cfg file for Address */ fw_cfg_add_file_callback(s, ACPI_HW_ERROR_ADDR_FW_CFG_FILE, NULL, NULL, NULL, &(ags->hw_error_le), sizeof(ags->hw_error_le), false); - - ags->present = true; } static void get_hw_error_offsets(uint64_t ghes_addr, uint64_t *cper_addr, uint64_t *read_ack_register_addr) { - if (!ghes_addr) { - return; - } - /* * non-HEST version supports only one source, so no need to change * the start offset based on the source ID. Also, we can't validate @@ -390,35 +384,20 @@ static void get_hw_error_offsets(uint64_t ghes_addr, *read_ack_register_addr = ghes_addr + sizeof(uint64_t); } -void ghes_record_cper_errors(const void *cper, size_t len, +void ghes_record_cper_errors(AcpiGhesState *ags, const void *cper, size_t len, uint16_t source_id, Error **errp) { uint64_t cper_addr = 0, read_ack_register_addr = 0, read_ack_register; - AcpiGedState *acpi_ged_state; - AcpiGhesState *ags; if (len > ACPI_GHES_MAX_RAW_DATA_LENGTH) { error_setg(errp, "GHES CPER record is too big: %zd", len); return; } - acpi_ged_state = ACPI_GED(object_resolve_path_type("", TYPE_ACPI_GED, - NULL)); - if (!acpi_ged_state) { - error_setg(errp, "Can't find ACPI_GED object"); - return; - } - ags = &acpi_ged_state->ghes_state; - assert(ACPI_GHES_ERROR_SOURCE_COUNT == 1); get_hw_error_offsets(le64_to_cpu(ags->hw_error_le), &cper_addr, &read_ack_register_addr); - if (!cper_addr) { - error_setg(errp, "can not find Generic Error Status Block"); - return; - } - cpu_physical_memory_read(read_ack_register_addr, &read_ack_register, sizeof(read_ack_register)); @@ -444,7 +423,8 @@ void ghes_record_cper_errors(const void *cper, size_t len, return; } -int acpi_ghes_memory_errors(uint16_t source_id, uint64_t physical_address) +int acpi_ghes_memory_errors(AcpiGhesState *ags, uint16_t source_id, + uint64_t physical_address) { /* Memory Error Section Type */ const uint8_t guid[] = @@ -470,7 +450,7 @@ int acpi_ghes_memory_errors(uint16_t source_id, uint64_t physical_address) acpi_ghes_build_append_mem_cper(block, physical_address); /* Report the error */ - ghes_record_cper_errors(block->data, block->len, source_id, &errp); + ghes_record_cper_errors(ags, block->data, block->len, source_id, &errp); g_array_free(block, true); @@ -482,7 +462,7 @@ int acpi_ghes_memory_errors(uint16_t source_id, uint64_t physical_address) return 0; } -bool acpi_ghes_present(void) +AcpiGhesState *acpi_ghes_get_state(void) { AcpiGedState *acpi_ged_state; AcpiGhesState *ags; @@ -491,8 +471,12 @@ bool acpi_ghes_present(void) NULL)); if (!acpi_ged_state) { - return false; + return NULL; } ags = &acpi_ged_state->ghes_state; - return ags->present; + + if (!ags->hw_error_le) { + return NULL; + } + return ags; } diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h index 39619a2457cbb..f96ac3e85ca26 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -66,7 +66,6 @@ enum { typedef struct AcpiGhesState { uint64_t hw_error_le; - bool present; /* True if GHES is present at all on this board */ } AcpiGhesState; void acpi_build_hest(GArray *table_data, GArray *hardware_errors, @@ -74,15 +73,16 @@ void acpi_build_hest(GArray *table_data, GArray *hardware_errors, const char *oem_id, const char *oem_table_id); void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s, GArray *hardware_errors); -int acpi_ghes_memory_errors(uint16_t source_id, uint64_t error_physical_addr); -void ghes_record_cper_errors(const void *cper, size_t len, +int acpi_ghes_memory_errors(AcpiGhesState *ags, uint16_t source_id, + uint64_t error_physical_addr); +void ghes_record_cper_errors(AcpiGhesState *ags, const void *cper, size_t len, uint16_t source_id, Error **errp); /** - * acpi_ghes_present: Report whether ACPI GHES table is present + * acpi_ghes_get_state: Get a pointer for ACPI ghes state * - * Returns: true if the system has an ACPI GHES table and it is - * safe to call acpi_ghes_memory_errors() to record a memory error. + * Returns: a pointer to ghes state if the system has an ACPI GHES table, + * NULL, otherwise. */ -bool acpi_ghes_present(void); +AcpiGhesState *acpi_ghes_get_state(void); #endif diff --git a/target/arm/kvm.c b/target/arm/kvm.c index b8a1c071f57a2..891d4fcde9d48 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -2433,10 +2433,12 @@ void kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr) { ram_addr_t ram_addr; hwaddr paddr; + AcpiGhesState *ags; assert(code == BUS_MCEERR_AR || code == BUS_MCEERR_AO); - if (acpi_ghes_present() && addr) { + ags = acpi_ghes_get_state(); + if (ags && addr) { ram_addr = qemu_ram_addr_from_host(addr); if (ram_addr != RAM_ADDR_INVALID && kvm_physical_memory_addr_from_host(c->kvm_state, addr, &paddr)) { @@ -2454,7 +2456,8 @@ void kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr) */ if (code == BUS_MCEERR_AR) { kvm_cpu_synchronize_state(c); - if (!acpi_ghes_memory_errors(ACPI_HEST_SRC_ID_SEA, paddr)) { + if (!acpi_ghes_memory_errors(ags, ACPI_HEST_SRC_ID_SEA, + paddr)) { kvm_inject_arm_sea(c); } else { error_report("failed to record the error"); From 2e9c5c5bc85a4f79a5e5c9d52df6c62bd1b2e116 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:03:57 +0200 Subject: [PATCH 1205/1794] acpi/ghes: prepare to change the way HEST offsets are calculated Add a new ags flag to change the way HEST offsets are calculated. Currently, offsets needed to store ACPI HEST offsets and read ack are calculated based on a previous knowledge from the logic which creates the HEST table. Such logic is not generic, not allowing to easily add more HEST entries nor replicates what OSPM does. As the next patches will be adding a more generic logic, add a new use_hest_addr, set to false, in preparation for such changes. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Message-ID: Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 39 ++++++++++++++++++++++++--------------- hw/arm/virt-acpi-build.c | 13 ++++++++++--- include/hw/acpi/ghes.h | 12 +++++++++++- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index 84b891fd3dcf9..9243b5ad4acb1 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -206,7 +206,8 @@ ghes_gen_err_data_uncorrectable_recoverable(GArray *block, * Initialize "etc/hardware_errors" and "etc/hardware_errors_addr" fw_cfg blobs. * See docs/specs/acpi_hest_ghes.rst for blobs format. */ -static void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker) +static void build_ghes_error_table(AcpiGhesState *ags, GArray *hardware_errors, + BIOSLinker *linker) { int i, error_status_block_offset; @@ -251,13 +252,15 @@ static void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker) i * ACPI_GHES_MAX_RAW_DATA_LENGTH); } - /* - * tell firmware to write hardware_errors GPA into - * hardware_errors_addr fw_cfg, once the former has been initialized. - */ - bios_linker_loader_write_pointer(linker, ACPI_HW_ERROR_ADDR_FW_CFG_FILE, 0, - sizeof(uint64_t), - ACPI_HW_ERROR_FW_CFG_FILE, 0); + if (!ags->use_hest_addr) { + /* + * Tell firmware to write hardware_errors GPA into + * hardware_errors_addr fw_cfg, once the former has been initialized. + */ + bios_linker_loader_write_pointer(linker, ACPI_HW_ERROR_ADDR_FW_CFG_FILE, + 0, sizeof(uint64_t), + ACPI_HW_ERROR_FW_CFG_FILE, 0); + } } /* Build Generic Hardware Error Source version 2 (GHESv2) */ @@ -331,14 +334,15 @@ static void build_ghes_v2(GArray *table_data, } /* Build Hardware Error Source Table */ -void acpi_build_hest(GArray *table_data, GArray *hardware_errors, +void acpi_build_hest(AcpiGhesState *ags, GArray *table_data, + GArray *hardware_errors, BIOSLinker *linker, const char *oem_id, const char *oem_table_id) { AcpiTable table = { .sig = "HEST", .rev = 1, .oem_id = oem_id, .oem_table_id = oem_table_id }; - build_ghes_error_table(hardware_errors, linker); + build_ghes_error_table(ags, hardware_errors, linker); acpi_table_begin(&table, table_data); @@ -357,9 +361,11 @@ void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s, fw_cfg_add_file(s, ACPI_HW_ERROR_FW_CFG_FILE, hardware_error->data, hardware_error->len); - /* Create a read-write fw_cfg file for Address */ - fw_cfg_add_file_callback(s, ACPI_HW_ERROR_ADDR_FW_CFG_FILE, NULL, NULL, - NULL, &(ags->hw_error_le), sizeof(ags->hw_error_le), false); + if (!ags->use_hest_addr) { + /* Create a read-write fw_cfg file for Address */ + fw_cfg_add_file_callback(s, ACPI_HW_ERROR_ADDR_FW_CFG_FILE, NULL, NULL, + NULL, &(ags->hw_error_le), sizeof(ags->hw_error_le), false); + } } static void get_hw_error_offsets(uint64_t ghes_addr, @@ -395,8 +401,11 @@ void ghes_record_cper_errors(AcpiGhesState *ags, const void *cper, size_t len, } assert(ACPI_GHES_ERROR_SOURCE_COUNT == 1); - get_hw_error_offsets(le64_to_cpu(ags->hw_error_le), - &cper_addr, &read_ack_register_addr); + + if (!ags->use_hest_addr) { + get_hw_error_offsets(le64_to_cpu(ags->hw_error_le), + &cper_addr, &read_ack_register_addr); + } cpu_physical_memory_read(read_ack_register_addr, &read_ack_register, sizeof(read_ack_register)); diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 96830f7c4ecfe..bbe83fab9abbc 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -1181,9 +1181,16 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) build_dbg2(tables_blob, tables->linker, vms); if (vms->ras) { - acpi_add_table(table_offsets, tables_blob); - acpi_build_hest(tables_blob, tables->hardware_errors, tables->linker, - vms->oem_id, vms->oem_table_id); + AcpiGedState *acpi_ged_state; + AcpiGhesState *ags; + + acpi_ged_state = ACPI_GED(vms->acpi_dev); + ags = &acpi_ged_state->ghes_state; + if (ags) { + acpi_add_table(table_offsets, tables_blob); + acpi_build_hest(ags, tables_blob, tables->hardware_errors, + tables->linker, vms->oem_id, vms->oem_table_id); + } } if (ms->numa_state->num_nodes > 0) { diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h index f96ac3e85ca26..411f592662af3 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -64,11 +64,21 @@ enum { ACPI_GHES_ERROR_SOURCE_COUNT }; +/* + * AcpiGhesState stores GPA values that will be used to fill HEST entries. + * + * When use_hest_addr is false, the GPA of the etc/hardware_errors firmware + * is stored at hw_error_le. This is the default on QEMU 9.x. + * + * An GPA value equal to zero means that GHES is not present. + */ typedef struct AcpiGhesState { uint64_t hw_error_le; + bool use_hest_addr; /* Currently, always false */ } AcpiGhesState; -void acpi_build_hest(GArray *table_data, GArray *hardware_errors, +void acpi_build_hest(AcpiGhesState *ags, GArray *table_data, + GArray *hardware_errors, BIOSLinker *linker, const char *oem_id, const char *oem_table_id); void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s, From db16153f196f0ed7560aa138f08e2ef312ecf005 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:03:58 +0200 Subject: [PATCH 1206/1794] acpi/ghes: add a firmware file with HEST address Store HEST table address at GPA, placing its the start of the table at hest_addr_le variable. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Message-ID: Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 22 ++++++++++++++++++++-- include/hw/acpi/ghes.h | 6 +++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index 9243b5ad4acb1..cbc96c1909f0e 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -30,6 +30,7 @@ #define ACPI_HW_ERROR_FW_CFG_FILE "etc/hardware_errors" #define ACPI_HW_ERROR_ADDR_FW_CFG_FILE "etc/hardware_errors_addr" +#define ACPI_HEST_ADDR_FW_CFG_FILE "etc/acpi_table_hest_addr" /* The max size in bytes for one error block */ #define ACPI_GHES_MAX_RAW_DATA_LENGTH (1 * KiB) @@ -341,6 +342,9 @@ void acpi_build_hest(AcpiGhesState *ags, GArray *table_data, { AcpiTable table = { .sig = "HEST", .rev = 1, .oem_id = oem_id, .oem_table_id = oem_table_id }; + uint32_t hest_offset; + + hest_offset = table_data->len; build_ghes_error_table(ags, hardware_errors, linker); @@ -352,6 +356,17 @@ void acpi_build_hest(AcpiGhesState *ags, GArray *table_data, ACPI_GHES_NOTIFY_SEA, ACPI_HEST_SRC_ID_SEA); acpi_table_end(linker, &table); + + if (ags->use_hest_addr) { + /* + * Tell firmware to write into GPA the address of HEST via fw_cfg, + * once initialized. + */ + bios_linker_loader_write_pointer(linker, + ACPI_HEST_ADDR_FW_CFG_FILE, 0, + sizeof(uint64_t), + ACPI_BUILD_TABLE_FILE, hest_offset); + } } void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s, @@ -361,7 +376,10 @@ void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s, fw_cfg_add_file(s, ACPI_HW_ERROR_FW_CFG_FILE, hardware_error->data, hardware_error->len); - if (!ags->use_hest_addr) { + if (ags->use_hest_addr) { + fw_cfg_add_file_callback(s, ACPI_HEST_ADDR_FW_CFG_FILE, NULL, NULL, + NULL, &(ags->hest_addr_le), sizeof(ags->hest_addr_le), false); + } else { /* Create a read-write fw_cfg file for Address */ fw_cfg_add_file_callback(s, ACPI_HW_ERROR_ADDR_FW_CFG_FILE, NULL, NULL, NULL, &(ags->hw_error_le), sizeof(ags->hw_error_le), false); @@ -484,7 +502,7 @@ AcpiGhesState *acpi_ghes_get_state(void) } ags = &acpi_ged_state->ghes_state; - if (!ags->hw_error_le) { + if (!ags->hw_error_le && !ags->hest_addr_le) { return NULL; } return ags; diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h index 411f592662af3..ea9baab764e2e 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -70,9 +70,13 @@ enum { * When use_hest_addr is false, the GPA of the etc/hardware_errors firmware * is stored at hw_error_le. This is the default on QEMU 9.x. * - * An GPA value equal to zero means that GHES is not present. + * When use_hest_addr is true, the GPA of the HEST table is stored at + * hest_addr_le. This is the default for QEMU 10.x and above. + * + * Whe both GPA values are equal to zero means that GHES is not present. */ typedef struct AcpiGhesState { + uint64_t hest_addr_le; uint64_t hw_error_le; bool use_hest_addr; /* Currently, always false */ } AcpiGhesState; From b74d843f7bf43f5e7e1e5b9c34da2034d622cf78 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:03:59 +0200 Subject: [PATCH 1207/1794] acpi/ghes: Use HEST table offsets when preparing GHES records There are two pointers that are needed during error injection: 1. The start address of the CPER block to be stored; 2. The address of the read ack. It is preferable to calculate them from the HEST table. This allows checking the source ID, the size of the table and the type of the HEST error block structures. Yet, keep the old code, as this is needed for migration purposes from older QEMU versions. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Message-ID: Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 100 +++++++++++++++++++++++++++++++++++++++++ include/hw/acpi/ghes.h | 2 +- 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index cbc96c1909f0e..668ca72587c70 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -41,6 +41,12 @@ /* Address offset in Generic Address Structure(GAS) */ #define GAS_ADDR_OFFSET 4 +/* + * ACPI spec 1.0b + * 5.2.3 System Description Table Header + */ +#define ACPI_DESC_HEADER_OFFSET 36 + /* * The total size of Generic Error Data Entry * ACPI 6.1/6.2: 18.3.2.7.1 Generic Error Data, @@ -61,6 +67,30 @@ */ #define ACPI_GHES_GESB_SIZE 20 +/* + * See the memory layout map at docs/specs/acpi_hest_ghes.rst. + */ + +/* + * ACPI 6.1: 18.3.2.8 Generic Hardware Error Source version 2 + * Table 18-344 Generic Hardware Error Source version 2 (GHESv2) Structure + */ +#define HEST_GHES_V2_ENTRY_SIZE 92 + +/* + * ACPI 6.1: 18.3.2.8 Generic Hardware Error Source version 2 + * Table 18-344 Generic Hardware Error Source version 2 (GHESv2) Structure + * Read Ack Register + */ +#define GHES_READ_ACK_ADDR_OFF 64 + +/* + * ACPI 6.1: 18.3.2.7: Generic Hardware Error Source + * Table 18-341 Generic Hardware Error Source Structure + * Error Status Address + */ +#define GHES_ERR_STATUS_ADDR_OFF 20 + /* * Values for error_severity field */ @@ -408,6 +438,73 @@ static void get_hw_error_offsets(uint64_t ghes_addr, *read_ack_register_addr = ghes_addr + sizeof(uint64_t); } +static void get_ghes_source_offsets(uint16_t source_id, + uint64_t hest_addr, + uint64_t *cper_addr, + uint64_t *read_ack_start_addr, + Error **errp) +{ + uint64_t hest_err_block_addr, hest_read_ack_addr; + uint64_t err_source_entry, error_block_addr; + uint32_t num_sources, i; + + hest_addr += ACPI_DESC_HEADER_OFFSET; + + cpu_physical_memory_read(hest_addr, &num_sources, + sizeof(num_sources)); + num_sources = le32_to_cpu(num_sources); + + err_source_entry = hest_addr + sizeof(num_sources); + + /* + * Currently, HEST Error source navigates only for GHESv2 tables + */ + for (i = 0; i < num_sources; i++) { + uint64_t addr = err_source_entry; + uint16_t type, src_id; + + cpu_physical_memory_read(addr, &type, sizeof(type)); + type = le16_to_cpu(type); + + /* For now, we only know the size of GHESv2 table */ + if (type != ACPI_GHES_SOURCE_GENERIC_ERROR_V2) { + error_setg(errp, "HEST: type %d not supported.", type); + return; + } + + /* Compare CPER source ID at the GHESv2 structure */ + addr += sizeof(type); + cpu_physical_memory_read(addr, &src_id, sizeof(src_id)); + if (le16_to_cpu(src_id) == source_id) { + break; + } + + err_source_entry += HEST_GHES_V2_ENTRY_SIZE; + } + if (i == num_sources) { + error_setg(errp, "HEST: Source %d not found.", source_id); + return; + } + + /* Navigate through table address pointers */ + hest_err_block_addr = err_source_entry + GHES_ERR_STATUS_ADDR_OFF + + GAS_ADDR_OFFSET; + + cpu_physical_memory_read(hest_err_block_addr, &error_block_addr, + sizeof(error_block_addr)); + error_block_addr = le64_to_cpu(error_block_addr); + + cpu_physical_memory_read(error_block_addr, cper_addr, + sizeof(*cper_addr)); + *cper_addr = le64_to_cpu(*cper_addr); + + hest_read_ack_addr = err_source_entry + GHES_READ_ACK_ADDR_OFF + + GAS_ADDR_OFFSET; + cpu_physical_memory_read(hest_read_ack_addr, read_ack_start_addr, + sizeof(*read_ack_start_addr)); + *read_ack_start_addr = le64_to_cpu(*read_ack_start_addr); +} + void ghes_record_cper_errors(AcpiGhesState *ags, const void *cper, size_t len, uint16_t source_id, Error **errp) { @@ -423,6 +520,9 @@ void ghes_record_cper_errors(AcpiGhesState *ags, const void *cper, size_t len, if (!ags->use_hest_addr) { get_hw_error_offsets(le64_to_cpu(ags->hw_error_le), &cper_addr, &read_ack_register_addr); + } else { + get_ghes_source_offsets(source_id, le64_to_cpu(ags->hest_addr_le), + &cper_addr, &read_ack_register_addr, errp); } cpu_physical_memory_read(read_ack_register_addr, diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h index ea9baab764e2e..5265102ba51f4 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -78,7 +78,7 @@ enum { typedef struct AcpiGhesState { uint64_t hest_addr_le; uint64_t hw_error_le; - bool use_hest_addr; /* Currently, always false */ + bool use_hest_addr; /* True if HEST address is present */ } AcpiGhesState; void acpi_build_hest(AcpiGhesState *ags, GArray *table_data, From 2c5a2616ed047e9d5e70970af5c4b2a54e9fa290 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:04:00 +0200 Subject: [PATCH 1208/1794] acpi/ghes: don't hard-code the number of sources for HEST table The current code is actually dependent on having just one error structure with a single source, as any change there would cause migration issues. As the number of sources should be arch-dependent, as it will depend on what kind of notifications will exist, and how many errors can be reported at the same time, change the logic to be more flexible, allowing the number of sources to be defined when building the HEST table by the caller. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Message-ID: <1698680848c11d6f26368426f1657e14faaf55c4.1758610789.git.mchehab+huawei@kernel.org> Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 39 +++++++++++++++++++++------------------ hw/arm/virt-acpi-build.c | 8 +++++++- include/hw/acpi/ghes.h | 17 ++++++++++++----- target/arm/kvm.c | 2 +- 4 files changed, 41 insertions(+), 25 deletions(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index 668ca72587c70..f49d0d628fc4d 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -238,17 +238,17 @@ ghes_gen_err_data_uncorrectable_recoverable(GArray *block, * See docs/specs/acpi_hest_ghes.rst for blobs format. */ static void build_ghes_error_table(AcpiGhesState *ags, GArray *hardware_errors, - BIOSLinker *linker) + BIOSLinker *linker, int num_sources) { int i, error_status_block_offset; /* Build error_block_address */ - for (i = 0; i < ACPI_GHES_ERROR_SOURCE_COUNT; i++) { + for (i = 0; i < num_sources; i++) { build_append_int_noprefix(hardware_errors, 0, sizeof(uint64_t)); } /* Build read_ack_register */ - for (i = 0; i < ACPI_GHES_ERROR_SOURCE_COUNT; i++) { + for (i = 0; i < num_sources; i++) { /* * Initialize the value of read_ack_register to 1, so GHES can be * writable after (re)boot. @@ -263,13 +263,13 @@ static void build_ghes_error_table(AcpiGhesState *ags, GArray *hardware_errors, /* Reserve space for Error Status Data Block */ acpi_data_push(hardware_errors, - ACPI_GHES_MAX_RAW_DATA_LENGTH * ACPI_GHES_ERROR_SOURCE_COUNT); + ACPI_GHES_MAX_RAW_DATA_LENGTH * num_sources); /* Tell guest firmware to place hardware_errors blob into RAM */ bios_linker_loader_alloc(linker, ACPI_HW_ERROR_FW_CFG_FILE, hardware_errors, sizeof(uint64_t), false); - for (i = 0; i < ACPI_GHES_ERROR_SOURCE_COUNT; i++) { + for (i = 0; i < num_sources; i++) { /* * Tell firmware to patch error_block_address entries to point to * corresponding "Generic Error Status Block" @@ -295,12 +295,14 @@ static void build_ghes_error_table(AcpiGhesState *ags, GArray *hardware_errors, } /* Build Generic Hardware Error Source version 2 (GHESv2) */ -static void build_ghes_v2(GArray *table_data, - BIOSLinker *linker, - enum AcpiGhesNotifyType notify, - uint16_t source_id) +static void build_ghes_v2_entry(GArray *table_data, + BIOSLinker *linker, + const AcpiNotificationSourceId *notif_src, + uint16_t index, int num_sources) { uint64_t address_offset; + const uint16_t notify = notif_src->notify; + const uint16_t source_id = notif_src->source_id; /* * Type: @@ -331,7 +333,7 @@ static void build_ghes_v2(GArray *table_data, address_offset + GAS_ADDR_OFFSET, sizeof(uint64_t), ACPI_HW_ERROR_FW_CFG_FILE, - source_id * sizeof(uint64_t)); + index * sizeof(uint64_t)); /* Notification Structure */ build_ghes_hw_error_notification(table_data, notify); @@ -351,8 +353,7 @@ static void build_ghes_v2(GArray *table_data, address_offset + GAS_ADDR_OFFSET, sizeof(uint64_t), ACPI_HW_ERROR_FW_CFG_FILE, - (ACPI_GHES_ERROR_SOURCE_COUNT + source_id) - * sizeof(uint64_t)); + (num_sources + index) * sizeof(uint64_t)); /* * Read Ack Preserve field @@ -368,22 +369,26 @@ static void build_ghes_v2(GArray *table_data, void acpi_build_hest(AcpiGhesState *ags, GArray *table_data, GArray *hardware_errors, BIOSLinker *linker, + const AcpiNotificationSourceId *notif_source, + int num_sources, const char *oem_id, const char *oem_table_id) { AcpiTable table = { .sig = "HEST", .rev = 1, .oem_id = oem_id, .oem_table_id = oem_table_id }; uint32_t hest_offset; + int i; hest_offset = table_data->len; - build_ghes_error_table(ags, hardware_errors, linker); + build_ghes_error_table(ags, hardware_errors, linker, num_sources); acpi_table_begin(&table, table_data); /* Error Source Count */ - build_append_int_noprefix(table_data, ACPI_GHES_ERROR_SOURCE_COUNT, 4); - build_ghes_v2(table_data, linker, - ACPI_GHES_NOTIFY_SEA, ACPI_HEST_SRC_ID_SEA); + build_append_int_noprefix(table_data, num_sources, 4); + for (i = 0; i < num_sources; i++) { + build_ghes_v2_entry(table_data, linker, ¬if_source[i], i, num_sources); + } acpi_table_end(linker, &table); @@ -515,8 +520,6 @@ void ghes_record_cper_errors(AcpiGhesState *ags, const void *cper, size_t len, return; } - assert(ACPI_GHES_ERROR_SOURCE_COUNT == 1); - if (!ags->use_hest_addr) { get_hw_error_offsets(le64_to_cpu(ags->hw_error_le), &cper_addr, &read_ack_register_addr); diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index bbe83fab9abbc..c856d293c6e8f 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -1125,6 +1125,10 @@ static void acpi_align_size(GArray *blob, unsigned align) g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align)); } +static const AcpiNotificationSourceId hest_ghes_notify[] = { + { ACPI_HEST_SRC_ID_SYNC, ACPI_GHES_NOTIFY_SEA }, +}; + static void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) { @@ -1189,7 +1193,9 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) if (ags) { acpi_add_table(table_offsets, tables_blob); acpi_build_hest(ags, tables_blob, tables->hardware_errors, - tables->linker, vms->oem_id, vms->oem_table_id); + tables->linker, hest_ghes_notify, + ARRAY_SIZE(hest_ghes_notify), + vms->oem_id, vms->oem_table_id); } } diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h index 5265102ba51f4..8c4b084337600 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -57,13 +57,18 @@ enum AcpiGhesNotifyType { ACPI_GHES_NOTIFY_RESERVED = 12 }; -enum { - ACPI_HEST_SRC_ID_SEA = 0, - /* future ids go here */ - - ACPI_GHES_ERROR_SOURCE_COUNT +/* + * ID numbers used to fill HEST source ID field + */ +enum AcpiGhesSourceID { + ACPI_HEST_SRC_ID_SYNC, }; +typedef struct AcpiNotificationSourceId { + enum AcpiGhesSourceID source_id; + enum AcpiGhesNotifyType notify; +} AcpiNotificationSourceId; + /* * AcpiGhesState stores GPA values that will be used to fill HEST entries. * @@ -84,6 +89,8 @@ typedef struct AcpiGhesState { void acpi_build_hest(AcpiGhesState *ags, GArray *table_data, GArray *hardware_errors, BIOSLinker *linker, + const AcpiNotificationSourceId * const notif_source, + int num_sources, const char *oem_id, const char *oem_table_id); void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s, GArray *hardware_errors); diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 891d4fcde9d48..4f769d69b38d5 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -2456,7 +2456,7 @@ void kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr) */ if (code == BUS_MCEERR_AR) { kvm_cpu_synchronize_state(c); - if (!acpi_ghes_memory_errors(ags, ACPI_HEST_SRC_ID_SEA, + if (!acpi_ghes_memory_errors(ags, ACPI_HEST_SRC_ID_SYNC, paddr)) { kvm_inject_arm_sea(c); } else { From 7a857a8933e073d6e0c5ae2c09ff6c45751be3d7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:04:01 +0200 Subject: [PATCH 1209/1794] acpi/ghes: add a notifier to notify when error data is ready Some error injection notify methods are async, like GPIO notify. Add a notifier to be used when the error record is ready to be sent to the guest OS. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Acked-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Message-ID: Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 5 ++++- include/hw/acpi/ghes.h | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index f49d0d628fc4d..d666f1b10b5ff 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -510,6 +510,9 @@ static void get_ghes_source_offsets(uint16_t source_id, *read_ack_start_addr = le64_to_cpu(*read_ack_start_addr); } +NotifierList acpi_generic_error_notifiers = + NOTIFIER_LIST_INITIALIZER(acpi_generic_error_notifiers); + void ghes_record_cper_errors(AcpiGhesState *ags, const void *cper, size_t len, uint16_t source_id, Error **errp) { @@ -550,7 +553,7 @@ void ghes_record_cper_errors(AcpiGhesState *ags, const void *cper, size_t len, /* Write the generic error data entry into guest memory */ cpu_physical_memory_write(cper_addr, cper, len); - return; + notifier_list_notify(&acpi_generic_error_notifiers, NULL); } int acpi_ghes_memory_errors(AcpiGhesState *ags, uint16_t source_id, diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h index 8c4b084337600..390943e46d99e 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -24,6 +24,9 @@ #include "hw/acpi/bios-linker-loader.h" #include "qapi/error.h" +#include "qemu/notify.h" + +extern NotifierList acpi_generic_error_notifiers; /* * Values for Hardware Error Notification Type field From d238dedae699a924c92228cb8b3bb8dc861008b5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:04:02 +0200 Subject: [PATCH 1210/1794] acpi/generic_event_device: Update GHES migration to cover hest addr The GHES migration logic should now support HEST table location too. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Message-ID: Signed-off-by: Michael S. Tsirkin --- hw/acpi/generic_event_device.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 95682b79a2df9..55998303c22f5 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -436,6 +436,34 @@ static const VMStateDescription vmstate_pcihp_state = { } }; +static const VMStateDescription vmstate_hest = { + .name = "acpi-hest", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(hest_addr_le, AcpiGhesState), + VMSTATE_END_OF_LIST() + }, +}; + +static bool hest_needed(void *opaque) +{ + AcpiGedState *s = opaque; + return s->ghes_state.hest_addr_le; +} + +static const VMStateDescription vmstate_hest_state = { + .name = "acpi-ged/hest", + .version_id = 1, + .minimum_version_id = 1, + .needed = hest_needed, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT(ghes_state, AcpiGedState, 1, + vmstate_hest, AcpiGhesState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_acpi_ged = { .name = "acpi-ged", .version_id = 1, @@ -449,6 +477,7 @@ static const VMStateDescription vmstate_acpi_ged = { &vmstate_cpuhp_state, &vmstate_ghes_state, &vmstate_pcihp_state, + &vmstate_hest_state, NULL } }; From f3f747ddcfc9af2c2c58f70a68723b99db82f778 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:04:03 +0200 Subject: [PATCH 1211/1794] acpi/generic_event_device: add logic to detect if HEST addr is available Create a new property (x-has-hest-addr) and use it to detect if the GHES table offsets can be calculated from the HEST address (qemu 10.0 and upper) or via the legacy way via an offset obtained from the hardware_errors firmware file. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Message-ID: Signed-off-by: Michael S. Tsirkin --- hw/acpi/generic_event_device.c | 2 ++ hw/arm/virt-acpi-build.c | 18 ++++++++++++++++-- hw/core/machine.c | 5 ++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 55998303c22f5..b1ff6ab74d8f9 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -349,6 +349,8 @@ static const Property acpi_ged_properties[] = { pcihp_state.use_acpi_hotplug_bridge, 0), DEFINE_PROP_LINK("bus", AcpiGedState, pcihp_state.root, TYPE_PCI_BUS, PCIBus *), + DEFINE_PROP_BOOL("x-has-hest-addr", AcpiGedState, + ghes_state.use_hest_addr, false), }; static const VMStateDescription vmstate_memhp_state = { diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index c856d293c6e8f..ff3b7a794b84b 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -1129,6 +1129,10 @@ static const AcpiNotificationSourceId hest_ghes_notify[] = { { ACPI_HEST_SRC_ID_SYNC, ACPI_GHES_NOTIFY_SEA }, }; +static const AcpiNotificationSourceId hest_ghes_notify_10_0[] = { + { ACPI_HEST_SRC_ID_SYNC, ACPI_GHES_NOTIFY_SEA }, +}; + static void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) { @@ -1186,15 +1190,25 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) if (vms->ras) { AcpiGedState *acpi_ged_state; + static const AcpiNotificationSourceId *notify; + unsigned int notify_sz; AcpiGhesState *ags; acpi_ged_state = ACPI_GED(vms->acpi_dev); ags = &acpi_ged_state->ghes_state; if (ags) { acpi_add_table(table_offsets, tables_blob); + + if (!ags->use_hest_addr) { + notify = hest_ghes_notify_10_0; + notify_sz = ARRAY_SIZE(hest_ghes_notify_10_0); + } else { + notify = hest_ghes_notify; + notify_sz = ARRAY_SIZE(hest_ghes_notify); + } + acpi_build_hest(ags, tables_blob, tables->hardware_errors, - tables->linker, hest_ghes_notify, - ARRAY_SIZE(hest_ghes_notify), + tables->linker, notify, notify_sz, vms->oem_id, vms->oem_table_id); } } diff --git a/hw/core/machine.c b/hw/core/machine.c index 38c949c4f2ce4..7b7a381b0aded 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -35,9 +35,12 @@ #include "hw/virtio/virtio-pci.h" #include "hw/virtio/virtio-net.h" #include "hw/virtio/virtio-iommu.h" +#include "hw/acpi/generic_event_device.h" #include "audio/audio.h" -GlobalProperty hw_compat_10_1[] = {}; +GlobalProperty hw_compat_10_1[] = { + { TYPE_ACPI_GED, "x-has-hest-addr", "false" }, +}; const size_t hw_compat_10_1_len = G_N_ELEMENTS(hw_compat_10_1); GlobalProperty hw_compat_10_0[] = { From c498a36bcb59d1f96faf4771efe8f655ff5faa20 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:04:04 +0200 Subject: [PATCH 1212/1794] acpi/generic_event_device: add an APEI error device Adds a generic error device to handle generic hardware error events as specified at ACPI 6.5 specification at 18.3.2.7.2: https://uefi.org/specs/ACPI/6.5/18_Platform_Error_Interfaces.html#event-notification-for-generic-error-sources using HID PNP0C33. The PNP0C33 device is used to report hardware errors to the guest via ACPI APEI Generic Hardware Error Source (GHES). Co-authored-by: Mauro Carvalho Chehab Co-authored-by: Jonathan Cameron Signed-off-by: Jonathan Cameron Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Message-ID: <2790f664c849d53de0ce3049fa8c7950c1de1f86.1758610789.git.mchehab+huawei@kernel.org> Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 10 ++++++++++ hw/acpi/generic_event_device.c | 13 +++++++++++++ include/hw/acpi/acpi_dev_interface.h | 1 + include/hw/acpi/aml-build.h | 2 ++ include/hw/acpi/generic_event_device.h | 1 + 5 files changed, 27 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 1e685f982f311..2d5826a8f134a 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -2629,3 +2629,13 @@ Aml *aml_i2c_serial_bus_device(uint16_t address, const char *resource_source) return var; } + +/* ACPI 5.0b: 18.3.2.6.2 Event Notification For Generic Error Sources */ +Aml *aml_error_device(void) +{ + Aml *dev = aml_device(ACPI_APEI_ERROR_DEVICE); + aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C33"))); + aml_append(dev, aml_name_decl("_UID", aml_int(0))); + + return dev; +} diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index b1ff6ab74d8f9..e575b9404be46 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -30,6 +30,7 @@ static const uint32_t ged_supported_events[] = { ACPI_GED_NVDIMM_HOTPLUG_EVT, ACPI_GED_CPU_HOTPLUG_EVT, ACPI_GED_PCI_HOTPLUG_EVT, + ACPI_GED_ERROR_EVT, }; /* @@ -120,6 +121,16 @@ void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev, aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE), aml_int(0x80))); break; + case ACPI_GED_ERROR_EVT: + /* + * ACPI 5.0b: 5.6.6 Device Object Notifications + * Table 5-135 Error Device Notification Values + * Defines 0x80 as the value to be used on notifications + */ + aml_append(if_ctx, + aml_notify(aml_name(ACPI_APEI_ERROR_DEVICE), + aml_int(0x80))); + break; case ACPI_GED_NVDIMM_HOTPLUG_EVT: aml_append(if_ctx, aml_notify(aml_name("\\_SB.NVDR"), @@ -320,6 +331,8 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) sel = ACPI_GED_MEM_HOTPLUG_EVT; } else if (ev & ACPI_POWER_DOWN_STATUS) { sel = ACPI_GED_PWR_DOWN_EVT; + } else if (ev & ACPI_GENERIC_ERROR) { + sel = ACPI_GED_ERROR_EVT; } else if (ev & ACPI_NVDIMM_HOTPLUG_STATUS) { sel = ACPI_GED_NVDIMM_HOTPLUG_EVT; } else if (ev & ACPI_CPU_HOTPLUG_STATUS) { diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h index 68d9d15f50aa7..8294f8f0ccca3 100644 --- a/include/hw/acpi/acpi_dev_interface.h +++ b/include/hw/acpi/acpi_dev_interface.h @@ -13,6 +13,7 @@ typedef enum { ACPI_NVDIMM_HOTPLUG_STATUS = 16, ACPI_VMGENID_CHANGE_STATUS = 32, ACPI_POWER_DOWN_STATUS = 64, + ACPI_GENERIC_ERROR = 128, } AcpiEventStatusBits; #define TYPE_ACPI_DEVICE_IF "acpi-device-interface" diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index c18f68134246a..f38e12971932c 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -252,6 +252,7 @@ struct CrsRangeSet { /* Consumer/Producer */ #define AML_SERIAL_BUS_FLAG_CONSUME_ONLY (1 << 1) +#define ACPI_APEI_ERROR_DEVICE "GEDD" /** * init_aml_allocator: * @@ -382,6 +383,7 @@ Aml *aml_dma(AmlDmaType typ, AmlDmaBusMaster bm, AmlTransferSize sz, uint8_t channel); Aml *aml_sleep(uint64_t msec); Aml *aml_i2c_serial_bus_device(uint16_t address, const char *resource_source); +Aml *aml_error_device(void); /* Block AML object primitives */ Aml *aml_scope(const char *name_format, ...) G_GNUC_PRINTF(1, 2); diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h index 2c5b055327a43..130c014d3f0eb 100644 --- a/include/hw/acpi/generic_event_device.h +++ b/include/hw/acpi/generic_event_device.h @@ -103,6 +103,7 @@ OBJECT_DECLARE_TYPE(AcpiGedState, AcpiGedClass, ACPI_GED) #define ACPI_GED_NVDIMM_HOTPLUG_EVT 0x4 #define ACPI_GED_CPU_HOTPLUG_EVT 0x8 #define ACPI_GED_PCI_HOTPLUG_EVT 0x10 +#define ACPI_GED_ERROR_EVT 0x20 typedef struct GEDState { MemoryRegion evt; From c187a67c9dcdece58138f4df5ca4dd846934eddc Mon Sep 17 00:00:00 2001 From: Filip Hejsek Date: Sat, 13 Sep 2025 00:58:35 +0200 Subject: [PATCH 1213/1794] ui/gtk: Fix callback function signature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The correct type for opaque pointer is gpointer, not gpointer * (which is a pointer to a pointer). Signed-off-by: Filip Hejsek Reviewed-by: Alex Bennée Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- ui/gtk.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 9a08cadc88ffb..48571bedbf5a7 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -766,9 +766,9 @@ static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context, } static void gd_resize_event(GtkGLArea *area, - gint width, gint height, gpointer *opaque) + gint width, gint height, gpointer opaque) { - VirtualConsole *vc = (void *)opaque; + VirtualConsole *vc = opaque; double pw = width, ph = height; double sx = vc->gfx.scale_x, sy = vc->gfx.scale_y; GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(area)); From d56a30a7bba66de1b796af775928f77da25ca89b Mon Sep 17 00:00:00 2001 From: SillyZ <1357816113@qq.com> Date: Tue, 16 Sep 2025 16:10:09 +0800 Subject: [PATCH 1214/1794] hw/net/can: Remove redundant status bit setting in can_sja1000 In PeliCAN mode reception, the RBS (Receive Buffer Status) bit is set twice at line 842 and 845 with identical operations: s->status_pel |= 0x01; s->status_pel |= (1 << 0); Between these two operations, only interrupt_pel is modified and status_pel bit 4 is cleared, neither affecting bit 0. The second operation is redundant. This cleanup aligns PeliCAN mode with BasicCAN mode, which correctly sets this bit only once (line 883). Signed-off-by: SillyZ <1357816113@qq.com> Reviewed-by: Peter Maydell Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- hw/net/can/can_sja1000.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/net/can/can_sja1000.c b/hw/net/can/can_sja1000.c index 5b6ba9df6c42f..6b08e977a1d79 100644 --- a/hw/net/can/can_sja1000.c +++ b/hw/net/can/can_sja1000.c @@ -842,7 +842,6 @@ ssize_t can_sja_receive(CanBusClientState *client, const qemu_can_frame *frames, s->status_pel |= 0x01; /* Set the Receive Buffer Status. DS-p23 */ s->interrupt_pel |= 0x01; s->status_pel &= ~(1 << 4); - s->status_pel |= (1 << 0); can_sja_update_pel_irq(s); } else { /* BasicCAN mode */ From 383c7224839cf3acd8f1a609ad394daf7cfc8b6d Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Wed, 17 Sep 2025 17:26:45 +0300 Subject: [PATCH 1215/1794] vhost-user-test: remove trailing newlines in g_test_message() calls Fixes: c9a1ea9c52 Revert "tests/qtest: use qos_printf instead of g_test_message" Reviewed-by: Laurent Vivier Reviewed-by: Markus Armbruster Signed-off-by: Michael Tokarev --- tests/qtest/vhost-user-test.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index 6ec4ec2d5adc6..609ff24059cad 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -391,7 +391,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * We don't need to do anything here, the remote is just * letting us know it is in charge. Just log it. */ - g_test_message("set_owner: start of session\n"); + g_test_message("set_owner: start of session"); break; case VHOST_USER_GET_PROTOCOL_FEATURES: @@ -417,7 +417,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * the remote end to send this. There is no handshake reply so * just log the details for debugging. */ - g_test_message("set_protocol_features: 0x%"PRIx64 "\n", msg.payload.u64); + g_test_message("set_protocol_features: 0x%"PRIx64, msg.payload.u64); break; /* @@ -425,11 +425,11 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * address of the vrings but we can simply report them. */ case VHOST_USER_SET_VRING_NUM: - g_test_message("set_vring_num: %d/%d\n", + g_test_message("set_vring_num: %d/%d", msg.payload.state.index, msg.payload.state.num); break; case VHOST_USER_SET_VRING_ADDR: - g_test_message("set_vring_addr: 0x%"PRIx64"/0x%"PRIx64"/0x%"PRIx64"\n", + g_test_message("set_vring_addr: 0x%"PRIx64"/0x%"PRIx64"/0x%"PRIx64, msg.payload.addr.avail_user_addr, msg.payload.addr.desc_user_addr, msg.payload.addr.used_user_addr); @@ -462,7 +462,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) case VHOST_USER_SET_VRING_CALL: /* consume the fd */ if (!qemu_chr_fe_get_msgfds(chr, &fd, 1) && fd < 0) { - g_test_message("call fd: %d, do not set non-blocking\n", fd); + g_test_message("call fd: %d, do not set non-blocking", fd); break; } /* @@ -507,12 +507,12 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * fully functioning vhost-user we would enable/disable the * vring monitoring. */ - g_test_message("set_vring(%d)=%s\n", msg.payload.state.index, + g_test_message("set_vring(%d)=%s", msg.payload.state.index, msg.payload.state.num ? "enabled" : "disabled"); break; default: - g_test_message("vhost-user: un-handled message: %d\n", msg.request); + g_test_message("vhost-user: un-handled message: %d", msg.request); break; } From 1c0f5142d921525f1023152eac63d2ff3d33e3b2 Mon Sep 17 00:00:00 2001 From: ShengYi Hung Date: Mon, 15 Sep 2025 13:57:30 -0400 Subject: [PATCH 1216/1794] hid: fix incorrect return value for hid The return value of hid_keyboard_write is used to set the packet's actual_length and pass to xhci directly to allow guest know how many byte actually processed. Therefore, return 1 to indicate a successful transfer or it will be considered as a wrong xfer. Signed-off-by: ShengYi Hung Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- hw/input/hid.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/input/hid.c b/hw/input/hid.c index 76bedc18443bf..de24cd0ef0452 100644 --- a/hw/input/hid.c +++ b/hw/input/hid.c @@ -478,6 +478,7 @@ int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len) ledstate |= QEMU_CAPS_LOCK_LED; } kbd_put_ledstate(ledstate); + return 1; } return 0; } From 5240186e82b9d0f8e81f1e8f74348a4fa089998a Mon Sep 17 00:00:00 2001 From: ShengYi Hung Date: Mon, 15 Sep 2025 14:07:33 -0400 Subject: [PATCH 1217/1794] wdt_i6300esb: fix incorrect mask for interrupt type According to Intel 6300ESB Controller Hub Datasheet 14.4.15, the interrupt type mask should be 0x03 (0b11) instead of 0x11. In the original implementation, when we want to disable all interrupt by setting the value to 0x03, we will get 0x01 which is incorrect when we want to read the value again. However, there is no problem when considering the correct behavior since 0x01 is reserved and unused just like 0x03. This patch is just a fix to return the register value. Signed-off-by: ShengYi Hung Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- hw/watchdog/wdt_i6300esb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c index bb8a2766b69b6..3aa01b8d68fe9 100644 --- a/hw/watchdog/wdt_i6300esb.c +++ b/hw/watchdog/wdt_i6300esb.c @@ -55,7 +55,7 @@ /* Config register bits */ #define ESB_WDT_REBOOT (0x01 << 5) /* Enable reboot on timeout */ #define ESB_WDT_FREQ (0x01 << 2) /* Decrement frequency */ -#define ESB_WDT_INTTYPE (0x11 << 0) /* Interrupt type on timer1 timeout */ +#define ESB_WDT_INTTYPE (0x03 << 0) /* Interrupt type on timer1 timeout */ /* Reload register bits */ #define ESB_WDT_RELOAD (0x01 << 8) /* prevent timeout */ From f65918497cc6b9034ce8f81a4df1d6407e110367 Mon Sep 17 00:00:00 2001 From: nanliu Date: Mon, 22 Sep 2025 14:57:14 +0800 Subject: [PATCH 1218/1794] docs/devel: Correct uefi-vars-x64 device name The documentation for UEFI variable storage in uefi-vars.rst incorrectly listed the device name as `uefi-vars-x86`. The correct device name as implemented in the source code is `uefi-vars-x64`. This commit updates the documentation to use the correct name, aligning it with the implementation. Signed-off-by: Nana Liu Reviewed-by: Thomas Huth Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- docs/devel/uefi-vars.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/devel/uefi-vars.rst b/docs/devel/uefi-vars.rst index 0151a26a0a6f8..b4013b5d12e54 100644 --- a/docs/devel/uefi-vars.rst +++ b/docs/devel/uefi-vars.rst @@ -34,7 +34,7 @@ configures the shared buffer location and size, and traps to the host to process the requests. The ``uefi-vars`` device implements the UEFI virtual device. It comes -in ``uefi-vars-x86`` and ``uefi-vars-sysbus`` flavours. The device +in ``uefi-vars-x64`` and ``uefi-vars-sysbus`` flavours. The device reimplements the handlers needed, specifically ``EfiSmmVariableProtocol`` and ``VarCheckPolicyLibMmiHandler``. It also consumes events (``EfiEndOfDxeEventGroup``, @@ -57,7 +57,7 @@ usage on x86_64 .. code:: qemu-system-x86_64 \ - -device uefi-vars-x86,jsonfile=/path/to/vars.json + -device uefi-vars-x64,jsonfile=/path/to/vars.json usage on aarch64 ---------------- From 9348c33779a7313c5fc8b24935d71f920138e3d7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 23 Sep 2025 13:01:18 +0100 Subject: [PATCH 1219/1794] docs/specs/spdm.rst: Fix typo in x86_64 architecture name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The spdm.rst docs call the 64-bit x86 architecture "x64-64". This is a typo; correct it to our canonical name for the architecture, "x86_64". Signed-off-by: Peter Maydell Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- docs/specs/spdm.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/specs/spdm.rst b/docs/specs/spdm.rst index f7de080ff0b45..0e3ad25bc698f 100644 --- a/docs/specs/spdm.rst +++ b/docs/specs/spdm.rst @@ -102,7 +102,7 @@ Then you can add this to your QEMU command line: At which point QEMU will try to connect to the SPDM server. -Note that if using x64-64 you will want to use the q35 machine instead +Note that if using x86_64 you will want to use the q35 machine instead of the default. So the entire QEMU command might look like this .. code-block:: shell From 0af5926a0feb0b69a127f5c24eaa605408fed2ec Mon Sep 17 00:00:00 2001 From: Marco Cavenati Date: Thu, 2 Oct 2025 16:41:45 +0200 Subject: [PATCH 1220/1794] system/runstate: remove duplicate in runstate transitions Remove duplicate entry PRELAUNCH->INMIGRATE from runstate_transitions_def. Move PRELAUNCH->SUSPENDED entry with all the other PRELAUNCH->XXX entries. Signed-off-by: Marco Cavenati Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- system/runstate.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/system/runstate.c b/system/runstate.c index 6178b0091a3e1..32467aa882543 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -76,9 +76,6 @@ typedef struct { } RunStateTransition; static const RunStateTransition runstate_transitions_def[] = { - { RUN_STATE_PRELAUNCH, RUN_STATE_INMIGRATE }, - { RUN_STATE_PRELAUNCH, RUN_STATE_SUSPENDED }, - { RUN_STATE_DEBUG, RUN_STATE_RUNNING }, { RUN_STATE_DEBUG, RUN_STATE_FINISH_MIGRATE }, { RUN_STATE_DEBUG, RUN_STATE_PRELAUNCH }, @@ -118,6 +115,7 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_PRELAUNCH, RUN_STATE_RUNNING }, { RUN_STATE_PRELAUNCH, RUN_STATE_FINISH_MIGRATE }, { RUN_STATE_PRELAUNCH, RUN_STATE_INMIGRATE }, + { RUN_STATE_PRELAUNCH, RUN_STATE_SUSPENDED }, { RUN_STATE_FINISH_MIGRATE, RUN_STATE_RUNNING }, { RUN_STATE_FINISH_MIGRATE, RUN_STATE_PAUSED }, From 79b10b6639456d244118cf564214495637494931 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:04:05 +0200 Subject: [PATCH 1221/1794] tests/acpi: virt: allow acpi table changes at DSDT and HEST tables We'll be adding a new GED device for HEST GPIO notification and increasing the number of entries at the HEST table. Blocklist testing HEST and DSDT tables until such changes are completed. Signed-off-by: Mauro Carvalho Chehab Acked-by: Igor Mammedov Reviewed-by: Jonathan Cameron Reviewed-by: Michael S. Tsirkin Message-ID: <7fca7eb9b801f1b196210f66538234b94bd31c23.1758610789.git.mchehab+huawei@kernel.org> Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test-allowed-diff.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8bf45..cb4b0a94691f7 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,12 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/aarch64/virt/HEST", +"tests/data/acpi/aarch64/virt/DSDT", +"tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt", +"tests/data/acpi/aarch64/virt/DSDT.acpipcihp", +"tests/data/acpi/aarch64/virt/DSDT.hpoffacpiindex", +"tests/data/acpi/aarch64/virt/DSDT.memhp", +"tests/data/acpi/aarch64/virt/DSDT.pxb", +"tests/data/acpi/aarch64/virt/DSDT.topology", +"tests/data/acpi/aarch64/virt/DSDT.viot", +"tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev", +"tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy", From d352e33e1f92e60e12a00be1ea80f47b4e024c88 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:04:06 +0200 Subject: [PATCH 1222/1794] arm/virt: Wire up a GED error device for ACPI / GHES Adds support to ARM virtualization to allow handling generic error ACPI Event via GED & error source device. It is aligned with Linux Kernel patch: https://lore.kernel.org/lkml/1272350481-27951-8-git-send-email-ying.huang@intel.com/ Co-authored-by: Mauro Carvalho Chehab Co-authored-by: Jonathan Cameron Signed-off-by: Jonathan Cameron Signed-off-by: Mauro Carvalho Chehab Acked-by: Igor Mammedov Reviewed-by: Jonathan Cameron Reviewed-by: Michael S. Tsirkin Message-ID: <3237a76b1469d669436399495825348bf34122cd.1758610789.git.mchehab+huawei@kernel.org> Signed-off-by: Michael S. Tsirkin --- hw/arm/virt-acpi-build.c | 1 + hw/arm/virt.c | 12 +++++++++++- include/hw/arm/virt.h | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index ff3b7a794b84b..2b63008df01da 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -1066,6 +1066,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } acpi_dsdt_add_power_button(scope); + aml_append(scope, aml_error_device()); #ifdef CONFIG_TPM acpi_dsdt_add_tpm(scope, vms); #endif diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 02209fadcfac9..6960f6113fefe 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -693,7 +693,7 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms) MachineState *ms = MACHINE(vms); SysBusDevice *sbdev; int irq = vms->irqmap[VIRT_ACPI_GED]; - uint32_t event = ACPI_GED_PWR_DOWN_EVT; + uint32_t event = ACPI_GED_PWR_DOWN_EVT | ACPI_GED_ERROR_EVT; bool acpi_pcihp; if (ms->ram_slots) { @@ -1050,6 +1050,13 @@ static void virt_powerdown_req(Notifier *n, void *opaque) } } +static void virt_generic_error_req(Notifier *n, void *opaque) +{ + VirtMachineState *s = container_of(n, VirtMachineState, generic_error_notifier); + + acpi_send_event(s->acpi_dev, ACPI_GENERIC_ERROR); +} + static void create_gpio_keys(char *fdt, DeviceState *pl061_dev, uint32_t phandle) { @@ -2500,6 +2507,9 @@ static void machvirt_init(MachineState *machine) if (has_ged && aarch64 && firmware_loaded && virt_is_acpi_enabled(vms)) { vms->acpi_dev = create_acpi_ged(vms); + vms->generic_error_notifier.notify = virt_generic_error_req; + notifier_list_add(&acpi_generic_error_notifiers, + &vms->generic_error_notifier); } else { create_gpio_devices(vms, VIRT_GPIO, sysmem); } diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index ea2cff05b02f2..e14ea0f9d42a9 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -174,6 +174,7 @@ struct VirtMachineState { DeviceState *gic; DeviceState *acpi_dev; Notifier powerdown_notifier; + Notifier generic_error_notifier; PCIBus *bus; char *oem_id; char *oem_table_id; From ddd8f3baa27dc05251f52e94d1b643bfb93cbc83 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:04:07 +0200 Subject: [PATCH 1223/1794] qapi/acpi-hest: add an interface to do generic CPER error injection Create a QMP command to be used for generic ACPI APEI hardware error injection (HEST) via GHESv2, and add support for it for ARM guests. Error injection uses ACPI_HEST_SRC_ID_QMP source ID to be platform independent. This is mapped at arch virt bindings, depending on the types supported by QEMU and by the BIOS. So, on ARM, this is supported via ACPI_GHES_NOTIFY_GPIO notification type. This patch was co-authored: - original ghes logic to inject a simple ARM record by Shiju Jose; - generic logic to handle block addresses by Jonathan Cameron; - generic GHESv2 error inject by Mauro Carvalho Chehab; Co-authored-by: Jonathan Cameron Co-authored-by: Shiju Jose Co-authored-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Cameron Signed-off-by: Shiju Jose Signed-off-by: Mauro Carvalho Chehab Acked-by: Igor Mammedov Acked-by: Markus Armbruster Reviewed-by: Michael S. Tsirkin Message-ID: <81e2118b3c8b7e5da341817f277d61251655e0db.1758610789.git.mchehab+huawei@kernel.org> Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 7 +++++++ hw/acpi/Kconfig | 5 +++++ hw/acpi/ghes.c | 2 +- hw/acpi/ghes_cper.c | 40 ++++++++++++++++++++++++++++++++++++++++ hw/acpi/ghes_cper_stub.c | 20 ++++++++++++++++++++ hw/acpi/meson.build | 2 ++ hw/arm/virt-acpi-build.c | 1 + hw/arm/virt.c | 7 +++++++ include/hw/acpi/ghes.h | 1 + include/hw/arm/virt.h | 1 + qapi/acpi-hest.json | 36 ++++++++++++++++++++++++++++++++++++ qapi/meson.build | 1 + qapi/qapi-schema.json | 1 + 13 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 hw/acpi/ghes_cper.c create mode 100644 hw/acpi/ghes_cper_stub.c create mode 100644 qapi/acpi-hest.json diff --git a/MAINTAINERS b/MAINTAINERS index 406cef88f0ca8..b31ae5193d93c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2173,6 +2173,13 @@ F: hw/acpi/ghes.c F: include/hw/acpi/ghes.h F: docs/specs/acpi_hest_ghes.rst +ACPI/HEST/GHES/ARM processor CPER +R: Mauro Carvalho Chehab +S: Maintained +F: hw/arm/ghes_cper.c +F: hw/acpi/ghes_cper_stub.c +F: qapi/acpi-hest.json + ppc4xx L: qemu-ppc@nongnu.org S: Orphan diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig index 1d4e9f0845c07..daabbe6cd11e2 100644 --- a/hw/acpi/Kconfig +++ b/hw/acpi/Kconfig @@ -51,6 +51,11 @@ config ACPI_APEI bool depends on ACPI +config GHES_CPER + bool + depends on ACPI_APEI + default y + config ACPI_PCI bool depends on ACPI && PCI diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index d666f1b10b5ff..06555905cebbf 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -553,7 +553,7 @@ void ghes_record_cper_errors(AcpiGhesState *ags, const void *cper, size_t len, /* Write the generic error data entry into guest memory */ cpu_physical_memory_write(cper_addr, cper, len); - notifier_list_notify(&acpi_generic_error_notifiers, NULL); + notifier_list_notify(&acpi_generic_error_notifiers, &source_id); } int acpi_ghes_memory_errors(AcpiGhesState *ags, uint16_t source_id, diff --git a/hw/acpi/ghes_cper.c b/hw/acpi/ghes_cper.c new file mode 100644 index 0000000000000..31cb2ffabeb48 --- /dev/null +++ b/hw/acpi/ghes_cper.c @@ -0,0 +1,40 @@ +/* + * CPER payload parser for error injection + * + * Copyright(C) 2024-2025 Huawei LTD. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" + +#include "qemu/base64.h" +#include "qemu/error-report.h" +#include "qemu/uuid.h" +#include "qapi/qapi-commands-acpi-hest.h" +#include "hw/acpi/ghes.h" + +void qmp_inject_ghes_v2_error(const char *qmp_cper, Error **errp) +{ + AcpiGhesState *ags; + uint8_t *cper; + size_t len; + + ags = acpi_ghes_get_state(); + if (!ags) { + return; + } + + cper = qbase64_decode(qmp_cper, -1, &len, errp); + if (!cper) { + error_setg(errp, "missing GHES CPER payload"); + return; + } + + ghes_record_cper_errors(ags, cper, len, ACPI_HEST_SRC_ID_QMP, errp); + + g_free(cper); +} diff --git a/hw/acpi/ghes_cper_stub.c b/hw/acpi/ghes_cper_stub.c new file mode 100644 index 0000000000000..b16be73502dbe --- /dev/null +++ b/hw/acpi/ghes_cper_stub.c @@ -0,0 +1,20 @@ +/* + * Stub interface for CPER payload parser for error injection + * + * Copyright(C) 2024-2025 Huawei LTD. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-acpi-hest.h" +#include "hw/acpi/ghes.h" + +void qmp_inject_ghes_v2_error(const char *cper, Error **errp) +{ + error_setg(errp, "GHES QMP error inject is not compiled in"); +} diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build index 73f02b96912ba..56b5d1ec9691b 100644 --- a/hw/acpi/meson.build +++ b/hw/acpi/meson.build @@ -34,4 +34,6 @@ endif system_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c', 'acpi_interface.c')) system_ss.add(when: 'CONFIG_ACPI_PCI_BRIDGE', if_false: files('pci-bridge-stub.c')) system_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss) +system_ss.add(when: 'CONFIG_GHES_CPER', if_true: files('ghes_cper.c')) +system_ss.add(when: 'CONFIG_GHES_CPER', if_false: files('ghes_cper_stub.c')) system_ss.add(files('acpi-qmp-cmds.c')) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 2b63008df01da..8bb6b60515484 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -1128,6 +1128,7 @@ static void acpi_align_size(GArray *blob, unsigned align) static const AcpiNotificationSourceId hest_ghes_notify[] = { { ACPI_HEST_SRC_ID_SYNC, ACPI_GHES_NOTIFY_SEA }, + { ACPI_HEST_SRC_ID_QMP, ACPI_GHES_NOTIFY_GPIO }, }; static const AcpiNotificationSourceId hest_ghes_notify_10_0[] = { diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 6960f6113fefe..aad557be1ae3f 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1052,6 +1052,13 @@ static void virt_powerdown_req(Notifier *n, void *opaque) static void virt_generic_error_req(Notifier *n, void *opaque) { + uint16_t *source_id = opaque; + + /* Currently, only QMP source ID is async */ + if (*source_id != ACPI_HEST_SRC_ID_QMP) { + return; + } + VirtMachineState *s = container_of(n, VirtMachineState, generic_error_notifier); acpi_send_event(s->acpi_dev, ACPI_GENERIC_ERROR); diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h index 390943e46d99e..df2ecbf6e4a9e 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -65,6 +65,7 @@ enum AcpiGhesNotifyType { */ enum AcpiGhesSourceID { ACPI_HEST_SRC_ID_SYNC, + ACPI_HEST_SRC_ID_QMP, /* Use it only for QMP injected errors */ }; typedef struct AcpiNotificationSourceId { diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index e14ea0f9d42a9..04a09af354061 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -33,6 +33,7 @@ #include "exec/hwaddr.h" #include "qemu/notify.h" #include "hw/boards.h" +#include "hw/acpi/ghes.h" #include "hw/arm/boot.h" #include "hw/arm/bsa.h" #include "hw/block/flash.h" diff --git a/qapi/acpi-hest.json b/qapi/acpi-hest.json new file mode 100644 index 0000000000000..28af1266a7772 --- /dev/null +++ b/qapi/acpi-hest.json @@ -0,0 +1,36 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# SPDX-License-Identifier: GPL-2.0-or-later + +## +# == GHESv2 CPER Error Injection +# +# Defined since ACPI Specification 6.1, +# section 18.3.2.8 Generic Hardware Error Source version 2. See: +# +# https://uefi.org/sites/default/files/resources/ACPI_6_1.pdf +## + + +## +# @inject-ghes-v2-error: +# +# Inject an error with additional ACPI 6.1 GHESv2 error information +# +# @cper: contains a base64 encoded string with raw data for a single +# CPER record with Generic Error Status Block, Generic Error Data +# Entry and generic error data payload, as described at +# https://uefi.org/specs/UEFI/2.10/Apx_N_Common_Platform_Error_Record.html#format +# +# Features: +# +# @unstable: This command is experimental. +# +# Since: 10.2 +## +{ 'command': 'inject-ghes-v2-error', + 'data': { + 'cper': 'str' + }, + 'features': [ 'unstable' ] +} diff --git a/qapi/meson.build b/qapi/meson.build index ca6b61a608d03..a46269b5a0c92 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -59,6 +59,7 @@ if have_system qapi_all_modules += [ 'accelerator', 'acpi', + 'acpi-hest', 'audio', 'cryptodev', 'qdev', diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 82f111ba063cb..b93dd68d94c63 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -68,6 +68,7 @@ { 'include': 'misc-i386.json' } { 'include': 'audio.json' } { 'include': 'acpi.json' } +{ 'include': 'acpi-hest.json' } { 'include': 'pci.json' } { 'include': 'stats.json' } { 'include': 'virtio.json' } From ecd06271dc3ec2e1963f9c294201fc6337ca7779 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:04:08 +0200 Subject: [PATCH 1224/1794] acpi/generic_event_device.c: enable use_hest_addr for QEMU 10.x Now that we have everything in place, enable using HEST GPA instead of etc/hardware_errors GPA. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Message-ID: Signed-off-by: Michael S. Tsirkin --- hw/acpi/generic_event_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index e575b9404be46..e7b773d84d50d 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -363,7 +363,7 @@ static const Property acpi_ged_properties[] = { DEFINE_PROP_LINK("bus", AcpiGedState, pcihp_state.root, TYPE_PCI_BUS, PCIBus *), DEFINE_PROP_BOOL("x-has-hest-addr", AcpiGedState, - ghes_state.use_hest_addr, false), + ghes_state.use_hest_addr, true), }; static const VMStateDescription vmstate_memhp_state = { From 237ca3d57898e4438bc3af6cd87f0a3279198bae Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:04:09 +0200 Subject: [PATCH 1225/1794] tests/acpi: virt: update HEST and DSDT tables The following changes for DSDT affecting all files under tests/data/acpi/aarch64/virt/DSDT* : -"tests/data/acpi/aarch64/virt/DSDT", -"tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt", -"tests/data/acpi/aarch64/virt/DSDT.acpipcihp", -"tests/data/acpi/aarch64/virt/DSDT.hpoffacpiindex", -"tests/data/acpi/aarch64/virt/DSDT.memhp", -"tests/data/acpi/aarch64/virt/DSDT.pxb", -"tests/data/acpi/aarch64/virt/DSDT.topology", -"tests/data/acpi/aarch64/virt/DSDT.viot", -"tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev", -"tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy", --- /tmp/DSDT_old.dsl 2025-09-05 15:03:18.964968499 +0200 +++ /tmp/DSDT.dsl 2025-09-05 15:03:18.966968470 +0200 @@ -1886,6 +1886,11 @@ { Notify (PWRB, 0x80) // Status Change } + + If (((Local0 & 0x20) == 0x20)) + { + Notify (GEDD, 0x80) // Status Change + } } } @@ -1894,6 +1899,12 @@ Name (_HID, "PNP0C0C" /* Power Button Device */) // _HID: Hardware ID Name (_UID, Zero) // _UID: Unique ID } + + Device (GEDD) + { + Name (_HID, "PNP0C33" /* Error Device */) // _HID: Hardware ID + Name (_UID, Zero) // _UID: Unique ID + } } Scope (\_SB.PCI0) Additionally, HEST changes: -"tests/data/acpi/aarch64/virt/HEST", --- /tmp/HEST_old.dsl 2025-09-05 15:03:19.078653625 +0200 +++ /tmp/HEST.dsl 2025-09-05 15:03:19.079511472 +0200 @@ -3,7 +3,7 @@ * AML/ASL+ Disassembler version 20240322 (64-bit version) * Copyright (c) 2000 - 2023 Intel Corporation * - * Disassembly of /tmp/HEST_old + * Disassembly of /tmp/HEST * * ACPI Data Table [HEST] * @@ -11,16 +11,16 @@ */ [000h 0000 004h] Signature : "HEST" [Hardware Error Source Table] -[004h 0004 004h] Table Length : 00000084 +[004h 0004 004h] Table Length : 000000E0 [008h 0008 001h] Revision : 01 -[009h 0009 001h] Checksum : E2 +[009h 0009 001h] Checksum : 6C [00Ah 0010 006h] Oem ID : "BOCHS " [010h 0016 008h] Oem Table ID : "BXPC " [018h 0024 004h] Oem Revision : 00000001 [01Ch 0028 004h] Asl Compiler ID : "BXPC" [020h 0032 004h] Asl Compiler Revision : 00000001 -[024h 0036 004h] Error Source Count : 00000001 +[024h 0036 004h] Error Source Count : 00000002 [028h 0040 002h] Subtable Type : 000A [Generic Hardware Error Source V2] [02Ah 0042 002h] Source Id : 0000 @@ -55,19 +55,62 @@ [069h 0105 001h] Bit Width : 40 [06Ah 0106 001h] Bit Offset : 00 [06Bh 0107 001h] Encoded Access Width : 04 [QWord Access:64] -[06Ch 0108 008h] Address : 0000000043DA0008 +[06Ch 0108 008h] Address : 0000000043DA0010 [074h 0116 008h] Read Ack Preserve : FFFFFFFFFFFFFFFE [07Ch 0124 008h] Read Ack Write : 0000000000000001 -Raw Table Data: Length 132 (0x84) +[084h 0132 002h] Subtable Type : 000A [Generic Hardware Error Source V2] +[086h 0134 002h] Source Id : 0001 +[088h 0136 002h] Related Source Id : FFFF +[08Ah 0138 001h] Reserved : 00 +[08Bh 0139 001h] Enabled : 01 +[08Ch 0140 004h] Records To Preallocate : 00000001 +[090h 0144 004h] Max Sections Per Record : 00000001 +[094h 0148 004h] Max Raw Data Length : 00000400 + +[098h 0152 00Ch] Error Status Address : [Generic Address Structure] +[098h 0152 001h] Space ID : 00 [SystemMemory] +[099h 0153 001h] Bit Width : 40 +[09Ah 0154 001h] Bit Offset : 00 +[09Bh 0155 001h] Encoded Access Width : 04 [QWord Access:64] +[09Ch 0156 008h] Address : 0000000043DA0008 + +[0A4h 0164 01Ch] Notify : [Hardware Error Notification Structure] +[0A4h 0164 001h] Notify Type : 07 [GPIO] +[0A5h 0165 001h] Notify Length : 1C +[0A6h 0166 002h] Configuration Write Enable : 0000 +[0A8h 0168 004h] PollInterval : 00000000 +[0ACh 0172 004h] Vector : 00000000 +[0B0h 0176 004h] Polling Threshold Value : 00000000 +[0B4h 0180 004h] Polling Threshold Window : 00000000 +[0B8h 0184 004h] Error Threshold Value : 00000000 +[0BCh 0188 004h] Error Threshold Window : 00000000 + +[0C0h 0192 004h] Error Status Block Length : 00000400 +[0C4h 0196 00Ch] Read Ack Register : [Generic Address Structure] +[0C4h 0196 001h] Space ID : 00 [SystemMemory] +[0C5h 0197 001h] Bit Width : 40 +[0C6h 0198 001h] Bit Offset : 00 +[0C7h 0199 001h] Encoded Access Width : 04 [QWord Access:64] +[0C8h 0200 008h] Address : 0000000043DA0018 - 0000: 48 45 53 54 84 00 00 00 01 E2 42 4F 43 48 53 20 // HEST......BOCHS +[0D0h 0208 008h] Read Ack Preserve : FFFFFFFFFFFFFFFE +[0D8h 0216 008h] Read Ack Write : 0000000000000001 + +Raw Table Data: Length 224 (0xE0) + + 0000: 48 45 53 54 E0 00 00 00 01 6C 42 4F 43 48 53 20 // HEST.....lBOCHS 0010: 42 58 50 43 20 20 20 20 01 00 00 00 42 58 50 43 // BXPC ....BXPC - 0020: 01 00 00 00 01 00 00 00 0A 00 00 00 FF FF 00 01 // ................ + 0020: 01 00 00 00 02 00 00 00 0A 00 00 00 FF FF 00 01 // ................ 0030: 01 00 00 00 01 00 00 00 00 04 00 00 00 40 00 04 // .............@.. 0040: 00 00 DA 43 00 00 00 00 08 1C 00 00 00 00 00 00 // ...C............ 0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // ................ - 0060: 00 00 00 00 00 04 00 00 00 40 00 04 08 00 DA 43 // .........@.....C + 0060: 00 00 00 00 00 04 00 00 00 40 00 04 10 00 DA 43 // .........@.....C 0070: 00 00 00 00 FE FF FF FF FF FF FF FF 01 00 00 00 // ................ - 0080: 00 00 00 00 // .... + 0080: 00 00 00 00 0A 00 01 00 FF FF 00 01 01 00 00 00 // ................ + 0090: 01 00 00 00 00 04 00 00 00 40 00 04 08 00 DA 43 // .........@.....C + 00A0: 00 00 00 00 07 1C 00 00 00 00 00 00 00 00 00 00 // ................ + 00B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // ................ + 00C0: 00 04 00 00 00 40 00 04 18 00 DA 43 00 00 00 00 // .....@.....C.... + 00D0: FE FF FF FF FF FF FF FF 01 00 00 00 00 00 00 00 // ................ Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Acked-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Message-ID: <2253eb50df797ab320b4ca610bd22a38e5cfd17a.1758610789.git.mchehab+huawei@kernel.org> Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/aarch64/virt/DSDT | Bin 5293 -> 5337 bytes .../data/acpi/aarch64/virt/DSDT.acpihmatvirt | Bin 5379 -> 5423 bytes tests/data/acpi/aarch64/virt/DSDT.acpipcihp | Bin 6202 -> 6246 bytes .../acpi/aarch64/virt/DSDT.hpoffacpiindex | Bin 5347 -> 5391 bytes tests/data/acpi/aarch64/virt/DSDT.memhp | Bin 6654 -> 6698 bytes tests/data/acpi/aarch64/virt/DSDT.pxb | Bin 7768 -> 7812 bytes tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev | Bin 10230 -> 10274 bytes .../data/acpi/aarch64/virt/DSDT.smmuv3-legacy | Bin 10230 -> 10274 bytes tests/data/acpi/aarch64/virt/DSDT.topology | Bin 5495 -> 5539 bytes tests/data/acpi/aarch64/virt/DSDT.viot | Bin 5310 -> 5354 bytes tests/data/acpi/aarch64/virt/HEST | Bin 132 -> 224 bytes tests/qtest/bios-tables-test-allowed-diff.h | 11 ----------- 12 files changed, 11 deletions(-) diff --git a/tests/data/acpi/aarch64/virt/DSDT b/tests/data/acpi/aarch64/virt/DSDT index 18d97e8f22979411a528705c0e314acb424bbfa5..38f01adb61e6e4704821cee5e397888bb6b7e46d 100644 GIT binary patch delta 83 zcmZ3hc~g_iCD96aWAK delta 68 zcmZ3l)vU$k66_MfEXu&Zv}_`mG*i3lM)ecIOit{RKMDs+%f`Egg>V+Q2D|zsED)Gn YoxsJ!z{S)S5FX?-xj;mA@(Pi50D|okbpQYW diff --git a/tests/data/acpi/aarch64/virt/DSDT.acpipcihp b/tests/data/acpi/aarch64/virt/DSDT.acpipcihp index 8d55a877a40cb4c4dffdc70378204e12d2261a75..04427e2d8eb8d2db0a7ae3dbe546d9072406d09b 100644 GIT binary patch delta 87 zcmdmG@XUbACDGDJ_X{NsE8`X=1nVh&M&lV15@@1X;LD-9bfxzVI1TF;z nE`>ICR~MJf79#enBGFBfAPz^oho=i~fM0-tv$65y0?9rA9#a?T delta 48 zcmZ2w^3RydCDt4B0b%0;xfgGB%#PMs^JVJ^~$7 delta 46 zcmZp%y{0+f CX%1!p diff --git a/tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev b/tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev index 53d4c07f423886d8c4b57f1da6498eef5a08b556..e8c2b376df7bddc2392945ea8cbb550b3d3b5e26 100644 GIT binary patch delta 107 zcmez7zbJsqCDP_ZUmvLAiFu6K`OM!t) zq0QaZ#f7UOx=Au1JjjV7-ow*{H^48zz}di=fg?WD(}e*j1yX|~Wo$gzQhhG~Lp2=r delta 63 zcmZ1!@XepgCDqsn(}Rgz(oEfj8`XQ%n4H)rZ&eFsl%33}E+Z1%BpDDMP_ZUmvLAiFu6K`OM!t) zq0QaZ#f7UOx=Au1JjjV7-ow*{H^48zz}di=fg?WD(}e*j1yX|~Wo$gzQhhG~Lp2=r delta 63 zcmZ1!@XepgCDqsn(}Rgz(oEfj8`XQ%n4H)rZ&eFsl%33}E+Z1%BpDDMP_A$BIB??U~+W=mjVNq zLYup*iwjpnbdzL2c#soEyoaX?Z-8HbfwO@#14n$Qrwap63Zw=}%Gh}FOOZYREs-59 delta 63 zcmZ3i{auU8CDHI`4X{K(Ujp{ZcOit{RQ$>OqWhd_xkr9b*k_-qBa^i^h T@O0q~@Cz_-HgKLSD%uACG~E#{ diff --git a/tests/data/acpi/aarch64/virt/DSDT.viot b/tests/data/acpi/aarch64/virt/DSDT.viot index b897d667971500da4732000091a6f0828d05d89e..dd3775a0762ae1a5ddb89dd656d81eee581dccb6 100644 GIT binary patch delta 83 zcmdm|`AU<^CDP>zxEW^J*U~+W=mjVNq lLYup*i_2tA5m}MwCP@&RBi_T)g*U)2z`)tqc(T06d;r-X7McJ6 delta 46 zcmaE*xlfbJCDEGJ@nLCJHmL%S;Ru UnV7)J#lXM<6mwu;kpQX#00=P+djJ3c delta 29 lcmaFB*uu!=;Tjy$!oa}5_-G=R6eHtARriT=I3|_|004Ge2nqlI diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index cb4b0a94691f7..dfb8523c8bf45 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,12 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/aarch64/virt/HEST", -"tests/data/acpi/aarch64/virt/DSDT", -"tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt", -"tests/data/acpi/aarch64/virt/DSDT.acpipcihp", -"tests/data/acpi/aarch64/virt/DSDT.hpoffacpiindex", -"tests/data/acpi/aarch64/virt/DSDT.memhp", -"tests/data/acpi/aarch64/virt/DSDT.pxb", -"tests/data/acpi/aarch64/virt/DSDT.topology", -"tests/data/acpi/aarch64/virt/DSDT.viot", -"tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev", -"tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy", From 92aad3fc4a0f511819a18a443797069aff77b9ba Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:04:10 +0200 Subject: [PATCH 1226/1794] docs: hest: add new "etc/acpi_table_hest_addr" and update workflow While the HEST layout didn't change, there are some internal changes related to how offsets are calculated and how memory error events are triggered. Update specs to reflect such changes. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Message-ID: Signed-off-by: Michael S. Tsirkin --- docs/specs/acpi_hest_ghes.rst | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/docs/specs/acpi_hest_ghes.rst b/docs/specs/acpi_hest_ghes.rst index c3e9f8d9a7023..aaf7b1ad11a5f 100644 --- a/docs/specs/acpi_hest_ghes.rst +++ b/docs/specs/acpi_hest_ghes.rst @@ -89,12 +89,21 @@ Design Details addresses in the "error_block_address" fields with a pointer to the respective "Error Status Data Block" in the "etc/hardware_errors" blob. -(8) QEMU defines a third and write-only fw_cfg blob which is called - "etc/hardware_errors_addr". Through that blob, the firmware can send back - the guest-side allocation addresses to QEMU. The "etc/hardware_errors_addr" - blob contains a 8-byte entry. QEMU generates a single WRITE_POINTER command - for the firmware. The firmware will write back the start address of - "etc/hardware_errors" blob to the fw_cfg file "etc/hardware_errors_addr". +(8) QEMU defines a third and write-only fw_cfg blob to store the location + where the error block offsets, read ack registers and CPER records are + stored. + + Up to QEMU 9.2, the location was at "etc/hardware_errors_addr", and + contains a GPA for the beginning of "etc/hardware_errors". + + Newer versions place the location at "etc/acpi_table_hest_addr", + pointing to the GPA of the HEST table. + + Using above mentioned 'fw_cfg' files, the firmware can send back the + guest-side allocation addresses to QEMU. They contain a 8-byte entry. + QEMU generates a single WRITE_POINTER command for the firmware. The + firmware will write back the start address of either "etc/hardware_errors" + or HEST table at the corresponding fw_cfg file. (9) When QEMU gets a SIGBUS from the kernel, QEMU writes CPER into corresponding "Error Status Data Block", guest memory, and then injects platform specific @@ -105,8 +114,5 @@ Design Details kernel, on receiving notification, guest APEI driver could read the CPER error and take appropriate action. -(11) kvm_arch_on_sigbus_vcpu() uses source_id as index in "etc/hardware_errors" to - find out "Error Status Data Block" entry corresponding to error source. So supported - source_id values should be assigned here and not be changed afterwards to make sure - that guest will write error into expected "Error Status Data Block" even if guest was - migrated to a newer QEMU. +(11) kvm_arch_on_sigbus_vcpu() reports RAS errors via a SEA notifications, + when a SIGBUS event is triggered. From 4cc103081b170a80afe3344da4899b525fd4d0a8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Sep 2025 09:04:11 +0200 Subject: [PATCH 1227/1794] scripts/ghes_inject: add a script to generate GHES error inject Using the QMP GHESv2 API requires preparing a raw data array containing a CPER record. Add a helper script with subcommands to prepare such data. Currently, only ARM Processor error CPER record is supported, by using: $ ghes_inject.py arm which produces those warnings on Linux: [ 705.032426] [Firmware Warn]: GHES: Unhandled processor error type 0x02: cache error [ 774.866308] {4}[Hardware Error]: Hardware error from APEI Generic Hardware Error Source: 1 [ 774.866583] {4}[Hardware Error]: event severity: recoverable [ 774.866738] {4}[Hardware Error]: Error 0, type: recoverable [ 774.866889] {4}[Hardware Error]: section_type: ARM processor error [ 774.867048] {4}[Hardware Error]: MIDR: 0x00000000000f0510 [ 774.867189] {4}[Hardware Error]: running state: 0x0 [ 774.867321] {4}[Hardware Error]: Power State Coordination Interface state: 0 [ 774.867511] {4}[Hardware Error]: Error info structure 0: [ 774.867679] {4}[Hardware Error]: num errors: 2 [ 774.867801] {4}[Hardware Error]: error_type: 0x02: cache error [ 774.867962] {4}[Hardware Error]: error_info: 0x000000000091000f [ 774.868124] {4}[Hardware Error]: transaction type: Data Access [ 774.868280] {4}[Hardware Error]: cache error, operation type: Data write [ 774.868465] {4}[Hardware Error]: cache level: 2 [ 774.868592] {4}[Hardware Error]: processor context not corrupted [ 774.868774] [Firmware Warn]: GHES: Unhandled processor error type 0x02: cache error Such script allows customizing the error data, allowing to change all fields at the record. Please use: $ ghes_inject.py arm -h For more details about its usage. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Michael S. Tsirkin Message-ID: <5ea174638e33d23635332fa6d4ae9d751355f127.1758610789.git.mchehab+huawei@kernel.org> Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 3 + scripts/arm_processor_error.py | 476 ++++++++++++++++++++++ scripts/ghes_inject.py | 51 +++ scripts/qmp_helper.py | 703 +++++++++++++++++++++++++++++++++ 4 files changed, 1233 insertions(+) create mode 100644 scripts/arm_processor_error.py create mode 100755 scripts/ghes_inject.py create mode 100755 scripts/qmp_helper.py diff --git a/MAINTAINERS b/MAINTAINERS index b31ae5193d93c..75e1fa5c307ea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2179,6 +2179,9 @@ S: Maintained F: hw/arm/ghes_cper.c F: hw/acpi/ghes_cper_stub.c F: qapi/acpi-hest.json +F: scripts/ghes_inject.py +F: scripts/arm_processor_error.py +F: scripts/qmp_helper.py ppc4xx L: qemu-ppc@nongnu.org diff --git a/scripts/arm_processor_error.py b/scripts/arm_processor_error.py new file mode 100644 index 0000000000000..73d069f070d44 --- /dev/null +++ b/scripts/arm_processor_error.py @@ -0,0 +1,476 @@ +#!/usr/bin/env python3 +# +# pylint: disable=C0301,C0114,R0903,R0912,R0913,R0914,R0915,W0511 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2024-2025 Mauro Carvalho Chehab + +# TODO: current implementation has dummy defaults. +# +# For a better implementation, a QMP addition/call is needed to +# retrieve some data for ARM Processor Error injection: +# +# - ARM registers: power_state, mpidr. + +""" +Generate an ARM processor error CPER, compatible with +UEFI 2.9A Errata. + +Injecting such errors can be done using: + + $ ./scripts/ghes_inject.py arm + Error injected. + +Produces a simple CPER register, as detected on a Linux guest: + +[Hardware Error]: Hardware error from APEI Generic Hardware Error Source: 1 +[Hardware Error]: event severity: recoverable +[Hardware Error]: Error 0, type: recoverable +[Hardware Error]: section_type: ARM processor error +[Hardware Error]: MIDR: 0x0000000000000000 +[Hardware Error]: running state: 0x0 +[Hardware Error]: Power State Coordination Interface state: 0 +[Hardware Error]: Error info structure 0: +[Hardware Error]: num errors: 2 +[Hardware Error]: error_type: 0x02: cache error +[Hardware Error]: error_info: 0x000000000091000f +[Hardware Error]: transaction type: Data Access +[Hardware Error]: cache error, operation type: Data write +[Hardware Error]: cache level: 2 +[Hardware Error]: processor context not corrupted +[Firmware Warn]: GHES: Unhandled processor error type 0x02: cache error + +The ARM Processor Error message can be customized via command line +parameters. For instance: + + $ ./scripts/ghes_inject.py arm --mpidr 0x444 --running --affinity 1 \ + --error-info 12345678 --vendor 0x13,123,4,5,1 --ctx-array 0,1,2,3,4,5 \ + -t cache tlb bus micro-arch tlb,micro-arch + Error injected. + +Injects this error, as detected on a Linux guest: + +[Hardware Error]: Hardware error from APEI Generic Hardware Error Source: 1 +[Hardware Error]: event severity: recoverable +[Hardware Error]: Error 0, type: recoverable +[Hardware Error]: section_type: ARM processor error +[Hardware Error]: MIDR: 0x0000000000000000 +[Hardware Error]: Multiprocessor Affinity Register (MPIDR): 0x0000000000000000 +[Hardware Error]: error affinity level: 0 +[Hardware Error]: running state: 0x1 +[Hardware Error]: Power State Coordination Interface state: 0 +[Hardware Error]: Error info structure 0: +[Hardware Error]: num errors: 2 +[Hardware Error]: error_type: 0x02: cache error +[Hardware Error]: error_info: 0x0000000000bc614e +[Hardware Error]: cache level: 2 +[Hardware Error]: processor context not corrupted +[Hardware Error]: Error info structure 1: +[Hardware Error]: num errors: 2 +[Hardware Error]: error_type: 0x04: TLB error +[Hardware Error]: error_info: 0x000000000054007f +[Hardware Error]: transaction type: Instruction +[Hardware Error]: TLB error, operation type: Instruction fetch +[Hardware Error]: TLB level: 1 +[Hardware Error]: processor context not corrupted +[Hardware Error]: the error has not been corrected +[Hardware Error]: PC is imprecise +[Hardware Error]: Error info structure 2: +[Hardware Error]: num errors: 2 +[Hardware Error]: error_type: 0x08: bus error +[Hardware Error]: error_info: 0x00000080d6460fff +[Hardware Error]: transaction type: Generic +[Hardware Error]: bus error, operation type: Generic read (type of instruction or data request cannot be determined) +[Hardware Error]: affinity level at which the bus error occurred: 1 +[Hardware Error]: processor context corrupted +[Hardware Error]: the error has been corrected +[Hardware Error]: PC is imprecise +[Hardware Error]: Program execution can be restarted reliably at the PC associated with the error. +[Hardware Error]: participation type: Local processor observed +[Hardware Error]: request timed out +[Hardware Error]: address space: External Memory Access +[Hardware Error]: memory access attributes:0x20 +[Hardware Error]: access mode: secure +[Hardware Error]: Error info structure 3: +[Hardware Error]: num errors: 2 +[Hardware Error]: error_type: 0x10: micro-architectural error +[Hardware Error]: error_info: 0x0000000078da03ff +[Hardware Error]: Error info structure 4: +[Hardware Error]: num errors: 2 +[Hardware Error]: error_type: 0x14: TLB error|micro-architectural error +[Hardware Error]: Context info structure 0: +[Hardware Error]: register context type: AArch64 EL1 context registers +[Hardware Error]: 00000000: 00000000 00000000 +[Hardware Error]: Vendor specific error info has 5 bytes: +[Hardware Error]: 00000000: 13 7b 04 05 01 .{... +[Firmware Warn]: GHES: Unhandled processor error type 0x02: cache error +[Firmware Warn]: GHES: Unhandled processor error type 0x04: TLB error +[Firmware Warn]: GHES: Unhandled processor error type 0x08: bus error +[Firmware Warn]: GHES: Unhandled processor error type 0x10: micro-architectural error +[Firmware Warn]: GHES: Unhandled processor error type 0x14: TLB error|micro-architectural error +""" + +import argparse +import re + +from qmp_helper import qmp, util, cper_guid + + +class ArmProcessorEinj: + """ + Implements ARM Processor Error injection via GHES + """ + + DESC = """ + Generates an ARM processor error CPER, compatible with + UEFI 2.9A Errata. + """ + + ACPI_GHES_ARM_CPER_LENGTH = 40 + ACPI_GHES_ARM_CPER_PEI_LENGTH = 32 + + # Context types + CONTEXT_AARCH32_EL1 = 1 + CONTEXT_AARCH64_EL1 = 5 + CONTEXT_MISC_REG = 8 + + def __init__(self, subparsers): + """Initialize the error injection class and add subparser""" + + # Valid choice values + self.arm_valid_bits = { + "mpidr": util.bit(0), + "affinity": util.bit(1), + "running": util.bit(2), + "vendor": util.bit(3), + } + + self.pei_flags = { + "first": util.bit(0), + "last": util.bit(1), + "propagated": util.bit(2), + "overflow": util.bit(3), + } + + self.pei_error_types = { + "cache": util.bit(1), + "tlb": util.bit(2), + "bus": util.bit(3), + "micro-arch": util.bit(4), + } + + self.pei_valid_bits = { + "multiple-error": util.bit(0), + "flags": util.bit(1), + "error-info": util.bit(2), + "virt-addr": util.bit(3), + "phy-addr": util.bit(4), + } + + self.data = bytearray() + + parser = subparsers.add_parser("arm", description=self.DESC) + + arm_valid_bits = ",".join(self.arm_valid_bits.keys()) + flags = ",".join(self.pei_flags.keys()) + error_types = ",".join(self.pei_error_types.keys()) + pei_valid_bits = ",".join(self.pei_valid_bits.keys()) + + # UEFI N.16 ARM Validation bits + g_arm = parser.add_argument_group("ARM processor") + g_arm.add_argument("--arm", "--arm-valid", + help=f"ARM valid bits: {arm_valid_bits}") + g_arm.add_argument("-a", "--affinity", "--level", "--affinity-level", + type=lambda x: int(x, 0), + help="Affinity level (when multiple levels apply)") + g_arm.add_argument("-l", "--mpidr", type=lambda x: int(x, 0), + help="Multiprocessor Affinity Register") + g_arm.add_argument("-i", "--midr", type=lambda x: int(x, 0), + help="Main ID Register") + g_arm.add_argument("-r", "--running", + action=argparse.BooleanOptionalAction, + default=None, + help="Indicates if the processor is running or not") + g_arm.add_argument("--psci", "--psci-state", + type=lambda x: int(x, 0), + help="Power State Coordination Interface - PSCI state") + + # TODO: Add vendor-specific support + + # UEFI N.17 bitmaps (type and flags) + g_pei = parser.add_argument_group("ARM Processor Error Info (PEI)") + g_pei.add_argument("-t", "--type", nargs="+", + help=f"one or more error types: {error_types}") + g_pei.add_argument("-f", "--flags", nargs="*", + help=f"zero or more error flags: {flags}") + g_pei.add_argument("-V", "--pei-valid", "--error-valid", nargs="*", + help=f"zero or more PEI valid bits: {pei_valid_bits}") + + # UEFI N.17 Integer values + g_pei.add_argument("-m", "--multiple-error", nargs="+", + help="Number of errors: 0: Single error, 1: Multiple errors, 2-65535: Error count if known") + g_pei.add_argument("-e", "--error-info", nargs="+", + help="Error information (UEFI 2.10 tables N.18 to N.20)") + g_pei.add_argument("-p", "--physical-address", nargs="+", + help="Physical address") + g_pei.add_argument("-v", "--virtual-address", nargs="+", + help="Virtual address") + + # UEFI N.21 Context + g_ctx = parser.add_argument_group("Processor Context") + g_ctx.add_argument("--ctx-type", "--context-type", nargs="*", + help="Type of the context (0=ARM32 GPR, 5=ARM64 EL1, other values supported)") + g_ctx.add_argument("--ctx-size", "--context-size", nargs="*", + help="Minimal size of the context") + g_ctx.add_argument("--ctx-array", "--context-array", nargs="*", + help="Comma-separated arrays for each context") + + # Vendor-specific data + g_vendor = parser.add_argument_group("Vendor-specific data") + g_vendor.add_argument("--vendor", "--vendor-specific", nargs="+", + help="Vendor-specific byte arrays of data") + + # Add arguments for Generic Error Data + qmp.argparse(parser) + + parser.set_defaults(func=self.send_cper) + + def send_cper(self, args): + """Parse subcommand arguments and send a CPER via QMP""" + + qmp_cmd = qmp(args.host, args.port, args.debug) + + # Handle Generic Error Data arguments if any + qmp_cmd.set_args(args) + + is_cpu_type = re.compile(r"^([\w+]+\-)?arm\-cpu$") + cpus = qmp_cmd.search_qom("/machine/unattached/device", + "type", is_cpu_type) + + cper = {} + pei = {} + ctx = {} + vendor = {} + + arg = vars(args) + + # Handle global parameters + if args.arm: + arm_valid_init = False + cper["valid"] = util.get_choice(name="valid", + value=args.arm, + choices=self.arm_valid_bits, + suffixes=["-error", "-err"]) + else: + cper["valid"] = 0 + arm_valid_init = True + + if "running" in arg: + if args.running: + cper["running-state"] = util.bit(0) + else: + cper["running-state"] = 0 + else: + cper["running-state"] = 0 + + if arm_valid_init: + if args.affinity: + cper["valid"] |= self.arm_valid_bits["affinity"] + + if args.mpidr: + cper["valid"] |= self.arm_valid_bits["mpidr"] + + if "running-state" in cper: + cper["valid"] |= self.arm_valid_bits["running"] + + if args.psci: + cper["valid"] |= self.arm_valid_bits["running"] + + # Handle PEI + if not args.type: + args.type = ["cache-error"] + + util.get_mult_choices( + pei, + name="valid", + values=args.pei_valid, + choices=self.pei_valid_bits, + suffixes=["-valid", "--addr"], + ) + util.get_mult_choices( + pei, + name="type", + values=args.type, + choices=self.pei_error_types, + suffixes=["-error", "-err"], + ) + util.get_mult_choices( + pei, + name="flags", + values=args.flags, + choices=self.pei_flags, + suffixes=["-error", "-cap"], + ) + util.get_mult_int(pei, "error-info", args.error_info) + util.get_mult_int(pei, "multiple-error", args.multiple_error) + util.get_mult_int(pei, "phy-addr", args.physical_address) + util.get_mult_int(pei, "virt-addr", args.virtual_address) + + # Handle context + util.get_mult_int(ctx, "type", args.ctx_type, allow_zero=True) + util.get_mult_int(ctx, "minimal-size", args.ctx_size, allow_zero=True) + util.get_mult_array(ctx, "register", args.ctx_array, allow_zero=True) + + util.get_mult_array(vendor, "bytes", args.vendor, max_val=255) + + # Store PEI + pei_data = bytearray() + default_flags = self.pei_flags["first"] + default_flags |= self.pei_flags["last"] + + error_info_num = 0 + + for i, p in pei.items(): # pylint: disable=W0612 + error_info_num += 1 + + # UEFI 2.10 doesn't define how to encode error information + # when multiple types are raised. So, provide a default only + # if a single type is there + if "error-info" not in p: + if p["type"] == util.bit(1): + p["error-info"] = 0x0091000F + if p["type"] == util.bit(2): + p["error-info"] = 0x0054007F + if p["type"] == util.bit(3): + p["error-info"] = 0x80D6460FFF + if p["type"] == util.bit(4): + p["error-info"] = 0x78DA03FF + + if "valid" not in p: + p["valid"] = 0 + if "multiple-error" in p: + p["valid"] |= self.pei_valid_bits["multiple-error"] + + if "flags" in p: + p["valid"] |= self.pei_valid_bits["flags"] + + if "error-info" in p: + p["valid"] |= self.pei_valid_bits["error-info"] + + if "phy-addr" in p: + p["valid"] |= self.pei_valid_bits["phy-addr"] + + if "virt-addr" in p: + p["valid"] |= self.pei_valid_bits["virt-addr"] + + # Version + util.data_add(pei_data, 0, 1) + + util.data_add(pei_data, + self.ACPI_GHES_ARM_CPER_PEI_LENGTH, 1) + + util.data_add(pei_data, p["valid"], 2) + util.data_add(pei_data, p["type"], 1) + util.data_add(pei_data, p.get("multiple-error", 1), 2) + util.data_add(pei_data, p.get("flags", default_flags), 1) + util.data_add(pei_data, p.get("error-info", 0), 8) + util.data_add(pei_data, p.get("virt-addr", 0xDEADBEEF), 8) + util.data_add(pei_data, p.get("phy-addr", 0xABBA0BAD), 8) + + # Store Context + ctx_data = bytearray() + context_info_num = 0 + + if ctx: + ret = qmp_cmd.send_cmd("query-target", may_open=True) + + default_ctx = self.CONTEXT_MISC_REG + + if "arch" in ret: + if ret["arch"] == "aarch64": + default_ctx = self.CONTEXT_AARCH64_EL1 + elif ret["arch"] == "arm": + default_ctx = self.CONTEXT_AARCH32_EL1 + + for k in sorted(ctx.keys()): + context_info_num += 1 + + if "type" not in ctx[k]: + ctx[k]["type"] = default_ctx + + if "register" not in ctx[k]: + ctx[k]["register"] = [] + + reg_size = len(ctx[k]["register"]) + size = 0 + + if "minimal-size" in ctx: + size = ctx[k]["minimal-size"] + + size = max(size, reg_size) + + size = (size + 1) % 0xFFFE + + # Version + util.data_add(ctx_data, 0, 2) + + util.data_add(ctx_data, ctx[k]["type"], 2) + + util.data_add(ctx_data, 8 * size, 4) + + for r in ctx[k]["register"]: + util.data_add(ctx_data, r, 8) + + for i in range(reg_size, size): # pylint: disable=W0612 + util.data_add(ctx_data, 0, 8) + + # Vendor-specific bytes are not grouped + vendor_data = bytearray() + if vendor: + for k in sorted(vendor.keys()): + for b in vendor[k]["bytes"]: + util.data_add(vendor_data, b, 1) + + # Encode ARM Processor Error + data = bytearray() + + util.data_add(data, cper["valid"], 4) + + util.data_add(data, error_info_num, 2) + util.data_add(data, context_info_num, 2) + + # Calculate the length of the CPER data + cper_length = self.ACPI_GHES_ARM_CPER_LENGTH + cper_length += len(pei_data) + cper_length += len(vendor_data) + cper_length += len(ctx_data) + util.data_add(data, cper_length, 4) + + util.data_add(data, arg.get("affinity-level", 0), 1) + + # Reserved + util.data_add(data, 0, 3) + + if "midr-el1" not in arg: + if cpus: + cmd_arg = { + 'path': cpus[0], + 'property': "midr" + } + ret = qmp_cmd.send_cmd("qom-get", cmd_arg, may_open=True) + if isinstance(ret, int): + arg["midr-el1"] = ret + + util.data_add(data, arg.get("mpidr-el1", 0), 8) + util.data_add(data, arg.get("midr-el1", 0), 8) + util.data_add(data, cper["running-state"], 4) + util.data_add(data, arg.get("psci-state", 0), 4) + + # Add PEI + data.extend(pei_data) + data.extend(ctx_data) + data.extend(vendor_data) + + self.data = data + + qmp_cmd.send_cper(cper_guid.CPER_PROC_ARM, self.data) diff --git a/scripts/ghes_inject.py b/scripts/ghes_inject.py new file mode 100755 index 0000000000000..9a235201418b4 --- /dev/null +++ b/scripts/ghes_inject.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2024-2025 Mauro Carvalho Chehab + +""" +Handle ACPI GHESv2 error injection logic QEMU QMP interface. +""" + +import argparse +import sys + +from arm_processor_error import ArmProcessorEinj + +EINJ_DESC = """ +Handle ACPI GHESv2 error injection logic QEMU QMP interface. + +It allows using UEFI BIOS EINJ features to generate GHES records. + +It helps testing CPER and GHES drivers at the guest OS and how +userspace applications at the guest handle them. +""" + +def main(): + """Main program""" + + # Main parser - handle generic args like QEMU QMP TCP socket options + parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, + usage="%(prog)s [options]", + description=EINJ_DESC) + + g_options = parser.add_argument_group("QEMU QMP socket options") + g_options.add_argument("-H", "--host", default="localhost", type=str, + help="host name") + g_options.add_argument("-P", "--port", default=4445, type=int, + help="TCP port number") + g_options.add_argument('-d', '--debug', action='store_true') + + subparsers = parser.add_subparsers() + + ArmProcessorEinj(subparsers) + + args = parser.parse_args() + if "func" in args: + args.func(args) + else: + sys.exit(f"Please specify a valid command for {sys.argv[0]}") + +if __name__ == "__main__": + main() diff --git a/scripts/qmp_helper.py b/scripts/qmp_helper.py new file mode 100755 index 0000000000000..c1e7e0fd80ce3 --- /dev/null +++ b/scripts/qmp_helper.py @@ -0,0 +1,703 @@ +#!/usr/bin/env python3 +# +# pylint: disable=C0103,E0213,E1135,E1136,E1137,R0902,R0903,R0912,R0913,R0917 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2024-2025 Mauro Carvalho Chehab + +""" +Helper classes to be used by ghes_inject command classes. +""" + +import json +import sys + +from datetime import datetime +from os import path as os_path + +try: + qemu_dir = os_path.abspath(os_path.dirname(os_path.dirname(__file__))) + sys.path.append(os_path.join(qemu_dir, 'python')) + + from qemu.qmp.legacy import QEMUMonitorProtocol + +except ModuleNotFoundError as exc: + print(f"Module '{exc.name}' not found.") + print("Try export PYTHONPATH=top-qemu-dir/python or run from top-qemu-dir") + sys.exit(1) + +from base64 import b64encode + +class util: + """ + Ancillary functions to deal with bitmaps, parse arguments, + generate GUID and encode data on a bytearray buffer. + """ + + # + # Helper routines to handle multiple choice arguments + # + def get_choice(name, value, choices, suffixes=None, bitmask=True): + """Produce a list from multiple choice argument""" + + new_values = 0 + + if not value: + return new_values + + for val in value.split(","): + val = val.lower() + + if suffixes: + for suffix in suffixes: + val = val.removesuffix(suffix) + + if val not in choices.keys(): + if suffixes: + for suffix in suffixes: + if val + suffix in choices.keys(): + val += suffix + break + + if val not in choices.keys(): + sys.exit(f"Error on '{name}': choice '{val}' is invalid.") + + val = choices[val] + + if bitmask: + new_values |= val + else: + if new_values: + sys.exit(f"Error on '{name}': only one value is accepted.") + + new_values = val + + return new_values + + def get_array(name, values, max_val=None): + """Add numbered hashes from integer lists into an array""" + + array = [] + + for value in values: + for val in value.split(","): + try: + val = int(val, 0) + except ValueError: + sys.exit(f"Error on '{name}': {val} is not an integer") + + if val < 0: + sys.exit(f"Error on '{name}': {val} is not unsigned") + + if max_val and val > max_val: + sys.exit(f"Error on '{name}': {val} is too little") + + array.append(val) + + return array + + def get_mult_array(mult, name, values, allow_zero=False, max_val=None): + """Add numbered hashes from integer lists""" + + if not allow_zero: + if not values: + return + else: + if values is None: + return + + if not values: + i = 0 + if i not in mult: + mult[i] = {} + + mult[i][name] = [] + return + + i = 0 + for value in values: + for val in value.split(","): + try: + val = int(val, 0) + except ValueError: + sys.exit(f"Error on '{name}': {val} is not an integer") + + if val < 0: + sys.exit(f"Error on '{name}': {val} is not unsigned") + + if max_val and val > max_val: + sys.exit(f"Error on '{name}': {val} is too little") + + if i not in mult: + mult[i] = {} + + if name not in mult[i]: + mult[i][name] = [] + + mult[i][name].append(val) + + i += 1 + + + def get_mult_choices(mult, name, values, choices, + suffixes=None, allow_zero=False): + """Add numbered hashes from multiple choice arguments""" + + if not allow_zero: + if not values: + return + else: + if values is None: + return + + i = 0 + for val in values: + new_values = util.get_choice(name, val, choices, suffixes) + + if i not in mult: + mult[i] = {} + + mult[i][name] = new_values + i += 1 + + + def get_mult_int(mult, name, values, allow_zero=False): + """Add numbered hashes from integer arguments""" + if not allow_zero: + if not values: + return + else: + if values is None: + return + + i = 0 + for val in values: + try: + val = int(val, 0) + except ValueError: + sys.exit(f"Error on '{name}': {val} is not an integer") + + if val < 0: + sys.exit(f"Error on '{name}': {val} is not unsigned") + + if i not in mult: + mult[i] = {} + + mult[i][name] = val + i += 1 + + + # + # Data encode helper functions + # + def bit(b): + """Simple macro to define a bit on a bitmask""" + return 1 << b + + + def data_add(data, value, num_bytes): + """Adds bytes from value inside a bitarray""" + + data.extend(value.to_bytes(num_bytes, byteorder="little")) # pylint: disable=E1101 + + def dump_bytearray(name, data): + """Does an hexdump of a byte array, grouping in bytes""" + + print(f"{name} ({len(data)} bytes):") + + for ln_start in range(0, len(data), 16): + ln_end = min(ln_start + 16, len(data)) + print(f" {ln_start:08x} ", end="") + for i in range(ln_start, ln_end): + print(f"{data[i]:02x} ", end="") + for i in range(ln_end, ln_start + 16): + print(" ", end="") + print(" ", end="") + for i in range(ln_start, ln_end): + if data[i] >= 32 and data[i] < 127: + print(chr(data[i]), end="") + else: + print(".", end="") + + print() + print() + + def time(string): + """Handle BCD timestamps used on Generic Error Data Block""" + + time = None + + # Formats to be used when parsing time stamps + formats = [ + "%Y-%m-%d %H:%M:%S", + ] + + if string == "now": + time = datetime.now() + + if time is None: + for fmt in formats: + try: + time = datetime.strptime(string, fmt) + break + except ValueError: + pass + + if time is None: + raise ValueError("Invalid time format") + + return time + +class guid: + """ + Simple class to handle GUID fields. + """ + + def __init__(self, time_low, time_mid, time_high, nodes): + """Initialize a GUID value""" + + assert len(nodes) == 8 + + self.time_low = time_low + self.time_mid = time_mid + self.time_high = time_high + self.nodes = nodes + + @classmethod + def UUID(cls, guid_str): + """Initialize a GUID using a string on its standard format""" + + if len(guid_str) != 36: + print("Size not 36") + raise ValueError('Invalid GUID size') + + # It is easier to parse without separators. So, drop them + guid_str = guid_str.replace('-', '') + + if len(guid_str) != 32: + print("Size not 32", guid_str, len(guid_str)) + raise ValueError('Invalid GUID hex size') + + time_low = 0 + time_mid = 0 + time_high = 0 + nodes = [] + + for i in reversed(range(16, 32, 2)): + h = guid_str[i:i + 2] + value = int(h, 16) + nodes.insert(0, value) + + time_high = int(guid_str[12:16], 16) + time_mid = int(guid_str[8:12], 16) + time_low = int(guid_str[0:8], 16) + + return cls(time_low, time_mid, time_high, nodes) + + def __str__(self): + """Output a GUID value on its default string representation""" + + clock = self.nodes[0] << 8 | self.nodes[1] + + node = 0 + for i in range(2, len(self.nodes)): + node = node << 8 | self.nodes[i] + + s = f"{self.time_low:08x}-{self.time_mid:04x}-" + s += f"{self.time_high:04x}-{clock:04x}-{node:012x}" + return s + + def to_bytes(self): + """Output a GUID value in bytes""" + + data = bytearray() + + util.data_add(data, self.time_low, 4) + util.data_add(data, self.time_mid, 2) + util.data_add(data, self.time_high, 2) + data.extend(bytearray(self.nodes)) + + return data + +class qmp: + """ + Opens a connection and send/receive QMP commands. + """ + + def send_cmd(self, command, args=None, may_open=False, return_error=True): + """Send a command to QMP, optinally opening a connection""" + + if may_open: + self._connect() + elif not self.connected: + return False + + msg = { 'execute': command } + if args: + msg['arguments'] = args + + try: + obj = self.qmp_monitor.cmd_obj(msg) + # Can we use some other exception class here? + except Exception as e: # pylint: disable=W0718 + print(f"Command: {command}") + print(f"Failed to inject error: {e}.") + return None + + if "return" in obj: + if isinstance(obj.get("return"), dict): + if obj["return"]: + return obj["return"] + return "OK" + + return obj["return"] + + if isinstance(obj.get("error"), dict): + error = obj["error"] + if return_error: + print(f"Command: {msg}") + print(f'{error["class"]}: {error["desc"]}') + else: + print(json.dumps(obj)) + + return None + + def _close(self): + """Shutdown and close the socket, if opened""" + if not self.connected: + return + + self.qmp_monitor.close() + self.connected = False + + def _connect(self): + """Connect to a QMP TCP/IP port, if not connected yet""" + + if self.connected: + return True + + try: + self.qmp_monitor.connect(negotiate=True) + except ConnectionError: + sys.exit(f"Can't connect to QMP host {self.host}:{self.port}") + + self.connected = True + + return True + + BLOCK_STATUS_BITS = { + "uncorrectable": util.bit(0), + "correctable": util.bit(1), + "multi-uncorrectable": util.bit(2), + "multi-correctable": util.bit(3), + } + + ERROR_SEVERITY = { + "recoverable": 0, + "fatal": 1, + "corrected": 2, + "none": 3, + } + + VALIDATION_BITS = { + "fru-id": util.bit(0), + "fru-text": util.bit(1), + "timestamp": util.bit(2), + } + + GEDB_FLAGS_BITS = { + "recovered": util.bit(0), + "prev-error": util.bit(1), + "simulated": util.bit(2), + } + + GENERIC_DATA_SIZE = 72 + + def argparse(parser): + """Prepare a parser group to query generic error data""" + + block_status_bits = ",".join(qmp.BLOCK_STATUS_BITS.keys()) + error_severity_enum = ",".join(qmp.ERROR_SEVERITY.keys()) + validation_bits = ",".join(qmp.VALIDATION_BITS.keys()) + gedb_flags_bits = ",".join(qmp.GEDB_FLAGS_BITS.keys()) + + g_gen = parser.add_argument_group("Generic Error Data") # pylint: disable=E1101 + g_gen.add_argument("--block-status", + help=f"block status bits: {block_status_bits}") + g_gen.add_argument("--raw-data", nargs="+", + help="Raw data inside the Error Status Block") + g_gen.add_argument("--error-severity", "--severity", + help=f"error severity: {error_severity_enum}") + g_gen.add_argument("--gen-err-valid-bits", + "--generic-error-validation-bits", + help=f"validation bits: {validation_bits}") + g_gen.add_argument("--fru-id", type=guid.UUID, + help="GUID representing a physical device") + g_gen.add_argument("--fru-text", + help="ASCII string identifying the FRU hardware") + g_gen.add_argument("--timestamp", type=util.time, + help="Time when the error info was collected") + g_gen.add_argument("--precise", "--precise-timestamp", + action='store_true', + help="Marks the timestamp as precise if --timestamp is used") + g_gen.add_argument("--gedb-flags", + help=f"General Error Data Block flags: {gedb_flags_bits}") + + def set_args(self, args): + """Set the arguments optionally defined via self.argparse()""" + + if args.block_status: + self.block_status = util.get_choice(name="block-status", + value=args.block_status, + choices=self.BLOCK_STATUS_BITS, + bitmask=False) + if args.raw_data: + self.raw_data = util.get_array("raw-data", args.raw_data, + max_val=255) + print(self.raw_data) + + if args.error_severity: + self.error_severity = util.get_choice(name="error-severity", + value=args.error_severity, + choices=self.ERROR_SEVERITY, + bitmask=False) + + if args.fru_id: + self.fru_id = args.fru_id.to_bytes() + if not args.gen_err_valid_bits: + self.validation_bits |= self.VALIDATION_BITS["fru-id"] + + if args.fru_text: + text = bytearray(args.fru_text.encode('ascii')) + if len(text) > 20: + sys.exit("FRU text is too big to fit") + + self.fru_text = text + if not args.gen_err_valid_bits: + self.validation_bits |= self.VALIDATION_BITS["fru-text"] + + if args.timestamp: + time = args.timestamp + century = int(time.year / 100) + + bcd = bytearray() + util.data_add(bcd, (time.second // 10) << 4 | (time.second % 10), 1) + util.data_add(bcd, (time.minute // 10) << 4 | (time.minute % 10), 1) + util.data_add(bcd, (time.hour // 10) << 4 | (time.hour % 10), 1) + + if args.precise: + util.data_add(bcd, 1, 1) + else: + util.data_add(bcd, 0, 1) + + util.data_add(bcd, (time.day // 10) << 4 | (time.day % 10), 1) + util.data_add(bcd, (time.month // 10) << 4 | (time.month % 10), 1) + util.data_add(bcd, + ((time.year % 100) // 10) << 4 | (time.year % 10), 1) + util.data_add(bcd, ((century % 100) // 10) << 4 | (century % 10), 1) + + self.timestamp = bcd + if not args.gen_err_valid_bits: + self.validation_bits |= self.VALIDATION_BITS["timestamp"] + + if args.gen_err_valid_bits: + self.validation_bits = util.get_choice(name="validation", + value=args.gen_err_valid_bits, + choices=self.VALIDATION_BITS) + + def __init__(self, host, port, debug=False): + """Initialize variables used by the QMP send logic""" + + self.connected = False + self.host = host + self.port = port + self.debug = debug + + # ACPI 6.1: 18.3.2.7.1 Generic Error Data: Generic Error Status Block + self.block_status = self.BLOCK_STATUS_BITS["uncorrectable"] + self.raw_data = [] + self.error_severity = self.ERROR_SEVERITY["recoverable"] + + # ACPI 6.1: 18.3.2.7.1 Generic Error Data: Generic Error Data Entry + self.validation_bits = 0 + self.flags = 0 + self.fru_id = bytearray(16) + self.fru_text = bytearray(20) + self.timestamp = bytearray(8) + + self.qmp_monitor = QEMUMonitorProtocol(address=(self.host, self.port)) + + # + # Socket QMP send command + # + def send_cper_raw(self, cper_data): + """Send a raw CPER data to QEMU though QMP TCP socket""" + + data = b64encode(bytes(cper_data)).decode('ascii') + + cmd_arg = { + 'cper': data + } + + self._connect() + + if self.send_cmd("inject-ghes-v2-error", cmd_arg): + print("Error injected.") + + def send_cper(self, notif_type, payload): + """Send commands to QEMU though QMP TCP socket""" + + # Fill CPER record header + + # NOTE: bits 4 to 13 of block status contain the number of + # data entries in the data section. This is currently unsupported. + + cper_length = len(payload) + data_length = cper_length + len(self.raw_data) + self.GENERIC_DATA_SIZE + + # Generic Error Data Entry + gede = bytearray() + + gede.extend(notif_type.to_bytes()) + util.data_add(gede, self.error_severity, 4) + util.data_add(gede, 0x300, 2) + util.data_add(gede, self.validation_bits, 1) + util.data_add(gede, self.flags, 1) + util.data_add(gede, cper_length, 4) + gede.extend(self.fru_id) + gede.extend(self.fru_text) + gede.extend(self.timestamp) + + # Generic Error Status Block + gebs = bytearray() + + if self.raw_data: + raw_data_offset = len(gebs) + else: + raw_data_offset = 0 + + util.data_add(gebs, self.block_status, 4) + util.data_add(gebs, raw_data_offset, 4) + util.data_add(gebs, len(self.raw_data), 4) + util.data_add(gebs, data_length, 4) + util.data_add(gebs, self.error_severity, 4) + + cper_data = bytearray() + cper_data.extend(gebs) + cper_data.extend(gede) + cper_data.extend(bytearray(self.raw_data)) + cper_data.extend(bytearray(payload)) + + if self.debug: + print(f"GUID: {notif_type}") + + util.dump_bytearray("Generic Error Status Block", gebs) + util.dump_bytearray("Generic Error Data Entry", gede) + + if self.raw_data: + util.dump_bytearray("Raw data", bytearray(self.raw_data)) + + util.dump_bytearray("Payload", payload) + + self.send_cper_raw(cper_data) + + + def search_qom(self, path, prop, regex): + """ + Return a list of devices that match path array like: + + /machine/unattached/device + /machine/peripheral-anon/device + ... + """ + + found = [] + + i = 0 + while 1: + dev = f"{path}[{i}]" + args = { + 'path': dev, + 'property': prop + } + ret = self.send_cmd("qom-get", args, may_open=True, + return_error=False) + if not ret: + break + + if isinstance(ret, str): + if regex.search(ret): + found.append(dev) + + i += 1 + if i > 10000: + print("Too many objects returned by qom-get!") + break + + return found + +class cper_guid: + """ + Contains CPER GUID, as per: + https://uefi.org/specs/UEFI/2.10/Apx_N_Common_Platform_Error_Record.html + """ + + CPER_PROC_GENERIC = guid(0x9876CCAD, 0x47B4, 0x4bdb, + [0xB6, 0x5E, 0x16, 0xF1, + 0x93, 0xC4, 0xF3, 0xDB]) + + CPER_PROC_X86 = guid(0xDC3EA0B0, 0xA144, 0x4797, + [0xB9, 0x5B, 0x53, 0xFA, + 0x24, 0x2B, 0x6E, 0x1D]) + + CPER_PROC_ITANIUM = guid(0xe429faf1, 0x3cb7, 0x11d4, + [0xbc, 0xa7, 0x00, 0x80, + 0xc7, 0x3c, 0x88, 0x81]) + + CPER_PROC_ARM = guid(0xE19E3D16, 0xBC11, 0x11E4, + [0x9C, 0xAA, 0xC2, 0x05, + 0x1D, 0x5D, 0x46, 0xB0]) + + CPER_PLATFORM_MEM = guid(0xA5BC1114, 0x6F64, 0x4EDE, + [0xB8, 0x63, 0x3E, 0x83, + 0xED, 0x7C, 0x83, 0xB1]) + + CPER_PLATFORM_MEM2 = guid(0x61EC04FC, 0x48E6, 0xD813, + [0x25, 0xC9, 0x8D, 0xAA, + 0x44, 0x75, 0x0B, 0x12]) + + CPER_PCIE = guid(0xD995E954, 0xBBC1, 0x430F, + [0xAD, 0x91, 0xB4, 0x4D, + 0xCB, 0x3C, 0x6F, 0x35]) + + CPER_PCI_BUS = guid(0xC5753963, 0x3B84, 0x4095, + [0xBF, 0x78, 0xED, 0xDA, + 0xD3, 0xF9, 0xC9, 0xDD]) + + CPER_PCI_DEV = guid(0xEB5E4685, 0xCA66, 0x4769, + [0xB6, 0xA2, 0x26, 0x06, + 0x8B, 0x00, 0x13, 0x26]) + + CPER_FW_ERROR = guid(0x81212A96, 0x09ED, 0x4996, + [0x94, 0x71, 0x8D, 0x72, + 0x9C, 0x8E, 0x69, 0xED]) + + CPER_DMA_GENERIC = guid(0x5B51FEF7, 0xC79D, 0x4434, + [0x8F, 0x1B, 0xAA, 0x62, + 0xDE, 0x3E, 0x2C, 0x64]) + + CPER_DMA_VT = guid(0x71761D37, 0x32B2, 0x45cd, + [0xA7, 0xD0, 0xB0, 0xFE, + 0xDD, 0x93, 0xE8, 0xCF]) + + CPER_DMA_IOMMU = guid(0x036F84E1, 0x7F37, 0x428c, + [0xA7, 0x9E, 0x57, 0x5F, + 0xDF, 0xAA, 0x84, 0xEC]) + + CPER_CCIX_PER = guid(0x91335EF6, 0xEBFB, 0x4478, + [0xA6, 0xA6, 0x88, 0xB7, + 0x28, 0xCF, 0x75, 0xD7]) + + CPER_CXL_PROT_ERR = guid(0x80B9EFB4, 0x52B5, 0x4DE3, + [0xA7, 0x77, 0x68, 0x78, + 0x4B, 0x77, 0x10, 0x48]) From d285591d29e28abe2841ecec9a82f57fc773719f Mon Sep 17 00:00:00 2001 From: Daniil Tatianin Date: Thu, 24 Jul 2025 22:54:09 +0300 Subject: [PATCH 1228/1794] hw/smbios: allow clearing the VM bit in SMBIOS table 0 This is useful to be able to freeze a specific version of SeaBIOS to prevent guest visible changes between BIOS updates. This is currently not possible since the extension byte 2 provided by SeaBIOS does not set the VM bit, whereas QEMU sets it unconditionally. Allowing to clear it also seems useful if we want to hide the fact that the guest system is running inside a virtual machine. Signed-off-by: Daniil Tatianin Reviewed-by: Michael S. Tsirkin Message-ID: <20250724195409.43499-1-d-tatianin@yandex-team.ru> Signed-off-by: Michael S. Tsirkin --- hw/smbios/smbios.c | 11 ++++++++++- include/hw/firmware/smbios.h | 2 +- qemu-options.hx | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 1ac063cfb4ba8..13e21a9c43e9c 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -179,6 +179,10 @@ static const QemuOptDesc qemu_smbios_type0_opts[] = { .name = "uefi", .type = QEMU_OPT_BOOL, .help = "uefi support", + },{ + .name = "vm", + .type = QEMU_OPT_BOOL, + .help = "virtual machine", }, { /* end of list */ } }; @@ -574,10 +578,14 @@ static void smbios_build_type_0_table(void) t->bios_characteristics = cpu_to_le64(0x08); /* Not supported */ t->bios_characteristics_extension_bytes[0] = 0; - t->bios_characteristics_extension_bytes[1] = 0x14; /* TCD/SVVP | VM */ + + t->bios_characteristics_extension_bytes[1] = 0x04; /* TCD/SVVP */ if (smbios_type0.uefi) { t->bios_characteristics_extension_bytes[1] |= 0x08; /* |= UEFI */ } + if (smbios_type0.vm) { + t->bios_characteristics_extension_bytes[1] |= 0x10; /* |= VM */ + } if (smbios_type0.have_major_minor) { t->system_bios_major_release = smbios_type0.major; @@ -1405,6 +1413,7 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) save_opt(&smbios_type0.version, opts, "version"); save_opt(&smbios_type0.date, opts, "date"); smbios_type0.uefi = qemu_opt_get_bool(opts, "uefi", false); + smbios_type0.vm = qemu_opt_get_bool(opts, "vm", true); val = qemu_opt_get(opts, "release"); if (val) { diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h index f066ab7262912..3ea732f4e6a60 100644 --- a/include/hw/firmware/smbios.h +++ b/include/hw/firmware/smbios.h @@ -22,7 +22,7 @@ extern GArray *usr_blobs_sizes; typedef struct { const char *vendor, *version, *date; - bool have_major_minor, uefi; + bool have_major_minor, uefi, vm; uint8_t major, minor; } smbios_type0_t; extern smbios_type0_t smbios_type0; diff --git a/qemu-options.hx b/qemu-options.hx index 075f4be2e3e67..3ff10ec09d9c6 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2700,7 +2700,7 @@ DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, "-smbios file=binary\n" " load SMBIOS entry from binary file\n" "-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d]\n" - " [,uefi=on|off]\n" + " [,uefi=on|off][,vm=on|off]\n" " specify SMBIOS type 0 fields\n" "-smbios type=1[,manufacturer=str][,product=str][,version=str][,serial=str]\n" " [,uuid=uuid][,sku=str][,family=str]\n" From d1193481dee63442fc41e47ca6ebc4cd34f1f69c Mon Sep 17 00:00:00 2001 From: peng guo Date: Tue, 5 Aug 2025 22:23:00 +0800 Subject: [PATCH 1229/1794] hw/i386/pc: Avoid overlap between CXL window and PCI 64bit BARs in QEMU When using a CXL Type 3 device together with a virtio 9p device in QEMU on a physical server, the 9p device fails to initialize properly. The kernel reports the following error: virtio: device uses modern interface but does not have VIRTIO_F_VERSION_1 9pnet_virtio virtio0: probe with driver 9pnet_virtio failed with error -22 Further investigation revealed that the 64-bit BAR space assigned to the 9pnet device was overlapped by the memory window allocated for the CXL devices. As a result, the kernel could not correctly access the BAR region, causing the virtio device to malfunction. An excerpt from /proc/iomem shows: 480010000-cffffffff : CXL Window 0 480010000-4bfffffff : PCI Bus 0000:00 4c0000000-4c01fffff : PCI Bus 0000:0c 4c0000000-4c01fffff : PCI Bus 0000:0d 4c0200000-cffffffff : PCI Bus 0000:00 4c0200000-4c0203fff : 0000:00:03.0 4c0200000-4c0203fff : virtio-pci-modern To address this issue, this patch adds the reserved memory end calculation for cxl devices to reserve sufficient address space and ensure that CXL memory windows are allocated beyond all PCI 64-bit BARs. This prevents overlap with 64-bit BARs regions such as those used by virtio or other pcie devices, resolving the conflict. QEMU Build Configuration: ./configure --prefix=/home/work/qemu_master/build/ \ --target-list=x86_64-softmmu \ --enable-kvm \ --enable-virtfs QEMU Boot Command: sudo /home/work/qemu_master/qemu/build/qemu-system-x86_64 \ -nographic -machine q35,cxl=on -enable-kvm -m 16G -smp 8 \ -hda /home/work/gp_qemu/rootfs.img \ -virtfs local,path=/home/work/gp_qemu/share,mount_tag=host0,security_model=passthrough,id=host0 \ -kernel /home/work/linux_output/arch/x86/boot/bzImage \ --append "console=ttyS0 crashkernel=256M root=/dev/sda rootfstype=ext4 rw loglevel=8" \ -object memory-backend-ram,id=vmem0,share=on,size=4096M \ -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \ -device cxl-rp,port=0,bus=cxl.1,id=root_port13,chassis=0,slot=2 \ -device cxl-type3,bus=root_port13,volatile-memdev=vmem0,id=cxl-vmem0,sn=0x123456789 \ -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G Fixes: 03b39fcf64bc ("hw/cxl: Make the CXL fixed memory window setup a machine parameter") Signed-off-by: peng guo Reviewed-by: Michael S. Tsirkin Message-ID: <20250805142300.15226-1-engguopeng@buaa.edu.cn> Signed-off-by: Michael S. Tsirkin --- hw/i386/pc.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index bc048a6d13743..eb36d50589672 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -837,6 +837,7 @@ void pc_memory_init(PCMachineState *pcms, hwaddr maxphysaddr, maxusedaddr; hwaddr cxl_base, cxl_resv_end = 0; X86CPU *cpu = X86_CPU(first_cpu); + uint64_t res_mem_end; assert(machine->ram_size == x86ms->below_4g_mem_size + x86ms->above_4g_mem_size); @@ -978,16 +979,17 @@ void pc_memory_init(PCMachineState *pcms, rom_set_fw(fw_cfg); - if (machine->device_memory) { - uint64_t *val = g_malloc(sizeof(*val)); - uint64_t res_mem_end; + if (pcms->cxl_devices_state.is_enabled) { + res_mem_end = cxl_resv_end; + } else if (machine->device_memory) { + res_mem_end = machine->device_memory->base + + memory_region_size(&machine->device_memory->mr); + } else { + res_mem_end = 0; + } - if (pcms->cxl_devices_state.is_enabled) { - res_mem_end = cxl_resv_end; - } else { - res_mem_end = machine->device_memory->base - + memory_region_size(&machine->device_memory->mr); - } + if (res_mem_end) { + uint64_t *val = g_malloc(sizeof(*val)); *val = cpu_to_le64(ROUND_UP(res_mem_end, 1 * GiB)); fw_cfg_add_file(fw_cfg, "etc/reserved-memory-end", val, sizeof(*val)); } From 2e54e5fda779a7ba45578884276dca62462f7a06 Mon Sep 17 00:00:00 2001 From: Damien Bergamini Date: Mon, 1 Sep 2025 15:14:23 +0000 Subject: [PATCH 1230/1794] pcie_sriov: Fix broken MMIO accesses from SR-IOV VFs Starting with commit cab1398a60eb, SR-IOV VFs are realized as soon as pcie_sriov_pf_init() is called. Because pcie_sriov_pf_init() must be called before pcie_sriov_pf_init_vf_bar(), the VF BARs types won't be known when the VF realize function calls pcie_sriov_vf_register_bar(). This breaks the memory regions of the VFs (for instance with igbvf): $ lspci ... Region 0: Memory at 281a00000 (64-bit, prefetchable) [virtual] [size=16K] Region 3: Memory at 281a20000 (64-bit, prefetchable) [virtual] [size=16K] $ info mtree ... address-space: pci_bridge_pci_mem 0000000000000000-ffffffffffffffff (prio 0, i/o): pci_bridge_pci 0000000081a00000-0000000081a03fff (prio 1, i/o): igbvf-mmio 0000000081a20000-0000000081a23fff (prio 1, i/o): igbvf-msix and causes MMIO accesses to fail: Invalid write at addr 0x281A01520, size 4, region '(null)', reason: rejected Invalid read at addr 0x281A00C40, size 4, region '(null)', reason: rejected To fix this, VF BARs are now registered with pci_register_bar() which has a type parameter and pcie_sriov_vf_register_bar() is removed. Fixes: cab1398a60eb ("pcie_sriov: Reuse SR-IOV VF device instances") Signed-off-by: Damien Bergamini Signed-off-by: Clement Mathieu--Drif Reviewed-by: Akihiko Odaki Reviewed-by: Michael S. Tsirkin Message-ID: <20250901151314.1038020-1-clement.mathieu--drif@eviden.com> Signed-off-by: Michael S. Tsirkin --- docs/pcie_sriov.txt | 5 ++--- hw/net/igbvf.c | 6 ++++-- hw/nvme/ctrl.c | 8 ++------ hw/pci/pci.c | 3 --- hw/pci/pcie_sriov.c | 11 ----------- include/hw/pci/pcie_sriov.h | 4 ---- 6 files changed, 8 insertions(+), 29 deletions(-) diff --git a/docs/pcie_sriov.txt b/docs/pcie_sriov.txt index ab2142807f796..00d7bd93fdff3 100644 --- a/docs/pcie_sriov.txt +++ b/docs/pcie_sriov.txt @@ -72,8 +72,7 @@ setting up a BAR for a VF. 2) Similarly in the implementation of the virtual function, you need to make it a PCI Express device and add a similar set of capabilities except for the SR/IOV capability. Then you need to set up the VF BARs as - subregions of the PFs SR/IOV VF BARs by calling - pcie_sriov_vf_register_bar() instead of the normal pci_register_bar() call: + subregions of the PFs SR/IOV VF BARs by calling pci_register_bar(): pci_your_vf_dev_realize( ... ) { @@ -83,7 +82,7 @@ setting up a BAR for a VF. pcie_ari_init(d, 0x100); ... memory_region_init(mr, ... ) - pcie_sriov_vf_register_bar(d, bar_nr, mr); + pci_register_bar(d, bar_nr, bar_type, mr); ... } diff --git a/hw/net/igbvf.c b/hw/net/igbvf.c index 31d72c4977d9a..9b0db8f841105 100644 --- a/hw/net/igbvf.c +++ b/hw/net/igbvf.c @@ -251,10 +251,12 @@ static void igbvf_pci_realize(PCIDevice *dev, Error **errp) memory_region_init_io(&s->mmio, OBJECT(dev), &mmio_ops, s, "igbvf-mmio", IGBVF_MMIO_SIZE); - pcie_sriov_vf_register_bar(dev, IGBVF_MMIO_BAR_IDX, &s->mmio); + pci_register_bar(dev, IGBVF_MMIO_BAR_IDX, PCI_BASE_ADDRESS_MEM_TYPE_64 | + PCI_BASE_ADDRESS_MEM_PREFETCH, &s->mmio); memory_region_init(&s->msix, OBJECT(dev), "igbvf-msix", IGBVF_MSIX_SIZE); - pcie_sriov_vf_register_bar(dev, IGBVF_MSIX_BAR_IDX, &s->msix); + pci_register_bar(dev, IGBVF_MSIX_BAR_IDX, PCI_BASE_ADDRESS_MEM_TYPE_64 | + PCI_BASE_ADDRESS_MEM_PREFETCH, &s->msix); ret = msix_init(dev, IGBVF_MSIX_VEC_NUM, &s->msix, IGBVF_MSIX_BAR_IDX, 0, &s->msix, IGBVF_MSIX_BAR_IDX, 0x2000, 0x70, errp); diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index f5ee6bf260f15..cd81f7399754b 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -8708,12 +8708,8 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) msix_table_offset); memory_region_add_subregion(&n->bar0, 0, &n->iomem); - if (pci_is_vf(pci_dev)) { - pcie_sriov_vf_register_bar(pci_dev, 0, &n->bar0); - } else { - pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_TYPE_64, &n->bar0); - } + pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, &n->bar0); ret = msix_init(pci_dev, nr_vectors, &n->bar0, 0, msix_table_offset, diff --git a/hw/pci/pci.c b/hw/pci/pci.c index c3df9d6656bf8..4854d3d6185d8 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1491,9 +1491,6 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, : pci_get_bus(pci_dev)->address_space_mem; if (pci_is_vf(pci_dev)) { - PCIDevice *pf = pci_dev->exp.sriov_vf.pf; - assert(!pf || type == pf->exp.sriov_pf.vf_bar_type[region_num]); - r->addr = pci_bar_address(pci_dev, region_num, r->type, r->size); if (r->addr != PCI_BAR_UNMAPPED) { memory_region_add_subregion_overlap(r->address_space, diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 8a4bf0d6f7c0c..29474d749a2f6 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -242,17 +242,6 @@ void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, dev->exp.sriov_pf.vf_bar_type[region_num] = type; } -void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, - MemoryRegion *memory) -{ - uint8_t type; - - assert(dev->exp.sriov_vf.pf); - type = dev->exp.sriov_vf.pf->exp.sriov_pf.vf_bar_type[region_num]; - - return pci_register_bar(dev, region_num, type, memory); -} - static gint compare_vf_devfns(gconstpointer a, gconstpointer b) { return (*(PCIDevice **)a)->devfn - (*(PCIDevice **)b)->devfn; diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index aeaa38cf3456d..b0ea6a62c7496 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -37,10 +37,6 @@ void pcie_sriov_pf_exit(PCIDevice *dev); void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, uint8_t type, dma_addr_t size); -/* Instantiate a bar for a VF */ -void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, - MemoryRegion *memory); - /** * pcie_sriov_pf_init_from_user_created_vfs() - Initialize PF with user-created * VFs, adding ARI to PF From 722e9022a0b6db175209bad75aa52b24033249e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 1 Sep 2025 11:59:48 +0100 Subject: [PATCH 1231/1794] hw/virtio: rename vhost-user-device and make user creatable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We didn't make the device user creatable in the first place because we were worried users might get confused. Rename the device to make its nature as a test device even more explicit. While we are at it add a Kconfig variable so it can be skipped for those that want to thin out their build configuration even further. Acked-by: Stefano Garzarella Reviewed-by: Manos Pitsidianakis Reviewed-by: Stefan Hajnoczi Signed-off-by: Alex Bennée Message-ID: <20250820195632.1956795-1-alex.bennee@linaro.org> Reviewed-by: Michael S. Tsirkin Message-ID: <20250901105948.982583-1-alex.bennee@linaro.org> Signed-off-by: Michael S. Tsirkin --- docs/system/devices/vhost-user.rst | 20 +++++++------------ hw/virtio/Kconfig | 5 +++++ hw/virtio/meson.build | 5 +++-- ...ice-pci.c => vhost-user-test-device-pci.c} | 17 +++++++--------- ...user-device.c => vhost-user-test-device.c} | 9 +++------ include/hw/virtio/vhost-user-base.h | 2 +- 6 files changed, 26 insertions(+), 32 deletions(-) rename hw/virtio/{vhost-user-device-pci.c => vhost-user-test-device-pci.c} (77%) rename hw/virtio/{vhost-user-device.c => vhost-user-test-device.c} (87%) diff --git a/docs/system/devices/vhost-user.rst b/docs/system/devices/vhost-user.rst index 35259d8ec7c66..bddf8df5ed504 100644 --- a/docs/system/devices/vhost-user.rst +++ b/docs/system/devices/vhost-user.rst @@ -62,26 +62,20 @@ platform details for what sort of virtio bus to use. The referenced *daemons* are not exhaustive, any conforming backend implementing the device and using the vhost-user protocol should work. -vhost-user-device -^^^^^^^^^^^^^^^^^ +vhost-user-test-device +^^^^^^^^^^^^^^^^^^^^^^ -The vhost-user-device is a generic development device intended for -expert use while developing new backends. The user needs to specify -all the required parameters including: +The vhost-user-test-device is a generic development device intended +for expert use while developing new backends. The user needs to +specify all the required parameters including: - Device ``virtio-id`` - The ``num_vqs`` it needs and their ``vq_size`` - The ``config_size`` if needed .. note:: - To prevent user confusion you cannot currently instantiate - vhost-user-device without first patching out:: - - /* Reason: stop inexperienced users confusing themselves */ - dc->user_creatable = false; - - in ``vhost-user-device.c`` and ``vhost-user-device-pci.c`` file and - rebuilding. + While this is a useful device for development it is not recommended + for production use. vhost-user daemon ================= diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig index 7648a2d68da97..10f5c53ac0937 100644 --- a/hw/virtio/Kconfig +++ b/hw/virtio/Kconfig @@ -126,3 +126,8 @@ config VHOST_USER_SCMI bool default y depends on VIRTIO && VHOST_USER && ARM + +config VHOST_USER_TEST + bool + default y + depends on VIRTIO && VHOST_USER diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index 3ea7b3cec832f..48b9fedfa569c 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -22,7 +22,7 @@ if have_vhost system_virtio_ss.add(files('vhost-user-base.c')) # MMIO Stubs - system_virtio_ss.add(files('vhost-user-device.c')) + system_virtio_ss.add(when: 'CONFIG_VHOST_USER_TEST', if_true: files('vhost-user-test-device.c')) system_virtio_ss.add(when: 'CONFIG_VHOST_USER_GPIO', if_true: files('vhost-user-gpio.c')) system_virtio_ss.add(when: 'CONFIG_VHOST_USER_I2C', if_true: files('vhost-user-i2c.c')) system_virtio_ss.add(when: 'CONFIG_VHOST_USER_RNG', if_true: files('vhost-user-rng.c')) @@ -30,7 +30,8 @@ if have_vhost system_virtio_ss.add(when: 'CONFIG_VHOST_USER_INPUT', if_true: files('vhost-user-input.c')) # PCI Stubs - system_virtio_ss.add(when: 'CONFIG_VIRTIO_PCI', if_true: files('vhost-user-device-pci.c')) + system_virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_TEST'], + if_true: files('vhost-user-test-device-pci.c')) system_virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_GPIO'], if_true: files('vhost-user-gpio-pci.c')) system_virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_I2C'], diff --git a/hw/virtio/vhost-user-device-pci.c b/hw/virtio/vhost-user-test-device-pci.c similarity index 77% rename from hw/virtio/vhost-user-device-pci.c rename to hw/virtio/vhost-user-test-device-pci.c index f10bac874e784..b4ed0efb50f28 100644 --- a/hw/virtio/vhost-user-device-pci.c +++ b/hw/virtio/vhost-user-test-device-pci.c @@ -18,13 +18,13 @@ struct VHostUserDevicePCI { VHostUserBase vub; }; -#define TYPE_VHOST_USER_DEVICE_PCI "vhost-user-device-pci-base" +#define TYPE_VHOST_USER_TEST_DEVICE_PCI "vhost-user-test-device-pci-base" -OBJECT_DECLARE_SIMPLE_TYPE(VHostUserDevicePCI, VHOST_USER_DEVICE_PCI) +OBJECT_DECLARE_SIMPLE_TYPE(VHostUserDevicePCI, VHOST_USER_TEST_DEVICE_PCI) static void vhost_user_device_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { - VHostUserDevicePCI *dev = VHOST_USER_DEVICE_PCI(vpci_dev); + VHostUserDevicePCI *dev = VHOST_USER_TEST_DEVICE_PCI(vpci_dev); DeviceState *vdev = DEVICE(&dev->vub); vpci_dev->nvectors = 1; @@ -38,9 +38,6 @@ static void vhost_user_device_pci_class_init(ObjectClass *klass, VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - /* Reason: stop users confusing themselves */ - dc->user_creatable = false; - k->realize = vhost_user_device_pci_realize; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; @@ -51,15 +48,15 @@ static void vhost_user_device_pci_class_init(ObjectClass *klass, static void vhost_user_device_pci_instance_init(Object *obj) { - VHostUserDevicePCI *dev = VHOST_USER_DEVICE_PCI(obj); + VHostUserDevicePCI *dev = VHOST_USER_TEST_DEVICE_PCI(obj); virtio_instance_init_common(obj, &dev->vub, sizeof(dev->vub), - TYPE_VHOST_USER_DEVICE); + TYPE_VHOST_USER_TEST_DEVICE); } static const VirtioPCIDeviceTypeInfo vhost_user_device_pci_info = { - .base_name = TYPE_VHOST_USER_DEVICE_PCI, - .non_transitional_name = "vhost-user-device-pci", + .base_name = TYPE_VHOST_USER_TEST_DEVICE_PCI, + .non_transitional_name = "vhost-user-test-device-pci", .instance_size = sizeof(VHostUserDevicePCI), .instance_init = vhost_user_device_pci_instance_init, .class_init = vhost_user_device_pci_class_init, diff --git a/hw/virtio/vhost-user-device.c b/hw/virtio/vhost-user-test-device.c similarity index 87% rename from hw/virtio/vhost-user-device.c rename to hw/virtio/vhost-user-test-device.c index 3939bdf755222..1b98ea3e4883d 100644 --- a/hw/virtio/vhost-user-device.c +++ b/hw/virtio/vhost-user-test-device.c @@ -1,5 +1,5 @@ /* - * Generic vhost-user-device implementation for any vhost-user-backend + * Generic vhost-user-test-device implementation for any vhost-user-backend * * This is a concrete implementation of vhost-user-base which can be * configured via properties. It is useful for development and @@ -25,7 +25,7 @@ */ static const VMStateDescription vud_vmstate = { - .name = "vhost-user-device", + .name = "vhost-user-test-device", .unmigratable = 1, }; @@ -41,16 +41,13 @@ static void vud_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - /* Reason: stop inexperienced users confusing themselves */ - dc->user_creatable = false; - device_class_set_props(dc, vud_properties); dc->vmsd = &vud_vmstate; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } static const TypeInfo vud_info = { - .name = TYPE_VHOST_USER_DEVICE, + .name = TYPE_VHOST_USER_TEST_DEVICE, .parent = TYPE_VHOST_USER_BASE, .class_init = vud_class_init, }; diff --git a/include/hw/virtio/vhost-user-base.h b/include/hw/virtio/vhost-user-base.h index 51d0968b893aa..387e434b8046d 100644 --- a/include/hw/virtio/vhost-user-base.h +++ b/include/hw/virtio/vhost-user-base.h @@ -44,6 +44,6 @@ struct VHostUserBaseClass { }; -#define TYPE_VHOST_USER_DEVICE "vhost-user-device" +#define TYPE_VHOST_USER_TEST_DEVICE "vhost-user-test-device" #endif /* QEMU_VHOST_USER_BASE_H */ From fa82ce2ddee8208c83a5a10bf3ab7348ad3d334a Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Mon, 1 Sep 2025 10:49:15 +0200 Subject: [PATCH 1232/1794] smbios: cap DIMM size to 2Tb as workaround for broken Windows With current limit set to match max spec size (2PTb), Windows fails to parse type 17 records when DIMM size reaches 4Tb+. Failure happens in GetPhysicallyInstalledSystemMemory() function, and fails "Check SMBIOS System Memory Tables" SVVP test. Though not fatal, it might cause issues for userspace apps, something like [1]. Lets cap default DIMM size to 2Tb for now, until MS fixes it. 1) https://issues.redhat.com/browse/RHEL-81999?focusedId=27731200&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-27731200 PS: It's obvious 32 int overflow math somewhere in Windows, MS admitted that it's Windows bug and in a process of fixing it. However it's unclear if W10 and earlier would get the fix. So however I dislike changing defaults, we heed to work around the issue (it looks like QEMU regression while not being it). Hopefully 2Tb/DIMM split will last longer until VM memory size will become large enough to cause to many type 17 records issue again. PPS: Alternatively, instead of messing with defaults, we can create a dedicated knob to ask for desired DIMM size cap explicitly on CLI. That will let users to enable workaround when they hit this corner case. Downside is that knob has to be propagated up all mgmt stack, which might be not desirable. PPPS: Yet alternatively, users can configure initial RAM to be less than 4Tb and all additional RAM add as DIMMs on QEMU CLI. (however it's the job to be done by mgmt which could know Windows version and total amount of RAM) Signed-off-by: Igor Mammedov Fixes: 62f182c97b ("smbios: make memory device size configurable per Machine") Reviewed-by: Michael S. Tsirkin Message-ID: <20250901084915.2607632-1-imammedo@redhat.com> Signed-off-by: Michael S. Tsirkin --- hw/arm/virt.c | 1 + hw/core/machine.c | 5 ++++- hw/i386/pc_piix.c | 1 + hw/i386/pc_q35.c | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index aad557be1ae3f..175023897a735 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -3537,6 +3537,7 @@ DEFINE_VIRT_MACHINE_AS_LATEST(10, 2) static void virt_machine_10_1_options(MachineClass *mc) { virt_machine_10_2_options(mc); + mc->smbios_memory_device_size = 2047 * TiB; compat_props_add(mc->compat_props, hw_compat_10_1, hw_compat_10_1_len); } DEFINE_VIRT_MACHINE(10, 1) diff --git a/hw/core/machine.c b/hw/core/machine.c index 7b7a381b0aded..681adbb7ac5d7 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -1118,8 +1118,11 @@ static void machine_class_init(ObjectClass *oc, const void *data) * SMBIOS 3.1.0 7.18.5 Memory Device — Extended Size * use max possible value that could be encoded into * 'Extended Size' field (2047Tb). + * + * Unfortunately (current) Windows Server 2025 and earlier do not handle + * 4Tb+ DIMM size. */ - mc->smbios_memory_device_size = 2047 * TiB; + mc->smbios_memory_device_size = 2 * TiB; /* numa node memory size aligned on 8MB by default. * On Linux, each node's border has to be 8MB aligned diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index caf8bab68e24a..7b3611e973cd0 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -448,6 +448,7 @@ DEFINE_I440FX_MACHINE_AS_LATEST(10, 2); static void pc_i440fx_machine_10_1_options(MachineClass *m) { pc_i440fx_machine_10_2_options(m); + m->smbios_memory_device_size = 2047 * TiB; compat_props_add(m->compat_props, hw_compat_10_1, hw_compat_10_1_len); compat_props_add(m->compat_props, pc_compat_10_1, pc_compat_10_1_len); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index e89951285e58d..6015e639d7bc0 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -384,6 +384,7 @@ DEFINE_Q35_MACHINE_AS_LATEST(10, 2); static void pc_q35_machine_10_1_options(MachineClass *m) { pc_q35_machine_10_2_options(m); + m->smbios_memory_device_size = 2047 * TiB; compat_props_add(m->compat_props, hw_compat_10_1, hw_compat_10_1_len); compat_props_add(m->compat_props, pc_compat_10_1, pc_compat_10_1_len); } From ec450d185e4c885396a8e9b9bd2a435c9e0c118f Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Mon, 1 Sep 2025 11:17:19 +0000 Subject: [PATCH 1233/1794] pcie: Add a way to get the outstanding page request allocation (pri) from the config space. Signed-off-by: Clement Mathieu--Drif Reviewed-by: Michael S. Tsirkin Message-ID: <20250901111630.1018573-2-clement.mathieu--drif@eviden.com> Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie.c | 8 ++++++++ include/hw/pci/pcie.h | 1 + 2 files changed, 9 insertions(+) diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index eaeb68894e6ec..b302de64191b2 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -1266,6 +1266,14 @@ void pcie_pri_init(PCIDevice *dev, uint16_t offset, uint32_t outstanding_pr_cap, dev->exp.pri_cap = offset; } +uint32_t pcie_pri_get_req_alloc(const PCIDevice *dev) +{ + if (!pcie_pri_enabled(dev)) { + return 0; + } + return pci_get_long(dev->config + dev->exp.pri_cap + PCI_PRI_ALLOC_REQ); +} + bool pcie_pri_enabled(const PCIDevice *dev) { if (!pci_is_express(dev) || !dev->exp.pri_cap) { diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index ff6ce08e135a4..42cebcd0338cd 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -158,6 +158,7 @@ void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, void pcie_pri_init(PCIDevice *dev, uint16_t offset, uint32_t outstanding_pr_cap, bool prg_response_pasid_req); +uint32_t pcie_pri_get_req_alloc(const PCIDevice *dev); bool pcie_pri_enabled(const PCIDevice *dev); bool pcie_pasid_enabled(const PCIDevice *dev); bool pcie_ats_enabled(const PCIDevice *dev); From 57ac646a2ecb2967c46febcfd0f40d396868b4dc Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Mon, 1 Sep 2025 11:17:20 +0000 Subject: [PATCH 1234/1794] intel_iommu: Bypass barrier wait descriptor wait_desc with SW=0,IF=0,FN=1 must not be considered as an invalid descriptor as it is used to implement section 7.10 of the VT-d spec. Signed-off-by: Clement Mathieu--Drif Reviewed-by: Michael S. Tsirkin Message-ID: <20250901111630.1018573-3-clement.mathieu--drif@eviden.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 83c5e444131a3..4e7ad3a29019f 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -2857,7 +2857,13 @@ static bool vtd_process_wait_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) vtd_generate_completion_event(s); } - if (!(inv_desc->lo & (VTD_INV_DESC_WAIT_IF | VTD_INV_DESC_WAIT_SW))) { + /* + * SW=0, IF=0, FN=1 is also a valid descriptor (VT-d 7.10) + * Nothing to do as we process the descriptors in order + */ + + if (!(inv_desc->lo & (VTD_INV_DESC_WAIT_IF | VTD_INV_DESC_WAIT_SW | + VTD_INV_DESC_WAIT_FN))) { error_report_once("%s: invalid wait desc: hi=%"PRIx64", lo=%"PRIx64 " (unknown type)", __func__, inv_desc->hi, inv_desc->lo); From fadc6b0402b6e3edc941b638dfca11789d07d730 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Mon, 1 Sep 2025 11:17:21 +0000 Subject: [PATCH 1235/1794] intel_iommu: Declare PRI constants and structures Signed-off-by: Clement Mathieu--Drif Reviewed-by: Michael S. Tsirkin Message-ID: <20250901111630.1018573-4-clement.mathieu--drif@eviden.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu_internal.h | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 360e937989dc6..04a8d4c76956c 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -190,6 +190,7 @@ #define VTD_ECAP_EIM (1ULL << 4) #define VTD_ECAP_PT (1ULL << 6) #define VTD_ECAP_SC (1ULL << 7) +#define VTD_ECAP_PRS (1ULL << 29) #define VTD_ECAP_MHMV (15ULL << 20) #define VTD_ECAP_SRS (1ULL << 31) #define VTD_ECAP_PSS (7ULL << 35) /* limit: MemTxAttrs::pid */ @@ -376,6 +377,18 @@ union VTDInvDesc { }; typedef union VTDInvDesc VTDInvDesc; +/* Page Request Descriptor */ +union VTDPRDesc { + struct { + uint64_t lo; + uint64_t hi; + }; + struct { + uint64_t val[4]; + }; +}; +typedef union VTDPRDesc VTDPRDesc; + /* Masks for struct VTDInvDesc */ #define VTD_INV_DESC_ALL_ONE -1ULL #define VTD_INV_DESC_TYPE(val) ((((val) >> 5) & 0x70ULL) | \ @@ -389,6 +402,7 @@ typedef union VTDInvDesc VTDInvDesc; #define VTD_INV_DESC_PIOTLB 0x6 /* PASID-IOTLB Invalidate Desc */ #define VTD_INV_DESC_PC 0x7 /* PASID-cache Invalidate Desc */ #define VTD_INV_DESC_DEV_PIOTLB 0x8 /* PASID-based-DIOTLB inv_desc*/ +#define VTD_INV_DESC_PGRESP 0x9 /* Page Group Response Desc */ #define VTD_INV_DESC_NONE 0 /* Not an Invalidate Descriptor */ /* Masks for Invalidation Wait Descriptor*/ @@ -440,6 +454,15 @@ typedef union VTDInvDesc VTDInvDesc; #define VTD_INV_DESC_PASID_DEVICE_IOTLB_RSVD_VAL0 0xfff000000000f000ULL #define VTD_INV_DESC_PASID_DEVICE_IOTLB_RSVD_VAL1 0x7feULL +/* Mask for Page Group Response Descriptor */ +#define VTD_INV_DESC_PGRESP_RSVD_HI 0xfffffffffffff003ULL +#define VTD_INV_DESC_PGRESP_RSVD_LO 0xfff00000000001e0ULL +#define VTD_INV_DESC_PGRESP_PP(val) (((val) >> 4) & 0x1ULL) +#define VTD_INV_DESC_PGRESP_RC(val) (((val) >> 12) & 0xfULL) +#define VTD_INV_DESC_PGRESP_RID(val) (((val) >> 16) & 0xffffULL) +#define VTD_INV_DESC_PGRESP_PASID(val) (((val) >> 32) & 0xfffffULL) +#define VTD_INV_DESC_PGRESP_PRGI(val) (((val) >> 3) & 0x1ffULL) + /* Rsvd field masks for spte */ #define VTD_SPTE_SNP 0x800ULL @@ -491,6 +514,31 @@ typedef union VTDInvDesc VTDInvDesc; #define VTD_INV_DESC_PIOTLB_RSVD_VAL0 0xfff000000000f1c0ULL #define VTD_INV_DESC_PIOTLB_RSVD_VAL1 0xf80ULL +/* Page Request Descriptor */ +/* For the low 64-bit of 128-bit */ +#define VTD_PRD_TYPE (1ULL) +#define VTD_PRD_PP(val) (((val) & 1ULL) << 8) +#define VTD_PRD_RID(val) (((val) & 0xffffULL) << 16) +#define VTD_PRD_PASID(val) (((val) & 0xfffffULL) << 32) +#define VTD_PRD_EXR(val) (((val) & 1ULL) << 52) +#define VTD_PRD_PMR(val) (((val) & 1ULL) << 53) +/* For the high 64-bit of 128-bit */ +#define VTD_PRD_RDR(val) ((val) & 1ULL) +#define VTD_PRD_WRR(val) (((val) & 1ULL) << 1) +#define VTD_PRD_LPIG(val) (((val) & 1ULL) << 2) +#define VTD_PRD_PRGI(val) (((val) & 0x1ffULL) << 3) +#define VTD_PRD_ADDR(val) ((val) & 0xfffffffffffff000ULL) + +/* Page Request Queue constants */ +#define VTD_PQA_ENTRY_SIZE 32 /* Size of an entry in bytes */ +/* Page Request Queue masks */ +#define VTD_PQA_ADDR 0xfffffffffffff000ULL /* PR queue address */ +#define VTD_PQA_SIZE 0x7ULL /* PR queue size */ +#define VTD_PR_STATUS_PPR 1UL /* Pending page request */ +#define VTD_PR_STATUS_PRO 2UL /* Page request overflow */ +#define VTD_PR_PECTL_IP 0x40000000UL /* PR control interrup pending */ +#define VTD_PR_PECTL_IM 0x80000000UL /* PR control interrup mask */ + /* Information about page-selective IOTLB invalidate */ struct VTDIOTLBPageInvInfo { uint16_t domain_id; @@ -550,6 +598,7 @@ typedef struct VTDRootEntry VTDRootEntry; #define VTD_SM_CONTEXT_ENTRY_RID2PASID_MASK 0xfffff #define VTD_SM_CONTEXT_ENTRY_RSVD_VAL0(aw) (0x1e0ULL | ~VTD_HAW_MASK(aw)) #define VTD_SM_CONTEXT_ENTRY_RSVD_VAL1 0xffffffffffe00000ULL +#define VTD_SM_CONTEXT_ENTRY_PRE 0x10ULL /* PASID Table Related Definitions */ #define VTD_PASID_DIR_BASE_ADDR_MASK (~0xfffULL) From b84e1e0730f3063fc453ce2d0a77136f101467a2 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Mon, 1 Sep 2025 11:17:23 +0000 Subject: [PATCH 1236/1794] intel_iommu: Declare registers for PRI Signed-off-by: Clement Mathieu--Drif Reviewed-by: Michael S. Tsirkin Message-ID: <20250901111630.1018573-5-clement.mathieu--drif@eviden.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 55 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 4e7ad3a29019f..d952ec1428e06 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -3386,6 +3386,27 @@ static void vtd_handle_iectl_write(IntelIOMMUState *s) } } +static void vtd_handle_prs_write(IntelIOMMUState *s) +{ + uint32_t prs = vtd_get_long_raw(s, DMAR_PRS_REG); + if (!(prs & VTD_PR_STATUS_PPR) && !(prs & VTD_PR_STATUS_PRO)) { + vtd_set_clear_mask_long(s, DMAR_PECTL_REG, VTD_PR_PECTL_IP, 0); + } +} + +static void vtd_handle_pectl_write(IntelIOMMUState *s) +{ + uint32_t pectl = vtd_get_long_raw(s, DMAR_PECTL_REG); + if ((pectl & VTD_PR_PECTL_IP) && !(pectl & VTD_PR_PECTL_IM)) { + /* + * If IP field was 1 when software clears the IM field, + * the interrupt is generated along with clearing the IP field. + */ + vtd_set_clear_mask_long(s, DMAR_PECTL_REG, VTD_PR_PECTL_IP, 0); + vtd_generate_interrupt(s, DMAR_PEADDR_REG, DMAR_PEDATA_REG); + } +} + static uint64_t vtd_mem_read(void *opaque, hwaddr addr, unsigned size) { IntelIOMMUState *s = opaque; @@ -3428,6 +3449,11 @@ static uint64_t vtd_mem_read(void *opaque, hwaddr addr, unsigned size) val = s->iq >> 32; break; + case DMAR_PEUADDR_REG: + assert(size == 4); + val = vtd_get_long_raw(s, DMAR_PEUADDR_REG); + break; + default: if (size == 4) { val = vtd_get_long(s, addr); @@ -3491,6 +3517,11 @@ static void vtd_mem_write(void *opaque, hwaddr addr, vtd_handle_iotlb_write(s); break; + case DMAR_PEUADDR_REG: + assert(size == 4); + vtd_set_long(s, addr, val); + break; + /* Invalidate Address Register, 64-bit */ case DMAR_IVA_REG: if (size == 4) { @@ -3671,6 +3702,18 @@ static void vtd_mem_write(void *opaque, hwaddr addr, vtd_set_long(s, addr, val); break; + case DMAR_PRS_REG: + assert(size == 4); + vtd_set_long(s, addr, val); + vtd_handle_prs_write(s); + break; + + case DMAR_PECTL_REG: + assert(size == 4); + vtd_set_long(s, addr, val); + vtd_handle_pectl_write(s); + break; + default: if (size == 4) { vtd_set_long(s, addr, val); @@ -4722,6 +4765,18 @@ static void vtd_init(IntelIOMMUState *s) * Interrupt remapping registers. */ vtd_define_quad(s, DMAR_IRTA_REG, 0, 0xfffffffffffff80fULL, 0); + + /* Page request registers */ + if (s->ecap & VTD_ECAP_PRS) { + vtd_define_quad(s, DMAR_PQH_REG, 0, 0x7ffe0ULL, 0); + vtd_define_quad(s, DMAR_PQT_REG, 0, 0x7ffe0ULL, 0); + vtd_define_quad(s, DMAR_PQA_REG, 0, 0xfffffffffffff007ULL, 0); + vtd_define_long(s, DMAR_PRS_REG, 0, 0, 0x3UL); + vtd_define_long(s, DMAR_PECTL_REG, 0, 0x80000000UL, 0); + vtd_define_long(s, DMAR_PEDATA_REG, 0, 0xffffUL, 0); + vtd_define_long(s, DMAR_PEADDR_REG, 0, 0xfffffffcUL, 0); + vtd_define_long(s, DMAR_PEUADDR_REG, 0, 0xffffffffUL, 0); + } } /* Should not reset address_spaces when reset because devices will still use From 676757e50ce0ce206969b02dc2433c9e825e55f5 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Mon, 1 Sep 2025 11:17:24 +0000 Subject: [PATCH 1237/1794] intel_iommu: Add PRI operations support Implement the PRI callbacks in vtd_iommu_ops. Signed-off-by: Clement Mathieu--Drif Reviewed-by: Michael S. Tsirkin Message-ID: <20250901111630.1018573-6-clement.mathieu--drif@eviden.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 274 +++++++++++++++++++++++++++++++++ hw/i386/intel_iommu_internal.h | 2 + include/hw/i386/intel_iommu.h | 1 + 3 files changed, 277 insertions(+) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index d952ec1428e06..2cc9bd5e45d3e 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -45,6 +45,8 @@ ((ce)->val[1] & VTD_SM_CONTEXT_ENTRY_RID2PASID_MASK) #define VTD_CE_GET_PASID_DIR_TABLE(ce) \ ((ce)->val[0] & VTD_PASID_DIR_BASE_ADDR_MASK) +#define VTD_CE_GET_PRE(ce) \ + ((ce)->val[0] & VTD_SM_CONTEXT_ENTRY_PRE) /* pe operations */ #define VTD_PE_GET_TYPE(pe) ((pe)->val[0] & VTD_SM_PASID_ENTRY_PGTT) @@ -1838,6 +1840,7 @@ static const bool vtd_qualified_faults[] = { [VTD_FR_FS_NON_CANONICAL] = true, [VTD_FR_FS_PAGING_ENTRY_US] = true, [VTD_FR_SM_WRITE] = true, + [VTD_FR_SM_PRE_ABS] = true, [VTD_FR_SM_INTERRUPT_ADDR] = true, [VTD_FR_FS_BIT_UPDATE_FAILED] = true, [VTD_FR_MAX] = false, @@ -3152,6 +3155,59 @@ static bool vtd_process_device_piotlb_desc(IntelIOMMUState *s, return true; } +static bool vtd_process_page_group_response_desc(IntelIOMMUState *s, + VTDInvDesc *inv_desc) +{ + VTDAddressSpace *vtd_dev_as; + bool pasid_present; + uint8_t response_code; + uint16_t rid; + uint32_t pasid; + uint16_t prgi; + IOMMUPRIResponse response; + + if ((inv_desc->lo & VTD_INV_DESC_PGRESP_RSVD_LO) || + (inv_desc->hi & VTD_INV_DESC_PGRESP_RSVD_HI)) { + error_report_once("%s: invalid page group response desc: hi=%"PRIx64 + ", lo=%"PRIx64" (reserved nonzero)", __func__, + inv_desc->hi, inv_desc->lo); + return false; + } + + pasid_present = VTD_INV_DESC_PGRESP_PP(inv_desc->lo); + response_code = VTD_INV_DESC_PGRESP_RC(inv_desc->lo); + rid = VTD_INV_DESC_PGRESP_RID(inv_desc->lo); + pasid = VTD_INV_DESC_PGRESP_PASID(inv_desc->lo); + prgi = VTD_INV_DESC_PGRESP_PRGI(inv_desc->hi); + + if (!pasid_present) { + error_report_once("Page group response without PASID is" + "not supported yet"); + return false; + } + + vtd_dev_as = vtd_get_as_by_sid_and_pasid(s, rid, pasid); + if (!vtd_dev_as) { + return true; + } + + response.prgi = prgi; + + if (response_code == 0x0u) { + response.response_code = IOMMU_PRI_RESP_SUCCESS; + } else if (response_code == 0x1u) { + response.response_code = IOMMU_PRI_RESP_INVALID_REQUEST; + } else { + response.response_code = IOMMU_PRI_RESP_FAILURE; + } + + if (vtd_dev_as->pri_notifier) { + vtd_dev_as->pri_notifier->notify(vtd_dev_as->pri_notifier, &response); + } + + return true; +} + static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) { @@ -3252,6 +3308,13 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) } break; + case VTD_INV_DESC_PGRESP: + trace_vtd_inv_desc("page group response", inv_desc.hi, inv_desc.lo); + if (!vtd_process_page_group_response_desc(s, &inv_desc)) { + return false; + } + break; + /* * TODO: the entity of below two cases will be implemented in future series. * To make guest (which integrates scalable mode support patch set in @@ -4864,6 +4927,194 @@ static ssize_t vtd_ats_request_translation(PCIBus *bus, void *opaque, return res_index; } +/* 11.4.11.3 : The number of entries in the page request queue is 2^(PQS + 7) */ +static inline uint64_t vtd_prq_size(IntelIOMMUState *s) +{ + return 1ULL << ((vtd_get_quad(s, DMAR_PQA_REG) & VTD_PQA_SIZE) + 7); +} + +/** + * Return true if the bit is accessible and correctly set, false otherwise + */ +static bool vtd_check_pre_bit(VTDAddressSpace *vtd_as, hwaddr addr, + uint16_t sid, bool is_write) +{ + int ret; + IntelIOMMUState *s = vtd_as->iommu_state; + uint8_t bus_n = pci_bus_num(vtd_as->bus); + VTDContextEntry ce; + bool is_fpd_set = false; + + ret = vtd_dev_to_context_entry(s, bus_n, vtd_as->devfn, &ce); + + if (ret) { + goto error_report; + } + + if (!VTD_CE_GET_PRE(&ce)) { + ret = -VTD_FR_SM_PRE_ABS; + goto error_get_fpd_and_report; + } + + return true; + +error_get_fpd_and_report: + /* Try to get fpd (may not work but we are already on an error path) */ + is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD; + vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set, vtd_as->pasid); +error_report: + vtd_report_fault(s, -ret, is_fpd_set, sid, addr, is_write, + vtd_as->pasid != PCI_NO_PASID, vtd_as->pasid); + return false; +} + +/* Logic described in section 7.5 */ +static void vtd_generate_page_request_event(IntelIOMMUState *s, + uint32_t old_pr_status) +{ + uint32_t current_pectl = vtd_get_long(s, DMAR_PECTL_REG); + /* + * Hardware evaluates PPR and PRO fields in the Page Request Status Register + * and if any of them is set, Page Request Event is not generated + */ + if (old_pr_status & (VTD_PR_STATUS_PRO | VTD_PR_STATUS_PPR)) { + return; + } + + vtd_set_clear_mask_long(s, DMAR_PECTL_REG, 0, VTD_PR_PECTL_IP); + if (!(current_pectl & VTD_PR_PECTL_IM)) { + vtd_set_clear_mask_long(s, DMAR_PECTL_REG, VTD_PR_PECTL_IP, 0); + vtd_generate_interrupt(s, DMAR_PEADDR_REG, DMAR_PEDATA_REG); + } +} + +/* When calling this function, we known that we are in scalable mode */ +static int vtd_pri_perform_implicit_invalidation(VTDAddressSpace *vtd_as, + hwaddr addr) +{ + IntelIOMMUState *s = vtd_as->iommu_state; + VTDContextEntry ce; + VTDPASIDEntry pe; + uint16_t pgtt; + uint16_t domain_id; + int ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce); + if (ret) { + return -EINVAL; + } + ret = vtd_ce_get_rid2pasid_entry(s, &ce, &pe, vtd_as->pasid); + if (ret) { + return -EINVAL; + } + pgtt = VTD_PE_GET_TYPE(&pe); + domain_id = VTD_SM_PASID_ENTRY_DID(pe.val[1]); + ret = 0; + switch (pgtt) { + case VTD_SM_PASID_ENTRY_FLT: + vtd_piotlb_page_invalidate(s, domain_id, vtd_as->pasid, addr, 0); + break; + /* Room for other pgtt values */ + default: + error_report_once("Translation type not supported yet : %d", pgtt); + ret = -EINVAL; + break; + } + + return ret; +} + +/* Page Request Descriptor : 7.4.1.1 */ +static int vtd_pri_request_page(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, bool priv_req, bool exec_req, + hwaddr addr, bool lpig, uint16_t prgi, + bool is_read, bool is_write) +{ + IntelIOMMUState *s = opaque; + VTDAddressSpace *vtd_as; + + vtd_as = vtd_find_add_as(s, bus, devfn, pasid); + + uint64_t queue_addr_reg = vtd_get_quad(s, DMAR_PQA_REG); + uint64_t queue_tail_offset_reg = vtd_get_quad(s, DMAR_PQT_REG); + uint64_t new_queue_tail_offset = ( + (queue_tail_offset_reg + VTD_PQA_ENTRY_SIZE) % + (vtd_prq_size(s) * VTD_PQA_ENTRY_SIZE)); + uint64_t queue_head_offset_reg = vtd_get_quad(s, DMAR_PQH_REG); + hwaddr queue_tail = (queue_addr_reg & VTD_PQA_ADDR) + queue_tail_offset_reg; + uint32_t old_pr_status = vtd_get_long(s, DMAR_PRS_REG); + uint16_t sid = PCI_BUILD_BDF(pci_bus_num(vtd_as->bus), vtd_as->devfn); + VTDPRDesc desc; + + if (!(s->ecap & VTD_ECAP_PRS)) { + return -EPERM; + } + + /* + * No need to check if scalable mode is enabled as we already known that + * VTD_ECAP_PRS is set (see vtd_decide_config) + */ + + /* We do not support PRI without PASID */ + if (vtd_as->pasid == PCI_NO_PASID) { + return -EPERM; + } + if (exec_req && !is_read) { + return -EINVAL; + } + + /* Check PRE bit in the scalable mode context entry */ + if (!vtd_check_pre_bit(vtd_as, addr, sid, is_write)) { + return -EPERM; + } + + if (old_pr_status & VTD_PR_STATUS_PRO) { + /* + * No action is taken by hardware to report a fault + * or generate an event + */ + return -ENOSPC; + } + + /* Check for overflow */ + if (new_queue_tail_offset == queue_head_offset_reg) { + vtd_set_clear_mask_long(s, DMAR_PRS_REG, 0, VTD_PR_STATUS_PRO); + vtd_generate_page_request_event(s, old_pr_status); + return -ENOSPC; + } + + if (vtd_pri_perform_implicit_invalidation(vtd_as, addr)) { + return -EINVAL; + } + + desc.lo = VTD_PRD_TYPE | VTD_PRD_PP(true) | VTD_PRD_RID(sid) | + VTD_PRD_PASID(vtd_as->pasid) | VTD_PRD_PMR(priv_req); + desc.hi = VTD_PRD_RDR(is_read) | VTD_PRD_WRR(is_write) | + VTD_PRD_LPIG(lpig) | VTD_PRD_PRGI(prgi) | VTD_PRD_ADDR(addr); + + desc.lo = cpu_to_le64(desc.lo); + desc.hi = cpu_to_le64(desc.hi); + if (dma_memory_write(&address_space_memory, queue_tail, &desc, sizeof(desc), + MEMTXATTRS_UNSPECIFIED)) { + error_report_once("IO error, the PQ tail cannot be updated"); + return -EIO; + } + + /* increment the tail register and set the pending request bit */ + vtd_set_quad(s, DMAR_PQT_REG, new_queue_tail_offset); + /* + * read status again so that the kernel does not miss a request. + * in some cases, we can trigger an unecessary interrupt but this strategy + * drastically improves performance as we don't need to take a lock. + */ + old_pr_status = vtd_get_long(s, DMAR_PRS_REG); + if (!(old_pr_status & VTD_PR_STATUS_PPR)) { + vtd_set_clear_mask_long(s, DMAR_PRS_REG, 0, VTD_PR_STATUS_PPR); + vtd_generate_page_request_event(s, old_pr_status); + } + + return 0; +} + static void vtd_init_iotlb_notifier(PCIBus *bus, void *opaque, int devfn, IOMMUNotifier *n, IOMMUNotify fn, void *user_opaque) @@ -4905,6 +5156,26 @@ static void vtd_unregister_iotlb_notifier(PCIBus *bus, void *opaque, memory_region_unregister_iommu_notifier(MEMORY_REGION(&vtd_as->iommu), n); } +static void vtd_pri_register_notifier(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, IOMMUPRINotifier *notifier) +{ + IntelIOMMUState *s = opaque; + VTDAddressSpace *vtd_as; + + vtd_as = vtd_find_add_as(s, bus, devfn, pasid); + vtd_as->pri_notifier = notifier; +} + +static void vtd_pri_unregister_notifier(PCIBus *bus, void *opaque, + int devfn, uint32_t pasid) +{ + IntelIOMMUState *s = opaque; + VTDAddressSpace *vtd_as; + + vtd_as = vtd_find_add_as(s, bus, devfn, pasid); + vtd_as->pri_notifier = NULL; +} + static PCIIOMMUOps vtd_iommu_ops = { .get_address_space = vtd_host_dma_iommu, .set_iommu_device = vtd_dev_set_iommu_device, @@ -4914,6 +5185,9 @@ static PCIIOMMUOps vtd_iommu_ops = { .register_iotlb_notifier = vtd_register_iotlb_notifier, .unregister_iotlb_notifier = vtd_unregister_iotlb_notifier, .ats_request_translation = vtd_ats_request_translation, + .pri_register_notifier = vtd_pri_register_notifier, + .pri_unregister_notifier = vtd_pri_unregister_notifier, + .pri_request_page = vtd_pri_request_page, }; static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 04a8d4c76956c..0d0069a612533 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -315,6 +315,8 @@ typedef enum VTDFaultReason { * request while disabled */ VTD_FR_IR_SID_ERR = 0x26, /* Invalid Source-ID */ + VTD_FR_SM_PRE_ABS = 0x47, /* SCT.8 : PRE bit in a present SM CE is 0 */ + /* PASID directory entry access failure */ VTD_FR_PASID_DIR_ACCESS_ERR = 0x50, /* The Present(P) field of pasid directory entry is 0 */ diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index e95477e8554c1..47730ac3c761c 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -110,6 +110,7 @@ struct VTDAddressSpace { QLIST_ENTRY(VTDAddressSpace) next; /* Superset of notifier flags that this address space has */ IOMMUNotifierFlag notifier_flags; + IOMMUPRINotifier *pri_notifier; /* * @iova_tree traces mapped IOVA ranges. * From b10166b104fad54d5d79024beafe1bcfe8a35c4a Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Mon, 22 Sep 2025 15:26:00 +0200 Subject: [PATCH 1238/1794] x86: ich9: fix default value of 'No Reboot' bit in GCS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [2] initialized 'No Reboot' bit to 1 by default. And due to quirk it happened to work with linux iTCO_wdt driver (which clears it on module load). However spec [1] states: " R/W. This bit is set when the “No Reboot” strap (SPKR pin on ICH9) is sampled high on PWROK. " So it should be set only when '-global ICH9-LPC.noreboot=true' and cleared when it's false (which should be default). Fix it to behave according to spec and set 'No Reboot' bit only when '-global ICH9-LPC.noreboot=true'. 1) Intel I/O Controller Hub 9 (ICH9) Family Datasheet (rev: 004) 2) Fixes: 920557971b6 (ich9: add TCO interface emulation) Signed-off-by: Igor Mammedov Tested-by: Daniel P. Berrangé Reviewed-by: Daniel P. Berrangé Reviewed-by: Michael S. Tsirkin Message-ID: <20250922132600.562193-1-imammedo@redhat.com> Signed-off-by: Michael S. Tsirkin --- hw/isa/lpc_ich9.c | 7 ++++++- include/hw/southbridge/ich9.h | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 304dffac3227f..c9cb8f7779952 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -132,6 +132,11 @@ static void ich9_cc_init(ICH9LPCState *lpc) static void ich9_cc_reset(ICH9LPCState *lpc) { uint8_t *c = lpc->chip_config; + uint32_t gcs = ICH9_CC_GCS_DEFAULT; + + if (lpc->pin_strap.spkr_hi) { + gcs |= ICH9_CC_GCS_NO_REBOOT; + } memset(lpc->chip_config, 0, sizeof(lpc->chip_config)); @@ -142,7 +147,7 @@ static void ich9_cc_reset(ICH9LPCState *lpc) pci_set_long(c + ICH9_CC_D27IR, ICH9_CC_DIR_DEFAULT); pci_set_long(c + ICH9_CC_D26IR, ICH9_CC_DIR_DEFAULT); pci_set_long(c + ICH9_CC_D25IR, ICH9_CC_DIR_DEFAULT); - pci_set_long(c + ICH9_CC_GCS, ICH9_CC_GCS_DEFAULT); + pci_set_long(c + ICH9_CC_GCS, gcs); ich9_cc_update(lpc); } diff --git a/include/hw/southbridge/ich9.h b/include/hw/southbridge/ich9.h index 1e231e89c9260..2c35dd04848ec 100644 --- a/include/hw/southbridge/ich9.h +++ b/include/hw/southbridge/ich9.h @@ -95,7 +95,7 @@ struct ICH9LPCState { #define ICH9_CC_OIC 0x31FF #define ICH9_CC_OIC_AEN 0x1 #define ICH9_CC_GCS 0x3410 -#define ICH9_CC_GCS_DEFAULT 0x00000020 +#define ICH9_CC_GCS_DEFAULT 0x00000000 #define ICH9_CC_GCS_NO_REBOOT (1 << 5) /* D28:F[0-5] */ From 2e41580328a9cdfffce3ee496857c1b2d6ed365e Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 22 Sep 2025 18:01:45 -0400 Subject: [PATCH 1239/1794] vhost: use virtio_config_get_guest_notifier() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a getter function so avoid accessing the ->config_notifier field directly. Signed-off-by: Stefan Hajnoczi Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Message-ID: <20250922220149.498967-2-stefanha@redhat.com> Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 5f485ad6cbc13..c120ef38b910d 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1846,7 +1846,7 @@ void vhost_config_mask(struct vhost_dev *hdev, VirtIODevice *vdev, bool mask) int r; EventNotifier *notifier = &hdev->vqs[VHOST_QUEUE_NUM_CONFIG_INR].masked_config_notifier; - EventNotifier *config_notifier = &vdev->config_notifier; + EventNotifier *config_notifier = virtio_config_get_guest_notifier(vdev); assert(hdev->vhost_ops); if ((hdev->started == false) || @@ -1877,13 +1877,15 @@ static void vhost_stop_config_intr(struct vhost_dev *dev) static void vhost_start_config_intr(struct vhost_dev *dev) { int r; + EventNotifier *config_notifier = + virtio_config_get_guest_notifier(dev->vdev); assert(dev->vhost_ops); - int fd = event_notifier_get_fd(&dev->vdev->config_notifier); + int fd = event_notifier_get_fd(config_notifier); if (dev->vhost_ops->vhost_set_config_call) { r = dev->vhost_ops->vhost_set_config_call(dev, fd); if (!r) { - event_notifier_set(&dev->vdev->config_notifier); + event_notifier_set(config_notifier); } } } @@ -2167,12 +2169,13 @@ static int do_vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, { int i; int rc = 0; + EventNotifier *config_notifier = virtio_config_get_guest_notifier(vdev); /* should only be called after backend is connected */ assert(hdev->vhost_ops); event_notifier_test_and_clear( &hdev->vqs[VHOST_QUEUE_NUM_CONFIG_INR].masked_config_notifier); - event_notifier_test_and_clear(&vdev->config_notifier); + event_notifier_test_and_clear(config_notifier); event_notifier_cleanup( &hdev->vqs[VHOST_QUEUE_NUM_CONFIG_INR].masked_config_notifier); From 1e9181dc5277f27fcda21f64a399f12bbf578e5e Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 22 Sep 2025 18:01:46 -0400 Subject: [PATCH 1240/1794] virtio: unify virtio_notify_irqfd() and virtio_notify() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The difference between these two functions: - virtio_notify() uses the interrupt code path (MSI or classic IRQs) - virtio_notify_irqfd() uses guest notifiers (irqfds) virtio_notify() can only be called with the BQL held because the interrupt code path requires the BQL. Device models use virtio_notify_irqfd() from IOThreads since the BQL is not held. The two functions can be unified by pushing down the if (qemu_in_iothread()) check from virtio-blk and virtio-scsi into core virtio code. This is in preparation for the next commit that will add irqfd support to virtio_notify_config() and where it's unattractive to introduce another irqfd-only API for device model callers. Signed-off-by: Stefan Hajnoczi Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Message-ID: <20250922220149.498967-3-stefanha@redhat.com> Signed-off-by: Michael S. Tsirkin --- hw/block/virtio-blk.c | 6 +----- hw/scsi/virtio-scsi.c | 6 +----- hw/virtio/trace-events | 1 - hw/virtio/virtio.c | 28 +++++++++++++--------------- include/hw/virtio/virtio.h | 1 - 5 files changed, 15 insertions(+), 27 deletions(-) diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 9bab2716c1456..64efce48462ea 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -62,11 +62,7 @@ void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) iov_discard_undo(&req->inhdr_undo); iov_discard_undo(&req->outhdr_undo); virtqueue_push(req->vq, &req->elem, req->in_len); - if (qemu_in_iothread()) { - virtio_notify_irqfd(vdev, req->vq); - } else { - virtio_notify(vdev, req->vq); - } + virtio_notify(vdev, req->vq); } static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 34ae14f7bf938..d817fc42b4c24 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -116,11 +116,7 @@ static void virtio_scsi_complete_req(VirtIOSCSIReq *req, QemuMutex *vq_lock) } virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size); - if (s->dataplane_started && !s->dataplane_fenced) { - virtio_notify_irqfd(vdev, vq); - } else { - virtio_notify(vdev, vq); - } + virtio_notify(vdev, vq); if (vq_lock) { qemu_mutex_unlock(vq_lock); diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 76f0d458b29c0..658cc365e7010 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -75,7 +75,6 @@ virtqueue_flush(void *vq, unsigned int count) "vq %p count %u" virtqueue_pop(void *vq, void *elem, unsigned int in_num, unsigned int out_num) "vq %p elem %p in_num %u out_num %u" virtio_queue_notify(void *vdev, int n, void *vq) "vdev %p n %d vq %p" virtio_notify_irqfd_deferred_fn(void *vdev, void *vq) "vdev %p vq %p" -virtio_notify_irqfd(void *vdev, void *vq) "vdev %p vq %p" virtio_notify(void *vdev, void *vq) "vdev %p vq %p" virtio_set_status(void *vdev, uint8_t val) "vdev %p val %u" diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 34f977a3c9e96..6ce5823898d1a 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -31,6 +31,7 @@ #include "hw/qdev-properties.h" #include "hw/virtio/virtio-access.h" #include "system/dma.h" +#include "system/iothread.h" #include "system/runstate.h" #include "virtio-qmp.h" @@ -2654,16 +2655,8 @@ static void virtio_notify_irqfd_deferred_fn(void *opaque) event_notifier_set(notifier); } -void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq) +static void virtio_irq(VirtQueue *vq) { - WITH_RCU_READ_LOCK_GUARD() { - if (!virtio_should_notify(vdev, vq)) { - return; - } - } - - trace_virtio_notify_irqfd(vdev, vq); - /* * virtio spec 1.0 says ISR bit 0 should be ignored with MSI, but * windows drivers included in virtio-win 1.8.0 (circa 2015) are @@ -2680,13 +2673,18 @@ void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq) * to an atomic operation. */ virtio_set_isr(vq->vdev, 0x1); - defer_call(virtio_notify_irqfd_deferred_fn, &vq->guest_notifier); -} -static void virtio_irq(VirtQueue *vq) -{ - virtio_set_isr(vq->vdev, 0x1); - virtio_notify_vector(vq->vdev, vq->vector); + /* + * The interrupt code path requires the Big QEMU Lock (BQL), so use the + * notifier instead when in an IOThread. This assumes that device models + * have already called ->set_guest_notifiers() sometime before calling this + * function. + */ + if (qemu_in_iothread()) { + defer_call(virtio_notify_irqfd_deferred_fn, &vq->guest_notifier); + } else { + virtio_notify_vector(vq->vdev, vq->vector); + } } void virtio_notify(VirtIODevice *vdev, VirtQueue *vq) diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 2aeb021fb3f1e..d97529c3f1eb3 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -294,7 +294,6 @@ int virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, unsigned int *out_bytes, unsigned max_in_bytes, unsigned max_out_bytes); -void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq); void virtio_notify(VirtIODevice *vdev, VirtQueue *vq); int virtio_save(VirtIODevice *vdev, QEMUFile *f); From 3b11003fa67b3f055637ff52e1fe8327f7590ee1 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 22 Sep 2025 18:01:47 -0400 Subject: [PATCH 1241/1794] virtio: support irqfd in virtio_notify_config() virtio_error() calls virtio_notify_config() to inject a VIRTIO Configuration Change Notification. This doesn't work from IOThreads because the BQL is not held and the interrupt code path requires the BQL. Follow the same approach as virtio_notify() and use ->config_notifier (an irqfd) when called from the IOThread. Signed-off-by: Stefan Hajnoczi Reviewed-by: Michael S. Tsirkin Message-ID: <20250922220149.498967-4-stefanha@redhat.com> Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 6ce5823898d1a..de89e8104a629 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2706,7 +2706,12 @@ void virtio_notify_config(VirtIODevice *vdev) virtio_set_isr(vdev, 0x3); vdev->generation++; - virtio_notify_vector(vdev, vdev->config_vector); + + if (qemu_in_iothread()) { + defer_call(virtio_notify_irqfd_deferred_fn, &vdev->config_notifier); + } else { + virtio_notify_vector(vdev, vdev->config_vector); + } } static bool virtio_device_endian_needed(void *opaque) From e06d96d5a9f81608ede08576445ac20ac2e73dcc Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 22 Sep 2025 18:01:48 -0400 Subject: [PATCH 1242/1794] tests/libqos: extract qvirtqueue_set_avail_idx() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setting the vring's avail.idx can be useful for low-level VIRTIO tests, especially for testing error scenarios with invalid vrings. Extract it into a new function so that the next commit can add a test that uses this new test API. Signed-off-by: Stefan Hajnoczi Acked-by: Fabiano Rosas Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Message-ID: <20250922220149.498967-5-stefanha@redhat.com> Signed-off-by: Michael S. Tsirkin --- tests/qtest/libqos/virtio.c | 16 ++++++++++++---- tests/qtest/libqos/virtio.h | 2 ++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/qtest/libqos/virtio.c b/tests/qtest/libqos/virtio.c index 5a709d0bc59f8..010ff40834021 100644 --- a/tests/qtest/libqos/virtio.c +++ b/tests/qtest/libqos/virtio.c @@ -265,8 +265,9 @@ void qvring_init(QTestState *qts, const QGuestAllocator *alloc, QVirtQueue *vq, /* vq->avail->flags */ qvirtio_writew(vq->vdev, qts, vq->avail, 0); - /* vq->avail->idx */ - qvirtio_writew(vq->vdev, qts, vq->avail + 2, 0); + + qvirtqueue_set_avail_idx(qts, vq->vdev, vq, 0); + /* vq->avail->used_event */ qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), 0); @@ -388,6 +389,13 @@ uint32_t qvirtqueue_add_indirect(QTestState *qts, QVirtQueue *vq, return vq->free_head++; /* Return and increase, in this order */ } +void qvirtqueue_set_avail_idx(QTestState *qts, QVirtioDevice *d, + QVirtQueue *vq, uint16_t idx) +{ + /* vq->avail->idx */ + qvirtio_writew(d, qts, vq->avail + 2, idx); +} + void qvirtqueue_kick(QTestState *qts, QVirtioDevice *d, QVirtQueue *vq, uint32_t free_head) { @@ -400,8 +408,8 @@ void qvirtqueue_kick(QTestState *qts, QVirtioDevice *d, QVirtQueue *vq, /* vq->avail->ring[idx % vq->size] */ qvirtio_writew(d, qts, vq->avail + 4 + (2 * (idx % vq->size)), free_head); - /* vq->avail->idx */ - qvirtio_writew(d, qts, vq->avail + 2, idx + 1); + + qvirtqueue_set_avail_idx(qts, d, vq, idx + 1); /* Must read after idx is updated */ flags = qvirtio_readw(d, qts, vq->used); diff --git a/tests/qtest/libqos/virtio.h b/tests/qtest/libqos/virtio.h index 7adc7cbd10502..e238f1726f15e 100644 --- a/tests/qtest/libqos/virtio.h +++ b/tests/qtest/libqos/virtio.h @@ -143,6 +143,8 @@ uint32_t qvirtqueue_add(QTestState *qts, QVirtQueue *vq, uint64_t data, uint32_t len, bool write, bool next); uint32_t qvirtqueue_add_indirect(QTestState *qts, QVirtQueue *vq, QVRingIndirectDesc *indirect); +void qvirtqueue_set_avail_idx(QTestState *qts, QVirtioDevice *d, + QVirtQueue *vq, uint16_t idx); void qvirtqueue_kick(QTestState *qts, QVirtioDevice *d, QVirtQueue *vq, uint32_t free_head); bool qvirtqueue_get_buf(QTestState *qts, QVirtQueue *vq, uint32_t *desc_idx, From acaa3be3f7f73d9aafeb2454c01fe50eb8b4a807 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 22 Sep 2025 18:01:49 -0400 Subject: [PATCH 1243/1794] tests/virtio-scsi: add a virtio_error() IOThread test Now that virtio_error() calls should work in an IOThread, add a virtio-scsi IOThread test cases that triggers virtio_error(). Signed-off-by: Stefan Hajnoczi Reviewed-by: Michael S. Tsirkin Message-ID: <20250922220149.498967-6-stefanha@redhat.com> Signed-off-by: Michael S. Tsirkin --- tests/qtest/virtio-scsi-test.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/qtest/virtio-scsi-test.c b/tests/qtest/virtio-scsi-test.c index db10d572d0fcf..e2350c52f6fc1 100644 --- a/tests/qtest/virtio-scsi-test.c +++ b/tests/qtest/virtio-scsi-test.c @@ -311,6 +311,31 @@ static void test_iothread_attach_node(void *obj, void *data, unlink(tmp_path); } +static void test_iothread_virtio_error(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + QVirtioSCSIPCI *scsi_pci = obj; + QVirtioSCSI *scsi = &scsi_pci->scsi; + QVirtioSCSIQueues *vs; + QVirtQueue *vq; + + alloc = t_alloc; + vs = qvirtio_scsi_init(scsi->vdev); + vq = vs->vq[2]; + + /* Move avail.idx out of bounds to trigger virtio_error() */ + qvirtqueue_set_avail_idx(global_qtest, scsi->vdev, vq, vq->size * 2); + scsi->vdev->bus->virtqueue_kick(scsi->vdev, vq); + + /* + * Reset the device out of the error state. If QEMU hangs or crashes then + * this will fail. + */ + qvirtio_reset(scsi->vdev); + + qvirtio_scsi_pci_free(vs); +} + static void *virtio_scsi_hotplug_setup(GString *cmd_line, void *arg) { g_string_append(cmd_line, @@ -383,6 +408,13 @@ static void register_virtio_scsi_test(void) }; qos_add_test("iothread-attach-node", "virtio-scsi-pci", test_iothread_attach_node, &opts); + + opts.before = virtio_scsi_setup_iothread; + opts.edge = (QOSGraphEdgeOptions) { + .extra_device_opts = "iothread=thread0", + }; + qos_add_test("iothread-virtio-error", "virtio-scsi-pci", + test_iothread_virtio_error, &opts); } libqos_init(register_virtio_scsi_test); From bab681f752048c3bc22d561b1d314c7ec16419c9 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 24 Sep 2025 11:51:53 -0400 Subject: [PATCH 1244/1794] pcie_sriov: make pcie_sriov_pf_exit() safe on non-SR-IOV devices Commit 3f9cfaa92c96 ("virtio-pci: Implement SR-IOV PF") added an unconditional call from virtio_pci_exit() to pcie_sriov_pf_exit(). pcie_sriov_pf_exit() reads from the SR-IOV Capability in Configuration Space: uint8_t *cfg = dev->config + dev->exp.sriov_cap; ... unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This results in undefined behavior when dev->exp.sriov_cap is 0 because this is not an SR-IOV device. For example, unparent_vfs() segfaults when total_vfs happens to be non-zero. Fix this by returning early from pcie_sriov_pf_exit() when dev->exp.sriov_cap is 0 because this is not an SR-IOV device. Cc: Akihiko Odaki Cc: Michael S. Tsirkin Reported-by: Qing Wang Buglink: https://issues.redhat.com/browse/RHEL-116443 Signed-off-by: Stefan Hajnoczi Reviewed-by: Akihiko Odaki Fixes: cab1398a60eb ("pcie_sriov: Reuse SR-IOV VF device instances") Reviewed-by: Michael S. Tsirkin Message-ID: <20250924155153.579495-1-stefanha@redhat.com> Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie_sriov.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 29474d749a2f6..c4f88f097571a 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -195,7 +195,9 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, void pcie_sriov_pf_exit(PCIDevice *dev) { - uint8_t *cfg = dev->config + dev->exp.sriov_cap; + if (dev->exp.sriov_cap == 0) { + return; + } if (dev->exp.sriov_pf.vf_user_created) { uint16_t ven_id = pci_get_word(dev->config + PCI_VENDOR_ID); @@ -211,6 +213,8 @@ void pcie_sriov_pf_exit(PCIDevice *dev) pci_config_set_device_id(dev->exp.sriov_pf.vf[i]->config, vf_dev_id); } } else { + uint8_t *cfg = dev->config + dev->exp.sriov_cap; + unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)); } } From 6d10e021318b16e3e90f98b7b2fa187826e26c0a Mon Sep 17 00:00:00 2001 From: Viktor Kurilko Date: Fri, 8 Aug 2025 21:29:25 +0700 Subject: [PATCH 1245/1794] Add a feature for mapping a host unix socket to a guest tcp socket This patch adds the ability to map a host unix socket to a guest tcp socket when using the slirp backend. This feature was added in libslirp version 4.7.0. A new syntax for unix socket: -hostfwd=unix:hostpath-[guestaddr]:guestport Signed-off-by: Viktor Kurilko Signed-off-by: Samuel Thibault Message-ID: <20250808143904.363907-1-murlockkinght@gmail.com> --- docs/system/devices/net.rst | 2 +- hmp-commands.hx | 4 +- net/slirp.c | 114 +++++++++++++++++++++++++++--------- qapi/net.json | 2 +- qemu-options.hx | 11 +++- 5 files changed, 100 insertions(+), 33 deletions(-) diff --git a/docs/system/devices/net.rst b/docs/system/devices/net.rst index 7d76fe88c4541..13199a44fda4d 100644 --- a/docs/system/devices/net.rst +++ b/docs/system/devices/net.rst @@ -79,7 +79,7 @@ those sockets. To allow ping for GID 100 (usually users group):: When using the built-in TFTP server, the router is also the TFTP server. -When using the ``'-netdev user,hostfwd=...'`` option, TCP or UDP +When using the ``'-netdev user,hostfwd=...'`` option, TCP, UDP or UNIX connections can be redirected from the host to the guest. It allows for example to redirect X11, telnet or SSH connections. diff --git a/hmp-commands.hx b/hmp-commands.hx index 3cace8f1f7bff..15f6082596837 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1357,8 +1357,8 @@ ERST { .name = "hostfwd_add", .args_type = "arg1:s,arg2:s?", - .params = "[netdev_id] [tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport", - .help = "redirect TCP or UDP connections from host to guest (requires -net user)", + .params = "[netdev_id] [tcp|udp|unix]:[[hostaddr]:hostport|hostpath]-[guestaddr]:guestport", + .help = "redirect TCP, UDP or UNIX connections from host to guest (requires -net user)", .cmd = hmp_hostfwd_add, }, #endif diff --git a/net/slirp.c b/net/slirp.c index 0a1c2a5eac3a5..c627a9dd24b99 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -732,6 +732,7 @@ static SlirpState *slirp_lookup(Monitor *mon, const char *id) void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) { + /* TODO: support removing unix fwd */ struct sockaddr_in host_addr = { .sin_family = AF_INET, .sin_addr = { @@ -800,12 +801,13 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp) { - struct sockaddr_in host_addr = { - .sin_family = AF_INET, - .sin_addr = { - .s_addr = INADDR_ANY, - }, - }; + union { + struct sockaddr_in in; +#if !defined(WIN32) && SLIRP_CHECK_VERSION(4, 7, 0) + struct sockaddr_un un; +#endif + } host_addr = {0}; + struct sockaddr_in guest_addr = { .sin_family = AF_INET, .sin_addr = { @@ -816,9 +818,13 @@ static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp) int host_port, guest_port; const char *p; char buf[256]; - int is_udp; + int is_udp = 0; +#if !defined(WIN32) && SLIRP_CHECK_VERSION(4, 7, 0) + int is_unix = 0; +#endif const char *end; const char *fail_reason = "Unknown reason"; + socklen_t host_addr_size; p = redir_str; if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) { @@ -829,30 +835,83 @@ static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp) is_udp = 0; } else if (!strcmp(buf, "udp")) { is_udp = 1; - } else { - fail_reason = "Bad protocol name"; - goto fail_syntax; } - - if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - fail_reason = "Missing : separator"; - goto fail_syntax; +#if !defined(WIN32) && SLIRP_CHECK_VERSION(4, 7, 0) + else if (!strcmp(buf, "unix")) { + is_unix = 1; } - if (buf[0] != '\0' && !inet_aton(buf, &host_addr.sin_addr)) { - fail_reason = "Bad host address"; +#endif + else { + fail_reason = "Bad protocol name"; goto fail_syntax; } - if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) { - fail_reason = "Bad host port separator"; - goto fail_syntax; - } - err = qemu_strtoi(buf, &end, 0, &host_port); - if (err || host_port < 0 || host_port > 65535) { - fail_reason = "Bad host port"; - goto fail_syntax; +#if !defined(WIN32) && SLIRP_CHECK_VERSION(4, 7, 0) + if (is_unix) { + if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) { + fail_reason = "Missing - separator"; + goto fail_syntax; + } + if (buf[0] == '\0') { + fail_reason = "Missing unix socket path"; + goto fail_syntax; + } + if (buf[0] != '/') { + fail_reason = "unix socket path must be absolute"; + goto fail_syntax; + } + + size_t path_len = strlen(buf); + if (path_len > sizeof(host_addr.un.sun_path) - 1) { + fail_reason = "Unix socket path is too long"; + goto fail_syntax; + } + + struct stat st; + if (stat(buf, &st) == 0) { + if (!S_ISSOCK(st.st_mode)) { + fail_reason = "file exists and it's not unix socket"; + goto fail_syntax; + } + + if (unlink(buf) < 0) { + error_setg_errno(errp, errno, "Failed to unlink '%s'", buf); + goto fail_syntax; + } + } + host_addr.un.sun_family = AF_UNIX; + memcpy(host_addr.un.sun_path, buf, path_len); + host_addr_size = sizeof(host_addr.un); + } else +#endif + { + host_addr.in.sin_family = AF_INET; + host_addr.in.sin_addr.s_addr = INADDR_ANY; + + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + fail_reason = "Missing : separator"; + goto fail_syntax; + } + + if (buf[0] != '\0' && !inet_aton(buf, &host_addr.in.sin_addr)) { + fail_reason = "Bad host address"; + goto fail_syntax; + } + + if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) { + fail_reason = "Bad host port separator"; + goto fail_syntax; + } + + err = qemu_strtoi(buf, &end, 0, &host_port); + if (err || host_port < 0 || host_port > 65535) { + fail_reason = "Bad host port"; + goto fail_syntax; + } + + host_addr.in.sin_port = htons(host_port); + host_addr_size = sizeof(host_addr.in); } - host_addr.sin_port = htons(host_port); if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { fail_reason = "Missing guest address"; @@ -872,12 +931,13 @@ static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp) #if SLIRP_CHECK_VERSION(4, 5, 0) err = slirp_add_hostxfwd(s->slirp, - (struct sockaddr *) &host_addr, sizeof(host_addr), + (struct sockaddr *) &host_addr, host_addr_size, (struct sockaddr *) &guest_addr, sizeof(guest_addr), is_udp ? SLIRP_HOSTFWD_UDP : 0); #else + (void) host_addr_size; err = slirp_add_hostfwd(s->slirp, is_udp, - host_addr.sin_addr, host_port, + host_addr.in.sin_addr, host_port, guest_addr.sin_addr, guest_port); #endif diff --git a/qapi/net.json b/qapi/net.json index 78bcc9871e0c1..60d196afe5ce8 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -281,7 +281,7 @@ # # @smbserver: IP address of the built-in SMB server # -# @hostfwd: redirect incoming TCP or UDP host connections to guest +# @hostfwd: redirect incoming TCP, UDP or UNIX host connections to guest # endpoints # # @guestfwd: forward guest TCP connections diff --git a/qemu-options.hx b/qemu-options.hx index 075f4be2e3e67..a9d640d9e6c57 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3324,8 +3324,8 @@ SRST Note that a SAMBA server must be installed on the host OS. - ``hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport`` - Redirect incoming TCP or UDP connections to the host port + ``hostfwd=[tcp|udp|unix]:[[hostaddr]:hostport|hostpath]-[guestaddr]:guestport`` + Redirect incoming TCP, UDP or UNIX connections to the host port hostport to the guest IP address guestaddr on guest port guestport. If guestaddr is not specified, its value is x.x.x.15 (default first address given by the built-in DHCP server). By @@ -3355,6 +3355,13 @@ SRST Then when you use on the host ``telnet localhost 5555``, you connect to the guest telnet server. + To redirect host unix socket /tmp/vm to guest tcp socket 23 use + following: + + .. parsed-literal:: + # on the host + |qemu_system| -nic user,hostfwd=unix:/tmp/vm-:23 + ``guestfwd=[tcp]:server:port-dev``; \ ``guestfwd=[tcp]:server:port-cmd:command`` Forward guest TCP connections to the IP address server on port port to the character device dev or to a program executed by From bc54fb0168961fc61f8752ecdf979e4ab4e2439d Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:34:54 +0000 Subject: [PATCH 1246/1794] memory: Adjust event ranges to fit within notifier boundaries Invalidating the entire address space (i.e. range of [0, ~0ULL]) is a valid and required operation by vIOMMU implementations. However, such invalidations currently trigger an assertion unless they originate from device IOTLB invalidations. Although in recent Linux guests this case is not exercised by the VTD implementation due to various optimizations, the assertion will be hit by upcoming AMD vIOMMU changes to support DMA address translation. More specifically, when running a Linux guest with VFIO passthrough device, and a kernel that does not contain commmit 3f2571fed2fa ("iommu/amd: Remove redundant domain flush from attach_device()"). Remove the assertion altogether and adjust the range to ensure it does not cross notifier boundaries. Signed-off-by: Alejandro Jimenez Acked-by: Michael S. Tsirkin Acked-by: David Hildenbrand Acked-by: Peter Xu Message-Id: <20201116165506.31315-6-eperezma@redhat.com> Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-2-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- system/memory.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/system/memory.c b/system/memory.c index cf8cad6961156..5c6ccc5c57412 100644 --- a/system/memory.c +++ b/system/memory.c @@ -2044,13 +2044,9 @@ void memory_region_notify_iommu_one(IOMMUNotifier *notifier, return; } - if (notifier->notifier_flags & IOMMU_NOTIFIER_DEVIOTLB_UNMAP) { - /* Crop (iova, addr_mask) to range */ - tmp.iova = MAX(tmp.iova, notifier->start); - tmp.addr_mask = MIN(entry_end, notifier->end) - tmp.iova; - } else { - assert(entry->iova >= notifier->start && entry_end <= notifier->end); - } + /* Crop (iova, addr_mask) to range */ + tmp.iova = MAX(tmp.iova, notifier->start); + tmp.addr_mask = MIN(entry_end, notifier->end) - tmp.iova; if (event->type & notifier->notifier_flags) { notifier->notify(notifier, &tmp); From 918973f1d476a62922761c9a5484b309491b652c Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:34:55 +0000 Subject: [PATCH 1247/1794] amd_iommu: Document '-device amd-iommu' common options Document the common parameters used when emulating AMD vIOMMU. Besides the two amd-iommu specific options: 'xtsup' and 'dma-remap', the the generic x86 IOMMU option 'intremap' is also included, since it is typically specified in QEMU command line examples and mailing list threads. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-3-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- qemu-options.hx | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/qemu-options.hx b/qemu-options.hx index 3ff10ec09d9c6..9b4e870350aad 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1238,6 +1238,29 @@ SRST Accepts either the default root complex (pcie.0) or a pxb-pcie based root complex. +``-device amd-iommu[,option=...]`` + Enables emulation of an AMD-Vi I/O Memory Management Unit (IOMMU). + Only available with ``-machine q35``, it supports the following options: + + ``dma-remap=on|off`` (default: off) + Support for DMA address translation and access permission checking for + guests attaching passthrough devices to paging domains, using the AMD v1 + I/O Page Table format. This enables ``-device vfio-pci,...`` to work + correctly with a guest using the DMA remapping feature of the vIOMMU. + + ``intremap=on|off`` (default: auto) + Generic x86 IOMMU functionality implemented by ``amd-iommu`` device. + Enables interrupt remapping feature in guests, which is also required to + enable x2apic support. + Currently only available with ``kernel-irqchip=off|split``, it is + automatically enabled when either of those modes is in use, and disabled + with ``kernel-irqchip=on``. + + ``xtsup=on|off`` (default: off) + Interrupt remapping table supports x2apic mode, enabling the use of + 128-bit IRTE format with 32-bit destination field by the guest. Required + to support routing interrupts to vCPUs with APIC IDs larger than 0xff. + ERST DEF("name", HAS_ARG, QEMU_OPTION_name, From f76fc5d4ca54ea27597b3f0503ddb6e9030bb52d Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:34:56 +0000 Subject: [PATCH 1248/1794] amd_iommu: Reorder device and page table helpers Move code related to Device Table and Page Table to an earlier location in the file, where it does not require forward declarations to be used by the various invalidation functions that will need to query the DTE and walk the page table in upcoming changes. This change consist of code movement only, no functional change intended. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-4-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 172 ++++++++++++++++++++++---------------------- 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 26be69bec8ae2..3cbc9499dbcc4 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -438,6 +438,92 @@ static void amdvi_completion_wait(AMDVIState *s, uint64_t *cmd) trace_amdvi_completion_wait(addr, data); } +static inline uint64_t amdvi_get_perms(uint64_t entry) +{ + return (entry & (AMDVI_DEV_PERM_READ | AMDVI_DEV_PERM_WRITE)) >> + AMDVI_DEV_PERM_SHIFT; +} + +/* validate that reserved bits are honoured */ +static bool amdvi_validate_dte(AMDVIState *s, uint16_t devid, + uint64_t *dte) +{ + if ((dte[0] & AMDVI_DTE_QUAD0_RESERVED) || + (dte[1] & AMDVI_DTE_QUAD1_RESERVED) || + (dte[2] & AMDVI_DTE_QUAD2_RESERVED) || + (dte[3] & AMDVI_DTE_QUAD3_RESERVED)) { + amdvi_log_illegaldevtab_error(s, devid, + s->devtab + + devid * AMDVI_DEVTAB_ENTRY_SIZE, 0); + return false; + } + + return true; +} + +/* get a device table entry given the devid */ +static bool amdvi_get_dte(AMDVIState *s, int devid, uint64_t *entry) +{ + uint32_t offset = devid * AMDVI_DEVTAB_ENTRY_SIZE; + + if (dma_memory_read(&address_space_memory, s->devtab + offset, entry, + AMDVI_DEVTAB_ENTRY_SIZE, MEMTXATTRS_UNSPECIFIED)) { + trace_amdvi_dte_get_fail(s->devtab, offset); + /* log error accessing dte */ + amdvi_log_devtab_error(s, devid, s->devtab + offset, 0); + return false; + } + + *entry = le64_to_cpu(*entry); + if (!amdvi_validate_dte(s, devid, entry)) { + trace_amdvi_invalid_dte(entry[0]); + return false; + } + + return true; +} + +/* get pte translation mode */ +static inline uint8_t get_pte_translation_mode(uint64_t pte) +{ + return (pte >> AMDVI_DEV_MODE_RSHIFT) & AMDVI_DEV_MODE_MASK; +} + +static inline uint64_t pte_override_page_mask(uint64_t pte) +{ + uint8_t page_mask = 13; + uint64_t addr = (pte & AMDVI_DEV_PT_ROOT_MASK) >> 12; + /* find the first zero bit */ + while (addr & 1) { + page_mask++; + addr = addr >> 1; + } + + return ~((1ULL << page_mask) - 1); +} + +static inline uint64_t pte_get_page_mask(uint64_t oldlevel) +{ + return ~((1UL << ((oldlevel * 9) + 3)) - 1); +} + +static inline uint64_t amdvi_get_pte_entry(AMDVIState *s, uint64_t pte_addr, + uint16_t devid) +{ + uint64_t pte; + + if (dma_memory_read(&address_space_memory, pte_addr, + &pte, sizeof(pte), MEMTXATTRS_UNSPECIFIED)) { + trace_amdvi_get_pte_hwerror(pte_addr); + amdvi_log_pagetab_error(s, devid, pte_addr, 0); + pte = 0; + return pte; + } + + pte = le64_to_cpu(pte); + return pte; +} + /* log error without aborting since linux seems to be using reserved bits */ static void amdvi_inval_devtab_entry(AMDVIState *s, uint64_t *cmd) { @@ -894,92 +980,6 @@ static void amdvi_mmio_write(void *opaque, hwaddr addr, uint64_t val, } } -static inline uint64_t amdvi_get_perms(uint64_t entry) -{ - return (entry & (AMDVI_DEV_PERM_READ | AMDVI_DEV_PERM_WRITE)) >> - AMDVI_DEV_PERM_SHIFT; -} - -/* validate that reserved bits are honoured */ -static bool amdvi_validate_dte(AMDVIState *s, uint16_t devid, - uint64_t *dte) -{ - if ((dte[0] & AMDVI_DTE_QUAD0_RESERVED) || - (dte[1] & AMDVI_DTE_QUAD1_RESERVED) || - (dte[2] & AMDVI_DTE_QUAD2_RESERVED) || - (dte[3] & AMDVI_DTE_QUAD3_RESERVED)) { - amdvi_log_illegaldevtab_error(s, devid, - s->devtab + - devid * AMDVI_DEVTAB_ENTRY_SIZE, 0); - return false; - } - - return true; -} - -/* get a device table entry given the devid */ -static bool amdvi_get_dte(AMDVIState *s, int devid, uint64_t *entry) -{ - uint32_t offset = devid * AMDVI_DEVTAB_ENTRY_SIZE; - - if (dma_memory_read(&address_space_memory, s->devtab + offset, entry, - AMDVI_DEVTAB_ENTRY_SIZE, MEMTXATTRS_UNSPECIFIED)) { - trace_amdvi_dte_get_fail(s->devtab, offset); - /* log error accessing dte */ - amdvi_log_devtab_error(s, devid, s->devtab + offset, 0); - return false; - } - - *entry = le64_to_cpu(*entry); - if (!amdvi_validate_dte(s, devid, entry)) { - trace_amdvi_invalid_dte(entry[0]); - return false; - } - - return true; -} - -/* get pte translation mode */ -static inline uint8_t get_pte_translation_mode(uint64_t pte) -{ - return (pte >> AMDVI_DEV_MODE_RSHIFT) & AMDVI_DEV_MODE_MASK; -} - -static inline uint64_t pte_override_page_mask(uint64_t pte) -{ - uint8_t page_mask = 13; - uint64_t addr = (pte & AMDVI_DEV_PT_ROOT_MASK) >> 12; - /* find the first zero bit */ - while (addr & 1) { - page_mask++; - addr = addr >> 1; - } - - return ~((1ULL << page_mask) - 1); -} - -static inline uint64_t pte_get_page_mask(uint64_t oldlevel) -{ - return ~((1UL << ((oldlevel * 9) + 3)) - 1); -} - -static inline uint64_t amdvi_get_pte_entry(AMDVIState *s, uint64_t pte_addr, - uint16_t devid) -{ - uint64_t pte; - - if (dma_memory_read(&address_space_memory, pte_addr, - &pte, sizeof(pte), MEMTXATTRS_UNSPECIFIED)) { - trace_amdvi_get_pte_hwerror(pte_addr); - amdvi_log_pagetab_error(s, devid, pte_addr, 0); - pte = 0; - return pte; - } - - pte = le64_to_cpu(pte); - return pte; -} - static void amdvi_page_walk(AMDVIAddressSpace *as, uint64_t *dte, IOMMUTLBEntry *ret, unsigned perms, hwaddr addr) From e8992fee661ace87c4e871f29f5b2caab1153327 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:34:57 +0000 Subject: [PATCH 1249/1794] amd_iommu: Helper to decode size of page invalidation command The size of the region to invalidate depends on the S bit and address encoded in the command. Add a helper to extract this information, which will be used to sync shadow page tables in upcoming changes. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-5-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 34 ++++++++++++++++++++++++++++++++++ hw/i386/amd_iommu.h | 4 ++++ 2 files changed, 38 insertions(+) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 3cbc9499dbcc4..202f0f8c6e90c 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -577,6 +577,40 @@ static gboolean amdvi_iotlb_remove_by_domid(gpointer key, gpointer value, return entry->domid == domid; } +/* + * Helper to decode the size of the range to invalidate encoded in the + * INVALIDATE_IOMMU_PAGES Command format. + * The size of the region to invalidate depends on the S bit and address. + * S bit value: + * 0 : Invalidation size is 4 Kbytes. + * 1 : Invalidation size is determined by first zero bit in the address + * starting from Address[12]. + * + * In the AMD IOMMU Linux driver, an invalidation command with address + * ((1 << 63) - 1) is sent when intending to clear the entire cache. + * However, Table 14: Example Page Size Encodings shows that an address of + * ((1ULL << 51) - 1) encodes the entire cache, so effectively any address with + * first zero at bit 51 or larger is a request to invalidate the entire address + * space. + */ +static uint64_t __attribute__((unused)) +amdvi_decode_invalidation_size(hwaddr addr, uint16_t flags) +{ + uint64_t size = AMDVI_PAGE_SIZE; + uint8_t fzbit = 0; + + if (flags & AMDVI_CMD_INVAL_IOMMU_PAGES_S) { + fzbit = cto64(addr | 0xFFF); + + if (fzbit >= 51) { + size = AMDVI_INV_ALL_PAGES; + } else { + size = 1ULL << (fzbit + 1); + } + } + return size; +} + /* we don't have devid - we can't remove pages by address */ static void amdvi_inval_pages(AMDVIState *s, uint64_t *cmd) { diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 2476296c49023..c1170a820257e 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -126,6 +126,10 @@ #define AMDVI_CMD_COMPLETE_PPR_REQUEST 0x07 #define AMDVI_CMD_INVAL_AMDVI_ALL 0x08 + +#define AMDVI_CMD_INVAL_IOMMU_PAGES_S (1ULL << 0) +#define AMDVI_INV_ALL_PAGES (1ULL << 52) + #define AMDVI_DEVTAB_ENTRY_SIZE 32 /* Device table entry bits 0:63 */ From 786d11853095af9c935aab19d5fd341cfb118feb Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:34:58 +0000 Subject: [PATCH 1250/1794] amd_iommu: Add helper function to extract the DTE Extracting the DTE from a given AMDVIAddressSpace pointer structure is a common operation required for syncing the shadow page tables. Implement a helper to do it and check for common error conditions. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-6-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 48 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 202f0f8c6e90c..dc7531fd4a8b9 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -77,6 +77,18 @@ typedef struct AMDVIIOTLBEntry { uint64_t page_mask; /* physical page size */ } AMDVIIOTLBEntry; +/* + * These 'fault' reasons have an overloaded meaning since they are not only + * intended for describing reasons that generate an IO_PAGE_FAULT as per the AMD + * IOMMU specification, but are also used to signal internal errors in the + * emulation code. + */ +typedef enum AMDVIFaultReason { + AMDVI_FR_DTE_RTR_ERR = 1, /* Failure to retrieve DTE */ + AMDVI_FR_DTE_V, /* DTE[V] = 0 */ + AMDVI_FR_DTE_TV, /* DTE[TV] = 0 */ +} AMDVIFaultReason; + uint64_t amdvi_extended_feature_register(AMDVIState *s) { uint64_t feature = AMDVI_DEFAULT_EXT_FEATURES; @@ -524,6 +536,28 @@ static inline uint64_t amdvi_get_pte_entry(AMDVIState *s, uint64_t pte_addr, return pte; } +static int amdvi_as_to_dte(AMDVIAddressSpace *as, uint64_t *dte) +{ + uint16_t devid = PCI_BUILD_BDF(as->bus_num, as->devfn); + AMDVIState *s = as->iommu_state; + + if (!amdvi_get_dte(s, devid, dte)) { + /* Unable to retrieve DTE for devid */ + return -AMDVI_FR_DTE_RTR_ERR; + } + + if (!(dte[0] & AMDVI_DEV_VALID)) { + /* DTE[V] not set, address is passed untranslated for devid */ + return -AMDVI_FR_DTE_V; + } + + if (!(dte[0] & AMDVI_DEV_TRANSLATION_VALID)) { + /* DTE[TV] not set, host page table not valid for devid */ + return -AMDVI_FR_DTE_TV; + } + return 0; +} + /* log error without aborting since linux seems to be using reserved bits */ static void amdvi_inval_devtab_entry(AMDVIState *s, uint64_t *cmd) { @@ -1081,6 +1115,7 @@ static void amdvi_do_translate(AMDVIAddressSpace *as, hwaddr addr, uint16_t devid = PCI_BUILD_BDF(as->bus_num, as->devfn); AMDVIIOTLBEntry *iotlb_entry = amdvi_iotlb_lookup(s, addr, devid); uint64_t entry[4]; + int dte_ret; if (iotlb_entry) { trace_amdvi_iotlb_hit(PCI_BUS_NUM(devid), PCI_SLOT(devid), @@ -1092,13 +1127,14 @@ static void amdvi_do_translate(AMDVIAddressSpace *as, hwaddr addr, return; } - if (!amdvi_get_dte(s, devid, entry)) { - return; - } + dte_ret = amdvi_as_to_dte(as, entry); - /* devices with V = 0 are not translated */ - if (!(entry[0] & AMDVI_DEV_VALID)) { - goto out; + if (dte_ret < 0) { + if (dte_ret == -AMDVI_FR_DTE_V) { + /* DTE[V]=0, address is passed untranslated */ + goto out; + } + return; } amdvi_page_walk(as, entry, ret, From 0f13cc5642ea81fc952ef20515891d12c5193d24 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:34:59 +0000 Subject: [PATCH 1251/1794] amd_iommu: Return an error when unable to read PTE from guest memory Make amdvi_get_pte_entry() return an error value (-1) in cases where the memory read fails, versus the current return of 0 to indicate failure. The reason is that 0 is also a valid value to have stored in the PTE in guest memory i.e. the guest does not have a mapping. Before this change, amdvi_get_pte_entry() returned 0 for both an error and for empty PTEs, but the page walker implementation that will be introduced in upcoming changes needs a method to differentiate between the two scenarios. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-7-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index dc7531fd4a8b9..29ed3f0ef292e 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -528,7 +528,7 @@ static inline uint64_t amdvi_get_pte_entry(AMDVIState *s, uint64_t pte_addr, &pte, sizeof(pte), MEMTXATTRS_UNSPECIFIED)) { trace_amdvi_get_pte_hwerror(pte_addr); amdvi_log_pagetab_error(s, devid, pte_addr, 0); - pte = 0; + pte = (uint64_t)-1; return pte; } @@ -1081,7 +1081,7 @@ static void amdvi_page_walk(AMDVIAddressSpace *as, uint64_t *dte, /* add offset and load pte */ pte_addr += ((addr >> (3 + 9 * level)) & 0x1FF) << 3; pte = amdvi_get_pte_entry(as->iommu_state, pte_addr, as->devfn); - if (!pte) { + if (!pte || (pte == (uint64_t)-1)) { return; } oldlevel = level; From a74bb3110a5b7b5da51ada160732cfc2c83d80d7 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:35:00 +0000 Subject: [PATCH 1252/1794] amd_iommu: Add helpers to walk AMD v1 Page Table format The current amdvi_page_walk() is designed to be called by the replay() method. Rather than drastically altering it, introduce helpers to fetch guest PTEs that will be used by a page walker implementation. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-8-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 123 ++++++++++++++++++++++++++++++++++++++++++++ hw/i386/amd_iommu.h | 40 ++++++++++++++ 2 files changed, 163 insertions(+) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 29ed3f0ef292e..c25981ff93c02 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -87,6 +87,8 @@ typedef enum AMDVIFaultReason { AMDVI_FR_DTE_RTR_ERR = 1, /* Failure to retrieve DTE */ AMDVI_FR_DTE_V, /* DTE[V] = 0 */ AMDVI_FR_DTE_TV, /* DTE[TV] = 0 */ + AMDVI_FR_PT_ROOT_INV, /* Page Table Root ptr invalid */ + AMDVI_FR_PT_ENTRY_INV, /* Failure to read PTE from guest memory */ } AMDVIFaultReason; uint64_t amdvi_extended_feature_register(AMDVIState *s) @@ -558,6 +560,127 @@ static int amdvi_as_to_dte(AMDVIAddressSpace *as, uint64_t *dte) return 0; } +/* + * For a PTE encoding a large page, return the page size it encodes as described + * by the AMD IOMMU Specification Table 14: Example Page Size Encodings. + * No need to adjust the value of the PTE to point to the first PTE in the large + * page since the encoding guarantees all "base" PTEs in the large page are the + * same. + */ +static uint64_t large_pte_page_size(uint64_t pte) +{ + assert(PTE_NEXT_LEVEL(pte) == 7); + + /* Determine size of the large/contiguous page encoded in the PTE */ + return PTE_LARGE_PAGE_SIZE(pte); +} + +/* + * Helper function to fetch a PTE using AMD v1 pgtable format. + * On successful page walk, returns 0 and pte parameter points to a valid PTE. + * On failure, returns: + * -AMDVI_FR_PT_ROOT_INV: A page walk is not possible due to conditions like DTE + * with invalid permissions, Page Table Root can not be read from DTE, or a + * larger IOVA than supported by page table level encoded in DTE[Mode]. + * -AMDVI_FR_PT_ENTRY_INV: A PTE could not be read from guest memory during a + * page table walk. This means that the DTE has valid data, but one of the + * lower level entries in the Page Table could not be read. + */ +static int __attribute__((unused)) +fetch_pte(AMDVIAddressSpace *as, hwaddr address, uint64_t dte, uint64_t *pte, + hwaddr *page_size) +{ + IOMMUAccessFlags perms = amdvi_get_perms(dte); + + uint8_t level, mode; + uint64_t pte_addr; + + *pte = dte; + *page_size = 0; + + if (perms == IOMMU_NONE) { + return -AMDVI_FR_PT_ROOT_INV; + } + + /* + * The Linux kernel driver initializes the default mode to 3, corresponding + * to a 39-bit GPA space, where each entry in the pagetable translates to a + * 1GB (2^30) page size. + */ + level = mode = get_pte_translation_mode(dte); + assert(mode > 0 && mode < 7); + + /* + * If IOVA is larger than the max supported by the current pgtable level, + * there is nothing to do. + */ + if (address > PT_LEVEL_MAX_ADDR(mode - 1)) { + /* IOVA too large for the current DTE */ + return -AMDVI_FR_PT_ROOT_INV; + } + + do { + level -= 1; + + /* Update the page_size */ + *page_size = PTE_LEVEL_PAGE_SIZE(level); + + /* Permission bits are ANDed at every level, including the DTE */ + perms &= amdvi_get_perms(*pte); + if (perms == IOMMU_NONE) { + return 0; + } + + /* Not Present */ + if (!IOMMU_PTE_PRESENT(*pte)) { + return 0; + } + + /* Large or Leaf PTE found */ + if (PTE_NEXT_LEVEL(*pte) == 7 || PTE_NEXT_LEVEL(*pte) == 0) { + /* Leaf PTE found */ + break; + } + + /* + * Index the pgtable using the IOVA bits corresponding to current level + * and walk down to the lower level. + */ + pte_addr = NEXT_PTE_ADDR(*pte, level, address); + *pte = amdvi_get_pte_entry(as->iommu_state, pte_addr, as->devfn); + + if (*pte == (uint64_t)-1) { + /* + * A returned PTE of -1 indicates a failure to read the page table + * entry from guest memory. + */ + if (level == mode - 1) { + /* Failure to retrieve the Page Table from Root Pointer */ + *page_size = 0; + return -AMDVI_FR_PT_ROOT_INV; + } else { + /* Failure to read PTE. Page walk skips a page_size chunk */ + return -AMDVI_FR_PT_ENTRY_INV; + } + } + } while (level > 0); + + assert(PTE_NEXT_LEVEL(*pte) == 0 || PTE_NEXT_LEVEL(*pte) == 7 || + level == 0); + /* + * Page walk ends when Next Level field on PTE shows that either a leaf PTE + * or a series of large PTEs have been reached. In the latter case, even if + * the range starts in the middle of a contiguous page, the returned PTE + * must be the first PTE of the series. + */ + if (PTE_NEXT_LEVEL(*pte) == 7) { + /* Update page_size with the large PTE page size */ + *page_size = large_pte_page_size(*pte); + } + + return 0; +} + /* log error without aborting since linux seems to be using reserved bits */ static void amdvi_inval_devtab_entry(AMDVIState *s, uint64_t *cmd) { diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index c1170a820257e..9f833b297d25c 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -178,6 +178,46 @@ #define AMDVI_GATS_MODE (2ULL << 12) #define AMDVI_HATS_MODE (2ULL << 10) +/* Page Table format */ + +#define AMDVI_PTE_PR (1ULL << 0) +#define AMDVI_PTE_NEXT_LEVEL_MASK GENMASK64(11, 9) + +#define IOMMU_PTE_PRESENT(pte) ((pte) & AMDVI_PTE_PR) + +/* Using level=0 for leaf PTE at 4K page size */ +#define PT_LEVEL_SHIFT(level) (12 + ((level) * 9)) + +/* Return IOVA bit group used to index the Page Table at specific level */ +#define PT_LEVEL_INDEX(level, iova) (((iova) >> PT_LEVEL_SHIFT(level)) & \ + GENMASK64(8, 0)) + +/* Return the max address for a specified level i.e. max_oaddr */ +#define PT_LEVEL_MAX_ADDR(x) (((x) < 5) ? \ + ((1ULL << PT_LEVEL_SHIFT((x + 1))) - 1) : \ + (~(0ULL))) + +/* Extract the NextLevel field from PTE/PDE */ +#define PTE_NEXT_LEVEL(pte) (((pte) & AMDVI_PTE_NEXT_LEVEL_MASK) >> 9) + +/* Take page table level and return default pagetable size for level */ +#define PTE_LEVEL_PAGE_SIZE(level) (1ULL << (PT_LEVEL_SHIFT(level))) + +/* + * Return address of lower level page table encoded in PTE and specified by + * current level and corresponding IOVA bit group at such level. + */ +#define NEXT_PTE_ADDR(pte, level, iova) (((pte) & AMDVI_DEV_PT_ROOT_MASK) + \ + (PT_LEVEL_INDEX(level, iova) * 8)) + +/* + * Take a PTE value with mode=0x07 and return the page size it encodes. + */ +#define PTE_LARGE_PAGE_SIZE(pte) (1ULL << (1 + cto64(((pte) | 0xfffULL)))) + +/* Return number of PTEs to use for a given page size (expected power of 2) */ +#define PAGE_SIZE_PTE_COUNT(pgsz) (1ULL << ((ctz64(pgsz) - 12) % 9)) + /* IOTLB */ #define AMDVI_IOTLB_MAX_SIZE 1024 #define AMDVI_DEVID_SHIFT 36 From 7a2ce64950fbe43038eeba1889a142c5784f5021 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:35:01 +0000 Subject: [PATCH 1253/1794] amd_iommu: Add a page walker to sync shadow page tables on invalidation For the specified address range, walk the page table identifying regions as mapped or unmapped and invoke registered notifiers with the corresponding event type. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-9-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 80 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index c25981ff93c02..0e45435c77be9 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -681,6 +681,86 @@ fetch_pte(AMDVIAddressSpace *as, hwaddr address, uint64_t dte, uint64_t *pte, return 0; } +/* + * Walk the guest page table for an IOVA and range and signal the registered + * notifiers to sync the shadow page tables in the host. + * Must be called with a valid DTE for DMA remapping i.e. V=1,TV=1 + */ +static void __attribute__((unused)) +amdvi_sync_shadow_page_table_range(AMDVIAddressSpace *as, uint64_t *dte, + hwaddr addr, uint64_t size, bool send_unmap) +{ + IOMMUTLBEvent event; + + hwaddr iova_next, page_mask, pagesize; + hwaddr iova = addr; + hwaddr end = iova + size - 1; + + uint64_t pte; + int ret; + + while (iova < end) { + + ret = fetch_pte(as, iova, dte[0], &pte, &pagesize); + + if (ret == -AMDVI_FR_PT_ROOT_INV) { + /* + * Invalid conditions such as the IOVA being larger than supported + * by current page table mode as configured in the DTE, or a failure + * to fetch the Page Table from the Page Table Root Pointer in DTE. + */ + assert(pagesize == 0); + return; + } + /* PTE has been validated for major errors and pagesize is set */ + assert(pagesize); + page_mask = ~(pagesize - 1); + iova_next = (iova & page_mask) + pagesize; + + if (ret == -AMDVI_FR_PT_ENTRY_INV) { + /* + * Failure to read PTE from memory, the pagesize matches the current + * level. Unable to determine the region type, so a safe strategy is + * to skip the range and continue the page walk. + */ + goto next; + } + + event.entry.target_as = &address_space_memory; + event.entry.iova = iova & page_mask; + /* translated_addr is irrelevant for the unmap case */ + event.entry.translated_addr = (pte & AMDVI_DEV_PT_ROOT_MASK) & + page_mask; + event.entry.addr_mask = ~page_mask; + event.entry.perm = amdvi_get_perms(pte); + + /* + * In cases where the leaf PTE is not found, or it has invalid + * permissions, an UNMAP type notification is sent, but only if the + * caller requested it. + */ + if (!IOMMU_PTE_PRESENT(pte) || (event.entry.perm == IOMMU_NONE)) { + if (!send_unmap) { + goto next; + } + event.type = IOMMU_NOTIFIER_UNMAP; + } else { + event.type = IOMMU_NOTIFIER_MAP; + } + + /* Invoke the notifiers registered for this address space */ + memory_region_notify_iommu(&as->iommu, 0, event); + +next: + /* Check for 64-bit overflow and terminate walk in such cases */ + if (iova_next < iova) { + break; + } else { + iova = iova_next; + } + } +} + /* log error without aborting since linux seems to be using reserved bits */ static void amdvi_inval_devtab_entry(AMDVIState *s, uint64_t *cmd) { From 557f4cda12da8cd4c77af3a2b41c8331116996af Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:35:02 +0000 Subject: [PATCH 1254/1794] amd_iommu: Add basic structure to support IOMMU notifier updates Add the minimal data structures required to maintain a list of address spaces (i.e. devices) with registered notifiers, and to update the type of events that require notifications. Note that the ability to register for MAP notifications is not available. It will be unblocked by following changes that enable the synchronization of guest I/O page tables with host IOMMU state, at which point an amd-iommu device property will be introduced to control this capability. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-10-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 20 ++++++++++++++++++++ hw/i386/amd_iommu.h | 3 +++ 2 files changed, 23 insertions(+) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 0e45435c77be9..d8a451b3a5ff1 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -66,6 +66,11 @@ struct AMDVIAddressSpace { MemoryRegion iommu_nodma; /* Alias of shared nodma memory region */ MemoryRegion iommu_ir; /* Device's interrupt remapping region */ AddressSpace as; /* device's corresponding address space */ + + /* DMA address translation support */ + IOMMUNotifierFlag notifier_flags; + /* entry in list of Address spaces with registered notifiers */ + QLIST_ENTRY(AMDVIAddressSpace) next; }; /* AMDVI cache entry */ @@ -1773,6 +1778,7 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) iommu_as[devfn]->bus_num = (uint8_t)bus_num; iommu_as[devfn]->devfn = (uint8_t)devfn; iommu_as[devfn]->iommu_state = s; + iommu_as[devfn]->notifier_flags = IOMMU_NOTIFIER_NONE; amdvi_dev_as = iommu_as[devfn]; @@ -1846,6 +1852,7 @@ static int amdvi_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, Error **errp) { AMDVIAddressSpace *as = container_of(iommu, AMDVIAddressSpace, iommu); + AMDVIState *s = as->iommu_state; if (new & IOMMU_NOTIFIER_MAP) { error_setg(errp, @@ -1854,6 +1861,19 @@ static int amdvi_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, PCI_FUNC(as->devfn)); return -EINVAL; } + + /* + * Update notifier flags for address space and the list of address spaces + * with registered notifiers. + */ + as->notifier_flags = new; + + if (old == IOMMU_NOTIFIER_NONE) { + QLIST_INSERT_HEAD(&s->amdvi_as_with_notifiers, as, next); + } else if (new == IOMMU_NOTIFIER_NONE) { + QLIST_REMOVE(as, next); + } + return 0; } diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 9f833b297d25c..b51aa74368995 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -409,6 +409,9 @@ struct AMDVIState { /* for each served device */ AMDVIAddressSpace **address_spaces[PCI_BUS_MAX]; + /* list of address spaces with registered notifiers */ + QLIST_HEAD(, AMDVIAddressSpace) amdvi_as_with_notifiers; + /* IOTLB */ GHashTable *iotlb; From a1c97c395729164747f2f30e9b8ec5d497af95c9 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:35:03 +0000 Subject: [PATCH 1255/1794] amd_iommu: Sync shadow page tables on page invalidation When the guest issues an INVALIDATE_IOMMU_PAGES command, decode the address and size of the invalidation and sync the guest page table state with the host. This requires walking the guest page table and calling notifiers registered for address spaces matching the domain ID encoded in the command. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-11-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 82 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 8 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index d8a451b3a5ff1..caae65c4b3565 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -591,9 +591,8 @@ static uint64_t large_pte_page_size(uint64_t pte) * page table walk. This means that the DTE has valid data, but one of the * lower level entries in the Page Table could not be read. */ -static int __attribute__((unused)) -fetch_pte(AMDVIAddressSpace *as, hwaddr address, uint64_t dte, uint64_t *pte, - hwaddr *page_size) +static uint64_t fetch_pte(AMDVIAddressSpace *as, hwaddr address, uint64_t dte, + uint64_t *pte, hwaddr *page_size) { IOMMUAccessFlags perms = amdvi_get_perms(dte); @@ -691,9 +690,9 @@ fetch_pte(AMDVIAddressSpace *as, hwaddr address, uint64_t dte, uint64_t *pte, * notifiers to sync the shadow page tables in the host. * Must be called with a valid DTE for DMA remapping i.e. V=1,TV=1 */ -static void __attribute__((unused)) -amdvi_sync_shadow_page_table_range(AMDVIAddressSpace *as, uint64_t *dte, - hwaddr addr, uint64_t size, bool send_unmap) +static void amdvi_sync_shadow_page_table_range(AMDVIAddressSpace *as, + uint64_t *dte, hwaddr addr, + uint64_t size, bool send_unmap) { IOMMUTLBEvent event; @@ -835,8 +834,7 @@ static gboolean amdvi_iotlb_remove_by_domid(gpointer key, gpointer value, * first zero at bit 51 or larger is a request to invalidate the entire address * space. */ -static uint64_t __attribute__((unused)) -amdvi_decode_invalidation_size(hwaddr addr, uint16_t flags) +static uint64_t amdvi_decode_invalidation_size(hwaddr addr, uint16_t flags) { uint64_t size = AMDVI_PAGE_SIZE; uint8_t fzbit = 0; @@ -853,10 +851,76 @@ amdvi_decode_invalidation_size(hwaddr addr, uint16_t flags) return size; } +/* + * Synchronize the guest page tables with the shadow page tables kept in the + * host for the specified range. + * The invalidation command issued by the guest and intercepted by the VMM + * does not specify a device, but a domain, since all devices in the same domain + * share the same page tables. However, vIOMMU emulation creates separate + * address spaces per device, so it is necessary to traverse the list of all of + * address spaces (i.e. devices) that have notifiers registered in order to + * propagate the changes to the host page tables. + * We cannot return early from this function once a matching domain has been + * identified and its page tables synced (based on the fact that all devices in + * the same domain share the page tables). The reason is that different devices + * (i.e. address spaces) could have different notifiers registered, and by + * skipping address spaces that appear later on the amdvi_as_with_notifiers list + * their notifiers (which could differ from the ones registered for the first + * device/address space) would not be invoked. + */ +static void amdvi_sync_domain(AMDVIState *s, uint16_t domid, uint64_t addr, + uint16_t flags) +{ + AMDVIAddressSpace *as; + + uint64_t size = amdvi_decode_invalidation_size(addr, flags); + + if (size == AMDVI_INV_ALL_PAGES) { + addr = 0; /* Set start address to 0 and invalidate entire AS */ + } else { + addr &= ~(size - 1); + } + + /* + * Call notifiers that have registered for each address space matching the + * domain ID, in order to sync the guest pagetable state with the host. + */ + QLIST_FOREACH(as, &s->amdvi_as_with_notifiers, next) { + + uint64_t dte[4] = { 0 }; + + /* + * Retrieve the Device Table entry for the devid corresponding to the + * current address space, and verify the DomainID matches i.e. the page + * tables to be synced belong to devices in the domain. + */ + if (amdvi_as_to_dte(as, dte)) { + continue; + } + + /* Only need to sync the Page Tables for a matching domain */ + if (domid != (dte[1] & AMDVI_DEV_DOMID_ID_MASK)) { + continue; + } + + /* + * We have determined that there is a valid Device Table Entry for a + * device matching the DomainID in the INV_IOMMU_PAGES command issued by + * the guest. Walk the guest page table to sync shadow page table. + */ + if (as->notifier_flags & IOMMU_NOTIFIER_MAP) { + /* Sync guest IOMMU mappings with host */ + amdvi_sync_shadow_page_table_range(as, &dte[0], addr, size, true); + } + } +} + /* we don't have devid - we can't remove pages by address */ static void amdvi_inval_pages(AMDVIState *s, uint64_t *cmd) { uint16_t domid = cpu_to_le16((uint16_t)extract64(cmd[0], 32, 16)); + uint64_t addr = cpu_to_le64(extract64(cmd[1], 12, 52)) << 12; + uint16_t flags = cpu_to_le16((uint16_t)extract64(cmd[1], 0, 3)); if (extract64(cmd[0], 20, 12) || extract64(cmd[0], 48, 12) || extract64(cmd[1], 3, 9)) { @@ -866,6 +930,8 @@ static void amdvi_inval_pages(AMDVIState *s, uint64_t *cmd) g_hash_table_foreach_remove(s->iotlb, amdvi_iotlb_remove_by_domid, &domid); + + amdvi_sync_domain(s, domid, addr, flags); trace_amdvi_pages_inval(domid); } From fe0a58f770330165c885d323632541c4d9f2983d Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:35:04 +0000 Subject: [PATCH 1256/1794] amd_iommu: Use iova_tree records to determine large page size on UNMAP Keep a record of mapped IOVA ranges per address space, using the iova_tree implementation. Besides enabling optimizations like avoiding unnecessary notifications, a record of existing mappings makes it possible to determine if a specific IOVA is mapped by the guest using a large page, and adjust the size when notifying UNMAP events. When unmapping a large page, the information in the guest PTE encoding the page size is lost, since the guest clears the PTE before issuing the invalidation command to the IOMMU. In such case, the size of the original mapping can be retrieved from the iova_tree and used to issue the UNMAP notification. Using the correct size is essential since the VFIO IOMMU Type1v2 driver in the host kernel will reject unmap requests that do not fully cover previous mappings. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-12-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 95 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 6 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index caae65c4b3565..4376e977f8886 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -33,6 +33,7 @@ #include "hw/i386/apic-msidef.h" #include "hw/qdev-properties.h" #include "kvm/kvm_i386.h" +#include "qemu/iova-tree.h" /* used AMD-Vi MMIO registers */ const char *amdvi_mmio_low[] = { @@ -71,6 +72,8 @@ struct AMDVIAddressSpace { IOMMUNotifierFlag notifier_flags; /* entry in list of Address spaces with registered notifiers */ QLIST_ENTRY(AMDVIAddressSpace) next; + /* Record DMA translation ranges */ + IOVATree *iova_tree; }; /* AMDVI cache entry */ @@ -685,6 +688,75 @@ static uint64_t fetch_pte(AMDVIAddressSpace *as, hwaddr address, uint64_t dte, return 0; } +/* + * Invoke notifiers registered for the address space. Update record of mapped + * ranges in IOVA Tree. + */ +static void amdvi_notify_iommu(AMDVIAddressSpace *as, IOMMUTLBEvent *event) +{ + IOMMUTLBEntry *entry = &event->entry; + + DMAMap target = { + .iova = entry->iova, + .size = entry->addr_mask, + .translated_addr = entry->translated_addr, + .perm = entry->perm, + }; + + /* + * Search the IOVA Tree for an existing translation for the target, and skip + * the notification if the mapping is already recorded. + * When the guest uses large pages, comparing against the record makes it + * possible to determine the size of the original MAP and adjust the UNMAP + * request to match it. This avoids failed checks against the mappings kept + * by the VFIO kernel driver. + */ + const DMAMap *mapped = iova_tree_find(as->iova_tree, &target); + + if (event->type == IOMMU_NOTIFIER_UNMAP) { + if (!mapped) { + /* No record exists of this mapping, nothing to do */ + return; + } + /* + * Adjust the size based on the original record. This is essential to + * determine when large/contiguous pages are used, since the guest has + * already cleared the PTE (erasing the pagesize encoded on it) before + * issuing the invalidation command. + */ + if (mapped->size != target.size) { + assert(mapped->size > target.size); + target.size = mapped->size; + /* Adjust event to invoke notifier with correct range */ + entry->addr_mask = mapped->size; + } + iova_tree_remove(as->iova_tree, target); + } else { /* IOMMU_NOTIFIER_MAP */ + if (mapped) { + /* + * If a mapping is present and matches the request, skip the + * notification. + */ + if (!memcmp(mapped, &target, sizeof(DMAMap))) { + return; + } else { + /* + * This should never happen unless a buggy guest OS omits or + * sends incorrect invalidation(s). Report an error in the event + * it does happen. + */ + error_report("Found conflicting translation. This could be due " + "to an incorrect or missing invalidation command"); + } + } + /* Record the new mapping */ + iova_tree_insert(as->iova_tree, &target); + } + + /* Invoke the notifiers registered for this address space */ + memory_region_notify_iommu(&as->iommu, 0, *event); +} + /* * Walk the guest page table for an IOVA and range and signal the registered * notifiers to sync the shadow page tables in the host. @@ -696,7 +768,7 @@ static void amdvi_sync_shadow_page_table_range(AMDVIAddressSpace *as, { IOMMUTLBEvent event; - hwaddr iova_next, page_mask, pagesize; + hwaddr page_mask, pagesize; hwaddr iova = addr; hwaddr end = iova + size - 1; @@ -719,7 +791,6 @@ static void amdvi_sync_shadow_page_table_range(AMDVIAddressSpace *as, /* PTE has been validated for major errors and pagesize is set */ assert(pagesize); page_mask = ~(pagesize - 1); - iova_next = (iova & page_mask) + pagesize; if (ret == -AMDVI_FR_PT_ENTRY_INV) { /* @@ -752,15 +823,26 @@ static void amdvi_sync_shadow_page_table_range(AMDVIAddressSpace *as, event.type = IOMMU_NOTIFIER_MAP; } - /* Invoke the notifiers registered for this address space */ - memory_region_notify_iommu(&as->iommu, 0, event); + /* + * The following call might need to adjust event.entry.size in cases + * where the guest unmapped a series of large pages. + */ + amdvi_notify_iommu(as, &event); + /* + * In the special scenario where the guest is unmapping a large page, + * addr_mask has been adjusted before sending the notification. Update + * pagesize accordingly in order to correctly compute the next IOVA. + */ + pagesize = event.entry.addr_mask + 1; next: + iova &= ~(pagesize - 1); + /* Check for 64-bit overflow and terminate walk in such cases */ - if (iova_next < iova) { + if ((iova + pagesize) < iova) { break; } else { - iova = iova_next; + iova += pagesize; } } } @@ -1845,6 +1927,7 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) iommu_as[devfn]->devfn = (uint8_t)devfn; iommu_as[devfn]->iommu_state = s; iommu_as[devfn]->notifier_flags = IOMMU_NOTIFIER_NONE; + iommu_as[devfn]->iova_tree = iova_tree_new(); amdvi_dev_as = iommu_as[devfn]; From 63e39ec114d51c15f2b76709e874bef5bbd413cb Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:35:05 +0000 Subject: [PATCH 1257/1794] amd_iommu: Unmap all address spaces under the AMD IOMMU on reset Support dropping all existing mappings on reset. When the guest kernel reboots it will create new ones, but other components that run before the kernel (e.g. OVMF) should not be able to use existing mappings from the previous boot. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-13-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 74 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 4376e977f8886..497f18c540666 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -847,6 +847,77 @@ static void amdvi_sync_shadow_page_table_range(AMDVIAddressSpace *as, } } +/* + * Unmap entire range that the notifier registered for i.e. the full AS. + * + * This is seemingly technically equivalent to directly calling + * memory_region_unmap_iommu_notifier_range(), but it allows to check for + * notifier boundaries and issue notifications with ranges within those bounds. + */ +static void amdvi_address_space_unmap(AMDVIAddressSpace *as, IOMMUNotifier *n) +{ + + hwaddr start = n->start; + hwaddr end = n->end; + hwaddr remain; + DMAMap map; + + assert(start <= end); + remain = end - start + 1; + + /* + * Divide the notifier range into chunks that are aligned and do not exceed + * the notifier boundaries. + */ + while (remain >= AMDVI_PAGE_SIZE) { + + IOMMUTLBEvent event; + + uint64_t mask = dma_aligned_pow2_mask(start, end, 64); + + event.type = IOMMU_NOTIFIER_UNMAP; + + IOMMUTLBEntry entry = { + .target_as = &address_space_memory, + .iova = start, + .translated_addr = 0, /* irrelevant for unmap case */ + .addr_mask = mask, + .perm = IOMMU_NONE, + }; + event.entry = entry; + + /* Call notifier registered for updates on this address space */ + memory_region_notify_iommu_one(n, &event); + + start += mask + 1; + remain -= mask + 1; + } + + assert(!remain); + + map.iova = n->start; + map.size = n->end - n->start; + + iova_tree_remove(as->iova_tree, map); +} + +/* + * For all the address spaces with notifiers registered, unmap the entire range + * the notifier registered for i.e. clear all the address spaces managed by the + * IOMMU. + */ +static void amdvi_address_space_unmap_all(AMDVIState *s) +{ + AMDVIAddressSpace *as; + IOMMUNotifier *n; + + QLIST_FOREACH(as, &s->amdvi_as_with_notifiers, next) { + IOMMU_NOTIFIER_FOREACH(n, &as->iommu) { + amdvi_address_space_unmap(as, n); + } + } +} + /* log error without aborting since linux seems to be using reserved bits */ static void amdvi_inval_devtab_entry(AMDVIState *s, uint64_t *cmd) { @@ -2099,6 +2170,9 @@ static void amdvi_sysbus_reset(DeviceState *dev) msi_reset(&s->pci->dev); amdvi_init(s); + + /* Discard all mappings on device reset */ + amdvi_address_space_unmap_all(s); } static const VMStateDescription vmstate_amdvi_sysbus_migratable = { From 1221c4a44902ed6162b06b30b7fcc245c39c2006 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:35:06 +0000 Subject: [PATCH 1258/1794] amd_iommu: Add replay callback A replay() method is necessary to efficiently synchronize the host page tables after VFIO registers a notifier for IOMMU events. It is called to ensure that existing mappings from an IOMMU memory region are "replayed" to a specified notifier, initializing or updating the shadow page tables on the host. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-14-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 497f18c540666..9027f7c0544a7 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -918,6 +918,29 @@ static void amdvi_address_space_unmap_all(AMDVIState *s) } } +/* + * For every translation present in the IOMMU, construct IOMMUTLBEntry data + * and pass it as parameter to notifier callback. + */ +static void amdvi_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) +{ + AMDVIAddressSpace *as = container_of(iommu_mr, AMDVIAddressSpace, iommu); + uint64_t dte[4] = { 0 }; + + if (!(n->notifier_flags & IOMMU_NOTIFIER_MAP)) { + return; + } + + if (amdvi_as_to_dte(as, dte)) { + return; + } + + /* Dropping all mappings for the address space. Also clears the IOVA tree */ + amdvi_address_space_unmap(as, n); + + amdvi_sync_shadow_page_table_range(as, &dte[0], 0, UINT64_MAX, false); +} + /* log error without aborting since linux seems to be using reserved bits */ static void amdvi_inval_devtab_entry(AMDVIState *s, uint64_t *cmd) { @@ -2364,6 +2387,7 @@ static void amdvi_iommu_memory_region_class_init(ObjectClass *klass, imrc->translate = amdvi_translate; imrc->notify_flag_changed = amdvi_iommu_notify_flag_changed; + imrc->replay = amdvi_iommu_replay; } static const TypeInfo amdvi_iommu_memory_region_info = { From b6a59a2ebf13df35370440b63ea1b8cf0f06435b Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:35:07 +0000 Subject: [PATCH 1259/1794] amd_iommu: Invalidate address translations on INVALIDATE_IOMMU_ALL When the kernel IOMMU driver issues an INVALIDATE_IOMMU_ALL, the address translation and interrupt remapping information must be cleared for all Device IDs and all domains. Introduce a helper to sync the shadow page table for all the address spaces with registered notifiers, which replays both MAP and UNMAP events. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-15-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 48 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 9027f7c0544a7..d74d42b3dda8e 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -941,6 +941,47 @@ static void amdvi_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) amdvi_sync_shadow_page_table_range(as, &dte[0], 0, UINT64_MAX, false); } +static void amdvi_address_space_sync(AMDVIAddressSpace *as) +{ + IOMMUNotifier *n; + uint64_t dte[4] = { 0 }; + + /* If only UNMAP notifiers are registered, drop all existing mappings */ + if (!(as->notifier_flags & IOMMU_NOTIFIER_MAP)) { + IOMMU_NOTIFIER_FOREACH(n, &as->iommu) { + /* + * Directly calling memory_region_unmap_iommu_notifier_range() does + * not guarantee that the addr_mask eventually passed as parameter + * to the notifier is valid. Use amdvi_address_space_unmap() which + * ensures the notifier range is divided into properly aligned + * regions, and issues notifications for each one. + */ + amdvi_address_space_unmap(as, n); + } + return; + } + + if (amdvi_as_to_dte(as, dte)) { + return; + } + + amdvi_sync_shadow_page_table_range(as, &dte[0], 0, UINT64_MAX, true); +} + +/* + * This differs from the replay() method in that it issues both MAP and UNMAP + * notifications since it is called after global invalidation events in order to + * re-sync all address spaces. + */ +static void amdvi_iommu_address_space_sync_all(AMDVIState *s) +{ + AMDVIAddressSpace *as; + + QLIST_FOREACH(as, &s->amdvi_as_with_notifiers, next) { + amdvi_address_space_sync(as); + } +} + /* log error without aborting since linux seems to be using reserved bits */ static void amdvi_inval_devtab_entry(AMDVIState *s, uint64_t *cmd) { @@ -983,6 +1024,13 @@ static void amdvi_inval_all(AMDVIState *s, uint64_t *cmd) amdvi_intremap_inval_notify_all(s, true, 0, 0); amdvi_iotlb_reset(s); + + /* + * Fully replay the address space i.e. send both UNMAP and MAP events in + * order to synchronize guest and host IO page tables tables. + */ + amdvi_iommu_address_space_sync_all(s); + trace_amdvi_all_inval(); } From cb906e6f69d9ac7de819ad83a0e8a89d836a6bb8 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:35:08 +0000 Subject: [PATCH 1260/1794] amd_iommu: Toggle memory regions based on address translation mode Enable the appropriate memory region for an address space depending on the address translation mode selected for it. This is currently based on a generic x86 IOMMU property, and only done during the address space initialization. Extract the code into a helper and toggle the regions based on whether the specific address space is using address translation (via the newly introduced addr_translation field). Later, region activation will also be controlled by availability of DMA remapping capability (via dma-remap property to be introduced in follow up changes). Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-16-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index d74d42b3dda8e..67a26f524706b 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -74,6 +74,8 @@ struct AMDVIAddressSpace { QLIST_ENTRY(AMDVIAddressSpace) next; /* Record DMA translation ranges */ IOVATree *iova_tree; + /* DMA address translation active */ + bool addr_translation; }; /* AMDVI cache entry */ @@ -982,6 +984,23 @@ static void amdvi_iommu_address_space_sync_all(AMDVIState *s) } } +/* + * Toggle between address translation and passthrough modes by enabling the + * corresponding memory regions. + */ +static void amdvi_switch_address_space(AMDVIAddressSpace *amdvi_as) +{ + if (amdvi_as->addr_translation) { + /* Enabling DMA region */ + memory_region_set_enabled(&amdvi_as->iommu_nodma, false); + memory_region_set_enabled(MEMORY_REGION(&amdvi_as->iommu), true); + } else { + /* Disabling DMA region, using passthrough */ + memory_region_set_enabled(MEMORY_REGION(&amdvi_as->iommu), false); + memory_region_set_enabled(&amdvi_as->iommu_nodma, true); + } +} + /* log error without aborting since linux seems to be using reserved bits */ static void amdvi_inval_devtab_entry(AMDVIState *s, uint64_t *cmd) { @@ -2070,6 +2089,7 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) iommu_as[devfn]->iommu_state = s; iommu_as[devfn]->notifier_flags = IOMMU_NOTIFIER_NONE; iommu_as[devfn]->iova_tree = iova_tree_new(); + iommu_as[devfn]->addr_translation = false; amdvi_dev_as = iommu_as[devfn]; @@ -2112,8 +2132,7 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) AMDVI_INT_ADDR_FIRST, &amdvi_dev_as->iommu_ir, 1); - memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, false); - memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu), true); + amdvi_switch_address_space(amdvi_dev_as); } return &iommu_as[devfn]->as; } From 01f599d39c0e845d1cd0f7521aac7d409a3a6ad7 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:35:09 +0000 Subject: [PATCH 1261/1794] amd_iommu: Set all address spaces to use passthrough mode on reset On reset, restore the default address translation mode (passthrough) for all the address spaces managed by the vIOMMU. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-17-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 67a26f524706b..e9ce7b46e854f 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1001,6 +1001,35 @@ static void amdvi_switch_address_space(AMDVIAddressSpace *amdvi_as) } } +/* + * For all existing address spaces managed by the IOMMU, enable/disable the + * corresponding memory regions to reset the address translation mode and + * use passthrough by default. + */ +static void amdvi_reset_address_translation_all(AMDVIState *s) +{ + AMDVIAddressSpace **iommu_as; + + for (int bus_num = 0; bus_num < PCI_BUS_MAX; bus_num++) { + + /* Nothing to do if there are no devices on the current bus */ + if (!s->address_spaces[bus_num]) { + continue; + } + iommu_as = s->address_spaces[bus_num]; + + for (int devfn = 0; devfn < PCI_DEVFN_MAX; devfn++) { + + if (!iommu_as[devfn]) { + continue; + } + /* Use passthrough as default mode after reset */ + iommu_as[devfn]->addr_translation = false; + amdvi_switch_address_space(iommu_as[devfn]); + } + } +} + /* log error without aborting since linux seems to be using reserved bits */ static void amdvi_inval_devtab_entry(AMDVIState *s, uint64_t *cmd) { @@ -2263,6 +2292,7 @@ static void amdvi_sysbus_reset(DeviceState *dev) /* Discard all mappings on device reset */ amdvi_address_space_unmap_all(s); + amdvi_reset_address_translation_all(s); } static const VMStateDescription vmstate_amdvi_sysbus_migratable = { From 7e24cddf1f7941825e195e3239f0345d81927c14 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:35:10 +0000 Subject: [PATCH 1262/1794] amd_iommu: Add dma-remap property to AMD vIOMMU device In order to enable device assignment with IOMMU protection and guest DMA address translation, IOMMU MAP notifier support is necessary to allow users like VFIO to synchronize the shadow page tables i.e. to receive notifications when the guest updates its I/O page tables and replay the mappings onto host I/O page tables. Provide a new dma-remap property to govern the ability to register for MAP notifications, effectively providing global control over the DMA address translation functionality that was implemented in previous changes. Note that DMA remapping support also requires the vIOMMU is configured with the NpCache capability, so a guest driver issues IOMMU invalidations for both map() and unmap() operations. This capability is already set by default and written to the configuration in amdvi_pci_realize() as part of AMDVI_CAPAB_FEATURES. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-18-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 24 +++++++++++++++++------- hw/i386/amd_iommu.h | 3 +++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index e9ce7b46e854f..ce5d4c36624fd 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -990,7 +990,9 @@ static void amdvi_iommu_address_space_sync_all(AMDVIState *s) */ static void amdvi_switch_address_space(AMDVIAddressSpace *amdvi_as) { - if (amdvi_as->addr_translation) { + AMDVIState *s = amdvi_as->iommu_state; + + if (s->dma_remap && amdvi_as->addr_translation) { /* Enabling DMA region */ memory_region_set_enabled(&amdvi_as->iommu_nodma, false); memory_region_set_enabled(MEMORY_REGION(&amdvi_as->iommu), true); @@ -2193,12 +2195,19 @@ static int amdvi_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, AMDVIAddressSpace *as = container_of(iommu, AMDVIAddressSpace, iommu); AMDVIState *s = as->iommu_state; - if (new & IOMMU_NOTIFIER_MAP) { - error_setg(errp, - "device %02x.%02x.%x requires iommu notifier which is not " - "currently supported", as->bus_num, PCI_SLOT(as->devfn), - PCI_FUNC(as->devfn)); - return -EINVAL; + /* + * Accurate synchronization of the vIOMMU page tables required to support + * MAP notifiers is provided by the dma-remap feature. In addition, this + * also requires that the vIOMMU presents the NpCache capability, so a guest + * driver issues invalidations for both map() and unmap() operations. The + * capability is already set by default as part of AMDVI_CAPAB_FEATURES and + * written to the configuration in amdvi_pci_realize(). + */ + if (!s->dma_remap && (new & IOMMU_NOTIFIER_MAP)) { + error_setg_errno(errp, ENOTSUP, + "device %02x.%02x.%x requires dma-remap=1", + as->bus_num, PCI_SLOT(as->devfn), PCI_FUNC(as->devfn)); + return -ENOTSUP; } /* @@ -2423,6 +2432,7 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) static const Property amdvi_properties[] = { DEFINE_PROP_BOOL("xtsup", AMDVIState, xtsup, false), DEFINE_PROP_STRING("pci-id", AMDVIState, pci_id), + DEFINE_PROP_BOOL("dma-remap", AMDVIState, dma_remap, false), }; static const VMStateDescription vmstate_amdvi_sysbus = { diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index b51aa74368995..e1354686b6f03 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -418,6 +418,9 @@ struct AMDVIState { /* Interrupt remapping */ bool ga_enabled; bool xtsup; + + /* DMA address translation */ + bool dma_remap; }; uint64_t amdvi_extended_feature_register(AMDVIState *s); From a9721c5d80ff23a018f40cc3693678a60630c659 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:35:11 +0000 Subject: [PATCH 1263/1794] amd_iommu: Toggle address translation mode on devtab entry invalidation A guest must issue an INVALIDATE_DEVTAB_ENTRY command after changing a Device Table entry (DTE) e.g. after attaching a device and setting up its DTE. When intercepting this event, determine if the DTE has been configured for paging or not, and toggle the appropriate memory regions to allow DMA address translation for the address space if needed. Requires dma-remap=on. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-19-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 122 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 2 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index ce5d4c36624fd..e916dcb2be381 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1032,18 +1032,136 @@ static void amdvi_reset_address_translation_all(AMDVIState *s) } } +static void enable_dma_mode(AMDVIAddressSpace *as, bool inval_current) +{ + /* + * When enabling DMA mode for the purpose of isolating guest devices on + * a failure to retrieve or invalid DTE, all existing mappings must be + * dropped. + */ + if (inval_current) { + IOMMUNotifier *n; + IOMMU_NOTIFIER_FOREACH(n, &as->iommu) { + amdvi_address_space_unmap(as, n); + } + } + + if (as->addr_translation) { + return; + } + + /* Installing DTE enabling translation, activate region */ + as->addr_translation = true; + amdvi_switch_address_space(as); + /* Sync shadow page tables */ + amdvi_address_space_sync(as); +} + +/* + * If paging was previously in use in the address space + * - invalidate all existing mappings + * - switch to no_dma memory region + */ +static void enable_nodma_mode(AMDVIAddressSpace *as) +{ + IOMMUNotifier *n; + + if (!as->addr_translation) { + /* passthrough is already active, nothing to do */ + return; + } + + as->addr_translation = false; + IOMMU_NOTIFIER_FOREACH(n, &as->iommu) { + /* Drop all mappings for the address space */ + amdvi_address_space_unmap(as, n); + } + amdvi_switch_address_space(as); +} + +/* + * A guest driver must issue the INVALIDATE_DEVTAB_ENTRY command to the IOMMU + * after changing a Device Table entry. We can use this fact to detect when a + * Device Table entry is created for a device attached to a paging domain and + * enable the corresponding IOMMU memory region to allow for DMA translation if + * appropriate. + */ +static void amdvi_update_addr_translation_mode(AMDVIState *s, uint16_t devid) +{ + uint8_t bus_num, devfn, dte_mode; + AMDVIAddressSpace *as; + uint64_t dte[4] = { 0 }; + int ret; + + /* + * Convert the devid encoded in the command to a bus and devfn in + * order to retrieve the corresponding address space. + */ + bus_num = PCI_BUS_NUM(devid); + devfn = devid & 0xff; + + /* + * The main buffer of size (AMDVIAddressSpace *) * (PCI_BUS_MAX) has already + * been allocated within AMDVIState, but must be careful to not access + * unallocated devfn. + */ + if (!s->address_spaces[bus_num] || !s->address_spaces[bus_num][devfn]) { + return; + } + as = s->address_spaces[bus_num][devfn]; + + ret = amdvi_as_to_dte(as, dte); + + if (!ret) { + dte_mode = (dte[0] >> AMDVI_DEV_MODE_RSHIFT) & AMDVI_DEV_MODE_MASK; + } + + switch (ret) { + case 0: + /* DTE was successfully retrieved */ + if (!dte_mode) { + enable_nodma_mode(as); /* DTE[V]=1 && DTE[Mode]=0 => passthrough */ + } else { + enable_dma_mode(as, false); /* Enable DMA translation */ + } + break; + case -AMDVI_FR_DTE_V: + /* DTE[V]=0, address is passed untranslated */ + enable_nodma_mode(as); + break; + case -AMDVI_FR_DTE_RTR_ERR: + case -AMDVI_FR_DTE_TV: + /* + * Enforce isolation by using DMA in rare scenarios where the DTE cannot + * be retrieved or DTE[TV]=0. Existing mappings are dropped. + */ + enable_dma_mode(as, true); + break; + } +} + /* log error without aborting since linux seems to be using reserved bits */ static void amdvi_inval_devtab_entry(AMDVIState *s, uint64_t *cmd) { uint16_t devid = cpu_to_le16((uint16_t)extract64(cmd[0], 0, 16)); + trace_amdvi_devtab_inval(PCI_BUS_NUM(devid), PCI_SLOT(devid), + PCI_FUNC(devid)); + /* This command should invalidate internal caches of which there isn't */ if (extract64(cmd[0], 16, 44) || cmd[1]) { amdvi_log_illegalcom_error(s, extract64(cmd[0], 60, 4), s->cmdbuf + s->cmdbuf_head); + return; + } + + /* + * When DMA remapping capability is enabled, check if updated DTE is setup + * for paging or not, and configure the corresponding memory regions. + */ + if (s->dma_remap) { + amdvi_update_addr_translation_mode(s, devid); } - trace_amdvi_devtab_inval(PCI_BUS_NUM(devid), PCI_SLOT(devid), - PCI_FUNC(devid)); } static void amdvi_complete_ppr(AMDVIState *s, uint64_t *cmd) From f6b5644c90847a9a7b9d2926556e1e71c4d1c797 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:35:12 +0000 Subject: [PATCH 1264/1794] amd_iommu: Do not assume passthrough translation when DTE[TV]=0 The AMD I/O Virtualization Technology (IOMMU) Specification (see Table 8: V, TV, and GV Fields in Device Table Entry), specifies that a DTE with V=1, TV=0 does not contain a valid address translation information. If a request requires a table walk, the walk is terminated when this condition is encountered. Do not assume that addresses for a device with DTE[TV]=0 are passed through (i.e. not remapped) and instead terminate the page table walk early. Fixes: d29a09ca6842 ("hw/i386: Introduce AMD IOMMU") Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-20-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 87 +++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index e916dcb2be381..1bda2a8ac3a16 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1722,51 +1722,60 @@ static void amdvi_page_walk(AMDVIAddressSpace *as, uint64_t *dte, uint64_t pte = dte[0], pte_addr, page_mask; /* make sure the DTE has TV = 1 */ - if (pte & AMDVI_DEV_TRANSLATION_VALID) { - level = get_pte_translation_mode(pte); - if (level >= 7) { - trace_amdvi_mode_invalid(level, addr); + if (!(pte & AMDVI_DEV_TRANSLATION_VALID)) { + /* + * A DTE with V=1, TV=0 does not have a valid Page Table Root Pointer. + * An IOMMU processing a request that requires a table walk terminates + * the walk when it encounters this condition. Do the same and return + * instead of assuming that the address is forwarded without translation + * i.e. the passthrough case, as it is done for the case where DTE[V]=0. + */ + return; + } + + level = get_pte_translation_mode(pte); + if (level >= 7) { + trace_amdvi_mode_invalid(level, addr); + return; + } + if (level == 0) { + goto no_remap; + } + + /* we are at the leaf page table or page table encodes a huge page */ + do { + pte_perms = amdvi_get_perms(pte); + present = pte & 1; + if (!present || perms != (perms & pte_perms)) { + amdvi_page_fault(as->iommu_state, as->devfn, addr, perms); + trace_amdvi_page_fault(addr); return; } - if (level == 0) { - goto no_remap; + /* go to the next lower level */ + pte_addr = pte & AMDVI_DEV_PT_ROOT_MASK; + /* add offset and load pte */ + pte_addr += ((addr >> (3 + 9 * level)) & 0x1FF) << 3; + pte = amdvi_get_pte_entry(as->iommu_state, pte_addr, as->devfn); + if (!pte) { + return; } + oldlevel = level; + level = get_pte_translation_mode(pte); + } while (level > 0 && level < 7); - /* we are at the leaf page table or page table encodes a huge page */ - do { - pte_perms = amdvi_get_perms(pte); - present = pte & 1; - if (!present || perms != (perms & pte_perms)) { - amdvi_page_fault(as->iommu_state, as->devfn, addr, perms); - trace_amdvi_page_fault(addr); - return; - } - - /* go to the next lower level */ - pte_addr = pte & AMDVI_DEV_PT_ROOT_MASK; - /* add offset and load pte */ - pte_addr += ((addr >> (3 + 9 * level)) & 0x1FF) << 3; - pte = amdvi_get_pte_entry(as->iommu_state, pte_addr, as->devfn); - if (!pte || (pte == (uint64_t)-1)) { - return; - } - oldlevel = level; - level = get_pte_translation_mode(pte); - } while (level > 0 && level < 7); + if (level == 0x7) { + page_mask = pte_override_page_mask(pte); + } else { + page_mask = pte_get_page_mask(oldlevel); + } - if (level == 0x7) { - page_mask = pte_override_page_mask(pte); - } else { - page_mask = pte_get_page_mask(oldlevel); - } + /* get access permissions from pte */ + ret->iova = addr & page_mask; + ret->translated_addr = (pte & AMDVI_DEV_PT_ROOT_MASK) & page_mask; + ret->addr_mask = ~page_mask; + ret->perm = amdvi_get_perms(pte); + return; - /* get access permissions from pte */ - ret->iova = addr & page_mask; - ret->translated_addr = (pte & AMDVI_DEV_PT_ROOT_MASK) & page_mask; - ret->addr_mask = ~page_mask; - ret->perm = amdvi_get_perms(pte); - return; - } no_remap: ret->iova = addr & AMDVI_PAGE_MASK_4K; ret->translated_addr = addr & AMDVI_PAGE_MASK_4K; From de83472bee07aea583b777dce4666faa677d1c18 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Fri, 19 Sep 2025 21:35:13 +0000 Subject: [PATCH 1265/1794] amd_iommu: Refactor amdvi_page_walk() to use common code for page walk Simplify amdvi_page_walk() by making it call the fetch_pte() helper that is already in use by the shadow page synchronization code. Ensures all code uses the same page table walking algorithm. Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-21-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 77 ++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 50 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 1bda2a8ac3a16..b6851784fb9f1 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -513,24 +513,6 @@ static inline uint8_t get_pte_translation_mode(uint64_t pte) return (pte >> AMDVI_DEV_MODE_RSHIFT) & AMDVI_DEV_MODE_MASK; } -static inline uint64_t pte_override_page_mask(uint64_t pte) -{ - uint8_t page_mask = 13; - uint64_t addr = (pte & AMDVI_DEV_PT_ROOT_MASK) >> 12; - /* find the first zero bit */ - while (addr & 1) { - page_mask++; - addr = addr >> 1; - } - - return ~((1ULL << page_mask) - 1); -} - -static inline uint64_t pte_get_page_mask(uint64_t oldlevel) -{ - return ~((1UL << ((oldlevel * 9) + 3)) - 1); -} - static inline uint64_t amdvi_get_pte_entry(AMDVIState *s, uint64_t pte_addr, uint16_t devid) { @@ -1718,11 +1700,13 @@ static void amdvi_page_walk(AMDVIAddressSpace *as, uint64_t *dte, IOMMUTLBEntry *ret, unsigned perms, hwaddr addr) { - unsigned level, present, pte_perms, oldlevel; - uint64_t pte = dte[0], pte_addr, page_mask; + hwaddr page_mask, pagesize = 0; + uint8_t mode; + uint64_t pte; + int fetch_ret; /* make sure the DTE has TV = 1 */ - if (!(pte & AMDVI_DEV_TRANSLATION_VALID)) { + if (!(dte[0] & AMDVI_DEV_TRANSLATION_VALID)) { /* * A DTE with V=1, TV=0 does not have a valid Page Table Root Pointer. * An IOMMU processing a request that requires a table walk terminates @@ -1733,42 +1717,35 @@ static void amdvi_page_walk(AMDVIAddressSpace *as, uint64_t *dte, return; } - level = get_pte_translation_mode(pte); - if (level >= 7) { - trace_amdvi_mode_invalid(level, addr); + mode = get_pte_translation_mode(dte[0]); + if (mode >= 7) { + trace_amdvi_mode_invalid(mode, addr); return; } - if (level == 0) { + if (mode == 0) { goto no_remap; } - /* we are at the leaf page table or page table encodes a huge page */ - do { - pte_perms = amdvi_get_perms(pte); - present = pte & 1; - if (!present || perms != (perms & pte_perms)) { - amdvi_page_fault(as->iommu_state, as->devfn, addr, perms); - trace_amdvi_page_fault(addr); - return; - } - /* go to the next lower level */ - pte_addr = pte & AMDVI_DEV_PT_ROOT_MASK; - /* add offset and load pte */ - pte_addr += ((addr >> (3 + 9 * level)) & 0x1FF) << 3; - pte = amdvi_get_pte_entry(as->iommu_state, pte_addr, as->devfn); - if (!pte) { - return; - } - oldlevel = level; - level = get_pte_translation_mode(pte); - } while (level > 0 && level < 7); + /* Attempt to fetch the PTE to determine if a valid mapping exists */ + fetch_ret = fetch_pte(as, addr, dte[0], &pte, &pagesize); - if (level == 0x7) { - page_mask = pte_override_page_mask(pte); - } else { - page_mask = pte_get_page_mask(oldlevel); + /* + * If walking the page table results in an error of any type, returns an + * empty PTE i.e. no mapping, or the permissions do not match, return since + * there is no translation available. + */ + if (fetch_ret < 0 || !IOMMU_PTE_PRESENT(pte) || + perms != (perms & amdvi_get_perms(pte))) { + + amdvi_page_fault(as->iommu_state, as->devfn, addr, perms); + trace_amdvi_page_fault(addr); + return; } + /* A valid PTE and page size has been retrieved */ + assert(pagesize); + page_mask = ~(pagesize - 1); + /* get access permissions from pte */ ret->iova = addr & page_mask; ret->translated_addr = (pte & AMDVI_DEV_PT_ROOT_MASK) & page_mask; @@ -1780,7 +1757,7 @@ static void amdvi_page_walk(AMDVIAddressSpace *as, uint64_t *dte, ret->iova = addr & AMDVI_PAGE_MASK_4K; ret->translated_addr = addr & AMDVI_PAGE_MASK_4K; ret->addr_mask = ~AMDVI_PAGE_MASK_4K; - ret->perm = amdvi_get_perms(pte); + ret->perm = amdvi_get_perms(dte[0]); } static void amdvi_do_translate(AMDVIAddressSpace *as, hwaddr addr, From b6b49c2cd6c2be8aac80f7563950398c7128f19e Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Fri, 19 Sep 2025 21:35:14 +0000 Subject: [PATCH 1266/1794] intel-iommu: Move dma_translation to x86-iommu To be later reused by AMD, now that it shares similar property. Signed-off-by: Joao Martins Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-22-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 5 ++--- hw/i386/x86-iommu.c | 1 + include/hw/i386/x86-iommu.h | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 2cc9bd5e45d3e..fa2ad9c2eb01d 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -2704,7 +2704,7 @@ static void vtd_handle_gcmd_write(IntelIOMMUState *s) uint32_t changed = status ^ val; trace_vtd_reg_write_gcmd(status, val); - if ((changed & VTD_GCMD_TE) && s->dma_translation) { + if ((changed & VTD_GCMD_TE) && x86_iommu->dma_translation) { /* Translation enable/disable */ vtd_handle_gcmd_te(s, val & VTD_GCMD_TE); } @@ -3947,7 +3947,6 @@ static const Property vtd_properties[] = { DEFINE_PROP_BOOL("snoop-control", IntelIOMMUState, snoop_control, false), DEFINE_PROP_BOOL("x-pasid-mode", IntelIOMMUState, pasid, false), DEFINE_PROP_BOOL("dma-drain", IntelIOMMUState, dma_drain, true), - DEFINE_PROP_BOOL("dma-translation", IntelIOMMUState, dma_translation, true), DEFINE_PROP_BOOL("stale-tm", IntelIOMMUState, stale_tm, false), DEFINE_PROP_BOOL("fs1gp", IntelIOMMUState, fs1gp, true), }; @@ -4665,7 +4664,7 @@ static void vtd_cap_init(IntelIOMMUState *s) if (s->dma_drain) { s->cap |= VTD_CAP_DRAIN; } - if (s->dma_translation) { + if (x86_iommu->dma_translation) { if (s->aw_bits >= VTD_HOST_AW_39BIT) { s->cap |= VTD_CAP_SAGAW_39bit; } diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c index d34a6849f4ae9..c127a44bb4bc8 100644 --- a/hw/i386/x86-iommu.c +++ b/hw/i386/x86-iommu.c @@ -130,6 +130,7 @@ static const Property x86_iommu_properties[] = { intr_supported, ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("device-iotlb", X86IOMMUState, dt_supported, false), DEFINE_PROP_BOOL("pt", X86IOMMUState, pt_supported, true), + DEFINE_PROP_BOOL("dma-translation", X86IOMMUState, dma_translation, true), }; static void x86_iommu_class_init(ObjectClass *klass, const void *data) diff --git a/include/hw/i386/x86-iommu.h b/include/hw/i386/x86-iommu.h index bfd21649d0838..e89f55a5c215c 100644 --- a/include/hw/i386/x86-iommu.h +++ b/include/hw/i386/x86-iommu.h @@ -64,6 +64,7 @@ struct X86IOMMUState { OnOffAuto intr_supported; /* Whether vIOMMU supports IR */ bool dt_supported; /* Whether vIOMMU supports DT */ bool pt_supported; /* Whether vIOMMU supports pass-through */ + bool dma_translation; /* Whether vIOMMU supports DMA translation */ QLIST_HEAD(, IEC_Notifier) iec_notifiers; /* IEC notify list */ }; From d9ec848fb515f4c98f5737ad1f788ef06887fc2c Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Fri, 19 Sep 2025 21:35:15 +0000 Subject: [PATCH 1267/1794] amd_iommu: HATDis/HATS=11 support Add a way to disable DMA translation support in AMD IOMMU by allowing to set IVHD HATDis to 1, and exposing HATS (Host Address Translation Size) as Reserved value. Signed-off-by: Joao Martins Signed-off-by: Alejandro Jimenez Reviewed-by: Michael S. Tsirkin Message-ID: <20250919213515.917111-23-alejandro.j.jimenez@oracle.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 6 +++++- hw/i386/amd_iommu.c | 19 +++++++++++++++++++ hw/i386/amd_iommu.h | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 423c4959fe809..9446a9f862ca4 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1863,7 +1863,11 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker, const char *oem_id, /* IOMMU info */ build_append_int_noprefix(table_data, 0, 2); /* IOMMU Attributes */ - build_append_int_noprefix(table_data, 0, 4); + if (!s->iommu.dma_translation) { + build_append_int_noprefix(table_data, (1UL << 0) /* HATDis */, 4); + } else { + build_append_int_noprefix(table_data, 0, 4); + } /* EFR Register Image */ build_append_int_noprefix(table_data, amdvi_extended_feature_register(s), diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index b6851784fb9f1..378e0cb55eab6 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -107,6 +107,9 @@ uint64_t amdvi_extended_feature_register(AMDVIState *s) if (s->xtsup) { feature |= AMDVI_FEATURE_XT; } + if (!s->iommu.dma_translation) { + feature |= AMDVI_HATS_MODE_RESERVED; + } return feature; } @@ -472,6 +475,9 @@ static inline uint64_t amdvi_get_perms(uint64_t entry) static bool amdvi_validate_dte(AMDVIState *s, uint16_t devid, uint64_t *dte) { + + uint64_t root; + if ((dte[0] & AMDVI_DTE_QUAD0_RESERVED) || (dte[1] & AMDVI_DTE_QUAD1_RESERVED) || (dte[2] & AMDVI_DTE_QUAD2_RESERVED) || @@ -482,6 +488,19 @@ static bool amdvi_validate_dte(AMDVIState *s, uint16_t devid, return false; } + /* + * 1 = Host Address Translation is not supported. Value in MMIO Offset + * 0030h[HATS] is not meaningful. A non-zero host page table root pointer + * in the DTE would result in an ILLEGAL_DEV_TABLE_ENTRY event. + */ + root = (dte[0] & AMDVI_DEV_PT_ROOT_MASK) >> 12; + if (root && !s->iommu.dma_translation) { + amdvi_log_illegaldevtab_error(s, devid, + s->devtab + + devid * AMDVI_DEVTAB_ENTRY_SIZE, 0); + return false; + } + return true; } diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index e1354686b6f03..daf82fc85f961 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -177,6 +177,7 @@ /* AMDVI paging mode */ #define AMDVI_GATS_MODE (2ULL << 12) #define AMDVI_HATS_MODE (2ULL << 10) +#define AMDVI_HATS_MODE_RESERVED (3ULL << 10) /* Page Table format */ From 221e59e3835c71bd3a09d05a39cd192e772aaec3 Mon Sep 17 00:00:00 2001 From: Li Zhaoxin Date: Fri, 26 Sep 2025 19:08:17 +0800 Subject: [PATCH 1268/1794] vdpa-dev: add get_vhost() callback for vhost-vdpa device Commit c255488d67 "virtio: add vhost support for virtio devices" added the get_vhost() function, but it did not include vhost-vdpa devices. So when I use the vdpa device and query the status of the vdpa device with the x-query-virtio-status qmp command, since vdpa does not implement vhost_get, it will cause qemu to crash. Therefore, in order to obtain the status of the virtio device under vhost-vdpa, we need to add a vhost_get implement for the vdpa device. Co-developed-by: Miao Kezhan Signed-off-by: Miao Kezhan Signed-off-by: Li Zhaoxin Reviewed-by: Michael S. Tsirkin Message-ID: <2778f817cb6740a15ecb37927804a67288b062d1.1758860411.git.lizhaoxin04@baidu.com> Signed-off-by: Michael S. Tsirkin --- hw/virtio/vdpa-dev.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c index d1da40afc8016..4a7b970976165 100644 --- a/hw/virtio/vdpa-dev.c +++ b/hw/virtio/vdpa-dev.c @@ -338,6 +338,12 @@ static int vhost_vdpa_device_set_status(VirtIODevice *vdev, uint8_t status) return 0; } +static struct vhost_dev *vhost_vdpa_device_get_vhost(VirtIODevice *vdev) +{ + VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); + return &s->dev; +} + static const Property vhost_vdpa_device_properties[] = { DEFINE_PROP_STRING("vhostdev", VhostVdpaDevice, vhostdev), DEFINE_PROP_UINT16("queue-size", VhostVdpaDevice, queue_size, 0), @@ -369,6 +375,7 @@ static void vhost_vdpa_device_class_init(ObjectClass *klass, const void *data) vdc->set_config = vhost_vdpa_device_set_config; vdc->get_features = vhost_vdpa_device_get_features; vdc->set_status = vhost_vdpa_device_set_status; + vdc->get_vhost = vhost_vdpa_device_get_vhost; } static void vhost_vdpa_device_instance_init(Object *obj) From 0305eceecdf100b98e4f230bf5a821621815a026 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Sun, 28 Sep 2025 23:42:04 -0400 Subject: [PATCH 1269/1794] intel_iommu: Enable Enhanced Set Root Table Pointer Support (ESRTPS) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to VTD spec rev 4.1 section 6.6: "For implementations reporting the Enhanced Set Root Table Pointer Support (ESRTPS) field as Clear, on a 'Set Root Table Pointer' operation, software must perform a global invalidate of the context cache, PASID-cache (if applicable), and IOTLB, in that order. This is required to ensure hardware references only the remapping structures referenced by the new root table pointer and not stale cached entries. For implementations reporting the Enhanced Set Root Table Pointer Support (ESRTPS) field as Set, as part of 'Set Root Table Pointer' operation, hardware performs global invalidation on all DMA remapping translation caches and hence software is not required to perform additional invalidations" We already implemented ESRTPS capability in vtd_handle_gcmd_srtp() by calling vtd_reset_caches(), just set ESRTPS in DMAR_CAP_REG to avoid unnecessary global invalidation requests of context, PASID-cache and IOTLB from guest. This change doesn't impact migration as the content of DMAR_CAP_REG is migrated too. Signed-off-by: Zhenzhong Duan Reviewed-by: Clément Mathieu--Drif Reviewed-by: Michael S. Tsirkin Message-ID: <20250929034206.439266-2-zhenzhong.duan@intel.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 2 +- hw/i386/intel_iommu_internal.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index fa2ad9c2eb01d..0378551038712 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -4660,7 +4660,7 @@ static void vtd_cap_init(IntelIOMMUState *s) s->cap = VTD_CAP_FRO | VTD_CAP_NFR | VTD_CAP_ND | VTD_CAP_MAMV | VTD_CAP_PSI | VTD_CAP_SLLPS | - VTD_CAP_MGAW(s->aw_bits); + VTD_CAP_ESRTPS | VTD_CAP_MGAW(s->aw_bits); if (s->dma_drain) { s->cap |= VTD_CAP_DRAIN; } diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 0d0069a612533..0f6a1237e4cf8 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -215,6 +215,7 @@ #define VTD_CAP_DRAIN_WRITE (1ULL << 54) #define VTD_CAP_DRAIN_READ (1ULL << 55) #define VTD_CAP_FS1GP (1ULL << 56) +#define VTD_CAP_ESRTPS (1ULL << 63) #define VTD_CAP_DRAIN (VTD_CAP_DRAIN_READ | VTD_CAP_DRAIN_WRITE) #define VTD_CAP_CM (1ULL << 7) #define VTD_PASID_ID_SHIFT 20 From 3cda33e06e72412512c0f376a5261e0ff6ebe73f Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Sun, 28 Sep 2025 23:42:05 -0400 Subject: [PATCH 1270/1794] intel_iommu: Simplify caching mode check with VFIO device In early days, we had different tricks to ensure caching-mode=on with VFIO device: 28cf553afe ("intel_iommu: Sanity check vfio-pci config on machine init done") c6cbc29d36 ("pc/q35: Disallow vfio-pci hotplug without VT-d caching mode") There is also a patch with the same purpose but for VDPA device: b8d78277c0 ("intel-iommu: fail MAP notifier without caching mode") Because without caching mode, MAP notifier won't work correctly since guest won't send IOTLB update event when it establishes new mappings in the I/O page tables. Now with host IOMMU device interface between VFIO and vIOMMU, we can simplify first two commits above with a small check in set_iommu_device(). This also works for future IOMMUFD backed VDPA implementation which may also need caching mode on. But for legacy VDPA we still need commit b8d78277c0 as it doesn't use the host IOMMU device interface. For coldplug VFIO device: qemu-system-x86_64: -device vfio-pci,host=0000:3b:00.0,id=hostdev3,bus=root0,iommufd=iommufd0: vfio 0000:3b:00.0: Failed to set vIOMMU: Device assignment is not allowed without enabling caching-mode=on for Intel IOMMU. For hotplug VFIO device: if "iommu=off" is configured in guest, Error: vfio 0000:3b:00.0: Failed to set vIOMMU: Device assignment is not allowed without enabling caching-mode=on for Intel IOMMU. else Error: vfio 0000:3b:00.0: memory listener initialization failed: Region vtd-00.0-dmar: device 01.00.0 requires caching mode: Operation not supported The specialty for hotplug is due to the check in commit b8d78277c0 happen before the check in set_iommu_device. Signed-off-by: Zhenzhong Duan Reviewed-by: Michael S. Tsirkin Message-ID: <20250929034206.439266-3-zhenzhong.duan@intel.com> Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 40 ++++++---------------------------------- hw/i386/pc.c | 20 -------------------- 2 files changed, 6 insertions(+), 54 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 0378551038712..6a168d5107725 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -87,13 +87,6 @@ struct vtd_iotlb_key { static void vtd_address_space_refresh_all(IntelIOMMUState *s); static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n); -static void vtd_panic_require_caching_mode(void) -{ - error_report("We need to set caching-mode=on for intel-iommu to enable " - "device assignment with IOMMU protection."); - exit(1); -} - static void vtd_define_quad(IntelIOMMUState *s, hwaddr addr, uint64_t val, uint64_t wmask, uint64_t w1cmask) { @@ -4489,6 +4482,12 @@ static bool vtd_dev_set_iommu_device(PCIBus *bus, void *opaque, int devfn, assert(hiod); + if (!s->caching_mode) { + error_setg(errp, "Device assignment is not allowed without enabling " + "caching-mode=on for Intel IOMMU."); + return false; + } + vtd_iommu_lock(s); if (g_hash_table_lookup(s->vtd_host_iommu_dev, &key)) { @@ -5244,32 +5243,6 @@ static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) return true; } -static int vtd_machine_done_notify_one(Object *child, void *unused) -{ - IntelIOMMUState *iommu = INTEL_IOMMU_DEVICE(x86_iommu_get_default()); - - /* - * We hard-coded here because vfio-pci is the only special case - * here. Let's be more elegant in the future when we can, but so - * far there seems to be no better way. - */ - if (object_dynamic_cast(child, "vfio-pci") && !iommu->caching_mode) { - vtd_panic_require_caching_mode(); - } - - return 0; -} - -static void vtd_machine_done_hook(Notifier *notifier, void *unused) -{ - object_child_foreach_recursive(object_get_root(), - vtd_machine_done_notify_one, NULL); -} - -static Notifier vtd_machine_done_notify = { - .notify = vtd_machine_done_hook, -}; - static void vtd_realize(DeviceState *dev, Error **errp) { MachineState *ms = MACHINE(qdev_get_machine()); @@ -5324,7 +5297,6 @@ static void vtd_realize(DeviceState *dev, Error **errp) pci_setup_iommu(bus, &vtd_iommu_ops, dev); /* Pseudo address space under root PCI bus. */ x86ms->ioapic_as = vtd_host_dma_iommu(bus, s, Q35_PSEUDO_DEVFN_IOAPIC); - qemu_add_machine_init_done_notifier(&vtd_machine_done_notify); } static void vtd_class_init(ObjectClass *klass, const void *data) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index eb36d50589672..34b00663f246e 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1722,25 +1722,6 @@ static void pc_machine_wakeup(MachineState *machine) cpu_synchronize_all_post_reset(); } -static bool pc_hotplug_allowed(MachineState *ms, DeviceState *dev, Error **errp) -{ - X86IOMMUState *iommu = x86_iommu_get_default(); - IntelIOMMUState *intel_iommu; - - if (iommu && - object_dynamic_cast((Object *)iommu, TYPE_INTEL_IOMMU_DEVICE) && - object_dynamic_cast((Object *)dev, "vfio-pci")) { - intel_iommu = INTEL_IOMMU_DEVICE(iommu); - if (!intel_iommu->caching_mode) { - error_setg(errp, "Device assignment is not allowed without " - "enabling caching-mode=on for Intel IOMMU."); - return false; - } - } - - return true; -} - static void pc_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1760,7 +1741,6 @@ static void pc_machine_class_init(ObjectClass *oc, const void *data) x86mc->apic_xrupt_override = true; assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = pc_get_hotplug_handler; - mc->hotplug_allowed = pc_hotplug_allowed; mc->auto_enable_numa_with_memhp = true; mc->auto_enable_numa_with_memdev = true; mc->has_hotpluggable_cpus = true; From 5c1ded52ac04ee818f642766e3f3b69c78fa160f Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Sun, 28 Sep 2025 23:42:06 -0400 Subject: [PATCH 1271/1794] pci: Fix wrong parameter passing to pci_device_get_iommu_bus_devfn() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 2nd parameter of pci_device_get_iommu_bus_devfn() about root PCIBus backed by an IOMMU for the PCI device, the 3rd is about aliased PCIBus of the PCI device. Meanwhile the 3rd and 4th parameters are optional, pass NULL if they are not needed. Reviewed-by: Clément Mathieu--Drif Reviewed-by: Michael S. Tsirkin Message-ID: <20250929034206.439266-4-zhenzhong.duan@intel.com> Fixes: a849ff5d6f ("pci: Add a pci-level initialization function for IOMMU notifiers") Fixes: f0f37daf8e ("pci: Add a PCI-level API for PRI") Fixes: e9b457500a ("pci: Add a pci-level API for ATS") Fixes: 042cbc9aec ("pci: Add an API to get IOMMU's min page size and virtual address width") Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 4854d3d6185d8..541e03b38c1f4 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2964,7 +2964,7 @@ int pci_iommu_init_iotlb_notifier(PCIDevice *dev, IOMMUNotifier *n, PCIBus *iommu_bus; int devfn; - pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + pci_device_get_iommu_bus_devfn(dev, &iommu_bus, &bus, &devfn); if (iommu_bus && iommu_bus->iommu_ops->init_iotlb_notifier) { iommu_bus->iommu_ops->init_iotlb_notifier(bus, iommu_bus->iommu_opaque, devfn, n, fn, opaque); @@ -3022,7 +3022,7 @@ int pci_pri_request_page(PCIDevice *dev, uint32_t pasid, bool priv_req, return -EPERM; } - pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + pci_device_get_iommu_bus_devfn(dev, &iommu_bus, &bus, &devfn); if (iommu_bus && iommu_bus->iommu_ops->pri_request_page) { return iommu_bus->iommu_ops->pri_request_page(bus, iommu_bus->iommu_opaque, @@ -3046,7 +3046,7 @@ int pci_pri_register_notifier(PCIDevice *dev, uint32_t pasid, return -EPERM; } - pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + pci_device_get_iommu_bus_devfn(dev, &iommu_bus, &bus, &devfn); if (iommu_bus && iommu_bus->iommu_ops->pri_register_notifier) { iommu_bus->iommu_ops->pri_register_notifier(bus, iommu_bus->iommu_opaque, @@ -3063,7 +3063,7 @@ void pci_pri_unregister_notifier(PCIDevice *dev, uint32_t pasid) PCIBus *iommu_bus; int devfn; - pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + pci_device_get_iommu_bus_devfn(dev, &iommu_bus, &bus, &devfn); if (iommu_bus && iommu_bus->iommu_ops->pri_unregister_notifier) { iommu_bus->iommu_ops->pri_unregister_notifier(bus, iommu_bus->iommu_opaque, @@ -3095,7 +3095,7 @@ ssize_t pci_ats_request_translation(PCIDevice *dev, uint32_t pasid, return -EPERM; } - pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + pci_device_get_iommu_bus_devfn(dev, &iommu_bus, &bus, &devfn); if (iommu_bus && iommu_bus->iommu_ops->ats_request_translation) { return iommu_bus->iommu_ops->ats_request_translation(bus, iommu_bus->iommu_opaque, @@ -3119,7 +3119,7 @@ int pci_iommu_register_iotlb_notifier(PCIDevice *dev, uint32_t pasid, return -EPERM; } - pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + pci_device_get_iommu_bus_devfn(dev, &iommu_bus, &bus, &devfn); if (iommu_bus && iommu_bus->iommu_ops->register_iotlb_notifier) { iommu_bus->iommu_ops->register_iotlb_notifier(bus, iommu_bus->iommu_opaque, devfn, @@ -3141,7 +3141,7 @@ int pci_iommu_unregister_iotlb_notifier(PCIDevice *dev, uint32_t pasid, return -EPERM; } - pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + pci_device_get_iommu_bus_devfn(dev, &iommu_bus, &bus, &devfn); if (iommu_bus && iommu_bus->iommu_ops->unregister_iotlb_notifier) { iommu_bus->iommu_ops->unregister_iotlb_notifier(bus, iommu_bus->iommu_opaque, @@ -3155,11 +3155,9 @@ int pci_iommu_unregister_iotlb_notifier(PCIDevice *dev, uint32_t pasid, int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width, uint32_t *min_page_size) { - PCIBus *bus; PCIBus *iommu_bus; - int devfn; - pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + pci_device_get_iommu_bus_devfn(dev, &iommu_bus, NULL, NULL); if (iommu_bus && iommu_bus->iommu_ops->get_iotlb_info) { iommu_bus->iommu_ops->get_iotlb_info(iommu_bus->iommu_opaque, addr_width, min_page_size); From e209d4d7a31b9f82925a2205e6e19e61a3facbe0 Mon Sep 17 00:00:00 2001 From: Alessandro Ratti Date: Wed, 24 Sep 2025 11:14:04 +0200 Subject: [PATCH 1272/1794] virtio: improve virtqueue mapping error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve error reporting when virtqueue ring mapping fails by including a device identifier in the error message. Introduce a helper qdev_get_printable_name() in qdev-core, which returns either: - the device ID, if explicitly provided (e.g. -device ...,id=foo) - the QOM path from qdev_get_dev_path(dev) otherwise - "" as a fallback when no identifier is present This makes it easier to identify which device triggered the error in multi-device setups or when debugging complex guest configurations. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/230 Buglink: https://bugs.launchpad.net/qemu/+bug/1919021 Suggested-by: Markus Armbruster Signed-off-by: Alessandro Ratti Message-Id: <20250924093138.559872-2-alessandro@0x65c.net> Reviewed-by: Daniel P. Berrangé Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/core/qdev.c | 29 +++++++++++++++++++++++++++++ hw/virtio/virtio.c | 15 ++++++++++++--- include/hw/qdev-core.h | 1 + 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index f600226176871..fab42a727059b 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -411,6 +411,35 @@ char *qdev_get_dev_path(DeviceState *dev) return NULL; } +const char *qdev_get_printable_name(DeviceState *vdev) +{ + /* + * Return device ID if explicity set + * (e.g. -device virtio-blk-pci,id=foo) + * This allows users to correlate errors with their custom device + * names. + */ + if (vdev->id) { + return vdev->id; + } + /* + * Fall back to the canonical QOM device path (eg. ID for PCI + * devices). + * This ensures the device is still uniquely and meaningfully + * identified. + */ + const char *path = qdev_get_dev_path(vdev); + if (path) { + return path; + } + + /* + * Final fallback: if all else fails, return a placeholder string. + * This ensures the error message always contains a valid string. + */ + return ""; +} + void qdev_add_unplug_blocker(DeviceState *dev, Error *reason) { dev->unplug_blockers = g_slist_prepend(dev->unplug_blockers, reason); diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index de89e8104a629..d38aafe5ebcf3 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -257,7 +257,10 @@ void virtio_init_region_cache(VirtIODevice *vdev, int n) len = address_space_cache_init(&new->desc, vdev->dma_as, addr, size, packed); if (len < size) { - virtio_error(vdev, "Cannot map desc"); + virtio_error(vdev, + "Failed to map descriptor ring for device %s: " + "invalid guest physical address or corrupted queue setup", + qdev_get_printable_name(DEVICE(vdev))); goto err_desc; } @@ -265,7 +268,10 @@ void virtio_init_region_cache(VirtIODevice *vdev, int n) len = address_space_cache_init(&new->used, vdev->dma_as, vq->vring.used, size, true); if (len < size) { - virtio_error(vdev, "Cannot map used"); + virtio_error(vdev, + "Failed to map used ring for device %s: " + "possible guest misconfiguration or insufficient memory", + qdev_get_printable_name(DEVICE(vdev))); goto err_used; } @@ -273,7 +279,10 @@ void virtio_init_region_cache(VirtIODevice *vdev, int n) len = address_space_cache_init(&new->avail, vdev->dma_as, vq->vring.avail, size, false); if (len < size) { - virtio_error(vdev, "Cannot map avail"); + virtio_error(vdev, + "Failed to map avalaible ring for device %s: " + "possible queue misconfiguration or overlapping memory region", + qdev_get_printable_name(DEVICE(vdev))); goto err_avail; } diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 530f3da70218d..a7bfb10dc70c7 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -1064,6 +1064,7 @@ bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp); extern bool qdev_hot_removed; char *qdev_get_dev_path(DeviceState *dev); +const char *qdev_get_printable_name(DeviceState *dev); void qbus_set_hotplug_handler(BusState *bus, Object *handler); void qbus_set_bus_hotplug_handler(BusState *bus); From 21659e726e3832b33d108faa1046db79eb6f611c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 24 Sep 2025 18:39:11 +0200 Subject: [PATCH 1273/1794] scripts/checkpatch: Avoid recommending legacy qemu_bh_new_guarded() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu_bh_new_guarded() is considered legacy since commit 9c86c97f12c ("async: Add an optional reentrancy guard to the BH API"); recommend the new API: aio_bh_new_guarded(). Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250924163911.51479-1-philmd@linaro.org> Signed-off-by: Stefan Hajnoczi --- scripts/checkpatch.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 40b6955c69863..d3d75f3f13982 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3198,9 +3198,9 @@ sub process { if ($line =~ /\bsignal\s*\(/ && !($line =~ /SIG_(?:IGN|DFL)/)) { ERROR("use sigaction to establish signal handlers; signal is not portable\n" . $herecurr); } -# recommend qemu_bh_new_guarded instead of qemu_bh_new - if ($realfile =~ /.*\/hw\/.*/ && $line =~ /\bqemu_bh_new\s*\(/) { - ERROR("use qemu_bh_new_guarded() instead of qemu_bh_new() to avoid reentrancy problems\n" . $herecurr); +# recommend aio_bh_new_guarded instead of legacy qemu_bh_new / qemu_bh_new_guarded + if ($realfile =~ /.*\/hw\/.*/ && $line =~ /\bqemu_bh_new(_guarded)?\s*\(/) { + ERROR("use aio_bh_new_guarded() instead of qemu_bh_new*() to avoid reentrancy problems\n" . $herecurr); } # recommend aio_bh_new_guarded instead of aio_bh_new if ($realfile =~ /.*\/hw\/.*/ && $line =~ /\baio_bh_new\s*\(/) { From eb7abb4a719f93ddd56571bf91681044b4159399 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Oct 2025 13:54:50 -0700 Subject: [PATCH 1274/1794] hw/intc/loongarch_dintc: Set class_size for LoongArchDINTCClass Fixes: 4d4baab24179 ("loongarch: add a direct interrupt controller device") Signed-off-by: Richard Henderson --- hw/intc/loongarch_dintc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/intc/loongarch_dintc.c b/hw/intc/loongarch_dintc.c index dc8f7ffdf6c1c..32bdd171c588f 100644 --- a/hw/intc/loongarch_dintc.c +++ b/hw/intc/loongarch_dintc.c @@ -197,6 +197,7 @@ static const TypeInfo loongarch_dintc_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LoongArchDINTCState), .instance_init = loongarch_dintc_init, + .class_size = sizeof(LoongArchDINTCClass), .class_init = loongarch_dintc_class_init, .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, From 971e25424c0e2cbbea28cc7e6d09a4569ca6cb06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 17:16:23 +0200 Subject: [PATCH 1275/1794] system/ramblock: Remove obsolete comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This comment was added almost 5 years ago in commit 41aa4e9fd84 ("ram_addr: Split RAMBlock definition"). Clearly it got ignored: $ git grep -l system/ramblock.h hw/display/virtio-gpu-udmabuf.c hw/hyperv/hv-balloon.c hw/virtio/vhost-user.c migration/dirtyrate.c migration/file.c migration/multifd-nocomp.c migration/multifd-qatzip.c migration/multifd-qpl.c migration/multifd-uadk.c migration/multifd-zero-page.c migration/multifd-zlib.c migration/multifd-zstd.c migration/multifd.c migration/postcopy-ram.c system/ram-block-attributes.c target/i386/kvm/tdx.c tests/qtest/fuzz/generic_fuzz.c At this point it seems saner to just remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: David Hildenbrand Reviewed-by: Richard Henderson Acked-by: Peter Xu Message-Id: <20251002032812.26069-2-philmd@linaro.org> --- include/system/ramblock.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/system/ramblock.h b/include/system/ramblock.h index 87e847e184aa4..8999206592d8e 100644 --- a/include/system/ramblock.h +++ b/include/system/ramblock.h @@ -11,11 +11,6 @@ * */ -/* - * This header is for use by exec.c and memory.c ONLY. Do not include it. - * The functions declared here will be removed soon. - */ - #ifndef SYSTEM_RAMBLOCK_H #define SYSTEM_RAMBLOCK_H From 34f9b0ad082268e347cee5c4903940e4ea67614f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 17:12:12 +0200 Subject: [PATCH 1276/1794] system/ramblock: Move ram_block_is_pmem() declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move ramblock_is_pmem() along with the RAM Block API exposed by the "system/ramblock.h" header. Rename as ram_block_is_pmem() to keep API prefix consistency. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Acked-by: Peter Xu Message-Id: <20251002032812.26069-3-philmd@linaro.org> --- include/system/ram_addr.h | 2 -- include/system/ramblock.h | 5 +++++ migration/ram.c | 3 ++- system/physmem.c | 5 +++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index 15a1b1a4fa2bd..53c0c8c3856a6 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -99,8 +99,6 @@ static inline unsigned long int ramblock_recv_bitmap_offset(void *host_addr, return host_addr_offset >> TARGET_PAGE_BITS; } -bool ramblock_is_pmem(RAMBlock *rb); - /** * qemu_ram_alloc_from_file, * qemu_ram_alloc_from_fd: Allocate a ram block from the specified backing diff --git a/include/system/ramblock.h b/include/system/ramblock.h index 8999206592d8e..7059b20d9199b 100644 --- a/include/system/ramblock.h +++ b/include/system/ramblock.h @@ -108,4 +108,9 @@ void ram_block_attributes_destroy(RamBlockAttributes *attr); int ram_block_attributes_state_change(RamBlockAttributes *attr, uint64_t offset, uint64_t size, bool to_discard); +/** + * ram_block_is_pmem: Whether the RAM block is of persistent memory + */ +bool ram_block_is_pmem(RAMBlock *rb); + #endif diff --git a/migration/ram.c b/migration/ram.c index 9aac89638aebb..189931d24dc01 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -53,6 +53,7 @@ #include "qemu/rcu_queue.h" #include "migration/colo.h" #include "system/cpu-throttle.h" +#include "system/ramblock.h" #include "savevm.h" #include "qemu/iov.h" #include "multifd.h" @@ -4370,7 +4371,7 @@ static bool ram_has_postcopy(void *opaque) { RAMBlock *rb; RAMBLOCK_FOREACH_NOT_IGNORED(rb) { - if (ramblock_is_pmem(rb)) { + if (ram_block_is_pmem(rb)) { info_report("Block: %s, host: %p is a nvdimm memory, postcopy" "is not supported now!", rb->idstr, rb->host); return false; diff --git a/system/physmem.c b/system/physmem.c index dbb2a4e017542..cb3e5fcfdc1d6 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -43,6 +43,7 @@ #include "system/kvm.h" #include "system/tcg.h" #include "system/qtest.h" +#include "system/ramblock.h" #include "qemu/timer.h" #include "qemu/config-file.h" #include "qemu/error-report.h" @@ -1802,7 +1803,7 @@ void qemu_ram_msync(RAMBlock *block, ram_addr_t start, ram_addr_t length) #ifdef CONFIG_LIBPMEM /* The lack of support for pmem should not block the sync */ - if (ramblock_is_pmem(block)) { + if (ram_block_is_pmem(block)) { void *addr = ramblock_ptr(block, start); pmem_persist(addr, length); return; @@ -3941,7 +3942,7 @@ int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t start, return ret; } -bool ramblock_is_pmem(RAMBlock *rb) +bool ram_block_is_pmem(RAMBlock *rb) { return rb->flags & RAM_PMEM; } From 8fe6ce40190872b4ccb4a3d601f4361b4bcdb0bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 16:25:59 +0200 Subject: [PATCH 1277/1794] system/ramblock: Move ram_block_discard_*_range() declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep RAM blocks API in the same header: "system/ramblock.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Acked-by: Peter Xu Message-Id: <20251002032812.26069-4-philmd@linaro.org> --- accel/kvm/kvm-all.c | 1 + hw/hyperv/hv-balloon-our_range_memslots.c | 1 + hw/virtio/virtio-balloon.c | 1 + hw/virtio/virtio-mem.c | 1 + include/exec/cpu-common.h | 3 --- include/system/ramblock.h | 4 ++++ 6 files changed, 8 insertions(+), 3 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 9060599cd736a..e3c8472340624 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -32,6 +32,7 @@ #include "system/runstate.h" #include "system/cpus.h" #include "system/accel-blocker.h" +#include "system/ramblock.h" #include "accel/accel-ops.h" #include "qemu/bswap.h" #include "exec/tswap.h" diff --git a/hw/hyperv/hv-balloon-our_range_memslots.c b/hw/hyperv/hv-balloon-our_range_memslots.c index 1505a395cf7da..1fc95e1648021 100644 --- a/hw/hyperv/hv-balloon-our_range_memslots.c +++ b/hw/hyperv/hv-balloon-our_range_memslots.c @@ -8,6 +8,7 @@ */ #include "qemu/osdep.h" +#include "system/ramblock.h" #include "hv-balloon-internal.h" #include "hv-balloon-our_range_memslots.h" #include "trace.h" diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index db787d00b312e..02cdd807d77e2 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -23,6 +23,7 @@ #include "hw/qdev-properties.h" #include "hw/boards.h" #include "system/balloon.h" +#include "system/ramblock.h" #include "hw/virtio/virtio-balloon.h" #include "system/address-spaces.h" #include "qapi/error.h" diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index c46f6f9c3e2f8..1de2d3de521fe 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -17,6 +17,7 @@ #include "qemu/units.h" #include "system/numa.h" #include "system/system.h" +#include "system/ramblock.h" #include "system/reset.h" #include "system/runstate.h" #include "hw/virtio/virtio.h" diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index b96ac49844aff..df520f15d3020 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -163,9 +163,6 @@ void cpu_flush_icache_range(hwaddr start, hwaddr len); typedef int (RAMBlockIterFunc)(RAMBlock *rb, void *opaque); int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque); -int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length); -int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t start, - size_t length); /* Returns: 0 on success, -1 on error */ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, diff --git a/include/system/ramblock.h b/include/system/ramblock.h index 7059b20d9199b..530c5a2e4c287 100644 --- a/include/system/ramblock.h +++ b/include/system/ramblock.h @@ -103,6 +103,10 @@ struct RamBlockAttributes { QLIST_HEAD(, RamDiscardListener) rdl_list; }; +int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length); +int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t start, + size_t length); + RamBlockAttributes *ram_block_attributes_create(RAMBlock *ram_block); void ram_block_attributes_destroy(RamBlockAttributes *attr); int ram_block_attributes_state_change(RamBlockAttributes *attr, uint64_t offset, From fe11c4957b8120c48ed650e7c970ea004d8432f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 16:18:18 +0200 Subject: [PATCH 1278/1794] system/ramblock: Rename @start -> @offset in ram_block_discard_range() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename @start as @offset, since it express an offset within a RAMBlock. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Acked-by: Peter Xu Message-Id: <20251002032812.26069-5-philmd@linaro.org> --- include/system/ramblock.h | 6 ++++-- system/physmem.c | 28 ++++++++++++++-------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/include/system/ramblock.h b/include/system/ramblock.h index 530c5a2e4c287..85cceff6bce01 100644 --- a/include/system/ramblock.h +++ b/include/system/ramblock.h @@ -103,8 +103,10 @@ struct RamBlockAttributes { QLIST_HEAD(, RamDiscardListener) rdl_list; }; -int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length); -int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t start, +/* @offset: the offset within the RAMBlock */ +int ram_block_discard_range(RAMBlock *rb, uint64_t offset, size_t length); +/* @offset: the offset within the RAMBlock */ +int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t offset, size_t length); RamBlockAttributes *ram_block_attributes_create(RAMBlock *ram_block); diff --git a/system/physmem.c b/system/physmem.c index cb3e5fcfdc1d6..a37592fd3209a 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3795,18 +3795,18 @@ int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque) } /* - * Unmap pages of memory from start to start+length such that + * Unmap pages of memory from offset to offset+length such that * they a) read as 0, b) Trigger whatever fault mechanism * the OS provides for postcopy. * The pages must be unmapped by the end of the function. * Returns: 0 on success, none-0 on failure * */ -int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) +int ram_block_discard_range(RAMBlock *rb, uint64_t offset, size_t length) { int ret = -1; - uint8_t *host_startaddr = rb->host + start; + uint8_t *host_startaddr = rb->host + offset; if (!QEMU_PTR_IS_ALIGNED(host_startaddr, rb->page_size)) { error_report("%s: Unaligned start address: %p", @@ -3814,7 +3814,7 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) goto err; } - if ((start + length) <= rb->max_length) { + if ((offset + length) <= rb->max_length) { bool need_madvise, need_fallocate; if (!QEMU_IS_ALIGNED(length, rb->page_size)) { error_report("%s: Unaligned length: %zx", __func__, length); @@ -3865,11 +3865,11 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) } ret = fallocate(rb->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, - start + rb->fd_offset, length); + offset + rb->fd_offset, length); if (ret) { ret = -errno; error_report("%s: Failed to fallocate %s:%" PRIx64 "+%" PRIx64 - " +%zx (%d)", __func__, rb->idstr, start, + " +%zx (%d)", __func__, rb->idstr, offset, rb->fd_offset, length, ret); goto err; } @@ -3877,7 +3877,7 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) ret = -ENOSYS; error_report("%s: fallocate not available/file" "%s:%" PRIx64 "+%" PRIx64 " +%zx (%d)", __func__, - rb->idstr, start, rb->fd_offset, length, ret); + rb->idstr, offset, rb->fd_offset, length, ret); goto err; #endif } @@ -3897,13 +3897,13 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) ret = -errno; error_report("%s: Failed to discard range " "%s:%" PRIx64 " +%zx (%d)", - __func__, rb->idstr, start, length, ret); + __func__, rb->idstr, offset, length, ret); goto err; } #else ret = -ENOSYS; error_report("%s: MADVISE not available %s:%" PRIx64 " +%zx (%d)", - __func__, rb->idstr, start, length, ret); + __func__, rb->idstr, offset, length, ret); goto err; #endif } @@ -3911,14 +3911,14 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) need_madvise, need_fallocate, ret); } else { error_report("%s: Overrun block '%s' (%" PRIu64 "/%zx/" RAM_ADDR_FMT")", - __func__, rb->idstr, start, length, rb->max_length); + __func__, rb->idstr, offset, length, rb->max_length); } err: return ret; } -int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t start, +int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t offset, size_t length) { int ret = -1; @@ -3926,17 +3926,17 @@ int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t start, #ifdef CONFIG_FALLOCATE_PUNCH_HOLE /* ignore fd_offset with guest_memfd */ ret = fallocate(rb->guest_memfd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, - start, length); + offset, length); if (ret) { ret = -errno; error_report("%s: Failed to fallocate %s:%" PRIx64 " +%zx (%d)", - __func__, rb->idstr, start, length, ret); + __func__, rb->idstr, offset, length, ret); } #else ret = -ENOSYS; error_report("%s: fallocate not available %s:%" PRIx64 " +%zx (%d)", - __func__, rb->idstr, start, length, ret); + __func__, rb->idstr, offset, length, ret); #endif return ret; From 6feb119d32ed1b8853e407495fad33795b9c2137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 17:36:46 +0200 Subject: [PATCH 1279/1794] system/ramblock: Move RAMBlock helpers out of "system/ram_addr.h" 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 Acked-by: Peter Xu Message-Id: <20251002032812.26069-6-philmd@linaro.org> --- include/system/ram_addr.h | 11 ----------- include/system/ramblock.h | 11 +++++++++++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index 53c0c8c3856a6..6b528338efcd7 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -80,17 +80,6 @@ static inline bool clear_bmap_test_and_clear(RAMBlock *rb, uint64_t page) return bitmap_test_and_clear(rb->clear_bmap, page >> shift, 1); } -static inline bool offset_in_ramblock(RAMBlock *b, ram_addr_t offset) -{ - return (b && b->host && offset < b->used_length) ? true : false; -} - -static inline void *ramblock_ptr(RAMBlock *block, ram_addr_t offset) -{ - assert(offset_in_ramblock(block, offset)); - return (char *)block->host + offset; -} - static inline unsigned long int ramblock_recv_bitmap_offset(void *host_addr, RAMBlock *rb) { diff --git a/include/system/ramblock.h b/include/system/ramblock.h index 85cceff6bce01..76694fe1b5bb9 100644 --- a/include/system/ramblock.h +++ b/include/system/ramblock.h @@ -119,4 +119,15 @@ int ram_block_attributes_state_change(RamBlockAttributes *attr, uint64_t offset, */ bool ram_block_is_pmem(RAMBlock *rb); +static inline bool offset_in_ramblock(RAMBlock *b, ram_addr_t offset) +{ + return b && b->host && (offset < b->used_length); +} + +static inline void *ramblock_ptr(RAMBlock *block, ram_addr_t offset) +{ + assert(offset_in_ramblock(block, offset)); + return (char *)block->host + offset; +} + #endif From bb02dcf2d4c0ec446fb53516eea76087f8b7a3ed Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 22 Sep 2025 12:29:40 -0700 Subject: [PATCH 1280/1794] system/memory: Split address_space_write_rom_internal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 2dbaf58bbe7 we conditionally skipped the increment of buf because ubsan warns incrementing NULL, and buf is always NULL for FLUSH_CACHE. However, the existence of the test for NULL caused Coverity to warn that the memcpy in the WRITE_DATA case lacked a test for NULL. Duplicate address_space_write_rom_internal into the two callers, dropping enum write_rom_type, and simplify. This eliminates buf in the flush case, and eliminates the conditional increment of buf in the write case. Coverity: CID 1621220 Fixes: 2dbaf58bbe7 ("system/physmem: Silence warning from ubsan") Signed-off-by: Richard Henderson Reviewed-by: Thomas Huth Message-ID: <20250922192940.2908002-1-richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- system/physmem.c | 74 +++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 45 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index a37592fd3209a..34216fa5384c5 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3186,63 +3186,33 @@ void cpu_physical_memory_rw(hwaddr addr, void *buf, buf, len, is_write); } -enum write_rom_type { - WRITE_DATA, - FLUSH_CACHE, -}; - -static inline MemTxResult address_space_write_rom_internal(AddressSpace *as, - hwaddr addr, - MemTxAttrs attrs, - const void *ptr, - hwaddr len, - enum write_rom_type type) +/* used for ROM loading : can write in RAM and ROM */ +MemTxResult address_space_write_rom(AddressSpace *as, hwaddr addr, + MemTxAttrs attrs, + const void *buf, hwaddr len) { - hwaddr l; - uint8_t *ram_ptr; - hwaddr addr1; - MemoryRegion *mr; - const uint8_t *buf = ptr; - RCU_READ_LOCK_GUARD(); while (len > 0) { - l = len; - mr = address_space_translate(as, addr, &addr1, &l, true, attrs); + hwaddr addr1, l = len; + MemoryRegion *mr = address_space_translate(as, addr, &addr1, &l, + true, attrs); if (!memory_region_supports_direct_access(mr)) { l = memory_access_size(mr, l, addr1); } else { /* ROM/RAM case */ - ram_ptr = qemu_map_ram_ptr(mr->ram_block, addr1); - switch (type) { - case WRITE_DATA: - memcpy(ram_ptr, buf, l); - invalidate_and_set_dirty(mr, addr1, l); - break; - case FLUSH_CACHE: - flush_idcache_range((uintptr_t)ram_ptr, (uintptr_t)ram_ptr, l); - break; - } + void *ram_ptr = qemu_map_ram_ptr(mr->ram_block, addr1); + memcpy(ram_ptr, buf, l); + invalidate_and_set_dirty(mr, addr1, l); } len -= l; addr += l; - if (buf) { - buf += l; - } + buf += l; } return MEMTX_OK; } -/* used for ROM loading : can write in RAM and ROM */ -MemTxResult address_space_write_rom(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, - const void *buf, hwaddr len) -{ - return address_space_write_rom_internal(as, addr, attrs, - buf, len, WRITE_DATA); -} - -void cpu_flush_icache_range(hwaddr start, hwaddr len) +void cpu_flush_icache_range(hwaddr addr, hwaddr len) { /* * This function should do the same thing as an icache flush that was @@ -3254,9 +3224,23 @@ void cpu_flush_icache_range(hwaddr start, hwaddr len) return; } - address_space_write_rom_internal(&address_space_memory, - start, MEMTXATTRS_UNSPECIFIED, - NULL, len, FLUSH_CACHE); + RCU_READ_LOCK_GUARD(); + while (len > 0) { + hwaddr addr1, l = len; + MemoryRegion *mr = address_space_translate(&address_space_memory, + addr, &addr1, &l, true, + MEMTXATTRS_UNSPECIFIED); + + if (!memory_region_supports_direct_access(mr)) { + l = memory_access_size(mr, l, addr1); + } else { + /* ROM/RAM case */ + void *ram_ptr = qemu_map_ram_ptr(mr->ram_block, addr1); + flush_idcache_range((uintptr_t)ram_ptr, (uintptr_t)ram_ptr, l); + } + len -= l; + addr += l; + } } /* From 7d4c9d2cb89818ef800718a4f462b34a21ee65cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 20:27:48 +0200 Subject: [PATCH 1281/1794] docs/devel/loads-stores: Stop mentioning cpu_physical_memory_write_rom() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the documentation after commit 3c8133f9737 ("Rename cpu_physical_memory_write_rom() to address_space_write_rom()"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251002084203.63899-2-philmd@linaro.org> --- docs/devel/loads-stores.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/devel/loads-stores.rst b/docs/devel/loads-stores.rst index 9471bac8599df..f9b565da57ade 100644 --- a/docs/devel/loads-stores.rst +++ b/docs/devel/loads-stores.rst @@ -474,7 +474,7 @@ This function is intended for use by the GDB stub and similar code. It takes a virtual address, converts it to a physical address via an MMU lookup using the current settings of the specified CPU, and then performs the access (using ``address_space_rw`` for -reads or ``cpu_physical_memory_write_rom`` for writes). +reads or ``address_space_write_rom`` for writes). This means that if the access is a write to a ROM then this function will modify the contents (whereas a normal guest CPU access would ignore the write attempt). From 839976e9da4b577aca284847b7e965332f2ca687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 14:36:19 +0200 Subject: [PATCH 1282/1794] system/memory: Factor address_space_is_io() out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor address_space_is_io() out of cpu_physical_memory_is_io(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20251002084203.63899-3-philmd@linaro.org> --- include/system/memory.h | 9 +++++++++ system/physmem.c | 21 ++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/include/system/memory.h b/include/system/memory.h index 08daf0fc59e1d..f222743b6fd11 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -3047,6 +3047,15 @@ static inline MemoryRegion *address_space_translate(AddressSpace *as, bool address_space_access_valid(AddressSpace *as, hwaddr addr, hwaddr len, bool is_write, MemTxAttrs attrs); +/** + * address_space_is_io: check whether an guest physical addresses + * whithin an address space is I/O memory. + * + * @as: #AddressSpace to be accessed + * @addr: address within that address space + */ +bool address_space_is_io(AddressSpace *as, hwaddr addr); + /* address_space_map: map a physical memory region into a host virtual address * * May map a subset of the requested range, given by and returned in @plen. diff --git a/system/physmem.c b/system/physmem.c index 34216fa5384c5..5574c424f023d 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3356,6 +3356,17 @@ bool address_space_access_valid(AddressSpace *as, hwaddr addr, return flatview_access_valid(fv, addr, len, is_write, attrs); } +bool address_space_is_io(AddressSpace *as, hwaddr addr) +{ + MemoryRegion *mr; + + RCU_READ_LOCK_GUARD(); + mr = address_space_translate(as, addr, &addr, NULL, false, + MEMTXATTRS_UNSPECIFIED); + + return !(memory_region_is_ram(mr) || memory_region_is_romd(mr)); +} + static hwaddr flatview_extend_translation(FlatView *fv, hwaddr addr, hwaddr target_len, @@ -3752,15 +3763,7 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, bool cpu_physical_memory_is_io(hwaddr phys_addr) { - MemoryRegion*mr; - hwaddr l = 1; - - RCU_READ_LOCK_GUARD(); - mr = address_space_translate(&address_space_memory, - phys_addr, &phys_addr, &l, false, - MEMTXATTRS_UNSPECIFIED); - - return !(memory_region_is_ram(mr) || memory_region_is_romd(mr)); + return address_space_is_io(&address_space_memory, phys_addr); } int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque) From 6ffaa9219616d12bc6c3e7cb26a77f7432ac0c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 15:35:28 +0200 Subject: [PATCH 1283/1794] target/i386/arch_memory_mapping: Use address_space_memory_is_io() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since all functions have an address space argument, it is trivial to replace cpu_physical_memory_is_io() by address_space_memory_is_io(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251002084203.63899-4-philmd@linaro.org> --- target/i386/arch_memory_mapping.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/i386/arch_memory_mapping.c b/target/i386/arch_memory_mapping.c index a2398c2173226..560f4689abc26 100644 --- a/target/i386/arch_memory_mapping.c +++ b/target/i386/arch_memory_mapping.c @@ -35,7 +35,7 @@ static void walk_pte(MemoryMappingList *list, AddressSpace *as, } start_paddr = (pte & ~0xfff) & ~(0x1ULL << 63); - if (cpu_physical_memory_is_io(start_paddr)) { + if (address_space_is_io(as, start_paddr)) { /* I/O region */ continue; } @@ -65,7 +65,7 @@ static void walk_pte2(MemoryMappingList *list, AddressSpace *as, } start_paddr = pte & ~0xfff; - if (cpu_physical_memory_is_io(start_paddr)) { + if (address_space_is_io(as, start_paddr)) { /* I/O region */ continue; } @@ -100,7 +100,7 @@ static void walk_pde(MemoryMappingList *list, AddressSpace *as, if (pde & PG_PSE_MASK) { /* 2 MB page */ start_paddr = (pde & ~0x1fffff) & ~(0x1ULL << 63); - if (cpu_physical_memory_is_io(start_paddr)) { + if (address_space_is_io(as, start_paddr)) { /* I/O region */ continue; } @@ -142,7 +142,7 @@ static void walk_pde2(MemoryMappingList *list, AddressSpace *as, */ high_paddr = ((hwaddr)(pde & 0x1fe000) << 19); start_paddr = (pde & ~0x3fffff) | high_paddr; - if (cpu_physical_memory_is_io(start_paddr)) { + if (address_space_is_io(as, start_paddr)) { /* I/O region */ continue; } @@ -203,7 +203,7 @@ static void walk_pdpe(MemoryMappingList *list, AddressSpace *as, if (pdpe & PG_PSE_MASK) { /* 1 GB page */ start_paddr = (pdpe & ~0x3fffffff) & ~(0x1ULL << 63); - if (cpu_physical_memory_is_io(start_paddr)) { + if (address_space_is_io(as, start_paddr)) { /* I/O region */ continue; } From 1e440937d699514207946c36c353a7a616f8b805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 15:28:06 +0200 Subject: [PATCH 1284/1794] hw/s390x/sclp: Use address_space_memory_is_io() in sclp_service_call() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When cpu_address_space_init() isn't called during vCPU creation, its single address space is the global &address_space_memory. As s390x boards don't call cpu_address_space_init(), cpu->as points to &address_space_memory. We can then replace cpu_physical_memory_is_io() by the semantically equivalent address_space_memory_is_io() call. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Reviewed-by: Eric Farman Message-Id: <20251002084203.63899-5-philmd@linaro.org> --- hw/s390x/sclp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 9718564fa42fe..16057356b1121 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -16,6 +16,7 @@ #include "qemu/units.h" #include "qapi/error.h" #include "hw/boards.h" +#include "system/memory.h" #include "hw/s390x/sclp.h" #include "hw/s390x/event-facility.h" #include "hw/s390x/s390-pci-bus.h" @@ -308,7 +309,7 @@ int sclp_service_call(S390CPU *cpu, uint64_t sccb, uint32_t code) if (env->psw.mask & PSW_MASK_PSTATE) { return -PGM_PRIVILEGED; } - if (cpu_physical_memory_is_io(sccb)) { + if (address_space_is_io(CPU(cpu)->as, sccb)) { return -PGM_ADDRESSING; } if ((sccb & ~0x1fffUL) == 0 || (sccb & ~0x1fffUL) == env->psa From ec1eb357cb86eee74d63940154db1e1bfa86026a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 15:36:08 +0200 Subject: [PATCH 1285/1794] system/physmem: Remove cpu_physical_memory_is_io() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are no more uses of the legacy cpu_physical_memory_is_io() method. Remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251002084203.63899-6-philmd@linaro.org> --- include/exec/cpu-common.h | 2 -- system/physmem.c | 5 ----- 2 files changed, 7 deletions(-) diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index df520f15d3020..55911c1d9fea8 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -149,8 +149,6 @@ void *cpu_physical_memory_map(hwaddr addr, void cpu_physical_memory_unmap(void *buffer, hwaddr len, bool is_write, hwaddr access_len); -bool cpu_physical_memory_is_io(hwaddr phys_addr); - /* Coalesced MMIO regions are areas where write operations can be reordered. * This usually implies that write operations are side-effect free. This allows * batching which can make a major impact on performance when using diff --git a/system/physmem.c b/system/physmem.c index 5574c424f023d..c8a1fda55b03c 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3761,11 +3761,6 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, return 0; } -bool cpu_physical_memory_is_io(hwaddr phys_addr) -{ - return address_space_is_io(&address_space_memory, phys_addr); -} - int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque) { RAMBlock *block; From c2cac27dba3db3348563154f9b4b436ecde7b09a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 15:40:33 +0200 Subject: [PATCH 1286/1794] system/physmem: Pass address space argument to cpu_flush_icache_range() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename cpu_flush_icache_range() as address_space_flush_icache_range(), passing an address space by argument. The single caller, rom_reset(), already operates on an address space. Use it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251002084203.63899-7-philmd@linaro.org> --- hw/core/loader.c | 2 +- include/exec/cpu-common.h | 2 -- include/system/memory.h | 2 ++ system/physmem.c | 5 ++--- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index 524af6f14a095..477661a0255c9 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -1242,7 +1242,7 @@ static void rom_reset(void *unused) * that the instruction cache for that new region is clear, so that the * CPU definitely fetches its instructions from the just written data. */ - cpu_flush_icache_range(rom->addr, rom->datasize); + address_space_flush_icache_range(rom->as, rom->addr, rom->datasize); trace_loader_write_rom(rom->name, rom->addr, rom->datasize, rom->isrom); } diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 55911c1d9fea8..2e5aa684e9295 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -156,8 +156,6 @@ void cpu_physical_memory_unmap(void *buffer, hwaddr len, */ void qemu_flush_coalesced_mmio_buffer(void); -void cpu_flush_icache_range(hwaddr start, hwaddr len); - typedef int (RAMBlockIterFunc)(RAMBlock *rb, void *opaque); int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque); diff --git a/include/system/memory.h b/include/system/memory.h index f222743b6fd11..3bd5ffa5e0d07 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -2995,6 +2995,8 @@ void address_space_cache_invalidate(MemoryRegionCache *cache, */ void address_space_cache_destroy(MemoryRegionCache *cache); +void address_space_flush_icache_range(AddressSpace *as, hwaddr addr, hwaddr len); + /* address_space_get_iotlb_entry: translate an address into an IOTLB * entry. Should be called from an RCU critical section. */ diff --git a/system/physmem.c b/system/physmem.c index c8a1fda55b03c..018b8f315778f 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3212,7 +3212,7 @@ MemTxResult address_space_write_rom(AddressSpace *as, hwaddr addr, return MEMTX_OK; } -void cpu_flush_icache_range(hwaddr addr, hwaddr len) +void address_space_flush_icache_range(AddressSpace *as, hwaddr addr, hwaddr len) { /* * This function should do the same thing as an icache flush that was @@ -3227,8 +3227,7 @@ void cpu_flush_icache_range(hwaddr addr, hwaddr len) RCU_READ_LOCK_GUARD(); while (len > 0) { hwaddr addr1, l = len; - MemoryRegion *mr = address_space_translate(&address_space_memory, - addr, &addr1, &l, true, + MemoryRegion *mr = address_space_translate(as, addr, &addr1, &l, true, MEMTXATTRS_UNSPECIFIED); if (!memory_region_supports_direct_access(mr)) { From 0d4e15e8f80e77a5bf22e3620ed990331f05256d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 30 Sep 2025 05:57:57 +0200 Subject: [PATCH 1287/1794] hw/s390x/sclp: Replace [cpu_physical_memory -> address_space]_r/w() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_physical_memory_read() and cpu_physical_memory_write() are legacy (see commit b7ecba0f6f6), replace by address_space_read() and address_space_write(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Reviewed-by: Eric Farman Message-Id: <20251002084203.63899-8-philmd@linaro.org> --- hw/s390x/sclp.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 16057356b1121..51e88ba8f1214 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -304,6 +304,8 @@ int sclp_service_call(S390CPU *cpu, uint64_t sccb, uint32_t code) SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); SCCBHeader header; g_autofree SCCB *work_sccb = NULL; + AddressSpace *as = CPU(cpu)->as; + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; /* first some basic checks on program checks */ if (env->psw.mask & PSW_MASK_PSTATE) { @@ -318,7 +320,7 @@ int sclp_service_call(S390CPU *cpu, uint64_t sccb, uint32_t code) } /* the header contains the actual length of the sccb */ - cpu_physical_memory_read(sccb, &header, sizeof(SCCBHeader)); + address_space_read(as, sccb, attrs, &header, sizeof(SCCBHeader)); /* Valid sccb sizes */ if (be16_to_cpu(header.length) < sizeof(SCCBHeader)) { @@ -331,7 +333,7 @@ int sclp_service_call(S390CPU *cpu, uint64_t sccb, uint32_t code) * the host has checked the values */ work_sccb = g_malloc0(be16_to_cpu(header.length)); - cpu_physical_memory_read(sccb, work_sccb, be16_to_cpu(header.length)); + address_space_read(as, sccb, attrs, work_sccb, be16_to_cpu(header.length)); if (!sclp_command_code_valid(code)) { work_sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); @@ -345,8 +347,7 @@ int sclp_service_call(S390CPU *cpu, uint64_t sccb, uint32_t code) sclp_c->execute(sclp, work_sccb, code); out_write: - cpu_physical_memory_write(sccb, work_sccb, - be16_to_cpu(work_sccb->h.length)); + address_space_write(as, sccb, attrs, work_sccb, be16_to_cpu(header.length)); sclp_c->service_interrupt(sclp, sccb); From 2fd38d9c2944efef3b96fe9047b3c0f4d4e25eb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 15:43:45 +0200 Subject: [PATCH 1288/1794] target/s390x/mmu: Replace [cpu_physical_memory -> address_space]_rw() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When cpu_address_space_init() isn't called during vCPU creation, its single address space is the global &address_space_memory. As s390x boards don't call cpu_address_space_init(), cpu->as points to &address_space_memory. We can then replace cpu_physical_memory_rw() by the semantically equivalent address_space_rw() call. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20251002084203.63899-9-philmd@linaro.org> --- target/s390x/mmu_helper.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index 00946e9c0fe05..487c41bf933f5 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -23,6 +23,7 @@ #include "kvm/kvm_s390x.h" #include "system/kvm.h" #include "system/tcg.h" +#include "system/memory.h" #include "exec/page-protection.h" #include "exec/target_page.h" #include "hw/hw.h" @@ -522,6 +523,7 @@ int s390_cpu_pv_mem_rw(S390CPU *cpu, unsigned int offset, void *hostbuf, int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf, int len, bool is_write) { + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; int currlen, nr_pages, i; target_ulong *pages; uint64_t tec; @@ -542,11 +544,13 @@ int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf, if (ret) { trigger_access_exception(&cpu->env, ret, tec); } else if (hostbuf != NULL) { + AddressSpace *as = CPU(cpu)->as; + /* Copy data by stepping through the area page by page */ for (i = 0; i < nr_pages; i++) { currlen = MIN(len, TARGET_PAGE_SIZE - (laddr % TARGET_PAGE_SIZE)); - cpu_physical_memory_rw(pages[i] | (laddr & ~TARGET_PAGE_MASK), - hostbuf, currlen, is_write); + address_space_rw(as, pages[i] | (laddr & ~TARGET_PAGE_MASK), + attrs, hostbuf, currlen, is_write); laddr += currlen; hostbuf += currlen; len -= currlen; From 04979ddde45f1064aa98317172b0a5b99b4b4d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 15:51:33 +0200 Subject: [PATCH 1289/1794] target/i386/whpx: Replace legacy cpu_physical_memory_rw() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Get the vCPU address space and convert the legacy cpu_physical_memory_rw() by address_space_rw(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251002084203.63899-10-philmd@linaro.org> --- target/i386/whpx/whpx-all.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 2a85168ed51a5..256761834c975 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -788,8 +788,11 @@ static HRESULT CALLBACK whpx_emu_mmio_callback( void *ctx, WHV_EMULATOR_MEMORY_ACCESS_INFO *ma) { - cpu_physical_memory_rw(ma->GpaAddress, ma->Data, ma->AccessSize, - ma->Direction); + CPUState *cs = (CPUState *)ctx; + AddressSpace *as = cpu_addressspace(cs, MEMTXATTRS_UNSPECIFIED); + + address_space_rw(as, ma->GpaAddress, MEMTXATTRS_UNSPECIFIED, + ma->Data, ma->AccessSize, ma->Direction); return S_OK; } From 12a65afbbfd8bef09cafb1e0e7883c30ddc1c956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 15:47:18 +0200 Subject: [PATCH 1290/1794] target/i386/kvm: Replace legacy cpu_physical_memory_rw() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Get the vCPU address space and convert the legacy cpu_physical_memory_rw() by address_space_rw(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251002084203.63899-11-philmd@linaro.org> --- target/i386/kvm/xen-emu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 284c5ef6f68ca..52de019834363 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -21,6 +21,7 @@ #include "system/address-spaces.h" #include "xen-emu.h" #include "trace.h" +#include "system/memory.h" #include "system/runstate.h" #include "hw/pci/msi.h" @@ -75,6 +76,7 @@ static bool kvm_gva_to_gpa(CPUState *cs, uint64_t gva, uint64_t *gpa, static int kvm_gva_rw(CPUState *cs, uint64_t gva, void *_buf, size_t sz, bool is_write) { + AddressSpace *as = cpu_addressspace(cs, MEMTXATTRS_UNSPECIFIED); uint8_t *buf = (uint8_t *)_buf; uint64_t gpa; size_t len; @@ -87,7 +89,7 @@ static int kvm_gva_rw(CPUState *cs, uint64_t gva, void *_buf, size_t sz, len = sz; } - cpu_physical_memory_rw(gpa, buf, len, is_write); + address_space_rw(as, gpa, MEMTXATTRS_UNSPECIFIED, buf, len, is_write); buf += len; sz -= len; From 5699f6a4409af985d45cd67a0695a97655fec8bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 15:53:21 +0200 Subject: [PATCH 1291/1794] target/i386/nvmm: Inline cpu_physical_memory_rw() in nvmm_mem_callback 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: <20251002084203.63899-12-philmd@linaro.org> --- target/i386/nvmm/nvmm-all.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index ed424251673b9..2e442baf4b715 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -15,6 +15,7 @@ #include "accel/accel-ops.h" #include "system/nvmm.h" #include "system/cpus.h" +#include "system/memory.h" #include "system/runstate.h" #include "qemu/main-loop.h" #include "qemu/error-report.h" @@ -516,7 +517,9 @@ nvmm_io_callback(struct nvmm_io *io) static void nvmm_mem_callback(struct nvmm_mem *mem) { - cpu_physical_memory_rw(mem->gpa, mem->data, mem->size, mem->write); + /* TODO: Get CPUState via mem->vcpu? */ + address_space_rw(&address_space_memory, mem->gpa, MEMTXATTRS_UNSPECIFIED, + mem->data, mem->size, mem->write); /* Needed, otherwise infinite loop. */ current_cpu->vcpu_dirty = false; From 94c460835df5b810309a5ae4c89f0cddddbd9f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 15:55:07 +0200 Subject: [PATCH 1292/1794] hw/xen/hvm: Inline cpu_physical_memory_rw() in rw_phys_req_item() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_physical_memory_rw() is legacy, replace by address_space_rw(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251002084203.63899-13-philmd@linaro.org> --- hw/xen/xen-hvm-common.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/xen/xen-hvm-common.c b/hw/xen/xen-hvm-common.c index 78e0bc8f644ea..52e2cce397a42 100644 --- a/hw/xen/xen-hvm-common.c +++ b/hw/xen/xen-hvm-common.c @@ -12,6 +12,7 @@ #include "hw/xen/xen-bus.h" #include "hw/boards.h" #include "hw/xen/arch_hvm.h" +#include "system/memory.h" #include "system/runstate.h" #include "system/system.h" #include "system/xen.h" @@ -279,8 +280,8 @@ static void do_outp(uint32_t addr, * memory, as part of the implementation of an ioreq. * * Equivalent to - * cpu_physical_memory_rw(addr + (req->df ? -1 : +1) * req->size * i, - * val, req->size, 0/1) + * address_space_rw(as, addr + (req->df ? -1 : +1) * req->size * i, + * attrs, val, req->size, 0/1) * except without the integer overflow problems. */ static void rw_phys_req_item(hwaddr addr, @@ -295,7 +296,8 @@ static void rw_phys_req_item(hwaddr addr, } else { addr += offset; } - cpu_physical_memory_rw(addr, val, req->size, rw); + address_space_rw(&address_space_memory, addr, MEMTXATTRS_UNSPECIFIED, + val, req->size, rw); } static inline void read_phys_req_item(hwaddr addr, From be481476bae495191c1184b368531401a5584904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 15:57:57 +0200 Subject: [PATCH 1293/1794] system/physmem: Un-inline cpu_physical_memory_read/write() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to remove cpu_physical_memory_rw() in a pair of commits, and due to a cyclic dependency between "exec/cpu-common.h" and "system/memory.h", un-inline cpu_physical_memory_read() and cpu_physical_memory_write() as a prerequired step. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251002084203.63899-14-philmd@linaro.org> --- include/exec/cpu-common.h | 12 ++---------- system/physmem.c | 10 ++++++++++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 2e5aa684e9295..ed35a18704cb4 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -133,16 +133,8 @@ void cpu_destroy_address_spaces(CPUState *cpu); void cpu_physical_memory_rw(hwaddr addr, void *buf, hwaddr len, bool is_write); -static inline void cpu_physical_memory_read(hwaddr addr, - void *buf, hwaddr len) -{ - cpu_physical_memory_rw(addr, buf, len, false); -} -static inline void cpu_physical_memory_write(hwaddr addr, - const void *buf, hwaddr len) -{ - cpu_physical_memory_rw(addr, (void *)buf, len, true); -} +void cpu_physical_memory_read(hwaddr addr, void *buf, hwaddr len); +void cpu_physical_memory_write(hwaddr addr, const void *buf, hwaddr len); void *cpu_physical_memory_map(hwaddr addr, hwaddr *plen, bool is_write); diff --git a/system/physmem.c b/system/physmem.c index 018b8f315778f..4dc66ac2c7bc5 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3186,6 +3186,16 @@ void cpu_physical_memory_rw(hwaddr addr, void *buf, buf, len, is_write); } +void cpu_physical_memory_read(hwaddr addr, void *buf, hwaddr len) +{ + cpu_physical_memory_rw(addr, buf, len, false); +} + +void cpu_physical_memory_write(hwaddr addr, const void *buf, hwaddr len) +{ + cpu_physical_memory_rw(addr, (void *)buf, len, true); +} + /* used for ROM loading : can write in RAM and ROM */ MemTxResult address_space_write_rom(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, From 5b7502234c7ede51ab6e684b6abc64d5fee3c1a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 30 Sep 2025 10:14:35 +0200 Subject: [PATCH 1294/1794] system/physmem: Avoid cpu_physical_memory_rw when is_write is constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following the mechanical changes of commit adeefe01671 ("Avoid cpu_physical_memory_rw() with a constant is_write argument"), replace: - cpu_physical_memory_rw(, is_write=false) -> address_space_read() - cpu_physical_memory_rw(, is_write=true) -> address_space_write() Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251002084203.63899-15-philmd@linaro.org> --- scripts/coccinelle/exec_rw_const.cocci | 12 ------------ system/physmem.c | 6 ++++-- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/scripts/coccinelle/exec_rw_const.cocci b/scripts/coccinelle/exec_rw_const.cocci index 1a202969519b1..35ab79e6d7467 100644 --- a/scripts/coccinelle/exec_rw_const.cocci +++ b/scripts/coccinelle/exec_rw_const.cocci @@ -62,18 +62,6 @@ symbol true, false; + address_space_write(E1, E2, E3, E4, E5) ) -// Avoid uses of cpu_physical_memory_rw() with a constant is_write argument. -@@ -expression E1, E2, E3; -@@ -( -- cpu_physical_memory_rw(E1, E2, E3, false) -+ cpu_physical_memory_read(E1, E2, E3) -| -- cpu_physical_memory_rw(E1, E2, E3, true) -+ cpu_physical_memory_write(E1, E2, E3) -) - // Remove useless cast @@ expression E1, E2, E3, E4, E5, E6; diff --git a/system/physmem.c b/system/physmem.c index 4dc66ac2c7bc5..8d65b4c125f3f 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3188,12 +3188,14 @@ void cpu_physical_memory_rw(hwaddr addr, void *buf, void cpu_physical_memory_read(hwaddr addr, void *buf, hwaddr len) { - cpu_physical_memory_rw(addr, buf, len, false); + address_space_read(&address_space_memory, addr, + MEMTXATTRS_UNSPECIFIED, buf, len); } void cpu_physical_memory_write(hwaddr addr, const void *buf, hwaddr len) { - cpu_physical_memory_rw(addr, (void *)buf, len, true); + address_space_write(&address_space_memory, addr, + MEMTXATTRS_UNSPECIFIED, buf, len); } /* used for ROM loading : can write in RAM and ROM */ From e98ae807c42e5c9f5089c947f7c6ef444248a946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 15:58:17 +0200 Subject: [PATCH 1295/1794] system/physmem: Remove legacy cpu_physical_memory_rw() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The legacy cpu_physical_memory_rw() method is no more used, remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251002084203.63899-16-philmd@linaro.org> --- docs/devel/loads-stores.rst | 4 +--- include/exec/cpu-common.h | 2 -- scripts/coccinelle/exec_rw_const.cocci | 10 ---------- system/physmem.c | 7 ------- 4 files changed, 1 insertion(+), 22 deletions(-) diff --git a/docs/devel/loads-stores.rst b/docs/devel/loads-stores.rst index f9b565da57ade..c906c6509eedd 100644 --- a/docs/devel/loads-stores.rst +++ b/docs/devel/loads-stores.rst @@ -460,10 +460,8 @@ For new code they are better avoided: ``cpu_physical_memory_write`` -``cpu_physical_memory_rw`` - Regexes for git grep: - - ``\`` + - ``\`` ``cpu_memory_rw_debug`` ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index ed35a18704cb4..67e15c8e5078f 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -131,8 +131,6 @@ void cpu_address_space_init(CPUState *cpu, int asidx, */ void cpu_destroy_address_spaces(CPUState *cpu); -void cpu_physical_memory_rw(hwaddr addr, void *buf, - hwaddr len, bool is_write); void cpu_physical_memory_read(hwaddr addr, void *buf, hwaddr len); void cpu_physical_memory_write(hwaddr addr, const void *buf, hwaddr len); void *cpu_physical_memory_map(hwaddr addr, diff --git a/scripts/coccinelle/exec_rw_const.cocci b/scripts/coccinelle/exec_rw_const.cocci index 35ab79e6d7467..4c02c94e04e5d 100644 --- a/scripts/coccinelle/exec_rw_const.cocci +++ b/scripts/coccinelle/exec_rw_const.cocci @@ -21,13 +21,6 @@ expression E1, E2, E3, E4, E5; + address_space_rw(E1, E2, E3, E4, E5, true) | -- cpu_physical_memory_rw(E1, E2, E3, 0) -+ cpu_physical_memory_rw(E1, E2, E3, false) -| -- cpu_physical_memory_rw(E1, E2, E3, 1) -+ cpu_physical_memory_rw(E1, E2, E3, true) -| - - cpu_physical_memory_map(E1, E2, 0) + cpu_physical_memory_map(E1, E2, false) | @@ -81,9 +74,6 @@ type T; + address_space_write_rom(E1, E2, E3, E4, E5) | -- cpu_physical_memory_rw(E1, (T *)(E2), E3, E4) -+ cpu_physical_memory_rw(E1, E2, E3, E4) -| - cpu_physical_memory_read(E1, (T *)(E2), E3) + cpu_physical_memory_read(E1, E2, E3) | diff --git a/system/physmem.c b/system/physmem.c index 8d65b4c125f3f..36f327ca94e12 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3179,13 +3179,6 @@ MemTxResult address_space_set(AddressSpace *as, hwaddr addr, return error; } -void cpu_physical_memory_rw(hwaddr addr, void *buf, - hwaddr len, bool is_write) -{ - address_space_rw(&address_space_memory, addr, MEMTXATTRS_UNSPECIFIED, - buf, len, is_write); -} - void cpu_physical_memory_read(hwaddr addr, void *buf, hwaddr len) { address_space_read(&address_space_memory, addr, From 41d5e37e0e2080e9d351f75ebd60d21e0b4e115e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 16:03:33 +0200 Subject: [PATCH 1296/1794] hw/virtio/vhost: Replace legacy cpu_physical_memory_*map() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use VirtIODevice::dma_as address space to convert the legacy cpu_physical_memory_[un]map() calls to address_space_[un]map(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251002084203.63899-17-philmd@linaro.org> --- hw/virtio/vhost.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index c120ef38b910d..266a11514a13e 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -27,6 +27,7 @@ #include "migration/blocker.h" #include "migration/qemu-file-types.h" #include "system/dma.h" +#include "system/memory.h" #include "trace.h" /* enabled until disconnected backend stabilizes */ @@ -455,7 +456,8 @@ static void *vhost_memory_map(struct vhost_dev *dev, hwaddr addr, hwaddr *plen, bool is_write) { if (!vhost_dev_has_iommu(dev)) { - return cpu_physical_memory_map(addr, plen, is_write); + return address_space_map(dev->vdev->dma_as, addr, plen, is_write, + MEMTXATTRS_UNSPECIFIED); } else { return (void *)(uintptr_t)addr; } @@ -466,7 +468,8 @@ static void vhost_memory_unmap(struct vhost_dev *dev, void *buffer, hwaddr access_len) { if (!vhost_dev_has_iommu(dev)) { - cpu_physical_memory_unmap(buffer, len, is_write, access_len); + address_space_unmap(dev->vdev->dma_as, buffer, len, is_write, + access_len); } } From f9f713f1b4a272826f4acc4d57483e9dd1faa3f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 16:06:27 +0200 Subject: [PATCH 1297/1794] hw/virtio/virtio: Replace legacy cpu_physical_memory_map() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Propagate VirtIODevice::dma_as to virtqueue_undo_map_desc() in order to replace the legacy cpu_physical_memory_unmap() call by address_space_unmap(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251002084203.63899-18-philmd@linaro.org> --- hw/virtio/virtio.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index be73753b59fb2..153ee0a0cf1dd 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -32,6 +32,7 @@ #include "hw/virtio/virtio-access.h" #include "system/dma.h" #include "system/iothread.h" +#include "system/memory.h" #include "system/runstate.h" #include "virtio-qmp.h" @@ -1632,7 +1633,8 @@ static bool virtqueue_map_desc(VirtIODevice *vdev, unsigned int *p_num_sg, * virtqueue_unmap_sg() can't be used). Assumes buffers weren't written to * yet. */ -static void virtqueue_undo_map_desc(unsigned int out_num, unsigned int in_num, +static void virtqueue_undo_map_desc(AddressSpace *as, + unsigned int out_num, unsigned int in_num, struct iovec *iov) { unsigned int i; @@ -1640,7 +1642,7 @@ static void virtqueue_undo_map_desc(unsigned int out_num, unsigned int in_num, for (i = 0; i < out_num + in_num; i++) { int is_write = i >= out_num; - cpu_physical_memory_unmap(iov->iov_base, iov->iov_len, is_write, 0); + address_space_unmap(as, iov->iov_base, iov->iov_len, is_write, 0); iov++; } } @@ -1842,7 +1844,7 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz) return elem; err_undo_map: - virtqueue_undo_map_desc(out_num, in_num, iov); + virtqueue_undo_map_desc(vdev->dma_as, out_num, in_num, iov); goto done; } @@ -1992,7 +1994,7 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz) return elem; err_undo_map: - virtqueue_undo_map_desc(out_num, in_num, iov); + virtqueue_undo_map_desc(vdev->dma_as, out_num, in_num, iov); goto done; } From 2fb5ae9832728f766b66120fd23c7cf9db9d3ec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 30 Sep 2025 06:50:01 +0200 Subject: [PATCH 1298/1794] system/ram_addr: Remove unnecessary 'exec/cpu-common.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nothing in "system/ram_addr.h" requires definitions from "exec/cpu-common.h", remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20251001175448.18933-2-philmd@linaro.org> --- include/system/ram_addr.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index 6b528338efcd7..f74a0ecee5620 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -29,7 +29,6 @@ #include "qemu/rcu.h" #include "exec/hwaddr.h" -#include "exec/cpu-common.h" extern uint64_t total_dirty_pages; From a55e8ffe47ebe0d7da7b5008b5efdfa652545a7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Oct 2025 09:52:40 +0200 Subject: [PATCH 1299/1794] accel/kvm: Include missing 'exec/target_page.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "exec/target_page.h" header is indirectly pulled from "system/ram_addr.h". Include it explicitly, in order to avoid unrelated issues when refactoring "system/ram_addr.h": accel/kvm/kvm-all.c: In function ‘kvm_init’: accel/kvm/kvm-all.c:2636:12: error: ‘TARGET_PAGE_SIZE’ undeclared (first use in this function); did you mean ‘TARGET_PAGE_BITS’? 2636 | assert(TARGET_PAGE_SIZE <= qemu_real_host_page_size()); | ^~~~~~~~~~~~~~~~ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251001175448.18933-3-philmd@linaro.org> --- accel/kvm/kvm-all.c | 1 + 1 file changed, 1 insertion(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index e3c8472340624..08b2b5a371cf7 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -36,6 +36,7 @@ #include "accel/accel-ops.h" #include "qemu/bswap.h" #include "exec/tswap.h" +#include "exec/target_page.h" #include "system/memory.h" #include "system/ram_addr.h" #include "qemu/event_notifier.h" From d08bc190a0302d1fad8ca71a3ba13c51f7337bbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Oct 2025 09:54:51 +0200 Subject: [PATCH 1300/1794] hw/s390x/s390-stattrib: Include missing 'exec/target_page.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "exec/target_page.h" header is indirectly pulled from "system/ram_addr.h". Include it explicitly, in order to avoid unrelated issues when refactoring "system/ram_addr.h": hw/s390x/s390-stattrib-kvm.c: In function ‘kvm_s390_stattrib_set_stattr’: hw/s390x/s390-stattrib-kvm.c:89:57: error: ‘TARGET_PAGE_SIZE’ undeclared (first use in this function); did you mean ‘TARGET_PAGE_BITS’? 89 | unsigned long max = s390_get_memory_limit(s390ms) / TARGET_PAGE_SIZE; | ^~~~~~~~~~~~~~~~ | TARGET_PAGE_BITS Since "system/ram_addr.h" is actually not needed, remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Eric Farman Reviewed-by: Thomas Huth Message-Id: <20251001175448.18933-4-philmd@linaro.org> --- hw/s390x/s390-stattrib-kvm.c | 2 +- hw/s390x/s390-stattrib.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/s390x/s390-stattrib-kvm.c b/hw/s390x/s390-stattrib-kvm.c index e1fee361dc379..73df1f600b9f8 100644 --- a/hw/s390x/s390-stattrib-kvm.c +++ b/hw/s390x/s390-stattrib-kvm.c @@ -10,13 +10,13 @@ */ #include "qemu/osdep.h" +#include "exec/target_page.h" #include "hw/s390x/s390-virtio-ccw.h" #include "migration/qemu-file.h" #include "hw/s390x/storage-attributes.h" #include "qemu/error-report.h" #include "system/kvm.h" #include "system/memory_mapping.h" -#include "system/ram_addr.h" #include "kvm/kvm_s390x.h" #include "qapi/error.h" diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index 13a678a80373b..aa18537291486 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -11,12 +11,12 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "exec/target_page.h" #include "migration/qemu-file.h" #include "migration/register.h" #include "hw/qdev-properties.h" #include "hw/s390x/storage-attributes.h" #include "qemu/error-report.h" -#include "system/ram_addr.h" #include "qapi/error.h" #include "qobject/qdict.h" #include "cpu.h" From edd1f91d38dfc341cac02529fcd315609e959763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Oct 2025 09:56:41 +0200 Subject: [PATCH 1301/1794] hw/vfio/listener: Include missing 'exec/target_page.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "exec/target_page.h" header is indirectly pulled from "system/ram_addr.h". Include it explicitly, in order to avoid unrelated issues when refactoring "system/ram_addr.h": hw/vfio/listener.c: In function ‘vfio_ram_discard_register_listener’: hw/vfio/listener.c:258:28: error: implicit declaration of function ‘qemu_target_page_size’; did you mean ‘qemu_ram_pagesize’? 258 | int target_page_size = qemu_target_page_size(); | ^~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Message-Id: <20251001175448.18933-5-philmd@linaro.org> --- hw/vfio/listener.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index a2c19a3cec1a9..b5cefc9395c93 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -25,6 +25,7 @@ #endif #include +#include "exec/target_page.h" #include "hw/vfio/vfio-device.h" #include "hw/vfio/pci.h" #include "system/address-spaces.h" From 6204f64260b78cc4080ca7b1ee221718273d6566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 30 Sep 2025 10:33:33 +0200 Subject: [PATCH 1302/1794] target/arm/tcg/mte: Include missing 'exec/target_page.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "exec/target_page.h" header is indirectly pulled from "system/ram_addr.h". Include it explicitly, in order to avoid unrelated issues when refactoring "system/ram_addr.h": target/arm/tcg/mte_helper.c:815:23: error: use of undeclared identifier 'TARGET_PAGE_MASK' 815 | prev_page = ptr & TARGET_PAGE_MASK; | ^ target/arm/tcg/mte_helper.c:816:29: error: use of undeclared identifier 'TARGET_PAGE_SIZE' 816 | next_page = prev_page + TARGET_PAGE_SIZE; | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251001175448.18933-6-philmd@linaro.org> --- target/arm/tcg/mte_helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 302e899287c73..7d80244788e52 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -21,6 +21,7 @@ #include "qemu/log.h" #include "cpu.h" #include "internals.h" +#include "exec/target_page.h" #include "exec/page-protection.h" #ifdef CONFIG_USER_ONLY #include "user/cpu_loop.h" From 97480ca692e94bb790190a43bb122bd0752b8f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 30 Sep 2025 09:20:38 +0200 Subject: [PATCH 1303/1794] hw: Remove unnecessary 'system/ram_addr.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit None of these files require definition exposed by "system/ram_addr.h", remove its inclusion. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Jagannathan Raman Reviewed-by: Cédric Le Goater Reviewed-by: Eric Farman Reviewed-by: Thomas Huth Acked-by: Michael S. Tsirkin Message-Id: <20251001175448.18933-7-philmd@linaro.org> --- hw/ppc/spapr.c | 1 - hw/ppc/spapr_caps.c | 1 - hw/ppc/spapr_pci.c | 1 - hw/remote/memory.c | 1 - hw/remote/proxy-memory-listener.c | 1 - hw/s390x/s390-virtio-ccw.c | 1 - hw/vfio/spapr.c | 1 - hw/virtio/virtio-mem.c | 1 - 8 files changed, 8 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 82fb23beaa8d6..97ab6bebd25c0 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -77,7 +77,6 @@ #include "hw/virtio/virtio-scsi.h" #include "hw/virtio/vhost-scsi-common.h" -#include "system/ram_addr.h" #include "system/confidential-guest-support.h" #include "hw/usb.h" #include "qemu/config-file.h" diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index f2f5722d8ad4f..0f94c192fd480 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -27,7 +27,6 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "system/hw_accel.h" -#include "system/ram_addr.h" #include "target/ppc/cpu.h" #include "target/ppc/mmu-hash64.h" #include "cpu-models.h" diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 1ac1185825e84..f9095552e865f 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -34,7 +34,6 @@ #include "hw/pci/pci_host.h" #include "hw/ppc/spapr.h" #include "hw/pci-host/spapr.h" -#include "system/ram_addr.h" #include #include "trace.h" #include "qemu/error-report.h" diff --git a/hw/remote/memory.c b/hw/remote/memory.c index 00193a552fa7d..8195aa5fb83db 100644 --- a/hw/remote/memory.c +++ b/hw/remote/memory.c @@ -11,7 +11,6 @@ #include "qemu/osdep.h" #include "hw/remote/memory.h" -#include "system/ram_addr.h" #include "qapi/error.h" static void remote_sysmem_reset(void) diff --git a/hw/remote/proxy-memory-listener.c b/hw/remote/proxy-memory-listener.c index 30ac74961dd3a..e1a52d24f0bfe 100644 --- a/hw/remote/proxy-memory-listener.c +++ b/hw/remote/proxy-memory-listener.c @@ -12,7 +12,6 @@ #include "qemu/range.h" #include "system/memory.h" #include "exec/cpu-common.h" -#include "system/ram_addr.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/remote/mpqemu-link.h" diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index d0c6e80cb0507..ad2c48188a878 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "system/ram_addr.h" #include "system/confidential-guest-support.h" #include "hw/boards.h" #include "hw/s390x/sclp.h" diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 8d9d68da4ec8f..0f23681a3f936 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -17,7 +17,6 @@ #include "hw/vfio/vfio-container-legacy.h" #include "hw/hw.h" -#include "system/ram_addr.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "trace.h" diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 1de2d3de521fe..15ba6799f2281 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -25,7 +25,6 @@ #include "hw/virtio/virtio-mem.h" #include "qapi/error.h" #include "qapi/visitor.h" -#include "system/ram_addr.h" #include "migration/misc.h" #include "hw/boards.h" #include "hw/qdev-properties.h" From 81aef73696c37c1680412e52d0a63c9c1fdd0466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 13:31:32 +0200 Subject: [PATCH 1304/1794] system/physmem: Un-inline cpu_physical_memory_get_dirty_flag() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid maintaining large functions in header, rely on the linker to optimize at linking time. cpu_physical_memory_get_dirty() doesn't involve any CPU, remove the 'cpu_' prefix. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251001175448.18933-8-philmd@linaro.org> --- include/system/ram_addr.h | 46 +-------------------------------------- system/physmem.c | 44 +++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index f74a0ecee5620..f8a307d1a3d72 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -142,46 +142,6 @@ static inline void qemu_ram_block_writeback(RAMBlock *block) #define DIRTY_CLIENTS_ALL ((1 << DIRTY_MEMORY_NUM) - 1) #define DIRTY_CLIENTS_NOCODE (DIRTY_CLIENTS_ALL & ~(1 << DIRTY_MEMORY_CODE)) -static inline bool cpu_physical_memory_get_dirty(ram_addr_t start, - ram_addr_t length, - unsigned client) -{ - DirtyMemoryBlocks *blocks; - unsigned long end, page; - unsigned long idx, offset, base; - bool dirty = false; - - assert(client < DIRTY_MEMORY_NUM); - - end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; - page = start >> TARGET_PAGE_BITS; - - WITH_RCU_READ_LOCK_GUARD() { - blocks = qatomic_rcu_read(&ram_list.dirty_memory[client]); - - idx = page / DIRTY_MEMORY_BLOCK_SIZE; - offset = page % DIRTY_MEMORY_BLOCK_SIZE; - base = page - offset; - while (page < end) { - unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE); - unsigned long num = next - base; - unsigned long found = find_next_bit(blocks->blocks[idx], - num, offset); - if (found < num) { - dirty = true; - break; - } - - page = next; - idx++; - offset = 0; - base += DIRTY_MEMORY_BLOCK_SIZE; - } - } - - return dirty; -} - static inline bool cpu_physical_memory_all_dirty(ram_addr_t start, ram_addr_t length, unsigned client) @@ -221,11 +181,7 @@ static inline bool cpu_physical_memory_all_dirty(ram_addr_t start, return dirty; } -static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, - unsigned client) -{ - return cpu_physical_memory_get_dirty(addr, 1, client); -} +bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, unsigned client); static inline bool cpu_physical_memory_is_clean(ram_addr_t addr) { diff --git a/system/physmem.c b/system/physmem.c index 36f327ca94e12..3d81272f9fb2c 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -899,6 +899,50 @@ void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) } } +static bool physical_memory_get_dirty(ram_addr_t start, ram_addr_t length, + unsigned client) +{ + DirtyMemoryBlocks *blocks; + unsigned long end, page; + unsigned long idx, offset, base; + bool dirty = false; + + assert(client < DIRTY_MEMORY_NUM); + + end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; + page = start >> TARGET_PAGE_BITS; + + WITH_RCU_READ_LOCK_GUARD() { + blocks = qatomic_rcu_read(&ram_list.dirty_memory[client]); + + idx = page / DIRTY_MEMORY_BLOCK_SIZE; + offset = page % DIRTY_MEMORY_BLOCK_SIZE; + base = page - offset; + while (page < end) { + unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE); + unsigned long num = next - base; + unsigned long found = find_next_bit(blocks->blocks[idx], + num, offset); + if (found < num) { + dirty = true; + break; + } + + page = next; + idx++; + offset = 0; + base += DIRTY_MEMORY_BLOCK_SIZE; + } + } + + return dirty; +} + +bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, unsigned client) +{ + return physical_memory_get_dirty(addr, 1, client); +} + /* Note: start and end must be within the same ram block. */ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, ram_addr_t length, From 3a0539afcbdf83aa919e32a36107bbe357ae9ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 13:33:02 +0200 Subject: [PATCH 1305/1794] system/physmem: Un-inline cpu_physical_memory_is_clean() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid maintaining large functions in header, rely on the linker to optimize at linking time. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251001175448.18933-9-philmd@linaro.org> --- include/system/ram_addr.h | 9 +-------- system/physmem.c | 9 +++++++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index f8a307d1a3d72..cdf25c315be56 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -183,14 +183,7 @@ static inline bool cpu_physical_memory_all_dirty(ram_addr_t start, bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, unsigned client); -static inline bool cpu_physical_memory_is_clean(ram_addr_t addr) -{ - bool vga = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_VGA); - bool code = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_CODE); - bool migration = - cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_MIGRATION); - return !(vga && code && migration); -} +bool cpu_physical_memory_is_clean(ram_addr_t addr); static inline uint8_t cpu_physical_memory_range_includes_clean(ram_addr_t start, ram_addr_t length, diff --git a/system/physmem.c b/system/physmem.c index 3d81272f9fb2c..e555b3196fa78 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -943,6 +943,15 @@ bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, unsigned client) return physical_memory_get_dirty(addr, 1, client); } +bool cpu_physical_memory_is_clean(ram_addr_t addr) +{ + bool vga = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_VGA); + bool code = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_CODE); + bool migration = + cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_MIGRATION); + return !(vga && code && migration); +} + /* Note: start and end must be within the same ram block. */ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, ram_addr_t length, From 84a8e6399bc06550e41f5b2f7b94e4a2213b2ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 13:35:49 +0200 Subject: [PATCH 1306/1794] system/physmem: Un-inline cpu_physical_memory_range_includes_clean() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid maintaining large functions in header, rely on the linker to optimize at linking time. cpu_physical_memory_all_dirty() doesn't involve any CPU, remove the 'cpu_' prefix. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251001175448.18933-10-philmd@linaro.org> --- include/system/ram_addr.h | 62 ++------------------------------------- system/physmem.c | 60 +++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 59 deletions(-) diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index cdf25c315be56..2dcca260b2be6 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -142,69 +142,13 @@ static inline void qemu_ram_block_writeback(RAMBlock *block) #define DIRTY_CLIENTS_ALL ((1 << DIRTY_MEMORY_NUM) - 1) #define DIRTY_CLIENTS_NOCODE (DIRTY_CLIENTS_ALL & ~(1 << DIRTY_MEMORY_CODE)) -static inline bool cpu_physical_memory_all_dirty(ram_addr_t start, - ram_addr_t length, - unsigned client) -{ - DirtyMemoryBlocks *blocks; - unsigned long end, page; - unsigned long idx, offset, base; - bool dirty = true; - - assert(client < DIRTY_MEMORY_NUM); - - end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; - page = start >> TARGET_PAGE_BITS; - - RCU_READ_LOCK_GUARD(); - - blocks = qatomic_rcu_read(&ram_list.dirty_memory[client]); - - idx = page / DIRTY_MEMORY_BLOCK_SIZE; - offset = page % DIRTY_MEMORY_BLOCK_SIZE; - base = page - offset; - while (page < end) { - unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE); - unsigned long num = next - base; - unsigned long found = find_next_zero_bit(blocks->blocks[idx], num, offset); - if (found < num) { - dirty = false; - break; - } - - page = next; - idx++; - offset = 0; - base += DIRTY_MEMORY_BLOCK_SIZE; - } - - return dirty; -} - bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, unsigned client); bool cpu_physical_memory_is_clean(ram_addr_t addr); -static inline uint8_t cpu_physical_memory_range_includes_clean(ram_addr_t start, - ram_addr_t length, - uint8_t mask) -{ - uint8_t ret = 0; - - if (mask & (1 << DIRTY_MEMORY_VGA) && - !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_VGA)) { - ret |= (1 << DIRTY_MEMORY_VGA); - } - if (mask & (1 << DIRTY_MEMORY_CODE) && - !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_CODE)) { - ret |= (1 << DIRTY_MEMORY_CODE); - } - if (mask & (1 << DIRTY_MEMORY_MIGRATION) && - !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_MIGRATION)) { - ret |= (1 << DIRTY_MEMORY_MIGRATION); - } - return ret; -} +uint8_t cpu_physical_memory_range_includes_clean(ram_addr_t start, + ram_addr_t length, + uint8_t mask); static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client) diff --git a/system/physmem.c b/system/physmem.c index e555b3196fa78..144fd7303bb57 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -952,6 +952,66 @@ bool cpu_physical_memory_is_clean(ram_addr_t addr) return !(vga && code && migration); } +static bool physical_memory_all_dirty(ram_addr_t start, ram_addr_t length, + unsigned client) +{ + DirtyMemoryBlocks *blocks; + unsigned long end, page; + unsigned long idx, offset, base; + bool dirty = true; + + assert(client < DIRTY_MEMORY_NUM); + + end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; + page = start >> TARGET_PAGE_BITS; + + RCU_READ_LOCK_GUARD(); + + blocks = qatomic_rcu_read(&ram_list.dirty_memory[client]); + + idx = page / DIRTY_MEMORY_BLOCK_SIZE; + offset = page % DIRTY_MEMORY_BLOCK_SIZE; + base = page - offset; + while (page < end) { + unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE); + unsigned long num = next - base; + unsigned long found = find_next_zero_bit(blocks->blocks[idx], + num, offset); + if (found < num) { + dirty = false; + break; + } + + page = next; + idx++; + offset = 0; + base += DIRTY_MEMORY_BLOCK_SIZE; + } + + return dirty; +} + +uint8_t cpu_physical_memory_range_includes_clean(ram_addr_t start, + ram_addr_t length, + uint8_t mask) +{ + uint8_t ret = 0; + + if (mask & (1 << DIRTY_MEMORY_VGA) && + !physical_memory_all_dirty(start, length, DIRTY_MEMORY_VGA)) { + ret |= (1 << DIRTY_MEMORY_VGA); + } + if (mask & (1 << DIRTY_MEMORY_CODE) && + !physical_memory_all_dirty(start, length, DIRTY_MEMORY_CODE)) { + ret |= (1 << DIRTY_MEMORY_CODE); + } + if (mask & (1 << DIRTY_MEMORY_MIGRATION) && + !physical_memory_all_dirty(start, length, DIRTY_MEMORY_MIGRATION)) { + ret |= (1 << DIRTY_MEMORY_MIGRATION); + } + return ret; +} + /* Note: start and end must be within the same ram block. */ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, ram_addr_t length, From 62c889eb7dfd4da5e30e9496496917bad53851fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 13:38:52 +0200 Subject: [PATCH 1307/1794] system/physmem: Un-inline cpu_physical_memory_set_dirty_flag() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid maintaining large functions in header, rely on the linker to optimize at linking time. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251001175448.18933-11-philmd@linaro.org> --- include/system/ram_addr.h | 19 +------------------ system/physmem.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index 2dcca260b2be6..81d26eb149266 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -150,24 +150,7 @@ uint8_t cpu_physical_memory_range_includes_clean(ram_addr_t start, ram_addr_t length, uint8_t mask); -static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, - unsigned client) -{ - unsigned long page, idx, offset; - DirtyMemoryBlocks *blocks; - - assert(client < DIRTY_MEMORY_NUM); - - page = addr >> TARGET_PAGE_BITS; - idx = page / DIRTY_MEMORY_BLOCK_SIZE; - offset = page % DIRTY_MEMORY_BLOCK_SIZE; - - RCU_READ_LOCK_GUARD(); - - blocks = qatomic_rcu_read(&ram_list.dirty_memory[client]); - - set_bit_atomic(offset, blocks->blocks[idx]); -} +void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client); static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length, diff --git a/system/physmem.c b/system/physmem.c index 144fd7303bb57..50235e8853e40 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1012,6 +1012,24 @@ uint8_t cpu_physical_memory_range_includes_clean(ram_addr_t start, return ret; } +void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client) +{ + unsigned long page, idx, offset; + DirtyMemoryBlocks *blocks; + + assert(client < DIRTY_MEMORY_NUM); + + page = addr >> TARGET_PAGE_BITS; + idx = page / DIRTY_MEMORY_BLOCK_SIZE; + offset = page % DIRTY_MEMORY_BLOCK_SIZE; + + RCU_READ_LOCK_GUARD(); + + blocks = qatomic_rcu_read(&ram_list.dirty_memory[client]); + + set_bit_atomic(offset, blocks->blocks[idx]); +} + /* Note: start and end must be within the same ram block. */ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, ram_addr_t length, From 6290580e9b712d5cbaa1cc2a314d07cd4a6c283d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 13:40:29 +0200 Subject: [PATCH 1308/1794] system/physmem: Un-inline cpu_physical_memory_set_dirty_range() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid maintaining large functions in header, rely on the linker to optimize at linking time. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251001175448.18933-12-philmd@linaro.org> --- include/system/ram_addr.h | 53 ++------------------------------------- system/physmem.c | 51 +++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index 81d26eb149266..ca5ae8424429a 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -152,57 +152,8 @@ uint8_t cpu_physical_memory_range_includes_clean(ram_addr_t start, void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client); -static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, - ram_addr_t length, - uint8_t mask) -{ - DirtyMemoryBlocks *blocks[DIRTY_MEMORY_NUM]; - unsigned long end, page; - unsigned long idx, offset, base; - int i; - - if (!mask && !xen_enabled()) { - return; - } - - end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; - page = start >> TARGET_PAGE_BITS; - - WITH_RCU_READ_LOCK_GUARD() { - for (i = 0; i < DIRTY_MEMORY_NUM; i++) { - blocks[i] = qatomic_rcu_read(&ram_list.dirty_memory[i]); - } - - idx = page / DIRTY_MEMORY_BLOCK_SIZE; - offset = page % DIRTY_MEMORY_BLOCK_SIZE; - base = page - offset; - while (page < end) { - unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE); - - if (likely(mask & (1 << DIRTY_MEMORY_MIGRATION))) { - bitmap_set_atomic(blocks[DIRTY_MEMORY_MIGRATION]->blocks[idx], - offset, next - page); - } - if (unlikely(mask & (1 << DIRTY_MEMORY_VGA))) { - bitmap_set_atomic(blocks[DIRTY_MEMORY_VGA]->blocks[idx], - offset, next - page); - } - if (unlikely(mask & (1 << DIRTY_MEMORY_CODE))) { - bitmap_set_atomic(blocks[DIRTY_MEMORY_CODE]->blocks[idx], - offset, next - page); - } - - page = next; - idx++; - offset = 0; - base += DIRTY_MEMORY_BLOCK_SIZE; - } - } - - if (xen_enabled()) { - xen_hvm_modified_memory(start, length); - } -} +void cpu_physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length, + uint8_t mask); #if !defined(_WIN32) diff --git a/system/physmem.c b/system/physmem.c index 50235e8853e40..bcd9d6fb8f053 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1030,6 +1030,57 @@ void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client) set_bit_atomic(offset, blocks->blocks[idx]); } +void cpu_physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length, + uint8_t mask) +{ + DirtyMemoryBlocks *blocks[DIRTY_MEMORY_NUM]; + unsigned long end, page; + unsigned long idx, offset, base; + int i; + + if (!mask && !xen_enabled()) { + return; + } + + end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; + page = start >> TARGET_PAGE_BITS; + + WITH_RCU_READ_LOCK_GUARD() { + for (i = 0; i < DIRTY_MEMORY_NUM; i++) { + blocks[i] = qatomic_rcu_read(&ram_list.dirty_memory[i]); + } + + idx = page / DIRTY_MEMORY_BLOCK_SIZE; + offset = page % DIRTY_MEMORY_BLOCK_SIZE; + base = page - offset; + while (page < end) { + unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE); + + if (likely(mask & (1 << DIRTY_MEMORY_MIGRATION))) { + bitmap_set_atomic(blocks[DIRTY_MEMORY_MIGRATION]->blocks[idx], + offset, next - page); + } + if (unlikely(mask & (1 << DIRTY_MEMORY_VGA))) { + bitmap_set_atomic(blocks[DIRTY_MEMORY_VGA]->blocks[idx], + offset, next - page); + } + if (unlikely(mask & (1 << DIRTY_MEMORY_CODE))) { + bitmap_set_atomic(blocks[DIRTY_MEMORY_CODE]->blocks[idx], + offset, next - page); + } + + page = next; + idx++; + offset = 0; + base += DIRTY_MEMORY_BLOCK_SIZE; + } + } + + if (xen_enabled()) { + xen_hvm_modified_memory(start, length); + } +} + /* Note: start and end must be within the same ram block. */ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, ram_addr_t length, From 3037def8b8e91ed3ce3f711dc646e07e0ef894dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Oct 2025 19:06:19 +0200 Subject: [PATCH 1309/1794] system/physmem: Remove _WIN32 #ifdef'ry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit fb3ecb7ea40 ("exec: Exclude non portable function for MinGW") guarded cpu_physical_memory_set_dirty_lebitmap() within _WIN32 #ifdef'ry because of the non-portable ffsl() call, which was later replaced for the same reason by commit 7224f66ec3c ("exec: replace ffsl with ctzl"); we don't need that anymore. Reported-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251001175448.18933-13-philmd@linaro.org> --- include/system/ram_addr.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index ca5ae8424429a..fbf57a05b2a3e 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -155,8 +155,6 @@ void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client); void cpu_physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length, uint8_t mask); -#if !defined(_WIN32) - /* * Contrary to cpu_physical_memory_sync_dirty_bitmap() this function returns * the number of dirty pages in @bitmap passed as argument. On the other hand, @@ -265,7 +263,6 @@ uint64_t cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, return num_dirty; } -#endif /* not _WIN32 */ static inline void cpu_physical_memory_dirty_bits_cleared(ram_addr_t start, ram_addr_t length) From 198c5f707f43d970b22e9b52a0ec573aa595ef35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 13:43:30 +0200 Subject: [PATCH 1310/1794] system/physmem: Un-inline cpu_physical_memory_set_dirty_lebitmap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid maintaining large functions in header, rely on the linker to optimize at linking time. Remove the now unneeded "system/xen.h" header. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251001175448.18933-14-philmd@linaro.org> --- include/system/ram_addr.h | 102 +------------------------------------ system/physmem.c | 103 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 101 deletions(-) diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index fbf57a05b2a3e..49e9a9c66d8f4 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -19,7 +19,6 @@ #ifndef SYSTEM_RAM_ADDR_H #define SYSTEM_RAM_ADDR_H -#include "system/xen.h" #include "system/tcg.h" #include "exec/cputlb.h" #include "exec/ramlist.h" @@ -161,108 +160,9 @@ void cpu_physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length, * cpu_physical_memory_sync_dirty_bitmap() returns newly dirtied pages that * weren't set in the global migration bitmap. */ -static inline uint64_t cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, ram_addr_t start, - ram_addr_t pages) -{ - unsigned long i, j; - unsigned long page_number, c, nbits; - hwaddr addr; - ram_addr_t ram_addr; - uint64_t num_dirty = 0; - unsigned long len = (pages + HOST_LONG_BITS - 1) / HOST_LONG_BITS; - unsigned long hpratio = qemu_real_host_page_size() / TARGET_PAGE_SIZE; - unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS); - - /* start address is aligned at the start of a word? */ - if ((((page * BITS_PER_LONG) << TARGET_PAGE_BITS) == start) && - (hpratio == 1)) { - unsigned long **blocks[DIRTY_MEMORY_NUM]; - unsigned long idx; - unsigned long offset; - long k; - long nr = BITS_TO_LONGS(pages); - - idx = (start >> TARGET_PAGE_BITS) / DIRTY_MEMORY_BLOCK_SIZE; - offset = BIT_WORD((start >> TARGET_PAGE_BITS) % - DIRTY_MEMORY_BLOCK_SIZE); - - WITH_RCU_READ_LOCK_GUARD() { - for (i = 0; i < DIRTY_MEMORY_NUM; i++) { - blocks[i] = - qatomic_rcu_read(&ram_list.dirty_memory[i])->blocks; - } - - for (k = 0; k < nr; k++) { - if (bitmap[k]) { - unsigned long temp = leul_to_cpu(bitmap[k]); - - nbits = ctpopl(temp); - qatomic_or(&blocks[DIRTY_MEMORY_VGA][idx][offset], temp); - - if (global_dirty_tracking) { - qatomic_or( - &blocks[DIRTY_MEMORY_MIGRATION][idx][offset], - temp); - if (unlikely( - global_dirty_tracking & GLOBAL_DIRTY_DIRTY_RATE)) { - total_dirty_pages += nbits; - } - } - - num_dirty += nbits; - - if (tcg_enabled()) { - qatomic_or(&blocks[DIRTY_MEMORY_CODE][idx][offset], - temp); - } - } - - if (++offset >= BITS_TO_LONGS(DIRTY_MEMORY_BLOCK_SIZE)) { - offset = 0; - idx++; - } - } - } - - if (xen_enabled()) { - xen_hvm_modified_memory(start, pages << TARGET_PAGE_BITS); - } - } else { - uint8_t clients = tcg_enabled() ? DIRTY_CLIENTS_ALL : DIRTY_CLIENTS_NOCODE; - - if (!global_dirty_tracking) { - clients &= ~(1 << DIRTY_MEMORY_MIGRATION); - } - - /* - * bitmap-traveling is faster than memory-traveling (for addr...) - * especially when most of the memory is not dirty. - */ - for (i = 0; i < len; i++) { - if (bitmap[i] != 0) { - c = leul_to_cpu(bitmap[i]); - nbits = ctpopl(c); - if (unlikely(global_dirty_tracking & GLOBAL_DIRTY_DIRTY_RATE)) { - total_dirty_pages += nbits; - } - num_dirty += nbits; - do { - j = ctzl(c); - c &= ~(1ul << j); - page_number = (i * HOST_LONG_BITS + j) * hpratio; - addr = page_number * TARGET_PAGE_SIZE; - ram_addr = start + addr; - cpu_physical_memory_set_dirty_range(ram_addr, - TARGET_PAGE_SIZE * hpratio, clients); - } while (c != 0); - } - } - } - - return num_dirty; -} + ram_addr_t pages); static inline void cpu_physical_memory_dirty_bits_cleared(ram_addr_t start, ram_addr_t length) diff --git a/system/physmem.c b/system/physmem.c index bcd9d6fb8f053..e85552483a8d2 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1205,6 +1205,109 @@ bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, return false; } +uint64_t cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, + ram_addr_t start, + ram_addr_t pages) +{ + unsigned long i, j; + unsigned long page_number, c, nbits; + hwaddr addr; + ram_addr_t ram_addr; + uint64_t num_dirty = 0; + unsigned long len = (pages + HOST_LONG_BITS - 1) / HOST_LONG_BITS; + unsigned long hpratio = qemu_real_host_page_size() / TARGET_PAGE_SIZE; + unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS); + + /* start address is aligned at the start of a word? */ + if ((((page * BITS_PER_LONG) << TARGET_PAGE_BITS) == start) && + (hpratio == 1)) { + unsigned long **blocks[DIRTY_MEMORY_NUM]; + unsigned long idx; + unsigned long offset; + long k; + long nr = BITS_TO_LONGS(pages); + + idx = (start >> TARGET_PAGE_BITS) / DIRTY_MEMORY_BLOCK_SIZE; + offset = BIT_WORD((start >> TARGET_PAGE_BITS) % + DIRTY_MEMORY_BLOCK_SIZE); + + WITH_RCU_READ_LOCK_GUARD() { + for (i = 0; i < DIRTY_MEMORY_NUM; i++) { + blocks[i] = + qatomic_rcu_read(&ram_list.dirty_memory[i])->blocks; + } + + for (k = 0; k < nr; k++) { + if (bitmap[k]) { + unsigned long temp = leul_to_cpu(bitmap[k]); + + nbits = ctpopl(temp); + qatomic_or(&blocks[DIRTY_MEMORY_VGA][idx][offset], temp); + + if (global_dirty_tracking) { + qatomic_or( + &blocks[DIRTY_MEMORY_MIGRATION][idx][offset], + temp); + if (unlikely( + global_dirty_tracking & GLOBAL_DIRTY_DIRTY_RATE)) { + total_dirty_pages += nbits; + } + } + + num_dirty += nbits; + + if (tcg_enabled()) { + qatomic_or(&blocks[DIRTY_MEMORY_CODE][idx][offset], + temp); + } + } + + if (++offset >= BITS_TO_LONGS(DIRTY_MEMORY_BLOCK_SIZE)) { + offset = 0; + idx++; + } + } + } + + if (xen_enabled()) { + xen_hvm_modified_memory(start, pages << TARGET_PAGE_BITS); + } + } else { + uint8_t clients = tcg_enabled() ? DIRTY_CLIENTS_ALL + : DIRTY_CLIENTS_NOCODE; + + if (!global_dirty_tracking) { + clients &= ~(1 << DIRTY_MEMORY_MIGRATION); + } + + /* + * bitmap-traveling is faster than memory-traveling (for addr...) + * especially when most of the memory is not dirty. + */ + for (i = 0; i < len; i++) { + if (bitmap[i] != 0) { + c = leul_to_cpu(bitmap[i]); + nbits = ctpopl(c); + if (unlikely(global_dirty_tracking & GLOBAL_DIRTY_DIRTY_RATE)) { + total_dirty_pages += nbits; + } + num_dirty += nbits; + do { + j = ctzl(c); + c &= ~(1ul << j); + page_number = (i * HOST_LONG_BITS + j) * hpratio; + addr = page_number * TARGET_PAGE_SIZE; + ram_addr = start + addr; + cpu_physical_memory_set_dirty_range(ram_addr, + TARGET_PAGE_SIZE * hpratio, clients); + } while (c != 0); + } + } + } + + return num_dirty; +} + static int subpage_register(subpage_t *mmio, uint32_t start, uint32_t end, uint16_t section); static subpage_t *subpage_init(FlatView *fv, hwaddr base); From 57f3d859bf947cdef418dec14445b1f8935f2d20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 13:53:07 +0200 Subject: [PATCH 1311/1794] system/physmem: Un-inline cpu_physical_memory_dirty_bits_cleared() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid maintaining large functions in header, rely on the linker to optimize at linking time. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251001175448.18933-15-philmd@linaro.org> --- include/system/ram_addr.h | 10 +--------- system/physmem.c | 7 +++++++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index 49e9a9c66d8f4..54b5f5ec16739 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -19,8 +19,6 @@ #ifndef SYSTEM_RAM_ADDR_H #define SYSTEM_RAM_ADDR_H -#include "system/tcg.h" -#include "exec/cputlb.h" #include "exec/ramlist.h" #include "system/ramblock.h" #include "system/memory.h" @@ -164,14 +162,8 @@ uint64_t cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, ram_addr_t start, ram_addr_t pages); -static inline void cpu_physical_memory_dirty_bits_cleared(ram_addr_t start, - ram_addr_t length) -{ - if (tcg_enabled()) { - tlb_reset_dirty_range_all(start, length); - } +void cpu_physical_memory_dirty_bits_cleared(ram_addr_t start, ram_addr_t length); -} bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, ram_addr_t length, unsigned client); diff --git a/system/physmem.c b/system/physmem.c index e85552483a8d2..24b654fb3f128 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -899,6 +899,13 @@ void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) } } +void cpu_physical_memory_dirty_bits_cleared(ram_addr_t start, ram_addr_t length) +{ + if (tcg_enabled()) { + tlb_reset_dirty_range_all(start, length); + } +} + static bool physical_memory_get_dirty(ram_addr_t start, ram_addr_t length, unsigned client) { From 1085a44ebb55346b30888ce1b7b18565a719f78b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 13:55:15 +0200 Subject: [PATCH 1312/1794] system/physmem: Reduce cpu_physical_memory_clear_dirty_range() scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_physical_memory_clear_dirty_range() is now only called within system/physmem.c, by qemu_ram_resize(). Reduce its scope by making it internal to this file. Since it doesn't involve any CPU, remove the 'cpu_' prefix. As it operates on a range, rename @start as @addr. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251001175448.18933-16-philmd@linaro.org> --- include/system/ram_addr.h | 9 --------- system/physmem.c | 9 ++++++++- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index 54b5f5ec16739..cafd258580edb 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -175,15 +175,6 @@ bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, ram_addr_t start, ram_addr_t length); -static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, - ram_addr_t length) -{ - cpu_physical_memory_test_and_clear_dirty(start, length, DIRTY_MEMORY_MIGRATION); - cpu_physical_memory_test_and_clear_dirty(start, length, DIRTY_MEMORY_VGA); - cpu_physical_memory_test_and_clear_dirty(start, length, DIRTY_MEMORY_CODE); -} - - /* Called with RCU critical section */ static inline uint64_t cpu_physical_memory_sync_dirty_bitmap(RAMBlock *rb, diff --git a/system/physmem.c b/system/physmem.c index 24b654fb3f128..079bdbff30f94 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1137,6 +1137,13 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, return dirty; } +static void physical_memory_clear_dirty_range(ram_addr_t addr, ram_addr_t length) +{ + cpu_physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_MIGRATION); + cpu_physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_VGA); + cpu_physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_CODE); +} + DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty (MemoryRegion *mr, hwaddr offset, hwaddr length, unsigned client) { @@ -2071,7 +2078,7 @@ int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp) ram_block_notify_resize(block->host, oldsize, newsize); } - cpu_physical_memory_clear_dirty_range(block->offset, block->used_length); + physical_memory_clear_dirty_range(block->offset, block->used_length); block->used_length = newsize; cpu_physical_memory_set_dirty_range(block->offset, block->used_length, DIRTY_CLIENTS_ALL); From 8bf3a883088b65430f58aeb3d2d1db87f01eabec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Sep 2025 13:58:03 +0200 Subject: [PATCH 1313/1794] system/physmem: Reduce cpu_physical_memory_sync_dirty_bitmap() scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_physical_memory_sync_dirty_bitmap() is now only called within system/physmem.c, by ramblock_sync_dirty_bitmap(). Reduce its scope by making it internal to this file. Since it doesn't involve any CPU, remove the 'cpu_' prefix. Remove the now unneeded "qemu/rcu.h" and "system/memory.h" headers. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251001175448.18933-17-philmd@linaro.org> --- include/system/ram_addr.h | 79 --------------------------------------- migration/ram.c | 77 +++++++++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 80 deletions(-) diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index cafd258580edb..d2d088bbea69c 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -21,10 +21,7 @@ #include "exec/ramlist.h" #include "system/ramblock.h" -#include "system/memory.h" #include "exec/target_page.h" -#include "qemu/rcu.h" - #include "exec/hwaddr.h" extern uint64_t total_dirty_pages; @@ -175,80 +172,4 @@ bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, ram_addr_t start, ram_addr_t length); -/* Called with RCU critical section */ -static inline -uint64_t cpu_physical_memory_sync_dirty_bitmap(RAMBlock *rb, - ram_addr_t start, - ram_addr_t length) -{ - ram_addr_t addr; - unsigned long word = BIT_WORD((start + rb->offset) >> TARGET_PAGE_BITS); - uint64_t num_dirty = 0; - unsigned long *dest = rb->bmap; - - /* start address and length is aligned at the start of a word? */ - if (((word * BITS_PER_LONG) << TARGET_PAGE_BITS) == - (start + rb->offset) && - !(length & ((BITS_PER_LONG << TARGET_PAGE_BITS) - 1))) { - int k; - int nr = BITS_TO_LONGS(length >> TARGET_PAGE_BITS); - unsigned long * const *src; - unsigned long idx = (word * BITS_PER_LONG) / DIRTY_MEMORY_BLOCK_SIZE; - unsigned long offset = BIT_WORD((word * BITS_PER_LONG) % - DIRTY_MEMORY_BLOCK_SIZE); - unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS); - - src = qatomic_rcu_read( - &ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION])->blocks; - - for (k = page; k < page + nr; k++) { - if (src[idx][offset]) { - unsigned long bits = qatomic_xchg(&src[idx][offset], 0); - unsigned long new_dirty; - new_dirty = ~dest[k]; - dest[k] |= bits; - new_dirty &= bits; - num_dirty += ctpopl(new_dirty); - } - - if (++offset >= BITS_TO_LONGS(DIRTY_MEMORY_BLOCK_SIZE)) { - offset = 0; - idx++; - } - } - if (num_dirty) { - cpu_physical_memory_dirty_bits_cleared(start, length); - } - - if (rb->clear_bmap) { - /* - * Postpone the dirty bitmap clear to the point before we - * really send the pages, also we will split the clear - * dirty procedure into smaller chunks. - */ - clear_bmap_set(rb, start >> TARGET_PAGE_BITS, - length >> TARGET_PAGE_BITS); - } else { - /* Slow path - still do that in a huge chunk */ - memory_region_clear_dirty_bitmap(rb->mr, start, length); - } - } else { - ram_addr_t offset = rb->offset; - - for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) { - if (cpu_physical_memory_test_and_clear_dirty( - start + addr + offset, - TARGET_PAGE_SIZE, - DIRTY_MEMORY_MIGRATION)) { - long k = (start + addr) >> TARGET_PAGE_BITS; - if (!test_and_set_bit(k, dest)) { - num_dirty++; - } - } - } - } - - return num_dirty; -} - #endif diff --git a/migration/ram.c b/migration/ram.c index 189931d24dc01..f7c165dfb3569 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -936,11 +936,86 @@ bool ramblock_page_is_discarded(RAMBlock *rb, ram_addr_t start) return false; } +/* Called with RCU critical section */ +static uint64_t physical_memory_sync_dirty_bitmap(RAMBlock *rb, + ram_addr_t start, + ram_addr_t length) +{ + ram_addr_t addr; + unsigned long word = BIT_WORD((start + rb->offset) >> TARGET_PAGE_BITS); + uint64_t num_dirty = 0; + unsigned long *dest = rb->bmap; + + /* start address and length is aligned at the start of a word? */ + if (((word * BITS_PER_LONG) << TARGET_PAGE_BITS) == + (start + rb->offset) && + !(length & ((BITS_PER_LONG << TARGET_PAGE_BITS) - 1))) { + int k; + int nr = BITS_TO_LONGS(length >> TARGET_PAGE_BITS); + unsigned long * const *src; + unsigned long idx = (word * BITS_PER_LONG) / DIRTY_MEMORY_BLOCK_SIZE; + unsigned long offset = BIT_WORD((word * BITS_PER_LONG) % + DIRTY_MEMORY_BLOCK_SIZE); + unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS); + + src = qatomic_rcu_read( + &ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION])->blocks; + + for (k = page; k < page + nr; k++) { + if (src[idx][offset]) { + unsigned long bits = qatomic_xchg(&src[idx][offset], 0); + unsigned long new_dirty; + new_dirty = ~dest[k]; + dest[k] |= bits; + new_dirty &= bits; + num_dirty += ctpopl(new_dirty); + } + + if (++offset >= BITS_TO_LONGS(DIRTY_MEMORY_BLOCK_SIZE)) { + offset = 0; + idx++; + } + } + if (num_dirty) { + cpu_physical_memory_dirty_bits_cleared(start, length); + } + + if (rb->clear_bmap) { + /* + * Postpone the dirty bitmap clear to the point before we + * really send the pages, also we will split the clear + * dirty procedure into smaller chunks. + */ + clear_bmap_set(rb, start >> TARGET_PAGE_BITS, + length >> TARGET_PAGE_BITS); + } else { + /* Slow path - still do that in a huge chunk */ + memory_region_clear_dirty_bitmap(rb->mr, start, length); + } + } else { + ram_addr_t offset = rb->offset; + + for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) { + if (cpu_physical_memory_test_and_clear_dirty( + start + addr + offset, + TARGET_PAGE_SIZE, + DIRTY_MEMORY_MIGRATION)) { + long k = (start + addr) >> TARGET_PAGE_BITS; + if (!test_and_set_bit(k, dest)) { + num_dirty++; + } + } + } + } + + return num_dirty; +} + /* Called with RCU critical section */ static void ramblock_sync_dirty_bitmap(RAMState *rs, RAMBlock *rb) { uint64_t new_dirty_pages = - cpu_physical_memory_sync_dirty_bitmap(rb, 0, rb->used_length); + physical_memory_sync_dirty_bitmap(rb, 0, rb->used_length); rs->migration_dirty_pages += new_dirty_pages; rs->num_dirty_pages_period += new_dirty_pages; From aa60bdb700f4afa8577a495c734870516910864f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 30 Sep 2025 09:08:44 +0200 Subject: [PATCH 1314/1794] system/physmem: Drop 'cpu_' prefix in Physical Memory API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The functions related to the Physical Memory API declared in "system/ram_addr.h" do not operate on vCPU. Remove the 'cpu_' prefix. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Message-Id: <20251001175448.18933-18-philmd@linaro.org> --- accel/kvm/kvm-all.c | 2 +- accel/tcg/cputlb.c | 12 +++++----- hw/vfio/container-legacy.c | 8 +++---- hw/vfio/container.c | 4 ++-- include/system/ram_addr.h | 24 +++++++++---------- migration/ram.c | 4 ++-- system/memory.c | 8 +++---- system/memory_ldst.c.inc | 2 +- system/physmem.c | 48 ++++++++++++++++++------------------- target/arm/tcg/mte_helper.c | 2 +- tests/tsan/ignore.tsan | 4 ++-- 11 files changed, 59 insertions(+), 59 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 08b2b5a371cf7..a7ece7db96477 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -758,7 +758,7 @@ static void kvm_slot_sync_dirty_pages(KVMSlot *slot) ram_addr_t start = slot->ram_start_offset; ram_addr_t pages = slot->memory_size / qemu_real_host_page_size(); - cpu_physical_memory_set_dirty_lebitmap(slot->dirty_bmap, start, pages); + physical_memory_set_dirty_lebitmap(slot->dirty_bmap, start, pages); } static void kvm_slot_reset_dirty_pages(KVMSlot *slot) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 2a6aa01c57c4c..a721235dea6e2 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -858,7 +858,7 @@ void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu, can be detected */ void tlb_protect_code(ram_addr_t ram_addr) { - cpu_physical_memory_test_and_clear_dirty(ram_addr & TARGET_PAGE_MASK, + physical_memory_test_and_clear_dirty(ram_addr & TARGET_PAGE_MASK, TARGET_PAGE_SIZE, DIRTY_MEMORY_CODE); } @@ -867,7 +867,7 @@ void tlb_protect_code(ram_addr_t ram_addr) tested for self modifying code */ void tlb_unprotect_code(ram_addr_t ram_addr) { - cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE); + physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE); } @@ -1085,7 +1085,7 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx, if (prot & PAGE_WRITE) { if (section->readonly) { write_flags |= TLB_DISCARD_WRITE; - } else if (cpu_physical_memory_is_clean(iotlb)) { + } else if (physical_memory_is_clean(iotlb)) { write_flags |= TLB_NOTDIRTY; } } @@ -1341,7 +1341,7 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size, trace_memory_notdirty_write_access(mem_vaddr, ram_addr, size); - if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) { + if (!physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) { tb_invalidate_phys_range_fast(cpu, ram_addr, size, retaddr); } @@ -1349,10 +1349,10 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size, * Set both VGA and migration bits for simplicity and to remove * the notdirty callback faster. */ - cpu_physical_memory_set_dirty_range(ram_addr, size, DIRTY_CLIENTS_NOCODE); + physical_memory_set_dirty_range(ram_addr, size, DIRTY_CLIENTS_NOCODE); /* We remove the notdirty callback only if the code has been flushed. */ - if (!cpu_physical_memory_is_clean(ram_addr)) { + if (!physical_memory_is_clean(ram_addr)) { trace_memory_notdirty_set_dirty(mem_vaddr); tlb_set_dirty(cpu, mem_vaddr); } diff --git a/hw/vfio/container-legacy.c b/hw/vfio/container-legacy.c index 629ff23b0ba2c..765be7f024b24 100644 --- a/hw/vfio/container-legacy.c +++ b/hw/vfio/container-legacy.c @@ -92,7 +92,7 @@ static int vfio_dma_unmap_bitmap(const VFIOLegacyContainer *container, bitmap = (struct vfio_bitmap *)&unmap->data; /* - * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of + * physical_memory_set_dirty_lebitmap() supports pages in bitmap of * qemu_real_host_page_size to mark those dirty. Hence set bitmap_pgsize * to qemu_real_host_page_size. */ @@ -108,7 +108,7 @@ static int vfio_dma_unmap_bitmap(const VFIOLegacyContainer *container, ret = ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, unmap); if (!ret) { - cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap, + physical_memory_set_dirty_lebitmap(vbmap.bitmap, iotlb->translated_addr, vbmap.pages); } else { error_report("VFIO_UNMAP_DMA with DIRTY_BITMAP : %m"); @@ -266,7 +266,7 @@ static int vfio_legacy_query_dirty_bitmap(const VFIOContainer *bcontainer, range->size = size; /* - * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of + * physical_memory_set_dirty_lebitmap() supports pages in bitmap of * qemu_real_host_page_size to mark those dirty. Hence set bitmap's pgsize * to qemu_real_host_page_size. */ @@ -485,7 +485,7 @@ static void vfio_get_iommu_info_migration(VFIOLegacyContainer *container, header); /* - * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of + * physical_memory_set_dirty_lebitmap() supports pages in bitmap of * qemu_real_host_page_size to mark those dirty. */ if (cap_mig->pgsize_bitmap & qemu_real_host_page_size()) { diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 41de343924614..3fb19a1c8ad97 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -255,7 +255,7 @@ int vfio_container_query_dirty_bitmap(const VFIOContainer *bcontainer, int ret; if (!bcontainer->dirty_pages_supported && !all_device_dirty_tracking) { - cpu_physical_memory_set_dirty_range(translated_addr, size, + physical_memory_set_dirty_range(translated_addr, size, tcg_enabled() ? DIRTY_CLIENTS_ALL : DIRTY_CLIENTS_NOCODE); return 0; @@ -280,7 +280,7 @@ int vfio_container_query_dirty_bitmap(const VFIOContainer *bcontainer, goto out; } - dirty_pages = cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap, + dirty_pages = physical_memory_set_dirty_lebitmap(vbmap.bitmap, translated_addr, vbmap.pages); diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index d2d088bbea69c..3894a84fb9c12 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -136,39 +136,39 @@ static inline void qemu_ram_block_writeback(RAMBlock *block) #define DIRTY_CLIENTS_ALL ((1 << DIRTY_MEMORY_NUM) - 1) #define DIRTY_CLIENTS_NOCODE (DIRTY_CLIENTS_ALL & ~(1 << DIRTY_MEMORY_CODE)) -bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, unsigned client); +bool physical_memory_get_dirty_flag(ram_addr_t addr, unsigned client); -bool cpu_physical_memory_is_clean(ram_addr_t addr); +bool physical_memory_is_clean(ram_addr_t addr); -uint8_t cpu_physical_memory_range_includes_clean(ram_addr_t start, +uint8_t physical_memory_range_includes_clean(ram_addr_t start, ram_addr_t length, uint8_t mask); -void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client); +void physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client); -void cpu_physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length, +void physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length, uint8_t mask); /* - * Contrary to cpu_physical_memory_sync_dirty_bitmap() this function returns + * Contrary to physical_memory_sync_dirty_bitmap() this function returns * the number of dirty pages in @bitmap passed as argument. On the other hand, - * cpu_physical_memory_sync_dirty_bitmap() returns newly dirtied pages that + * physical_memory_sync_dirty_bitmap() returns newly dirtied pages that * weren't set in the global migration bitmap. */ -uint64_t cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, +uint64_t physical_memory_set_dirty_lebitmap(unsigned long *bitmap, ram_addr_t start, ram_addr_t pages); -void cpu_physical_memory_dirty_bits_cleared(ram_addr_t start, ram_addr_t length); +void physical_memory_dirty_bits_cleared(ram_addr_t start, ram_addr_t length); -bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, +bool physical_memory_test_and_clear_dirty(ram_addr_t start, ram_addr_t length, unsigned client); -DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty +DirtyBitmapSnapshot *physical_memory_snapshot_and_clear_dirty (MemoryRegion *mr, hwaddr offset, hwaddr length, unsigned client); -bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, +bool physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, ram_addr_t start, ram_addr_t length); diff --git a/migration/ram.c b/migration/ram.c index f7c165dfb3569..ba016542288c9 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -977,7 +977,7 @@ static uint64_t physical_memory_sync_dirty_bitmap(RAMBlock *rb, } } if (num_dirty) { - cpu_physical_memory_dirty_bits_cleared(start, length); + physical_memory_dirty_bits_cleared(start, length); } if (rb->clear_bmap) { @@ -996,7 +996,7 @@ static uint64_t physical_memory_sync_dirty_bitmap(RAMBlock *rb, ram_addr_t offset = rb->offset; for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) { - if (cpu_physical_memory_test_and_clear_dirty( + if (physical_memory_test_and_clear_dirty( start + addr + offset, TARGET_PAGE_SIZE, DIRTY_MEMORY_MIGRATION)) { diff --git a/system/memory.c b/system/memory.c index 41797ceef4e13..5c14e56ebb1da 100644 --- a/system/memory.c +++ b/system/memory.c @@ -2271,7 +2271,7 @@ void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size) { assert(mr->ram_block); - cpu_physical_memory_set_dirty_range(memory_region_get_ram_addr(mr) + addr, + physical_memory_set_dirty_range(memory_region_get_ram_addr(mr) + addr, size, memory_region_get_dirty_log_mask(mr)); } @@ -2375,7 +2375,7 @@ DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr, DirtyBitmapSnapshot *snapshot; assert(mr->ram_block); memory_region_sync_dirty_bitmap(mr, false); - snapshot = cpu_physical_memory_snapshot_and_clear_dirty(mr, addr, size, client); + snapshot = physical_memory_snapshot_and_clear_dirty(mr, addr, size, client); memory_global_after_dirty_log_sync(); return snapshot; } @@ -2384,7 +2384,7 @@ bool memory_region_snapshot_get_dirty(MemoryRegion *mr, DirtyBitmapSnapshot *sna hwaddr addr, hwaddr size) { assert(mr->ram_block); - return cpu_physical_memory_snapshot_get_dirty(snap, + return physical_memory_snapshot_get_dirty(snap, memory_region_get_ram_addr(mr) + addr, size); } @@ -2422,7 +2422,7 @@ void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size, unsigned client) { assert(mr->ram_block); - cpu_physical_memory_test_and_clear_dirty( + physical_memory_test_and_clear_dirty( memory_region_get_ram_addr(mr) + addr, size, client); } diff --git a/system/memory_ldst.c.inc b/system/memory_ldst.c.inc index 7f32d3d9ff39d..333da209d1abc 100644 --- a/system/memory_ldst.c.inc +++ b/system/memory_ldst.c.inc @@ -287,7 +287,7 @@ void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, dirty_log_mask = memory_region_get_dirty_log_mask(mr); dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE); - cpu_physical_memory_set_dirty_range(memory_region_get_ram_addr(mr) + addr, + physical_memory_set_dirty_range(memory_region_get_ram_addr(mr) + addr, 4, dirty_log_mask); r = MEMTX_OK; } diff --git a/system/physmem.c b/system/physmem.c index 079bdbff30f94..347119b8c2ac4 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -899,7 +899,7 @@ void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) } } -void cpu_physical_memory_dirty_bits_cleared(ram_addr_t start, ram_addr_t length) +void physical_memory_dirty_bits_cleared(ram_addr_t start, ram_addr_t length) { if (tcg_enabled()) { tlb_reset_dirty_range_all(start, length); @@ -945,17 +945,17 @@ static bool physical_memory_get_dirty(ram_addr_t start, ram_addr_t length, return dirty; } -bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, unsigned client) +bool physical_memory_get_dirty_flag(ram_addr_t addr, unsigned client) { return physical_memory_get_dirty(addr, 1, client); } -bool cpu_physical_memory_is_clean(ram_addr_t addr) +bool physical_memory_is_clean(ram_addr_t addr) { - bool vga = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_VGA); - bool code = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_CODE); + bool vga = physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_VGA); + bool code = physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_CODE); bool migration = - cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_MIGRATION); + physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_MIGRATION); return !(vga && code && migration); } @@ -998,7 +998,7 @@ static bool physical_memory_all_dirty(ram_addr_t start, ram_addr_t length, return dirty; } -uint8_t cpu_physical_memory_range_includes_clean(ram_addr_t start, +uint8_t physical_memory_range_includes_clean(ram_addr_t start, ram_addr_t length, uint8_t mask) { @@ -1019,7 +1019,7 @@ uint8_t cpu_physical_memory_range_includes_clean(ram_addr_t start, return ret; } -void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client) +void physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client) { unsigned long page, idx, offset; DirtyMemoryBlocks *blocks; @@ -1037,7 +1037,7 @@ void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client) set_bit_atomic(offset, blocks->blocks[idx]); } -void cpu_physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length, +void physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length, uint8_t mask) { DirtyMemoryBlocks *blocks[DIRTY_MEMORY_NUM]; @@ -1089,7 +1089,7 @@ void cpu_physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length, } /* Note: start and end must be within the same ram block. */ -bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, +bool physical_memory_test_and_clear_dirty(ram_addr_t start, ram_addr_t length, unsigned client) { @@ -1131,7 +1131,7 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, } if (dirty) { - cpu_physical_memory_dirty_bits_cleared(start, length); + physical_memory_dirty_bits_cleared(start, length); } return dirty; @@ -1139,12 +1139,12 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, static void physical_memory_clear_dirty_range(ram_addr_t addr, ram_addr_t length) { - cpu_physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_MIGRATION); - cpu_physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_VGA); - cpu_physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_CODE); + physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_MIGRATION); + physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_VGA); + physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_CODE); } -DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty +DirtyBitmapSnapshot *physical_memory_snapshot_and_clear_dirty (MemoryRegion *mr, hwaddr offset, hwaddr length, unsigned client) { DirtyMemoryBlocks *blocks; @@ -1191,14 +1191,14 @@ DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty } } - cpu_physical_memory_dirty_bits_cleared(start, length); + physical_memory_dirty_bits_cleared(start, length); memory_region_clear_dirty_bitmap(mr, offset, length); return snap; } -bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, +bool physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, ram_addr_t start, ram_addr_t length) { @@ -1219,7 +1219,7 @@ bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, return false; } -uint64_t cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, +uint64_t physical_memory_set_dirty_lebitmap(unsigned long *bitmap, ram_addr_t start, ram_addr_t pages) { @@ -1312,7 +1312,7 @@ uint64_t cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, page_number = (i * HOST_LONG_BITS + j) * hpratio; addr = page_number * TARGET_PAGE_SIZE; ram_addr = start + addr; - cpu_physical_memory_set_dirty_range(ram_addr, + physical_memory_set_dirty_range(ram_addr, TARGET_PAGE_SIZE * hpratio, clients); } while (c != 0); } @@ -2080,7 +2080,7 @@ int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp) physical_memory_clear_dirty_range(block->offset, block->used_length); block->used_length = newsize; - cpu_physical_memory_set_dirty_range(block->offset, block->used_length, + physical_memory_set_dirty_range(block->offset, block->used_length, DIRTY_CLIENTS_ALL); memory_region_set_size(block->mr, unaligned_size); if (block->resized) { @@ -2285,7 +2285,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) ram_list.version++; qemu_mutex_unlock_ramlist(); - cpu_physical_memory_set_dirty_range(new_block->offset, + physical_memory_set_dirty_range(new_block->offset, new_block->used_length, DIRTY_CLIENTS_ALL); @@ -3134,19 +3134,19 @@ static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr, addr += ramaddr; /* No early return if dirty_log_mask is or becomes 0, because - * cpu_physical_memory_set_dirty_range will still call + * physical_memory_set_dirty_range will still call * xen_modified_memory. */ if (dirty_log_mask) { dirty_log_mask = - cpu_physical_memory_range_includes_clean(addr, length, dirty_log_mask); + physical_memory_range_includes_clean(addr, length, dirty_log_mask); } if (dirty_log_mask & (1 << DIRTY_MEMORY_CODE)) { assert(tcg_enabled()); tb_invalidate_phys_range(NULL, addr, addr + length - 1); dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE); } - cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask); + physical_memory_set_dirty_range(addr, length, dirty_log_mask); } void memory_region_flush_rom_device(MemoryRegion *mr, hwaddr addr, hwaddr size) diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 7d80244788e52..077ff4b2b2c8f 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -189,7 +189,7 @@ uint8_t *allocation_tag_mem_probe(CPUARMState *env, int ptr_mmu_idx, */ if (tag_access == MMU_DATA_STORE) { ram_addr_t tag_ra = memory_region_get_ram_addr(mr) + xlat; - cpu_physical_memory_set_dirty_flag(tag_ra, DIRTY_MEMORY_MIGRATION); + physical_memory_set_dirty_flag(tag_ra, DIRTY_MEMORY_MIGRATION); } return memory_region_get_ram_ptr(mr) + xlat; diff --git a/tests/tsan/ignore.tsan b/tests/tsan/ignore.tsan index 423e482d2f9ec..8fa00a2c49baf 100644 --- a/tests/tsan/ignore.tsan +++ b/tests/tsan/ignore.tsan @@ -4,7 +4,7 @@ # The eventual goal would be to fix these warnings. # TSan is not happy about setting/getting of dirty bits, -# for example, cpu_physical_memory_set_dirty_range, -# and cpu_physical_memory_get_dirty. +# for example, physical_memory_set_dirty_range, +# and physical_memory_get_dirty. src:bitops.c src:bitmap.c From 4db362f68c7cd28a6b1aa2f9ada68aa3556f9613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 30 Sep 2025 09:08:54 +0200 Subject: [PATCH 1315/1794] system/physmem: Extract API out of 'system/ram_addr.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Very few files use the Physical Memory API. Declare its methods in their own header: "system/physmem.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Message-Id: <20251001175448.18933-19-philmd@linaro.org> --- MAINTAINERS | 1 + accel/kvm/kvm-all.c | 2 +- accel/tcg/cputlb.c | 1 + hw/vfio/container-legacy.c | 2 +- hw/vfio/container.c | 1 + hw/vfio/listener.c | 1 - include/system/physmem.h | 54 +++++++++++++++++++++++++++++++++++++ include/system/ram_addr.h | 40 --------------------------- migration/ram.c | 1 + system/memory.c | 1 + system/physmem.c | 1 + target/arm/tcg/mte_helper.c | 2 +- 12 files changed, 63 insertions(+), 44 deletions(-) create mode 100644 include/system/physmem.h diff --git a/MAINTAINERS b/MAINTAINERS index 75e1fa5c307ea..43d8d0bab5429 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3223,6 +3223,7 @@ S: Supported F: include/system/ioport.h F: include/exec/memop.h F: include/system/memory.h +F: include/system/physmem.h F: include/system/ram_addr.h F: include/system/ramblock.h F: include/system/memory_mapping.h diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index a7ece7db96477..58802f7c3cc15 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -32,13 +32,13 @@ #include "system/runstate.h" #include "system/cpus.h" #include "system/accel-blocker.h" +#include "system/physmem.h" #include "system/ramblock.h" #include "accel/accel-ops.h" #include "qemu/bswap.h" #include "exec/tswap.h" #include "exec/target_page.h" #include "system/memory.h" -#include "system/ram_addr.h" #include "qemu/event_notifier.h" #include "qemu/main-loop.h" #include "trace.h" diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index a721235dea6e2..7214d41cb5d2d 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -25,6 +25,7 @@ #include "accel/tcg/probe.h" #include "exec/page-protection.h" #include "system/memory.h" +#include "system/physmem.h" #include "accel/tcg/cpu-ldst-common.h" #include "accel/tcg/cpu-mmu-index.h" #include "exec/cputlb.h" diff --git a/hw/vfio/container-legacy.c b/hw/vfio/container-legacy.c index 765be7f024b24..a3615d7b5dba8 100644 --- a/hw/vfio/container-legacy.c +++ b/hw/vfio/container-legacy.c @@ -25,7 +25,7 @@ #include "hw/vfio/vfio-device.h" #include "system/address-spaces.h" #include "system/memory.h" -#include "system/ram_addr.h" +#include "system/physmem.h" #include "qemu/error-report.h" #include "qemu/range.h" #include "system/reset.h" diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 3fb19a1c8ad97..9ddec300e35c7 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -20,6 +20,7 @@ #include "qemu/error-report.h" #include "hw/vfio/vfio-container.h" #include "hw/vfio/vfio-device.h" /* vfio_device_reset_handler */ +#include "system/physmem.h" #include "system/reset.h" #include "vfio-helpers.h" diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index b5cefc9395c93..c6bb58f5209a4 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -30,7 +30,6 @@ #include "hw/vfio/pci.h" #include "system/address-spaces.h" #include "system/memory.h" -#include "system/ram_addr.h" #include "hw/hw.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" diff --git a/include/system/physmem.h b/include/system/physmem.h new file mode 100644 index 0000000000000..879f6eae38b07 --- /dev/null +++ b/include/system/physmem.h @@ -0,0 +1,54 @@ +/* + * QEMU physical memory interfaces (target independent). + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef QEMU_SYSTEM_PHYSMEM_H +#define QEMU_SYSTEM_PHYSMEM_H + +#include "exec/hwaddr.h" +#include "exec/ramlist.h" + +#define DIRTY_CLIENTS_ALL ((1 << DIRTY_MEMORY_NUM) - 1) +#define DIRTY_CLIENTS_NOCODE (DIRTY_CLIENTS_ALL & ~(1 << DIRTY_MEMORY_CODE)) + +bool physical_memory_get_dirty_flag(ram_addr_t addr, unsigned client); + +bool physical_memory_is_clean(ram_addr_t addr); + +uint8_t physical_memory_range_includes_clean(ram_addr_t start, + ram_addr_t length, + uint8_t mask); + +void physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client); + +void physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length, + uint8_t mask); + +/* + * Contrary to physical_memory_sync_dirty_bitmap() this function returns + * the number of dirty pages in @bitmap passed as argument. On the other hand, + * physical_memory_sync_dirty_bitmap() returns newly dirtied pages that + * weren't set in the global migration bitmap. + */ +uint64_t physical_memory_set_dirty_lebitmap(unsigned long *bitmap, + ram_addr_t start, + ram_addr_t pages); + +void physical_memory_dirty_bits_cleared(ram_addr_t start, ram_addr_t length); + +bool physical_memory_test_and_clear_dirty(ram_addr_t start, + ram_addr_t length, + unsigned client); + +DirtyBitmapSnapshot * +physical_memory_snapshot_and_clear_dirty(MemoryRegion *mr, hwaddr offset, + hwaddr length, unsigned client); + +bool physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, + ram_addr_t start, + ram_addr_t length); + +#endif diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index 3894a84fb9c12..683485980cea6 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -19,7 +19,6 @@ #ifndef SYSTEM_RAM_ADDR_H #define SYSTEM_RAM_ADDR_H -#include "exec/ramlist.h" #include "system/ramblock.h" #include "exec/target_page.h" #include "exec/hwaddr.h" @@ -133,43 +132,4 @@ static inline void qemu_ram_block_writeback(RAMBlock *block) qemu_ram_msync(block, 0, block->used_length); } -#define DIRTY_CLIENTS_ALL ((1 << DIRTY_MEMORY_NUM) - 1) -#define DIRTY_CLIENTS_NOCODE (DIRTY_CLIENTS_ALL & ~(1 << DIRTY_MEMORY_CODE)) - -bool physical_memory_get_dirty_flag(ram_addr_t addr, unsigned client); - -bool physical_memory_is_clean(ram_addr_t addr); - -uint8_t physical_memory_range_includes_clean(ram_addr_t start, - ram_addr_t length, - uint8_t mask); - -void physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client); - -void physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length, - uint8_t mask); - -/* - * Contrary to physical_memory_sync_dirty_bitmap() this function returns - * the number of dirty pages in @bitmap passed as argument. On the other hand, - * physical_memory_sync_dirty_bitmap() returns newly dirtied pages that - * weren't set in the global migration bitmap. - */ -uint64_t physical_memory_set_dirty_lebitmap(unsigned long *bitmap, - ram_addr_t start, - ram_addr_t pages); - -void physical_memory_dirty_bits_cleared(ram_addr_t start, ram_addr_t length); - -bool physical_memory_test_and_clear_dirty(ram_addr_t start, - ram_addr_t length, - unsigned client); - -DirtyBitmapSnapshot *physical_memory_snapshot_and_clear_dirty - (MemoryRegion *mr, hwaddr offset, hwaddr length, unsigned client); - -bool physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, - ram_addr_t start, - ram_addr_t length); - #endif diff --git a/migration/ram.c b/migration/ram.c index ba016542288c9..5eef2efc781f1 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -53,6 +53,7 @@ #include "qemu/rcu_queue.h" #include "migration/colo.h" #include "system/cpu-throttle.h" +#include "system/physmem.h" #include "system/ramblock.h" #include "savevm.h" #include "qemu/iov.h" diff --git a/system/memory.c b/system/memory.c index 5c14e56ebb1da..8b84661ae36c5 100644 --- a/system/memory.c +++ b/system/memory.c @@ -25,6 +25,7 @@ #include "qemu/target-info.h" #include "qom/object.h" #include "trace.h" +#include "system/physmem.h" #include "system/ram_addr.h" #include "system/kvm.h" #include "system/runstate.h" diff --git a/system/physmem.c b/system/physmem.c index 347119b8c2ac4..a340ca3e61663 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -43,6 +43,7 @@ #include "system/kvm.h" #include "system/tcg.h" #include "system/qtest.h" +#include "system/physmem.h" #include "system/ramblock.h" #include "qemu/timer.h" #include "qemu/config-file.h" diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 077ff4b2b2c8f..b96c953f809e9 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -27,7 +27,7 @@ #include "user/cpu_loop.h" #include "user/page-protection.h" #else -#include "system/ram_addr.h" +#include "system/physmem.h" #endif #include "accel/tcg/cpu-ldst.h" #include "accel/tcg/probe.h" From 547d94ffc62a96736953c35c94c7916181805895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 1 Oct 2025 18:09:41 +0100 Subject: [PATCH 1316/1794] .gitpublish: use origin/master as default base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is very much the result of my recent fat finger but I think it's safer to assume that origin/master points to a recent commit (or at least a commit a given branch is based on) than master. Acked-by: Stefan Hajnoczi Signed-off-by: Alex Bennée Message-ID: <20251001170947.2769296-2-alex.bennee@linaro.org> --- .gitpublish | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.gitpublish b/.gitpublish index a13f8c7c0ecd9..a3adb21ffa150 100644 --- a/.gitpublish +++ b/.gitpublish @@ -4,48 +4,48 @@ # See https://github.com/stefanha/git-publish for more information # [gitpublishprofile "default"] -base = master +base = origin/master to = qemu-devel@nongnu.org cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null [gitpublishprofile "rfc"] -base = master +base = origin/master prefix = RFC PATCH to = qemu-devel@nongnu.org cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null [gitpublishprofile "stable"] -base = master +base = origin/master to = qemu-devel@nongnu.org cc = qemu-stable@nongnu.org cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null [gitpublishprofile "trivial"] -base = master +base = origin/master to = qemu-devel@nongnu.org cc = qemu-trivial@nongnu.org cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null [gitpublishprofile "block"] -base = master +base = origin/master to = qemu-devel@nongnu.org cc = qemu-block@nongnu.org cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null [gitpublishprofile "arm"] -base = master +base = origin/master to = qemu-devel@nongnu.org cc = qemu-arm@nongnu.org cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null [gitpublishprofile "s390"] -base = master +base = origin/master to = qemu-devel@nongnu.org cc = qemu-s390@nongnu.org cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null [gitpublishprofile "ppc"] -base = master +base = origin/master to = qemu-devel@nongnu.org cc = qemu-ppc@nongnu.org cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null From 43a1c177426ff1babb863b3c45281fbc9f47f130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 1 Oct 2025 18:09:42 +0100 Subject: [PATCH 1317/1794] .gitmodules: restore qemu-project mirror of u-boot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this change also reference the upstream repo. Signed-off-by: Alex Bennée Message-ID: <20251001170947.2769296-3-alex.bennee@linaro.org> --- .gitmodules | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index e27dfe8c2c11d..e51abe6525897 100644 --- a/.gitmodules +++ b/.gitmodules @@ -15,7 +15,8 @@ url = https://gitlab.com/qemu-project/qemu-palcode.git [submodule "roms/u-boot"] path = roms/u-boot - url = https://gitlab.com/qemu-project-mirrors/u-boot.git + # upstream is https://github.com/u-boot/u-boot + url = https://gitlab.com/qemu-project/u-boot.git [submodule "roms/skiboot"] path = roms/skiboot url = https://gitlab.com/qemu-project/skiboot.git From 88091789f0d6234d9050c1c7a2dfc2515044ece0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 1 Oct 2025 18:09:43 +0100 Subject: [PATCH 1318/1794] .gitmodules: restore qemu-project mirror of u-boot-sam460ex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this change also reference the upstream repo. Signed-off-by: Alex Bennée Message-ID: <20251001170947.2769296-4-alex.bennee@linaro.org> --- .gitmodules | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index e51abe6525897..c307216d173ca 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,7 +28,8 @@ url = https://gitlab.com/qemu-project/seabios-hppa.git [submodule "roms/u-boot-sam460ex"] path = roms/u-boot-sam460ex - url = https://gitlab.com/qemu-project-mirrors/u-boot-sam460ex.git + # upstream is https://github.com/zbalaton/u-boot-sam460ex + url = https://gitlab.com/qemu-project/u-boot-sam460ex.git [submodule "roms/edk2"] path = roms/edk2 url = https://gitlab.com/qemu-project/edk2.git From 3d8c04b0eee35b7345c34c5265bb0478fcfc90ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 1 Oct 2025 18:09:44 +0100 Subject: [PATCH 1319/1794] tests/lcitool: drop 64 bit guests from i686 cross build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With only TCG available we can't support 64 bit guests on a 32 bit host. Fixes: 5c27baf9519 (docs/about/deprecated: Deprecate 32-bit x86 hosts for system emulation) Reviewed-by: Marc-André Lureau Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-ID: <20251001170947.2769296-5-alex.bennee@linaro.org> --- tests/docker/dockerfiles/debian-i686-cross.docker | 2 +- tests/lcitool/refresh | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index 4e8b3a8293d24..2998764065f6c 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -178,7 +178,7 @@ ENV ABI "i686-linux-gnu" ENV MESON_OPTS "--cross-file=i686-linux-gnu" ENV RUST_TARGET "i686-unknown-linux-gnu" ENV QEMU_CONFIGURE_OPTS --cross-prefix=i686-linux-gnu- -ENV DEF_TARGET_LIST x86_64-softmmu,x86_64-linux-user,i386-softmmu,i386-linux-user +ENV DEF_TARGET_LIST i386-softmmu,i386-linux-user # As a final step configure the user (if env is defined) ARG USER ARG UID diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 645959318a3ad..185a47cebe6a6 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -216,8 +216,6 @@ try: generate_dockerfile("debian-i686-cross", "debian-13", cross="i686", trailer=cross_build("i686-linux-gnu-", - "x86_64-softmmu," - "x86_64-linux-user," "i386-softmmu,i386-linux-user")) # mips no longer supported in debian-13 From 07a58aac21c80c3b74b2955ea9f9ba02070be75b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 1 Oct 2025 18:09:45 +0100 Subject: [PATCH 1320/1794] tests/lcitool: bump custom runner packages to Ubuntu 24.04 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In anticipation of new runners lets move to a newer Ubuntu LTS. Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-ID: <20251001170947.2769296-6-alex.bennee@linaro.org> --- scripts/ci/setup/gitlab-runner.yml | 2 +- scripts/ci/setup/ubuntu/build-environment.yml | 12 ++++++------ ...tu-2204-aarch64.yaml => ubuntu-2404-aarch64.yaml} | 4 +++- ...ubuntu-2204-s390x.yaml => ubuntu-2404-s390x.yaml} | 4 +++- tests/lcitool/refresh | 4 ++-- 5 files changed, 15 insertions(+), 11 deletions(-) rename scripts/ci/setup/ubuntu/{ubuntu-2204-aarch64.yaml => ubuntu-2404-aarch64.yaml} (96%) rename scripts/ci/setup/ubuntu/{ubuntu-2204-s390x.yaml => ubuntu-2404-s390x.yaml} (96%) diff --git a/scripts/ci/setup/gitlab-runner.yml b/scripts/ci/setup/gitlab-runner.yml index 57e7faebf10e0..7350f6cff4a43 100644 --- a/scripts/ci/setup/gitlab-runner.yml +++ b/scripts/ci/setup/gitlab-runner.yml @@ -103,7 +103,7 @@ when: - ansible_facts['distribution'] == 'Ubuntu' - ansible_facts['architecture'] == 'aarch64' - - ansible_facts['distribution_version'] == '22.04' + - ansible_facts['distribution_version'] == '24.04' - name: Install the gitlab-runner service using its own functionality command: "/usr/bin/gitlab-runner install --user gitlab-runner --working-directory /home/gitlab-runner" diff --git a/scripts/ci/setup/ubuntu/build-environment.yml b/scripts/ci/setup/ubuntu/build-environment.yml index 56b51609e3800..6042750cb4dee 100644 --- a/scripts/ci/setup/ubuntu/build-environment.yml +++ b/scripts/ci/setup/ubuntu/build-environment.yml @@ -35,19 +35,19 @@ # the package lists are updated by "make lcitool-refresh" - name: Include package lists based on OS and architecture include_vars: - file: "ubuntu-2204-{{ ansible_facts['architecture'] }}.yaml" + file: "ubuntu-2404-{{ ansible_facts['architecture'] }}.yaml" when: - ansible_facts['distribution'] == 'Ubuntu' - - ansible_facts['distribution_version'] == '22.04' + - ansible_facts['distribution_version'] == '24.04' - - name: Install packages for QEMU on Ubuntu 22.04 + - name: Install packages for QEMU on Ubuntu 24.04 package: name: "{{ packages }}" when: - ansible_facts['distribution'] == 'Ubuntu' - - ansible_facts['distribution_version'] == '22.04' + - ansible_facts['distribution_version'] == '24.04' - - name: Install armhf cross-compile packages to build QEMU on AArch64 Ubuntu 22.04 + - name: Install armhf cross-compile packages to build QEMU on AArch64 Ubuntu 24.04 package: name: - binutils-arm-linux-gnueabihf @@ -62,6 +62,6 @@ - zlib1g-dev:armhf when: - ansible_facts['distribution'] == 'Ubuntu' - - ansible_facts['distribution_version'] == '22.04' + - ansible_facts['distribution_version'] == '24.04' - ansible_facts['architecture'] == 'aarch64' diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml b/scripts/ci/setup/ubuntu/ubuntu-2404-aarch64.yaml similarity index 96% rename from scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml rename to scripts/ci/setup/ubuntu/ubuntu-2404-aarch64.yaml index 2ca4a5392f57f..ce632d97108b9 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2404-aarch64.yaml @@ -1,12 +1,13 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool variables --host-arch aarch64 ubuntu-2204 qemu +# $ lcitool variables --host-arch aarch64 ubuntu-2404 qemu # # https://gitlab.com/libvirt/libvirt-ci packages: - bash - bc + - bindgen - bison - bsdextrautils - bzip2 @@ -92,6 +93,7 @@ packages: - libvdeplug-dev - libvirglrenderer-dev - libvte-2.91-dev + - libxdp-dev - libxen-dev - libzstd-dev - llvm diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml b/scripts/ci/setup/ubuntu/ubuntu-2404-s390x.yaml similarity index 96% rename from scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml rename to scripts/ci/setup/ubuntu/ubuntu-2404-s390x.yaml index 7198fbbcbb76f..f45f75c960209 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2404-s390x.yaml @@ -1,12 +1,13 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool variables --host-arch s390x ubuntu-2204 qemu +# $ lcitool variables --host-arch s390x ubuntu-2404 qemu # # https://gitlab.com/libvirt/libvirt-ci packages: - bash - bc + - bindgen - bison - bsdextrautils - bzip2 @@ -91,6 +92,7 @@ packages: - libvdeplug-dev - libvirglrenderer-dev - libvte-2.91-dev + - libxdp-dev - libzstd-dev - llvm - locales diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 185a47cebe6a6..056cfb6e9d79c 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -270,8 +270,8 @@ try: # # Ansible package lists # - generate_yaml("ubuntu", "ubuntu-2204", "aarch64") - generate_yaml("ubuntu", "ubuntu-2204", "s390x") + generate_yaml("ubuntu", "ubuntu-2404", "aarch64") + generate_yaml("ubuntu", "ubuntu-2404", "s390x") sys.exit(0) From eafc5f69e621725dcdcedcc1955d820af7abfc82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 1 Oct 2025 18:09:46 +0100 Subject: [PATCH 1321/1794] gitlab: move custom runners to Ubuntu 24.04 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-ID: <20251001170947.2769296-7-alex.bennee@linaro.org> --- .gitlab-ci.d/custom-runners.yml | 6 ++-- ...4-aarch32.yml => ubuntu-24.04-aarch32.yml} | 8 ++--- ...4-aarch64.yml => ubuntu-24.04-aarch64.yml} | 32 +++++++++---------- ...22.04-s390x.yml => ubuntu-24.04-s390x.yml} | 28 ++++++++-------- 4 files changed, 37 insertions(+), 37 deletions(-) rename .gitlab-ci.d/custom-runners/{ubuntu-22.04-aarch32.yml => ubuntu-24.04-aarch32.yml} (78%) rename .gitlab-ci.d/custom-runners/{ubuntu-22.04-aarch64.yml => ubuntu-24.04-aarch64.yml} (89%) rename .gitlab-ci.d/custom-runners/{ubuntu-22.04-s390x.yml => ubuntu-24.04-s390x.yml} (88%) diff --git a/.gitlab-ci.d/custom-runners.yml b/.gitlab-ci.d/custom-runners.yml index 2d493f70f7aab..3eb8216d571a3 100644 --- a/.gitlab-ci.d/custom-runners.yml +++ b/.gitlab-ci.d/custom-runners.yml @@ -29,6 +29,6 @@ junit: build/meson-logs/*.junit.xml include: - - local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml' - - local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml' - - local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml' + - local: '/.gitlab-ci.d/custom-runners/ubuntu-24.04-s390x.yml' + - local: '/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch64.yml' + - local: '/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch32.yml' diff --git a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml b/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch32.yml similarity index 78% rename from .gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml rename to .gitlab-ci.d/custom-runners/ubuntu-24.04-aarch32.yml index 8727687e2b469..75029c9187e83 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch32.yml @@ -1,13 +1,13 @@ -# All ubuntu-22.04 jobs should run successfully in an environment +# All ubuntu-24.04 jobs should run successfully in an environment # setup by the scripts/ci/setup/ubuntu/build-environment.yml task -# "Install basic packages to build QEMU on Ubuntu 22.04" +# "Install basic packages to build QEMU on Ubuntu 24.04" -ubuntu-22.04-aarch32-all: +ubuntu-24.04-aarch32-all: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_22.04 + - ubuntu_24.04 - aarch32 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' diff --git a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml b/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch64.yml similarity index 89% rename from .gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml rename to .gitlab-ci.d/custom-runners/ubuntu-24.04-aarch64.yml index ca2f1404710e3..d26c7827f45b8 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch64.yml @@ -1,13 +1,13 @@ -# All ubuntu-22.04 jobs should run successfully in an environment +# All ubuntu-24.04 jobs should run successfully in an environment # setup by the scripts/ci/setup/ubuntu/build-environment.yml task -# "Install basic packages to build QEMU on Ubuntu 22.04" +# "Install basic packages to build QEMU on Ubuntu 24.04" -ubuntu-22.04-aarch64-all-linux-static: +ubuntu-24.04-aarch64-all-linux-static: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_22.04 + - ubuntu_24.04 - aarch64 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' @@ -23,12 +23,12 @@ ubuntu-22.04-aarch64-all-linux-static: - make check-tcg - make --output-sync -j`nproc --ignore=40` check -ubuntu-22.04-aarch64-all: +ubuntu-24.04-aarch64-all: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_22.04 + - ubuntu_24.04 - aarch64 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' @@ -45,12 +45,12 @@ ubuntu-22.04-aarch64-all: - make --output-sync -j`nproc --ignore=40` - make --output-sync -j`nproc --ignore=40` check -ubuntu-22.04-aarch64-without-defaults: +ubuntu-24.04-aarch64-without-defaults: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_22.04 + - ubuntu_24.04 - aarch64 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' @@ -67,12 +67,12 @@ ubuntu-22.04-aarch64-without-defaults: - make --output-sync -j`nproc --ignore=40` - make --output-sync -j`nproc --ignore=40` check -ubuntu-22.04-aarch64-alldbg: +ubuntu-24.04-aarch64-alldbg: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_22.04 + - ubuntu_24.04 - aarch64 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' @@ -86,12 +86,12 @@ ubuntu-22.04-aarch64-alldbg: - make --output-sync -j`nproc --ignore=40` - make --output-sync -j`nproc --ignore=40` check -ubuntu-22.04-aarch64-clang: +ubuntu-24.04-aarch64-clang: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_22.04 + - ubuntu_24.04 - aarch64 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' @@ -108,11 +108,11 @@ ubuntu-22.04-aarch64-clang: - make --output-sync -j`nproc --ignore=40` - make --output-sync -j`nproc --ignore=40` check -ubuntu-22.04-aarch64-tci: +ubuntu-24.04-aarch64-tci: needs: [] stage: build tags: - - ubuntu_22.04 + - ubuntu_24.04 - aarch64 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' @@ -128,12 +128,12 @@ ubuntu-22.04-aarch64-tci: || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc --ignore=40` -ubuntu-22.04-aarch64-notcg: +ubuntu-24.04-aarch64-notcg: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_22.04 + - ubuntu_24.04 - aarch64 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' diff --git a/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml b/.gitlab-ci.d/custom-runners/ubuntu-24.04-s390x.yml similarity index 88% rename from .gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml rename to .gitlab-ci.d/custom-runners/ubuntu-24.04-s390x.yml index e62ff1763fa0d..45dbee178802b 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-24.04-s390x.yml @@ -1,13 +1,13 @@ -# All ubuntu-22.04 jobs should run successfully in an environment +# All ubuntu-24.04 jobs should run successfully in an environment # setup by the scripts/ci/setup/ubuntu/build-environment.yml task -# "Install basic packages to build QEMU on Ubuntu 22.04" +# "Install basic packages to build QEMU on Ubuntu 24.04" -ubuntu-22.04-s390x-all-linux: +ubuntu-24.04-s390x-all-linux: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_22.04 + - ubuntu_24.04 - s390x rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' @@ -21,12 +21,12 @@ ubuntu-22.04-s390x-all-linux: - make --output-sync check-tcg - make --output-sync -j`nproc` check -ubuntu-22.04-s390x-all-system: +ubuntu-24.04-s390x-all-system: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_22.04 + - ubuntu_24.04 - s390x timeout: 75m rules: @@ -42,12 +42,12 @@ ubuntu-22.04-s390x-all-system: - make --output-sync -j`nproc` - make --output-sync -j`nproc` check -ubuntu-22.04-s390x-alldbg: +ubuntu-24.04-s390x-alldbg: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_22.04 + - ubuntu_24.04 - s390x rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' @@ -65,12 +65,12 @@ ubuntu-22.04-s390x-alldbg: - make --output-sync -j`nproc` - make --output-sync -j`nproc` check -ubuntu-22.04-s390x-clang: +ubuntu-24.04-s390x-clang: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_22.04 + - ubuntu_24.04 - s390x rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' @@ -87,11 +87,11 @@ ubuntu-22.04-s390x-clang: - make --output-sync -j`nproc` - make --output-sync -j`nproc` check -ubuntu-22.04-s390x-tci: +ubuntu-24.04-s390x-tci: needs: [] stage: build tags: - - ubuntu_22.04 + - ubuntu_24.04 - s390x rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' @@ -107,12 +107,12 @@ ubuntu-22.04-s390x-tci: || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` -ubuntu-22.04-s390x-notcg: +ubuntu-24.04-s390x-notcg: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_22.04 + - ubuntu_24.04 - s390x rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' From 1f3a37ae1c170114ae2730176b4a8cba73abb620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 1 Oct 2025 18:09:47 +0100 Subject: [PATCH 1322/1794] scripts/ci: use recommended registration command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The registration-token method is being deprecated: https://docs.gitlab.com/ci/runners/new_creation_workflow/ As a result we can drop a bunch of the descriptive stuff as that is entered on the web UI. We don't need a secondary runner if we just create one with both aarch64 and aarch32 tags. Signed-off-by: Alex Bennée Message-ID: <20251001170947.2769296-8-alex.bennee@linaro.org> --- scripts/ci/setup/gitlab-runner.yml | 12 ++---------- scripts/ci/setup/vars.yml.template | 5 +++-- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/scripts/ci/setup/gitlab-runner.yml b/scripts/ci/setup/gitlab-runner.yml index 7350f6cff4a43..7025935487ae7 100644 --- a/scripts/ci/setup/gitlab-runner.yml +++ b/scripts/ci/setup/gitlab-runner.yml @@ -16,7 +16,7 @@ tasks: - debug: msg: 'Checking for a valid GitLab registration token' - failed_when: "gitlab_runner_registration_token == 'PLEASE_PROVIDE_A_VALID_TOKEN'" + failed_when: "gitlab_runner_authentication_token == 'PLEASE_PROVIDE_A_VALID_TOKEN'" - name: Create a group for the gitlab-runner service group: @@ -95,15 +95,7 @@ # Register Runners - name: Register the gitlab-runner - command: "/usr/bin/gitlab-runner register --non-interactive --url {{ gitlab_runner_server_url }} --registration-token {{ gitlab_runner_registration_token }} --executor shell --tag-list {{ ansible_facts[\"architecture\"] }},{{ ansible_facts[\"distribution\"]|lower }}_{{ ansible_facts[\"distribution_version\"] }} --description '{{ ansible_facts[\"distribution\"] }} {{ ansible_facts[\"distribution_version\"] }} {{ ansible_facts[\"architecture\"] }} ({{ ansible_facts[\"os_family\"] }})'" - - # The secondary runner will still run under the single gitlab-runner service - - name: Register secondary gitlab-runner - command: "/usr/bin/gitlab-runner register --non-interactive --url {{ gitlab_runner_server_url }} --registration-token {{ gitlab_runner_registration_token }} --executor shell --tag-list aarch32,{{ ansible_facts[\"distribution\"]|lower }}_{{ ansible_facts[\"distribution_version\"] }} --description '{{ ansible_facts[\"distribution\"] }} {{ ansible_facts[\"distribution_version\"] }} {{ ansible_facts[\"architecture\"] }} ({{ ansible_facts[\"os_family\"] }})'" - when: - - ansible_facts['distribution'] == 'Ubuntu' - - ansible_facts['architecture'] == 'aarch64' - - ansible_facts['distribution_version'] == '24.04' + command: "/usr/bin/gitlab-runner register --non-interactive --url {{ gitlab_runner_server_url }} --token {{ gitlab_runner_authentication_token }} --executor shell" - name: Install the gitlab-runner service using its own functionality command: "/usr/bin/gitlab-runner install --user gitlab-runner --working-directory /home/gitlab-runner" diff --git a/scripts/ci/setup/vars.yml.template b/scripts/ci/setup/vars.yml.template index 4b355fb80fd10..e9ddc05f3b085 100644 --- a/scripts/ci/setup/vars.yml.template +++ b/scripts/ci/setup/vars.yml.template @@ -6,5 +6,6 @@ ansible_to_gitlab_arch: x86_64: amd64 aarch64: arm64 s390x: s390x -# A unique token made available by GitLab to your project for registering runners -gitlab_runner_registration_token: PLEASE_PROVIDE_A_VALID_TOKEN +# A unique token made obtained from GitLab for each runner +# see: https://gitlab.com/PROJECT/REPO/-/runners/new +gitlab_runner_authentication_token: PLEASE_PROVIDE_A_VALID_TOKEN From 75b1786996c422878d09bd12f166004a7d32e459 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 3 Oct 2025 14:18:12 +0000 Subject: [PATCH 1323/1794] tests/functional: Re-activate the check-venv target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add check-venv target as a dependency for the functional tests. This causes Python modules listed in pythondeps.toml, under the testdeps group, to be installed when 'make check-functional{-}' is executed to prepare and run the functional tests. Suggested-by: Thomas Huth Reviewed-by: Thomas Huth Reviewed-by: Daniel P. Berrangé Signed-off-by: Gustavo Romero Message-ID: <20251003141820.85278-2-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée --- tests/Makefile.include | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 62a4fc8ed3126..e47ef4d45c993 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -105,11 +105,11 @@ check-venv: $(TESTS_VENV_TOKEN) FUNCTIONAL_TARGETS=$(patsubst %-softmmu,check-functional-%, $(filter %-softmmu,$(TARGETS))) .PHONY: $(FUNCTIONAL_TARGETS) -$(FUNCTIONAL_TARGETS): +$(FUNCTIONAL_TARGETS): check-venv @$(MAKE) SPEED=thorough $(subst -functional,-func,$@) .PHONY: check-functional -check-functional: +check-functional: check-venv @$(NINJA) precache-functional @QEMU_TEST_NO_DOWNLOAD=1 $(MAKE) SPEED=thorough check-func check-func-quick From c8f24e1b95430759f79223dc75c66fa7ec3ef2ee Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 3 Oct 2025 14:18:13 +0000 Subject: [PATCH 1324/1794] python: Install pygdbmi in meson's venv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upcoming changes in the reverse_debugging functional test to remove Avocado as a dependency will require pygdbmi for interacting with GDB, so install it in meson's venv (located in the build dir's pyvenv/). Reviewed-by: Thomas Huth Reviewed-by: Daniel P. Berrangé Signed-off-by: Gustavo Romero Message-ID: <20251003141820.85278-3-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée --- pythondeps.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pythondeps.toml b/pythondeps.toml index 16fb2a989cf34..98e99e79005b2 100644 --- a/pythondeps.toml +++ b/pythondeps.toml @@ -33,3 +33,4 @@ sphinx_rtd_theme = { accepted = ">=0.5", installed = "1.2.2" } [testdeps] qemu.qmp = { accepted = ">=0.0.3", installed = "0.0.3" } +pygdbmi = { accepted = ">=0.11.0.0", installed = "0.11.0.0" } From b46b3818cf8d838991b15f20d3cb747b71ce040e Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 3 Oct 2025 14:18:14 +0000 Subject: [PATCH 1325/1794] tests/functional: Provide GDB to the functional tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe of GDB is done in 'configure' and the full path is passed to meson.build via the -Dgdb=option. Meson then can pass the location of GDB to the functional tests via an environment variable: QEMU_TEST_GDB. Signed-off-by: Thomas Huth Reviewed-by: Alex Bennée Reviewed-by: Daniel P. Berrangé Signed-off-by: Gustavo Romero Message-ID: <20251003141820.85278-4-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée --- configure | 2 ++ meson_options.txt | 2 ++ scripts/meson-buildoptions.sh | 2 ++ tests/functional/meson.build | 6 ++++++ 4 files changed, 12 insertions(+) diff --git a/configure b/configure index 78445cbb4b374..21d1679f58e8f 100755 --- a/configure +++ b/configure @@ -2003,6 +2003,8 @@ if test "$skip_meson" = no; then test -n "${LIB_FUZZING_ENGINE+xxx}" && meson_option_add "-Dfuzzing_engine=$LIB_FUZZING_ENGINE" test "$plugins" = yes && meson_option_add "-Dplugins=true" test "$tcg" != enabled && meson_option_add "-Dtcg=$tcg" + test -n "$gdb_bin" && meson_option_add "-Dgdb=$gdb_bin" + run_meson() { NINJA=$ninja $meson setup "$@" "$PWD" "$source_path" } diff --git a/meson_options.txt b/meson_options.txt index fff1521e580de..5bb41bcbc4307 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -36,6 +36,8 @@ option('trace_file', type: 'string', value: 'trace', option('coroutine_backend', type: 'combo', choices: ['ucontext', 'sigaltstack', 'windows', 'wasm', 'auto'], value: 'auto', description: 'coroutine backend to use') +option('gdb', type: 'string', value: '', + description: 'Path to GDB') # Everything else can be set via --enable/--disable-* option # on the configure script command line. After adding an option diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 0ebe6bc52a6b9..f4bd21220ee5a 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -58,6 +58,7 @@ meson_options_help() { printf "%s\n" ' --enable-ubsan enable undefined behaviour sanitizer' printf "%s\n" ' --firmwarepath=VALUES search PATH for firmware files [share/qemu-' printf "%s\n" ' firmware]' + printf "%s\n" ' --gdb=VALUE Path to GDB' printf "%s\n" ' --iasl=VALUE Path to ACPI disassembler' printf "%s\n" ' --includedir=VALUE Header file directory [include]' printf "%s\n" ' --interp-prefix=VALUE where to find shared libraries etc., use %M for' @@ -323,6 +324,7 @@ _meson_option_parse() { --disable-fuzzing) printf "%s" -Dfuzzing=false ;; --enable-gcrypt) printf "%s" -Dgcrypt=enabled ;; --disable-gcrypt) printf "%s" -Dgcrypt=disabled ;; + --gdb=*) quote_sh "-Dgdb=$2" ;; --enable-gettext) printf "%s" -Dgettext=enabled ;; --disable-gettext) printf "%s" -Dgettext=disabled ;; --enable-gio) printf "%s" -Dgio=enabled ;; diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 2a0c5aa141807..725630d30826e 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -77,6 +77,12 @@ foreach speed : ['quick', 'thorough'] test_env.set('PYTHONPATH', meson.project_source_root() / 'python:' + meson.current_source_dir()) + # Define the GDB environment variable if gdb is available. + gdb = get_option('gdb') + if gdb != '' + test_env.set('QEMU_TEST_GDB', gdb) + endif + foreach test : target_tests testname = '@0@-@1@'.format(target_base, test) if fs.exists('generic' / 'test_' + test + '.py') From aa7b1b726a0009c9147d9e2f64e0f6e82d4394af Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 3 Oct 2025 14:18:15 +0000 Subject: [PATCH 1326/1794] tests/functional: Add GDB class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add GDB class, which provides methods to run GDB commands and capture their output. The GDB class is a wrapper around the pygdbmi module and interacts with GDB via GDB's machine interface (MI). Acked-by: Thomas Huth Reviewed-by: Daniel P. Berrangé Signed-off-by: Gustavo Romero Message-ID: <20251003141820.85278-5-gustavo.romero@linaro.org> [AJB: trimmed excess license text] Signed-off-by: Alex Bennée --- tests/functional/qemu_test/__init__.py | 1 + tests/functional/qemu_test/gdb.py | 86 ++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 tests/functional/qemu_test/gdb.py diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py index 6e666a059fc69..60d19891bfc31 100644 --- a/tests/functional/qemu_test/__init__.py +++ b/tests/functional/qemu_test/__init__.py @@ -18,3 +18,4 @@ skipIfMissingImports, skipIfOperatingSystem, skipLockedMemoryTest from .archive import archive_extract from .uncompress import uncompress +from .gdb import GDB diff --git a/tests/functional/qemu_test/gdb.py b/tests/functional/qemu_test/gdb.py new file mode 100644 index 0000000000000..558d476a68296 --- /dev/null +++ b/tests/functional/qemu_test/gdb.py @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# A simple interface module built around pygdbmi for handling GDB commands. +# +# Copyright (c) 2025 Linaro Limited +# +# Author: +# Gustavo Romero +# + +import re + + +class GDB: + """Provides methods to run and capture GDB command output.""" + + + def __init__(self, gdb_path, echo=True, suffix='# ', prompt="$ "): + from pygdbmi.gdbcontroller import GdbController + from pygdbmi.constants import GdbTimeoutError + type(self).TimeoutError = GdbTimeoutError + + gdb_cmd = [gdb_path, "-q", "--interpreter=mi2"] + self.gdbmi = GdbController(gdb_cmd) + self.echo = echo + self.suffix = suffix + self.prompt = prompt + self.response = None + self.cmd_output = None + + + def get_payload(self, response, kind): + output = [] + for o in response: + # Unpack payloads of the same type. + _type, _, payload, *_ = o.values() + if _type == kind: + output += [payload] + + # Some output lines do not end with \n but begin with it, + # so remove the leading \n and merge them with the next line + # that ends with \n. + lines = [line.lstrip('\n') for line in output] + lines = "".join(lines) + lines = lines.splitlines(keepends=True) + + return lines + + + def cli(self, cmd, timeout=32.0): + self.response = self.gdbmi.write(cmd, timeout_sec=timeout) + self.cmd_output = self.get_payload(self.response, kind="console") + if self.echo: + print(self.suffix + self.prompt + cmd) + + if len(self.cmd_output) > 0: + cmd_output = self.suffix.join(self.cmd_output) + print(self.suffix + cmd_output, end="") + + return self + + + def get_addr(self): + address_pattern = r"0x[0-9A-Fa-f]+" + cmd_output = "".join(self.cmd_output) # Concat output lines. + + match = re.search(address_pattern, cmd_output) + + return int(match[0], 16) if match else None + + + def get_log(self): + r = self.get_payload(self.response, kind="log") + r = "".join(r) + + return r + + + def get_console(self): + r = "".join(self.cmd_output) + + return r + + + def exit(self): + self.gdbmi.exit() From a0ad64c390b19242ae1d246d1ccfff77dcc752a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 3 Oct 2025 14:18:16 +0000 Subject: [PATCH 1327/1794] tests/functional: replace avocado process with subprocess MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The standard python subprocess.check_call method is better than avocado.utils.process as it doesn't require stuffing all args into a single string. Signed-off-by: Daniel P. Berrangé Reviewed-by: Alex Bennée Reviewed-by: Thomas Huth Message-ID: <20251003141820.85278-6-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée --- tests/functional/reverse_debugging.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/functional/reverse_debugging.py b/tests/functional/reverse_debugging.py index f9a1d395f1d1a..a7ff47cb90c81 100644 --- a/tests/functional/reverse_debugging.py +++ b/tests/functional/reverse_debugging.py @@ -11,6 +11,7 @@ # later. See the COPYING file in the top-level directory. import os import logging +from subprocess import check_output from qemu_test import LinuxKernelTest, get_qemu_img from qemu_test.ports import Ports @@ -100,7 +101,6 @@ def vm_get_icount(vm): def reverse_debugging(self, shift=7, args=None): from avocado.utils import gdb - from avocado.utils import process logger = logging.getLogger('replay') @@ -111,8 +111,9 @@ def reverse_debugging(self, shift=7, args=None): if qemu_img is None: self.skipTest('Could not find "qemu-img", which is required to ' 'create the temporary qcow2 image') - cmd = '%s create -f qcow2 %s 128M' % (qemu_img, image_path) - process.run(cmd) + out = check_output([qemu_img, 'create', '-f', 'qcow2', image_path, '128M'], + encoding='utf8') + logger.info("qemu-img: %s" % out) replay_path = os.path.join(self.workdir, 'replay.bin') From db205774d1b2dc8e3a0e925f090a5b2f59b9bb70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 3 Oct 2025 14:18:17 +0000 Subject: [PATCH 1328/1794] tests/functional: drop datadrainer class in reverse debugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reverse debugging test uses the avocado datadrainer class to create a background thread that reads from the console socket and dumps it via python logger. Most tests log console output as a side effect of doing calls to match strings, but this test never tries to match anything. This isn't critical, so just drop the functionality. Signed-off-by: Daniel P. Berrangé Reviewed-by: Alex Bennée Reviewed-by: Thomas Huth Message-ID: <20251003141820.85278-7-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée --- tests/functional/reverse_debugging.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/functional/reverse_debugging.py b/tests/functional/reverse_debugging.py index a7ff47cb90c81..7fd8c7607f571 100644 --- a/tests/functional/reverse_debugging.py +++ b/tests/functional/reverse_debugging.py @@ -34,8 +34,6 @@ class ReverseDebugging(LinuxKernelTest): endian_is_le = True def run_vm(self, record, shift, args, replay_path, image_path, port): - from avocado.utils import datadrainer - logger = logging.getLogger('replay') vm = self.get_vm(name='record' if record else 'replay') vm.set_console() @@ -53,10 +51,6 @@ def run_vm(self, record, shift, args, replay_path, image_path, port): if args: vm.add_args(*args) vm.launch() - console_drainer = datadrainer.LineLogger(vm.console_socket.fileno(), - logger=self.log.getChild('console'), - stop_check=(lambda : not vm.is_running())) - console_drainer.start() return vm @staticmethod From fb8f35949384c3a00ef56528cb43e851c95268e6 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 3 Oct 2025 14:18:18 +0000 Subject: [PATCH 1329/1794] tests/functional: Add decorator to skip test on missing env vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a decorator to skip tests on missing env variable(s). Multiple variable names can be provided and if one or more of them are not set in the test environment the test is skipped and the missing vars are printed out. Reviewed-by: Thomas Huth Reviewed-by: Daniel P. Berrangé Signed-off-by: Gustavo Romero Message-ID: <20251003141820.85278-8-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée --- tests/functional/qemu_test/__init__.py | 3 ++- tests/functional/qemu_test/decorators.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py index 60d19891bfc31..320193591b28f 100644 --- a/tests/functional/qemu_test/__init__.py +++ b/tests/functional/qemu_test/__init__.py @@ -15,7 +15,8 @@ from .linuxkernel import LinuxKernelTest from .decorators import skipIfMissingCommands, skipIfNotMachine, \ skipFlakyTest, skipUntrustedTest, skipBigDataTest, skipSlowTest, \ - skipIfMissingImports, skipIfOperatingSystem, skipLockedMemoryTest + skipIfMissingImports, skipIfOperatingSystem, skipLockedMemoryTest, \ + skipIfMissingEnv from .archive import archive_extract from .uncompress import uncompress from .gdb import GDB diff --git a/tests/functional/qemu_test/decorators.py b/tests/functional/qemu_test/decorators.py index c0d1567b1422c..b2392958041fb 100644 --- a/tests/functional/qemu_test/decorators.py +++ b/tests/functional/qemu_test/decorators.py @@ -11,6 +11,24 @@ 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_): + missing_vars = [] + for var in vars_: + if os.getenv(var) == None: + missing_vars.append(var) + + has_vars = True if len(missing_vars) == 0 else False + + 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: From bddce216192122c51ff7fef171a16af9da514b61 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 3 Oct 2025 14:18:19 +0000 Subject: [PATCH 1330/1794] tests/functional: Adapt reverse_debugging to run w/o Avocado MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit removes Avocado as a dependency for running the reverse_debugging test. The main benefit, beyond eliminating an extra dependency, is that there is no longer any need to handle GDB packets manually. This removes the need for ad-hoc functions dealing with endianness and arch-specific register numbers, making the test easier to read. The timeout variable is also removed, since Meson now manages timeouts automatically. reverse_debugging now uses the pygdbmi module to interact with GDB, if it is available in the test environment, otherwise the test is skipped. GDB is detect via the QEMU_TEST_GDB env. variable. This commit also significantly improves the output for the test and now prints all the GDB commands used in sequence. It also adds some clarifications to existing comments, for example, clarifying that once the replay-break is reached, a SIGINT is captured in GDB. reverse_debugging is kept "skipped" for aarch64, ppc64, and x86_64, so won't run unless QEMU_TEST_FLAKY_TESTS=1 is set in the test environment, before running 'make check-functional' or 'meson test [...]'. Reviewed-by: Daniel P. Berrangé Signed-off-by: Gustavo Romero Message-ID: <20251003141820.85278-9-gustavo.romero@linaro.org> [AJB: it is and broke long line] Signed-off-by: Alex Bennée Tested-by: Thomas Huth --- tests/functional/reverse_debugging.py | 139 ++++++++++++++------------ 1 file changed, 75 insertions(+), 64 deletions(-) diff --git a/tests/functional/reverse_debugging.py b/tests/functional/reverse_debugging.py index 7fd8c7607f571..68cfcb398564a 100644 --- a/tests/functional/reverse_debugging.py +++ b/tests/functional/reverse_debugging.py @@ -1,19 +1,23 @@ -# Reverse debugging test -# # SPDX-License-Identifier: GPL-2.0-or-later # +# Reverse debugging test +# # Copyright (c) 2020 ISP RAS +# Copyright (c) 2025 Linaro Limited # # Author: # Pavel Dovgalyuk +# Gustavo Romero (Run without Avocado) # # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -import os + import logging +import os from subprocess import check_output -from qemu_test import LinuxKernelTest, get_qemu_img +from qemu_test import LinuxKernelTest, get_qemu_img, GDB, \ + skipIfMissingEnv, skipIfMissingImports from qemu_test.ports import Ports @@ -29,9 +33,7 @@ class ReverseDebugging(LinuxKernelTest): that the execution is stopped at the last of them. """ - timeout = 10 STEPS = 10 - endian_is_le = True def run_vm(self, record, shift, args, replay_path, image_path, port): logger = logging.getLogger('replay') @@ -54,47 +56,17 @@ def run_vm(self, record, shift, args, replay_path, image_path, port): return vm @staticmethod - def get_reg_le(g, reg): - res = g.cmd(b'p%x' % reg) - num = 0 - for i in range(len(res))[-2::-2]: - num = 0x100 * num + int(res[i:i + 2], 16) - return num - - @staticmethod - def get_reg_be(g, reg): - res = g.cmd(b'p%x' % reg) - return int(res, 16) - - def get_reg(self, g, reg): - # value may be encoded in BE or LE order - if self.endian_is_le: - return self.get_reg_le(g, reg) - else: - return self.get_reg_be(g, reg) - - def get_pc(self, g): - return self.get_reg(g, self.REG_PC) - - def check_pc(self, g, addr): - pc = self.get_pc(g) - if pc != addr: - self.fail('Invalid PC (read %x instead of %x)' % (pc, addr)) - - @staticmethod - def gdb_step(g): - g.cmd(b's', b'T05thread:01;') - - @staticmethod - def gdb_bstep(g): - g.cmd(b'bs', b'T05thread:01;') + def get_pc(gdb: GDB): + return gdb.cli("print $pc").get_addr() @staticmethod def vm_get_icount(vm): return vm.qmp('query-replay')['return']['icount'] - def reverse_debugging(self, shift=7, args=None): - from avocado.utils import gdb + @skipIfMissingImports("pygdbmi") # Required by GDB class + @skipIfMissingEnv("QEMU_TEST_GDB") + def reverse_debugging(self, gdb_arch, shift=7, args=None): + from qemu_test import GDB logger = logging.getLogger('replay') @@ -124,68 +96,107 @@ def reverse_debugging(self, shift=7, args=None): with Ports() as ports: port = ports.find_free_port() vm = self.run_vm(False, shift, args, replay_path, image_path, port) - logger.info('connecting to gdbstub') - g = gdb.GDBRemote('127.0.0.1', port, False, False) - g.connect() - r = g.cmd(b'qSupported') - if b'qXfer:features:read+' in r: - g.cmd(b'qXfer:features:read:target.xml:0,ffb') - if b'ReverseStep+' not in r: + + try: + logger.info('Connecting to gdbstub...') + self.reverse_debugging_run(vm, port, gdb_arch, last_icount) + logger.info('Test passed.') + except GDB.TimeoutError: + # Convert a GDB timeout exception into a unittest failure exception. + raise self.failureException("Timeout while connecting to or " + "communicating with gdbstub...") from None + except Exception: + # Re-throw exceptions from unittest, like the ones caused by fail(), + # skipTest(), etc. + raise + + def reverse_debugging_run(self, vm, port, gdb_arch, last_icount): + logger = logging.getLogger('replay') + + gdb_cmd = os.getenv('QEMU_TEST_GDB') + gdb = GDB(gdb_cmd) + + r = gdb.cli("set architecture").get_log() + if gdb_arch not in r: + self.skipTest(f"GDB does not support arch '{gdb_arch}'") + + gdb.cli("set debug remote 1") + + c = gdb.cli(f"target remote localhost:{port}").get_console() + if not f"Remote debugging using localhost:{port}" in c: + self.fail("Could not connect to gdbstub!") + + # Remote debug messages are in 'log' payloads. + r = gdb.get_log() + if 'ReverseStep+' not in r: self.fail('Reverse step is not supported by QEMU') - if b'ReverseContinue+' not in r: + if 'ReverseContinue+' not in r: self.fail('Reverse continue is not supported by QEMU') + gdb.cli("set debug remote 0") + logger.info('stepping forward') steps = [] # record first instruction addresses for _ in range(self.STEPS): - pc = self.get_pc(g) + pc = self.get_pc(gdb) logger.info('saving position %x' % pc) steps.append(pc) - self.gdb_step(g) + gdb.cli("stepi") # visit the recorded instruction in reverse order logger.info('stepping backward') for addr in steps[::-1]: - self.gdb_bstep(g) - self.check_pc(g, addr) logger.info('found position %x' % addr) + gdb.cli("reverse-stepi") + pc = self.get_pc(gdb) + if pc != addr: + logger.info('Invalid PC (read %x instead of %x)' % (pc, addr)) + self.fail('Reverse stepping failed!') # visit the recorded instruction in forward order logger.info('stepping forward') for addr in steps: - self.check_pc(g, addr) - self.gdb_step(g) logger.info('found position %x' % addr) + pc = self.get_pc(gdb) + if pc != addr: + logger.info('Invalid PC (read %x instead of %x)' % (pc, addr)) + self.fail('Forward stepping failed!') + gdb.cli("stepi") # set breakpoints for the instructions just stepped over logger.info('setting breakpoints') for addr in steps: - # hardware breakpoint at addr with len=1 - g.cmd(b'Z1,%x,1' % addr, b'OK') + gdb.cli(f"break *{hex(addr)}") # this may hit a breakpoint if first instructions are executed # again logger.info('continuing execution') vm.qmp('replay-break', icount=last_icount - 1) # continue - will return after pausing - # This could stop at the end and get a T02 return, or by - # re-executing one of the breakpoints and get a T05 return. - g.cmd(b'c') + # This can stop at the end of the replay-break and gdb gets a SIGINT, + # or by re-executing one of the breakpoints and gdb stops at a + # breakpoint. + gdb.cli("continue") + if self.vm_get_icount(vm) == last_icount - 1: logger.info('reached the end (icount %s)' % (last_icount - 1)) else: logger.info('hit a breakpoint again at %x (icount %s)' % - (self.get_pc(g), self.vm_get_icount(vm))) + (self.get_pc(gdb), self.vm_get_icount(vm))) logger.info('running reverse continue to reach %x' % steps[-1]) # reverse continue - will return after stopping at the breakpoint - g.cmd(b'bc', b'T05thread:01;') + gdb.cli("reverse-continue") # assume that none of the first instructions is executed again # breaking the order of the breakpoints - self.check_pc(g, steps[-1]) + pc = self.get_pc(gdb) + if pc != steps[-1]: + self.fail("'reverse-continue' did not hit the first PC in reverse order!") + logger.info('successfully reached %x' % steps[-1]) logger.info('exiting gdb and qemu') + gdb.exit() vm.shutdown() From 4d3eb329ccf34da76b0b7822eecaaacc6e6fe646 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 3 Oct 2025 14:18:20 +0000 Subject: [PATCH 1331/1794] tests/functional: Adapt arches to reverse_debugging w/o Avocado MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reverse_debugging no longer depends on Avocado, so remove the import checks for Avocado, the per-arch endianness tweaks, and the per-arch register settings. All of these are now handled in the ReverseDebugging class, automatically. Reviewed-by: Thomas Huth Reviewed-by: Daniel P. Berrangé Signed-off-by: Gustavo Romero Message-ID: <20251003141820.85278-10-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée --- tests/functional/aarch64/test_reverse_debug.py | 11 +++++------ tests/functional/ppc64/test_reverse_debug.py | 15 ++++++--------- tests/functional/x86_64/test_reverse_debug.py | 15 +++++---------- 3 files changed, 16 insertions(+), 25 deletions(-) diff --git a/tests/functional/aarch64/test_reverse_debug.py b/tests/functional/aarch64/test_reverse_debug.py index 8bc91ccfde711..906f10aec5565 100755 --- a/tests/functional/aarch64/test_reverse_debug.py +++ b/tests/functional/aarch64/test_reverse_debug.py @@ -2,25 +2,24 @@ # # SPDX-License-Identifier: GPL-2.0-or-later # -# Reverse debugging test +# Reverse debugging test for aarch64 # # Copyright (c) 2020 ISP RAS +# Copyright (c) 2025 Linaro Limited # # Author: # Pavel Dovgalyuk +# Gustavo Romero (Run without Avocado) # # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from qemu_test import Asset, skipIfMissingImports, skipFlakyTest +from qemu_test import Asset, skipFlakyTest from reverse_debugging import ReverseDebugging -@skipIfMissingImports('avocado.utils') class ReverseDebugging_AArch64(ReverseDebugging): - REG_PC = 32 - ASSET_KERNEL = Asset( ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' 'releases/29/Everything/aarch64/os/images/pxeboot/vmlinuz'), @@ -31,7 +30,7 @@ def test_aarch64_virt(self): self.set_machine('virt') self.cpu = 'cortex-a53' kernel_path = self.ASSET_KERNEL.fetch() - self.reverse_debugging(args=('-kernel', kernel_path)) + self.reverse_debugging(gdb_arch='aarch64', args=('-kernel', kernel_path)) if __name__ == '__main__': diff --git a/tests/functional/ppc64/test_reverse_debug.py b/tests/functional/ppc64/test_reverse_debug.py index 5931adef5a9b5..69551fb84df00 100755 --- a/tests/functional/ppc64/test_reverse_debug.py +++ b/tests/functional/ppc64/test_reverse_debug.py @@ -2,39 +2,36 @@ # # SPDX-License-Identifier: GPL-2.0-or-later # -# Reverse debugging test +# Reverse debugging test for ppc64 # # Copyright (c) 2020 ISP RAS +# Copyright (c) 2025 Linaro Limited # # Author: # Pavel Dovgalyuk +# Gustavo Romero (Run without Avocado) # # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from qemu_test import skipIfMissingImports, skipFlakyTest +from qemu_test import skipFlakyTest from reverse_debugging import ReverseDebugging -@skipIfMissingImports('avocado.utils') class ReverseDebugging_ppc64(ReverseDebugging): - REG_PC = 0x40 - @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/1992") def test_ppc64_pseries(self): self.set_machine('pseries') # SLOF branches back to its entry point, which causes this test # to take the 'hit a breakpoint again' path. That's not a problem, # just slightly different than the other machines. - self.endian_is_le = False - self.reverse_debugging() + self.reverse_debugging(gdb_arch='powerpc:common64') @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/1992") def test_ppc64_powernv(self): self.set_machine('powernv') - self.endian_is_le = False - self.reverse_debugging() + self.reverse_debugging(gdb_arch='powerpc:common64') if __name__ == '__main__': diff --git a/tests/functional/x86_64/test_reverse_debug.py b/tests/functional/x86_64/test_reverse_debug.py index d713e91e14460..2b31ae8724331 100755 --- a/tests/functional/x86_64/test_reverse_debug.py +++ b/tests/functional/x86_64/test_reverse_debug.py @@ -2,34 +2,29 @@ # # SPDX-License-Identifier: GPL-2.0-or-later # -# Reverse debugging test +# Reverse debugging test for x86_64 # # Copyright (c) 2020 ISP RAS +# Copyright (c) 2025 Linaro Limited # # Author: # Pavel Dovgalyuk +# Gustavo Romero (Run without Avocado) # # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from qemu_test import skipIfMissingImports, skipFlakyTest +from qemu_test import skipFlakyTest from reverse_debugging import ReverseDebugging -@skipIfMissingImports('avocado.utils') class ReverseDebugging_X86_64(ReverseDebugging): - REG_PC = 0x10 - REG_CS = 0x12 - def get_pc(self, g): - return self.get_reg_le(g, self.REG_PC) \ - + self.get_reg_le(g, self.REG_CS) * 0x10 - @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/2922") def test_x86_64_pc(self): self.set_machine('pc') # start with BIOS only - self.reverse_debugging() + self.reverse_debugging(gdb_arch='x86-64') if __name__ == '__main__': From 41f8f2be27736192bab29aa38380c9ebaae810fa Mon Sep 17 00:00:00 2001 From: Vladimir Lukianov <1844144@gmail.com> Date: Tue, 3 Jun 2025 14:54:59 +0200 Subject: [PATCH 1332/1794] record/replay: fix race condition on test_aarch64_reverse_debug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensures EVENT_INSTRUCTION written to replay.bin before EVENT_SHUTDOWN_HOST_QMP Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2921 Signed-off-by: Vladimir Lukianov <1844144@gmail.com> Message-ID: <20250603125459.17688-1-1844144@gmail.com> [AJB: fix re-base file mode] Signed-off-by: Alex Bennée --- replay/replay.c | 2 ++ tests/functional/aarch64/test_reverse_debug.py | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/replay/replay.c b/replay/replay.c index a3e24c967ae9f..b2121788c1d72 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -263,6 +263,8 @@ bool replay_has_interrupt(void) void replay_shutdown_request(ShutdownCause cause) { + replay_save_instructions(); + if (replay_mode == REPLAY_MODE_RECORD) { g_assert(replay_mutex_locked()); replay_put_event(EVENT_SHUTDOWN + cause); diff --git a/tests/functional/aarch64/test_reverse_debug.py b/tests/functional/aarch64/test_reverse_debug.py index 906f10aec5565..ec3348c96d88a 100755 --- a/tests/functional/aarch64/test_reverse_debug.py +++ b/tests/functional/aarch64/test_reverse_debug.py @@ -25,7 +25,6 @@ class ReverseDebugging_AArch64(ReverseDebugging): 'releases/29/Everything/aarch64/os/images/pxeboot/vmlinuz'), '7e1430b81c26bdd0da025eeb8fbd77b5dc961da4364af26e771bd39f379cbbf7') - @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/2921") def test_aarch64_virt(self): self.set_machine('virt') self.cpu = 'cortex-a53' From a23e719ca8e80d22eafe4b2b57833918d439fa0c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 25 Sep 2025 12:57:23 +0100 Subject: [PATCH 1333/1794] target/arm: Don't set HCR.RW for AArch32 only CPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 39ec3fc0301 we fixed a bug where we were not implementing HCR_EL2.RW as RAO/WI for CPUs where EL1 doesn't support AArch32. However, we got the condition wrong, so we now set this bit even on CPUs which have no AArch64 support at all. This is wrong because the AArch32 HCR register defines this bit as RES0. Correct the condition we use for forcing HCR_RW to be set. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3128 Fixes: 39ec3fc0301 ("target/arm: HCR_EL2.RW should be RAO/WI if EL1 doesn't support AArch32") Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250925115723.1293233-1-peter.maydell@linaro.org --- target/arm/helper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index aa730addf2fc1..792a47a9c50dc 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -3742,7 +3742,8 @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask) value &= valid_mask; /* RW is RAO/WI if EL1 is AArch64 only */ - if (!cpu_isar_feature(aa64_aa32_el1, cpu)) { + if (arm_feature(env, ARM_FEATURE_AARCH64) && + !cpu_isar_feature(aa64_aa32_el1, cpu)) { value |= HCR_RW; } From 3471ae96b10bc0a1efeeeff85aea7a823cb57f77 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:19 +0200 Subject: [PATCH 1334/1794] hw/arm/xlnx-versal: split the xlnx-versal type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the xlnx-versal device into two classes, a base, abstract class and the existing concrete one. Introduce a VersalVersion type that will be used across several device models when versal2 implementation is added. This is in preparation for versal2 implementation. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-2-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 31 +++++++++++++++++++++------- include/hw/arm/xlnx-versal-version.h | 15 ++++++++++++++ include/hw/arm/xlnx-versal.h | 12 ++++++++++- 3 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 include/hw/arm/xlnx-versal-version.h diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index a42b9e7140b61..4da656318f6d7 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -2,6 +2,7 @@ * Xilinx Versal SoC model. * * Copyright (c) 2018 Xilinx Inc. + * Copyright (c) 2025 Advanced Micro Devices, Inc. * Written by Edgar E. Iglesias * * This program is free software; you can redistribute it and/or modify @@ -920,7 +921,7 @@ static void versal_unimp(Versal *s) static void versal_realize(DeviceState *dev, Error **errp) { - Versal *s = XLNX_VERSAL(dev); + Versal *s = XLNX_VERSAL_BASE(dev); qemu_irq pic[XLNX_VERSAL_NR_IRQS]; versal_create_apu_cpus(s); @@ -955,9 +956,9 @@ static void versal_realize(DeviceState *dev, Error **errp) &s->lpd.rpu.mr_ps_alias, 0); } -static void versal_init(Object *obj) +static void versal_base_init(Object *obj) { - Versal *s = XLNX_VERSAL(obj); + Versal *s = XLNX_VERSAL_BASE(obj); memory_region_init(&s->fpd.apu.mr, obj, "mr-apu", UINT64_MAX); memory_region_init(&s->lpd.rpu.mr, obj, "mr-rpu", UINT64_MAX); @@ -975,7 +976,7 @@ static const Property versal_properties[] = { TYPE_CAN_BUS, CanBusState *), }; -static void versal_class_init(ObjectClass *klass, const void *data) +static void versal_base_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -984,16 +985,32 @@ static void versal_class_init(ObjectClass *klass, const void *data) /* No VMSD since we haven't got any top-level SoC state to save. */ } -static const TypeInfo versal_info = { - .name = TYPE_XLNX_VERSAL, +static void versal_class_init(ObjectClass *klass, const void *data) +{ + VersalClass *vc = XLNX_VERSAL_BASE_CLASS(klass); + + vc->version = VERSAL_VER_VERSAL; +} + +static const TypeInfo versal_base_info = { + .name = TYPE_XLNX_VERSAL_BASE, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Versal), - .instance_init = versal_init, + .instance_init = versal_base_init, + .class_init = versal_base_class_init, + .class_size = sizeof(VersalClass), + .abstract = true, +}; + +static const TypeInfo versal_info = { + .name = TYPE_XLNX_VERSAL, + .parent = TYPE_XLNX_VERSAL_BASE, .class_init = versal_class_init, }; static void versal_register_types(void) { + type_register_static(&versal_base_info); type_register_static(&versal_info); } diff --git a/include/hw/arm/xlnx-versal-version.h b/include/hw/arm/xlnx-versal-version.h new file mode 100644 index 0000000000000..c4307d1304a77 --- /dev/null +++ b/include/hw/arm/xlnx-versal-version.h @@ -0,0 +1,15 @@ +/* + * AMD Versal versions + * + * Copyright (c) 2025 Advanced Micro Devices, Inc. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_ARM_XLNX_VERSAL_VERSION_H +#define HW_ARM_XLNX_VERSAL_VERSION_H + +typedef enum VersalVersion { + VERSAL_VER_VERSAL, +} VersalVersion; + +#endif diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 05ed641b6b6c8..1f92e314d6c40 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -2,6 +2,7 @@ * Model of the Xilinx Versal * * Copyright (c) 2018 Xilinx Inc. + * Copyright (c) 2025 Advanced Micro Devices, Inc. * Written by Edgar E. Iglesias * * This program is free software; you can redistribute it and/or modify @@ -35,9 +36,12 @@ #include "hw/misc/xlnx-versal-cfu.h" #include "hw/misc/xlnx-versal-cframe-reg.h" #include "target/arm/cpu.h" +#include "hw/arm/xlnx-versal-version.h" + +#define TYPE_XLNX_VERSAL_BASE "xlnx-versal-base" +OBJECT_DECLARE_TYPE(Versal, VersalClass, XLNX_VERSAL_BASE) #define TYPE_XLNX_VERSAL "xlnx-versal" -OBJECT_DECLARE_SIMPLE_TYPE(Versal, XLNX_VERSAL) #define XLNX_VERSAL_NR_ACPUS 2 #define XLNX_VERSAL_NR_RCPUS 2 @@ -137,6 +141,12 @@ struct Versal { } cfg; }; +struct VersalClass { + SysBusDeviceClass parent; + + VersalVersion version; +}; + /* Memory-map and IRQ definitions. Copied a subset from * auto-generated files. */ From d82be8c5de7a8bc1602b89678e69312c1716bd10 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:20 +0200 Subject: [PATCH 1335/1794] hw/arm/xlnx-versal: prepare for FDT creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The following commits will move FDT creation logic from the xlnx-versal-virt machine to the xlnx-versal SoC itself. Prepare this by passing the FDT handle to the SoC before it is realized. For now the SoC only creates the two clock nodes. The ones from the xlnx-versal virt machine are renamed with a `old-' prefix and will be removed once they are not referenced anymore. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-3-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 9 ++++++--- hw/arm/xlnx-versal.c | 24 ++++++++++++++++++++++++ include/hw/arm/xlnx-versal.h | 12 ++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index adadbb7290258..d1c65afa2ac55 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -2,6 +2,7 @@ * Xilinx Versal Virtual board. * * Copyright (c) 2018 Xilinx Inc. + * Copyright (c) 2025 Advanced Micro Devices, Inc. * Written by Edgar E. Iglesias * * This program is free software; you can redistribute it and/or modify @@ -697,10 +698,12 @@ static void versal_virt_init(MachineState *machine) &error_abort); object_property_set_link(OBJECT(&s->soc), "canbus1", OBJECT(s->canbus[1]), &error_abort); - sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal); fdt_create(s); + versal_set_fdt(&s->soc, s->fdt); + sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal); create_virtio_regions(s); + fdt_add_gem_nodes(s); fdt_add_uart_nodes(s); fdt_add_canfd_nodes(s); @@ -714,8 +717,8 @@ static void versal_virt_init(MachineState *machine) fdt_add_efuse_ctrl_node(s); fdt_add_efuse_cache_node(s); fdt_add_cpu_nodes(s, psci_conduit); - fdt_add_clk_node(s, "/clk125", 125000000, s->phandle.clk_125Mhz); - fdt_add_clk_node(s, "/clk25", 25000000, s->phandle.clk_25Mhz); + fdt_add_clk_node(s, "/old-clk125", 125000000, s->phandle.clk_125Mhz); + fdt_add_clk_node(s, "/old-clk25", 25000000, s->phandle.clk_25Mhz); /* Make the APU cpu address space visible to virtio and other * modules unaware of multiple address-spaces. */ diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 4da656318f6d7..3b59621956169 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -24,6 +24,8 @@ #include "qemu/log.h" #include "target/arm/cpu-qom.h" #include "target/arm/gtimer.h" +#include "system/device_tree.h" +#include "hw/arm/fdt.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -919,11 +921,33 @@ static void versal_unimp(Versal *s) gpio_in); } +static uint32_t fdt_add_clk_node(Versal *s, const char *name, + unsigned int freq_hz) +{ + uint32_t phandle; + + phandle = qemu_fdt_alloc_phandle(s->cfg.fdt); + + qemu_fdt_add_subnode(s->cfg.fdt, name); + qemu_fdt_setprop_cell(s->cfg.fdt, name, "phandle", phandle); + qemu_fdt_setprop_cell(s->cfg.fdt, name, "clock-frequency", freq_hz); + qemu_fdt_setprop_cell(s->cfg.fdt, name, "#clock-cells", 0x0); + qemu_fdt_setprop_string(s->cfg.fdt, name, "compatible", "fixed-clock"); + qemu_fdt_setprop(s->cfg.fdt, name, "u-boot,dm-pre-reloc", NULL, 0); + + return phandle; +} + static void versal_realize(DeviceState *dev, Error **errp) { Versal *s = XLNX_VERSAL_BASE(dev); qemu_irq pic[XLNX_VERSAL_NR_IRQS]; + g_assert(s->cfg.fdt != NULL); + + s->phandle.clk_25mhz = fdt_add_clk_node(s, "/clk25", 25 * 1000 * 1000); + s->phandle.clk_125mhz = fdt_add_clk_node(s, "/clk125", 125 * 1000 * 1000); + versal_create_apu_cpus(s); versal_create_apu_gic(s, pic); versal_create_rpu_cpus(s); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 1f92e314d6c40..f2a62b43552d9 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -136,8 +136,14 @@ struct Versal { OrIRQState apb_irq_orgate; } pmc; + struct { + uint32_t clk_25mhz; + uint32_t clk_125mhz; + } phandle; + struct { MemoryRegion *mr_ddr; + void *fdt; } cfg; }; @@ -147,6 +153,12 @@ struct VersalClass { VersalVersion version; }; +static inline void versal_set_fdt(Versal *s, void *fdt) +{ + g_assert(!qdev_is_realized(DEVICE(s))); + s->cfg.fdt = fdt; +} + /* Memory-map and IRQ definitions. Copied a subset from * auto-generated files. */ From 288dc87244c97c8674967c12bb5c8e38fd7d9ff5 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:21 +0200 Subject: [PATCH 1336/1794] hw/arm/xlnx-versal: uart: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the UARTs creations. The VersalMap struct is now used to describe the SoC and its peripherals. For now it contains the two UARTs mapping information. The creation function now embeds the FDT creation logic as well. The devices are now created dynamically using qdev_new and (qdev|sysbus)_realize_and_unref. This will allow to rely entirely on the VersalMap structure to create the SoC and allow easy addition of new SoCs of the same family (like versal2 coming with next commits). Note that the connection to the CRL is removed for now and will be re-added by next commits. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-4-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 36 +-------- hw/arm/xlnx-versal.c | 150 ++++++++++++++++++++++++++++------- include/hw/arm/xlnx-versal.h | 2 - 3 files changed, 122 insertions(+), 66 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index d1c65afa2ac55..e1deae113179c 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -77,6 +77,7 @@ static void fdt_create(VersalVirt *s) s->phandle.dwc = qemu_fdt_alloc_phandle(s->fdt); /* Create /chosen node for load_dtb. */ qemu_fdt_add_subnode(s->fdt, "/chosen"); + qemu_fdt_add_subnode(s->fdt, "/aliases"); /* Header */ qemu_fdt_setprop_cell(s->fdt, "/", "interrupt-parent", s->phandle.gic); @@ -208,40 +209,6 @@ static void fdt_add_usb_xhci_nodes(VersalVirt *s) g_free(name); } -static void fdt_add_uart_nodes(VersalVirt *s) -{ - uint64_t addrs[] = { MM_UART1, MM_UART0 }; - unsigned int irqs[] = { VERSAL_UART1_IRQ_0, VERSAL_UART0_IRQ_0 }; - const char compat[] = "arm,pl011\0arm,sbsa-uart"; - const char clocknames[] = "uartclk\0apb_pclk"; - int i; - - for (i = 0; i < ARRAY_SIZE(addrs); i++) { - char *name = g_strdup_printf("/uart@%" PRIx64, addrs[i]); - qemu_fdt_add_subnode(s->fdt, name); - qemu_fdt_setprop_cell(s->fdt, name, "current-speed", 115200); - qemu_fdt_setprop_cells(s->fdt, name, "clocks", - s->phandle.clk_125Mhz, s->phandle.clk_125Mhz); - qemu_fdt_setprop(s->fdt, name, "clock-names", - clocknames, sizeof(clocknames)); - - qemu_fdt_setprop_cells(s->fdt, name, "interrupts", - GIC_FDT_IRQ_TYPE_SPI, irqs[i], - GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", - 2, addrs[i], 2, 0x1000); - qemu_fdt_setprop(s->fdt, name, "compatible", - compat, sizeof(compat)); - qemu_fdt_setprop(s->fdt, name, "u-boot,dm-pre-reloc", NULL, 0); - - if (addrs[i] == MM_UART0) { - /* Select UART0. */ - qemu_fdt_setprop_string(s->fdt, "/chosen", "stdout-path", name); - } - g_free(name); - } -} - static void fdt_add_canfd_nodes(VersalVirt *s) { uint64_t addrs[] = { MM_CANFD1, MM_CANFD0 }; @@ -705,7 +672,6 @@ static void versal_virt_init(MachineState *machine) create_virtio_regions(s); fdt_add_gem_nodes(s); - fdt_add_uart_nodes(s); fdt_add_canfd_nodes(s); fdt_add_gic_nodes(s); fdt_add_timer_nodes(s); diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 3b59621956169..b16af79e8a90b 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -26,6 +26,7 @@ #include "target/arm/gtimer.h" #include "system/device_tree.h" #include "hw/arm/fdt.h" +#include "hw/char/pl011.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -34,6 +35,83 @@ #define VERSAL_NUM_PMC_APB_IRQS 18 #define NUM_OSPI_IRQ_LINES 3 +typedef struct VersalSimplePeriphMap { + uint64_t addr; + int irq; +} VersalSimplePeriphMap; + +typedef struct VersalMap { + VersalSimplePeriphMap uart[2]; + size_t num_uart; +} VersalMap; + +static const VersalMap VERSAL_MAP = { + .uart[0] = { 0xff000000, 18 }, + .uart[1] = { 0xff010000, 19 }, + .num_uart = 2, +}; + +static const VersalMap *VERSION_TO_MAP[] = { + [VERSAL_VER_VERSAL] = &VERSAL_MAP, +}; + +static inline VersalVersion versal_get_version(Versal *s) +{ + return XLNX_VERSAL_BASE_GET_CLASS(s)->version; +} + +static inline const VersalMap *versal_get_map(Versal *s) +{ + return VERSION_TO_MAP[versal_get_version(s)]; +} + + +static qemu_irq versal_get_irq(Versal *s, int irq_idx) +{ + return qdev_get_gpio_in(DEVICE(&s->fpd.apu.gic), irq_idx); +} + +static void versal_sysbus_connect_irq(Versal *s, SysBusDevice *sbd, + int sbd_idx, int irq_idx) +{ + qemu_irq irq = versal_get_irq(s, irq_idx); + + if (irq == NULL) { + return; + } + + sysbus_connect_irq(sbd, sbd_idx, irq); +} + +static inline char *versal_fdt_add_subnode(Versal *s, const char *path, + uint64_t at, const char *compat, + size_t compat_sz) +{ + char *p; + + p = g_strdup_printf("%s@%" PRIx64, path, at); + qemu_fdt_add_subnode(s->cfg.fdt, p); + + if (!strncmp(compat, "memory", compat_sz)) { + qemu_fdt_setprop(s->cfg.fdt, p, "device_type", compat, compat_sz); + } else { + qemu_fdt_setprop(s->cfg.fdt, p, "compatible", compat, compat_sz); + } + + return p; +} + +static inline char *versal_fdt_add_simple_subnode(Versal *s, const char *path, + uint64_t addr, uint64_t len, + const char *compat, + size_t compat_sz) +{ + char *p = versal_fdt_add_subnode(s, path, addr, compat, compat_sz); + + qemu_fdt_setprop_sized_cells(s->cfg.fdt, p, "reg", 2, addr, 2, len); + return p; +} + static void versal_create_apu_cpus(Versal *s) { int i; @@ -167,28 +245,44 @@ static void versal_create_rpu_cpus(Versal *s) qdev_realize(DEVICE(&s->lpd.rpu.cluster), NULL, &error_fatal); } -static void versal_create_uarts(Versal *s, qemu_irq *pic) +static void versal_create_uart(Versal *s, + const VersalSimplePeriphMap *map, + int chardev_idx) { - int i; - - for (i = 0; i < ARRAY_SIZE(s->lpd.iou.uart); i++) { - static const int irqs[] = { VERSAL_UART0_IRQ_0, VERSAL_UART1_IRQ_0}; - static const uint64_t addrs[] = { MM_UART0, MM_UART1 }; - char *name = g_strdup_printf("uart%d", i); - DeviceState *dev; - MemoryRegion *mr; - - object_initialize_child(OBJECT(s), name, &s->lpd.iou.uart[i], - TYPE_PL011); - dev = DEVICE(&s->lpd.iou.uart[i]); - qdev_prop_set_chr(dev, "chardev", serial_hd(i)); - sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal); - - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); - memory_region_add_subregion(&s->mr_ps, addrs[i], mr); + DeviceState *dev; + MemoryRegion *mr; + g_autofree char *node; + g_autofree char *alias; + const char compatible[] = "arm,pl011\0arm,sbsa-uart"; + const char clocknames[] = "uartclk\0apb_pclk"; + + dev = qdev_new(TYPE_PL011); + object_property_add_child(OBJECT(s), "uart[*]", OBJECT(dev)); + qdev_prop_set_chr(dev, "chardev", serial_hd(chardev_idx)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[irqs[i]]); - g_free(name); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + memory_region_add_subregion(&s->mr_ps, map->addr, mr); + + versal_sysbus_connect_irq(s, SYS_BUS_DEVICE(dev), 0, map->irq); + + node = versal_fdt_add_simple_subnode(s, "/uart", map->addr, 0x1000, + compatible, sizeof(compatible)); + qemu_fdt_setprop_cell(s->cfg.fdt, node, "current-speed", 115200); + qemu_fdt_setprop_cells(s->cfg.fdt, node, "clocks", + s->phandle.clk_125mhz, s->phandle.clk_125mhz); + qemu_fdt_setprop(s->cfg.fdt, node, "clock-names", clocknames, + sizeof(clocknames)); + qemu_fdt_setprop_cells(s->cfg.fdt, node, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, map->irq, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + qemu_fdt_setprop(s->cfg.fdt, node, "u-boot,dm-pre-reloc", NULL, 0); + + alias = g_strdup_printf("serial%d", chardev_idx); + qemu_fdt_setprop_string(s->cfg.fdt, "/aliases", alias, node); + + if (chardev_idx == 0) { + qemu_fdt_setprop_string(s->cfg.fdt, "/chosen", "stdout-path", node); } } @@ -783,14 +877,6 @@ static void versal_create_crl(Versal *s, qemu_irq *pic) &error_abort); } - for (i = 0; i < ARRAY_SIZE(s->lpd.iou.uart); i++) { - g_autofree gchar *name = g_strdup_printf("uart[%d]", i); - - object_property_set_link(OBJECT(&s->lpd.crl), - name, OBJECT(&s->lpd.iou.uart[i]), - &error_abort); - } - object_property_set_link(OBJECT(&s->lpd.crl), "usb", OBJECT(&s->lpd.iou.usb), &error_abort); @@ -942,6 +1028,8 @@ static void versal_realize(DeviceState *dev, Error **errp) { Versal *s = XLNX_VERSAL_BASE(dev); qemu_irq pic[XLNX_VERSAL_NR_IRQS]; + const VersalMap *map = versal_get_map(s); + size_t i; g_assert(s->cfg.fdt != NULL); @@ -951,7 +1039,11 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_apu_cpus(s); versal_create_apu_gic(s, pic); versal_create_rpu_cpus(s); - versal_create_uarts(s, pic); + + for (i = 0; i < map->num_uart; i++) { + versal_create_uart(s, &map->uart[i], i); + } + versal_create_canfds(s, pic); versal_create_usbs(s, pic); versal_create_gems(s, pic); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index f2a62b43552d9..b01ddeb142366 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -18,7 +18,6 @@ #include "hw/or-irq.h" #include "hw/sd/sdhci.h" #include "hw/intc/arm_gicv3.h" -#include "hw/char/pl011.h" #include "hw/dma/xlnx-zdma.h" #include "hw/net/cadence_gem.h" #include "hw/rtc/xlnx-zynqmp-rtc.h" @@ -80,7 +79,6 @@ struct Versal { MemoryRegion mr_ocm; struct { - PL011State uart[XLNX_VERSAL_NR_UARTS]; CadenceGEMState gem[XLNX_VERSAL_NR_GEMS]; OrIRQState gem_irq_orgate[XLNX_VERSAL_NR_GEMS]; XlnxZDMA adma[XLNX_VERSAL_NR_ADMAS]; From d70df574d67c459521f7b93fae42451b8e83bbea Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:22 +0200 Subject: [PATCH 1337/1794] hw/arm/xlnx-versal: canfd: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the CAN controllers creation using the VersalMap structure. Note that the connection to the CRL is removed for now and will be re-added by next commits. The xlnx-versal-virt machine now dynamically creates the correct amount of CAN bus link properties based on the number of CAN controller advertised by the SoC. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-5-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 73 +++++++++------------------- hw/arm/xlnx-versal.c | 94 +++++++++++++++++++++++++----------- include/hw/arm/xlnx-versal.h | 7 +-- 3 files changed, 95 insertions(+), 79 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index e1deae113179c..334252564be8f 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -43,11 +43,11 @@ struct VersalVirt { uint32_t clk_25Mhz; uint32_t usb; uint32_t dwc; - uint32_t canfd[2]; } phandle; struct arm_boot_info binfo; - CanBusState *canbus[XLNX_VERSAL_NR_CANFD]; + CanBusState **canbus; + struct { bool secure; } cfg; @@ -209,38 +209,6 @@ static void fdt_add_usb_xhci_nodes(VersalVirt *s) g_free(name); } -static void fdt_add_canfd_nodes(VersalVirt *s) -{ - uint64_t addrs[] = { MM_CANFD1, MM_CANFD0 }; - uint32_t size[] = { MM_CANFD1_SIZE, MM_CANFD0_SIZE }; - unsigned int irqs[] = { VERSAL_CANFD1_IRQ_0, VERSAL_CANFD0_IRQ_0 }; - const char clocknames[] = "can_clk\0s_axi_aclk"; - int i; - - /* Create and connect CANFD0 and CANFD1 nodes to canbus0. */ - for (i = 0; i < ARRAY_SIZE(addrs); i++) { - char *name = g_strdup_printf("/canfd@%" PRIx64, addrs[i]); - qemu_fdt_add_subnode(s->fdt, name); - - qemu_fdt_setprop_cell(s->fdt, name, "rx-fifo-depth", 0x40); - qemu_fdt_setprop_cell(s->fdt, name, "tx-mailbox-count", 0x20); - - qemu_fdt_setprop_cells(s->fdt, name, "clocks", - s->phandle.clk_25Mhz, s->phandle.clk_25Mhz); - qemu_fdt_setprop(s->fdt, name, "clock-names", - clocknames, sizeof(clocknames)); - qemu_fdt_setprop_cells(s->fdt, name, "interrupts", - GIC_FDT_IRQ_TYPE_SPI, irqs[i], - GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", - 2, addrs[i], 2, size[i]); - qemu_fdt_setprop_string(s->fdt, name, "compatible", - "xlnx,canfd-2.0"); - - g_free(name); - } -} - static void fdt_add_fixed_link_nodes(VersalVirt *s, char *gemname, uint32_t phandle) { @@ -661,10 +629,14 @@ static void versal_virt_init(MachineState *machine) TYPE_XLNX_VERSAL); object_property_set_link(OBJECT(&s->soc), "ddr", OBJECT(machine->ram), &error_abort); - object_property_set_link(OBJECT(&s->soc), "canbus0", OBJECT(s->canbus[0]), - &error_abort); - object_property_set_link(OBJECT(&s->soc), "canbus1", OBJECT(s->canbus[1]), - &error_abort); + + for (i = 0; i < versal_get_num_can(VERSAL_VER_VERSAL); i++) { + g_autofree char *prop_name = g_strdup_printf("canbus%d", i); + + object_property_set_link(OBJECT(&s->soc), prop_name, + OBJECT(s->canbus[i]), + &error_abort); + } fdt_create(s); versal_set_fdt(&s->soc, s->fdt); @@ -672,7 +644,6 @@ static void versal_virt_init(MachineState *machine) create_virtio_regions(s); fdt_add_gem_nodes(s); - fdt_add_canfd_nodes(s); fdt_add_gic_nodes(s); fdt_add_timer_nodes(s); fdt_add_zdma_nodes(s); @@ -755,19 +726,22 @@ static void versal_virt_init(MachineState *machine) static void versal_virt_machine_instance_init(Object *obj) { VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); + size_t i, num_can; + + num_can = versal_get_num_can(VERSAL_VER_VERSAL); + s->canbus = g_new0(CanBusState *, num_can); /* - * User can set canbus0 and canbus1 properties to can-bus object and connect - * to socketcan(optional) interface via command line. + * User can set canbusx properties to can-bus object and optionally connect + * to socketcan interface via command line. */ - object_property_add_link(obj, "canbus0", TYPE_CAN_BUS, - (Object **)&s->canbus[0], - object_property_allow_set_link, - 0); - object_property_add_link(obj, "canbus1", TYPE_CAN_BUS, - (Object **)&s->canbus[1], - object_property_allow_set_link, - 0); + for (i = 0; i < num_can; i++) { + g_autofree char *prop_name = g_strdup_printf("canbus%zu", i); + + object_property_add_link(obj, prop_name, TYPE_CAN_BUS, + (Object **) &s->canbus[i], + object_property_allow_set_link, 0); + } } static void versal_virt_machine_finalize(Object *obj) @@ -775,6 +749,7 @@ static void versal_virt_machine_finalize(Object *obj) VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); g_free(s->ospi_model); + g_free(s->canbus); } static void versal_virt_machine_class_init(ObjectClass *oc, const void *data) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index b16af79e8a90b..3d2e33d3dacb0 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -27,6 +27,7 @@ #include "system/device_tree.h" #include "hw/arm/fdt.h" #include "hw/char/pl011.h" +#include "hw/net/xlnx-versal-canfd.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -43,12 +44,19 @@ typedef struct VersalSimplePeriphMap { typedef struct VersalMap { VersalSimplePeriphMap uart[2]; size_t num_uart; + + VersalSimplePeriphMap canfd[4]; + size_t num_canfd; } VersalMap; static const VersalMap VERSAL_MAP = { .uart[0] = { 0xff000000, 18 }, .uart[1] = { 0xff010000, 19 }, .num_uart = 2, + + .canfd[0] = { 0xff060000, 20 }, + .canfd[1] = { 0xff070000, 21 }, + .num_canfd = 2, }; static const VersalMap *VERSION_TO_MAP[] = { @@ -286,36 +294,42 @@ static void versal_create_uart(Versal *s, } } -static void versal_create_canfds(Versal *s, qemu_irq *pic) +static void versal_create_canfd(Versal *s, const VersalSimplePeriphMap *map, + CanBusState *bus) { - int i; - uint32_t irqs[] = { VERSAL_CANFD0_IRQ_0, VERSAL_CANFD1_IRQ_0}; - uint64_t addrs[] = { MM_CANFD0, MM_CANFD1 }; + SysBusDevice *sbd; + MemoryRegion *mr; + g_autofree char *node; + const char compatible[] = "xlnx,canfd-2.0"; + const char clocknames[] = "can_clk\0s_axi_aclk"; - for (i = 0; i < ARRAY_SIZE(s->lpd.iou.canfd); i++) { - char *name = g_strdup_printf("canfd%d", i); - SysBusDevice *sbd; - MemoryRegion *mr; + sbd = SYS_BUS_DEVICE(qdev_new(TYPE_XILINX_CANFD)); + object_property_add_child(OBJECT(s), "canfd[*]", OBJECT(sbd)); - object_initialize_child(OBJECT(s), name, &s->lpd.iou.canfd[i], - TYPE_XILINX_CANFD); - sbd = SYS_BUS_DEVICE(&s->lpd.iou.canfd[i]); + object_property_set_int(OBJECT(sbd), "ext_clk_freq", + 25 * 1000 * 1000 , &error_abort); - object_property_set_int(OBJECT(&s->lpd.iou.canfd[i]), "ext_clk_freq", - XLNX_VERSAL_CANFD_REF_CLK , &error_abort); + object_property_set_link(OBJECT(sbd), "canfdbus", OBJECT(bus), + &error_abort); - object_property_set_link(OBJECT(&s->lpd.iou.canfd[i]), "canfdbus", - OBJECT(s->lpd.iou.canbus[i]), - &error_abort); + sysbus_realize_and_unref(sbd, &error_fatal); - sysbus_realize(sbd, &error_fatal); + mr = sysbus_mmio_get_region(sbd, 0); + memory_region_add_subregion(&s->mr_ps, map->addr, mr); - mr = sysbus_mmio_get_region(sbd, 0); - memory_region_add_subregion(&s->mr_ps, addrs[i], mr); + versal_sysbus_connect_irq(s, sbd, 0, map->irq); - sysbus_connect_irq(sbd, 0, pic[irqs[i]]); - g_free(name); - } + node = versal_fdt_add_simple_subnode(s, "/canfd", map->addr, 0x10000, + compatible, sizeof(compatible)); + qemu_fdt_setprop_cell(s->cfg.fdt, node, "rx-fifo-depth", 0x40); + qemu_fdt_setprop_cell(s->cfg.fdt, node, "tx-mailbox-count", 0x20); + qemu_fdt_setprop_cells(s->cfg.fdt, node, "clocks", + s->phandle.clk_25mhz, s->phandle.clk_25mhz); + qemu_fdt_setprop(s->cfg.fdt, node, "clock-names", + clocknames, sizeof(clocknames)); + qemu_fdt_setprop_cells(s->cfg.fdt, node, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, map->irq, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); } static void versal_create_usbs(Versal *s, qemu_irq *pic) @@ -1044,7 +1058,10 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_uart(s, &map->uart[i], i); } - versal_create_canfds(s, pic); + for (i = 0; i < map->num_canfd; i++) { + versal_create_canfd(s, &map->canfd[i], s->cfg.canbus[i]); + } + versal_create_usbs(s, pic); versal_create_gems(s, pic); versal_create_admas(s, pic); @@ -1072,24 +1089,46 @@ static void versal_realize(DeviceState *dev, Error **errp) &s->lpd.rpu.mr_ps_alias, 0); } +int versal_get_num_can(VersalVersion version) +{ + const VersalMap *map = VERSION_TO_MAP[version]; + + return map->num_canfd; +} + static void versal_base_init(Object *obj) { Versal *s = XLNX_VERSAL_BASE(obj); + size_t i, num_can; memory_region_init(&s->fpd.apu.mr, obj, "mr-apu", UINT64_MAX); memory_region_init(&s->lpd.rpu.mr, obj, "mr-rpu", UINT64_MAX); memory_region_init(&s->mr_ps, obj, "mr-ps-switch", UINT64_MAX); memory_region_init_alias(&s->lpd.rpu.mr_ps_alias, OBJECT(s), "mr-rpu-ps-alias", &s->mr_ps, 0, UINT64_MAX); + + num_can = versal_get_map(s)->num_canfd; + s->cfg.canbus = g_new0(CanBusState *, num_can); + + for (i = 0; i < num_can; i++) { + g_autofree char *prop_name = g_strdup_printf("canbus%zu", i); + + object_property_add_link(obj, prop_name, TYPE_CAN_BUS, + (Object **) &s->cfg.canbus[i], + object_property_allow_set_link, 0); + } +} + +static void versal_base_finalize(Object *obj) +{ + Versal *s = XLNX_VERSAL_BASE(obj); + + g_free(s->cfg.canbus); } static const Property versal_properties[] = { DEFINE_PROP_LINK("ddr", Versal, cfg.mr_ddr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_LINK("canbus0", Versal, lpd.iou.canbus[0], - TYPE_CAN_BUS, CanBusState *), - DEFINE_PROP_LINK("canbus1", Versal, lpd.iou.canbus[1], - TYPE_CAN_BUS, CanBusState *), }; static void versal_base_class_init(ObjectClass *klass, const void *data) @@ -1113,6 +1152,7 @@ static const TypeInfo versal_base_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Versal), .instance_init = versal_base_init, + .instance_finalize = versal_base_finalize, .class_init = versal_base_class_init, .class_size = sizeof(VersalClass), .abstract = true, diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index b01ddeb142366..007c91b596ed9 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -31,7 +31,7 @@ #include "hw/misc/xlnx-versal-crl.h" #include "hw/misc/xlnx-versal-pmc-iou-slcr.h" #include "hw/misc/xlnx-versal-trng.h" -#include "hw/net/xlnx-versal-canfd.h" +#include "net/can_emu.h" #include "hw/misc/xlnx-versal-cfu.h" #include "hw/misc/xlnx-versal-cframe-reg.h" #include "target/arm/cpu.h" @@ -83,8 +83,6 @@ struct Versal { OrIRQState gem_irq_orgate[XLNX_VERSAL_NR_GEMS]; XlnxZDMA adma[XLNX_VERSAL_NR_ADMAS]; VersalUsb2 usb; - CanBusState *canbus[XLNX_VERSAL_NR_CANFD]; - XlnxVersalCANFDState canfd[XLNX_VERSAL_NR_CANFD]; } iou; /* Real-time Processing Unit. */ @@ -141,6 +139,7 @@ struct Versal { struct { MemoryRegion *mr_ddr; + CanBusState **canbus; void *fdt; } cfg; }; @@ -157,6 +156,8 @@ static inline void versal_set_fdt(Versal *s, void *fdt) s->cfg.fdt = fdt; } +int versal_get_num_can(VersalVersion version); + /* Memory-map and IRQ definitions. Copied a subset from * auto-generated files. */ From b392177e42e25b076104eb8fe3a484a9a16642c7 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:23 +0200 Subject: [PATCH 1338/1794] hw/arm/xlnx-versal: sdhci: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the SDHCI controllers creation using the VersalMap structure. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-6-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 43 ++-------------- hw/arm/xlnx-versal.c | 96 ++++++++++++++++++++++++++++-------- include/hw/arm/xlnx-versal.h | 5 +- 3 files changed, 83 insertions(+), 61 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 334252564be8f..52852082d4b3e 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -284,32 +284,6 @@ static void fdt_add_zdma_nodes(VersalVirt *s) } } -static void fdt_add_sd_nodes(VersalVirt *s) -{ - const char clocknames[] = "clk_xin\0clk_ahb"; - const char compat[] = "arasan,sdhci-8.9a"; - int i; - - for (i = ARRAY_SIZE(s->soc.pmc.iou.sd) - 1; i >= 0; i--) { - uint64_t addr = MM_PMC_SD0 + MM_PMC_SD0_SIZE * i; - char *name = g_strdup_printf("/sdhci@%" PRIx64, addr); - - qemu_fdt_add_subnode(s->fdt, name); - - qemu_fdt_setprop_cells(s->fdt, name, "clocks", - s->phandle.clk_25Mhz, s->phandle.clk_25Mhz); - qemu_fdt_setprop(s->fdt, name, "clock-names", - clocknames, sizeof(clocknames)); - qemu_fdt_setprop_cells(s->fdt, name, "interrupts", - GIC_FDT_IRQ_TYPE_SPI, VERSAL_SD0_IRQ_0 + i * 2, - GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", - 2, addr, 2, MM_PMC_SD0_SIZE); - qemu_fdt_setprop(s->fdt, name, "compatible", compat, sizeof(compat)); - g_free(name); - } -} - static void fdt_add_rtc_node(VersalVirt *s) { const char compat[] = "xlnx,zynqmp-rtc"; @@ -564,16 +538,11 @@ static void efuse_attach_drive(XlnxEFuse *dev) } } -static void sd_plugin_card(SDHCIState *sd, DriveInfo *di) +static void sd_plug_card(VersalVirt *s, int idx, DriveInfo *di) { BlockBackend *blk = di ? blk_by_legacy_dinfo(di) : NULL; - DeviceState *card; - card = qdev_new(TYPE_SD_CARD); - object_property_add_child(OBJECT(sd), "card[*]", OBJECT(card)); - qdev_prop_set_drive_err(card, "drive", blk, &error_fatal); - qdev_realize_and_unref(card, qdev_get_child_bus(DEVICE(sd), "sd-bus"), - &error_fatal); + versal_sdhci_plug_card(&s->soc, idx, blk); } static char *versal_get_ospi_model(Object *obj, Error **errp) @@ -648,7 +617,6 @@ static void versal_virt_init(MachineState *machine) fdt_add_timer_nodes(s); fdt_add_zdma_nodes(s); fdt_add_usb_xhci_nodes(s); - fdt_add_sd_nodes(s); fdt_add_rtc_node(s); fdt_add_bbram_node(s); fdt_add_efuse_ctrl_node(s); @@ -668,10 +636,9 @@ static void versal_virt_init(MachineState *machine) /* Attach efuse backend, if given */ efuse_attach_drive(&s->soc.pmc.efuse); - /* Plugin SD cards. */ - for (i = 0; i < ARRAY_SIZE(s->soc.pmc.iou.sd); i++) { - sd_plugin_card(&s->soc.pmc.iou.sd[i], - drive_get(IF_SD, 0, i)); + /* Plug SD cards */ + for (i = 0; i < versal_get_num_sdhci(VERSAL_VER_VERSAL); i++) { + sd_plug_card(s, i, drive_get(IF_SD, 0, i)); } s->binfo.ram_size = machine->ram_size; diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 3d2e33d3dacb0..ff2f47daad9fe 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -28,6 +28,7 @@ #include "hw/arm/fdt.h" #include "hw/char/pl011.h" #include "hw/net/xlnx-versal-canfd.h" +#include "hw/sd/sdhci.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -47,6 +48,9 @@ typedef struct VersalMap { VersalSimplePeriphMap canfd[4]; size_t num_canfd; + + VersalSimplePeriphMap sdhci[2]; + size_t num_sdhci; } VersalMap; static const VersalMap VERSAL_MAP = { @@ -57,6 +61,10 @@ static const VersalMap VERSAL_MAP = { .canfd[0] = { 0xff060000, 20 }, .canfd[1] = { 0xff070000, 21 }, .num_canfd = 2, + + .sdhci[0] = { 0xf1040000, 126 }, + .sdhci[1] = { 0xf1050000, 128 }, + .num_sdhci = 2, }; static const VersalMap *VERSION_TO_MAP[] = { @@ -73,6 +81,18 @@ static inline const VersalMap *versal_get_map(Versal *s) return VERSION_TO_MAP[versal_get_version(s)]; } +static inline Object *versal_get_child(Versal *s, const char *child) +{ + return object_resolve_path_at(OBJECT(s), child); +} + +static inline Object *versal_get_child_idx(Versal *s, const char *child, + size_t idx) +{ + g_autofree char *n = g_strdup_printf("%s[%zu]", child, idx); + + return versal_get_child(s, n); +} static qemu_irq versal_get_irq(Versal *s, int irq_idx) { @@ -424,32 +444,39 @@ static void versal_create_admas(Versal *s, qemu_irq *pic) } #define SDHCI_CAPABILITIES 0x280737ec6481 /* Same as on ZynqMP. */ -static void versal_create_sds(Versal *s, qemu_irq *pic) +static void versal_create_sdhci(Versal *s, + const VersalSimplePeriphMap *map) { - int i; + DeviceState *dev; + MemoryRegion *mr; + g_autofree char *node; + const char compatible[] = "arasan,sdhci-8.9a"; + const char clocknames[] = "clk_xin\0clk_ahb"; - for (i = 0; i < ARRAY_SIZE(s->pmc.iou.sd); i++) { - DeviceState *dev; - MemoryRegion *mr; + dev = qdev_new(TYPE_SYSBUS_SDHCI); + object_property_add_child(OBJECT(s), "sdhci[*]", OBJECT(dev)); - object_initialize_child(OBJECT(s), "sd[*]", &s->pmc.iou.sd[i], - TYPE_SYSBUS_SDHCI); - dev = DEVICE(&s->pmc.iou.sd[i]); + object_property_set_uint(OBJECT(dev), "sd-spec-version", 3, + &error_fatal); + object_property_set_uint(OBJECT(dev), "capareg", SDHCI_CAPABILITIES, + &error_fatal); + object_property_set_uint(OBJECT(dev), "uhs", UHS_I, &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - object_property_set_uint(OBJECT(dev), "sd-spec-version", 3, - &error_fatal); - object_property_set_uint(OBJECT(dev), "capareg", SDHCI_CAPABILITIES, - &error_fatal); - object_property_set_uint(OBJECT(dev), "uhs", UHS_I, &error_fatal); - sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + memory_region_add_subregion(&s->mr_ps, map->addr, mr); - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); - memory_region_add_subregion(&s->mr_ps, - MM_PMC_SD0 + i * MM_PMC_SD0_SIZE, mr); + versal_sysbus_connect_irq(s, SYS_BUS_DEVICE(dev), 0, map->irq); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, - pic[VERSAL_SD0_IRQ_0 + i * 2]); - } + node = versal_fdt_add_simple_subnode(s, "/sdhci", map->addr, 0x10000, + compatible, sizeof(compatible)); + qemu_fdt_setprop_cells(s->cfg.fdt, node, "clocks", + s->phandle.clk_25mhz, s->phandle.clk_25mhz); + qemu_fdt_setprop(s->cfg.fdt, node, "clock-names", + clocknames, sizeof(clocknames)); + qemu_fdt_setprop_cells(s->cfg.fdt, node, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, map->irq, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); } static void versal_create_pmc_apb_irq_orgate(Versal *s, qemu_irq *pic) @@ -1062,10 +1089,13 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_canfd(s, &map->canfd[i], s->cfg.canbus[i]); } + for (i = 0; i < map->num_sdhci; i++) { + versal_create_sdhci(s, &map->sdhci[i]); + } + versal_create_usbs(s, pic); versal_create_gems(s, pic); versal_create_admas(s, pic); - versal_create_sds(s, pic); versal_create_pmc_apb_irq_orgate(s, pic); versal_create_rtc(s, pic); versal_create_trng(s, pic); @@ -1089,6 +1119,23 @@ static void versal_realize(DeviceState *dev, Error **errp) &s->lpd.rpu.mr_ps_alias, 0); } +void versal_sdhci_plug_card(Versal *s, int sd_idx, BlockBackend *blk) +{ + DeviceState *sdhci, *card; + + sdhci = DEVICE(versal_get_child_idx(s, "sdhci", sd_idx)); + + if (sdhci == NULL) { + return; + } + + card = qdev_new(TYPE_SD_CARD); + object_property_add_child(OBJECT(sdhci), "card[*]", OBJECT(card)); + qdev_prop_set_drive_err(card, "drive", blk, &error_fatal); + qdev_realize_and_unref(card, qdev_get_child_bus(DEVICE(sdhci), "sd-bus"), + &error_fatal); +} + int versal_get_num_can(VersalVersion version) { const VersalMap *map = VERSION_TO_MAP[version]; @@ -1096,6 +1143,13 @@ int versal_get_num_can(VersalVersion version) return map->num_canfd; } +int versal_get_num_sdhci(VersalVersion version) +{ + const VersalMap *map = VERSION_TO_MAP[version]; + + return map->num_sdhci; +} + static void versal_base_init(Object *obj) { Versal *s = XLNX_VERSAL_BASE(obj); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 007c91b596ed9..4a7a2d85aac5b 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -16,7 +16,6 @@ #include "hw/sysbus.h" #include "hw/cpu/cluster.h" #include "hw/or-irq.h" -#include "hw/sd/sdhci.h" #include "hw/intc/arm_gicv3.h" #include "hw/dma/xlnx-zdma.h" #include "hw/net/cadence_gem.h" @@ -105,7 +104,6 @@ struct Versal { /* The Platform Management Controller subsystem. */ struct { struct { - SDHCIState sd[XLNX_VERSAL_NR_SDS]; XlnxVersalPmcIouSlcr slcr; struct { @@ -156,7 +154,10 @@ static inline void versal_set_fdt(Versal *s, void *fdt) s->cfg.fdt = fdt; } +void versal_sdhci_plug_card(Versal *s, int sd_idx, BlockBackend *blk); + int versal_get_num_can(VersalVersion version); +int versal_get_num_sdhci(VersalVersion version); /* Memory-map and IRQ definitions. Copied a subset from * auto-generated files. */ From 8aaeb96405d2e4b6593cb9d5d943a54e36cbccbc Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:24 +0200 Subject: [PATCH 1339/1794] hw/arm/xlnx-versal: gem: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the GEM ethernet controllers creation using the VersalMap structure. Note that the connection to the CRL is removed for now and will be re-added by next commits. The FDT nodes are created in reverse order compared to the devices creation to keep backward compatibility with the previous generated FDTs. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-7-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 54 ------------ hw/arm/xlnx-versal.c | 157 ++++++++++++++++++++++++++--------- include/hw/arm/xlnx-versal.h | 3 - 3 files changed, 117 insertions(+), 97 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 52852082d4b3e..0634cc90eace1 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -38,7 +38,6 @@ struct VersalVirt { int fdt_size; struct { uint32_t gic; - uint32_t ethernet_phy[2]; uint32_t clk_125Mhz; uint32_t clk_25Mhz; uint32_t usb; @@ -57,7 +56,6 @@ struct VersalVirt { static void fdt_create(VersalVirt *s) { MachineClass *mc = MACHINE_GET_CLASS(s); - int i; s->fdt = create_device_tree(&s->fdt_size); if (!s->fdt) { @@ -67,9 +65,6 @@ static void fdt_create(VersalVirt *s) /* Allocate all phandles. */ s->phandle.gic = qemu_fdt_alloc_phandle(s->fdt); - for (i = 0; i < ARRAY_SIZE(s->phandle.ethernet_phy); i++) { - s->phandle.ethernet_phy[i] = qemu_fdt_alloc_phandle(s->fdt); - } s->phandle.clk_25Mhz = qemu_fdt_alloc_phandle(s->fdt); s->phandle.clk_125Mhz = qemu_fdt_alloc_phandle(s->fdt); @@ -209,54 +204,6 @@ static void fdt_add_usb_xhci_nodes(VersalVirt *s) g_free(name); } -static void fdt_add_fixed_link_nodes(VersalVirt *s, char *gemname, - uint32_t phandle) -{ - char *name = g_strdup_printf("%s/fixed-link", gemname); - - qemu_fdt_add_subnode(s->fdt, name); - qemu_fdt_setprop_cell(s->fdt, name, "phandle", phandle); - qemu_fdt_setprop(s->fdt, name, "full-duplex", NULL, 0); - qemu_fdt_setprop_cell(s->fdt, name, "speed", 1000); - g_free(name); -} - -static void fdt_add_gem_nodes(VersalVirt *s) -{ - uint64_t addrs[] = { MM_GEM1, MM_GEM0 }; - unsigned int irqs[] = { VERSAL_GEM1_IRQ_0, VERSAL_GEM0_IRQ_0 }; - const char clocknames[] = "pclk\0hclk\0tx_clk\0rx_clk"; - const char compat_gem[] = "cdns,zynqmp-gem\0cdns,gem"; - int i; - - for (i = 0; i < ARRAY_SIZE(addrs); i++) { - char *name = g_strdup_printf("/ethernet@%" PRIx64, addrs[i]); - qemu_fdt_add_subnode(s->fdt, name); - - fdt_add_fixed_link_nodes(s, name, s->phandle.ethernet_phy[i]); - qemu_fdt_setprop_string(s->fdt, name, "phy-mode", "rgmii-id"); - qemu_fdt_setprop_cell(s->fdt, name, "phy-handle", - s->phandle.ethernet_phy[i]); - qemu_fdt_setprop_cells(s->fdt, name, "clocks", - s->phandle.clk_25Mhz, s->phandle.clk_25Mhz, - s->phandle.clk_125Mhz, s->phandle.clk_125Mhz); - qemu_fdt_setprop(s->fdt, name, "clock-names", - clocknames, sizeof(clocknames)); - qemu_fdt_setprop_cells(s->fdt, name, "interrupts", - GIC_FDT_IRQ_TYPE_SPI, irqs[i], - GIC_FDT_IRQ_FLAGS_LEVEL_HI, - GIC_FDT_IRQ_TYPE_SPI, irqs[i], - GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", - 2, addrs[i], 2, 0x1000); - qemu_fdt_setprop(s->fdt, name, "compatible", - compat_gem, sizeof(compat_gem)); - qemu_fdt_setprop_cell(s->fdt, name, "#address-cells", 1); - qemu_fdt_setprop_cell(s->fdt, name, "#size-cells", 0); - g_free(name); - } -} - static void fdt_add_zdma_nodes(VersalVirt *s) { const char clocknames[] = "clk_main\0clk_apb"; @@ -612,7 +559,6 @@ static void versal_virt_init(MachineState *machine) sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal); create_virtio_regions(s); - fdt_add_gem_nodes(s); fdt_add_gic_nodes(s); fdt_add_timer_nodes(s); fdt_add_zdma_nodes(s); diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index ff2f47daad9fe..7c53bc82a20c0 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -29,6 +29,7 @@ #include "hw/char/pl011.h" #include "hw/net/xlnx-versal-canfd.h" #include "hw/sd/sdhci.h" +#include "hw/net/cadence_gem.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -51,6 +52,14 @@ typedef struct VersalMap { VersalSimplePeriphMap sdhci[2]; size_t num_sdhci; + + struct VersalGemMap { + VersalSimplePeriphMap map; + size_t num_prio_queue; + const char *phy_mode; + const uint32_t speed; + } gem[3]; + size_t num_gem; } VersalMap; static const VersalMap VERSAL_MAP = { @@ -65,6 +74,10 @@ static const VersalMap VERSAL_MAP = { .sdhci[0] = { 0xf1040000, 126 }, .sdhci[1] = { 0xf1050000, 128 }, .num_sdhci = 2, + + .gem[0] = { { 0xff0c0000, 56 }, 2, "rgmii-id", 1000 }, + .gem[1] = { { 0xff0d0000, 58 }, 2, "rgmii-id", 1000 }, + .num_gem = 2, }; static const VersalMap *VERSION_TO_MAP[] = { @@ -111,6 +124,18 @@ static void versal_sysbus_connect_irq(Versal *s, SysBusDevice *sbd, sysbus_connect_irq(sbd, sbd_idx, irq); } +static void versal_qdev_connect_gpio_out(Versal *s, DeviceState *dev, + int dev_idx, int irq_idx) +{ + qemu_irq irq = versal_get_irq(s, irq_idx); + + if (irq == NULL) { + return; + } + + qdev_connect_gpio_out(dev, dev_idx, irq); +} + static inline char *versal_fdt_add_subnode(Versal *s, const char *path, uint64_t at, const char *compat, size_t compat_sz) @@ -140,6 +165,21 @@ static inline char *versal_fdt_add_simple_subnode(Versal *s, const char *path, return p; } +static inline DeviceState *create_or_gate(Versal *s, Object *parent, + const char *name, uint16_t num_lines, + int irq_idx) +{ + DeviceState *or; + + or = qdev_new(TYPE_OR_IRQ); + qdev_prop_set_uint16(or, "num-lines", num_lines); + object_property_add_child(parent, name, OBJECT(or)); + qdev_realize_and_unref(or, NULL, &error_abort); + versal_qdev_connect_gpio_out(s, or, 0, irq_idx); + + return or; +} + static void versal_create_apu_cpus(Versal *s) { int i; @@ -377,46 +417,82 @@ static void versal_create_usbs(Versal *s, qemu_irq *pic) memory_region_add_subregion(&s->mr_ps, MM_USB2_CTRL_REGS, mr); } -static void versal_create_gems(Versal *s, qemu_irq *pic) +static void versal_create_gem(Versal *s, + const struct VersalGemMap *map) { + DeviceState *dev; + MemoryRegion *mr; + DeviceState *or; int i; - for (i = 0; i < ARRAY_SIZE(s->lpd.iou.gem); i++) { - static const int irqs[] = { VERSAL_GEM0_IRQ_0, VERSAL_GEM1_IRQ_0}; - static const uint64_t addrs[] = { MM_GEM0, MM_GEM1 }; - char *name = g_strdup_printf("gem%d", i); - DeviceState *dev; - MemoryRegion *mr; - OrIRQState *or_irq; - - object_initialize_child(OBJECT(s), name, &s->lpd.iou.gem[i], - TYPE_CADENCE_GEM); - or_irq = &s->lpd.iou.gem_irq_orgate[i]; - object_initialize_child(OBJECT(s), "gem-irq-orgate[*]", - or_irq, TYPE_OR_IRQ); - dev = DEVICE(&s->lpd.iou.gem[i]); - qemu_configure_nic_device(dev, true, NULL); - object_property_set_int(OBJECT(dev), "phy-addr", 23, &error_abort); - object_property_set_int(OBJECT(dev), "num-priority-queues", 2, - &error_abort); - object_property_set_int(OBJECT(or_irq), - "num-lines", 2, &error_fatal); - qdev_realize(DEVICE(or_irq), NULL, &error_fatal); - qdev_connect_gpio_out(DEVICE(or_irq), 0, pic[irqs[i]]); + dev = qdev_new(TYPE_CADENCE_GEM); + object_property_add_child(OBJECT(s), "gem[*]", OBJECT(dev)); - object_property_set_link(OBJECT(dev), "dma", OBJECT(&s->mr_ps), - &error_abort); - sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal); + qemu_configure_nic_device(dev, true, NULL); + object_property_set_int(OBJECT(dev), "phy-addr", 23, &error_abort); + object_property_set_int(OBJECT(dev), "num-priority-queues", + map->num_prio_queue, &error_abort); - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); - memory_region_add_subregion(&s->mr_ps, addrs[i], mr); + object_property_set_link(OBJECT(dev), "dma", OBJECT(&s->mr_ps), + &error_abort); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(DEVICE(or_irq), 0)); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, qdev_get_gpio_in(DEVICE(or_irq), 1)); - g_free(name); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + memory_region_add_subregion(&s->mr_ps, map->map.addr, mr); + + /* + * The GEM controller exposes one IRQ line per priority queue. In Versal + * family devices, those are OR'ed together. + */ + or = create_or_gate(s, OBJECT(dev), "irq-orgate", + map->num_prio_queue, map->map.irq); + + for (i = 0; i < map->num_prio_queue; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, qdev_get_gpio_in(or, i)); + } +} + +static void versal_create_gem_fdt(Versal *s, + const struct VersalGemMap *map) +{ + int i; + g_autofree char *node; + g_autofree char *phy_node; + int phy_phandle; + const char compatible[] = "cdns,zynqmp-gem\0cdns,gem"; + const char clocknames[] = "pclk\0hclk\0tx_clk\0rx_clk"; + g_autofree uint32_t *irq_prop; + + node = versal_fdt_add_simple_subnode(s, "/ethernet", map->map.addr, 0x1000, + compatible, sizeof(compatible)); + phy_node = g_strdup_printf("%s/fixed-link", node); + phy_phandle = qemu_fdt_alloc_phandle(s->cfg.fdt); + + /* Fixed link PHY node */ + qemu_fdt_add_subnode(s->cfg.fdt, phy_node); + qemu_fdt_setprop_cell(s->cfg.fdt, phy_node, "phandle", phy_phandle); + qemu_fdt_setprop(s->cfg.fdt, phy_node, "full-duplex", NULL, 0); + qemu_fdt_setprop_cell(s->cfg.fdt, phy_node, "speed", map->speed); + + qemu_fdt_setprop_string(s->cfg.fdt, node, "phy-mode", map->phy_mode); + qemu_fdt_setprop_cell(s->cfg.fdt, node, "phy-handle", phy_phandle); + qemu_fdt_setprop_cells(s->cfg.fdt, node, "clocks", + s->phandle.clk_25mhz, s->phandle.clk_25mhz, + s->phandle.clk_125mhz, s->phandle.clk_125mhz); + qemu_fdt_setprop(s->cfg.fdt, node, "clock-names", + clocknames, sizeof(clocknames)); + + irq_prop = g_new(uint32_t, map->num_prio_queue * 3); + for (i = 0; i < map->num_prio_queue; i++) { + irq_prop[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI); + irq_prop[3 * i + 1] = cpu_to_be32(map->map.irq); + irq_prop[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI); } + qemu_fdt_setprop(s->cfg.fdt, node, "interrupts", irq_prop, + sizeof(uint32_t) * map->num_prio_queue * 3); } + static void versal_create_admas(Versal *s, qemu_irq *pic) { int i; @@ -902,14 +978,6 @@ static void versal_create_crl(Versal *s, qemu_irq *pic) &error_abort); } - for (i = 0; i < ARRAY_SIZE(s->lpd.iou.gem); i++) { - g_autofree gchar *name = g_strdup_printf("gem[%d]", i); - - object_property_set_link(OBJECT(&s->lpd.crl), - name, OBJECT(&s->lpd.iou.gem[i]), - &error_abort); - } - for (i = 0; i < ARRAY_SIZE(s->lpd.iou.adma); i++) { g_autofree gchar *name = g_strdup_printf("adma[%d]", i); @@ -1093,8 +1161,17 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_sdhci(s, &map->sdhci[i]); } + for (i = 0; i < map->num_gem; i++) { + versal_create_gem(s, &map->gem[i]); + /* + * Create fdt node in reverse order to keep backward compatibility with + * previous versions of the generated FDT. This affects Linux kernel + * interface naming order when persistent naming scheme is not in use. + */ + versal_create_gem_fdt(s, &map->gem[map->num_gem - 1 - i]); + } + versal_create_usbs(s, pic); - versal_create_gems(s, pic); versal_create_admas(s, pic); versal_create_pmc_apb_irq_orgate(s, pic); versal_create_rtc(s, pic); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 4a7a2d85aac5b..1fcc2b623da89 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -18,7 +18,6 @@ #include "hw/or-irq.h" #include "hw/intc/arm_gicv3.h" #include "hw/dma/xlnx-zdma.h" -#include "hw/net/cadence_gem.h" #include "hw/rtc/xlnx-zynqmp-rtc.h" #include "qom/object.h" #include "hw/usb/xlnx-usb-subsystem.h" @@ -78,8 +77,6 @@ struct Versal { MemoryRegion mr_ocm; struct { - CadenceGEMState gem[XLNX_VERSAL_NR_GEMS]; - OrIRQState gem_irq_orgate[XLNX_VERSAL_NR_GEMS]; XlnxZDMA adma[XLNX_VERSAL_NR_ADMAS]; VersalUsb2 usb; } iou; From 51668b79be5ac5d490ffc7fcde2a95f9a2460997 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:25 +0200 Subject: [PATCH 1340/1794] hw/arm/xlnx-versal: adma: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the ADMA creation using the VersalMap structure. Note that the connection to the CRL is removed for now and will be re-added by next commits. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-8-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 28 -------------- hw/arm/xlnx-versal.c | 74 ++++++++++++++++++++++++------------ include/hw/arm/xlnx-versal.h | 2 - 3 files changed, 49 insertions(+), 55 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 0634cc90eace1..418e4c6e9835d 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -204,33 +204,6 @@ static void fdt_add_usb_xhci_nodes(VersalVirt *s) g_free(name); } -static void fdt_add_zdma_nodes(VersalVirt *s) -{ - const char clocknames[] = "clk_main\0clk_apb"; - const char compat[] = "xlnx,zynqmp-dma-1.0"; - int i; - - for (i = XLNX_VERSAL_NR_ADMAS - 1; i >= 0; i--) { - uint64_t addr = MM_ADMA_CH0 + MM_ADMA_CH0_SIZE * i; - char *name = g_strdup_printf("/dma@%" PRIx64, addr); - - qemu_fdt_add_subnode(s->fdt, name); - - qemu_fdt_setprop_cell(s->fdt, name, "xlnx,bus-width", 64); - qemu_fdt_setprop_cells(s->fdt, name, "clocks", - s->phandle.clk_25Mhz, s->phandle.clk_25Mhz); - qemu_fdt_setprop(s->fdt, name, "clock-names", - clocknames, sizeof(clocknames)); - qemu_fdt_setprop_cells(s->fdt, name, "interrupts", - GIC_FDT_IRQ_TYPE_SPI, VERSAL_ADMA_IRQ_0 + i, - GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", - 2, addr, 2, 0x1000); - qemu_fdt_setprop(s->fdt, name, "compatible", compat, sizeof(compat)); - g_free(name); - } -} - static void fdt_add_rtc_node(VersalVirt *s) { const char compat[] = "xlnx,zynqmp-rtc"; @@ -561,7 +534,6 @@ static void versal_virt_init(MachineState *machine) fdt_add_gic_nodes(s); fdt_add_timer_nodes(s); - fdt_add_zdma_nodes(s); fdt_add_usb_xhci_nodes(s); fdt_add_rtc_node(s); fdt_add_bbram_node(s); diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 7c53bc82a20c0..5c2bd4be1f726 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -30,6 +30,7 @@ #include "hw/net/xlnx-versal-canfd.h" #include "hw/sd/sdhci.h" #include "hw/net/cadence_gem.h" +#include "hw/dma/xlnx-zdma.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -60,6 +61,16 @@ typedef struct VersalMap { const uint32_t speed; } gem[3]; size_t num_gem; + + struct VersalZDMAMap { + const char *name; + VersalSimplePeriphMap map; + size_t num_chan; + uint64_t chan_stride; + int irq_stride; + } zdma[2]; + size_t num_zdma; + } VersalMap; static const VersalMap VERSAL_MAP = { @@ -78,6 +89,9 @@ static const VersalMap VERSAL_MAP = { .gem[0] = { { 0xff0c0000, 56 }, 2, "rgmii-id", 1000 }, .gem[1] = { { 0xff0d0000, 58 }, 2, "rgmii-id", 1000 }, .num_gem = 2, + + .zdma[0] = { "adma", { 0xffa80000, 60 }, 8, 0x10000, 1 }, + .num_zdma = 1, }; static const VersalMap *VERSION_TO_MAP[] = { @@ -492,30 +506,45 @@ static void versal_create_gem_fdt(Versal *s, sizeof(uint32_t) * map->num_prio_queue * 3); } - -static void versal_create_admas(Versal *s, qemu_irq *pic) +static void versal_create_zdma(Versal *s, + const struct VersalZDMAMap *map) { - int i; + DeviceState *dev; + MemoryRegion *mr; + g_autofree char *name; + const char compatible[] = "xlnx,zynqmp-dma-1.0"; + const char clocknames[] = "clk_main\0clk_apb"; + size_t i; - for (i = 0; i < ARRAY_SIZE(s->lpd.iou.adma); i++) { - char *name = g_strdup_printf("adma%d", i); - DeviceState *dev; - MemoryRegion *mr; + name = g_strdup_printf("%s[*]", map->name); + + for (i = 0; i < map->num_chan; i++) { + uint64_t addr = map->map.addr + map->chan_stride * i; + int irq = map->map.irq + map->irq_stride * i; + g_autofree char *node; - object_initialize_child(OBJECT(s), name, &s->lpd.iou.adma[i], - TYPE_XLNX_ZDMA); - dev = DEVICE(&s->lpd.iou.adma[i]); + dev = qdev_new(TYPE_XLNX_ZDMA); + object_property_add_child(OBJECT(s), name, OBJECT(dev)); object_property_set_int(OBJECT(dev), "bus-width", 128, &error_abort); object_property_set_link(OBJECT(dev), "dma", OBJECT(get_system_memory()), &error_fatal); - sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); - memory_region_add_subregion(&s->mr_ps, - MM_ADMA_CH0 + i * MM_ADMA_CH0_SIZE, mr); - - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[VERSAL_ADMA_IRQ_0 + i]); - g_free(name); + memory_region_add_subregion(&s->mr_ps, addr, mr); + + versal_sysbus_connect_irq(s, SYS_BUS_DEVICE(dev), 0, irq); + + node = versal_fdt_add_simple_subnode(s, "/dma", addr, 0x1000, + compatible, sizeof(compatible)); + qemu_fdt_setprop_cell(s->cfg.fdt, node, "xlnx,bus-width", 64); + qemu_fdt_setprop_cells(s->cfg.fdt, node, "clocks", + s->phandle.clk_25mhz, s->phandle.clk_25mhz); + qemu_fdt_setprop(s->cfg.fdt, node, "clock-names", + clocknames, sizeof(clocknames)); + qemu_fdt_setprop_cells(s->cfg.fdt, node, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irq, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); } } @@ -978,14 +1007,6 @@ static void versal_create_crl(Versal *s, qemu_irq *pic) &error_abort); } - for (i = 0; i < ARRAY_SIZE(s->lpd.iou.adma); i++) { - g_autofree gchar *name = g_strdup_printf("adma[%d]", i); - - object_property_set_link(OBJECT(&s->lpd.crl), - name, OBJECT(&s->lpd.iou.adma[i]), - &error_abort); - } - object_property_set_link(OBJECT(&s->lpd.crl), "usb", OBJECT(&s->lpd.iou.usb), &error_abort); @@ -1171,8 +1192,11 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_gem_fdt(s, &map->gem[map->num_gem - 1 - i]); } + for (i = 0; i < map->num_zdma; i++) { + versal_create_zdma(s, &map->zdma[i]); + } + versal_create_usbs(s, pic); - versal_create_admas(s, pic); versal_create_pmc_apb_irq_orgate(s, pic); versal_create_rtc(s, pic); versal_create_trng(s, pic); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 1fcc2b623da89..4eeea98ff3437 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -17,7 +17,6 @@ #include "hw/cpu/cluster.h" #include "hw/or-irq.h" #include "hw/intc/arm_gicv3.h" -#include "hw/dma/xlnx-zdma.h" #include "hw/rtc/xlnx-zynqmp-rtc.h" #include "qom/object.h" #include "hw/usb/xlnx-usb-subsystem.h" @@ -77,7 +76,6 @@ struct Versal { MemoryRegion mr_ocm; struct { - XlnxZDMA adma[XLNX_VERSAL_NR_ADMAS]; VersalUsb2 usb; } iou; From 3d10ac78480f4bdb17b908733fa9d2cf30ea2939 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:26 +0200 Subject: [PATCH 1341/1794] hw/arm/xlnx-versal: xram: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the XRAM devices creation using the VersalMap structure. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-9-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 59 +++++++++++++++++++++--------------- include/hw/arm/xlnx-versal.h | 6 ---- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 5c2bd4be1f726..295fca3d60d8e 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -31,6 +31,7 @@ #include "hw/sd/sdhci.h" #include "hw/net/cadence_gem.h" #include "hw/dma/xlnx-zdma.h" +#include "hw/misc/xlnx-versal-xramc.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -71,6 +72,14 @@ typedef struct VersalMap { } zdma[2]; size_t num_zdma; + struct VersalXramMap { + uint64_t mem; + uint64_t mem_stride; + uint64_t ctrl; + uint64_t ctrl_stride; + int irq; + size_t num; + } xram; } VersalMap; static const VersalMap VERSAL_MAP = { @@ -92,6 +101,13 @@ static const VersalMap VERSAL_MAP = { .zdma[0] = { "adma", { 0xffa80000, 60 }, 8, 0x10000, 1 }, .num_zdma = 1, + + .xram = { + .num = 4, + .mem = 0xfe800000, .mem_stride = 1 * MiB, + .ctrl = 0xff8e0000, .ctrl_stride = 0x10000, + .irq = 79, + }, }; static const VersalMap *VERSION_TO_MAP[] = { @@ -641,37 +657,31 @@ static void versal_create_trng(Versal *s, qemu_irq *pic) sysbus_connect_irq(sbd, 0, pic[VERSAL_TRNG_IRQ]); } -static void versal_create_xrams(Versal *s, qemu_irq *pic) +static void versal_create_xrams(Versal *s, const struct VersalXramMap *map) { - int nr_xrams = ARRAY_SIZE(s->lpd.xram.ctrl); - DeviceState *orgate; - int i; + SysBusDevice *sbd; + MemoryRegion *mr; + DeviceState *or; + size_t i; - /* XRAM IRQs get ORed into a single line. */ - object_initialize_child(OBJECT(s), "xram-irq-orgate", - &s->lpd.xram.irq_orgate, TYPE_OR_IRQ); - orgate = DEVICE(&s->lpd.xram.irq_orgate); - object_property_set_int(OBJECT(orgate), - "num-lines", nr_xrams, &error_fatal); - qdev_realize(orgate, NULL, &error_fatal); - qdev_connect_gpio_out(orgate, 0, pic[VERSAL_XRAM_IRQ_0]); + or = create_or_gate(s, OBJECT(s), "xram-orgate", map->num, map->irq); - for (i = 0; i < ARRAY_SIZE(s->lpd.xram.ctrl); i++) { - SysBusDevice *sbd; - MemoryRegion *mr; + for (i = 0; i < map->num; i++) { + hwaddr ctrl, mem; + + sbd = SYS_BUS_DEVICE(qdev_new(TYPE_XLNX_XRAM_CTRL)); + object_property_add_child(OBJECT(s), "xram[*]", OBJECT(sbd)); + sysbus_realize_and_unref(sbd, &error_fatal); - object_initialize_child(OBJECT(s), "xram[*]", &s->lpd.xram.ctrl[i], - TYPE_XLNX_XRAM_CTRL); - sbd = SYS_BUS_DEVICE(&s->lpd.xram.ctrl[i]); - sysbus_realize(sbd, &error_fatal); + ctrl = map->ctrl + map->ctrl_stride * i; + mem = map->mem + map->mem_stride * i; mr = sysbus_mmio_get_region(sbd, 0); - memory_region_add_subregion(&s->mr_ps, - MM_XRAMC + i * MM_XRAMC_SIZE, mr); + memory_region_add_subregion(&s->mr_ps, ctrl, mr); mr = sysbus_mmio_get_region(sbd, 1); - memory_region_add_subregion(&s->mr_ps, MM_XRAM + i * MiB, mr); + memory_region_add_subregion(&s->mr_ps, mem, mr); - sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(orgate, i)); + sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(or, i)); } } @@ -1196,11 +1206,12 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_zdma(s, &map->zdma[i]); } + versal_create_xrams(s, &map->xram); + versal_create_usbs(s, pic); versal_create_pmc_apb_irq_orgate(s, pic); versal_create_rtc(s, pic); versal_create_trng(s, pic); - versal_create_xrams(s, pic); versal_create_bbram(s, pic); versal_create_efuse(s, pic); versal_create_pmc_iou_slcr(s, pic); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 4eeea98ff3437..71c3314b8b4bd 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -20,7 +20,6 @@ #include "hw/rtc/xlnx-zynqmp-rtc.h" #include "qom/object.h" #include "hw/usb/xlnx-usb-subsystem.h" -#include "hw/misc/xlnx-versal-xramc.h" #include "hw/nvram/xlnx-bbram.h" #include "hw/nvram/xlnx-versal-efuse.h" #include "hw/ssi/xlnx-versal-ospi.h" @@ -88,11 +87,6 @@ struct Versal { ARMCPU cpu[XLNX_VERSAL_NR_RCPUS]; } rpu; - struct { - OrIRQState irq_orgate; - XlnxXramCtrl ctrl[XLNX_VERSAL_NR_XRAM]; - } xram; - XlnxVersalCRL crl; } lpd; From a0b535a57b28fe2c58481fb07e17b2f4a5dcae2e Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:27 +0200 Subject: [PATCH 1342/1794] hw/arm/xlnx-versal: usb: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the USB controller creation using the VersalMap structure. Note that the connection to the CRL is removed for now and will be re-added by next commits. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-10-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 56 +-------------------------- hw/arm/xlnx-versal.c | 74 +++++++++++++++++++++++++++++------- include/hw/arm/xlnx-versal.h | 5 --- 3 files changed, 62 insertions(+), 73 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 418e4c6e9835d..5801598da7ccc 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -14,6 +14,7 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "system/device_tree.h" +#include "system/address-spaces.h" #include "hw/block/flash.h" #include "hw/boards.h" #include "hw/sysbus.h" @@ -40,8 +41,6 @@ struct VersalVirt { uint32_t gic; uint32_t clk_125Mhz; uint32_t clk_25Mhz; - uint32_t usb; - uint32_t dwc; } phandle; struct arm_boot_info binfo; @@ -68,8 +67,6 @@ static void fdt_create(VersalVirt *s) s->phandle.clk_25Mhz = qemu_fdt_alloc_phandle(s->fdt); s->phandle.clk_125Mhz = qemu_fdt_alloc_phandle(s->fdt); - s->phandle.usb = qemu_fdt_alloc_phandle(s->fdt); - s->phandle.dwc = qemu_fdt_alloc_phandle(s->fdt); /* Create /chosen node for load_dtb. */ qemu_fdt_add_subnode(s->fdt, "/chosen"); qemu_fdt_add_subnode(s->fdt, "/aliases"); @@ -154,56 +151,6 @@ static void fdt_add_timer_nodes(VersalVirt *s) compat, sizeof(compat)); } -static void fdt_add_usb_xhci_nodes(VersalVirt *s) -{ - const char clocknames[] = "bus_clk\0ref_clk"; - const char irq_name[] = "dwc_usb3"; - const char compatVersalDWC3[] = "xlnx,versal-dwc3"; - const char compatDWC3[] = "snps,dwc3"; - char *name = g_strdup_printf("/usb@%" PRIx32, MM_USB2_CTRL_REGS); - - qemu_fdt_add_subnode(s->fdt, name); - qemu_fdt_setprop(s->fdt, name, "compatible", - compatVersalDWC3, sizeof(compatVersalDWC3)); - qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", - 2, MM_USB2_CTRL_REGS, - 2, MM_USB2_CTRL_REGS_SIZE); - qemu_fdt_setprop(s->fdt, name, "clock-names", - clocknames, sizeof(clocknames)); - qemu_fdt_setprop_cells(s->fdt, name, "clocks", - s->phandle.clk_25Mhz, s->phandle.clk_125Mhz); - qemu_fdt_setprop(s->fdt, name, "ranges", NULL, 0); - qemu_fdt_setprop_cell(s->fdt, name, "#address-cells", 2); - qemu_fdt_setprop_cell(s->fdt, name, "#size-cells", 2); - qemu_fdt_setprop_cell(s->fdt, name, "phandle", s->phandle.usb); - g_free(name); - - name = g_strdup_printf("/usb@%" PRIx32 "/dwc3@%" PRIx32, - MM_USB2_CTRL_REGS, MM_USB_0); - qemu_fdt_add_subnode(s->fdt, name); - qemu_fdt_setprop(s->fdt, name, "compatible", - compatDWC3, sizeof(compatDWC3)); - qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", - 2, MM_USB_0, 2, MM_USB_0_SIZE); - qemu_fdt_setprop(s->fdt, name, "interrupt-names", - irq_name, sizeof(irq_name)); - qemu_fdt_setprop_cells(s->fdt, name, "interrupts", - GIC_FDT_IRQ_TYPE_SPI, VERSAL_USB0_IRQ_0, - GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop_cell(s->fdt, name, - "snps,quirk-frame-length-adjustment", 0x20); - qemu_fdt_setprop_cells(s->fdt, name, "#stream-id-cells", 1); - qemu_fdt_setprop_string(s->fdt, name, "dr_mode", "host"); - qemu_fdt_setprop_string(s->fdt, name, "phy-names", "usb3-phy"); - qemu_fdt_setprop(s->fdt, name, "snps,dis_u2_susphy_quirk", NULL, 0); - qemu_fdt_setprop(s->fdt, name, "snps,dis_u3_susphy_quirk", NULL, 0); - qemu_fdt_setprop(s->fdt, name, "snps,refclk_fladj", NULL, 0); - qemu_fdt_setprop(s->fdt, name, "snps,mask_phy_reset", NULL, 0); - qemu_fdt_setprop_cell(s->fdt, name, "phandle", s->phandle.dwc); - qemu_fdt_setprop_string(s->fdt, name, "maximum-speed", "high-speed"); - g_free(name); -} - static void fdt_add_rtc_node(VersalVirt *s) { const char compat[] = "xlnx,zynqmp-rtc"; @@ -534,7 +481,6 @@ static void versal_virt_init(MachineState *machine) fdt_add_gic_nodes(s); fdt_add_timer_nodes(s); - fdt_add_usb_xhci_nodes(s); fdt_add_rtc_node(s); fdt_add_bbram_node(s); fdt_add_efuse_ctrl_node(s); diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 295fca3d60d8e..946c01706740a 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -32,6 +32,7 @@ #include "hw/net/cadence_gem.h" #include "hw/dma/xlnx-zdma.h" #include "hw/misc/xlnx-versal-xramc.h" +#include "hw/usb/xlnx-usb-subsystem.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -80,6 +81,13 @@ typedef struct VersalMap { int irq; size_t num; } xram; + + struct VersalUsbMap { + uint64_t xhci; + uint64_t ctrl; + int irq; + } usb[2]; + size_t num_usb; } VersalMap; static const VersalMap VERSAL_MAP = { @@ -108,6 +116,9 @@ static const VersalMap VERSAL_MAP = { .ctrl = 0xff8e0000, .ctrl_stride = 0x10000, .irq = 79, }, + + .usb[0] = { .xhci = 0xfe200000, .ctrl = 0xff9d0000, .irq = 22 }, + .num_usb = 1, }; static const VersalMap *VERSION_TO_MAP[] = { @@ -422,29 +433,67 @@ static void versal_create_canfd(Versal *s, const VersalSimplePeriphMap *map, GIC_FDT_IRQ_FLAGS_LEVEL_HI); } -static void versal_create_usbs(Versal *s, qemu_irq *pic) +static void versal_create_usb(Versal *s, + const struct VersalUsbMap *map) { DeviceState *dev; MemoryRegion *mr; + g_autofree char *node, *subnode; + const char clocknames[] = "bus_clk\0ref_clk"; + const char irq_name[] = "dwc_usb3"; + const char compat_versal_dwc3[] = "xlnx,versal-dwc3"; + const char compat_dwc3[] = "snps,dwc3"; - object_initialize_child(OBJECT(s), "usb2", &s->lpd.iou.usb, - TYPE_XILINX_VERSAL_USB2); - dev = DEVICE(&s->lpd.iou.usb); + dev = qdev_new(TYPE_XILINX_VERSAL_USB2); + object_property_add_child(OBJECT(s), "usb[*]", OBJECT(dev)); object_property_set_link(OBJECT(dev), "dma", OBJECT(&s->mr_ps), &error_abort); qdev_prop_set_uint32(dev, "intrs", 1); qdev_prop_set_uint32(dev, "slots", 2); - sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); - memory_region_add_subregion(&s->mr_ps, MM_USB_0, mr); + memory_region_add_subregion(&s->mr_ps, map->xhci, mr); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[VERSAL_USB0_IRQ_0]); + versal_sysbus_connect_irq(s, SYS_BUS_DEVICE(dev), 0, map->irq); mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); - memory_region_add_subregion(&s->mr_ps, MM_USB2_CTRL_REGS, mr); + memory_region_add_subregion(&s->mr_ps, map->ctrl, mr); + + node = versal_fdt_add_simple_subnode(s, "/usb", map->ctrl, 0x10000, + compat_versal_dwc3, + sizeof(compat_versal_dwc3)); + qemu_fdt_setprop(s->cfg.fdt, node, "clock-names", + clocknames, sizeof(clocknames)); + qemu_fdt_setprop_cells(s->cfg.fdt, node, "clocks", + s->phandle.clk_25mhz, s->phandle.clk_125mhz); + qemu_fdt_setprop(s->cfg.fdt, node, "ranges", NULL, 0); + qemu_fdt_setprop_cell(s->cfg.fdt, node, "#address-cells", 2); + qemu_fdt_setprop_cell(s->cfg.fdt, node, "#size-cells", 2); + + subnode = g_strdup_printf("/%s/dwc3", node); + g_free(node); + + node = versal_fdt_add_simple_subnode(s, subnode, map->xhci, 0x10000, + compat_dwc3, + sizeof(compat_dwc3)); + qemu_fdt_setprop(s->cfg.fdt, node, "interrupt-names", + irq_name, sizeof(irq_name)); + qemu_fdt_setprop_cells(s->cfg.fdt, node, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, map->irq, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + qemu_fdt_setprop_cell(s->cfg.fdt, node, + "snps,quirk-frame-length-adjustment", 0x20); + qemu_fdt_setprop_cells(s->cfg.fdt, node, "#stream-id-cells", 1); + qemu_fdt_setprop_string(s->cfg.fdt, node, "dr_mode", "host"); + qemu_fdt_setprop_string(s->cfg.fdt, node, "phy-names", "usb3-phy"); + qemu_fdt_setprop(s->cfg.fdt, node, "snps,dis_u2_susphy_quirk", NULL, 0); + qemu_fdt_setprop(s->cfg.fdt, node, "snps,dis_u3_susphy_quirk", NULL, 0); + qemu_fdt_setprop(s->cfg.fdt, node, "snps,refclk_fladj", NULL, 0); + qemu_fdt_setprop(s->cfg.fdt, node, "snps,mask_phy_reset", NULL, 0); + qemu_fdt_setprop_string(s->cfg.fdt, node, "maximum-speed", "high-speed"); } static void versal_create_gem(Versal *s, @@ -1017,10 +1066,6 @@ static void versal_create_crl(Versal *s, qemu_irq *pic) &error_abort); } - object_property_set_link(OBJECT(&s->lpd.crl), - "usb", OBJECT(&s->lpd.iou.usb), - &error_abort); - sysbus_realize(sbd, &error_fatal); memory_region_add_subregion(&s->mr_ps, MM_CRL, sysbus_mmio_get_region(sbd, 0)); @@ -1208,7 +1253,10 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_xrams(s, &map->xram); - versal_create_usbs(s, pic); + for (i = 0; i < map->num_usb; i++) { + versal_create_usb(s, &map->usb[i]); + } + versal_create_pmc_apb_irq_orgate(s, pic); versal_create_rtc(s, pic); versal_create_trng(s, pic); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 71c3314b8b4bd..5d4b30f0ff9ab 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -19,7 +19,6 @@ #include "hw/intc/arm_gicv3.h" #include "hw/rtc/xlnx-zynqmp-rtc.h" #include "qom/object.h" -#include "hw/usb/xlnx-usb-subsystem.h" #include "hw/nvram/xlnx-bbram.h" #include "hw/nvram/xlnx-versal-efuse.h" #include "hw/ssi/xlnx-versal-ospi.h" @@ -74,10 +73,6 @@ struct Versal { struct { MemoryRegion mr_ocm; - struct { - VersalUsb2 usb; - } iou; - /* Real-time Processing Unit. */ struct { MemoryRegion mr; From 98cb650c5cd73e28ec389373dcc7a656bf70368a Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:28 +0200 Subject: [PATCH 1343/1794] hw/arm/xlnx-versal: efuse: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactore the eFuse devices creation using the VersalMap structure. Note that the corresponding FDT nodes are removed. They do not correspond to any real node in standard Versal DTBs. No matching drivers exist for them. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-11-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 43 ++------------------ hw/arm/xlnx-versal.c | 78 +++++++++++++++++++++++------------- include/hw/arm/xlnx-versal.h | 5 +-- 3 files changed, 54 insertions(+), 72 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 5801598da7ccc..b6c49dafe09bd 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -192,41 +192,6 @@ static void fdt_add_bbram_node(VersalVirt *s) g_free(name); } -static void fdt_add_efuse_ctrl_node(VersalVirt *s) -{ - const char compat[] = TYPE_XLNX_VERSAL_EFUSE_CTRL; - const char interrupt_names[] = "pmc_efuse"; - char *name = g_strdup_printf("/pmc_efuse@%x", MM_PMC_EFUSE_CTRL); - - qemu_fdt_add_subnode(s->fdt, name); - - qemu_fdt_setprop_cells(s->fdt, name, "interrupts", - GIC_FDT_IRQ_TYPE_SPI, VERSAL_EFUSE_IRQ, - GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop(s->fdt, name, "interrupt-names", - interrupt_names, sizeof(interrupt_names)); - qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", - 2, MM_PMC_EFUSE_CTRL, - 2, MM_PMC_EFUSE_CTRL_SIZE); - qemu_fdt_setprop(s->fdt, name, "compatible", compat, sizeof(compat)); - g_free(name); -} - -static void fdt_add_efuse_cache_node(VersalVirt *s) -{ - const char compat[] = TYPE_XLNX_VERSAL_EFUSE_CACHE; - char *name = g_strdup_printf("/xlnx_pmc_efuse_cache@%x", - MM_PMC_EFUSE_CACHE); - - qemu_fdt_add_subnode(s->fdt, name); - - qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", - 2, MM_PMC_EFUSE_CACHE, - 2, MM_PMC_EFUSE_CACHE_SIZE); - qemu_fdt_setprop(s->fdt, name, "compatible", compat, sizeof(compat)); - g_free(name); -} - static void fdt_nop_memory_nodes(void *fdt, Error **errp) { Error *err = NULL; @@ -393,7 +358,7 @@ static void bbram_attach_drive(XlnxBBRam *dev) } } -static void efuse_attach_drive(XlnxEFuse *dev) +static void efuse_attach_drive(VersalVirt *s) { DriveInfo *dinfo; BlockBackend *blk; @@ -401,7 +366,7 @@ static void efuse_attach_drive(XlnxEFuse *dev) dinfo = drive_get_by_index(IF_PFLASH, 1); blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; if (blk) { - qdev_prop_set_drive(DEVICE(dev), "drive", blk); + versal_efuse_attach_drive(&s->soc, blk); } } @@ -483,8 +448,6 @@ static void versal_virt_init(MachineState *machine) fdt_add_timer_nodes(s); fdt_add_rtc_node(s); fdt_add_bbram_node(s); - fdt_add_efuse_ctrl_node(s); - fdt_add_efuse_cache_node(s); fdt_add_cpu_nodes(s, psci_conduit); fdt_add_clk_node(s, "/old-clk125", 125000000, s->phandle.clk_125Mhz); fdt_add_clk_node(s, "/old-clk25", 25000000, s->phandle.clk_25Mhz); @@ -498,7 +461,7 @@ static void versal_virt_init(MachineState *machine) bbram_attach_drive(&s->soc.pmc.bbram); /* Attach efuse backend, if given */ - efuse_attach_drive(&s->soc.pmc.efuse); + efuse_attach_drive(s); /* Plug SD cards */ for (i = 0; i < versal_get_num_sdhci(VERSAL_VER_VERSAL); i++) { diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 946c01706740a..f8291ac614bf7 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -33,6 +33,7 @@ #include "hw/dma/xlnx-zdma.h" #include "hw/misc/xlnx-versal-xramc.h" #include "hw/usb/xlnx-usb-subsystem.h" +#include "hw/nvram/xlnx-versal-efuse.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -88,6 +89,12 @@ typedef struct VersalMap { int irq; } usb[2]; size_t num_usb; + + struct VersalEfuseMap { + uint64_t ctrl; + uint64_t cache; + int irq; + } efuse; } VersalMap; static const VersalMap VERSAL_MAP = { @@ -119,6 +126,8 @@ static const VersalMap VERSAL_MAP = { .usb[0] = { .xhci = 0xfe200000, .ctrl = 0xff9d0000, .irq = 22 }, .num_usb = 1, + + .efuse = { .ctrl = 0xf1240000, .cache = 0xf1250000, .irq = 139 }, }; static const VersalMap *VERSION_TO_MAP[] = { @@ -752,42 +761,41 @@ static void versal_create_bbram(Versal *s, qemu_irq *pic) qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 1)); } -static void versal_realize_efuse_part(Versal *s, Object *dev, hwaddr base) +static void versal_create_efuse(Versal *s, + const struct VersalEfuseMap *map) { - SysBusDevice *part = SYS_BUS_DEVICE(dev); + DeviceState *bits; + DeviceState *ctrl; + DeviceState *cache; - object_property_set_link(OBJECT(part), "efuse", - OBJECT(&s->pmc.efuse), &error_abort); + ctrl = qdev_new(TYPE_XLNX_VERSAL_EFUSE_CTRL); + cache = qdev_new(TYPE_XLNX_VERSAL_EFUSE_CACHE); + bits = qdev_new(TYPE_XLNX_EFUSE); - sysbus_realize(part, &error_abort); - memory_region_add_subregion(&s->mr_ps, base, - sysbus_mmio_get_region(part, 0)); -} + qdev_prop_set_uint32(bits, "efuse-nr", 3); + qdev_prop_set_uint32(bits, "efuse-size", 8192); -static void versal_create_efuse(Versal *s, qemu_irq *pic) -{ - Object *bits = OBJECT(&s->pmc.efuse); - Object *ctrl = OBJECT(&s->pmc.efuse_ctrl); - Object *cache = OBJECT(&s->pmc.efuse_cache); + object_property_add_child(OBJECT(s), "efuse", OBJECT(bits)); + qdev_realize_and_unref(bits, NULL, &error_abort); - object_initialize_child(OBJECT(s), "efuse-ctrl", &s->pmc.efuse_ctrl, - TYPE_XLNX_VERSAL_EFUSE_CTRL); + object_property_set_link(OBJECT(ctrl), "efuse", OBJECT(bits), &error_abort); - object_initialize_child(OBJECT(s), "efuse-cache", &s->pmc.efuse_cache, - TYPE_XLNX_VERSAL_EFUSE_CACHE); + object_property_set_link(OBJECT(cache), "efuse", OBJECT(bits), + &error_abort); - object_initialize_child_with_props(ctrl, "xlnx-efuse@0", bits, - sizeof(s->pmc.efuse), - TYPE_XLNX_EFUSE, &error_abort, - "efuse-nr", "3", - "efuse-size", "8192", - NULL); + object_property_add_child(OBJECT(s), "efuse-cache", OBJECT(cache)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(cache), &error_abort); - qdev_realize(DEVICE(bits), NULL, &error_abort); - versal_realize_efuse_part(s, ctrl, MM_PMC_EFUSE_CTRL); - versal_realize_efuse_part(s, cache, MM_PMC_EFUSE_CACHE); + object_property_add_child(OBJECT(s), "efuse-ctrl", OBJECT(ctrl)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(ctrl), &error_abort); - sysbus_connect_irq(SYS_BUS_DEVICE(ctrl), 0, pic[VERSAL_EFUSE_IRQ]); + memory_region_add_subregion(&s->mr_ps, map->ctrl, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ctrl), + 0)); + memory_region_add_subregion(&s->mr_ps, map->cache, + sysbus_mmio_get_region(SYS_BUS_DEVICE(cache), + 0)); + versal_sysbus_connect_irq(s, SYS_BUS_DEVICE(ctrl), 0, map->irq); } static void versal_create_pmc_iou_slcr(Versal *s, qemu_irq *pic) @@ -1257,11 +1265,12 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_usb(s, &map->usb[i]); } + versal_create_efuse(s, &map->efuse); + versal_create_pmc_apb_irq_orgate(s, pic); versal_create_rtc(s, pic); versal_create_trng(s, pic); versal_create_bbram(s, pic); - versal_create_efuse(s, pic); versal_create_pmc_iou_slcr(s, pic); versal_create_ospi(s, pic); versal_create_crl(s, pic); @@ -1296,6 +1305,19 @@ void versal_sdhci_plug_card(Versal *s, int sd_idx, BlockBackend *blk) &error_fatal); } +void versal_efuse_attach_drive(Versal *s, BlockBackend *blk) +{ + DeviceState *efuse; + + efuse = DEVICE(versal_get_child(s, "efuse")); + + if (efuse == NULL) { + return; + } + + qdev_prop_set_drive(efuse, "drive", blk); +} + int versal_get_num_can(VersalVersion version) { const VersalMap *map = VERSION_TO_MAP[version]; diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 5d4b30f0ff9ab..79ca9b1332104 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -20,7 +20,6 @@ #include "hw/rtc/xlnx-zynqmp-rtc.h" #include "qom/object.h" #include "hw/nvram/xlnx-bbram.h" -#include "hw/nvram/xlnx-versal-efuse.h" #include "hw/ssi/xlnx-versal-ospi.h" #include "hw/dma/xlnx_csu_dma.h" #include "hw/misc/xlnx-versal-crl.h" @@ -102,9 +101,6 @@ struct Versal { XlnxZynqMPRTC rtc; XlnxVersalTRng trng; XlnxBBRam bbram; - XlnxEFuse efuse; - XlnxVersalEFuseCtrl efuse_ctrl; - XlnxVersalEFuseCache efuse_cache; XlnxVersalCFUAPB cfu_apb; XlnxVersalCFUFDRO cfu_fdro; XlnxVersalCFUSFR cfu_sfr; @@ -139,6 +135,7 @@ static inline void versal_set_fdt(Versal *s, void *fdt) } void versal_sdhci_plug_card(Versal *s, int sd_idx, BlockBackend *blk); +void versal_efuse_attach_drive(Versal *s, BlockBackend *blk); int versal_get_num_can(VersalVersion version); int versal_get_num_sdhci(VersalVersion version); From 6532bc7cc7f5560b7782a2799d9d5f0ac2719c42 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:29 +0200 Subject: [PATCH 1344/1794] hw/arm/xlnx-versal: ospi: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the OSPI controller creation using the VersalMap structure. Note that the connection to the PMC IOU SLCR is removed for now and will be re-added by next commits. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-12-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 41 ++++------ hw/arm/xlnx-versal.c | 142 ++++++++++++++++++++--------------- include/hw/arm/xlnx-versal.h | 12 +-- 3 files changed, 98 insertions(+), 97 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index b6c49dafe09bd..a948e24aea066 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -48,8 +48,8 @@ struct VersalVirt { struct { bool secure; + char *ospi_model; } cfg; - char *ospi_model; }; static void fdt_create(VersalVirt *s) @@ -381,15 +381,15 @@ static char *versal_get_ospi_model(Object *obj, Error **errp) { VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); - return g_strdup(s->ospi_model); + return g_strdup(s->cfg.ospi_model); } static void versal_set_ospi_model(Object *obj, const char *value, Error **errp) { VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); - g_free(s->ospi_model); - s->ospi_model = g_strdup(value); + g_free(s->cfg.ospi_model); + s->cfg.ospi_model = g_strdup(value); } @@ -482,38 +482,27 @@ static void versal_virt_init(MachineState *machine) arm_load_kernel(&s->soc.fpd.apu.cpu[0], machine, &s->binfo); for (i = 0; i < XLNX_VERSAL_NUM_OSPI_FLASH; i++) { - BusState *spi_bus; - DeviceState *flash_dev; ObjectClass *flash_klass; - qemu_irq cs_line; DriveInfo *dinfo = drive_get(IF_MTD, 0, i); + BlockBackend *blk; + const char *mdl; - spi_bus = qdev_get_child_bus(DEVICE(&s->soc.pmc.iou.ospi), "spi0"); - - if (s->ospi_model) { - flash_klass = object_class_by_name(s->ospi_model); + if (s->cfg.ospi_model) { + flash_klass = object_class_by_name(s->cfg.ospi_model); if (!flash_klass || object_class_is_abstract(flash_klass) || !object_class_dynamic_cast(flash_klass, TYPE_M25P80)) { error_report("'%s' is either abstract or" - " not a subtype of m25p80", s->ospi_model); + " not a subtype of m25p80", s->cfg.ospi_model); exit(1); } + mdl = s->cfg.ospi_model; + } else { + mdl = "mt35xu01g"; } - flash_dev = qdev_new(s->ospi_model ? s->ospi_model : "mt35xu01g"); - - if (dinfo) { - qdev_prop_set_drive_err(flash_dev, "drive", - blk_by_legacy_dinfo(dinfo), &error_fatal); - } - qdev_prop_set_uint8(flash_dev, "cs", i); - qdev_realize_and_unref(flash_dev, spi_bus, &error_fatal); - - cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0); - - sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.pmc.iou.ospi), - i + 1, cs_line); + blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; + versal_ospi_create_flash(&s->soc, i, mdl, blk); } } @@ -542,7 +531,7 @@ static void versal_virt_machine_finalize(Object *obj) { VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); - g_free(s->ospi_model); + g_free(s->cfg.ospi_model); g_free(s->canbus); } diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index f8291ac614bf7..964250bf15187 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -34,6 +34,7 @@ #include "hw/misc/xlnx-versal-xramc.h" #include "hw/usb/xlnx-usb-subsystem.h" #include "hw/nvram/xlnx-versal-efuse.h" +#include "hw/ssi/xlnx-versal-ospi.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -95,6 +96,15 @@ typedef struct VersalMap { uint64_t cache; int irq; } efuse; + + struct VersalOspiMap { + uint64_t ctrl; + uint64_t dac; + uint64_t dac_sz; + uint64_t dma_src; + uint64_t dma_dst; + int irq; + } ospi; } VersalMap; static const VersalMap VERSAL_MAP = { @@ -128,6 +138,13 @@ static const VersalMap VERSAL_MAP = { .num_usb = 1, .efuse = { .ctrl = 0xf1240000, .cache = 0xf1250000, .irq = 139 }, + + .ospi = { + .ctrl = 0xf1010000, + .dac = 0xc0000000, .dac_sz = 0x20000000, + .dma_src = 0xf1011000, .dma_dst = 0xf1011800, + .irq = 124, + }, }; static const VersalMap *VERSION_TO_MAP[] = { @@ -815,95 +832,74 @@ static void versal_create_pmc_iou_slcr(Versal *s, qemu_irq *pic) qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 2)); } -static void versal_create_ospi(Versal *s, qemu_irq *pic) +static DeviceState *versal_create_ospi(Versal *s, + const struct VersalOspiMap *map) { SysBusDevice *sbd; MemoryRegion *mr_dac; - qemu_irq ospi_mux_sel; - DeviceState *orgate; + DeviceState *dev, *dma_dst, *dma_src, *orgate; + MemoryRegion *linear_mr = g_new(MemoryRegion, 1); - memory_region_init(&s->pmc.iou.ospi.linear_mr, OBJECT(s), - "versal-ospi-linear-mr" , MM_PMC_OSPI_DAC_SIZE); + dev = qdev_new(TYPE_XILINX_VERSAL_OSPI); + object_property_add_child(OBJECT(s), "ospi", OBJECT(dev)); - object_initialize_child(OBJECT(s), "versal-ospi", &s->pmc.iou.ospi.ospi, - TYPE_XILINX_VERSAL_OSPI); + memory_region_init(linear_mr, OBJECT(dev), "linear-mr", map->dac_sz); - mr_dac = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->pmc.iou.ospi.ospi), 1); - memory_region_add_subregion(&s->pmc.iou.ospi.linear_mr, 0x0, mr_dac); + mr_dac = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); + memory_region_add_subregion(linear_mr, 0x0, mr_dac); /* Create the OSPI destination DMA */ - object_initialize_child(OBJECT(s), "versal-ospi-dma-dst", - &s->pmc.iou.ospi.dma_dst, - TYPE_XLNX_CSU_DMA); - - object_property_set_link(OBJECT(&s->pmc.iou.ospi.dma_dst), - "dma", OBJECT(get_system_memory()), - &error_abort); + dma_dst = qdev_new(TYPE_XLNX_CSU_DMA); + object_property_add_child(OBJECT(dev), "dma-dst-dev", OBJECT(dma_dst)); + object_property_set_link(OBJECT(dma_dst), "dma", + OBJECT(get_system_memory()), &error_abort); - sbd = SYS_BUS_DEVICE(&s->pmc.iou.ospi.dma_dst); - sysbus_realize(sbd, &error_fatal); + sbd = SYS_BUS_DEVICE(dma_dst); + sysbus_realize_and_unref(sbd, &error_fatal); - memory_region_add_subregion(&s->mr_ps, MM_PMC_OSPI_DMA_DST, + memory_region_add_subregion(&s->mr_ps, map->dma_dst, sysbus_mmio_get_region(sbd, 0)); /* Create the OSPI source DMA */ - object_initialize_child(OBJECT(s), "versal-ospi-dma-src", - &s->pmc.iou.ospi.dma_src, - TYPE_XLNX_CSU_DMA); + dma_src = qdev_new(TYPE_XLNX_CSU_DMA); + object_property_add_child(OBJECT(dev), "dma-src-dev", OBJECT(dma_src)); - object_property_set_bool(OBJECT(&s->pmc.iou.ospi.dma_src), "is-dst", - false, &error_abort); + object_property_set_bool(OBJECT(dma_src), "is-dst", false, &error_abort); - object_property_set_link(OBJECT(&s->pmc.iou.ospi.dma_src), - "dma", OBJECT(mr_dac), &error_abort); - - object_property_set_link(OBJECT(&s->pmc.iou.ospi.dma_src), - "stream-connected-dma", - OBJECT(&s->pmc.iou.ospi.dma_dst), + object_property_set_link(OBJECT(dma_src), "dma", OBJECT(mr_dac), &error_abort); - sbd = SYS_BUS_DEVICE(&s->pmc.iou.ospi.dma_src); - sysbus_realize(sbd, &error_fatal); + object_property_set_link(OBJECT(dma_src), "stream-connected-dma", + OBJECT(dma_dst), &error_abort); - memory_region_add_subregion(&s->mr_ps, MM_PMC_OSPI_DMA_SRC, + sbd = SYS_BUS_DEVICE(dma_src); + sysbus_realize_and_unref(sbd, &error_fatal); + + memory_region_add_subregion(&s->mr_ps, map->dma_src, sysbus_mmio_get_region(sbd, 0)); /* Realize the OSPI */ - object_property_set_link(OBJECT(&s->pmc.iou.ospi.ospi), "dma-src", - OBJECT(&s->pmc.iou.ospi.dma_src), &error_abort); + object_property_set_link(OBJECT(dev), "dma-src", + OBJECT(dma_src), &error_abort); - sbd = SYS_BUS_DEVICE(&s->pmc.iou.ospi.ospi); - sysbus_realize(sbd, &error_fatal); + sbd = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sbd, &error_fatal); - memory_region_add_subregion(&s->mr_ps, MM_PMC_OSPI, + memory_region_add_subregion(&s->mr_ps, map->ctrl, sysbus_mmio_get_region(sbd, 0)); - memory_region_add_subregion(&s->mr_ps, MM_PMC_OSPI_DAC, - &s->pmc.iou.ospi.linear_mr); - - /* ospi_mux_sel */ - ospi_mux_sel = qdev_get_gpio_in_named(DEVICE(&s->pmc.iou.ospi.ospi), - "ospi-mux-sel", 0); - qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr), "ospi-mux-sel", 0, - ospi_mux_sel); + memory_region_add_subregion(&s->mr_ps, map->dac, + linear_mr); /* OSPI irq */ - object_initialize_child(OBJECT(s), "ospi-irq-orgate", - &s->pmc.iou.ospi.irq_orgate, TYPE_OR_IRQ); - object_property_set_int(OBJECT(&s->pmc.iou.ospi.irq_orgate), - "num-lines", NUM_OSPI_IRQ_LINES, &error_fatal); + orgate = create_or_gate(s, OBJECT(dev), "irq-orgate", NUM_OSPI_IRQ_LINES, + map->irq); - orgate = DEVICE(&s->pmc.iou.ospi.irq_orgate); - qdev_realize(orgate, NULL, &error_fatal); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(orgate, 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(dma_src), 0, qdev_get_gpio_in(orgate, 1)); + sysbus_connect_irq(SYS_BUS_DEVICE(dma_dst), 0, qdev_get_gpio_in(orgate, 2)); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->pmc.iou.ospi.ospi), 0, - qdev_get_gpio_in(orgate, 0)); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->pmc.iou.ospi.dma_src), 0, - qdev_get_gpio_in(orgate, 1)); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->pmc.iou.ospi.dma_dst), 0, - qdev_get_gpio_in(orgate, 2)); - - qdev_connect_gpio_out(orgate, 0, pic[VERSAL_OSPI_IRQ]); + return dev; } static void versal_create_cfu(Versal *s, qemu_irq *pic) @@ -1266,13 +1262,13 @@ static void versal_realize(DeviceState *dev, Error **errp) } versal_create_efuse(s, &map->efuse); + versal_create_ospi(s, &map->ospi); versal_create_pmc_apb_irq_orgate(s, pic); versal_create_rtc(s, pic); versal_create_trng(s, pic); versal_create_bbram(s, pic); versal_create_pmc_iou_slcr(s, pic); - versal_create_ospi(s, pic); versal_create_crl(s, pic); versal_create_cfu(s, pic); versal_map_ddr(s); @@ -1318,6 +1314,30 @@ void versal_efuse_attach_drive(Versal *s, BlockBackend *blk) qdev_prop_set_drive(efuse, "drive", blk); } +void versal_ospi_create_flash(Versal *s, int flash_idx, const char *flash_mdl, + BlockBackend *blk) +{ + BusState *spi_bus; + DeviceState *flash, *ospi; + qemu_irq cs_line; + + ospi = DEVICE(versal_get_child(s, "ospi")); + spi_bus = qdev_get_child_bus(ospi, "spi0"); + + flash = qdev_new(flash_mdl); + + if (blk) { + qdev_prop_set_drive_err(flash, "drive", blk, &error_fatal); + } + qdev_prop_set_uint8(flash, "cs", flash_idx); + qdev_realize_and_unref(flash, spi_bus, &error_fatal); + + cs_line = qdev_get_gpio_in_named(flash, SSI_GPIO_CS, 0); + + sysbus_connect_irq(SYS_BUS_DEVICE(ospi), + flash_idx + 1, cs_line); +} + int versal_get_num_can(VersalVersion version) { const VersalMap *map = VERSION_TO_MAP[version]; diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 79ca9b1332104..b7ef255d6fd44 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -20,8 +20,6 @@ #include "hw/rtc/xlnx-zynqmp-rtc.h" #include "qom/object.h" #include "hw/nvram/xlnx-bbram.h" -#include "hw/ssi/xlnx-versal-ospi.h" -#include "hw/dma/xlnx_csu_dma.h" #include "hw/misc/xlnx-versal-crl.h" #include "hw/misc/xlnx-versal-pmc-iou-slcr.h" #include "hw/misc/xlnx-versal-trng.h" @@ -88,14 +86,6 @@ struct Versal { struct { struct { XlnxVersalPmcIouSlcr slcr; - - struct { - XlnxVersalOspi ospi; - XlnxCSUDMA dma_src; - XlnxCSUDMA dma_dst; - MemoryRegion linear_mr; - OrIRQState irq_orgate; - } ospi; } iou; XlnxZynqMPRTC rtc; @@ -136,6 +126,8 @@ static inline void versal_set_fdt(Versal *s, void *fdt) void versal_sdhci_plug_card(Versal *s, int sd_idx, BlockBackend *blk); void versal_efuse_attach_drive(Versal *s, BlockBackend *blk); +void versal_ospi_create_flash(Versal *s, int flash_idx, const char *flash_mdl, + BlockBackend *blk); int versal_get_num_can(VersalVersion version); int versal_get_num_sdhci(VersalVersion version); From 1bec18fc13dce2f9a066fcb52b5d15b6d3141499 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:30 +0200 Subject: [PATCH 1345/1794] hw/arm/xlnx-versal: VersalMap: add support for OR'ed IRQs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve the IRQ index in the VersalMap structure to turn it into a descriptor: - the lower 16 bits still represent the IRQ index - bit 18 is used to indicate a shared IRQ connected to a OR gate - bits 19 to 22 indicate the index on the OR gate. This allows to share an IRQ among multiple devices. An OR gate is created to connect the devices to the actual IRQ pin. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-13-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 63 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 964250bf15187..d3a084a0639e0 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -43,6 +43,17 @@ #define VERSAL_NUM_PMC_APB_IRQS 18 #define NUM_OSPI_IRQ_LINES 3 +/* + * IRQ descriptor to catch the following cases: + * - Multiple devices can connect to the same IRQ. They are OR'ed together. + */ +FIELD(VERSAL_IRQ, IRQ, 0, 16) +FIELD(VERSAL_IRQ, ORED, 18, 1) +FIELD(VERSAL_IRQ, OR_IDX, 19, 4) /* input index on the IRQ OR gate */ + +#define OR_IRQ(irq, or_idx) \ + (R_VERSAL_IRQ_ORED_MASK | ((or_idx) << R_VERSAL_IRQ_OR_IDX_SHIFT) | (irq)) + typedef struct VersalSimplePeriphMap { uint64_t addr; int irq; @@ -174,9 +185,53 @@ static inline Object *versal_get_child_idx(Versal *s, const char *child, return versal_get_child(s, n); } +/* + * When the R_VERSAL_IRQ_ORED flag is set on an IRQ descriptor, this function is + * used to return the corresponding or gate input IRQ. The or gate is created if + * not already existant. + * + * Or gates are placed under the /soc/irq-or-gates QOM container. + */ +static qemu_irq versal_get_irq_or_gate_in(Versal *s, int irq_idx, + qemu_irq target_irq) +{ + Object *container = versal_get_child(s, "irq-or-gates"); + DeviceState *dev; + g_autofree char *name; + int idx, or_idx; + + idx = FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ); + or_idx = FIELD_EX32(irq_idx, VERSAL_IRQ, OR_IDX); + + name = g_strdup_printf("irq[%d]", idx); + dev = DEVICE(object_resolve_path_at(container, name)); + + if (dev == NULL) { + dev = qdev_new(TYPE_OR_IRQ); + object_property_add_child(container, name, OBJECT(dev)); + qdev_prop_set_uint16(dev, "num-lines", 1 << R_VERSAL_IRQ_OR_IDX_LENGTH); + qdev_realize_and_unref(dev, NULL, &error_abort); + qdev_connect_gpio_out(dev, 0, target_irq); + } + + return qdev_get_gpio_in(dev, or_idx); +} + static qemu_irq versal_get_irq(Versal *s, int irq_idx) { - return qdev_get_gpio_in(DEVICE(&s->fpd.apu.gic), irq_idx); + qemu_irq irq; + bool ored; + + ored = FIELD_EX32(irq_idx, VERSAL_IRQ, ORED); + + irq = qdev_get_gpio_in(DEVICE(&s->fpd.apu.gic), + FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ)); + + if (ored) { + irq = versal_get_irq_or_gate_in(s, irq_idx, irq); + } + + return irq; } static void versal_sysbus_connect_irq(Versal *s, SysBusDevice *sbd, @@ -1217,6 +1272,7 @@ static void versal_realize(DeviceState *dev, Error **errp) { Versal *s = XLNX_VERSAL_BASE(dev); qemu_irq pic[XLNX_VERSAL_NR_IRQS]; + Object *container; const VersalMap *map = versal_get_map(s); size_t i; @@ -1227,6 +1283,11 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_apu_cpus(s); versal_create_apu_gic(s, pic); + + container = object_new(TYPE_CONTAINER); + object_property_add_child(OBJECT(s), "irq-or-gates", container); + object_unref(container); + versal_create_rpu_cpus(s); for (i = 0; i < map->num_uart; i++) { From 482f95526a52eff123b810953b6f5eb9674e279b Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:31 +0200 Subject: [PATCH 1346/1794] hw/arm/xlnx-versal: PMC IOU SCLR: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the PMC IOU SLCR device creation using the VersalMap structure. This is the first user of a shared IRQ using an OR gate. The OSPI controller is reconnected to the SLCR. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-14-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 48 +++++++++++++++++++++--------------- include/hw/arm/xlnx-versal.h | 5 ---- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index d3a084a0639e0..e71c774e72ef5 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -35,6 +35,7 @@ #include "hw/usb/xlnx-usb-subsystem.h" #include "hw/nvram/xlnx-versal-efuse.h" #include "hw/ssi/xlnx-versal-ospi.h" +#include "hw/misc/xlnx-versal-pmc-iou-slcr.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -116,6 +117,8 @@ typedef struct VersalMap { uint64_t dma_dst; int irq; } ospi; + + VersalSimplePeriphMap pmc_iou_slcr; } VersalMap; static const VersalMap VERSAL_MAP = { @@ -156,6 +159,8 @@ static const VersalMap VERSAL_MAP = { .dma_src = 0xf1011000, .dma_dst = 0xf1011800, .irq = 124, }, + + .pmc_iou_slcr = { 0xf1060000, OR_IRQ(121, 0) }, }; static const VersalMap *VERSION_TO_MAP[] = { @@ -870,21 +875,24 @@ static void versal_create_efuse(Versal *s, versal_sysbus_connect_irq(s, SYS_BUS_DEVICE(ctrl), 0, map->irq); } -static void versal_create_pmc_iou_slcr(Versal *s, qemu_irq *pic) +static DeviceState *versal_create_pmc_iou_slcr(Versal *s, + const VersalSimplePeriphMap *map) { SysBusDevice *sbd; + DeviceState *dev; - object_initialize_child(OBJECT(s), "versal-pmc-iou-slcr", &s->pmc.iou.slcr, - TYPE_XILINX_VERSAL_PMC_IOU_SLCR); + dev = qdev_new(TYPE_XILINX_VERSAL_PMC_IOU_SLCR); + object_property_add_child(OBJECT(s), "pmc-iou-slcr", OBJECT(dev)); - sbd = SYS_BUS_DEVICE(&s->pmc.iou.slcr); - sysbus_realize(sbd, &error_fatal); + sbd = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sbd, &error_fatal); - memory_region_add_subregion(&s->mr_ps, MM_PMC_PMC_IOU_SLCR, + memory_region_add_subregion(&s->mr_ps, map->addr, sysbus_mmio_get_region(sbd, 0)); - sysbus_connect_irq(sbd, 0, - qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 2)); + versal_sysbus_connect_irq(s, sbd, 0, map->irq); + + return dev; } static DeviceState *versal_create_ospi(Versal *s, @@ -1210,6 +1218,7 @@ static void versal_unimp_irq_parity_imr(void *opaque, int n, int level) static void versal_unimp(Versal *s) { + DeviceState *slcr; qemu_irq gpio_in; versal_unimp_area(s, "psm", &s->mr_ps, @@ -1232,23 +1241,18 @@ static void versal_unimp(Versal *s) qdev_init_gpio_in_named(DEVICE(s), versal_unimp_irq_parity_imr, "irq-parity-imr-dummy", 1); + slcr = DEVICE(versal_get_child(s, "pmc-iou-slcr")); gpio_in = qdev_get_gpio_in_named(DEVICE(s), "sd-emmc-sel-dummy", 0); - qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr), "sd-emmc-sel", 0, - gpio_in); + qdev_connect_gpio_out_named(slcr, "sd-emmc-sel", 0, gpio_in); gpio_in = qdev_get_gpio_in_named(DEVICE(s), "sd-emmc-sel-dummy", 1); - qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr), "sd-emmc-sel", 1, - gpio_in); + qdev_connect_gpio_out_named(slcr, "sd-emmc-sel", 1, gpio_in); gpio_in = qdev_get_gpio_in_named(DEVICE(s), "qspi-ospi-mux-sel-dummy", 0); - qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr), - "qspi-ospi-mux-sel", 0, - gpio_in); + qdev_connect_gpio_out_named(slcr, "qspi-ospi-mux-sel", 0, gpio_in); gpio_in = qdev_get_gpio_in_named(DEVICE(s), "irq-parity-imr-dummy", 0); - qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr), - SYSBUS_DEVICE_GPIO_IRQ, 0, - gpio_in); + qdev_connect_gpio_out_named(slcr, SYSBUS_DEVICE_GPIO_IRQ, 0, gpio_in); } static uint32_t fdt_add_clk_node(Versal *s, const char *name, @@ -1271,6 +1275,7 @@ static uint32_t fdt_add_clk_node(Versal *s, const char *name, static void versal_realize(DeviceState *dev, Error **errp) { Versal *s = XLNX_VERSAL_BASE(dev); + DeviceState *slcr, *ospi; qemu_irq pic[XLNX_VERSAL_NR_IRQS]; Object *container; const VersalMap *map = versal_get_map(s); @@ -1323,13 +1328,16 @@ static void versal_realize(DeviceState *dev, Error **errp) } versal_create_efuse(s, &map->efuse); - versal_create_ospi(s, &map->ospi); + ospi = versal_create_ospi(s, &map->ospi); + slcr = versal_create_pmc_iou_slcr(s, &map->pmc_iou_slcr); + qdev_connect_gpio_out_named(slcr, "ospi-mux-sel", 0, + qdev_get_gpio_in_named(ospi, + "ospi-mux-sel", 0)); versal_create_pmc_apb_irq_orgate(s, pic); versal_create_rtc(s, pic); versal_create_trng(s, pic); versal_create_bbram(s, pic); - versal_create_pmc_iou_slcr(s, pic); versal_create_crl(s, pic); versal_create_cfu(s, pic); versal_map_ddr(s); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index b7ef255d6fd44..78442e6c2c508 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -21,7 +21,6 @@ #include "qom/object.h" #include "hw/nvram/xlnx-bbram.h" #include "hw/misc/xlnx-versal-crl.h" -#include "hw/misc/xlnx-versal-pmc-iou-slcr.h" #include "hw/misc/xlnx-versal-trng.h" #include "net/can_emu.h" #include "hw/misc/xlnx-versal-cfu.h" @@ -84,10 +83,6 @@ struct Versal { /* The Platform Management Controller subsystem. */ struct { - struct { - XlnxVersalPmcIouSlcr slcr; - } iou; - XlnxZynqMPRTC rtc; XlnxVersalTRng trng; XlnxBBRam bbram; From c58d45a2b9d62638b94cc003c61603f5030dcba7 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:32 +0200 Subject: [PATCH 1347/1794] hw/arm/xlnx-versal: bbram: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the BBRAM device creation using the VersalMap structure. Note that the corresponding FDT node is removed. It does not correspond to any real node in standard Versal DTBs. No matching drivers exist for it. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-15-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 27 +++--------------------- hw/arm/xlnx-versal.c | 41 +++++++++++++++++++++++++----------- include/hw/arm/xlnx-versal.h | 3 +-- 3 files changed, 33 insertions(+), 38 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index a948e24aea066..f766a3e1027e1 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -172,26 +172,6 @@ static void fdt_add_rtc_node(VersalVirt *s) g_free(name); } -static void fdt_add_bbram_node(VersalVirt *s) -{ - const char compat[] = TYPE_XLNX_BBRAM; - const char interrupt_names[] = "bbram-error"; - char *name = g_strdup_printf("/bbram@%x", MM_PMC_BBRAM_CTRL); - - qemu_fdt_add_subnode(s->fdt, name); - - qemu_fdt_setprop_cells(s->fdt, name, "interrupts", - GIC_FDT_IRQ_TYPE_SPI, VERSAL_PMC_APB_IRQ, - GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop(s->fdt, name, "interrupt-names", - interrupt_names, sizeof(interrupt_names)); - qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", - 2, MM_PMC_BBRAM_CTRL, - 2, MM_PMC_BBRAM_CTRL_SIZE); - qemu_fdt_setprop(s->fdt, name, "compatible", compat, sizeof(compat)); - g_free(name); -} - static void fdt_nop_memory_nodes(void *fdt, Error **errp) { Error *err = NULL; @@ -346,7 +326,7 @@ static void create_virtio_regions(VersalVirt *s) } } -static void bbram_attach_drive(XlnxBBRam *dev) +static void bbram_attach_drive(VersalVirt *s) { DriveInfo *dinfo; BlockBackend *blk; @@ -354,7 +334,7 @@ static void bbram_attach_drive(XlnxBBRam *dev) dinfo = drive_get_by_index(IF_PFLASH, 0); blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; if (blk) { - qdev_prop_set_drive(DEVICE(dev), "drive", blk); + versal_bbram_attach_drive(&s->soc, blk); } } @@ -447,7 +427,6 @@ static void versal_virt_init(MachineState *machine) fdt_add_gic_nodes(s); fdt_add_timer_nodes(s); fdt_add_rtc_node(s); - fdt_add_bbram_node(s); fdt_add_cpu_nodes(s, psci_conduit); fdt_add_clk_node(s, "/old-clk125", 125000000, s->phandle.clk_125Mhz); fdt_add_clk_node(s, "/old-clk25", 25000000, s->phandle.clk_25Mhz); @@ -458,7 +437,7 @@ static void versal_virt_init(MachineState *machine) 0, &s->soc.fpd.apu.mr, 0); /* Attach bbram backend, if given */ - bbram_attach_drive(&s->soc.pmc.bbram); + bbram_attach_drive(s); /* Attach efuse backend, if given */ efuse_attach_drive(s); diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index e71c774e72ef5..31ceaf61bed99 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -36,6 +36,7 @@ #include "hw/nvram/xlnx-versal-efuse.h" #include "hw/ssi/xlnx-versal-ospi.h" #include "hw/misc/xlnx-versal-pmc-iou-slcr.h" +#include "hw/nvram/xlnx-bbram.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -119,6 +120,7 @@ typedef struct VersalMap { } ospi; VersalSimplePeriphMap pmc_iou_slcr; + VersalSimplePeriphMap bbram; } VersalMap; static const VersalMap VERSAL_MAP = { @@ -161,6 +163,7 @@ static const VersalMap VERSAL_MAP = { }, .pmc_iou_slcr = { 0xf1060000, OR_IRQ(121, 0) }, + .bbram = { 0xf11f0000, OR_IRQ(121, 1) }, }; static const VersalMap *VERSION_TO_MAP[] = { @@ -820,22 +823,21 @@ static void versal_create_xrams(Versal *s, const struct VersalXramMap *map) } } -static void versal_create_bbram(Versal *s, qemu_irq *pic) +static void versal_create_bbram(Versal *s, + const VersalSimplePeriphMap *map) { + DeviceState *dev; SysBusDevice *sbd; - object_initialize_child_with_props(OBJECT(s), "bbram", &s->pmc.bbram, - sizeof(s->pmc.bbram), TYPE_XLNX_BBRAM, - &error_fatal, - "crc-zpads", "0", - NULL); - sbd = SYS_BUS_DEVICE(&s->pmc.bbram); + dev = qdev_new(TYPE_XLNX_BBRAM); + sbd = SYS_BUS_DEVICE(dev); - sysbus_realize(sbd, &error_fatal); - memory_region_add_subregion(&s->mr_ps, MM_PMC_BBRAM_CTRL, + object_property_add_child(OBJECT(s), "bbram", OBJECT(dev)); + qdev_prop_set_uint32(dev, "crc-zpads", 0); + sysbus_realize_and_unref(sbd, &error_abort); + memory_region_add_subregion(&s->mr_ps, map->addr, sysbus_mmio_get_region(sbd, 0)); - sysbus_connect_irq(sbd, 0, - qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 1)); + versal_sysbus_connect_irq(s, sbd, 0, map->irq); } static void versal_create_efuse(Versal *s, @@ -1334,10 +1336,12 @@ static void versal_realize(DeviceState *dev, Error **errp) qdev_connect_gpio_out_named(slcr, "ospi-mux-sel", 0, qdev_get_gpio_in_named(ospi, "ospi-mux-sel", 0)); + + versal_create_bbram(s, &map->bbram); + versal_create_pmc_apb_irq_orgate(s, pic); versal_create_rtc(s, pic); versal_create_trng(s, pic); - versal_create_bbram(s, pic); versal_create_crl(s, pic); versal_create_cfu(s, pic); versal_map_ddr(s); @@ -1383,6 +1387,19 @@ void versal_efuse_attach_drive(Versal *s, BlockBackend *blk) qdev_prop_set_drive(efuse, "drive", blk); } +void versal_bbram_attach_drive(Versal *s, BlockBackend *blk) +{ + DeviceState *bbram; + + bbram = DEVICE(versal_get_child(s, "bbram")); + + if (bbram == NULL) { + return; + } + + qdev_prop_set_drive(bbram, "drive", blk); +} + void versal_ospi_create_flash(Versal *s, int flash_idx, const char *flash_mdl, BlockBackend *blk) { diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 78442e6c2c508..9adce02f8a906 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -19,7 +19,6 @@ #include "hw/intc/arm_gicv3.h" #include "hw/rtc/xlnx-zynqmp-rtc.h" #include "qom/object.h" -#include "hw/nvram/xlnx-bbram.h" #include "hw/misc/xlnx-versal-crl.h" #include "hw/misc/xlnx-versal-trng.h" #include "net/can_emu.h" @@ -85,7 +84,6 @@ struct Versal { struct { XlnxZynqMPRTC rtc; XlnxVersalTRng trng; - XlnxBBRam bbram; XlnxVersalCFUAPB cfu_apb; XlnxVersalCFUFDRO cfu_fdro; XlnxVersalCFUSFR cfu_sfr; @@ -121,6 +119,7 @@ static inline void versal_set_fdt(Versal *s, void *fdt) void versal_sdhci_plug_card(Versal *s, int sd_idx, BlockBackend *blk); void versal_efuse_attach_drive(Versal *s, BlockBackend *blk); +void versal_bbram_attach_drive(Versal *s, BlockBackend *blk); void versal_ospi_create_flash(Versal *s, int flash_idx, const char *flash_mdl, BlockBackend *blk); From 060b809271f9d0d6205fe2232d0aead4a37fa0e7 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:33 +0200 Subject: [PATCH 1348/1794] hw/arm/xlnx-versal: trng: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the TRNG device creation using the VersalMap structure. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-16-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 18 ++++++++++-------- include/hw/arm/xlnx-versal.h | 2 -- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 31ceaf61bed99..7a97d5df6b8de 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -37,6 +37,7 @@ #include "hw/ssi/xlnx-versal-ospi.h" #include "hw/misc/xlnx-versal-pmc-iou-slcr.h" #include "hw/nvram/xlnx-bbram.h" +#include "hw/misc/xlnx-versal-trng.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -121,6 +122,7 @@ typedef struct VersalMap { VersalSimplePeriphMap pmc_iou_slcr; VersalSimplePeriphMap bbram; + VersalSimplePeriphMap trng; } VersalMap; static const VersalMap VERSAL_MAP = { @@ -164,6 +166,7 @@ static const VersalMap VERSAL_MAP = { .pmc_iou_slcr = { 0xf1060000, OR_IRQ(121, 0) }, .bbram = { 0xf11f0000, OR_IRQ(121, 1) }, + .trng = { 0xf1230000, 141 }, }; static const VersalMap *VERSION_TO_MAP[] = { @@ -780,19 +783,18 @@ static void versal_create_rtc(Versal *s, qemu_irq *pic) qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 0)); } -static void versal_create_trng(Versal *s, qemu_irq *pic) +static void versal_create_trng(Versal *s, const VersalSimplePeriphMap *map) { SysBusDevice *sbd; MemoryRegion *mr; - object_initialize_child(OBJECT(s), "trng", &s->pmc.trng, - TYPE_XLNX_VERSAL_TRNG); - sbd = SYS_BUS_DEVICE(&s->pmc.trng); - sysbus_realize(sbd, &error_fatal); + sbd = SYS_BUS_DEVICE(qdev_new(TYPE_XLNX_VERSAL_TRNG)); + object_property_add_child(OBJECT(s), "trng", OBJECT(sbd)); + sysbus_realize_and_unref(sbd, &error_abort); mr = sysbus_mmio_get_region(sbd, 0); - memory_region_add_subregion(&s->mr_ps, MM_PMC_TRNG, mr); - sysbus_connect_irq(sbd, 0, pic[VERSAL_TRNG_IRQ]); + memory_region_add_subregion(&s->mr_ps, map->addr, mr); + versal_sysbus_connect_irq(s, sbd, 0, map->irq); } static void versal_create_xrams(Versal *s, const struct VersalXramMap *map) @@ -1338,10 +1340,10 @@ static void versal_realize(DeviceState *dev, Error **errp) "ospi-mux-sel", 0)); versal_create_bbram(s, &map->bbram); + versal_create_trng(s, &map->trng); versal_create_pmc_apb_irq_orgate(s, pic); versal_create_rtc(s, pic); - versal_create_trng(s, pic); versal_create_crl(s, pic); versal_create_cfu(s, pic); versal_map_ddr(s); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 9adce02f8a906..bba96201d3755 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -20,7 +20,6 @@ #include "hw/rtc/xlnx-zynqmp-rtc.h" #include "qom/object.h" #include "hw/misc/xlnx-versal-crl.h" -#include "hw/misc/xlnx-versal-trng.h" #include "net/can_emu.h" #include "hw/misc/xlnx-versal-cfu.h" #include "hw/misc/xlnx-versal-cframe-reg.h" @@ -83,7 +82,6 @@ struct Versal { /* The Platform Management Controller subsystem. */ struct { XlnxZynqMPRTC rtc; - XlnxVersalTRng trng; XlnxVersalCFUAPB cfu_apb; XlnxVersalCFUFDRO cfu_fdro; XlnxVersalCFUSFR cfu_sfr; From 176faad4bf7565966398df0d6e8cd05825dfceea Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:34 +0200 Subject: [PATCH 1349/1794] hw/arm/xlnx-versal: rtc: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the RTC device creation using the VersalMap structure. The sysbus IRQ output 0 (APB IRQ) is connected instead of the output 1 (addr error IRQ). This does not change the current behaviour since the RTC model does not implement those IRQs anyway. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-17-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 22 -------------------- hw/arm/xlnx-versal.c | 40 ++++++++++++++++++++++++++++-------- include/hw/arm/xlnx-versal.h | 2 -- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index f766a3e1027e1..d96f343392963 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -151,27 +151,6 @@ static void fdt_add_timer_nodes(VersalVirt *s) compat, sizeof(compat)); } -static void fdt_add_rtc_node(VersalVirt *s) -{ - const char compat[] = "xlnx,zynqmp-rtc"; - const char interrupt_names[] = "alarm\0sec"; - char *name = g_strdup_printf("/rtc@%x", MM_PMC_RTC); - - qemu_fdt_add_subnode(s->fdt, name); - - qemu_fdt_setprop_cells(s->fdt, name, "interrupts", - GIC_FDT_IRQ_TYPE_SPI, VERSAL_RTC_ALARM_IRQ, - GIC_FDT_IRQ_FLAGS_LEVEL_HI, - GIC_FDT_IRQ_TYPE_SPI, VERSAL_RTC_SECONDS_IRQ, - GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop(s->fdt, name, "interrupt-names", - interrupt_names, sizeof(interrupt_names)); - qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", - 2, MM_PMC_RTC, 2, MM_PMC_RTC_SIZE); - qemu_fdt_setprop(s->fdt, name, "compatible", compat, sizeof(compat)); - g_free(name); -} - static void fdt_nop_memory_nodes(void *fdt, Error **errp) { Error *err = NULL; @@ -426,7 +405,6 @@ static void versal_virt_init(MachineState *machine) fdt_add_gic_nodes(s); fdt_add_timer_nodes(s); - fdt_add_rtc_node(s); fdt_add_cpu_nodes(s, psci_conduit); fdt_add_clk_node(s, "/old-clk125", 125000000, s->phandle.clk_125Mhz); fdt_add_clk_node(s, "/old-clk25", 25000000, s->phandle.clk_25Mhz); diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 7a97d5df6b8de..9b1e0d46f1b44 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -38,6 +38,7 @@ #include "hw/misc/xlnx-versal-pmc-iou-slcr.h" #include "hw/nvram/xlnx-bbram.h" #include "hw/misc/xlnx-versal-trng.h" +#include "hw/rtc/xlnx-zynqmp-rtc.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -123,6 +124,12 @@ typedef struct VersalMap { VersalSimplePeriphMap pmc_iou_slcr; VersalSimplePeriphMap bbram; VersalSimplePeriphMap trng; + + struct VersalRtcMap { + VersalSimplePeriphMap map; + int alarm_irq; + int second_irq; + } rtc; } VersalMap; static const VersalMap VERSAL_MAP = { @@ -167,6 +174,10 @@ static const VersalMap VERSAL_MAP = { .pmc_iou_slcr = { 0xf1060000, OR_IRQ(121, 0) }, .bbram = { 0xf11f0000, OR_IRQ(121, 1) }, .trng = { 0xf1230000, 141 }, + .rtc = { + { 0xf12a0000, OR_IRQ(121, 2) }, + .alarm_irq = 142, .second_irq = 143 + }, }; static const VersalMap *VERSION_TO_MAP[] = { @@ -762,25 +773,36 @@ static void versal_create_pmc_apb_irq_orgate(Versal *s, qemu_irq *pic) qdev_connect_gpio_out(orgate, 0, pic[VERSAL_PMC_APB_IRQ]); } -static void versal_create_rtc(Versal *s, qemu_irq *pic) +static void versal_create_rtc(Versal *s, const struct VersalRtcMap *map) { SysBusDevice *sbd; MemoryRegion *mr; + g_autofree char *node; + const char compatible[] = "xlnx,zynqmp-rtc"; + const char interrupt_names[] = "alarm\0sec"; - object_initialize_child(OBJECT(s), "rtc", &s->pmc.rtc, - TYPE_XLNX_ZYNQMP_RTC); - sbd = SYS_BUS_DEVICE(&s->pmc.rtc); - sysbus_realize(sbd, &error_fatal); + sbd = SYS_BUS_DEVICE(qdev_new(TYPE_XLNX_ZYNQMP_RTC)); + object_property_add_child(OBJECT(s), "rtc", OBJECT(sbd)); + sysbus_realize_and_unref(sbd, &error_abort); mr = sysbus_mmio_get_region(sbd, 0); - memory_region_add_subregion(&s->mr_ps, MM_PMC_RTC, mr); + memory_region_add_subregion(&s->mr_ps, map->map.addr, mr); /* * TODO: Connect the ALARM and SECONDS interrupts once our RTC model * supports them. */ - sysbus_connect_irq(sbd, 1, - qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 0)); + versal_sysbus_connect_irq(s, sbd, 0, map->map.irq); + + node = versal_fdt_add_simple_subnode(s, "/rtc", map->map.addr, 0x10000, + compatible, sizeof(compatible)); + qemu_fdt_setprop_cells(s->cfg.fdt, node, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, map->alarm_irq, + GIC_FDT_IRQ_FLAGS_LEVEL_HI, + GIC_FDT_IRQ_TYPE_SPI, map->second_irq, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + qemu_fdt_setprop(s->cfg.fdt, node, "interrupt-names", + interrupt_names, sizeof(interrupt_names)); } static void versal_create_trng(Versal *s, const VersalSimplePeriphMap *map) @@ -1341,9 +1363,9 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_bbram(s, &map->bbram); versal_create_trng(s, &map->trng); + versal_create_rtc(s, &map->rtc); versal_create_pmc_apb_irq_orgate(s, pic); - versal_create_rtc(s, pic); versal_create_crl(s, pic); versal_create_cfu(s, pic); versal_map_ddr(s); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index bba96201d3755..abdbed1568956 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -17,7 +17,6 @@ #include "hw/cpu/cluster.h" #include "hw/or-irq.h" #include "hw/intc/arm_gicv3.h" -#include "hw/rtc/xlnx-zynqmp-rtc.h" #include "qom/object.h" #include "hw/misc/xlnx-versal-crl.h" #include "net/can_emu.h" @@ -81,7 +80,6 @@ struct Versal { /* The Platform Management Controller subsystem. */ struct { - XlnxZynqMPRTC rtc; XlnxVersalCFUAPB cfu_apb; XlnxVersalCFUFDRO cfu_fdro; XlnxVersalCFUSFR cfu_sfr; From b913b3cb26d531f71ccf89e886c0d0f520e80077 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:35 +0200 Subject: [PATCH 1350/1794] hw/arm/xlnx-versal: cfu: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the CFU device creation using the VersalMap structure. All users of the APB IRQ OR gate have now been converted. The OR gate device can be dropped. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-18-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 254 ++++++++++++++++------------------- include/hw/arm/xlnx-versal.h | 14 -- 2 files changed, 113 insertions(+), 155 deletions(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 9b1e0d46f1b44..81adf8d35b658 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -39,6 +39,9 @@ #include "hw/nvram/xlnx-bbram.h" #include "hw/misc/xlnx-versal-trng.h" #include "hw/rtc/xlnx-zynqmp-rtc.h" +#include "hw/misc/xlnx-versal-cfu.h" +#include "hw/misc/xlnx-versal-cframe-reg.h" +#include "hw/or-irq.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -130,6 +133,24 @@ typedef struct VersalMap { int alarm_irq; int second_irq; } rtc; + + struct VersalCfuMap { + uint64_t cframe_base; + uint64_t cframe_stride; + uint64_t cfu_fdro; + uint64_t cframe_bcast_reg; + uint64_t cframe_bcast_fdri; + uint64_t cfu_apb; + uint64_t cfu_stream; + uint64_t cfu_stream_2; + uint64_t cfu_sfr; + int cfu_apb_irq; + int cframe_irq; + size_t num_cframe; + struct VersalCfuCframeCfg { + uint32_t blktype_frames[7]; + } cframe_cfg[15]; + } cfu; } VersalMap; static const VersalMap VERSAL_MAP = { @@ -178,6 +199,22 @@ static const VersalMap VERSAL_MAP = { { 0xf12a0000, OR_IRQ(121, 2) }, .alarm_irq = 142, .second_irq = 143 }, + + .cfu = { + .cframe_base = 0xf12d0000, .cframe_stride = 0x1000, + .cframe_bcast_reg = 0xf12ee000, .cframe_bcast_fdri = 0xf12ef000, + .cfu_apb = 0xf12b0000, .cfu_sfr = 0xf12c1000, + .cfu_stream = 0xf12c0000, .cfu_stream_2 = 0xf1f80000, + .cfu_fdro = 0xf12c2000, + .cfu_apb_irq = 120, .cframe_irq = OR_IRQ(121, 3), + .num_cframe = 15, + .cframe_cfg = { + { { 34111, 3528, 12800, 11, 5, 1, 1 } }, + { { 38498, 3841, 15361, 13, 7, 3, 1 } }, + { { 38498, 3841, 15361, 13, 7, 3, 1 } }, + { { 38498, 3841, 15361, 13, 7, 3, 1 } }, + }, + }, }; static const VersalMap *VERSION_TO_MAP[] = { @@ -752,27 +789,6 @@ static void versal_create_sdhci(Versal *s, GIC_FDT_IRQ_FLAGS_LEVEL_HI); } -static void versal_create_pmc_apb_irq_orgate(Versal *s, qemu_irq *pic) -{ - DeviceState *orgate; - - /* - * The VERSAL_PMC_APB_IRQ is an 'or' of the interrupts from the following - * models: - * - RTC - * - BBRAM - * - PMC SLCR - * - CFRAME regs (input 3 - 17 to the orgate) - */ - object_initialize_child(OBJECT(s), "pmc-apb-irq-orgate", - &s->pmc.apb_irq_orgate, TYPE_OR_IRQ); - orgate = DEVICE(&s->pmc.apb_irq_orgate); - object_property_set_int(OBJECT(orgate), - "num-lines", VERSAL_NUM_PMC_APB_IRQS, &error_fatal); - qdev_realize(orgate, NULL, &error_fatal); - qdev_connect_gpio_out(orgate, 0, pic[VERSAL_PMC_APB_IRQ]); -} - static void versal_create_rtc(Versal *s, const struct VersalRtcMap *map) { SysBusDevice *sbd; @@ -991,154 +1007,111 @@ static DeviceState *versal_create_ospi(Versal *s, return dev; } -static void versal_create_cfu(Versal *s, qemu_irq *pic) +static void versal_create_cfu(Versal *s, const struct VersalCfuMap *map) { SysBusDevice *sbd; - DeviceState *dev; + Object *container; + DeviceState *cfu_fdro, *cfu_apb, *cfu_sfr, *cframe_bcast; + DeviceState *cframe_irq_or; int i; - const struct { - uint64_t reg_base; - uint64_t fdri_base; - } cframe_addr[] = { - { MM_PMC_CFRAME0_REG, MM_PMC_CFRAME0_FDRI }, - { MM_PMC_CFRAME1_REG, MM_PMC_CFRAME1_FDRI }, - { MM_PMC_CFRAME2_REG, MM_PMC_CFRAME2_FDRI }, - { MM_PMC_CFRAME3_REG, MM_PMC_CFRAME3_FDRI }, - { MM_PMC_CFRAME4_REG, MM_PMC_CFRAME4_FDRI }, - { MM_PMC_CFRAME5_REG, MM_PMC_CFRAME5_FDRI }, - { MM_PMC_CFRAME6_REG, MM_PMC_CFRAME6_FDRI }, - { MM_PMC_CFRAME7_REG, MM_PMC_CFRAME7_FDRI }, - { MM_PMC_CFRAME8_REG, MM_PMC_CFRAME8_FDRI }, - { MM_PMC_CFRAME9_REG, MM_PMC_CFRAME9_FDRI }, - { MM_PMC_CFRAME10_REG, MM_PMC_CFRAME10_FDRI }, - { MM_PMC_CFRAME11_REG, MM_PMC_CFRAME11_FDRI }, - { MM_PMC_CFRAME12_REG, MM_PMC_CFRAME12_FDRI }, - { MM_PMC_CFRAME13_REG, MM_PMC_CFRAME13_FDRI }, - { MM_PMC_CFRAME14_REG, MM_PMC_CFRAME14_FDRI }, - }; - const struct { - uint32_t blktype0_frames; - uint32_t blktype1_frames; - uint32_t blktype2_frames; - uint32_t blktype3_frames; - uint32_t blktype4_frames; - uint32_t blktype5_frames; - uint32_t blktype6_frames; - } cframe_cfg[] = { - [0] = { 34111, 3528, 12800, 11, 5, 1, 1 }, - [1] = { 38498, 3841, 15361, 13, 7, 3, 1 }, - [2] = { 38498, 3841, 15361, 13, 7, 3, 1 }, - [3] = { 38498, 3841, 15361, 13, 7, 3, 1 }, - }; + + container = object_new(TYPE_CONTAINER); + object_property_add_child(OBJECT(s), "cfu", container); + object_unref(container); /* CFU FDRO */ - object_initialize_child(OBJECT(s), "cfu-fdro", &s->pmc.cfu_fdro, - TYPE_XLNX_VERSAL_CFU_FDRO); - sbd = SYS_BUS_DEVICE(&s->pmc.cfu_fdro); + cfu_fdro = qdev_new(TYPE_XLNX_VERSAL_CFU_FDRO); + object_property_add_child(container, "cfu-fdro", OBJECT(cfu_fdro)); + sbd = SYS_BUS_DEVICE(cfu_fdro); - sysbus_realize(sbd, &error_fatal); - memory_region_add_subregion(&s->mr_ps, MM_PMC_CFU_FDRO, + sysbus_realize_and_unref(sbd, &error_fatal); + memory_region_add_subregion(&s->mr_ps, map->cfu_fdro, sysbus_mmio_get_region(sbd, 0)); - /* CFRAME REG */ - for (i = 0; i < ARRAY_SIZE(s->pmc.cframe); i++) { - g_autofree char *name = g_strdup_printf("cframe%d", i); + /* cframe bcast */ + cframe_bcast = qdev_new(TYPE_XLNX_VERSAL_CFRAME_BCAST_REG); + object_property_add_child(container, "cframe-bcast", OBJECT(cframe_bcast)); - object_initialize_child(OBJECT(s), name, &s->pmc.cframe[i], - TYPE_XLNX_VERSAL_CFRAME_REG); + /* CFU APB */ + cfu_apb = qdev_new(TYPE_XLNX_VERSAL_CFU_APB); + object_property_add_child(container, "cfu-apb", OBJECT(cfu_apb)); + + /* IRQ or gate for cframes */ + cframe_irq_or = qdev_new(TYPE_OR_IRQ); + object_property_add_child(container, "cframe-irq-or-gate", + OBJECT(cframe_irq_or)); + qdev_prop_set_uint16(cframe_irq_or, "num-lines", map->num_cframe); + qdev_realize_and_unref(cframe_irq_or, NULL, &error_abort); + versal_qdev_connect_gpio_out(s, cframe_irq_or, 0, map->cframe_irq); + + /* cframe reg */ + for (i = 0; i < map->num_cframe; i++) { + uint64_t reg_base; + uint64_t fdri_base; + DeviceState *dev; + g_autofree char *prop_name; + size_t j; - sbd = SYS_BUS_DEVICE(&s->pmc.cframe[i]); - dev = DEVICE(&s->pmc.cframe[i]); + dev = qdev_new(TYPE_XLNX_VERSAL_CFRAME_REG); + object_property_add_child(container, "cframe[*]", OBJECT(dev)); - if (i < ARRAY_SIZE(cframe_cfg)) { - object_property_set_int(OBJECT(dev), "blktype0-frames", - cframe_cfg[i].blktype0_frames, - &error_abort); - object_property_set_int(OBJECT(dev), "blktype1-frames", - cframe_cfg[i].blktype1_frames, - &error_abort); - object_property_set_int(OBJECT(dev), "blktype2-frames", - cframe_cfg[i].blktype2_frames, - &error_abort); - object_property_set_int(OBJECT(dev), "blktype3-frames", - cframe_cfg[i].blktype3_frames, - &error_abort); - object_property_set_int(OBJECT(dev), "blktype4-frames", - cframe_cfg[i].blktype4_frames, - &error_abort); - object_property_set_int(OBJECT(dev), "blktype5-frames", - cframe_cfg[i].blktype5_frames, - &error_abort); - object_property_set_int(OBJECT(dev), "blktype6-frames", - cframe_cfg[i].blktype6_frames, + sbd = SYS_BUS_DEVICE(dev); + + for (j = 0; j < ARRAY_SIZE(map->cframe_cfg[i].blktype_frames); j++) { + g_autofree char *blktype_prop_name; + + blktype_prop_name = g_strdup_printf("blktype%zu-frames", j); + object_property_set_int(OBJECT(dev), blktype_prop_name, + map->cframe_cfg[i].blktype_frames[j], &error_abort); } + object_property_set_link(OBJECT(dev), "cfu-fdro", - OBJECT(&s->pmc.cfu_fdro), &error_fatal); + OBJECT(cfu_fdro), &error_abort); - sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_abort); - memory_region_add_subregion(&s->mr_ps, cframe_addr[i].reg_base, + reg_base = map->cframe_base + i * map->cframe_stride * 2; + fdri_base = reg_base + map->cframe_stride; + memory_region_add_subregion(&s->mr_ps, reg_base, sysbus_mmio_get_region(sbd, 0)); - memory_region_add_subregion(&s->mr_ps, cframe_addr[i].fdri_base, + memory_region_add_subregion(&s->mr_ps, fdri_base, sysbus_mmio_get_region(sbd, 1)); - sysbus_connect_irq(sbd, 0, - qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), - 3 + i)); - } + sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(cframe_irq_or, i)); - /* CFRAME BCAST */ - object_initialize_child(OBJECT(s), "cframe_bcast", &s->pmc.cframe_bcast, - TYPE_XLNX_VERSAL_CFRAME_BCAST_REG); - - sbd = SYS_BUS_DEVICE(&s->pmc.cframe_bcast); - dev = DEVICE(&s->pmc.cframe_bcast); - - for (i = 0; i < ARRAY_SIZE(s->pmc.cframe); i++) { - g_autofree char *propname = g_strdup_printf("cframe%d", i); - object_property_set_link(OBJECT(dev), propname, - OBJECT(&s->pmc.cframe[i]), &error_fatal); + prop_name = g_strdup_printf("cframe%d", i); + object_property_set_link(OBJECT(cframe_bcast), prop_name, + OBJECT(dev), &error_abort); + object_property_set_link(OBJECT(cfu_apb), prop_name, + OBJECT(dev), &error_abort); } - sysbus_realize(sbd, &error_fatal); - - memory_region_add_subregion(&s->mr_ps, MM_PMC_CFRAME_BCAST_REG, + sbd = SYS_BUS_DEVICE(cframe_bcast); + sysbus_realize_and_unref(sbd, &error_abort); + memory_region_add_subregion(&s->mr_ps, map->cframe_bcast_reg, sysbus_mmio_get_region(sbd, 0)); - memory_region_add_subregion(&s->mr_ps, MM_PMC_CFRAME_BCAST_FDRI, + memory_region_add_subregion(&s->mr_ps, map->cframe_bcast_fdri, sysbus_mmio_get_region(sbd, 1)); - /* CFU APB */ - object_initialize_child(OBJECT(s), "cfu-apb", &s->pmc.cfu_apb, - TYPE_XLNX_VERSAL_CFU_APB); - sbd = SYS_BUS_DEVICE(&s->pmc.cfu_apb); - dev = DEVICE(&s->pmc.cfu_apb); - - for (i = 0; i < ARRAY_SIZE(s->pmc.cframe); i++) { - g_autofree char *propname = g_strdup_printf("cframe%d", i); - object_property_set_link(OBJECT(dev), propname, - OBJECT(&s->pmc.cframe[i]), &error_fatal); - } - - sysbus_realize(sbd, &error_fatal); - memory_region_add_subregion(&s->mr_ps, MM_PMC_CFU_APB, + sbd = SYS_BUS_DEVICE(cfu_apb); + sysbus_realize_and_unref(sbd, &error_fatal); + memory_region_add_subregion(&s->mr_ps, map->cfu_apb, sysbus_mmio_get_region(sbd, 0)); - memory_region_add_subregion(&s->mr_ps, MM_PMC_CFU_STREAM, + memory_region_add_subregion(&s->mr_ps, map->cfu_stream, sysbus_mmio_get_region(sbd, 1)); - memory_region_add_subregion(&s->mr_ps, MM_PMC_CFU_STREAM_2, + memory_region_add_subregion(&s->mr_ps, map->cfu_stream_2, sysbus_mmio_get_region(sbd, 2)); - sysbus_connect_irq(sbd, 0, pic[VERSAL_CFU_IRQ_0]); + versal_sysbus_connect_irq(s, sbd, 0, map->cfu_apb_irq); /* CFU SFR */ - object_initialize_child(OBJECT(s), "cfu-sfr", &s->pmc.cfu_sfr, - TYPE_XLNX_VERSAL_CFU_SFR); - - sbd = SYS_BUS_DEVICE(&s->pmc.cfu_sfr); - - object_property_set_link(OBJECT(&s->pmc.cfu_sfr), - "cfu", OBJECT(&s->pmc.cfu_apb), &error_abort); + cfu_sfr = qdev_new(TYPE_XLNX_VERSAL_CFU_SFR); + object_property_add_child(container, "cfu-sfr", OBJECT(cfu_sfr)); + sbd = SYS_BUS_DEVICE(cfu_sfr); - sysbus_realize(sbd, &error_fatal); - memory_region_add_subregion(&s->mr_ps, MM_PMC_CFU_SFR, + object_property_set_link(OBJECT(cfu_sfr), + "cfu", OBJECT(cfu_apb), &error_abort); + sysbus_realize_and_unref(sbd, &error_fatal); + memory_region_add_subregion(&s->mr_ps, map->cfu_sfr, sysbus_mmio_get_region(sbd, 0)); } @@ -1364,10 +1337,9 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_bbram(s, &map->bbram); versal_create_trng(s, &map->trng); versal_create_rtc(s, &map->rtc); + versal_create_cfu(s, &map->cfu); - versal_create_pmc_apb_irq_orgate(s, pic); versal_create_crl(s, pic); - versal_create_cfu(s, pic); versal_map_ddr(s); versal_unimp(s); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index abdbed1568956..5a685aea6d4d8 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -15,13 +15,10 @@ #include "hw/sysbus.h" #include "hw/cpu/cluster.h" -#include "hw/or-irq.h" #include "hw/intc/arm_gicv3.h" #include "qom/object.h" #include "hw/misc/xlnx-versal-crl.h" #include "net/can_emu.h" -#include "hw/misc/xlnx-versal-cfu.h" -#include "hw/misc/xlnx-versal-cframe-reg.h" #include "target/arm/cpu.h" #include "hw/arm/xlnx-versal-version.h" @@ -78,17 +75,6 @@ struct Versal { XlnxVersalCRL crl; } lpd; - /* The Platform Management Controller subsystem. */ - struct { - XlnxVersalCFUAPB cfu_apb; - XlnxVersalCFUFDRO cfu_fdro; - XlnxVersalCFUSFR cfu_sfr; - XlnxVersalCFrameReg cframe[XLNX_VERSAL_NR_CFRAME]; - XlnxVersalCFrameBcastReg cframe_bcast; - - OrIRQState apb_irq_orgate; - } pmc; - struct { uint32_t clk_25mhz; uint32_t clk_125mhz; From aaf889ebdc6ed054a975465fee18feea2450c446 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:36 +0200 Subject: [PATCH 1351/1794] hw/arm/xlnx-versal: crl: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the CRL device creation using the VersalMap structure. The connections to the RPU CPUs are temporarily removed and will be reintroduced with next refactoring commits. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-19-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 36 +++++++++++++++++++----------------- include/hw/arm/xlnx-versal.h | 3 --- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 81adf8d35b658..f5f98a3030d1c 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -42,6 +42,7 @@ #include "hw/misc/xlnx-versal-cfu.h" #include "hw/misc/xlnx-versal-cframe-reg.h" #include "hw/or-irq.h" +#include "hw/misc/xlnx-versal-crl.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -151,6 +152,8 @@ typedef struct VersalMap { uint32_t blktype_frames[7]; } cframe_cfg[15]; } cfu; + + VersalSimplePeriphMap crl; } VersalMap; static const VersalMap VERSAL_MAP = { @@ -215,6 +218,8 @@ static const VersalMap VERSAL_MAP = { { { 38498, 3841, 15361, 13, 7, 3, 1 } }, }, }, + + .crl = { 0xff5e0000, 10 }, }; static const VersalMap *VERSION_TO_MAP[] = { @@ -1115,27 +1120,24 @@ static void versal_create_cfu(Versal *s, const struct VersalCfuMap *map) sysbus_mmio_get_region(sbd, 0)); } -static void versal_create_crl(Versal *s, qemu_irq *pic) +static inline void versal_create_crl(Versal *s) { - SysBusDevice *sbd; - int i; + const VersalMap *map; + const char *crl_class; + DeviceState *dev; - object_initialize_child(OBJECT(s), "crl", &s->lpd.crl, - TYPE_XLNX_VERSAL_CRL); - sbd = SYS_BUS_DEVICE(&s->lpd.crl); + map = versal_get_map(s); - for (i = 0; i < ARRAY_SIZE(s->lpd.rpu.cpu); i++) { - g_autofree gchar *name = g_strdup_printf("cpu_r5[%d]", i); + crl_class = TYPE_XLNX_VERSAL_CRL; + dev = qdev_new(crl_class); + object_property_add_child(OBJECT(s), "crl", OBJECT(dev)); - object_property_set_link(OBJECT(&s->lpd.crl), - name, OBJECT(&s->lpd.rpu.cpu[i]), - &error_abort); - } + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_abort); - sysbus_realize(sbd, &error_fatal); - memory_region_add_subregion(&s->mr_ps, MM_CRL, - sysbus_mmio_get_region(sbd, 0)); - sysbus_connect_irq(sbd, 0, pic[VERSAL_CRL_IRQ]); + memory_region_add_subregion(&s->mr_ps, map->crl.addr, + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); + + versal_sysbus_connect_irq(s, SYS_BUS_DEVICE(dev), 0, map->crl.irq); } /* This takes the board allocated linear DDR memory and creates aliases @@ -1338,8 +1340,8 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_trng(s, &map->trng); versal_create_rtc(s, &map->rtc); versal_create_cfu(s, &map->cfu); + versal_create_crl(s); - versal_create_crl(s, pic); versal_map_ddr(s); versal_unimp(s); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 5a685aea6d4d8..d3ce13e69deb1 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -17,7 +17,6 @@ #include "hw/cpu/cluster.h" #include "hw/intc/arm_gicv3.h" #include "qom/object.h" -#include "hw/misc/xlnx-versal-crl.h" #include "net/can_emu.h" #include "target/arm/cpu.h" #include "hw/arm/xlnx-versal-version.h" @@ -71,8 +70,6 @@ struct Versal { CPUClusterState cluster; ARMCPU cpu[XLNX_VERSAL_NR_RCPUS]; } rpu; - - XlnxVersalCRL crl; } lpd; struct { From e17df660769d24dc6d5d0c75ab4de16033705924 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:37 +0200 Subject: [PATCH 1352/1794] hw/arm/xlnx-versal-virt: virtio: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the creation of virtio devices. Use the accessors provided by the Versal SoC to retrieve the reserved MMIO and IRQ space. Those are defined in the VersalMap structure. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-20-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 31 ++++++++++++------------------- hw/arm/xlnx-versal.c | 26 ++++++++++++++++++++++++++ include/hw/arm/xlnx-versal.h | 3 +++ 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index d96f343392963..b981d0125585c 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -271,37 +271,30 @@ static void create_virtio_regions(VersalVirt *s) int i; for (i = 0; i < NUM_VIRTIO_TRANSPORT; i++) { - char *name = g_strdup_printf("virtio%d", i); - hwaddr base = MM_TOP_RSVD + i * virtio_mmio_size; - int irq = VERSAL_RSVD_IRQ_FIRST + i; + hwaddr base = versal_get_reserved_mmio_addr(&s->soc) + + i * virtio_mmio_size; + g_autofree char *node = g_strdup_printf("/virtio_mmio@%" PRIx64, base); + int dtb_irq; MemoryRegion *mr; DeviceState *dev; qemu_irq pic_irq; - pic_irq = qdev_get_gpio_in(DEVICE(&s->soc.fpd.apu.gic), irq); + pic_irq = versal_get_reserved_irq(&s->soc, i, &dtb_irq); dev = qdev_new("virtio-mmio"); - object_property_add_child(OBJECT(&s->soc), name, OBJECT(dev)); + object_property_add_child(OBJECT(s), "virtio-mmio[*]", OBJECT(dev)); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic_irq); mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); memory_region_add_subregion(&s->soc.mr_ps, base, mr); - g_free(name); - } - - for (i = 0; i < NUM_VIRTIO_TRANSPORT; i++) { - hwaddr base = MM_TOP_RSVD + i * virtio_mmio_size; - int irq = VERSAL_RSVD_IRQ_FIRST + i; - char *name = g_strdup_printf("/virtio_mmio@%" PRIx64, base); - qemu_fdt_add_subnode(s->fdt, name); - qemu_fdt_setprop(s->fdt, name, "dma-coherent", NULL, 0); - qemu_fdt_setprop_cells(s->fdt, name, "interrupts", - GIC_FDT_IRQ_TYPE_SPI, irq, + qemu_fdt_add_subnode(s->fdt, node); + qemu_fdt_setprop(s->fdt, node, "dma-coherent", NULL, 0); + qemu_fdt_setprop_cells(s->fdt, node, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, dtb_irq, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); - qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", + qemu_fdt_setprop_sized_cells(s->fdt, node, "reg", 2, base, 2, virtio_mmio_size); - qemu_fdt_setprop_string(s->fdt, name, "compatible", "virtio,mmio"); - g_free(name); + qemu_fdt_setprop_string(s->fdt, node, "compatible", "virtio,mmio"); } } diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index f5f98a3030d1c..23aac709dc439 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -154,6 +154,13 @@ typedef struct VersalMap { } cfu; VersalSimplePeriphMap crl; + + /* reserved MMIO/IRQ space that can safely be used for virtio devices */ + struct VersalReserved { + uint64_t mmio_start; + int irq_start; + int irq_num; + } reserved; } VersalMap; static const VersalMap VERSAL_MAP = { @@ -220,6 +227,8 @@ static const VersalMap VERSAL_MAP = { }, .crl = { 0xff5e0000, 10 }, + + .reserved = { 0xa0000000, 111, 8 }, }; static const VersalMap *VERSION_TO_MAP[] = { @@ -1422,6 +1431,23 @@ void versal_ospi_create_flash(Versal *s, int flash_idx, const char *flash_mdl, flash_idx + 1, cs_line); } +qemu_irq versal_get_reserved_irq(Versal *s, int idx, int *dtb_idx) +{ + const VersalMap *map = versal_get_map(s); + + g_assert(idx < map->reserved.irq_num); + + *dtb_idx = map->reserved.irq_start + idx; + return versal_get_irq(s, *dtb_idx); +} + +hwaddr versal_get_reserved_mmio_addr(Versal *s) +{ + const VersalMap *map = versal_get_map(s); + + return map->reserved.mmio_start; +} + int versal_get_num_can(VersalVersion version) { const VersalMap *map = VERSION_TO_MAP[version]; diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index d3ce13e69deb1..af47acb288f45 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -102,6 +102,9 @@ void versal_bbram_attach_drive(Versal *s, BlockBackend *blk); void versal_ospi_create_flash(Versal *s, int flash_idx, const char *flash_mdl, BlockBackend *blk); +qemu_irq versal_get_reserved_irq(Versal *s, int idx, int *dtb_idx); +hwaddr versal_get_reserved_mmio_addr(Versal *s); + int versal_get_num_can(VersalVersion version); int versal_get_num_sdhci(VersalVersion version); From 7c21633676fe3249cb8af80e3831bffea1457242 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:38 +0200 Subject: [PATCH 1353/1794] hw/arm/xlnx-versal: refactor CPU cluster creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the CPU cluster creation using the VersalMap structure. There is no functional change. The clusters properties are now described in the VersalMap structure. For now only the APU is converted. The RPU will be taken care of by next commits. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-21-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 80 +------- hw/arm/xlnx-versal.c | 354 ++++++++++++++++++++++++++--------- include/hw/arm/xlnx-versal.h | 12 +- 3 files changed, 276 insertions(+), 170 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index b981d0125585c..27594f78c8f30 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -38,7 +38,6 @@ struct VersalVirt { void *fdt; int fdt_size; struct { - uint32_t gic; uint32_t clk_125Mhz; uint32_t clk_25Mhz; } phandle; @@ -63,7 +62,6 @@ static void fdt_create(VersalVirt *s) } /* Allocate all phandles. */ - s->phandle.gic = qemu_fdt_alloc_phandle(s->fdt); s->phandle.clk_25Mhz = qemu_fdt_alloc_phandle(s->fdt); s->phandle.clk_125Mhz = qemu_fdt_alloc_phandle(s->fdt); @@ -72,9 +70,6 @@ static void fdt_create(VersalVirt *s) qemu_fdt_add_subnode(s->fdt, "/aliases"); /* Header */ - qemu_fdt_setprop_cell(s->fdt, "/", "interrupt-parent", s->phandle.gic); - qemu_fdt_setprop_cell(s->fdt, "/", "#size-cells", 0x2); - qemu_fdt_setprop_cell(s->fdt, "/", "#address-cells", 0x2); qemu_fdt_setprop_string(s->fdt, "/", "model", mc->desc); qemu_fdt_setprop_string(s->fdt, "/", "compatible", "xlnx-versal-virt"); } @@ -90,67 +85,6 @@ static void fdt_add_clk_node(VersalVirt *s, const char *name, qemu_fdt_setprop(s->fdt, name, "u-boot,dm-pre-reloc", NULL, 0); } -static void fdt_add_cpu_nodes(VersalVirt *s, uint32_t psci_conduit) -{ - int i; - - qemu_fdt_add_subnode(s->fdt, "/cpus"); - qemu_fdt_setprop_cell(s->fdt, "/cpus", "#size-cells", 0x0); - qemu_fdt_setprop_cell(s->fdt, "/cpus", "#address-cells", 1); - - for (i = XLNX_VERSAL_NR_ACPUS - 1; i >= 0; i--) { - char *name = g_strdup_printf("/cpus/cpu@%d", i); - ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i)); - - qemu_fdt_add_subnode(s->fdt, name); - qemu_fdt_setprop_cell(s->fdt, name, "reg", - arm_cpu_mp_affinity(armcpu)); - if (psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) { - qemu_fdt_setprop_string(s->fdt, name, "enable-method", "psci"); - } - qemu_fdt_setprop_string(s->fdt, name, "device_type", "cpu"); - qemu_fdt_setprop_string(s->fdt, name, "compatible", - armcpu->dtb_compatible); - g_free(name); - } -} - -static void fdt_add_gic_nodes(VersalVirt *s) -{ - char *nodename; - - nodename = g_strdup_printf("/gic@%x", MM_GIC_APU_DIST_MAIN); - qemu_fdt_add_subnode(s->fdt, nodename); - qemu_fdt_setprop_cell(s->fdt, nodename, "phandle", s->phandle.gic); - qemu_fdt_setprop_cells(s->fdt, nodename, "interrupts", - GIC_FDT_IRQ_TYPE_PPI, VERSAL_GIC_MAINT_IRQ, - GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop(s->fdt, nodename, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_sized_cells(s->fdt, nodename, "reg", - 2, MM_GIC_APU_DIST_MAIN, - 2, MM_GIC_APU_DIST_MAIN_SIZE, - 2, MM_GIC_APU_REDIST_0, - 2, MM_GIC_APU_REDIST_0_SIZE); - qemu_fdt_setprop_cell(s->fdt, nodename, "#interrupt-cells", 3); - qemu_fdt_setprop_string(s->fdt, nodename, "compatible", "arm,gic-v3"); - g_free(nodename); -} - -static void fdt_add_timer_nodes(VersalVirt *s) -{ - const char compat[] = "arm,armv8-timer"; - uint32_t irqflags = GIC_FDT_IRQ_FLAGS_LEVEL_HI; - - qemu_fdt_add_subnode(s->fdt, "/timer"); - qemu_fdt_setprop_cells(s->fdt, "/timer", "interrupts", - GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_S_EL1_IRQ, irqflags, - GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_NS_EL1_IRQ, irqflags, - GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_VIRT_IRQ, irqflags, - GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_NS_EL2_IRQ, irqflags); - qemu_fdt_setprop(s->fdt, "/timer", "compatible", - compat, sizeof(compat)); -} - static void fdt_nop_memory_nodes(void *fdt, Error **errp) { Error *err = NULL; @@ -396,16 +330,14 @@ static void versal_virt_init(MachineState *machine) sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal); create_virtio_regions(s); - fdt_add_gic_nodes(s); - fdt_add_timer_nodes(s); - fdt_add_cpu_nodes(s, psci_conduit); fdt_add_clk_node(s, "/old-clk125", 125000000, s->phandle.clk_125Mhz); fdt_add_clk_node(s, "/old-clk25", 25000000, s->phandle.clk_25Mhz); - /* Make the APU cpu address space visible to virtio and other - * modules unaware of multiple address-spaces. */ - memory_region_add_subregion_overlap(get_system_memory(), - 0, &s->soc.fpd.apu.mr, 0); + /* + * Map the SoC address space onto system memory. This will allow virtio and + * other modules unaware of multiple address-spaces to work. + */ + memory_region_add_subregion(get_system_memory(), 0, &s->soc.mr_ps); /* Attach bbram backend, if given */ bbram_attach_drive(s); @@ -429,7 +361,7 @@ static void versal_virt_init(MachineState *machine) s->binfo.loader_start = 0x1000; s->binfo.dtb_limit = 0x1000000; } - arm_load_kernel(&s->soc.fpd.apu.cpu[0], machine, &s->binfo); + arm_load_kernel(ARM_CPU(versal_get_boot_cpu(&s->soc)), machine, &s->binfo); for (i = 0; i < XLNX_VERSAL_NUM_OSPI_FLASH; i++) { ObjectClass *flash_klass; diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 23aac709dc439..b4cad856dc2ef 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -43,6 +43,7 @@ #include "hw/misc/xlnx-versal-cframe-reg.h" #include "hw/or-irq.h" #include "hw/misc/xlnx-versal-crl.h" +#include "hw/intc/arm_gicv3_common.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -67,7 +68,34 @@ typedef struct VersalSimplePeriphMap { int irq; } VersalSimplePeriphMap; +typedef struct VersalGicMap { + int version; + uint64_t dist; + uint64_t redist; + size_t num_irq; +} VersalGicMap; + +enum StartPoweredOffMode { + SPO_SECONDARIES, + SPO_ALL, +}; + +typedef struct VersalCpuClusterMap { + VersalGicMap gic; + + const char *name; + const char *cpu_model; + size_t num_core; + size_t num_cluster; + uint32_t qemu_cluster_id; + bool dtb_expose; + + enum StartPoweredOffMode start_powered_off; +} VersalCpuClusterMap; + typedef struct VersalMap { + VersalCpuClusterMap apu; + VersalSimplePeriphMap uart[2]; size_t num_uart; @@ -164,6 +192,22 @@ typedef struct VersalMap { } VersalMap; static const VersalMap VERSAL_MAP = { + .apu = { + .name = "apu", + .cpu_model = ARM_CPU_TYPE_NAME("cortex-a72"), + .num_cluster = 1, + .num_core = 2, + .qemu_cluster_id = 0, + .start_powered_off = SPO_SECONDARIES, + .dtb_expose = true, + .gic = { + .version = 3, + .dist = 0xf9000000, + .redist = 0xf9080000, + .num_irq = 192, + }, + }, + .uart[0] = { 0xff000000, 18 }, .uart[1] = { 0xff010000, 19 }, .num_uart = 2, @@ -294,11 +338,12 @@ static qemu_irq versal_get_irq(Versal *s, int irq_idx) { qemu_irq irq; bool ored; + DeviceState *gic; ored = FIELD_EX32(irq_idx, VERSAL_IRQ, ORED); - irq = qdev_get_gpio_in(DEVICE(&s->fpd.apu.gic), - FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ)); + gic = DEVICE(versal_get_child_idx(s, "apu-gic", 0)); + irq = qdev_get_gpio_in(gic, FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ)); if (ored) { irq = versal_get_irq_or_gate_in(s, irq_idx, irq); @@ -375,107 +420,239 @@ static inline DeviceState *create_or_gate(Versal *s, Object *parent, return or; } -static void versal_create_apu_cpus(Versal *s) +static MemoryRegion *create_cpu_mr(Versal *s, DeviceState *cluster, + const VersalCpuClusterMap *map) { - int i; + MemoryRegion *mr, *root_alias; + char *name; + + mr = g_new(MemoryRegion, 1); + name = g_strdup_printf("%s-mr", map->name); + memory_region_init(mr, OBJECT(cluster), name, UINT64_MAX); + g_free(name); + + root_alias = g_new(MemoryRegion, 1); + name = g_strdup_printf("ps-alias-for-%s", map->name); + memory_region_init_alias(root_alias, OBJECT(cluster), name, + &s->mr_ps, 0, UINT64_MAX); + g_free(name); + memory_region_add_subregion(mr, 0, root_alias); + + return mr; +} - object_initialize_child(OBJECT(s), "apu-cluster", &s->fpd.apu.cluster, - TYPE_CPU_CLUSTER); - qdev_prop_set_uint32(DEVICE(&s->fpd.apu.cluster), "cluster-id", 0); +static DeviceState *versal_create_gic(Versal *s, + const VersalCpuClusterMap *map, + MemoryRegion *mr, + size_t num_cpu) +{ + DeviceState *dev; + SysBusDevice *sbd; + QList *redist_region_count; + g_autofree char *node = NULL; + g_autofree char *name = NULL; + const char compatible[] = "arm,gic-v3"; - for (i = 0; i < ARRAY_SIZE(s->fpd.apu.cpu); i++) { - Object *obj; + dev = qdev_new(gicv3_class_name()); + name = g_strdup_printf("%s-gic[*]", map->name); + object_property_add_child(OBJECT(s), name, OBJECT(dev)); + sbd = SYS_BUS_DEVICE(dev); + qdev_prop_set_uint32(dev, "revision", 3); + qdev_prop_set_uint32(dev, "num-cpu", num_cpu); + qdev_prop_set_uint32(dev, "num-irq", map->gic.num_irq + 32); - object_initialize_child(OBJECT(&s->fpd.apu.cluster), - "apu-cpu[*]", &s->fpd.apu.cpu[i], - XLNX_VERSAL_ACPU_TYPE); - obj = OBJECT(&s->fpd.apu.cpu[i]); - if (i) { - /* Secondary CPUs start in powered-down state */ - object_property_set_bool(obj, "start-powered-off", true, - &error_abort); - } + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, num_cpu); + qdev_prop_set_array(dev, "redist-region-count", redist_region_count); - object_property_set_int(obj, "core-count", ARRAY_SIZE(s->fpd.apu.cpu), - &error_abort); - object_property_set_link(obj, "memory", OBJECT(&s->fpd.apu.mr), - &error_abort); - qdev_realize(DEVICE(obj), NULL, &error_fatal); + qdev_prop_set_bit(dev, "has-security-extensions", true); + object_property_set_link(OBJECT(dev), "sysmem", OBJECT(mr), &error_abort); + + sysbus_realize_and_unref(sbd, &error_fatal); + + memory_region_add_subregion(mr, map->gic.dist, + sysbus_mmio_get_region(sbd, 0)); + memory_region_add_subregion(mr, map->gic.redist, + sysbus_mmio_get_region(sbd, 1)); + + if (map->dtb_expose) { + node = versal_fdt_add_subnode(s, "/gic", map->gic.dist, compatible, + sizeof(compatible)); + qemu_fdt_setprop_cell(s->cfg.fdt, node, "phandle", s->phandle.gic); + qemu_fdt_setprop_cell(s->cfg.fdt, node, "#interrupt-cells", 3); + qemu_fdt_setprop_sized_cells(s->cfg.fdt, node, "reg", + 2, map->gic.dist, + 2, 0x10000, + 2, map->gic.redist, + 2, GICV3_REDIST_SIZE * num_cpu); + qemu_fdt_setprop_cells(s->cfg.fdt, node, "interrupts", + GIC_FDT_IRQ_TYPE_PPI, VERSAL_GIC_MAINT_IRQ, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + qemu_fdt_setprop(s->cfg.fdt, node, "interrupt-controller", NULL, 0); } - qdev_realize(DEVICE(&s->fpd.apu.cluster), NULL, &error_fatal); + return dev; } -static void versal_create_apu_gic(Versal *s, qemu_irq *pic) +static void connect_gic_to_cpu(const VersalCpuClusterMap *map, + DeviceState *gic, DeviceState *cpu, size_t idx, + size_t num_cpu) { - static const uint64_t addrs[] = { - MM_GIC_APU_DIST_MAIN, - MM_GIC_APU_REDIST_0 + SysBusDevice *sbd = SYS_BUS_DEVICE(gic); + int ppibase = map->gic.num_irq + idx * GIC_INTERNAL + GIC_NR_SGIS; + int ti; + bool has_gtimer; + /* + * Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs. + */ + const int timer_irq[] = { + [GTIMER_PHYS] = VERSAL_TIMER_NS_EL1_IRQ, + [GTIMER_VIRT] = VERSAL_TIMER_VIRT_IRQ, + [GTIMER_HYP] = VERSAL_TIMER_NS_EL2_IRQ, + [GTIMER_SEC] = VERSAL_TIMER_S_EL1_IRQ, }; - SysBusDevice *gicbusdev; - DeviceState *gicdev; - QList *redist_region_count; - int nr_apu_cpus = ARRAY_SIZE(s->fpd.apu.cpu); - int i; - object_initialize_child(OBJECT(s), "apu-gic", &s->fpd.apu.gic, - gicv3_class_name()); - gicbusdev = SYS_BUS_DEVICE(&s->fpd.apu.gic); - gicdev = DEVICE(&s->fpd.apu.gic); - qdev_prop_set_uint32(gicdev, "revision", 3); - qdev_prop_set_uint32(gicdev, "num-cpu", nr_apu_cpus); - qdev_prop_set_uint32(gicdev, "num-irq", XLNX_VERSAL_NR_IRQS + 32); + has_gtimer = arm_feature(&ARM_CPU(cpu)->env, ARM_FEATURE_GENERIC_TIMER); - redist_region_count = qlist_new(); - qlist_append_int(redist_region_count, nr_apu_cpus); - qdev_prop_set_array(gicdev, "redist-region-count", redist_region_count); + if (has_gtimer) { + for (ti = 0; ti < ARRAY_SIZE(timer_irq); ti++) { + qdev_connect_gpio_out(cpu, ti, + qdev_get_gpio_in(gic, + ppibase + timer_irq[ti])); + } + } - qdev_prop_set_bit(gicdev, "has-security-extensions", true); + if (map->gic.version == 3) { + qemu_irq maint_irq; - sysbus_realize(SYS_BUS_DEVICE(&s->fpd.apu.gic), &error_fatal); + maint_irq = qdev_get_gpio_in(gic, + ppibase + VERSAL_GIC_MAINT_IRQ); + qdev_connect_gpio_out_named(cpu, "gicv3-maintenance-interrupt", + 0, maint_irq); + } - for (i = 0; i < ARRAY_SIZE(addrs); i++) { - MemoryRegion *mr; + sysbus_connect_irq(sbd, idx, qdev_get_gpio_in(cpu, ARM_CPU_IRQ)); + sysbus_connect_irq(sbd, idx + num_cpu, + qdev_get_gpio_in(cpu, ARM_CPU_FIQ)); + sysbus_connect_irq(sbd, idx + 2 * num_cpu, + qdev_get_gpio_in(cpu, ARM_CPU_VIRQ)); + sysbus_connect_irq(sbd, idx + 3 * num_cpu, + qdev_get_gpio_in(cpu, ARM_CPU_VFIQ)); +} - mr = sysbus_mmio_get_region(gicbusdev, i); - memory_region_add_subregion(&s->fpd.apu.mr, addrs[i], mr); +static inline void versal_create_and_connect_gic(Versal *s, + const VersalCpuClusterMap *map, + MemoryRegion *mr, + DeviceState **cpus, + size_t num_cpu) +{ + DeviceState *gic; + size_t i; + + gic = versal_create_gic(s, map, mr, num_cpu); + + for (i = 0; i < num_cpu; i++) { + connect_gic_to_cpu(map, gic, cpus[i], i, num_cpu); } +} - for (i = 0; i < nr_apu_cpus; i++) { - DeviceState *cpudev = DEVICE(&s->fpd.apu.cpu[i]); - int ppibase = XLNX_VERSAL_NR_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS; - qemu_irq maint_irq; - int ti; - /* Mapping from the output timer irq lines from the CPU to the - * GIC PPI inputs. - */ - const int timer_irq[] = { - [GTIMER_PHYS] = VERSAL_TIMER_NS_EL1_IRQ, - [GTIMER_VIRT] = VERSAL_TIMER_VIRT_IRQ, - [GTIMER_HYP] = VERSAL_TIMER_NS_EL2_IRQ, - [GTIMER_SEC] = VERSAL_TIMER_S_EL1_IRQ, - }; +static DeviceState *versal_create_cpu(Versal *s, + const VersalCpuClusterMap *map, + DeviceState *qemu_cluster, + MemoryRegion *cpu_mr, + size_t cluster_idx, + size_t core_idx) +{ + DeviceState *cpu = qdev_new(map->cpu_model); + ARMCPU *arm_cpu = ARM_CPU(cpu); + Object *obj = OBJECT(cpu); + bool start_off; + size_t idx = cluster_idx * map->num_core + core_idx; + g_autofree char *name; + g_autofree char *node = NULL; - for (ti = 0; ti < ARRAY_SIZE(timer_irq); ti++) { - qdev_connect_gpio_out(cpudev, ti, - qdev_get_gpio_in(gicdev, - ppibase + timer_irq[ti])); + start_off = map->start_powered_off == SPO_ALL + || ((map->start_powered_off == SPO_SECONDARIES) + && (cluster_idx || core_idx)); + + name = g_strdup_printf("%s[*]", map->name); + object_property_add_child(OBJECT(qemu_cluster), name, obj); + object_property_set_bool(obj, "start-powered-off", start_off, + &error_abort); + qdev_prop_set_int32(cpu, "core-count", map->num_core); + object_property_set_link(obj, "memory", OBJECT(cpu_mr), &error_abort); + qdev_realize_and_unref(cpu, NULL, &error_fatal); + + if (!map->dtb_expose) { + return cpu; + } + + node = versal_fdt_add_subnode(s, "/cpus/cpu", idx, + arm_cpu->dtb_compatible, + strlen(arm_cpu->dtb_compatible) + 1); + qemu_fdt_setprop_cell(s->cfg.fdt, node, "reg", + arm_cpu_mp_affinity(arm_cpu) & ARM64_AFFINITY_MASK); + qemu_fdt_setprop_string(s->cfg.fdt, node, "device_type", "cpu"); + qemu_fdt_setprop_string(s->cfg.fdt, node, "enable-method", "psci"); + + return cpu; +} + +static void versal_create_cpu_cluster(Versal *s, const VersalCpuClusterMap *map) +{ + size_t i, j; + DeviceState *cluster; + MemoryRegion *mr; + char *name; + g_autofree DeviceState **cpus; + const char compatible[] = "arm,armv8-timer"; + bool has_gtimer; + + cluster = qdev_new(TYPE_CPU_CLUSTER); + name = g_strdup_printf("%s-cluster", map->name); + object_property_add_child(OBJECT(s), name, OBJECT(cluster)); + g_free(name); + qdev_prop_set_uint32(cluster, "cluster-id", map->qemu_cluster_id); + + mr = create_cpu_mr(s, cluster, map); + + cpus = g_new(DeviceState *, map->num_cluster * map->num_core); + + if (map->dtb_expose) { + qemu_fdt_add_subnode(s->cfg.fdt, "/cpus"); + qemu_fdt_setprop_cell(s->cfg.fdt, "/cpus", "#size-cells", 0); + qemu_fdt_setprop_cell(s->cfg.fdt, "/cpus", "#address-cells", 1); + } + + for (i = 0; i < map->num_cluster; i++) { + for (j = 0; j < map->num_core; j++) { + DeviceState *cpu = versal_create_cpu(s, map, cluster, mr, i, j); + + cpus[i * map->num_core + j] = cpu; } - maint_irq = qdev_get_gpio_in(gicdev, - ppibase + VERSAL_GIC_MAINT_IRQ); - qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", - 0, maint_irq); - sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); - sysbus_connect_irq(gicbusdev, i + nr_apu_cpus, - qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); - sysbus_connect_irq(gicbusdev, i + 2 * nr_apu_cpus, - qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); - sysbus_connect_irq(gicbusdev, i + 3 * nr_apu_cpus, - qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + } - for (i = 0; i < XLNX_VERSAL_NR_IRQS; i++) { - pic[i] = qdev_get_gpio_in(gicdev, i); + qdev_realize_and_unref(cluster, NULL, &error_fatal); + + versal_create_and_connect_gic(s, map, mr, cpus, + map->num_cluster * map->num_core); + + has_gtimer = arm_feature(&ARM_CPU(cpus[0])->env, ARM_FEATURE_GENERIC_TIMER); + if (map->dtb_expose && has_gtimer) { + qemu_fdt_add_subnode(s->cfg.fdt, "/timer"); + qemu_fdt_setprop_cells(s->cfg.fdt, "/timer", "interrupts", + GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_S_EL1_IRQ, + GIC_FDT_IRQ_FLAGS_LEVEL_HI, + GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_NS_EL1_IRQ, + GIC_FDT_IRQ_FLAGS_LEVEL_HI, + GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_VIRT_IRQ, + GIC_FDT_IRQ_FLAGS_LEVEL_HI, + GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_NS_EL2_IRQ, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + qemu_fdt_setprop(s->cfg.fdt, "/timer", "compatible", + compatible, sizeof(compatible)); } } @@ -1286,7 +1463,6 @@ static void versal_realize(DeviceState *dev, Error **errp) { Versal *s = XLNX_VERSAL_BASE(dev); DeviceState *slcr, *ospi; - qemu_irq pic[XLNX_VERSAL_NR_IRQS]; Object *container; const VersalMap *map = versal_get_map(s); size_t i; @@ -1295,14 +1471,17 @@ static void versal_realize(DeviceState *dev, Error **errp) s->phandle.clk_25mhz = fdt_add_clk_node(s, "/clk25", 25 * 1000 * 1000); s->phandle.clk_125mhz = fdt_add_clk_node(s, "/clk125", 125 * 1000 * 1000); - - versal_create_apu_cpus(s); - versal_create_apu_gic(s, pic); + s->phandle.gic = qemu_fdt_alloc_phandle(s->cfg.fdt); container = object_new(TYPE_CONTAINER); object_property_add_child(OBJECT(s), "irq-or-gates", container); object_unref(container); + qemu_fdt_setprop_cell(s->cfg.fdt, "/", "interrupt-parent", s->phandle.gic); + qemu_fdt_setprop_cell(s->cfg.fdt, "/", "#size-cells", 0x2); + qemu_fdt_setprop_cell(s->cfg.fdt, "/", "#address-cells", 0x2); + + versal_create_cpu_cluster(s, &map->apu); versal_create_rpu_cpus(s); for (i = 0; i < map->num_uart; i++) { @@ -1359,11 +1538,15 @@ static void versal_realize(DeviceState *dev, Error **errp) MM_OCM_SIZE, &error_fatal); memory_region_add_subregion_overlap(&s->mr_ps, MM_OCM, &s->lpd.mr_ocm, 0); - memory_region_add_subregion_overlap(&s->fpd.apu.mr, 0, &s->mr_ps, 0); memory_region_add_subregion_overlap(&s->lpd.rpu.mr, 0, &s->lpd.rpu.mr_ps_alias, 0); } +DeviceState *versal_get_boot_cpu(Versal *s) +{ + return DEVICE(versal_get_child_idx(s, "apu-cluster/apu", 0)); +} + void versal_sdhci_plug_card(Versal *s, int sd_idx, BlockBackend *blk) { DeviceState *sdhci, *card; @@ -1467,7 +1650,6 @@ static void versal_base_init(Object *obj) Versal *s = XLNX_VERSAL_BASE(obj); size_t i, num_can; - memory_region_init(&s->fpd.apu.mr, obj, "mr-apu", UINT64_MAX); memory_region_init(&s->lpd.rpu.mr, obj, "mr-rpu", UINT64_MAX); memory_region_init(&s->mr_ps, obj, "mr-ps-switch", UINT64_MAX); memory_region_init_alias(&s->lpd.rpu.mr_ps_alias, OBJECT(s), diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index af47acb288f45..9d9ccfb0014af 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -15,7 +15,6 @@ #include "hw/sysbus.h" #include "hw/cpu/cluster.h" -#include "hw/intc/arm_gicv3.h" #include "qom/object.h" #include "net/can_emu.h" #include "target/arm/cpu.h" @@ -43,15 +42,6 @@ struct Versal { SysBusDevice parent_obj; /*< public >*/ - struct { - struct { - MemoryRegion mr; - CPUClusterState cluster; - ARMCPU cpu[XLNX_VERSAL_NR_ACPUS]; - GICv3State gic; - } apu; - } fpd; - MemoryRegion mr_ps; struct { @@ -75,6 +65,7 @@ struct Versal { struct { uint32_t clk_25mhz; uint32_t clk_125mhz; + uint32_t gic; } phandle; struct { @@ -96,6 +87,7 @@ static inline void versal_set_fdt(Versal *s, void *fdt) s->cfg.fdt = fdt; } +DeviceState *versal_get_boot_cpu(Versal *s); void versal_sdhci_plug_card(Versal *s, int sd_idx, BlockBackend *blk); void versal_efuse_attach_drive(Versal *s, BlockBackend *blk); void versal_bbram_attach_drive(Versal *s, BlockBackend *blk); From c2ba2adf2dc0f51146f70eaeb32c84932573a906 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:39 +0200 Subject: [PATCH 1354/1794] hw/arm/xlnx-versal: add the mp_affinity property to the CPU mapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a way to configure the MP affinity value of the CPUs given their core and cluster IDs. For the Versal APU CPUs, the MP affinity value is given by the core ID in Aff0. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-22-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index b4cad856dc2ef..ccb78fadd7f65 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -90,6 +90,12 @@ typedef struct VersalCpuClusterMap { uint32_t qemu_cluster_id; bool dtb_expose; + struct { + uint64_t base; + uint64_t core_shift; + uint64_t cluster_shift; + } mp_affinity; + enum StartPoweredOffMode start_powered_off; } VersalCpuClusterMap; @@ -198,6 +204,10 @@ static const VersalMap VERSAL_MAP = { .num_cluster = 1, .num_core = 2, .qemu_cluster_id = 0, + .mp_affinity = { + .core_shift = ARM_AFF0_SHIFT, + .cluster_shift = ARM_AFF1_SHIFT, + }, .start_powered_off = SPO_SECONDARIES, .dtb_expose = true, .gic = { @@ -567,11 +577,16 @@ static DeviceState *versal_create_cpu(Versal *s, DeviceState *cpu = qdev_new(map->cpu_model); ARMCPU *arm_cpu = ARM_CPU(cpu); Object *obj = OBJECT(cpu); + uint64_t affinity; bool start_off; size_t idx = cluster_idx * map->num_core + core_idx; g_autofree char *name; g_autofree char *node = NULL; + affinity = map->mp_affinity.base; + affinity |= (cluster_idx & 0xff) << map->mp_affinity.cluster_shift; + affinity |= (core_idx & 0xff) << map->mp_affinity.core_shift; + start_off = map->start_powered_off == SPO_ALL || ((map->start_powered_off == SPO_SECONDARIES) && (cluster_idx || core_idx)); @@ -580,6 +595,7 @@ static DeviceState *versal_create_cpu(Versal *s, object_property_add_child(OBJECT(qemu_cluster), name, obj); object_property_set_bool(obj, "start-powered-off", start_off, &error_abort); + qdev_prop_set_uint64(cpu, "mp-affinity", affinity); qdev_prop_set_int32(cpu, "core-count", map->num_core); object_property_set_link(obj, "memory", OBJECT(cpu_mr), &error_abort); qdev_realize_and_unref(cpu, NULL, &error_fatal); From 6926b0b26071ce740f026212f5c437a694117645 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:40 +0200 Subject: [PATCH 1355/1794] hw/arm/xlnx-versal: instantiate the GIC ITS in the APU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the instance of the GIC ITS in the APU. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-23-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index ccb78fadd7f65..e03411bc212d3 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -44,6 +44,7 @@ #include "hw/or-irq.h" #include "hw/misc/xlnx-versal-crl.h" #include "hw/intc/arm_gicv3_common.h" +#include "hw/intc/arm_gicv3_its_common.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -72,7 +73,9 @@ typedef struct VersalGicMap { int version; uint64_t dist; uint64_t redist; + uint64_t its; size_t num_irq; + bool has_its; } VersalGicMap; enum StartPoweredOffMode { @@ -215,6 +218,8 @@ static const VersalMap VERSAL_MAP = { .dist = 0xf9000000, .redist = 0xf9080000, .num_irq = 192, + .has_its = true, + .its = 0xf9020000, }, }, @@ -451,6 +456,48 @@ static MemoryRegion *create_cpu_mr(Versal *s, DeviceState *cluster, return mr; } +static void versal_create_gic_its(Versal *s, + const VersalCpuClusterMap *map, + DeviceState *gic, + MemoryRegion *mr, + char *gic_node) +{ + DeviceState *dev; + SysBusDevice *sbd; + g_autofree char *node_pat = NULL, *node = NULL; + const char compatible[] = "arm,gic-v3-its"; + + if (!map->gic.has_its) { + return; + } + + dev = qdev_new(TYPE_ARM_GICV3_ITS); + sbd = SYS_BUS_DEVICE(dev); + + object_property_add_child(OBJECT(gic), "its", OBJECT(dev)); + object_property_set_link(OBJECT(dev), "parent-gicv3", OBJECT(gic), + &error_abort); + + sysbus_realize_and_unref(sbd, &error_abort); + + memory_region_add_subregion(mr, map->gic.its, + sysbus_mmio_get_region(sbd, 0)); + + if (!map->dtb_expose) { + return; + } + + qemu_fdt_setprop(s->cfg.fdt, gic_node, "ranges", NULL, 0); + qemu_fdt_setprop_cell(s->cfg.fdt, gic_node, "#address-cells", 2); + qemu_fdt_setprop_cell(s->cfg.fdt, gic_node, "#size-cells", 2); + + node_pat = g_strdup_printf("%s/its", gic_node); + node = versal_fdt_add_simple_subnode(s, node_pat, map->gic.its, 0x20000, + compatible, sizeof(compatible)); + qemu_fdt_setprop(s->cfg.fdt, node, "msi-controller", NULL, 0); + qemu_fdt_setprop_cell(s->cfg.fdt, node, "#msi-cells", 1); +} + static DeviceState *versal_create_gic(Versal *s, const VersalCpuClusterMap *map, MemoryRegion *mr, @@ -476,6 +523,7 @@ static DeviceState *versal_create_gic(Versal *s, qdev_prop_set_array(dev, "redist-region-count", redist_region_count); qdev_prop_set_bit(dev, "has-security-extensions", true); + qdev_prop_set_bit(dev, "has-lpi", map->gic.has_its); object_property_set_link(OBJECT(dev), "sysmem", OBJECT(mr), &error_abort); sysbus_realize_and_unref(sbd, &error_fatal); @@ -501,6 +549,8 @@ static DeviceState *versal_create_gic(Versal *s, qemu_fdt_setprop(s->cfg.fdt, node, "interrupt-controller", NULL, 0); } + versal_create_gic_its(s, map, dev, mr, node); + return dev; } From 27493e5e687e65829f548bf0145dd44bc223fbe5 Mon Sep 17 00:00:00 2001 From: Francisco Iglesias Date: Fri, 26 Sep 2025 09:07:41 +0200 Subject: [PATCH 1356/1794] hw/intc/arm_gicv3: Introduce a 'first-cpu-index' property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a 'first-cpu-index' property for specifying the first QEMU CPU connected to the GICv3. This makes it possible to have multiple instances of the GICv3 connected to different CPU clusters. For KVM, mark this property has unsupported. It probably does not make much sense as it is intented to be used to model non-SMP systems. Signed-off-by: Luc Michel Signed-off-by: Francisco Iglesias Reviewed-by: Sai Pavan Boddu Reviewed-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-24-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_common.c | 3 ++- hw/intc/arm_gicv3_cpuif.c | 2 +- hw/intc/arm_gicv3_kvm.c | 6 ++++++ include/hw/intc/arm_gicv3_common.h | 1 + 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index e438d8c042d67..2d0df6da86c23 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -436,7 +436,7 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) s->cpu = g_new0(GICv3CPUState, s->num_cpu); for (i = 0; i < s->num_cpu; i++) { - CPUState *cpu = qemu_get_cpu(i); + CPUState *cpu = qemu_get_cpu(s->first_cpu_idx + i); uint64_t cpu_affid; s->cpu[i].cpu = cpu; @@ -622,6 +622,7 @@ static const Property arm_gicv3_common_properties[] = { redist_region_count, qdev_prop_uint32, uint32_t), DEFINE_PROP_LINK("sysmem", GICv3State, dma, TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_UINT32("first-cpu-index", GICv3State, first_cpu_idx, 0), }; static void arm_gicv3_common_class_init(ObjectClass *klass, const void *data) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 72e91f971a46f..2e6c1f778a9cf 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -3024,7 +3024,7 @@ void gicv3_init_cpuif(GICv3State *s) int i; for (i = 0; i < s->num_cpu; i++) { - ARMCPU *cpu = ARM_CPU(qemu_get_cpu(i)); + ARMCPU *cpu = ARM_CPU(qemu_get_cpu(s->first_cpu_idx + i)); GICv3CPUState *cs = &s->cpu[i]; /* diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 0cd14d78a7596..9829e2146daba 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -821,6 +821,12 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) return; } + if (s->first_cpu_idx != 0) { + error_setg(errp, "Non-zero first-cpu-idx is unsupported with the " + "in-kernel GIC"); + return; + } + gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL); for (i = 0; i < s->num_cpu; i++) { diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index 572d971d22c05..38aa1961c5096 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -229,6 +229,7 @@ struct GICv3State { uint32_t *redist_region_count; /* redistributor count within each region */ uint32_t nb_redist_regions; /* number of redist regions */ + uint32_t first_cpu_idx; uint32_t num_cpu; uint32_t num_irq; uint32_t revision; From d95bf385567dc635f4cf275af118f3774e3b3d29 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:42 +0200 Subject: [PATCH 1357/1794] hw/arm/xlnx-versal: add support for multiple GICs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Versal SoC contains two GICs: one GICv3 in the APU and one GICv2 in the RPU (currently not instantiated). To prepare for the GICv2 instantiation, add support for multiple GICs when connecting interrupts. When a GIC is created, the first-cpu-index property is set on it, and a pointer to the GIC is stored in the intc array. When connecting an IRQ, a TYPE_SPLIT_IRQ device is created with its num-lines property set to the number of GICs in the SoC. The split device is used to fan out the IRQ to all the GICs. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-25-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 56 +++++++++++++++++++++++++++++++++--- include/hw/arm/xlnx-versal.h | 1 + 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index e03411bc212d3..9256eceffc700 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -45,6 +45,7 @@ #include "hw/misc/xlnx-versal-crl.h" #include "hw/intc/arm_gicv3_common.h" #include "hw/intc/arm_gicv3_its_common.h" +#include "hw/core/split-irq.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -317,6 +318,43 @@ static inline Object *versal_get_child_idx(Versal *s, const char *child, return versal_get_child(s, n); } +/* + * The SoC embeds multiple GICs. They all receives the same IRQ lines at the + * same index. This function creates a TYPE_SPLIT_IRQ device to fan out the + * given IRQ input to all the GICs. + * + * The TYPE_SPLIT_IRQ devices lie in the /soc/irq-splits QOM container + */ +static qemu_irq versal_get_gic_irq(Versal *s, int irq_idx) +{ + DeviceState *split; + Object *container = versal_get_child(s, "irq-splits"); + int idx = FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ); + g_autofree char *name = g_strdup_printf("irq[%d]", idx); + + split = DEVICE(object_resolve_path_at(container, name)); + + if (split == NULL) { + size_t i; + + split = qdev_new(TYPE_SPLIT_IRQ); + qdev_prop_set_uint16(split, "num-lines", s->intc->len); + object_property_add_child(container, name, OBJECT(split)); + qdev_realize_and_unref(split, NULL, &error_abort); + + for (i = 0; i < s->intc->len; i++) { + DeviceState *gic; + + gic = g_array_index(s->intc, DeviceState *, i); + qdev_connect_gpio_out(split, i, qdev_get_gpio_in(gic, idx)); + } + } else { + g_assert(FIELD_EX32(irq_idx, VERSAL_IRQ, ORED)); + } + + return qdev_get_gpio_in(split, 0); +} + /* * When the R_VERSAL_IRQ_ORED flag is set on an IRQ descriptor, this function is * used to return the corresponding or gate input IRQ. The or gate is created if @@ -353,12 +391,10 @@ static qemu_irq versal_get_irq(Versal *s, int irq_idx) { qemu_irq irq; bool ored; - DeviceState *gic; ored = FIELD_EX32(irq_idx, VERSAL_IRQ, ORED); - gic = DEVICE(versal_get_child_idx(s, "apu-gic", 0)); - irq = qdev_get_gpio_in(gic, FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ)); + irq = versal_get_gic_irq(s, irq_idx); if (ored) { irq = versal_get_irq_or_gate_in(s, irq_idx, irq); @@ -501,6 +537,7 @@ static void versal_create_gic_its(Versal *s, static DeviceState *versal_create_gic(Versal *s, const VersalCpuClusterMap *map, MemoryRegion *mr, + int first_cpu_idx, size_t num_cpu) { DeviceState *dev; @@ -525,6 +562,7 @@ static DeviceState *versal_create_gic(Versal *s, qdev_prop_set_bit(dev, "has-security-extensions", true); qdev_prop_set_bit(dev, "has-lpi", map->gic.has_its); object_property_set_link(OBJECT(dev), "sysmem", OBJECT(mr), &error_abort); + qdev_prop_set_uint32(dev, "first-cpu-index", first_cpu_idx); sysbus_realize_and_unref(sbd, &error_fatal); @@ -551,6 +589,8 @@ static DeviceState *versal_create_gic(Versal *s, versal_create_gic_its(s, map, dev, mr, node); + g_array_append_val(s->intc, dev); + return dev; } @@ -608,9 +648,11 @@ static inline void versal_create_and_connect_gic(Versal *s, size_t num_cpu) { DeviceState *gic; + int first_cpu_idx; size_t i; - gic = versal_create_gic(s, map, mr, num_cpu); + first_cpu_idx = CPU(cpus[0])->cpu_index; + gic = versal_create_gic(s, map, mr, first_cpu_idx, num_cpu); for (i = 0; i < num_cpu; i++) { connect_gic_to_cpu(map, gic, cpus[i], i, num_cpu); @@ -1539,6 +1581,10 @@ static void versal_realize(DeviceState *dev, Error **errp) s->phandle.clk_125mhz = fdt_add_clk_node(s, "/clk125", 125 * 1000 * 1000); s->phandle.gic = qemu_fdt_alloc_phandle(s->cfg.fdt); + container = object_new(TYPE_CONTAINER); + object_property_add_child(OBJECT(s), "irq-splits", container); + object_unref(container); + container = object_new(TYPE_CONTAINER); object_property_add_child(OBJECT(s), "irq-or-gates", container); object_unref(container); @@ -1720,6 +1766,7 @@ static void versal_base_init(Object *obj) memory_region_init(&s->mr_ps, obj, "mr-ps-switch", UINT64_MAX); memory_region_init_alias(&s->lpd.rpu.mr_ps_alias, OBJECT(s), "mr-rpu-ps-alias", &s->mr_ps, 0, UINT64_MAX); + s->intc = g_array_new(false, false, sizeof(DeviceState *)); num_can = versal_get_map(s)->num_canfd; s->cfg.canbus = g_new0(CanBusState *, num_can); @@ -1737,6 +1784,7 @@ static void versal_base_finalize(Object *obj) { Versal *s = XLNX_VERSAL_BASE(obj); + g_array_free(s->intc, true); g_free(s->cfg.canbus); } diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 9d9ccfb0014af..984f9f2ccddc2 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -42,6 +42,7 @@ struct Versal { SysBusDevice parent_obj; /*< public >*/ + GArray *intc; MemoryRegion mr_ps; struct { From 0dfff625b5dafcb6cd722dd094ddc54c031e9169 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:43 +0200 Subject: [PATCH 1358/1794] hw/arm/xlnx-versal: add support for GICv2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for GICv2 instantiation in the Versal SoC. This is in preparation for the RPU refactoring. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-26-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 82 +++++++++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 20 deletions(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 9256eceffc700..45ea47a8b97d2 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -45,6 +45,7 @@ #include "hw/misc/xlnx-versal-crl.h" #include "hw/intc/arm_gicv3_common.h" #include "hw/intc/arm_gicv3_its_common.h" +#include "hw/intc/arm_gic.h" #include "hw/core/split-irq.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") @@ -74,6 +75,7 @@ typedef struct VersalGicMap { int version; uint64_t dist; uint64_t redist; + uint64_t cpu_iface; uint64_t its; size_t num_irq; bool has_its; @@ -503,6 +505,10 @@ static void versal_create_gic_its(Versal *s, g_autofree char *node_pat = NULL, *node = NULL; const char compatible[] = "arm,gic-v3-its"; + if (map->gic.version != 3) { + return; + } + if (!map->gic.has_its) { return; } @@ -542,45 +548,81 @@ static DeviceState *versal_create_gic(Versal *s, { DeviceState *dev; SysBusDevice *sbd; - QList *redist_region_count; g_autofree char *node = NULL; g_autofree char *name = NULL; - const char compatible[] = "arm,gic-v3"; + const char gicv3_compat[] = "arm,gic-v3"; + const char gicv2_compat[] = "arm,cortex-a15-gic"; + + switch (map->gic.version) { + case 2: + dev = qdev_new(gic_class_name()); + break; + + case 3: + dev = qdev_new(gicv3_class_name()); + break; + + default: + g_assert_not_reached(); + } - dev = qdev_new(gicv3_class_name()); name = g_strdup_printf("%s-gic[*]", map->name); object_property_add_child(OBJECT(s), name, OBJECT(dev)); sbd = SYS_BUS_DEVICE(dev); - qdev_prop_set_uint32(dev, "revision", 3); + qdev_prop_set_uint32(dev, "revision", map->gic.version); qdev_prop_set_uint32(dev, "num-cpu", num_cpu); qdev_prop_set_uint32(dev, "num-irq", map->gic.num_irq + 32); - - redist_region_count = qlist_new(); - qlist_append_int(redist_region_count, num_cpu); - qdev_prop_set_array(dev, "redist-region-count", redist_region_count); - qdev_prop_set_bit(dev, "has-security-extensions", true); - qdev_prop_set_bit(dev, "has-lpi", map->gic.has_its); - object_property_set_link(OBJECT(dev), "sysmem", OBJECT(mr), &error_abort); qdev_prop_set_uint32(dev, "first-cpu-index", first_cpu_idx); + if (map->gic.version == 3) { + QList *redist_region_count; + + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, num_cpu); + qdev_prop_set_array(dev, "redist-region-count", redist_region_count); + qdev_prop_set_bit(dev, "has-lpi", map->gic.has_its); + object_property_set_link(OBJECT(dev), "sysmem", OBJECT(mr), + &error_abort); + + } + sysbus_realize_and_unref(sbd, &error_fatal); memory_region_add_subregion(mr, map->gic.dist, sysbus_mmio_get_region(sbd, 0)); - memory_region_add_subregion(mr, map->gic.redist, - sysbus_mmio_get_region(sbd, 1)); + + if (map->gic.version == 3) { + memory_region_add_subregion(mr, map->gic.redist, + sysbus_mmio_get_region(sbd, 1)); + } else { + memory_region_add_subregion(mr, map->gic.cpu_iface, + sysbus_mmio_get_region(sbd, 1)); + } if (map->dtb_expose) { - node = versal_fdt_add_subnode(s, "/gic", map->gic.dist, compatible, - sizeof(compatible)); + if (map->gic.version == 3) { + node = versal_fdt_add_subnode(s, "/gic", map->gic.dist, + gicv3_compat, + sizeof(gicv3_compat)); + qemu_fdt_setprop_sized_cells(s->cfg.fdt, node, "reg", + 2, map->gic.dist, + 2, 0x10000, + 2, map->gic.redist, + 2, GICV3_REDIST_SIZE * num_cpu); + } else { + node = versal_fdt_add_subnode(s, "/gic", map->gic.dist, + gicv2_compat, + sizeof(gicv2_compat)); + qemu_fdt_setprop_sized_cells(s->cfg.fdt, node, "reg", + 2, map->gic.dist, + 2, 0x1000, + 2, map->gic.cpu_iface, + 2, 0x1000); + } + qemu_fdt_setprop_cell(s->cfg.fdt, node, "phandle", s->phandle.gic); qemu_fdt_setprop_cell(s->cfg.fdt, node, "#interrupt-cells", 3); - qemu_fdt_setprop_sized_cells(s->cfg.fdt, node, "reg", - 2, map->gic.dist, - 2, 0x10000, - 2, map->gic.redist, - 2, GICV3_REDIST_SIZE * num_cpu); qemu_fdt_setprop_cells(s->cfg.fdt, node, "interrupts", GIC_FDT_IRQ_TYPE_PPI, VERSAL_GIC_MAINT_IRQ, GIC_FDT_IRQ_FLAGS_LEVEL_HI); From 805bdea08a17db38f0d860f9acad2eb3cf8e6fb5 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:44 +0200 Subject: [PATCH 1359/1794] hw/arm/xlnx-versal: rpu: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the RPU cluster creation using the VersalMap structure. This effectively instantiate the RPU GICv2 which was not instantiated before. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-27-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 1 + hw/arm/xlnx-versal.c | 60 +++++++++++++++--------------------- include/hw/arm/xlnx-versal.h | 11 ------- 3 files changed, 26 insertions(+), 46 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 27594f78c8f30..5958e71251973 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -24,6 +24,7 @@ #include "hw/arm/boot.h" #include "target/arm/multiprocessing.h" #include "qom/object.h" +#include "target/arm/cpu.h" #define TYPE_XLNX_VERSAL_VIRT_MACHINE MACHINE_TYPE_NAME("xlnx-versal-virt") OBJECT_DECLARE_SIMPLE_TYPE(VersalVirt, XLNX_VERSAL_VIRT_MACHINE) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 45ea47a8b97d2..e89c66313c19d 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -47,6 +47,8 @@ #include "hw/intc/arm_gicv3_its_common.h" #include "hw/intc/arm_gic.h" #include "hw/core/split-irq.h" +#include "target/arm/cpu.h" +#include "hw/cpu/cluster.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -107,6 +109,7 @@ typedef struct VersalCpuClusterMap { typedef struct VersalMap { VersalCpuClusterMap apu; + VersalCpuClusterMap rpu; VersalSimplePeriphMap uart[2]; size_t num_uart; @@ -226,6 +229,27 @@ static const VersalMap VERSAL_MAP = { }, }, + .rpu = { + .name = "rpu", + .cpu_model = ARM_CPU_TYPE_NAME("cortex-r5f"), + .num_cluster = 1, + .num_core = 2, + .qemu_cluster_id = 1, + .mp_affinity = { + .base = 0x100, + .core_shift = ARM_AFF0_SHIFT, + .cluster_shift = ARM_AFF1_SHIFT, + }, + .start_powered_off = SPO_ALL, + .dtb_expose = false, + .gic = { + .version = 2, + .dist = 0xf9000000, + .cpu_iface = 0xf9001000, + .num_irq = 192, + }, + }, + .uart[0] = { 0xff000000, 18 }, .uart[1] = { 0xff010000, 19 }, .num_uart = 2, @@ -806,35 +830,6 @@ static void versal_create_cpu_cluster(Versal *s, const VersalCpuClusterMap *map) } } -static void versal_create_rpu_cpus(Versal *s) -{ - int i; - - object_initialize_child(OBJECT(s), "rpu-cluster", &s->lpd.rpu.cluster, - TYPE_CPU_CLUSTER); - qdev_prop_set_uint32(DEVICE(&s->lpd.rpu.cluster), "cluster-id", 1); - - for (i = 0; i < ARRAY_SIZE(s->lpd.rpu.cpu); i++) { - Object *obj; - - object_initialize_child(OBJECT(&s->lpd.rpu.cluster), - "rpu-cpu[*]", &s->lpd.rpu.cpu[i], - XLNX_VERSAL_RCPU_TYPE); - obj = OBJECT(&s->lpd.rpu.cpu[i]); - object_property_set_bool(obj, "start-powered-off", true, - &error_abort); - - object_property_set_int(obj, "mp-affinity", 0x100 | i, &error_abort); - object_property_set_int(obj, "core-count", ARRAY_SIZE(s->lpd.rpu.cpu), - &error_abort); - object_property_set_link(obj, "memory", OBJECT(&s->lpd.rpu.mr), - &error_abort); - qdev_realize(DEVICE(obj), NULL, &error_fatal); - } - - qdev_realize(DEVICE(&s->lpd.rpu.cluster), NULL, &error_fatal); -} - static void versal_create_uart(Versal *s, const VersalSimplePeriphMap *map, int chardev_idx) @@ -1636,7 +1631,7 @@ static void versal_realize(DeviceState *dev, Error **errp) qemu_fdt_setprop_cell(s->cfg.fdt, "/", "#address-cells", 0x2); versal_create_cpu_cluster(s, &map->apu); - versal_create_rpu_cpus(s); + versal_create_cpu_cluster(s, &map->rpu); for (i = 0; i < map->num_uart; i++) { versal_create_uart(s, &map->uart[i], i); @@ -1692,8 +1687,6 @@ static void versal_realize(DeviceState *dev, Error **errp) MM_OCM_SIZE, &error_fatal); memory_region_add_subregion_overlap(&s->mr_ps, MM_OCM, &s->lpd.mr_ocm, 0); - memory_region_add_subregion_overlap(&s->lpd.rpu.mr, 0, - &s->lpd.rpu.mr_ps_alias, 0); } DeviceState *versal_get_boot_cpu(Versal *s) @@ -1804,10 +1797,7 @@ static void versal_base_init(Object *obj) Versal *s = XLNX_VERSAL_BASE(obj); size_t i, num_can; - memory_region_init(&s->lpd.rpu.mr, obj, "mr-rpu", UINT64_MAX); memory_region_init(&s->mr_ps, obj, "mr-ps-switch", UINT64_MAX); - memory_region_init_alias(&s->lpd.rpu.mr_ps_alias, OBJECT(s), - "mr-rpu-ps-alias", &s->mr_ps, 0, UINT64_MAX); s->intc = g_array_new(false, false, sizeof(DeviceState *)); num_can = versal_get_map(s)->num_canfd; diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 984f9f2ccddc2..0a91ec7ae3652 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -14,10 +14,8 @@ #define XLNX_VERSAL_H #include "hw/sysbus.h" -#include "hw/cpu/cluster.h" #include "qom/object.h" #include "net/can_emu.h" -#include "target/arm/cpu.h" #include "hw/arm/xlnx-versal-version.h" #define TYPE_XLNX_VERSAL_BASE "xlnx-versal-base" @@ -52,15 +50,6 @@ struct Versal { struct { MemoryRegion mr_ocm; - - /* Real-time Processing Unit. */ - struct { - MemoryRegion mr; - MemoryRegion mr_ps_alias; - - CPUClusterState cluster; - ARMCPU cpu[XLNX_VERSAL_NR_RCPUS]; - } rpu; } lpd; struct { From cf8f12b6e719dca00e015febb5d5bbb1f3d6aa7f Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:45 +0200 Subject: [PATCH 1360/1794] hw/arm/xlnx-versal: ocm: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the OCM creation using the VersalMap structure. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-28-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 20 ++++++++++++++++---- include/hw/arm/xlnx-versal.h | 4 ---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index e89c66313c19d..8aa82ceb839fb 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -73,6 +73,11 @@ typedef struct VersalSimplePeriphMap { int irq; } VersalSimplePeriphMap; +typedef struct VersalMemMap { + uint64_t addr; + uint64_t size; +} VersalMemMap; + typedef struct VersalGicMap { int version; uint64_t dist; @@ -108,6 +113,8 @@ typedef struct VersalCpuClusterMap { } VersalCpuClusterMap; typedef struct VersalMap { + VersalMemMap ocm; + VersalCpuClusterMap apu; VersalCpuClusterMap rpu; @@ -207,6 +214,11 @@ typedef struct VersalMap { } VersalMap; static const VersalMap VERSAL_MAP = { + .ocm = { + .addr = 0xfffc0000, + .size = 0x40000, + }, + .apu = { .name = "apu", .cpu_model = ARM_CPU_TYPE_NAME("cortex-a72"), @@ -1608,6 +1620,7 @@ static void versal_realize(DeviceState *dev, Error **errp) { Versal *s = XLNX_VERSAL_BASE(dev); DeviceState *slcr, *ospi; + MemoryRegion *ocm; Object *container; const VersalMap *map = versal_get_map(s); size_t i; @@ -1683,10 +1696,9 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_unimp(s); /* Create the On Chip Memory (OCM). */ - memory_region_init_ram(&s->lpd.mr_ocm, OBJECT(s), "ocm", - MM_OCM_SIZE, &error_fatal); - - memory_region_add_subregion_overlap(&s->mr_ps, MM_OCM, &s->lpd.mr_ocm, 0); + ocm = g_new(MemoryRegion, 1); + memory_region_init_ram(ocm, OBJECT(s), "ocm", map->ocm.size, &error_fatal); + memory_region_add_subregion_overlap(&s->mr_ps, map->ocm.addr, ocm, 0); } DeviceState *versal_get_boot_cpu(Versal *s) diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 0a91ec7ae3652..e1d6e545495ad 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -48,10 +48,6 @@ struct Versal { MemoryRegion mr_ddr_ranges[4]; } noc; - struct { - MemoryRegion mr_ocm; - } lpd; - struct { uint32_t clk_25mhz; uint32_t clk_125mhz; From e4a9bc0aae9f98c64a9689f9471234636e6d3ae0 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:46 +0200 Subject: [PATCH 1361/1794] hw/arm/xlnx-versal: ddr: refactor creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the DDR aperture regions creation using the VersalMap structure. Device creation and FDT node creation are split into two functions because the later must happen during ARM virtual bootloader modify_dtb callback. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-29-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 79 +----------------------------------- hw/arm/xlnx-versal.c | 73 ++++++++++++++++++++++----------- include/hw/arm/xlnx-versal.h | 7 +--- 3 files changed, 53 insertions(+), 106 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 5958e71251973..ad7b3135a671f 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -106,88 +106,13 @@ static void fdt_nop_memory_nodes(void *fdt, Error **errp) g_strfreev(node_path); } -static void fdt_add_memory_nodes(VersalVirt *s, void *fdt, uint64_t ram_size) -{ - /* Describes the various split DDR access regions. */ - static const struct { - uint64_t base; - uint64_t size; - } addr_ranges[] = { - { MM_TOP_DDR, MM_TOP_DDR_SIZE }, - { MM_TOP_DDR_2, MM_TOP_DDR_2_SIZE }, - { MM_TOP_DDR_3, MM_TOP_DDR_3_SIZE }, - { MM_TOP_DDR_4, MM_TOP_DDR_4_SIZE } - }; - uint64_t mem_reg_prop[8] = {0}; - uint64_t size = ram_size; - Error *err = NULL; - char *name; - int i; - - fdt_nop_memory_nodes(fdt, &err); - if (err) { - error_report_err(err); - return; - } - - name = g_strdup_printf("/memory@%x", MM_TOP_DDR); - for (i = 0; i < ARRAY_SIZE(addr_ranges) && size; i++) { - uint64_t mapsize; - - mapsize = size < addr_ranges[i].size ? size : addr_ranges[i].size; - - mem_reg_prop[i * 2] = addr_ranges[i].base; - mem_reg_prop[i * 2 + 1] = mapsize; - size -= mapsize; - } - qemu_fdt_add_subnode(fdt, name); - qemu_fdt_setprop_string(fdt, name, "device_type", "memory"); - - switch (i) { - case 1: - qemu_fdt_setprop_sized_cells(fdt, name, "reg", - 2, mem_reg_prop[0], - 2, mem_reg_prop[1]); - break; - case 2: - qemu_fdt_setprop_sized_cells(fdt, name, "reg", - 2, mem_reg_prop[0], - 2, mem_reg_prop[1], - 2, mem_reg_prop[2], - 2, mem_reg_prop[3]); - break; - case 3: - qemu_fdt_setprop_sized_cells(fdt, name, "reg", - 2, mem_reg_prop[0], - 2, mem_reg_prop[1], - 2, mem_reg_prop[2], - 2, mem_reg_prop[3], - 2, mem_reg_prop[4], - 2, mem_reg_prop[5]); - break; - case 4: - qemu_fdt_setprop_sized_cells(fdt, name, "reg", - 2, mem_reg_prop[0], - 2, mem_reg_prop[1], - 2, mem_reg_prop[2], - 2, mem_reg_prop[3], - 2, mem_reg_prop[4], - 2, mem_reg_prop[5], - 2, mem_reg_prop[6], - 2, mem_reg_prop[7]); - break; - default: - g_assert_not_reached(); - } - g_free(name); -} - static void versal_virt_modify_dtb(const struct arm_boot_info *binfo, void *fdt) { VersalVirt *s = container_of(binfo, VersalVirt, binfo); - fdt_add_memory_nodes(s, fdt, binfo->ram_size); + fdt_nop_memory_nodes(s->fdt, &error_abort); + versal_fdt_add_memory_nodes(&s->soc, binfo->ram_size); } static void *versal_virt_get_dtb(const struct arm_boot_info *binfo, diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 8aa82ceb839fb..f1b704175ff9c 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -115,6 +115,11 @@ typedef struct VersalCpuClusterMap { typedef struct VersalMap { VersalMemMap ocm; + struct VersalDDRMap { + VersalMemMap chan[4]; + size_t num_chan; + } ddr; + VersalCpuClusterMap apu; VersalCpuClusterMap rpu; @@ -219,6 +224,14 @@ static const VersalMap VERSAL_MAP = { .size = 0x40000, }, + .ddr = { + .chan[0] = { .addr = 0x0, .size = 2 * GiB }, + .chan[1] = { .addr = 0x800000000ull, .size = 32 * GiB }, + .chan[2] = { .addr = 0xc00000000ull, .size = 256 * GiB }, + .chan[3] = { .addr = 0x10000000000ull, .size = 734 * GiB }, + .num_chan = 4, + }, + .apu = { .name = "apu", .cpu_model = ARM_CPU_TYPE_NAME("cortex-a72"), @@ -1483,46 +1496,58 @@ static inline void versal_create_crl(Versal *s) versal_sysbus_connect_irq(s, SYS_BUS_DEVICE(dev), 0, map->crl.irq); } -/* This takes the board allocated linear DDR memory and creates aliases +/* + * This takes the board allocated linear DDR memory and creates aliases * for each split DDR range/aperture on the Versal address map. */ -static void versal_map_ddr(Versal *s) +static void versal_map_ddr(Versal *s, const struct VersalDDRMap *map) { uint64_t size = memory_region_size(s->cfg.mr_ddr); - /* Describes the various split DDR access regions. */ - static const struct { - uint64_t base; - uint64_t size; - } addr_ranges[] = { - { MM_TOP_DDR, MM_TOP_DDR_SIZE }, - { MM_TOP_DDR_2, MM_TOP_DDR_2_SIZE }, - { MM_TOP_DDR_3, MM_TOP_DDR_3_SIZE }, - { MM_TOP_DDR_4, MM_TOP_DDR_4_SIZE } - }; uint64_t offset = 0; int i; - assert(ARRAY_SIZE(addr_ranges) == ARRAY_SIZE(s->noc.mr_ddr_ranges)); - for (i = 0; i < ARRAY_SIZE(addr_ranges) && size; i++) { - char *name; + for (i = 0; i < map->num_chan && size; i++) { uint64_t mapsize; + MemoryRegion *alias; + + mapsize = MIN(size, map->chan[i].size); - mapsize = size < addr_ranges[i].size ? size : addr_ranges[i].size; - name = g_strdup_printf("noc-ddr-range%d", i); /* Create the MR alias. */ - memory_region_init_alias(&s->noc.mr_ddr_ranges[i], OBJECT(s), - name, s->cfg.mr_ddr, - offset, mapsize); + alias = g_new(MemoryRegion, 1); + memory_region_init_alias(alias, OBJECT(s), "noc-ddr-range", + s->cfg.mr_ddr, offset, mapsize); /* Map it onto the NoC MR. */ - memory_region_add_subregion(&s->mr_ps, addr_ranges[i].base, - &s->noc.mr_ddr_ranges[i]); + memory_region_add_subregion(&s->mr_ps, map->chan[i].addr, alias); offset += mapsize; size -= mapsize; - g_free(name); } } +void versal_fdt_add_memory_nodes(Versal *s, uint64_t size) +{ + const struct VersalDDRMap *map = &versal_get_map(s)->ddr; + g_autofree char *node; + g_autofree uint64_t *reg; + int i; + + reg = g_new(uint64_t, map->num_chan * 2); + + for (i = 0; i < map->num_chan && size; i++) { + uint64_t mapsize; + + mapsize = MIN(size, map->chan[i].size); + + reg[i * 2] = cpu_to_be64(map->chan[i].addr); + reg[i * 2 + 1] = cpu_to_be64(mapsize); + + size -= mapsize; + } + + node = versal_fdt_add_subnode(s, "/memory", 0, "memory", sizeof("memory")); + qemu_fdt_setprop(s->cfg.fdt, node, "reg", reg, sizeof(uint64_t) * i * 2); +} + static void versal_unimp_area(Versal *s, const char *name, MemoryRegion *mr, hwaddr base, hwaddr size) @@ -1692,7 +1717,7 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_cfu(s, &map->cfu); versal_create_crl(s); - versal_map_ddr(s); + versal_map_ddr(s, &map->ddr); versal_unimp(s); /* Create the On Chip Memory (OCM). */ diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index e1d6e545495ad..39bc414c85c7c 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -43,11 +43,6 @@ struct Versal { GArray *intc; MemoryRegion mr_ps; - struct { - /* 4 ranges to access DDR. */ - MemoryRegion mr_ddr_ranges[4]; - } noc; - struct { uint32_t clk_25mhz; uint32_t clk_125mhz; @@ -73,6 +68,8 @@ static inline void versal_set_fdt(Versal *s, void *fdt) s->cfg.fdt = fdt; } +void versal_fdt_add_memory_nodes(Versal *s, uint64_t ram_size); + DeviceState *versal_get_boot_cpu(Versal *s); void versal_sdhci_plug_card(Versal *s, int sd_idx, BlockBackend *blk); void versal_efuse_attach_drive(Versal *s, BlockBackend *blk); From 87830c4dd626804072181cf126daa206c5721dc4 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:47 +0200 Subject: [PATCH 1362/1794] hw/arm/xlnx-versal: add the versal_get_num_cpu accessor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the versal_get_num_cpu accessor to the Versal SoC to retrieve the number of CPUs in the SoC. Use it in the xlnx-versal-virt machine. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-30-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 7 ++++--- hw/arm/xlnx-versal.c | 8 ++++++++ include/hw/arm/xlnx-versal.h | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index ad7b3135a671f..274a7ef9889cb 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -346,12 +346,13 @@ static void versal_virt_machine_finalize(Object *obj) static void versal_virt_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); + int num_cpu = versal_get_num_cpu(VERSAL_VER_VERSAL); mc->desc = "Xilinx Versal Virtual development board"; mc->init = versal_virt_init; - mc->min_cpus = XLNX_VERSAL_NR_ACPUS + XLNX_VERSAL_NR_RCPUS; - mc->max_cpus = XLNX_VERSAL_NR_ACPUS + XLNX_VERSAL_NR_RCPUS; - mc->default_cpus = XLNX_VERSAL_NR_ACPUS + XLNX_VERSAL_NR_RCPUS; + mc->min_cpus = num_cpu; + mc->max_cpus = num_cpu; + mc->default_cpus = num_cpu; mc->no_cdrom = true; mc->auto_create_sdcard = true; mc->default_ram_id = "ddr"; diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index f1b704175ff9c..2e28b807d71e4 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -1815,6 +1815,14 @@ hwaddr versal_get_reserved_mmio_addr(Versal *s) return map->reserved.mmio_start; } +int versal_get_num_cpu(VersalVersion version) +{ + const VersalMap *map = VERSION_TO_MAP[version]; + + return map->apu.num_cluster * map->apu.num_core + + map->rpu.num_cluster * map->rpu.num_core; +} + int versal_get_num_can(VersalVersion version) { const VersalMap *map = VERSION_TO_MAP[version]; diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 39bc414c85c7c..7bdf6dab629bf 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -80,6 +80,7 @@ void versal_ospi_create_flash(Versal *s, int flash_idx, const char *flash_mdl, qemu_irq versal_get_reserved_irq(Versal *s, int idx, int *dtb_idx); hwaddr versal_get_reserved_mmio_addr(Versal *s); +int versal_get_num_cpu(VersalVersion version); int versal_get_num_can(VersalVersion version); int versal_get_num_sdhci(VersalVersion version); From 3074e35115f51387d26774a37d79ca761721fb5c Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:48 +0200 Subject: [PATCH 1363/1794] hw/misc/xlnx-versal-crl: remove unnecessary include directives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop unused include directives from xlnx-versal-crl.c Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-31-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/misc/xlnx-versal-crl.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/hw/misc/xlnx-versal-crl.c b/hw/misc/xlnx-versal-crl.c index 08ff2fcc24ff9..f288545967a73 100644 --- a/hw/misc/xlnx-versal-crl.c +++ b/hw/misc/xlnx-versal-crl.c @@ -1,16 +1,13 @@ /* * QEMU model of the Clock-Reset-LPD (CRL). * - * Copyright (c) 2022 Advanced Micro Devices, Inc. + * Copyright (c) 2022-2025 Advanced Micro Devices, Inc. * SPDX-License-Identifier: GPL-2.0-or-later * * Written by Edgar E. Iglesias */ #include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/log.h" -#include "qemu/bitops.h" #include "migration/vmstate.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" From c27ca57288109b434a464b8ee8d05ec4a387e468 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:49 +0200 Subject: [PATCH 1364/1794] hw/misc/xlnx-versal-crl: split into base/concrete classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the TYPE_XLNX_VERSAL_CRL type into base and concrete classes. This is in preparation for the versal2 version of the CRL. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-32-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/misc/xlnx-versal-crl.c | 48 +++++++++++++++++++------------ include/hw/misc/xlnx-versal-crl.h | 31 ++++++++++++++++++-- 2 files changed, 58 insertions(+), 21 deletions(-) diff --git a/hw/misc/xlnx-versal-crl.c b/hw/misc/xlnx-versal-crl.c index f288545967a73..be89e0da40d98 100644 --- a/hw/misc/xlnx-versal-crl.c +++ b/hw/misc/xlnx-versal-crl.c @@ -298,7 +298,7 @@ static const RegisterAccessInfo crl_regs_info[] = { } }; -static void crl_reset_enter(Object *obj, ResetType type) +static void versal_crl_reset_enter(Object *obj, ResetType type) { XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj); unsigned int i; @@ -308,7 +308,7 @@ static void crl_reset_enter(Object *obj, ResetType type) } } -static void crl_reset_hold(Object *obj, ResetType type) +static void versal_crl_reset_hold(Object *obj, ResetType type) { XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj); @@ -325,20 +325,22 @@ static const MemoryRegionOps crl_ops = { }, }; -static void crl_init(Object *obj) +static void versal_crl_init(Object *obj) { XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj); + XlnxVersalCRLBase *xvcb = XLNX_VERSAL_CRL_BASE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); int i; - s->reg_array = + xvcb->reg_array = register_init_block32(DEVICE(obj), crl_regs_info, ARRAY_SIZE(crl_regs_info), s->regs_info, s->regs, &crl_ops, XLNX_VERSAL_CRL_ERR_DEBUG, CRL_R_MAX * 4); - sysbus_init_mmio(sbd, &s->reg_array->mem); + xvcb->regs = s->regs; + sysbus_init_mmio(sbd, &xvcb->reg_array->mem); sysbus_init_irq(sbd, &s->irq); for (i = 0; i < ARRAY_SIZE(s->cfg.cpu_r5); ++i) { @@ -377,11 +379,11 @@ static void crl_init(Object *obj) static void crl_finalize(Object *obj) { - XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj); + XlnxVersalCRLBase *s = XLNX_VERSAL_CRL_BASE(obj); register_finalize_block(s->reg_array); } -static const VMStateDescription vmstate_crl = { +static const VMStateDescription vmstate_versal_crl = { .name = TYPE_XLNX_VERSAL_CRL, .version_id = 1, .minimum_version_id = 1, @@ -391,29 +393,37 @@ static const VMStateDescription vmstate_crl = { } }; -static void crl_class_init(ObjectClass *klass, const void *data) +static void versal_crl_class_init(ObjectClass *klass, const void *data) { - ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); - dc->vmsd = &vmstate_crl; - - rc->phases.enter = crl_reset_enter; - rc->phases.hold = crl_reset_hold; + dc->vmsd = &vmstate_versal_crl; + rc->phases.enter = versal_crl_reset_enter; + rc->phases.hold = versal_crl_reset_hold; } -static const TypeInfo crl_info = { - .name = TYPE_XLNX_VERSAL_CRL, +static const TypeInfo crl_base_info = { + .name = TYPE_XLNX_VERSAL_CRL_BASE, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(XlnxVersalCRL), - .class_init = crl_class_init, - .instance_init = crl_init, + .instance_size = sizeof(XlnxVersalCRLBase), + .class_size = sizeof(XlnxVersalCRLBaseClass), .instance_finalize = crl_finalize, + .abstract = true, +}; + +static const TypeInfo versal_crl_info = { + .name = TYPE_XLNX_VERSAL_CRL, + .parent = TYPE_XLNX_VERSAL_CRL_BASE, + .instance_size = sizeof(XlnxVersalCRL), + .instance_init = versal_crl_init, + .class_init = versal_crl_class_init, }; static void crl_register_types(void) { - type_register_static(&crl_info); + type_register_static(&crl_base_info); + type_register_static(&versal_crl_info); } type_init(crl_register_types) diff --git a/include/hw/misc/xlnx-versal-crl.h b/include/hw/misc/xlnx-versal-crl.h index dba6d3585d12b..2b39d203a67b6 100644 --- a/include/hw/misc/xlnx-versal-crl.h +++ b/include/hw/misc/xlnx-versal-crl.h @@ -2,6 +2,7 @@ * QEMU model of the Clock-Reset-LPD (CRL). * * Copyright (c) 2022 Xilinx Inc. + * Copyright (c) 2025 Advanced Micro Devices, Inc. * SPDX-License-Identifier: GPL-2.0-or-later * * Written by Edgar E. Iglesias @@ -12,8 +13,13 @@ #include "hw/sysbus.h" #include "hw/register.h" #include "target/arm/cpu-qom.h" +#include "hw/arm/xlnx-versal-version.h" +#define TYPE_XLNX_VERSAL_CRL_BASE "xlnx-versal-crl-base" #define TYPE_XLNX_VERSAL_CRL "xlnx-versal-crl" + +OBJECT_DECLARE_TYPE(XlnxVersalCRLBase, XlnxVersalCRLBaseClass, + XLNX_VERSAL_CRL_BASE) OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalCRL, XLNX_VERSAL_CRL) REG32(ERR_CTRL, 0x0) @@ -216,8 +222,19 @@ REG32(PSM_RST_MODE, 0x370) #define RPU_MAX_CPU 2 -struct XlnxVersalCRL { +struct XlnxVersalCRLBase { SysBusDevice parent_obj; + + RegisterInfoArray *reg_array; + uint32_t *regs; +}; + +struct XlnxVersalCRLBaseClass { + SysBusDeviceClass parent_class; +}; + +struct XlnxVersalCRL { + XlnxVersalCRLBase parent_obj; qemu_irq irq; struct { @@ -228,8 +245,18 @@ struct XlnxVersalCRL { DeviceState *usb; } cfg; - RegisterInfoArray *reg_array; uint32_t regs[CRL_R_MAX]; RegisterInfo regs_info[CRL_R_MAX]; }; + +static inline const char *xlnx_versal_crl_class_name(VersalVersion ver) +{ + switch (ver) { + case VERSAL_VER_VERSAL: + return TYPE_XLNX_VERSAL_CRL; + default: + g_assert_not_reached(); + } +} + #endif From ff789d1556af9994494f0bebf7191b50eaacc126 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:50 +0200 Subject: [PATCH 1365/1794] hw/misc/xlnx-versal-crl: refactor device reset logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the device reset logic to have a common register write callback for all the devices. This uses a decode function to map the register address to the actual peripheral to reset. This refactoring changes the CPU property name from cpu_r5[*] to rpu[*] to ease with the connections in the Versal SoC. It also fixes a bug where the gem device pointer was mapped to the usb link property. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-33-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/misc/xlnx-versal-crl.c | 163 ++++++++++++++++-------------- include/hw/misc/xlnx-versal-crl.h | 8 +- 2 files changed, 92 insertions(+), 79 deletions(-) diff --git a/hw/misc/xlnx-versal-crl.c b/hw/misc/xlnx-versal-crl.c index be89e0da40d98..6225a92e0bdec 100644 --- a/hw/misc/xlnx-versal-crl.c +++ b/hw/misc/xlnx-versal-crl.c @@ -55,90 +55,99 @@ static uint64_t crl_disable_prew(RegisterInfo *reg, uint64_t val64) return 0; } -static void crl_reset_dev(XlnxVersalCRL *s, DeviceState *dev, - bool rst_old, bool rst_new) +static DeviceState **versal_decode_periph_rst(XlnxVersalCRLBase *s, + hwaddr addr, size_t *count) { - device_cold_reset(dev); -} + size_t idx; + XlnxVersalCRL *xvc = XLNX_VERSAL_CRL(s); -static void crl_reset_cpu(XlnxVersalCRL *s, ARMCPU *armcpu, - bool rst_old, bool rst_new) -{ - if (rst_new) { - arm_set_cpu_off(arm_cpu_mp_affinity(armcpu)); - } else { - arm_set_cpu_on_and_reset(arm_cpu_mp_affinity(armcpu)); - } -} + *count = 1; -#define REGFIELD_RESET(type, s, reg, f, new_val, dev) { \ - bool old_f = ARRAY_FIELD_EX32((s)->regs, reg, f); \ - bool new_f = FIELD_EX32(new_val, reg, f); \ - \ - /* Detect edges. */ \ - if (dev && old_f != new_f) { \ - crl_reset_ ## type(s, dev, old_f, new_f); \ - } \ -} + switch (addr) { + case A_RST_CPU_R5: + return xvc->cfg.rpu; -static uint64_t crl_rst_r5_prew(RegisterInfo *reg, uint64_t val64) -{ - XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); + case A_RST_ADMA: + /* A single register fans out to all DMA reset inputs */ + *count = ARRAY_SIZE(xvc->cfg.adma); + return xvc->cfg.adma; - REGFIELD_RESET(cpu, s, RST_CPU_R5, RESET_CPU0, val64, s->cfg.cpu_r5[0]); - REGFIELD_RESET(cpu, s, RST_CPU_R5, RESET_CPU1, val64, s->cfg.cpu_r5[1]); - return val64; -} + case A_RST_UART0 ... A_RST_UART1: + idx = (addr - A_RST_UART0) / sizeof(uint32_t); + return xvc->cfg.uart + idx; -static uint64_t crl_rst_adma_prew(RegisterInfo *reg, uint64_t val64) -{ - XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); - int i; + case A_RST_GEM0 ... A_RST_GEM1: + idx = (addr - A_RST_GEM0) / sizeof(uint32_t); + return xvc->cfg.gem + idx; + + case A_RST_USB0: + return xvc->cfg.usb; - /* A single register fans out to all ADMA reset inputs. */ - for (i = 0; i < ARRAY_SIZE(s->cfg.adma); i++) { - REGFIELD_RESET(dev, s, RST_ADMA, RESET, val64, s->cfg.adma[i]); + default: + /* invalid or unimplemented */ + return NULL; } - return val64; } -static uint64_t crl_rst_uart0_prew(RegisterInfo *reg, uint64_t val64) +static uint64_t crl_rst_cpu_prew(RegisterInfo *reg, uint64_t val64) { - XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); + XlnxVersalCRLBase *s = XLNX_VERSAL_CRL_BASE(reg->opaque); + XlnxVersalCRLBaseClass *xvcbc = XLNX_VERSAL_CRL_BASE_GET_CLASS(s); + DeviceState **dev; + size_t i, count; - REGFIELD_RESET(dev, s, RST_UART0, RESET, val64, s->cfg.uart[0]); - return val64; -} + dev = xvcbc->decode_periph_rst(s, reg->access->addr, &count); -static uint64_t crl_rst_uart1_prew(RegisterInfo *reg, uint64_t val64) -{ - XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); + for (i = 0; i < 2; i++) { + bool prev, new; + uint64_t aff; - REGFIELD_RESET(dev, s, RST_UART1, RESET, val64, s->cfg.uart[1]); - return val64; -} + prev = extract32(s->regs[reg->access->addr / 4], i, 1); + new = extract32(val64, i, 1); -static uint64_t crl_rst_gem0_prew(RegisterInfo *reg, uint64_t val64) -{ - XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); + if (prev == new) { + continue; + } - REGFIELD_RESET(dev, s, RST_GEM0, RESET, val64, s->cfg.gem[0]); - return val64; -} + aff = arm_cpu_mp_affinity(ARM_CPU(dev[i])); -static uint64_t crl_rst_gem1_prew(RegisterInfo *reg, uint64_t val64) -{ - XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); + if (new) { + arm_set_cpu_off(aff); + } else { + arm_set_cpu_on_and_reset(aff); + } + } - REGFIELD_RESET(dev, s, RST_GEM1, RESET, val64, s->cfg.gem[1]); return val64; } -static uint64_t crl_rst_usb_prew(RegisterInfo *reg, uint64_t val64) +static uint64_t crl_rst_dev_prew(RegisterInfo *reg, uint64_t val64) { - XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); + XlnxVersalCRLBase *s = XLNX_VERSAL_CRL_BASE(reg->opaque); + XlnxVersalCRLBaseClass *xvcbc = XLNX_VERSAL_CRL_BASE_GET_CLASS(s); + DeviceState **dev; + bool prev, new; + size_t i, count; + + dev = xvcbc->decode_periph_rst(s, reg->access->addr, &count); + + if (dev == NULL) { + return val64; + } + + prev = s->regs[reg->access->addr / 4] & 0x1; + new = val64 & 0x1; + + if (prev == new) { + return val64; + } + + for (i = 0; i < count; i++) { + if (dev[i]) { + device_cold_reset(dev[i]); + } + } - REGFIELD_RESET(dev, s, RST_USB0, RESET, val64, s->cfg.usb); return val64; } @@ -244,27 +253,27 @@ static const RegisterAccessInfo crl_regs_info[] = { },{ .name = "RST_CPU_R5", .addr = A_RST_CPU_R5, .reset = 0x17, .rsvd = 0x8, - .pre_write = crl_rst_r5_prew, + .pre_write = crl_rst_cpu_prew, },{ .name = "RST_ADMA", .addr = A_RST_ADMA, .reset = 0x1, - .pre_write = crl_rst_adma_prew, + .pre_write = crl_rst_dev_prew, },{ .name = "RST_GEM0", .addr = A_RST_GEM0, .reset = 0x1, - .pre_write = crl_rst_gem0_prew, + .pre_write = crl_rst_dev_prew, },{ .name = "RST_GEM1", .addr = A_RST_GEM1, .reset = 0x1, - .pre_write = crl_rst_gem1_prew, + .pre_write = crl_rst_dev_prew, },{ .name = "RST_SPARE", .addr = A_RST_SPARE, .reset = 0x1, },{ .name = "RST_USB0", .addr = A_RST_USB0, .reset = 0x1, - .pre_write = crl_rst_usb_prew, + .pre_write = crl_rst_dev_prew, },{ .name = "RST_UART0", .addr = A_RST_UART0, .reset = 0x1, - .pre_write = crl_rst_uart0_prew, + .pre_write = crl_rst_dev_prew, },{ .name = "RST_UART1", .addr = A_RST_UART1, .reset = 0x1, - .pre_write = crl_rst_uart1_prew, + .pre_write = crl_rst_dev_prew, },{ .name = "RST_SPI0", .addr = A_RST_SPI0, .reset = 0x1, },{ .name = "RST_SPI1", .addr = A_RST_SPI1, @@ -343,9 +352,9 @@ static void versal_crl_init(Object *obj) sysbus_init_mmio(sbd, &xvcb->reg_array->mem); sysbus_init_irq(sbd, &s->irq); - for (i = 0; i < ARRAY_SIZE(s->cfg.cpu_r5); ++i) { - object_property_add_link(obj, "cpu_r5[*]", TYPE_ARM_CPU, - (Object **)&s->cfg.cpu_r5[i], + for (i = 0; i < ARRAY_SIZE(s->cfg.rpu); ++i) { + object_property_add_link(obj, "rpu[*]", TYPE_ARM_CPU, + (Object **)&s->cfg.rpu[i], qdev_prop_allow_set_link_before_realize, OBJ_PROP_LINK_STRONG); } @@ -371,10 +380,12 @@ static void versal_crl_init(Object *obj) OBJ_PROP_LINK_STRONG); } - object_property_add_link(obj, "usb", TYPE_DEVICE, - (Object **)&s->cfg.gem[i], - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_STRONG); + for (i = 0; i < ARRAY_SIZE(s->cfg.usb); ++i) { + object_property_add_link(obj, "usb[*]", TYPE_DEVICE, + (Object **)&s->cfg.usb[i], + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG); + } } static void crl_finalize(Object *obj) @@ -396,11 +407,13 @@ static const VMStateDescription vmstate_versal_crl = { static void versal_crl_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + XlnxVersalCRLBaseClass *xvcc = XLNX_VERSAL_CRL_BASE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); dc->vmsd = &vmstate_versal_crl; rc->phases.enter = versal_crl_reset_enter; rc->phases.hold = versal_crl_reset_hold; + xvcc->decode_periph_rst = versal_decode_periph_rst; } static const TypeInfo crl_base_info = { diff --git a/include/hw/misc/xlnx-versal-crl.h b/include/hw/misc/xlnx-versal-crl.h index 2b39d203a67b6..7e50a95ad3caa 100644 --- a/include/hw/misc/xlnx-versal-crl.h +++ b/include/hw/misc/xlnx-versal-crl.h @@ -220,8 +220,6 @@ REG32(PSM_RST_MODE, 0x370) #define CRL_R_MAX (R_PSM_RST_MODE + 1) -#define RPU_MAX_CPU 2 - struct XlnxVersalCRLBase { SysBusDevice parent_obj; @@ -231,6 +229,8 @@ struct XlnxVersalCRLBase { struct XlnxVersalCRLBaseClass { SysBusDeviceClass parent_class; + + DeviceState ** (*decode_periph_rst)(XlnxVersalCRLBase *s, hwaddr, size_t *); }; struct XlnxVersalCRL { @@ -238,11 +238,11 @@ struct XlnxVersalCRL { qemu_irq irq; struct { - ARMCPU *cpu_r5[RPU_MAX_CPU]; + DeviceState *rpu[2]; DeviceState *adma[8]; DeviceState *uart[2]; DeviceState *gem[2]; - DeviceState *usb; + DeviceState *usb[1]; } cfg; uint32_t regs[CRL_R_MAX]; From 9401022bcddf3de377c5d64a68dad59b493a1315 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:51 +0200 Subject: [PATCH 1366/1794] hw/arm/xlnx-versal: reconnect the CRL to the other devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CRL connects to various devices through link properties to be able to reset them. The connections were dropped during the SoC refactoring. Reintroduce them now. Rely on the QOM tree to retrieve the devices to connect. The component parts of the device names are chosen to match the properties on the CRL. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-34-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 2e28b807d71e4..6604e24a9cde9 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -1476,17 +1476,46 @@ static void versal_create_cfu(Versal *s, const struct VersalCfuMap *map) sysbus_mmio_get_region(sbd, 0)); } +static inline void crl_connect_dev(Object *crl, Object *dev) +{ + const char *prop = object_get_canonical_path_component(dev); + + /* The component part of the device path matches the CRL property name */ + object_property_set_link(crl, prop, dev, &error_abort); +} + +static inline void crl_connect_dev_by_name(Versal *s, Object *crl, + const char *name, size_t num) +{ + size_t i; + + for (i = 0; i < num; i++) { + Object *dev = versal_get_child_idx(s, name, i); + + crl_connect_dev(crl, dev); + } +} + static inline void versal_create_crl(Versal *s) { const VersalMap *map; const char *crl_class; DeviceState *dev; + Object *obj; map = versal_get_map(s); crl_class = TYPE_XLNX_VERSAL_CRL; dev = qdev_new(crl_class); - object_property_add_child(OBJECT(s), "crl", OBJECT(dev)); + obj = OBJECT(dev); + object_property_add_child(OBJECT(s), "crl", obj); + + crl_connect_dev_by_name(s, obj, "rpu-cluster/rpu", + map->rpu.num_cluster * map->rpu.num_core); + crl_connect_dev_by_name(s, obj, map->zdma[0].name, map->zdma[0].num_chan); + crl_connect_dev_by_name(s, obj, "uart", map->num_uart); + crl_connect_dev_by_name(s, obj, "gem", map->num_gem); + crl_connect_dev_by_name(s, obj, "usb", map->num_usb); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_abort); From 48111f7e7266cc94816507bf8c82d8986edfec9c Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:52 +0200 Subject: [PATCH 1367/1794] hw/arm/xlnx-versal: use hw/arm/bsa.h for timer IRQ indices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the bsa.h header for ARM timer and maintainance IRQ indices instead of redefining our owns. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-35-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 28 +++++++++++++++++----------- include/hw/arm/xlnx-versal.h | 6 ------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 6604e24a9cde9..dc388300185b0 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -49,6 +49,7 @@ #include "hw/core/split-irq.h" #include "target/arm/cpu.h" #include "hw/cpu/cluster.h" +#include "hw/arm/bsa.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") @@ -673,7 +674,8 @@ static DeviceState *versal_create_gic(Versal *s, qemu_fdt_setprop_cell(s->cfg.fdt, node, "phandle", s->phandle.gic); qemu_fdt_setprop_cell(s->cfg.fdt, node, "#interrupt-cells", 3); qemu_fdt_setprop_cells(s->cfg.fdt, node, "interrupts", - GIC_FDT_IRQ_TYPE_PPI, VERSAL_GIC_MAINT_IRQ, + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_GIC_MAINT_IRQ), GIC_FDT_IRQ_FLAGS_LEVEL_HI); qemu_fdt_setprop(s->cfg.fdt, node, "interrupt-controller", NULL, 0); } @@ -698,10 +700,10 @@ static void connect_gic_to_cpu(const VersalCpuClusterMap *map, * GIC PPI inputs. */ const int timer_irq[] = { - [GTIMER_PHYS] = VERSAL_TIMER_NS_EL1_IRQ, - [GTIMER_VIRT] = VERSAL_TIMER_VIRT_IRQ, - [GTIMER_HYP] = VERSAL_TIMER_NS_EL2_IRQ, - [GTIMER_SEC] = VERSAL_TIMER_S_EL1_IRQ, + [GTIMER_PHYS] = INTID_TO_PPI(ARCH_TIMER_NS_EL1_IRQ), + [GTIMER_VIRT] = INTID_TO_PPI(ARCH_TIMER_VIRT_IRQ), + [GTIMER_HYP] = INTID_TO_PPI(ARCH_TIMER_NS_EL2_IRQ), + [GTIMER_SEC] = INTID_TO_PPI(ARCH_TIMER_S_EL1_IRQ), }; has_gtimer = arm_feature(&ARM_CPU(cpu)->env, ARM_FEATURE_GENERIC_TIMER); @@ -716,9 +718,9 @@ static void connect_gic_to_cpu(const VersalCpuClusterMap *map, if (map->gic.version == 3) { qemu_irq maint_irq; + int maint_idx = ppibase + INTID_TO_PPI(ARCH_GIC_MAINT_IRQ); - maint_irq = qdev_get_gpio_in(gic, - ppibase + VERSAL_GIC_MAINT_IRQ); + maint_irq = qdev_get_gpio_in(gic, maint_idx); qdev_connect_gpio_out_named(cpu, "gicv3-maintenance-interrupt", 0, maint_irq); } @@ -842,13 +844,17 @@ static void versal_create_cpu_cluster(Versal *s, const VersalCpuClusterMap *map) if (map->dtb_expose && has_gtimer) { qemu_fdt_add_subnode(s->cfg.fdt, "/timer"); qemu_fdt_setprop_cells(s->cfg.fdt, "/timer", "interrupts", - GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_S_EL1_IRQ, + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_TIMER_S_EL1_IRQ), GIC_FDT_IRQ_FLAGS_LEVEL_HI, - GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_NS_EL1_IRQ, + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_TIMER_NS_EL1_IRQ), GIC_FDT_IRQ_FLAGS_LEVEL_HI, - GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_VIRT_IRQ, + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_TIMER_VIRT_IRQ), GIC_FDT_IRQ_FLAGS_LEVEL_HI, - GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_NS_EL2_IRQ, + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_TIMER_NS_EL2_IRQ), GIC_FDT_IRQ_FLAGS_LEVEL_HI); qemu_fdt_setprop(s->cfg.fdt, "/timer", "compatible", compatible, sizeof(compatible)); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 7bdf6dab629bf..da0260b83de96 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -87,12 +87,6 @@ int versal_get_num_sdhci(VersalVersion version); /* Memory-map and IRQ definitions. Copied a subset from * auto-generated files. */ -#define VERSAL_GIC_MAINT_IRQ 9 -#define VERSAL_TIMER_VIRT_IRQ 11 -#define VERSAL_TIMER_S_EL1_IRQ 13 -#define VERSAL_TIMER_NS_EL1_IRQ 14 -#define VERSAL_TIMER_NS_EL2_IRQ 10 - #define VERSAL_CRL_IRQ 10 #define VERSAL_UART0_IRQ_0 18 #define VERSAL_UART1_IRQ_0 19 From 4707024bc3362f4b9859214357a687b9154c84cf Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:53 +0200 Subject: [PATCH 1368/1794] hw/arm/xlnx-versal: tidy up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove now unused macros in xlnx-versal.[ch]. Those macros have been replaced by the VersalMap structure that serves as a central description for the SoC. The ones still in use in the versal_unimp function are inlined. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-36-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 28 ++--- include/hw/arm/xlnx-versal.h | 204 ----------------------------------- 2 files changed, 7 insertions(+), 225 deletions(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index dc388300185b0..49b5b244403f8 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -18,7 +18,6 @@ #include "hw/sysbus.h" #include "net/net.h" #include "system/system.h" -#include "hw/arm/boot.h" #include "hw/misc/unimp.h" #include "hw/arm/xlnx-versal.h" #include "qemu/log.h" @@ -51,13 +50,6 @@ #include "hw/cpu/cluster.h" #include "hw/arm/bsa.h" -#define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") -#define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") -#define GEM_REVISION 0x40070106 - -#define VERSAL_NUM_PMC_APB_IRQS 18 -#define NUM_OSPI_IRQ_LINES 3 - /* * IRQ descriptor to catch the following cases: * - Multiple devices can connect to the same IRQ. They are OR'ed together. @@ -1364,7 +1356,7 @@ static DeviceState *versal_create_ospi(Versal *s, linear_mr); /* OSPI irq */ - orgate = create_or_gate(s, OBJECT(dev), "irq-orgate", NUM_OSPI_IRQ_LINES, + orgate = create_or_gate(s, OBJECT(dev), "irq-orgate", 3, map->irq); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(orgate, 0)); @@ -1625,18 +1617,12 @@ static void versal_unimp(Versal *s) DeviceState *slcr; qemu_irq gpio_in; - versal_unimp_area(s, "psm", &s->mr_ps, - MM_PSM_START, MM_PSM_END - MM_PSM_START); - versal_unimp_area(s, "crf", &s->mr_ps, - MM_FPD_CRF, MM_FPD_CRF_SIZE); - versal_unimp_area(s, "apu", &s->mr_ps, - MM_FPD_FPD_APU, MM_FPD_FPD_APU_SIZE); - versal_unimp_area(s, "crp", &s->mr_ps, - MM_PMC_CRP, MM_PMC_CRP_SIZE); - versal_unimp_area(s, "iou-scntr", &s->mr_ps, - MM_IOU_SCNTR, MM_IOU_SCNTR_SIZE); - versal_unimp_area(s, "iou-scntr-seucre", &s->mr_ps, - MM_IOU_SCNTRS, MM_IOU_SCNTRS_SIZE); + versal_unimp_area(s, "psm", &s->mr_ps, 0xffc80000, 0x70000); + versal_unimp_area(s, "crf", &s->mr_ps, 0xfd1a0000, 0x140000); + versal_unimp_area(s, "apu", &s->mr_ps, 0xfd5c0000, 0x100); + versal_unimp_area(s, "crp", &s->mr_ps, 0xf1260000, 0x10000); + versal_unimp_area(s, "iou-scntr", &s->mr_ps, 0xff130000, 0x10000); + versal_unimp_area(s, "iou-scntr-seucre", &s->mr_ps, 0xff140000, 0x10000); qdev_init_gpio_in_named(DEVICE(s), versal_unimp_sd_emmc_sel, "sd-emmc-sel-dummy", 2); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index da0260b83de96..b6cc71f7209df 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -23,18 +23,6 @@ OBJECT_DECLARE_TYPE(Versal, VersalClass, XLNX_VERSAL_BASE) #define TYPE_XLNX_VERSAL "xlnx-versal" -#define XLNX_VERSAL_NR_ACPUS 2 -#define XLNX_VERSAL_NR_RCPUS 2 -#define XLNX_VERSAL_NR_UARTS 2 -#define XLNX_VERSAL_NR_GEMS 2 -#define XLNX_VERSAL_NR_ADMAS 8 -#define XLNX_VERSAL_NR_SDS 2 -#define XLNX_VERSAL_NR_XRAM 4 -#define XLNX_VERSAL_NR_IRQS 192 -#define XLNX_VERSAL_NR_CANFD 2 -#define XLNX_VERSAL_CANFD_REF_CLK (24 * 1000 * 1000) -#define XLNX_VERSAL_NR_CFRAME 15 - struct Versal { /*< private >*/ SysBusDevice parent_obj; @@ -84,196 +72,4 @@ int versal_get_num_cpu(VersalVersion version); int versal_get_num_can(VersalVersion version); int versal_get_num_sdhci(VersalVersion version); -/* Memory-map and IRQ definitions. Copied a subset from - * auto-generated files. */ - -#define VERSAL_CRL_IRQ 10 -#define VERSAL_UART0_IRQ_0 18 -#define VERSAL_UART1_IRQ_0 19 -#define VERSAL_CANFD0_IRQ_0 20 -#define VERSAL_CANFD1_IRQ_0 21 -#define VERSAL_USB0_IRQ_0 22 -#define VERSAL_GEM0_IRQ_0 56 -#define VERSAL_GEM0_WAKE_IRQ_0 57 -#define VERSAL_GEM1_IRQ_0 58 -#define VERSAL_GEM1_WAKE_IRQ_0 59 -#define VERSAL_ADMA_IRQ_0 60 -#define VERSAL_XRAM_IRQ_0 79 -#define VERSAL_CFU_IRQ_0 120 -#define VERSAL_PMC_APB_IRQ 121 -#define VERSAL_OSPI_IRQ 124 -#define VERSAL_SD0_IRQ_0 126 -#define VERSAL_EFUSE_IRQ 139 -#define VERSAL_TRNG_IRQ 141 -#define VERSAL_RTC_ALARM_IRQ 142 -#define VERSAL_RTC_SECONDS_IRQ 143 - -/* Architecturally reserved IRQs suitable for virtualization. */ -#define VERSAL_RSVD_IRQ_FIRST 111 -#define VERSAL_RSVD_IRQ_LAST 118 - -#define MM_TOP_RSVD 0xa0000000U -#define MM_TOP_RSVD_SIZE 0x4000000 -#define MM_GIC_APU_DIST_MAIN 0xf9000000U -#define MM_GIC_APU_DIST_MAIN_SIZE 0x10000 -#define MM_GIC_APU_REDIST_0 0xf9080000U -#define MM_GIC_APU_REDIST_0_SIZE 0x80000 - -#define MM_UART0 0xff000000U -#define MM_UART0_SIZE 0x10000 -#define MM_UART1 0xff010000U -#define MM_UART1_SIZE 0x10000 - -#define MM_CANFD0 0xff060000U -#define MM_CANFD0_SIZE 0x10000 -#define MM_CANFD1 0xff070000U -#define MM_CANFD1_SIZE 0x10000 - -#define MM_GEM0 0xff0c0000U -#define MM_GEM0_SIZE 0x10000 -#define MM_GEM1 0xff0d0000U -#define MM_GEM1_SIZE 0x10000 - -#define MM_ADMA_CH0 0xffa80000U -#define MM_ADMA_CH0_SIZE 0x10000 - -#define MM_OCM 0xfffc0000U -#define MM_OCM_SIZE 0x40000 - -#define MM_XRAM 0xfe800000 -#define MM_XRAMC 0xff8e0000 -#define MM_XRAMC_SIZE 0x10000 - -#define MM_USB2_CTRL_REGS 0xFF9D0000 -#define MM_USB2_CTRL_REGS_SIZE 0x10000 - -#define MM_USB_0 0xFE200000 -#define MM_USB_0_SIZE 0x10000 - -#define MM_TOP_DDR 0x0 -#define MM_TOP_DDR_SIZE 0x80000000U -#define MM_TOP_DDR_2 0x800000000ULL -#define MM_TOP_DDR_2_SIZE 0x800000000ULL -#define MM_TOP_DDR_3 0xc000000000ULL -#define MM_TOP_DDR_3_SIZE 0x4000000000ULL -#define MM_TOP_DDR_4 0x10000000000ULL -#define MM_TOP_DDR_4_SIZE 0xb780000000ULL - -#define MM_PSM_START 0xffc80000U -#define MM_PSM_END 0xffcf0000U - -#define MM_CRL 0xff5e0000U -#define MM_CRL_SIZE 0x300000 -#define MM_IOU_SCNTR 0xff130000U -#define MM_IOU_SCNTR_SIZE 0x10000 -#define MM_IOU_SCNTRS 0xff140000U -#define MM_IOU_SCNTRS_SIZE 0x10000 -#define MM_FPD_CRF 0xfd1a0000U -#define MM_FPD_CRF_SIZE 0x140000 -#define MM_FPD_FPD_APU 0xfd5c0000 -#define MM_FPD_FPD_APU_SIZE 0x100 - -#define MM_PMC_PMC_IOU_SLCR 0xf1060000 -#define MM_PMC_PMC_IOU_SLCR_SIZE 0x10000 - -#define MM_PMC_OSPI 0xf1010000 -#define MM_PMC_OSPI_SIZE 0x10000 - -#define MM_PMC_OSPI_DAC 0xc0000000 -#define MM_PMC_OSPI_DAC_SIZE 0x20000000 - -#define MM_PMC_OSPI_DMA_DST 0xf1011800 -#define MM_PMC_OSPI_DMA_SRC 0xf1011000 - -#define MM_PMC_SD0 0xf1040000U -#define MM_PMC_SD0_SIZE 0x10000 -#define MM_PMC_BBRAM_CTRL 0xf11f0000 -#define MM_PMC_BBRAM_CTRL_SIZE 0x00050 -#define MM_PMC_EFUSE_CTRL 0xf1240000 -#define MM_PMC_EFUSE_CTRL_SIZE 0x00104 -#define MM_PMC_EFUSE_CACHE 0xf1250000 -#define MM_PMC_EFUSE_CACHE_SIZE 0x00C00 - -#define MM_PMC_CFU_APB 0xf12b0000 -#define MM_PMC_CFU_APB_SIZE 0x10000 -#define MM_PMC_CFU_STREAM 0xf12c0000 -#define MM_PMC_CFU_STREAM_SIZE 0x1000 -#define MM_PMC_CFU_SFR 0xf12c1000 -#define MM_PMC_CFU_SFR_SIZE 0x1000 -#define MM_PMC_CFU_FDRO 0xf12c2000 -#define MM_PMC_CFU_FDRO_SIZE 0x1000 -#define MM_PMC_CFU_STREAM_2 0xf1f80000 -#define MM_PMC_CFU_STREAM_2_SIZE 0x40000 - -#define MM_PMC_CFRAME0_REG 0xf12d0000 -#define MM_PMC_CFRAME0_REG_SIZE 0x1000 -#define MM_PMC_CFRAME0_FDRI 0xf12d1000 -#define MM_PMC_CFRAME0_FDRI_SIZE 0x1000 -#define MM_PMC_CFRAME1_REG 0xf12d2000 -#define MM_PMC_CFRAME1_REG_SIZE 0x1000 -#define MM_PMC_CFRAME1_FDRI 0xf12d3000 -#define MM_PMC_CFRAME1_FDRI_SIZE 0x1000 -#define MM_PMC_CFRAME2_REG 0xf12d4000 -#define MM_PMC_CFRAME2_REG_SIZE 0x1000 -#define MM_PMC_CFRAME2_FDRI 0xf12d5000 -#define MM_PMC_CFRAME2_FDRI_SIZE 0x1000 -#define MM_PMC_CFRAME3_REG 0xf12d6000 -#define MM_PMC_CFRAME3_REG_SIZE 0x1000 -#define MM_PMC_CFRAME3_FDRI 0xf12d7000 -#define MM_PMC_CFRAME3_FDRI_SIZE 0x1000 -#define MM_PMC_CFRAME4_REG 0xf12d8000 -#define MM_PMC_CFRAME4_REG_SIZE 0x1000 -#define MM_PMC_CFRAME4_FDRI 0xf12d9000 -#define MM_PMC_CFRAME4_FDRI_SIZE 0x1000 -#define MM_PMC_CFRAME5_REG 0xf12da000 -#define MM_PMC_CFRAME5_REG_SIZE 0x1000 -#define MM_PMC_CFRAME5_FDRI 0xf12db000 -#define MM_PMC_CFRAME5_FDRI_SIZE 0x1000 -#define MM_PMC_CFRAME6_REG 0xf12dc000 -#define MM_PMC_CFRAME6_REG_SIZE 0x1000 -#define MM_PMC_CFRAME6_FDRI 0xf12dd000 -#define MM_PMC_CFRAME6_FDRI_SIZE 0x1000 -#define MM_PMC_CFRAME7_REG 0xf12de000 -#define MM_PMC_CFRAME7_REG_SIZE 0x1000 -#define MM_PMC_CFRAME7_FDRI 0xf12df000 -#define MM_PMC_CFRAME7_FDRI_SIZE 0x1000 -#define MM_PMC_CFRAME8_REG 0xf12e0000 -#define MM_PMC_CFRAME8_REG_SIZE 0x1000 -#define MM_PMC_CFRAME8_FDRI 0xf12e1000 -#define MM_PMC_CFRAME8_FDRI_SIZE 0x1000 -#define MM_PMC_CFRAME9_REG 0xf12e2000 -#define MM_PMC_CFRAME9_REG_SIZE 0x1000 -#define MM_PMC_CFRAME9_FDRI 0xf12e3000 -#define MM_PMC_CFRAME9_FDRI_SIZE 0x1000 -#define MM_PMC_CFRAME10_REG 0xf12e4000 -#define MM_PMC_CFRAME10_REG_SIZE 0x1000 -#define MM_PMC_CFRAME10_FDRI 0xf12e5000 -#define MM_PMC_CFRAME10_FDRI_SIZE 0x1000 -#define MM_PMC_CFRAME11_REG 0xf12e6000 -#define MM_PMC_CFRAME11_REG_SIZE 0x1000 -#define MM_PMC_CFRAME11_FDRI 0xf12e7000 -#define MM_PMC_CFRAME11_FDRI_SIZE 0x1000 -#define MM_PMC_CFRAME12_REG 0xf12e8000 -#define MM_PMC_CFRAME12_REG_SIZE 0x1000 -#define MM_PMC_CFRAME12_FDRI 0xf12e9000 -#define MM_PMC_CFRAME12_FDRI_SIZE 0x1000 -#define MM_PMC_CFRAME13_REG 0xf12ea000 -#define MM_PMC_CFRAME13_REG_SIZE 0x1000 -#define MM_PMC_CFRAME13_FDRI 0xf12eb000 -#define MM_PMC_CFRAME13_FDRI_SIZE 0x1000 -#define MM_PMC_CFRAME14_REG 0xf12ec000 -#define MM_PMC_CFRAME14_REG_SIZE 0x1000 -#define MM_PMC_CFRAME14_FDRI 0xf12ed000 -#define MM_PMC_CFRAME14_FDRI_SIZE 0x1000 -#define MM_PMC_CFRAME_BCAST_REG 0xf12ee000 -#define MM_PMC_CFRAME_BCAST_REG_SIZE 0x1000 -#define MM_PMC_CFRAME_BCAST_FDRI 0xf12ef000 -#define MM_PMC_CFRAME_BCAST_FDRI_SIZE 0x1000 - -#define MM_PMC_CRP 0xf1260000U -#define MM_PMC_CRP_SIZE 0x10000 -#define MM_PMC_RTC 0xf12a0000 -#define MM_PMC_RTC_SIZE 0x10000 -#define MM_PMC_TRNG 0xf1230000 -#define MM_PMC_TRNG_SIZE 0x10000 #endif From 00580a9d715138ec1560b91c596dedee07286fee Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:54 +0200 Subject: [PATCH 1369/1794] hw/misc/xlnx-versal-crl: add the versal2 version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the versal2 version of the CRL device. For the implemented part, it is similar to the versal version but drives reset line of more devices. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-37-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/misc/xlnx-versal-crl.c | 392 +++++++++++++++++++++++++++ include/hw/arm/xlnx-versal-version.h | 1 + include/hw/misc/xlnx-versal-crl.h | 329 ++++++++++++++++++++++ 3 files changed, 722 insertions(+) diff --git a/hw/misc/xlnx-versal-crl.c b/hw/misc/xlnx-versal-crl.c index 6225a92e0bdec..10e6af002bae3 100644 --- a/hw/misc/xlnx-versal-crl.c +++ b/hw/misc/xlnx-versal-crl.c @@ -83,6 +83,51 @@ static DeviceState **versal_decode_periph_rst(XlnxVersalCRLBase *s, case A_RST_USB0: return xvc->cfg.usb; + default: + /* invalid or unimplemented */ + g_assert_not_reached(); + } +} + +static DeviceState **versal2_decode_periph_rst(XlnxVersalCRLBase *s, + hwaddr addr, size_t *count) +{ + size_t idx; + XlnxVersal2CRL *xvc = XLNX_VERSAL2_CRL(s); + + *count = 1; + + switch (addr) { + case A_VERSAL2_RST_RPU_A ... A_VERSAL2_RST_RPU_E: + idx = (addr - A_VERSAL2_RST_RPU_A) / sizeof(uint32_t); + idx *= 2; /* two RPUs per RST_RPU_x registers */ + return xvc->cfg.rpu + idx; + + case A_VERSAL2_RST_ADMA: + /* A single register fans out to all DMA reset inputs */ + *count = ARRAY_SIZE(xvc->cfg.adma); + return xvc->cfg.adma; + + case A_VERSAL2_RST_SDMA: + *count = ARRAY_SIZE(xvc->cfg.sdma); + return xvc->cfg.sdma; + + case A_VERSAL2_RST_UART0 ... A_VERSAL2_RST_UART1: + idx = (addr - A_VERSAL2_RST_UART0) / sizeof(uint32_t); + return xvc->cfg.uart + idx; + + case A_VERSAL2_RST_GEM0 ... A_VERSAL2_RST_GEM1: + idx = (addr - A_VERSAL2_RST_GEM0) / sizeof(uint32_t); + return xvc->cfg.gem + idx; + + case A_VERSAL2_RST_USB0 ... A_VERSAL2_RST_USB1: + idx = (addr - A_VERSAL2_RST_USB0) / sizeof(uint32_t); + return xvc->cfg.usb + idx; + + case A_VERSAL2_RST_CAN0 ... A_VERSAL2_RST_CAN3: + idx = (addr - A_VERSAL2_RST_CAN0) / sizeof(uint32_t); + return xvc->cfg.can + idx; + default: /* invalid or unimplemented */ return NULL; @@ -307,6 +352,246 @@ static const RegisterAccessInfo crl_regs_info[] = { } }; +static const RegisterAccessInfo versal2_crl_regs_info[] = { + { .name = "ERR_CTRL", .addr = A_VERSAL2_ERR_CTRL, + .reset = 0x1, + },{ .name = "WPROT", .addr = A_VERSAL2_WPROT, + },{ .name = "RPLL_CTRL", .addr = A_VERSAL2_RPLL_CTRL, + .reset = 0x24809, + .rsvd = 0xf88c00f6, + },{ .name = "RPLL_CFG", .addr = A_VERSAL2_RPLL_CFG, + .reset = 0x7e5dcc6c, + .rsvd = 0x1801210, + },{ .name = "FLXPLL_CTRL", .addr = A_VERSAL2_FLXPLL_CTRL, + .reset = 0x24809, + .rsvd = 0xf88c00f6, + },{ .name = "FLXPLL_CFG", .addr = A_VERSAL2_FLXPLL_CFG, + .reset = 0x7e5dcc6c, + .rsvd = 0x1801210, + },{ .name = "PLL_STATUS", .addr = A_VERSAL2_PLL_STATUS, + .reset = 0xf, + .rsvd = 0xf0, + .ro = 0xf, + },{ .name = "RPLL_TO_XPD_CTRL", .addr = A_VERSAL2_RPLL_TO_XPD_CTRL, + .reset = 0x2000100, + .rsvd = 0xfdfc00ff, + },{ .name = "LPX_TOP_SWITCH_CTRL", .addr = A_VERSAL2_LPX_TOP_SWITCH_CTRL, + .reset = 0xe000300, + .rsvd = 0xf1fc00f8, + },{ .name = "LPX_LSBUS_CLK_CTRL", .addr = A_VERSAL2_LPX_LSBUS_CLK_CTRL, + .reset = 0x2000800, + .rsvd = 0xfdfc00f8, + },{ .name = "RPU_CLK_CTRL", .addr = A_VERSAL2_RPU_CLK_CTRL, + .reset = 0x3f00300, + .rsvd = 0xfc0c00f8, + },{ .name = "OCM_CLK_CTRL", .addr = A_VERSAL2_OCM_CLK_CTRL, + .reset = 0x1e00000, + .rsvd = 0xfe1fffff, + },{ .name = "IOU_SWITCH_CLK_CTRL", .addr = A_VERSAL2_IOU_SWITCH_CLK_CTRL, + .reset = 0x2000500, + .rsvd = 0xfdfc00f8, + },{ .name = "GEM0_REF_CTRL", .addr = A_VERSAL2_GEM0_REF_CTRL, + .reset = 0xe000a00, + .rsvd = 0xf1fc00f8, + },{ .name = "GEM1_REF_CTRL", .addr = A_VERSAL2_GEM1_REF_CTRL, + .reset = 0xe000a00, + .rsvd = 0xf1fc00f8, + },{ .name = "GEM_TSU_REF_CLK_CTRL", .addr = A_VERSAL2_GEM_TSU_REF_CLK_CTRL, + .reset = 0x300, + .rsvd = 0xfdfc00f8, + },{ .name = "USB0_BUS_REF_CLK_CTRL", + .addr = A_VERSAL2_USB0_BUS_REF_CLK_CTRL, + .reset = 0x2001900, + .rsvd = 0xfdfc00f8, + },{ .name = "USB1_BUS_REF_CLK_CTRL", + .addr = A_VERSAL2_USB1_BUS_REF_CLK_CTRL, + .reset = 0x2001900, + .rsvd = 0xfdfc00f8, + },{ .name = "UART0_REF_CLK_CTRL", .addr = A_VERSAL2_UART0_REF_CLK_CTRL, + .reset = 0xc00, + .rsvd = 0xfdfc00f8, + },{ .name = "UART1_REF_CLK_CTRL", .addr = A_VERSAL2_UART1_REF_CLK_CTRL, + .reset = 0xc00, + .rsvd = 0xfdfc00f8, + },{ .name = "SPI0_REF_CLK_CTRL", .addr = A_VERSAL2_SPI0_REF_CLK_CTRL, + .reset = 0x600, + .rsvd = 0xfdfc00f8, + },{ .name = "SPI1_REF_CLK_CTRL", .addr = A_VERSAL2_SPI1_REF_CLK_CTRL, + .reset = 0x600, + .rsvd = 0xfdfc00f8, + },{ .name = "CAN0_REF_2X_CTRL", .addr = A_VERSAL2_CAN0_REF_2X_CTRL, + .reset = 0xc00, + .rsvd = 0xfdfc00f8, + },{ .name = "CAN1_REF_2X_CTRL", .addr = A_VERSAL2_CAN1_REF_2X_CTRL, + .reset = 0xc00, + .rsvd = 0xfdfc00f8, + },{ .name = "CAN2_REF_2X_CTRL", .addr = A_VERSAL2_CAN2_REF_2X_CTRL, + .reset = 0xc00, + .rsvd = 0xfdfc00f8, + },{ .name = "CAN3_REF_2X_CTRL", .addr = A_VERSAL2_CAN3_REF_2X_CTRL, + .reset = 0xc00, + .rsvd = 0xfdfc00f8, + },{ .name = "I3C0_REF_CTRL", .addr = A_VERSAL2_I3C0_REF_CTRL, + .reset = 0x2000c00, + .rsvd = 0xfdfc00f8, + },{ .name = "I3C1_REF_CTRL", .addr = A_VERSAL2_I3C1_REF_CTRL, + .reset = 0x2000c00, + .rsvd = 0xfdfc00f8, + },{ .name = "I3C2_REF_CTRL", .addr = A_VERSAL2_I3C2_REF_CTRL, + .reset = 0x2000c00, + .rsvd = 0xfdfc00f8, + },{ .name = "I3C3_REF_CTRL", .addr = A_VERSAL2_I3C3_REF_CTRL, + .reset = 0x2000c00, + .rsvd = 0xfdfc00f8, + },{ .name = "I3C4_REF_CTRL", .addr = A_VERSAL2_I3C4_REF_CTRL, + .reset = 0x2000c00, + .rsvd = 0xfdfc00f8, + },{ .name = "I3C5_REF_CTRL", .addr = A_VERSAL2_I3C5_REF_CTRL, + .reset = 0x2000c00, + .rsvd = 0xfdfc00f8, + },{ .name = "I3C6_REF_CTRL", .addr = A_VERSAL2_I3C6_REF_CTRL, + .reset = 0x2000c00, + .rsvd = 0xfdfc00f8, + },{ .name = "I3C7_REF_CTRL", .addr = A_VERSAL2_I3C7_REF_CTRL, + .reset = 0x2000c00, + .rsvd = 0xfdfc00f8, + },{ .name = "DBG_LPX_CTRL", .addr = A_VERSAL2_DBG_LPX_CTRL, + .reset = 0x300, + .rsvd = 0xfdfc00f8, + },{ .name = "TIMESTAMP_REF_CTRL", .addr = A_VERSAL2_TIMESTAMP_REF_CTRL, + .reset = 0x2000c00, + .rsvd = 0xfdfc00f8, + },{ .name = "SAFETY_CHK", .addr = A_VERSAL2_SAFETY_CHK, + },{ .name = "ASU_CLK_CTRL", .addr = A_VERSAL2_ASU_CLK_CTRL, + .reset = 0x2000f04, + .rsvd = 0xfdfc00f8, + },{ .name = "DBG_TSTMP_CLK_CTRL", .addr = A_VERSAL2_DBG_TSTMP_CLK_CTRL, + .reset = 0x300, + .rsvd = 0xfdfc00f8, + },{ .name = "MMI_TOPSW_CLK_CTRL", .addr = A_VERSAL2_MMI_TOPSW_CLK_CTRL, + .reset = 0x2000300, + .rsvd = 0xfdfc00f8, + },{ .name = "WWDT_PLL_CLK_CTRL", .addr = A_VERSAL2_WWDT_PLL_CLK_CTRL, + .reset = 0xc00, + .rsvd = 0xfffc00f8, + },{ .name = "RCLK_CTRL", .addr = A_VERSAL2_RCLK_CTRL, + .rsvd = 0xc040, + },{ .name = "RST_RPU_A", .addr = A_VERSAL2_RST_RPU_A, + .reset = 0x10303, + .rsvd = 0xfffefcfc, + .pre_write = crl_rst_cpu_prew, + },{ .name = "RST_RPU_B", .addr = A_VERSAL2_RST_RPU_B, + .reset = 0x10303, + .rsvd = 0xfffefcfc, + .pre_write = crl_rst_cpu_prew, + },{ .name = "RST_RPU_C", .addr = A_VERSAL2_RST_RPU_C, + .reset = 0x10303, + .rsvd = 0xfffefcfc, + .pre_write = crl_rst_cpu_prew, + },{ .name = "RST_RPU_D", .addr = A_VERSAL2_RST_RPU_D, + .reset = 0x10303, + .rsvd = 0xfffefcfc, + .pre_write = crl_rst_cpu_prew, + },{ .name = "RST_RPU_E", .addr = A_VERSAL2_RST_RPU_E, + .reset = 0x10303, + .rsvd = 0xfffefcfc, + .pre_write = crl_rst_cpu_prew, + },{ .name = "RST_RPU_GD_0", .addr = A_VERSAL2_RST_RPU_GD_0, + .reset = 0x3, + },{ .name = "RST_RPU_GD_1", .addr = A_VERSAL2_RST_RPU_GD_1, + .reset = 0x3, + },{ .name = "RST_ASU_GD", .addr = A_VERSAL2_RST_ASU_GD, + .reset = 0x3, + },{ .name = "RST_ADMA", .addr = A_VERSAL2_RST_ADMA, + .reset = 0x1, + .pre_write = crl_rst_dev_prew, + },{ .name = "RST_SDMA", .addr = A_VERSAL2_RST_SDMA, + .pre_write = crl_rst_dev_prew, + .reset = 0x1, + },{ .name = "RST_GEM0", .addr = A_VERSAL2_RST_GEM0, + .reset = 0x1, + .pre_write = crl_rst_dev_prew, + },{ .name = "RST_GEM1", .addr = A_VERSAL2_RST_GEM1, + .reset = 0x1, + .pre_write = crl_rst_dev_prew, + },{ .name = "RST_USB0", .addr = A_VERSAL2_RST_USB0, + .reset = 0x1, + .pre_write = crl_rst_dev_prew, + },{ .name = "RST_USB1", .addr = A_VERSAL2_RST_USB1, + .reset = 0x1, + .pre_write = crl_rst_dev_prew, + },{ .name = "RST_UART0", .addr = A_VERSAL2_RST_UART0, + .reset = 0x1, + .pre_write = crl_rst_dev_prew, + },{ .name = "RST_UART1", .addr = A_VERSAL2_RST_UART1, + .reset = 0x1, + .pre_write = crl_rst_dev_prew, + },{ .name = "RST_SPI0", .addr = A_VERSAL2_RST_SPI0, + .reset = 0x1, + },{ .name = "RST_SPI1", .addr = A_VERSAL2_RST_SPI1, + .reset = 0x1, + },{ .name = "RST_CAN0", .addr = A_VERSAL2_RST_CAN0, + .reset = 0x1, + .pre_write = crl_rst_dev_prew, + },{ .name = "RST_CAN1", .addr = A_VERSAL2_RST_CAN1, + .reset = 0x1, + .pre_write = crl_rst_dev_prew, + },{ .name = "RST_CAN2", .addr = A_VERSAL2_RST_CAN2, + .reset = 0x1, + .pre_write = crl_rst_dev_prew, + },{ .name = "RST_CAN3", .addr = A_VERSAL2_RST_CAN3, + .reset = 0x1, + .pre_write = crl_rst_dev_prew, + },{ .name = "RST_I3C0", .addr = A_VERSAL2_RST_I3C0, + .reset = 0x1, + },{ .name = "RST_I3C1", .addr = A_VERSAL2_RST_I3C1, + .reset = 0x1, + },{ .name = "RST_I3C2", .addr = A_VERSAL2_RST_I3C2, + .reset = 0x1, + },{ .name = "RST_I3C3", .addr = A_VERSAL2_RST_I3C3, + .reset = 0x1, + },{ .name = "RST_I3C4", .addr = A_VERSAL2_RST_I3C4, + .reset = 0x1, + },{ .name = "RST_I3C5", .addr = A_VERSAL2_RST_I3C5, + .reset = 0x1, + },{ .name = "RST_I3C6", .addr = A_VERSAL2_RST_I3C6, + .reset = 0x1, + },{ .name = "RST_I3C7", .addr = A_VERSAL2_RST_I3C7, + .reset = 0x1, + },{ .name = "RST_DBG_LPX", .addr = A_VERSAL2_RST_DBG_LPX, + .reset = 0x3, + .rsvd = 0xfc, + },{ .name = "RST_GPIO", .addr = A_VERSAL2_RST_GPIO, + .reset = 0x1, + },{ .name = "RST_TTC", .addr = A_VERSAL2_RST_TTC, + .reset = 0xff, + },{ .name = "RST_TIMESTAMP", .addr = A_VERSAL2_RST_TIMESTAMP, + .reset = 0x1, + },{ .name = "RST_SWDT0", .addr = A_VERSAL2_RST_SWDT0, + .reset = 0x1, + },{ .name = "RST_SWDT1", .addr = A_VERSAL2_RST_SWDT1, + .reset = 0x1, + },{ .name = "RST_SWDT2", .addr = A_VERSAL2_RST_SWDT2, + .reset = 0x1, + },{ .name = "RST_SWDT3", .addr = A_VERSAL2_RST_SWDT3, + .reset = 0x1, + },{ .name = "RST_SWDT4", .addr = A_VERSAL2_RST_SWDT4, + .reset = 0x1, + },{ .name = "RST_IPI", .addr = A_VERSAL2_RST_IPI, + },{ .name = "RST_SYSMON", .addr = A_VERSAL2_RST_SYSMON, + },{ .name = "ASU_MB_RST_MODE", .addr = A_VERSAL2_ASU_MB_RST_MODE, + .reset = 0x1, + .rsvd = 0xf8, + },{ .name = "FPX_TOPSW_MUX_CTRL", .addr = A_VERSAL2_FPX_TOPSW_MUX_CTRL, + .reset = 0x1, + },{ .name = "RST_FPX", .addr = A_VERSAL2_RST_FPX, + .reset = 0x3, + },{ .name = "RST_MMI", .addr = A_VERSAL2_RST_MMI, + .reset = 0x1, + },{ .name = "RST_OCM", .addr = A_VERSAL2_RST_OCM, + } +}; + static void versal_crl_reset_enter(Object *obj, ResetType type) { XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj); @@ -317,6 +602,16 @@ static void versal_crl_reset_enter(Object *obj, ResetType type) } } +static void versal2_crl_reset_enter(Object *obj, ResetType type) +{ + XlnxVersal2CRL *s = XLNX_VERSAL2_CRL(obj); + size_t i; + + for (i = 0; i < VERSAL2_CRL_R_MAX; ++i) { + register_reset(&s->regs_info[i]); + } +} + static void versal_crl_reset_hold(Object *obj, ResetType type) { XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj); @@ -388,6 +683,73 @@ static void versal_crl_init(Object *obj) } } +static void versal2_crl_init(Object *obj) +{ + XlnxVersal2CRL *s = XLNX_VERSAL2_CRL(obj); + XlnxVersalCRLBase *xvcb = XLNX_VERSAL_CRL_BASE(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + size_t i; + + xvcb->reg_array = register_init_block32(DEVICE(obj), versal2_crl_regs_info, + ARRAY_SIZE(versal2_crl_regs_info), + s->regs_info, s->regs, + &crl_ops, + XLNX_VERSAL_CRL_ERR_DEBUG, + VERSAL2_CRL_R_MAX * 4); + xvcb->regs = s->regs; + + sysbus_init_mmio(sbd, &xvcb->reg_array->mem); + + for (i = 0; i < ARRAY_SIZE(s->cfg.rpu); ++i) { + object_property_add_link(obj, "rpu[*]", TYPE_ARM_CPU, + (Object **)&s->cfg.rpu[i], + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG); + } + + for (i = 0; i < ARRAY_SIZE(s->cfg.adma); ++i) { + object_property_add_link(obj, "adma[*]", TYPE_DEVICE, + (Object **)&s->cfg.adma[i], + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG); + } + + for (i = 0; i < ARRAY_SIZE(s->cfg.sdma); ++i) { + object_property_add_link(obj, "sdma[*]", TYPE_DEVICE, + (Object **)&s->cfg.sdma[i], + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG); + } + + for (i = 0; i < ARRAY_SIZE(s->cfg.uart); ++i) { + object_property_add_link(obj, "uart[*]", TYPE_DEVICE, + (Object **)&s->cfg.uart[i], + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG); + } + + for (i = 0; i < ARRAY_SIZE(s->cfg.gem); ++i) { + object_property_add_link(obj, "gem[*]", TYPE_DEVICE, + (Object **)&s->cfg.gem[i], + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG); + } + + for (i = 0; i < ARRAY_SIZE(s->cfg.usb); ++i) { + object_property_add_link(obj, "usb[*]", TYPE_DEVICE, + (Object **)&s->cfg.usb[i], + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG); + } + + for (i = 0; i < ARRAY_SIZE(s->cfg.can); ++i) { + object_property_add_link(obj, "can[*]", TYPE_DEVICE, + (Object **)&s->cfg.can[i], + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG); + } +} + static void crl_finalize(Object *obj) { XlnxVersalCRLBase *s = XLNX_VERSAL_CRL_BASE(obj); @@ -404,6 +766,16 @@ static const VMStateDescription vmstate_versal_crl = { } }; +static const VMStateDescription vmstate_versal2_crl = { + .name = TYPE_XLNX_VERSAL2_CRL, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, XlnxVersal2CRL, VERSAL2_CRL_R_MAX), + VMSTATE_END_OF_LIST(), + } +}; + static void versal_crl_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -416,6 +788,17 @@ static void versal_crl_class_init(ObjectClass *klass, const void *data) xvcc->decode_periph_rst = versal_decode_periph_rst; } +static void versal2_crl_class_init(ObjectClass *klass, const void *data) +{ + XlnxVersalCRLBaseClass *xvcc = XLNX_VERSAL_CRL_BASE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->vmsd = &vmstate_versal2_crl; + rc->phases.enter = versal2_crl_reset_enter; + xvcc->decode_periph_rst = versal2_decode_periph_rst; +} + static const TypeInfo crl_base_info = { .name = TYPE_XLNX_VERSAL_CRL_BASE, .parent = TYPE_SYS_BUS_DEVICE, @@ -433,10 +816,19 @@ static const TypeInfo versal_crl_info = { .class_init = versal_crl_class_init, }; +static const TypeInfo versal2_crl_info = { + .name = TYPE_XLNX_VERSAL2_CRL, + .parent = TYPE_XLNX_VERSAL_CRL_BASE, + .instance_size = sizeof(XlnxVersal2CRL), + .instance_init = versal2_crl_init, + .class_init = versal2_crl_class_init, +}; + static void crl_register_types(void) { type_register_static(&crl_base_info); type_register_static(&versal_crl_info); + type_register_static(&versal2_crl_info); } type_init(crl_register_types) diff --git a/include/hw/arm/xlnx-versal-version.h b/include/hw/arm/xlnx-versal-version.h index c4307d1304a77..5b6b6e57a573a 100644 --- a/include/hw/arm/xlnx-versal-version.h +++ b/include/hw/arm/xlnx-versal-version.h @@ -10,6 +10,7 @@ typedef enum VersalVersion { VERSAL_VER_VERSAL, + VERSAL_VER_VERSAL2, } VersalVersion; #endif diff --git a/include/hw/misc/xlnx-versal-crl.h b/include/hw/misc/xlnx-versal-crl.h index 7e50a95ad3caa..f6b8694ebeae2 100644 --- a/include/hw/misc/xlnx-versal-crl.h +++ b/include/hw/misc/xlnx-versal-crl.h @@ -17,10 +17,12 @@ #define TYPE_XLNX_VERSAL_CRL_BASE "xlnx-versal-crl-base" #define TYPE_XLNX_VERSAL_CRL "xlnx-versal-crl" +#define TYPE_XLNX_VERSAL2_CRL "xlnx-versal2-crl" OBJECT_DECLARE_TYPE(XlnxVersalCRLBase, XlnxVersalCRLBaseClass, XLNX_VERSAL_CRL_BASE) OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalCRL, XLNX_VERSAL_CRL) +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersal2CRL, XLNX_VERSAL2_CRL) REG32(ERR_CTRL, 0x0) FIELD(ERR_CTRL, SLVERR_ENABLE, 0, 1) @@ -220,6 +222,314 @@ REG32(PSM_RST_MODE, 0x370) #define CRL_R_MAX (R_PSM_RST_MODE + 1) +REG32(VERSAL2_ERR_CTRL, 0x0) +REG32(VERSAL2_WPROT, 0x1c) + FIELD(VERSAL2_WPROT, ACTIVE, 0, 1) +REG32(VERSAL2_RPLL_CTRL, 0x40) + FIELD(VERSAL2_RPLL_CTRL, POST_SRC, 24, 3) + FIELD(VERSAL2_RPLL_CTRL, PRE_SRC, 20, 3) + FIELD(VERSAL2_RPLL_CTRL, CLKOUTDIV, 16, 2) + FIELD(VERSAL2_RPLL_CTRL, FBDIV, 8, 8) + FIELD(VERSAL2_RPLL_CTRL, BYPASS, 3, 1) + FIELD(VERSAL2_RPLL_CTRL, RESET, 0, 1) +REG32(VERSAL2_RPLL_CFG, 0x44) + FIELD(VERSAL2_RPLL_CFG, LOCK_DLY, 25, 7) + FIELD(VERSAL2_RPLL_CFG, LOCK_CNT, 13, 10) + FIELD(VERSAL2_RPLL_CFG, LFHF, 10, 2) + FIELD(VERSAL2_RPLL_CFG, CP, 5, 4) + FIELD(VERSAL2_RPLL_CFG, RES, 0, 4) +REG32(VERSAL2_FLXPLL_CTRL, 0x50) + FIELD(VERSAL2_FLXPLL_CTRL, POST_SRC, 24, 3) + FIELD(VERSAL2_FLXPLL_CTRL, PRE_SRC, 20, 3) + FIELD(VERSAL2_FLXPLL_CTRL, CLKOUTDIV, 16, 2) + FIELD(VERSAL2_FLXPLL_CTRL, FBDIV, 8, 8) + FIELD(VERSAL2_FLXPLL_CTRL, BYPASS, 3, 1) + FIELD(VERSAL2_FLXPLL_CTRL, RESET, 0, 1) +REG32(VERSAL2_FLXPLL_CFG, 0x54) + FIELD(VERSAL2_FLXPLL_CFG, LOCK_DLY, 25, 7) + FIELD(VERSAL2_FLXPLL_CFG, LOCK_CNT, 13, 10) + FIELD(VERSAL2_FLXPLL_CFG, LFHF, 10, 2) + FIELD(VERSAL2_FLXPLL_CFG, CP, 5, 4) + FIELD(VERSAL2_FLXPLL_CFG, RES, 0, 4) +REG32(VERSAL2_PLL_STATUS, 0x60) + FIELD(VERSAL2_PLL_STATUS, FLXPLL_STABLE, 3, 1) + FIELD(VERSAL2_PLL_STATUS, RPLL_STABLE, 2, 1) + FIELD(VERSAL2_PLL_STATUS, FLXPLL_LOCK, 1, 1) + FIELD(VERSAL2_PLL_STATUS, RPLL_LOCK, 0, 1) +REG32(VERSAL2_RPLL_TO_XPD_CTRL, 0x100) + FIELD(VERSAL2_RPLL_TO_XPD_CTRL, DIVISOR0, 8, 10) +REG32(VERSAL2_LPX_TOP_SWITCH_CTRL, 0x104) + FIELD(VERSAL2_LPX_TOP_SWITCH_CTRL, CLKACT_ADMA, 26, 1) + FIELD(VERSAL2_LPX_TOP_SWITCH_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_LPX_TOP_SWITCH_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_LPX_TOP_SWITCH_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_LPX_LSBUS_CLK_CTRL, 0x108) + FIELD(VERSAL2_LPX_LSBUS_CLK_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_LPX_LSBUS_CLK_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_LPX_LSBUS_CLK_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_RPU_CLK_CTRL, 0x10c) + FIELD(VERSAL2_RPU_CLK_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_RPU_CLK_CTRL, CLKACT_CLUSTERE, 24, 1) + FIELD(VERSAL2_RPU_CLK_CTRL, CLKACT_CLUSTERD, 23, 1) + FIELD(VERSAL2_RPU_CLK_CTRL, CLKACT_CLUSTERC, 22, 1) + FIELD(VERSAL2_RPU_CLK_CTRL, CLKACT_CLUSTERB, 21, 1) + FIELD(VERSAL2_RPU_CLK_CTRL, CLKACT_CLUSTERA, 20, 1) + FIELD(VERSAL2_RPU_CLK_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_RPU_CLK_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_OCM_CLK_CTRL, 0x120) + FIELD(VERSAL2_OCM_CLK_CTRL, CLKACT_OCM3, 24, 1) + FIELD(VERSAL2_OCM_CLK_CTRL, CLKACT_OCM2, 23, 1) + FIELD(VERSAL2_OCM_CLK_CTRL, CLKACT_OCM1, 22, 1) + FIELD(VERSAL2_OCM_CLK_CTRL, CLKACT_OCM0, 21, 1) +REG32(VERSAL2_IOU_SWITCH_CLK_CTRL, 0x124) + FIELD(VERSAL2_IOU_SWITCH_CLK_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_IOU_SWITCH_CLK_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_IOU_SWITCH_CLK_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_GEM0_REF_CTRL, 0x128) + FIELD(VERSAL2_GEM0_REF_CTRL, CLKACT_RX, 27, 1) + FIELD(VERSAL2_GEM0_REF_CTRL, CLKACT_TX, 26, 1) + FIELD(VERSAL2_GEM0_REF_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_GEM0_REF_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_GEM0_REF_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_GEM1_REF_CTRL, 0x12c) + FIELD(VERSAL2_GEM1_REF_CTRL, CLKACT_RX, 27, 1) + FIELD(VERSAL2_GEM1_REF_CTRL, CLKACT_TX, 26, 1) + FIELD(VERSAL2_GEM1_REF_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_GEM1_REF_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_GEM1_REF_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_GEM_TSU_REF_CLK_CTRL, 0x130) + FIELD(VERSAL2_GEM_TSU_REF_CLK_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_GEM_TSU_REF_CLK_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_GEM_TSU_REF_CLK_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_USB0_BUS_REF_CLK_CTRL, 0x134) + FIELD(VERSAL2_USB0_BUS_REF_CLK_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_USB0_BUS_REF_CLK_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_USB0_BUS_REF_CLK_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_USB1_BUS_REF_CLK_CTRL, 0x138) + FIELD(VERSAL2_USB1_BUS_REF_CLK_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_USB1_BUS_REF_CLK_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_USB1_BUS_REF_CLK_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_UART0_REF_CLK_CTRL, 0x13c) + FIELD(VERSAL2_UART0_REF_CLK_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_UART0_REF_CLK_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_UART0_REF_CLK_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_UART1_REF_CLK_CTRL, 0x140) + FIELD(VERSAL2_UART1_REF_CLK_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_UART1_REF_CLK_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_UART1_REF_CLK_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_SPI0_REF_CLK_CTRL, 0x144) + FIELD(VERSAL2_SPI0_REF_CLK_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_SPI0_REF_CLK_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_SPI0_REF_CLK_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_SPI1_REF_CLK_CTRL, 0x148) + FIELD(VERSAL2_SPI1_REF_CLK_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_SPI1_REF_CLK_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_SPI1_REF_CLK_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_CAN0_REF_2X_CTRL, 0x14c) + FIELD(VERSAL2_CAN0_REF_2X_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_CAN0_REF_2X_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_CAN0_REF_2X_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_CAN1_REF_2X_CTRL, 0x150) + FIELD(VERSAL2_CAN1_REF_2X_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_CAN1_REF_2X_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_CAN1_REF_2X_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_CAN2_REF_2X_CTRL, 0x154) + FIELD(VERSAL2_CAN2_REF_2X_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_CAN2_REF_2X_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_CAN2_REF_2X_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_CAN3_REF_2X_CTRL, 0x158) + FIELD(VERSAL2_CAN3_REF_2X_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_CAN3_REF_2X_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_CAN3_REF_2X_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_I3C0_REF_CTRL, 0x15c) + FIELD(VERSAL2_I3C0_REF_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_I3C0_REF_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_I3C0_REF_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_I3C1_REF_CTRL, 0x160) + FIELD(VERSAL2_I3C1_REF_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_I3C1_REF_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_I3C1_REF_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_I3C2_REF_CTRL, 0x164) + FIELD(VERSAL2_I3C2_REF_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_I3C2_REF_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_I3C2_REF_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_I3C3_REF_CTRL, 0x168) + FIELD(VERSAL2_I3C3_REF_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_I3C3_REF_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_I3C3_REF_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_I3C4_REF_CTRL, 0x16c) + FIELD(VERSAL2_I3C4_REF_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_I3C4_REF_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_I3C4_REF_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_I3C5_REF_CTRL, 0x170) + FIELD(VERSAL2_I3C5_REF_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_I3C5_REF_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_I3C5_REF_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_I3C6_REF_CTRL, 0x174) + FIELD(VERSAL2_I3C6_REF_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_I3C6_REF_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_I3C6_REF_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_I3C7_REF_CTRL, 0x178) + FIELD(VERSAL2_I3C7_REF_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_I3C7_REF_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_I3C7_REF_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_DBG_LPX_CTRL, 0x17c) + FIELD(VERSAL2_DBG_LPX_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_DBG_LPX_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_DBG_LPX_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_TIMESTAMP_REF_CTRL, 0x180) + FIELD(VERSAL2_TIMESTAMP_REF_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_TIMESTAMP_REF_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_TIMESTAMP_REF_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_SAFETY_CHK, 0x184) +REG32(VERSAL2_ASU_CLK_CTRL, 0x188) + FIELD(VERSAL2_ASU_CLK_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_ASU_CLK_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_DBG_TSTMP_CLK_CTRL, 0x18c) + FIELD(VERSAL2_DBG_TSTMP_CLK_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_DBG_TSTMP_CLK_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_DBG_TSTMP_CLK_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_MMI_TOPSW_CLK_CTRL, 0x190) + FIELD(VERSAL2_MMI_TOPSW_CLK_CTRL, CLKACT, 25, 1) + FIELD(VERSAL2_MMI_TOPSW_CLK_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_MMI_TOPSW_CLK_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_WWDT_PLL_CLK_CTRL, 0x194) + FIELD(VERSAL2_WWDT_PLL_CLK_CTRL, DIVISOR0, 8, 10) + FIELD(VERSAL2_WWDT_PLL_CLK_CTRL, SRCSEL, 0, 3) +REG32(VERSAL2_RCLK_CTRL, 0x1a0) + FIELD(VERSAL2_RCLK_CTRL, CLKACT, 8, 6) + FIELD(VERSAL2_RCLK_CTRL, SELECT, 0, 6) +REG32(VERSAL2_RST_RPU_A, 0x310) + FIELD(VERSAL2_RST_RPU_A, TOPRESET, 16, 1) + FIELD(VERSAL2_RST_RPU_A, CORE1_POR, 9, 1) + FIELD(VERSAL2_RST_RPU_A, CORE0_POR, 8, 1) + FIELD(VERSAL2_RST_RPU_A, CORE1_RESET, 1, 1) + FIELD(VERSAL2_RST_RPU_A, CORE0_RESET, 0, 1) +REG32(VERSAL2_RST_RPU_B, 0x314) + FIELD(VERSAL2_RST_RPU_B, TOPRESET, 16, 1) + FIELD(VERSAL2_RST_RPU_B, CORE1_POR, 9, 1) + FIELD(VERSAL2_RST_RPU_B, CORE0_POR, 8, 1) + FIELD(VERSAL2_RST_RPU_B, CORE1_RESET, 1, 1) + FIELD(VERSAL2_RST_RPU_B, CORE0_RESET, 0, 1) +REG32(VERSAL2_RST_RPU_C, 0x318) + FIELD(VERSAL2_RST_RPU_C, TOPRESET, 16, 1) + FIELD(VERSAL2_RST_RPU_C, CORE1_POR, 9, 1) + FIELD(VERSAL2_RST_RPU_C, CORE0_POR, 8, 1) + FIELD(VERSAL2_RST_RPU_C, CORE1_RESET, 1, 1) + FIELD(VERSAL2_RST_RPU_C, CORE0_RESET, 0, 1) +REG32(VERSAL2_RST_RPU_D, 0x31c) + FIELD(VERSAL2_RST_RPU_D, TOPRESET, 16, 1) + FIELD(VERSAL2_RST_RPU_D, CORE1_POR, 9, 1) + FIELD(VERSAL2_RST_RPU_D, CORE0_POR, 8, 1) + FIELD(VERSAL2_RST_RPU_D, CORE1_RESET, 1, 1) + FIELD(VERSAL2_RST_RPU_D, CORE0_RESET, 0, 1) +REG32(VERSAL2_RST_RPU_E, 0x320) + FIELD(VERSAL2_RST_RPU_E, TOPRESET, 16, 1) + FIELD(VERSAL2_RST_RPU_E, CORE1_POR, 9, 1) + FIELD(VERSAL2_RST_RPU_E, CORE0_POR, 8, 1) + FIELD(VERSAL2_RST_RPU_E, CORE1_RESET, 1, 1) + FIELD(VERSAL2_RST_RPU_E, CORE0_RESET, 0, 1) +REG32(VERSAL2_RST_RPU_GD_0, 0x324) + FIELD(VERSAL2_RST_RPU_GD_0, RESET, 1, 1) + FIELD(VERSAL2_RST_RPU_GD_0, TOP_RESET, 0, 1) +REG32(VERSAL2_RST_RPU_GD_1, 0x328) + FIELD(VERSAL2_RST_RPU_GD_1, RESET, 1, 1) + FIELD(VERSAL2_RST_RPU_GD_1, TOP_RESET, 0, 1) +REG32(VERSAL2_RST_ASU_GD, 0x32c) + FIELD(VERSAL2_RST_ASU_GD, RESET, 1, 1) + FIELD(VERSAL2_RST_ASU_GD, TOP_RESET, 0, 1) +REG32(VERSAL2_RST_ADMA, 0x334) + FIELD(VERSAL2_RST_ADMA, RESET, 0, 1) +REG32(VERSAL2_RST_SDMA, 0x338) + FIELD(VERSAL2_RST_SDMA, RESET, 0, 1) +REG32(VERSAL2_RST_GEM0, 0x33c) + FIELD(VERSAL2_RST_GEM0, RESET, 0, 1) +REG32(VERSAL2_RST_GEM1, 0x340) + FIELD(VERSAL2_RST_GEM1, RESET, 0, 1) +REG32(VERSAL2_RST_USB0, 0x348) + FIELD(VERSAL2_RST_USB0, RESET, 0, 1) +REG32(VERSAL2_RST_USB1, 0x34c) + FIELD(VERSAL2_RST_USB1, RESET, 0, 1) +REG32(VERSAL2_RST_UART0, 0x350) + FIELD(VERSAL2_RST_UART0, RESET, 0, 1) +REG32(VERSAL2_RST_UART1, 0x354) + FIELD(VERSAL2_RST_UART1, RESET, 0, 1) +REG32(VERSAL2_RST_SPI0, 0x358) + FIELD(VERSAL2_RST_SPI0, RESET, 0, 1) +REG32(VERSAL2_RST_SPI1, 0x35c) + FIELD(VERSAL2_RST_SPI1, RESET, 0, 1) +REG32(VERSAL2_RST_CAN0, 0x360) + FIELD(VERSAL2_RST_CAN0, RESET, 0, 1) +REG32(VERSAL2_RST_CAN1, 0x364) + FIELD(VERSAL2_RST_CAN1, RESET, 0, 1) +REG32(VERSAL2_RST_CAN2, 0x368) + FIELD(VERSAL2_RST_CAN2, RESET, 0, 1) +REG32(VERSAL2_RST_CAN3, 0x36c) + FIELD(VERSAL2_RST_CAN3, RESET, 0, 1) +REG32(VERSAL2_RST_I3C0, 0x374) + FIELD(VERSAL2_RST_I3C0, RESET, 0, 1) +REG32(VERSAL2_RST_I3C1, 0x378) + FIELD(VERSAL2_RST_I3C1, RESET, 0, 1) +REG32(VERSAL2_RST_I3C2, 0x37c) + FIELD(VERSAL2_RST_I3C2, RESET, 0, 1) +REG32(VERSAL2_RST_I3C3, 0x380) + FIELD(VERSAL2_RST_I3C3, RESET, 0, 1) +REG32(VERSAL2_RST_I3C4, 0x384) + FIELD(VERSAL2_RST_I3C4, RESET, 0, 1) +REG32(VERSAL2_RST_I3C5, 0x388) + FIELD(VERSAL2_RST_I3C5, RESET, 0, 1) +REG32(VERSAL2_RST_I3C6, 0x38c) + FIELD(VERSAL2_RST_I3C6, RESET, 0, 1) +REG32(VERSAL2_RST_I3C7, 0x390) + FIELD(VERSAL2_RST_I3C7, RESET, 0, 1) +REG32(VERSAL2_RST_DBG_LPX, 0x398) + FIELD(VERSAL2_RST_DBG_LPX, RESET_HSDP, 1, 1) + FIELD(VERSAL2_RST_DBG_LPX, RESET, 0, 1) +REG32(VERSAL2_RST_GPIO, 0x39c) + FIELD(VERSAL2_RST_GPIO, RESET, 0, 1) +REG32(VERSAL2_RST_TTC, 0x3a0) + FIELD(VERSAL2_RST_TTC, TTC7_RESET, 7, 1) + FIELD(VERSAL2_RST_TTC, TTC6_RESET, 6, 1) + FIELD(VERSAL2_RST_TTC, TTC5_RESET, 5, 1) + FIELD(VERSAL2_RST_TTC, TTC4_RESET, 4, 1) + FIELD(VERSAL2_RST_TTC, TTC3_RESET, 3, 1) + FIELD(VERSAL2_RST_TTC, TTC2_RESET, 2, 1) + FIELD(VERSAL2_RST_TTC, TTC1_RESET, 1, 1) + FIELD(VERSAL2_RST_TTC, TTC0_RESET, 0, 1) +REG32(VERSAL2_RST_TIMESTAMP, 0x3a4) + FIELD(VERSAL2_RST_TIMESTAMP, RESET, 0, 1) +REG32(VERSAL2_RST_SWDT0, 0x3a8) + FIELD(VERSAL2_RST_SWDT0, RESET, 0, 1) +REG32(VERSAL2_RST_SWDT1, 0x3ac) + FIELD(VERSAL2_RST_SWDT1, RESET, 0, 1) +REG32(VERSAL2_RST_SWDT2, 0x3b0) + FIELD(VERSAL2_RST_SWDT2, RESET, 0, 1) +REG32(VERSAL2_RST_SWDT3, 0x3b4) + FIELD(VERSAL2_RST_SWDT3, RESET, 0, 1) +REG32(VERSAL2_RST_SWDT4, 0x3b8) + FIELD(VERSAL2_RST_SWDT4, RESET, 0, 1) +REG32(VERSAL2_RST_IPI, 0x3bc) + FIELD(VERSAL2_RST_IPI, RESET, 0, 1) +REG32(VERSAL2_RST_SYSMON, 0x3c0) + FIELD(VERSAL2_RST_SYSMON, CFG_RST, 0, 1) +REG32(VERSAL2_ASU_MB_RST_MODE, 0x3c4) + FIELD(VERSAL2_ASU_MB_RST_MODE, WAKEUP, 2, 1) + FIELD(VERSAL2_ASU_MB_RST_MODE, RST_MODE, 0, 2) +REG32(VERSAL2_FPX_TOPSW_MUX_CTRL, 0x3c8) + FIELD(VERSAL2_FPX_TOPSW_MUX_CTRL, SELECT, 0, 1) +REG32(VERSAL2_RST_FPX, 0x3d0) + FIELD(VERSAL2_RST_FPX, SRST, 1, 1) + FIELD(VERSAL2_RST_FPX, POR, 0, 1) +REG32(VERSAL2_RST_MMI, 0x3d4) + FIELD(VERSAL2_RST_MMI, POR, 0, 1) +REG32(VERSAL2_RST_OCM, 0x3d8) + FIELD(VERSAL2_RST_OCM, RESET_OCM3, 3, 1) + FIELD(VERSAL2_RST_OCM, RESET_OCM2, 2, 1) + FIELD(VERSAL2_RST_OCM, RESET_OCM1, 1, 1) + FIELD(VERSAL2_RST_OCM, RESET_OCM0, 0, 1) + +#define VERSAL2_CRL_R_MAX (R_VERSAL2_RST_OCM + 1) + struct XlnxVersalCRLBase { SysBusDevice parent_obj; @@ -249,11 +559,30 @@ struct XlnxVersalCRL { RegisterInfo regs_info[CRL_R_MAX]; }; +struct XlnxVersal2CRL { + XlnxVersalCRLBase parent_obj; + + struct { + DeviceState *rpu[10]; + DeviceState *adma[8]; + DeviceState *sdma[8]; + DeviceState *uart[2]; + DeviceState *gem[2]; + DeviceState *usb[2]; + DeviceState *can[4]; + } cfg; + + RegisterInfo regs_info[VERSAL2_CRL_R_MAX]; + uint32_t regs[VERSAL2_CRL_R_MAX]; +}; + static inline const char *xlnx_versal_crl_class_name(VersalVersion ver) { switch (ver) { case VERSAL_VER_VERSAL: return TYPE_XLNX_VERSAL_CRL; + case VERSAL_VER_VERSAL2: + return TYPE_XLNX_VERSAL2_CRL; default: g_assert_not_reached(); } From 268f7a3d24a73b94bfab1dd86c6b41f7afd196fc Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:55 +0200 Subject: [PATCH 1370/1794] hw/arm/xlnx-versal: add a per_cluster_gic switch to VersalCpuClusterMap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the per_cluster_gic switch to the VersalCpuClusterMap structure. When set, this indicates that a GIC instance should by created per-cluster instead of globally for the whole RPU or APU. This is in preparation for versal2. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-38-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 49b5b244403f8..3d960ed2636f4 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -88,6 +88,11 @@ enum StartPoweredOffMode { typedef struct VersalCpuClusterMap { VersalGicMap gic; + /* + * true: one GIC per cluster. + * false: one GIC for all CPUs + */ + bool per_cluster_gic; const char *name; const char *cpu_model; @@ -825,12 +830,18 @@ static void versal_create_cpu_cluster(Versal *s, const VersalCpuClusterMap *map) cpus[i * map->num_core + j] = cpu; } + if (map->per_cluster_gic) { + versal_create_and_connect_gic(s, map, mr, &cpus[i * map->num_core], + map->num_core); + } } qdev_realize_and_unref(cluster, NULL, &error_fatal); - versal_create_and_connect_gic(s, map, mr, cpus, - map->num_cluster * map->num_core); + if (!map->per_cluster_gic) { + versal_create_and_connect_gic(s, map, mr, cpus, + map->num_cluster * map->num_core); + } has_gtimer = arm_feature(&ARM_CPU(cpus[0])->env, ARM_FEATURE_GENERIC_TIMER); if (map->dtb_expose && has_gtimer) { From 16aa53c971c4a2fef5608f8aac372dd03305f0d2 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:56 +0200 Subject: [PATCH 1371/1794] hw/arm/xlnx-versal: add the target field in IRQ descriptor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the target field in the IRQ descriptor. This allows to target an IRQ to another IRQ controller than the GIC(s). Other supported targets are the PMC PPU1 CPU interrupt controller and the EAM (Error management) device. Those two devices are currently not implemented so IRQs targeting those will be left unconnected. This is in preparation for versal2. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-39-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 3d960ed2636f4..6474440118212 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -52,14 +52,26 @@ /* * IRQ descriptor to catch the following cases: + * - An IRQ can either connect to the GICs, to the PPU1 intc, or the the EAM * - Multiple devices can connect to the same IRQ. They are OR'ed together. */ FIELD(VERSAL_IRQ, IRQ, 0, 16) +FIELD(VERSAL_IRQ, TARGET, 16, 2) FIELD(VERSAL_IRQ, ORED, 18, 1) FIELD(VERSAL_IRQ, OR_IDX, 19, 4) /* input index on the IRQ OR gate */ +typedef enum VersalIrqTarget { + IRQ_TARGET_GIC, + IRQ_TARGET_PPU1, + IRQ_TARGET_EAM, +} VersalIrqTarget; + +#define PPU1_IRQ(irq) ((IRQ_TARGET_PPU1 << R_VERSAL_IRQ_TARGET_SHIFT) | (irq)) +#define EAM_IRQ(irq) ((IRQ_TARGET_EAM << R_VERSAL_IRQ_TARGET_SHIFT) | (irq)) #define OR_IRQ(irq, or_idx) \ (R_VERSAL_IRQ_ORED_MASK | ((or_idx) << R_VERSAL_IRQ_OR_IDX_SHIFT) | (irq)) +#define PPU1_OR_IRQ(irq, or_idx) \ + ((IRQ_TARGET_PPU1 << R_VERSAL_IRQ_TARGET_SHIFT) | OR_IRQ(irq, or_idx)) typedef struct VersalSimplePeriphMap { uint64_t addr; @@ -414,6 +426,13 @@ static qemu_irq versal_get_gic_irq(Versal *s, int irq_idx) static qemu_irq versal_get_irq_or_gate_in(Versal *s, int irq_idx, qemu_irq target_irq) { + static const char *TARGET_STR[] = { + [IRQ_TARGET_GIC] = "gic", + [IRQ_TARGET_PPU1] = "ppu1", + [IRQ_TARGET_EAM] = "eam", + }; + + VersalIrqTarget target; Object *container = versal_get_child(s, "irq-or-gates"); DeviceState *dev; g_autofree char *name; @@ -421,8 +440,9 @@ static qemu_irq versal_get_irq_or_gate_in(Versal *s, int irq_idx, idx = FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ); or_idx = FIELD_EX32(irq_idx, VERSAL_IRQ, OR_IDX); + target = FIELD_EX32(irq_idx, VERSAL_IRQ, TARGET); - name = g_strdup_printf("irq[%d]", idx); + name = g_strdup_printf("%s-irq[%d]", TARGET_STR[target], idx); dev = DEVICE(object_resolve_path_at(container, name)); if (dev == NULL) { @@ -438,12 +458,29 @@ static qemu_irq versal_get_irq_or_gate_in(Versal *s, int irq_idx, static qemu_irq versal_get_irq(Versal *s, int irq_idx) { + VersalIrqTarget target; qemu_irq irq; bool ored; + target = FIELD_EX32(irq_idx, VERSAL_IRQ, TARGET); ored = FIELD_EX32(irq_idx, VERSAL_IRQ, ORED); - irq = versal_get_gic_irq(s, irq_idx); + switch (target) { + case IRQ_TARGET_EAM: + /* EAM not implemented */ + return NULL; + + case IRQ_TARGET_PPU1: + /* PPU1 CPU not implemented */ + return NULL; + + case IRQ_TARGET_GIC: + irq = versal_get_gic_irq(s, irq_idx); + break; + + default: + g_assert_not_reached(); + } if (ored) { irq = versal_get_irq_or_gate_in(s, irq_idx, irq); From 0252b88c796d05ffbfefe57d4c8f7dc7a69556dd Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:57 +0200 Subject: [PATCH 1372/1794] target/arm/tcg/cpu64: add the cortex-a78ae CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the ARM Cortex-A78AE CPU. Signed-off-by: Luc Michel Reviewed-by: Edgar E. Iglesias Reviewed-by: Peter Maydell Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-40-luc.michel@amd.com Signed-off-by: Peter Maydell --- target/arm/tcg/cpu64.c | 78 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index abef6a246e8f0..90b6c0ebb0e62 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -406,6 +406,79 @@ static void aarch64_a76_initfn(Object *obj) cpu->isar.reset_pmcr_el0 = 0x410b3000; } +static void aarch64_a78ae_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; + + cpu->dtb_compatible = "arm,cortex-a78ae"; + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + set_feature(&cpu->env, ARM_FEATURE_EL2); + set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); + + /* Ordered by 3.2.4 AArch64 registers by functional group */ + SET_IDREG(isar, CLIDR, 0x82000023); + cpu->ctr = 0x9444c004; + cpu->dcz_blocksize = 4; + SET_IDREG(isar, ID_AA64DFR0, 0x0000000110305408ull); + SET_IDREG(isar, ID_AA64ISAR0, 0x0010100010211120ull); + SET_IDREG(isar, ID_AA64ISAR1, 0x0000000001200031ull); + SET_IDREG(isar, ID_AA64MMFR0, 0x0000000000101125ull); + SET_IDREG(isar, ID_AA64MMFR1, 0x0000000010212122ull); + SET_IDREG(isar, ID_AA64MMFR2, 0x0000000100001011ull); + SET_IDREG(isar, ID_AA64PFR0, 0x1100000010111112ull); /* GIC filled in later */ + SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000010ull); + SET_IDREG(isar, ID_AFR0, 0x00000000); + SET_IDREG(isar, ID_DFR0, 0x04010088); + SET_IDREG(isar, ID_ISAR0, 0x02101110); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232042); + SET_IDREG(isar, ID_ISAR3, 0x01112131); + SET_IDREG(isar, ID_ISAR4, 0x00010142); + SET_IDREG(isar, ID_ISAR5, 0x01011121); + SET_IDREG(isar, ID_ISAR6, 0x00000010); + SET_IDREG(isar, ID_MMFR0, 0x10201105); + SET_IDREG(isar, ID_MMFR1, 0x40000000); + SET_IDREG(isar, ID_MMFR2, 0x01260000); + SET_IDREG(isar, ID_MMFR3, 0x02122211); + SET_IDREG(isar, ID_MMFR4, 0x00021110); + SET_IDREG(isar, ID_PFR0, 0x10010131); + SET_IDREG(isar, ID_PFR1, 0x00010000); /* GIC filled in later */ + SET_IDREG(isar, ID_PFR2, 0x00000011); + cpu->midr = 0x410fd423; /* r0p3 */ + cpu->revidr = 0; + + /* From 3.2.33 CCSIDR_EL1 */ + /* 64KB L1 dcache */ + cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 64 * KiB, 7); + /* 64KB L1 icache */ + cpu->ccsidr[1] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 64 * KiB, 2); + /* 512KB L2 cache */ + cpu->ccsidr[2] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 8, 64, 512 * KiB, 7); + + /* From 3.2.118 SCTLR_EL3 */ + cpu->reset_sctlr = 0x30c50838; + + /* From 3.4.23 ICH_VTR_EL2 */ + cpu->gic_num_lrs = 4; + cpu->gic_vpribits = 5; + cpu->gic_vprebits = 5; + /* From 3.4.8 ICC_CTLR_EL3 */ + cpu->gic_pribits = 5; + + /* From 3.5.1 AdvSIMD AArch64 register summary */ + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x13211111; + cpu->isar.mvfr2 = 0x00000043; + + /* From 5.5.1 AArch64 PMU register summary */ + cpu->isar.reset_pmcr_el0 = 0x41223000; +} + static void aarch64_a64fx_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -1321,6 +1394,11 @@ static const ARMCPUInfo aarch64_cpus[] = { { .name = "cortex-a55", .initfn = aarch64_a55_initfn }, { .name = "cortex-a72", .initfn = aarch64_a72_initfn }, { .name = "cortex-a76", .initfn = aarch64_a76_initfn }, + /* + * The Cortex-A78AE differs slightly from the plain Cortex-A78. We don't + * currently model the latter. + */ + { .name = "cortex-a78ae", .initfn = aarch64_a78ae_initfn }, { .name = "cortex-a710", .initfn = aarch64_a710_initfn }, { .name = "a64fx", .initfn = aarch64_a64fx_initfn }, { .name = "neoverse-n1", .initfn = aarch64_neoverse_n1_initfn }, From 8cd160dccf763b6680a8c7c6b503965d5aa04cbd Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:58 +0200 Subject: [PATCH 1373/1794] hw/arm/xlnx-versal: add versal2 SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the Versal Gen 2 (versal2) version of the Versal SoC family. This version embeds up to 8 Cortex-A78AE cores (split into 4 clusters) and 10 Cortex-R52 cores (split into 5 clusters). The similarities between versal and versal2 in term of architecture allow to reuse the VersalMap structure to almost fully describe the implemented parts of versal2. The versal2 eFuse device differs quite a lot from the versal one and is left as future work. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-41-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 207 ++++++++++++++++++++++++++++++++--- include/hw/arm/xlnx-versal.h | 17 ++- 2 files changed, 209 insertions(+), 15 deletions(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 6474440118212..81cb6294cfa64 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -1,5 +1,5 @@ /* - * Xilinx Versal SoC model. + * AMD/Xilinx Versal family SoC model. * * Copyright (c) 2018 Xilinx Inc. * Copyright (c) 2025 Advanced Micro Devices, Inc. @@ -352,8 +352,124 @@ static const VersalMap VERSAL_MAP = { .reserved = { 0xa0000000, 111, 8 }, }; +static const VersalMap VERSAL2_MAP = { + .ocm = { + .addr = 0xbbe00000, + .size = 2 * MiB, + }, + + .ddr = { + .chan[0] = { .addr = 0x0, .size = 2046 * MiB }, + .chan[1] = { .addr = 0x800000000ull, .size = 32 * GiB }, + .chan[2] = { .addr = 0xc00000000ull, .size = 256 * GiB }, + .chan[3] = { .addr = 0x10000000000ull, .size = 734 * GiB }, + .num_chan = 4, + }, + + .apu = { + .name = "apu", + .cpu_model = ARM_CPU_TYPE_NAME("cortex-a78ae"), + .num_cluster = 4, + .num_core = 2, + .qemu_cluster_id = 0, + .mp_affinity = { + .base = 0x0, /* TODO: the MT bit should be set */ + .core_shift = ARM_AFF1_SHIFT, + .cluster_shift = ARM_AFF2_SHIFT, + }, + .start_powered_off = SPO_SECONDARIES, + .dtb_expose = true, + .gic = { + .version = 3, + .dist = 0xe2000000, + .redist = 0xe2060000, + .num_irq = 544, + .has_its = true, + .its = 0xe2040000, + }, + }, + + .rpu = { + .name = "rpu", + .cpu_model = ARM_CPU_TYPE_NAME("cortex-r52"), + .num_cluster = 5, + .num_core = 2, + .qemu_cluster_id = 1, + .mp_affinity = { + .core_shift = ARM_AFF0_SHIFT, + .cluster_shift = ARM_AFF1_SHIFT, + }, + .start_powered_off = SPO_ALL, + .dtb_expose = false, + .per_cluster_gic = true, + .gic = { + .version = 3, + .dist = 0x0, + .redist = 0x100000, + .num_irq = 288, + }, + }, + + .uart[0] = { 0xf1920000, 25 }, + .uart[1] = { 0xf1930000, 26 }, + .num_uart = 2, + + .canfd[0] = { 0xf19e0000, 27 }, + .canfd[1] = { 0xf19f0000, 28 }, + .canfd[2] = { 0xf1a00000, 95 }, + .canfd[3] = { 0xf1a10000, 96 }, + .num_canfd = 4, + + .gem[0] = { { 0xf1a60000, 39 }, 2, "rgmii-id", 1000 }, + .gem[1] = { { 0xf1a70000, 41 }, 2, "rgmii-id", 1000 }, + .gem[2] = { { 0xed920000, 164 }, 4, "usxgmii", 10000 }, /* MMI 10Gb GEM */ + .num_gem = 3, + + .zdma[0] = { "adma", { 0xebd00000, 72 }, 8, 0x10000, 1 }, + .zdma[1] = { "sdma", { 0xebd80000, 112 }, 8, 0x10000, 1 }, + .num_zdma = 2, + + .usb[0] = { .xhci = 0xf1b00000, .ctrl = 0xf1ee0000, .irq = 29 }, + .usb[1] = { .xhci = 0xf1c00000, .ctrl = 0xf1ef0000, .irq = 34 }, + .num_usb = 2, + + .efuse = { .ctrl = 0xf1240000, .cache = 0xf1250000, .irq = 230 }, + + .ospi = { + .ctrl = 0xf1010000, + .dac = 0xc0000000, .dac_sz = 0x20000000, + .dma_src = 0xf1011000, .dma_dst = 0xf1011800, + .irq = 216, + }, + + .sdhci[0] = { 0xf1040000, 218 }, + .sdhci[1] = { 0xf1050000, 220 }, /* eMMC */ + .num_sdhci = 2, + + .pmc_iou_slcr = { 0xf1060000, 222 }, + .bbram = { 0xf11f0000, PPU1_OR_IRQ(18, 0) }, + .crl = { 0xeb5e0000 }, + .trng = { 0xf1230000, 233 }, + .rtc = { + { 0xf12a0000, PPU1_OR_IRQ(18, 1) }, + .alarm_irq = 200, .second_irq = 201 + }, + + .cfu = { + .cframe_base = 0xf12d0000, .cframe_stride = 0x1000, + .cframe_bcast_reg = 0xf12ee000, .cframe_bcast_fdri = 0xf12ef000, + .cfu_apb = 0xf12b0000, .cfu_sfr = 0xf12c1000, + .cfu_stream = 0xf12c0000, .cfu_stream_2 = 0xf1f80000, + .cfu_fdro = 0xf12c2000, + .cfu_apb_irq = 235, .cframe_irq = EAM_IRQ(7), + }, + + .reserved = { 0xf5e00000, 270, 8 }, +}; + static const VersalMap *VERSION_TO_MAP[] = { [VERSAL_VER_VERSAL] = &VERSAL_MAP, + [VERSAL_VER_VERSAL2] = &VERSAL2_MAP, }; static inline VersalVersion versal_get_version(Versal *s) @@ -1294,6 +1410,11 @@ static void versal_create_efuse(Versal *s, DeviceState *ctrl; DeviceState *cache; + if (versal_get_version(s) != VERSAL_VER_VERSAL) { + /* TODO for versal2 */ + return; + } + ctrl = qdev_new(TYPE_XLNX_VERSAL_EFUSE_CTRL); cache = qdev_new(TYPE_XLNX_VERSAL_EFUSE_CACHE); bits = qdev_new(TYPE_XLNX_EFUSE); @@ -1545,22 +1666,32 @@ static inline void crl_connect_dev_by_name(Versal *s, Object *crl, static inline void versal_create_crl(Versal *s) { const VersalMap *map; + VersalVersion ver; const char *crl_class; DeviceState *dev; + size_t num_gem; Object *obj; map = versal_get_map(s); + ver = versal_get_version(s); - crl_class = TYPE_XLNX_VERSAL_CRL; + crl_class = xlnx_versal_crl_class_name(ver); dev = qdev_new(crl_class); obj = OBJECT(dev); object_property_add_child(OBJECT(s), "crl", obj); + /* + * The 3rd GEM controller on versal2 is in the MMI subsystem. + * Its reset line is not connected to the CRL. Consider only the first two + * ones. + */ + num_gem = ver == VERSAL_VER_VERSAL2 ? 2 : map->num_gem; + crl_connect_dev_by_name(s, obj, "rpu-cluster/rpu", map->rpu.num_cluster * map->rpu.num_core); crl_connect_dev_by_name(s, obj, map->zdma[0].name, map->zdma[0].num_chan); crl_connect_dev_by_name(s, obj, "uart", map->num_uart); - crl_connect_dev_by_name(s, obj, "gem", map->num_gem); + crl_connect_dev_by_name(s, obj, "gem", num_gem); crl_connect_dev_by_name(s, obj, "usb", map->num_usb); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_abort); @@ -1568,7 +1699,10 @@ static inline void versal_create_crl(Versal *s) memory_region_add_subregion(&s->mr_ps, map->crl.addr, sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); - versal_sysbus_connect_irq(s, SYS_BUS_DEVICE(dev), 0, map->crl.irq); + if (ver == VERSAL_VER_VERSAL) { + /* CRL IRQ line has been removed in versal2 */ + versal_sysbus_connect_irq(s, SYS_BUS_DEVICE(dev), 0, map->crl.irq); + } } /* @@ -1660,17 +1794,12 @@ static void versal_unimp_irq_parity_imr(void *opaque, int n, int level) "is not yet implemented\n"); } -static void versal_unimp(Versal *s) +static void versal_unimp_common(Versal *s) { DeviceState *slcr; qemu_irq gpio_in; - versal_unimp_area(s, "psm", &s->mr_ps, 0xffc80000, 0x70000); - versal_unimp_area(s, "crf", &s->mr_ps, 0xfd1a0000, 0x140000); - versal_unimp_area(s, "apu", &s->mr_ps, 0xfd5c0000, 0x100); versal_unimp_area(s, "crp", &s->mr_ps, 0xf1260000, 0x10000); - versal_unimp_area(s, "iou-scntr", &s->mr_ps, 0xff130000, 0x10000); - versal_unimp_area(s, "iou-scntr-seucre", &s->mr_ps, 0xff140000, 0x10000); qdev_init_gpio_in_named(DEVICE(s), versal_unimp_sd_emmc_sel, "sd-emmc-sel-dummy", 2); @@ -1693,6 +1822,25 @@ static void versal_unimp(Versal *s) qdev_connect_gpio_out_named(slcr, SYSBUS_DEVICE_GPIO_IRQ, 0, gpio_in); } +static void versal_unimp(Versal *s) +{ + versal_unimp_area(s, "psm", &s->mr_ps, 0xffc80000, 0x70000); + versal_unimp_area(s, "crf", &s->mr_ps, 0xfd1a0000, 0x140000); + versal_unimp_area(s, "apu", &s->mr_ps, 0xfd5c0000, 0x100); + versal_unimp_area(s, "iou-scntr", &s->mr_ps, 0xff130000, 0x10000); + versal_unimp_area(s, "iou-scntr-secure", &s->mr_ps, 0xff140000, 0x10000); + + versal_unimp_common(s); +} + +static void versal2_unimp(Versal *s) +{ + versal_unimp_area(s, "fpd-systmr-ctrl", &s->mr_ps, 0xec920000, 0x1000); + versal_unimp_area(s, "crf", &s->mr_ps, 0xec200000, 0x100000); + + versal_unimp_common(s); +} + static uint32_t fdt_add_clk_node(Versal *s, const char *name, unsigned int freq_hz) { @@ -1710,9 +1858,8 @@ static uint32_t fdt_add_clk_node(Versal *s, const char *name, return phandle; } -static void versal_realize(DeviceState *dev, Error **errp) +static void versal_realize_common(Versal *s) { - Versal *s = XLNX_VERSAL_BASE(dev); DeviceState *slcr, *ospi; MemoryRegion *ocm; Object *container; @@ -1787,7 +1934,6 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_crl(s); versal_map_ddr(s, &map->ddr); - versal_unimp(s); /* Create the On Chip Memory (OCM). */ ocm = g_new(MemoryRegion, 1); @@ -1795,6 +1941,22 @@ static void versal_realize(DeviceState *dev, Error **errp) memory_region_add_subregion_overlap(&s->mr_ps, map->ocm.addr, ocm, 0); } +static void versal_realize(DeviceState *dev, Error **errp) +{ + Versal *s = XLNX_VERSAL_BASE(dev); + + versal_realize_common(s); + versal_unimp(s); +} + +static void versal2_realize(DeviceState *dev, Error **errp) +{ + Versal *s = XLNX_VERSAL_BASE(dev); + + versal_realize_common(s); + versal2_unimp(s); +} + DeviceState *versal_get_boot_cpu(Versal *s) { return DEVICE(versal_get_child_idx(s, "apu-cluster/apu", 0)); @@ -1943,7 +2105,6 @@ static void versal_base_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->realize = versal_realize; device_class_set_props(dc, versal_properties); /* No VMSD since we haven't got any top-level SoC state to save. */ } @@ -1951,8 +2112,19 @@ static void versal_base_class_init(ObjectClass *klass, const void *data) static void versal_class_init(ObjectClass *klass, const void *data) { VersalClass *vc = XLNX_VERSAL_BASE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); vc->version = VERSAL_VER_VERSAL; + dc->realize = versal_realize; +} + +static void versal2_class_init(ObjectClass *klass, const void *data) +{ + VersalClass *vc = XLNX_VERSAL_BASE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + vc->version = VERSAL_VER_VERSAL2; + dc->realize = versal2_realize; } static const TypeInfo versal_base_info = { @@ -1972,10 +2144,17 @@ static const TypeInfo versal_info = { .class_init = versal_class_init, }; +static const TypeInfo versal2_info = { + .name = TYPE_XLNX_VERSAL2, + .parent = TYPE_XLNX_VERSAL_BASE, + .class_init = versal2_class_init, +}; + static void versal_register_types(void) { type_register_static(&versal_base_info); type_register_static(&versal_info); + type_register_static(&versal2_info); } type_init(versal_register_types); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index b6cc71f7209df..e1fb1f4cf5b3d 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -1,5 +1,5 @@ /* - * Model of the Xilinx Versal + * AMD/Xilinx Versal family SoC model. * * Copyright (c) 2018 Xilinx Inc. * Copyright (c) 2025 Advanced Micro Devices, Inc. @@ -22,6 +22,7 @@ OBJECT_DECLARE_TYPE(Versal, VersalClass, XLNX_VERSAL_BASE) #define TYPE_XLNX_VERSAL "xlnx-versal" +#define TYPE_XLNX_VERSAL2 "xlnx-versal2" struct Versal { /*< private >*/ @@ -72,4 +73,18 @@ int versal_get_num_cpu(VersalVersion version); int versal_get_num_can(VersalVersion version); int versal_get_num_sdhci(VersalVersion version); +static inline const char *versal_get_class(VersalVersion version) +{ + switch (version) { + case VERSAL_VER_VERSAL: + return TYPE_XLNX_VERSAL; + + case VERSAL_VER_VERSAL2: + return TYPE_XLNX_VERSAL2; + + default: + g_assert_not_reached(); + } +} + #endif From 84a759c3d24baf13bbf3e95667280758ebb70545 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:07:59 +0200 Subject: [PATCH 1374/1794] hw/arm/xlnx-versal-virt: rename the machine to amd-versal-virt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To align with current branding and ensure coherency with the upcoming versal2 machine, rename the xlnx-versal-virt machine to amd-versal-virt. Keep an alias of the old name to the new one for command-line backward compatibility. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-42-luc.michel@amd.com Signed-off-by: Peter Maydell --- docs/system/arm/xlnx-versal-virt.rst | 26 +++++++++++--------- hw/arm/xlnx-versal-virt.c | 11 ++++++--- tests/functional/aarch64/test_xlnx_versal.py | 6 ++--- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/docs/system/arm/xlnx-versal-virt.rst b/docs/system/arm/xlnx-versal-virt.rst index c5f35f28e4f54..2c63fbf519ff3 100644 --- a/docs/system/arm/xlnx-versal-virt.rst +++ b/docs/system/arm/xlnx-versal-virt.rst @@ -1,19 +1,23 @@ -Xilinx Versal Virt (``xlnx-versal-virt``) -========================================= +AMD Versal Virt (``amd-versal-virt``) +===================================== -Xilinx Versal is a family of heterogeneous multi-core SoCs +AMD Versal is a family of heterogeneous multi-core SoCs (System on Chip) that combine traditional hardened CPUs and I/O peripherals in a Processing System (PS) with runtime programmable FPGA logic (PL) and an Artificial Intelligence Engine (AIE). +QEMU implements the Versal Series variant of Versal SoCs, with the +``amd-versal-virt`` machine. The alias ``xlnx-versal-virt`` is kept for +backward compatibility. + More details here: -https://www.xilinx.com/products/silicon-devices/acap/versal.html +https://www.amd.com/en/products/adaptive-socs-and-fpgas/versal.html The family of Versal SoCs share a single architecture but come in different parts with different speed grades, amounts of PL and other differences. -The Xilinx Versal Virt board in QEMU is a model of a virtual board +The AMD Versal Virt board in QEMU is a model of a virtual board (does not exist in reality) with a virtual Versal SoC without I/O limitations. Currently, we support the following cores and devices: @@ -74,7 +78,7 @@ Direct Linux boot of a generic ARM64 upstream Linux kernel: .. code-block:: bash - $ qemu-system-aarch64 -M xlnx-versal-virt -m 2G \ + $ qemu-system-aarch64 -M amd-versal-virt -m 2G \ -serial mon:stdio -display none \ -kernel arch/arm64/boot/Image \ -nic user -nic user \ @@ -87,7 +91,7 @@ Direct Linux boot of PetaLinux 2019.2: .. code-block:: bash - $ qemu-system-aarch64 -M xlnx-versal-virt -m 2G \ + $ qemu-system-aarch64 -M amd-versal-virt -m 2G \ -serial mon:stdio -display none \ -kernel petalinux-v2019.2/Image \ -append "rdinit=/sbin/init console=ttyAMA0,115200n8 earlycon=pl011,mmio,0xFF000000,115200n8" \ @@ -100,7 +104,7 @@ version of ATF tries to configure the CCI which we don't model) and U-boot: .. code-block:: bash - $ qemu-system-aarch64 -M xlnx-versal-virt -m 2G \ + $ qemu-system-aarch64 -M amd-versal-virt -m 2G \ -serial stdio -display none \ -device loader,file=petalinux-v2018.3/bl31.elf,cpu-num=0 \ -device loader,file=petalinux-v2019.2/u-boot.elf \ @@ -125,7 +129,7 @@ Boot Linux as DOM0 on Xen via U-Boot: .. code-block:: bash - $ qemu-system-aarch64 -M xlnx-versal-virt -m 4G \ + $ qemu-system-aarch64 -M amd-versal-virt -m 4G \ -serial stdio -display none \ -device loader,file=petalinux-v2019.2/u-boot.elf,cpu-num=0 \ -device loader,addr=0x30000000,file=linux/2018-04-24/xen \ @@ -153,7 +157,7 @@ Boot Linux as Dom0 on Xen via ARM Trusted Firmware and U-Boot: .. code-block:: bash - $ qemu-system-aarch64 -M xlnx-versal-virt -m 4G \ + $ qemu-system-aarch64 -M amd-versal-virt -m 4G \ -serial stdio -display none \ -device loader,file=petalinux-v2018.3/bl31.elf,cpu-num=0 \ -device loader,file=petalinux-v2019.2/u-boot.elf \ @@ -227,7 +231,7 @@ To use a different index value, N, from default of 1, add: is highly recommended (albeit with usage complexity). Better yet, do not use actual product data when running guest image - on this Xilinx Versal Virt board. + on this AMD Versal Virt board. Using CANFDs for Versal Virt """""""""""""""""""""""""""" diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 274a7ef9889cb..6ef56103a7578 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -1,5 +1,5 @@ /* - * Xilinx Versal Virtual board. + * AMD/Xilinx Versal Virtual board. * * Copyright (c) 2018 Xilinx Inc. * Copyright (c) 2025 Advanced Micro Devices, Inc. @@ -26,7 +26,7 @@ #include "qom/object.h" #include "target/arm/cpu.h" -#define TYPE_XLNX_VERSAL_VIRT_MACHINE MACHINE_TYPE_NAME("xlnx-versal-virt") +#define TYPE_XLNX_VERSAL_VIRT_MACHINE MACHINE_TYPE_NAME("amd-versal-virt") OBJECT_DECLARE_SIMPLE_TYPE(VersalVirt, XLNX_VERSAL_VIRT_MACHINE) #define XLNX_VERSAL_NUM_OSPI_FLASH 4 @@ -55,6 +55,7 @@ struct VersalVirt { static void fdt_create(VersalVirt *s) { MachineClass *mc = MACHINE_GET_CLASS(s); + const char versal_compat[] = "amd-versal-virt\0xlnx-versal-virt"; s->fdt = create_device_tree(&s->fdt_size); if (!s->fdt) { @@ -72,7 +73,8 @@ static void fdt_create(VersalVirt *s) /* Header */ qemu_fdt_setprop_string(s->fdt, "/", "model", mc->desc); - qemu_fdt_setprop_string(s->fdt, "/", "compatible", "xlnx-versal-virt"); + qemu_fdt_setprop(s->fdt, "/", "compatible", versal_compat, + sizeof(versal_compat)); } static void fdt_add_clk_node(VersalVirt *s, const char *name, @@ -348,7 +350,8 @@ static void versal_virt_machine_class_init(ObjectClass *oc, const void *data) MachineClass *mc = MACHINE_CLASS(oc); int num_cpu = versal_get_num_cpu(VERSAL_VER_VERSAL); - mc->desc = "Xilinx Versal Virtual development board"; + mc->desc = "AMD Versal Virtual development board"; + mc->alias = "xlnx-versal-virt"; mc->init = versal_virt_init; mc->min_cpus = num_cpu; mc->max_cpus = num_cpu; diff --git a/tests/functional/aarch64/test_xlnx_versal.py b/tests/functional/aarch64/test_xlnx_versal.py index 4b9c49e5d6464..95e5c44771fd6 100755 --- a/tests/functional/aarch64/test_xlnx_versal.py +++ b/tests/functional/aarch64/test_xlnx_versal.py @@ -6,7 +6,7 @@ from qemu_test import LinuxKernelTest, Asset -class XlnxVersalVirtMachine(LinuxKernelTest): +class AmdVersalVirtMachine(LinuxKernelTest): ASSET_KERNEL = Asset( ('http://ports.ubuntu.com/ubuntu-ports/dists/bionic-updates/main/' @@ -20,8 +20,8 @@ class XlnxVersalVirtMachine(LinuxKernelTest): '/ubuntu-installer/arm64/initrd.gz'), 'e7a5e716b6f516d8be315c06e7331aaf16994fe4222e0e7cfb34bc015698929e') - def test_aarch64_xlnx_versal_virt(self): - self.set_machine('xlnx-versal-virt') + def test_aarch64_amd_versal_virt(self): + self.set_machine('amd-versal-virt') kernel_path = self.ASSET_KERNEL.fetch() initrd_path = self.ASSET_INITRD.fetch() From ea7028b87b62c4e93f4c2455760253a224626773 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:08:00 +0200 Subject: [PATCH 1375/1794] hw/arm/xlnx-versal-virt: split into base/concrete classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the xlnx-versal-virt machine type into a base abstract type and a concrete type. There is no functional change. This is in preparation for the versal2 machine. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-43-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 74 +++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 6ef56103a7578..f9abb9ed639be 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -26,8 +26,11 @@ #include "qom/object.h" #include "target/arm/cpu.h" +#define TYPE_XLNX_VERSAL_VIRT_BASE_MACHINE \ + MACHINE_TYPE_NAME("amd-versal-virt-base") +OBJECT_DECLARE_TYPE(VersalVirt, VersalVirtClass, XLNX_VERSAL_VIRT_BASE_MACHINE) + #define TYPE_XLNX_VERSAL_VIRT_MACHINE MACHINE_TYPE_NAME("amd-versal-virt") -OBJECT_DECLARE_SIMPLE_TYPE(VersalVirt, XLNX_VERSAL_VIRT_MACHINE) #define XLNX_VERSAL_NUM_OSPI_FLASH 4 @@ -52,6 +55,12 @@ struct VersalVirt { } cfg; }; +struct VersalVirtClass { + MachineClass parent_class; + + VersalVersion version; +}; + static void fdt_create(VersalVirt *s) { MachineClass *mc = MACHINE_GET_CLASS(s); @@ -193,14 +202,14 @@ static void sd_plug_card(VersalVirt *s, int idx, DriveInfo *di) static char *versal_get_ospi_model(Object *obj, Error **errp) { - VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); + VersalVirt *s = XLNX_VERSAL_VIRT_BASE_MACHINE(obj); return g_strdup(s->cfg.ospi_model); } static void versal_set_ospi_model(Object *obj, const char *value, Error **errp) { - VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); + VersalVirt *s = XLNX_VERSAL_VIRT_BASE_MACHINE(obj); g_free(s->cfg.ospi_model); s->cfg.ospi_model = g_strdup(value); @@ -209,7 +218,8 @@ static void versal_set_ospi_model(Object *obj, const char *value, Error **errp) static void versal_virt_init(MachineState *machine) { - VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(machine); + VersalVirt *s = XLNX_VERSAL_VIRT_BASE_MACHINE(machine); + VersalVirtClass *vvc = XLNX_VERSAL_VIRT_BASE_MACHINE_GET_CLASS(machine); int psci_conduit = QEMU_PSCI_CONDUIT_DISABLED; int i; @@ -241,11 +251,11 @@ static void versal_virt_init(MachineState *machine) } object_initialize_child(OBJECT(machine), "xlnx-versal", &s->soc, - TYPE_XLNX_VERSAL); + versal_get_class(vvc->version)); object_property_set_link(OBJECT(&s->soc), "ddr", OBJECT(machine->ram), &error_abort); - for (i = 0; i < versal_get_num_can(VERSAL_VER_VERSAL); i++) { + for (i = 0; i < versal_get_num_can(vvc->version); i++) { g_autofree char *prop_name = g_strdup_printf("canbus%d", i); object_property_set_link(OBJECT(&s->soc), prop_name, @@ -274,7 +284,7 @@ static void versal_virt_init(MachineState *machine) efuse_attach_drive(s); /* Plug SD cards */ - for (i = 0; i < versal_get_num_sdhci(VERSAL_VER_VERSAL); i++) { + for (i = 0; i < versal_get_num_sdhci(vvc->version); i++) { sd_plug_card(s, i, drive_get(IF_SD, 0, i)); } @@ -318,10 +328,11 @@ static void versal_virt_init(MachineState *machine) static void versal_virt_machine_instance_init(Object *obj) { - VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); + VersalVirt *s = XLNX_VERSAL_VIRT_BASE_MACHINE(obj); + VersalVirtClass *vvc = XLNX_VERSAL_VIRT_BASE_MACHINE_GET_CLASS(s); size_t i, num_can; - num_can = versal_get_num_can(VERSAL_VER_VERSAL); + num_can = versal_get_num_can(vvc->version); s->canbus = g_new0(CanBusState *, num_can); /* @@ -339,45 +350,64 @@ static void versal_virt_machine_instance_init(Object *obj) static void versal_virt_machine_finalize(Object *obj) { - VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); + VersalVirt *s = XLNX_VERSAL_VIRT_BASE_MACHINE(obj); g_free(s->cfg.ospi_model); g_free(s->canbus); } -static void versal_virt_machine_class_init(ObjectClass *oc, const void *data) +static void versal_virt_machine_class_init_common(ObjectClass *oc) { MachineClass *mc = MACHINE_CLASS(oc); - int num_cpu = versal_get_num_cpu(VERSAL_VER_VERSAL); + VersalVirtClass *vvc = XLNX_VERSAL_VIRT_BASE_MACHINE_CLASS(mc); + int num_cpu = versal_get_num_cpu(vvc->version); - mc->desc = "AMD Versal Virtual development board"; - mc->alias = "xlnx-versal-virt"; - mc->init = versal_virt_init; - mc->min_cpus = num_cpu; - mc->max_cpus = num_cpu; - mc->default_cpus = num_cpu; mc->no_cdrom = true; mc->auto_create_sdcard = true; mc->default_ram_id = "ddr"; + mc->min_cpus = num_cpu; + mc->max_cpus = num_cpu; + mc->default_cpus = num_cpu; + mc->init = versal_virt_init; + object_class_property_add_str(oc, "ospi-flash", versal_get_ospi_model, versal_set_ospi_model); object_class_property_set_description(oc, "ospi-flash", "Change the OSPI Flash model"); } -static const TypeInfo versal_virt_machine_init_typeinfo = { - .name = TYPE_XLNX_VERSAL_VIRT_MACHINE, +static void versal_virt_machine_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + VersalVirtClass *vvc = XLNX_VERSAL_VIRT_BASE_MACHINE_CLASS(oc); + + mc->desc = "AMD Versal Virtual development board"; + mc->alias = "xlnx-versal-virt"; + vvc->version = VERSAL_VER_VERSAL; + + versal_virt_machine_class_init_common(oc); +} + +static const TypeInfo versal_virt_base_machine_init_typeinfo = { + .name = TYPE_XLNX_VERSAL_VIRT_BASE_MACHINE, .parent = TYPE_MACHINE, - .class_init = versal_virt_machine_class_init, + .class_size = sizeof(VersalVirtClass), .instance_init = versal_virt_machine_instance_init, .instance_size = sizeof(VersalVirt), .instance_finalize = versal_virt_machine_finalize, + .abstract = true, +}; + +static const TypeInfo versal_virt_machine_init_typeinfo = { + .name = TYPE_XLNX_VERSAL_VIRT_MACHINE, + .parent = TYPE_XLNX_VERSAL_VIRT_BASE_MACHINE, + .class_init = versal_virt_machine_class_init, }; static void versal_virt_machine_init_register_types(void) { + type_register_static(&versal_virt_base_machine_init_typeinfo); type_register_static(&versal_virt_machine_init_typeinfo); } type_init(versal_virt_machine_init_register_types) - From e2e9b9e129f484398326f262de2933a3db3f8c62 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:08:01 +0200 Subject: [PATCH 1376/1794] hw/arm/xlnx-versal-virt: tidy up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove now unused clock nodes. They have been replaced by the ones created in the SoC. Remove the unused cfg.secure VersalVirt field. Remove unecessary include directives. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-44-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index f9abb9ed639be..14c2d5cc9243e 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -19,10 +19,8 @@ #include "hw/boards.h" #include "hw/sysbus.h" #include "hw/arm/fdt.h" -#include "hw/qdev-properties.h" #include "hw/arm/xlnx-versal.h" #include "hw/arm/boot.h" -#include "target/arm/multiprocessing.h" #include "qom/object.h" #include "target/arm/cpu.h" @@ -41,16 +39,11 @@ struct VersalVirt { void *fdt; int fdt_size; - struct { - uint32_t clk_125Mhz; - uint32_t clk_25Mhz; - } phandle; struct arm_boot_info binfo; CanBusState **canbus; struct { - bool secure; char *ospi_model; } cfg; }; @@ -72,10 +65,6 @@ static void fdt_create(VersalVirt *s) exit(1); } - /* Allocate all phandles. */ - s->phandle.clk_25Mhz = qemu_fdt_alloc_phandle(s->fdt); - s->phandle.clk_125Mhz = qemu_fdt_alloc_phandle(s->fdt); - /* Create /chosen node for load_dtb. */ qemu_fdt_add_subnode(s->fdt, "/chosen"); qemu_fdt_add_subnode(s->fdt, "/aliases"); @@ -86,17 +75,6 @@ static void fdt_create(VersalVirt *s) sizeof(versal_compat)); } -static void fdt_add_clk_node(VersalVirt *s, const char *name, - unsigned int freq_hz, uint32_t phandle) -{ - qemu_fdt_add_subnode(s->fdt, name); - qemu_fdt_setprop_cell(s->fdt, name, "phandle", phandle); - qemu_fdt_setprop_cell(s->fdt, name, "clock-frequency", freq_hz); - qemu_fdt_setprop_cell(s->fdt, name, "#clock-cells", 0x0); - qemu_fdt_setprop_string(s->fdt, name, "compatible", "fixed-clock"); - qemu_fdt_setprop(s->fdt, name, "u-boot,dm-pre-reloc", NULL, 0); -} - static void fdt_nop_memory_nodes(void *fdt, Error **errp) { Error *err = NULL; @@ -268,9 +246,6 @@ static void versal_virt_init(MachineState *machine) sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal); create_virtio_regions(s); - fdt_add_clk_node(s, "/old-clk125", 125000000, s->phandle.clk_125Mhz); - fdt_add_clk_node(s, "/old-clk25", 25000000, s->phandle.clk_25Mhz); - /* * Map the SoC address space onto system memory. This will allow virtio and * other modules unaware of multiple address-spaces to work. From 0ec8c4296d48efc0a9ee7ac7bdc7523b793de4bd Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:08:02 +0200 Subject: [PATCH 1377/1794] docs/system/arm/xlnx-versal-virt: update supported devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the list of supported devices in the Versal SoCs. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-45-luc.michel@amd.com Signed-off-by: Peter Maydell --- docs/system/arm/xlnx-versal-virt.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/system/arm/xlnx-versal-virt.rst b/docs/system/arm/xlnx-versal-virt.rst index 2c63fbf519ff3..94c8bacf61a13 100644 --- a/docs/system/arm/xlnx-versal-virt.rst +++ b/docs/system/arm/xlnx-versal-virt.rst @@ -23,11 +23,11 @@ limitations. Currently, we support the following cores and devices: Implemented CPU cores: -- 2 ACPUs (ARM Cortex-A72) +- 2 ACPUs (ARM Cortex-A72) with their GICv3 and ITS +- 2 RCPUs (ARM Cortex-R5F) with their GICv2 Implemented devices: -- Interrupt controller (ARM GICv3) - 2 UARTs (ARM PL011) - An RTC (Versal built-in) - 2 GEMs (Cadence MACB Ethernet MACs) @@ -39,6 +39,9 @@ Implemented devices: - BBRAM (36 bytes of Battery-backed RAM) - eFUSE (3072 bytes of one-time field-programmable bit array) - 2 CANFDs +- USB controller +- OSPI controller +- TRNG controller QEMU does not yet model any other devices, including the PL and the AI Engine. From 39cf696fc57a74a40beafaa1d341d4ef9b41bbd0 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:08:03 +0200 Subject: [PATCH 1378/1794] docs/system/arm/xlnx-versal-virt: add a note about dumpdtb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a note in the DTB section explaining how to dump the generated DTB using the dumpdtb machine option. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-46-luc.michel@amd.com Signed-off-by: Peter Maydell --- docs/system/arm/xlnx-versal-virt.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/system/arm/xlnx-versal-virt.rst b/docs/system/arm/xlnx-versal-virt.rst index 94c8bacf61a13..5d7fa18592be7 100644 --- a/docs/system/arm/xlnx-versal-virt.rst +++ b/docs/system/arm/xlnx-versal-virt.rst @@ -65,7 +65,13 @@ When loading an OS, QEMU generates a DTB and selects an appropriate address where it gets loaded. This DTB will be passed to the kernel in register x0. If there's no ``-kernel`` option, we generate a DTB and place it at 0x1000 -for boot-loaders or firmware to pick it up. +for boot-loaders or firmware to pick it up. To dump and observe the generated +DTB, one can use the ``dumpdtb`` machine option: + +.. code-block:: bash + + $ qemu-system-aarch64 -M amd-versal-virt,dumpdtb=example.dtb -m 2G + If users want to provide their own DTB, they can use the ``-dtb`` option. These DTBs will have their memory nodes modified to match QEMU's From 3429cdd8bfe6ee8239ea19d0f8211c671832e44b Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:08:04 +0200 Subject: [PATCH 1379/1794] hw/arm/xlnx-versal-virt: add the xlnx-versal2-virt machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the Versal Gen 2 Virtual development machine embedding a versal2 SoC. This machine follows the same principle than the xlnx-versal-virt machine. It creates its own DTB and feeds it to the software payload. This way only implemented devices are exposed to the guest and the user does not need to provide a DTB. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-47-luc.michel@amd.com Signed-off-by: Peter Maydell --- docs/system/arm/xlnx-versal-virt.rst | 49 ++++++++++++++++++++++++---- hw/arm/xlnx-versal-virt.c | 37 +++++++++++++++++++-- 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/docs/system/arm/xlnx-versal-virt.rst b/docs/system/arm/xlnx-versal-virt.rst index 5d7fa18592be7..640cc07f808bc 100644 --- a/docs/system/arm/xlnx-versal-virt.rst +++ b/docs/system/arm/xlnx-versal-virt.rst @@ -1,14 +1,16 @@ -AMD Versal Virt (``amd-versal-virt``) -===================================== +AMD Versal Virt (``amd-versal-virt``, ``amd-versal2-virt``) +=========================================================== AMD Versal is a family of heterogeneous multi-core SoCs (System on Chip) that combine traditional hardened CPUs and I/O peripherals in a Processing System (PS) with runtime programmable FPGA logic (PL) and an Artificial Intelligence Engine (AIE). -QEMU implements the Versal Series variant of Versal SoCs, with the -``amd-versal-virt`` machine. The alias ``xlnx-versal-virt`` is kept for -backward compatibility. +QEMU implements the following Versal SoCs variants: + +- Versal (the ``amd-versal-virt`` machine, the alias ``xlnx-versal-virt`` is + kept for backward compatibility) +- Versal Gen 2 (the ``amd-versal2-virt`` machine) More details here: https://www.amd.com/en/products/adaptive-socs-and-fpgas/versal.html @@ -21,6 +23,8 @@ The AMD Versal Virt board in QEMU is a model of a virtual board (does not exist in reality) with a virtual Versal SoC without I/O limitations. Currently, we support the following cores and devices: +Versal +"""""" Implemented CPU cores: - 2 ACPUs (ARM Cortex-A72) with their GICv3 and ITS @@ -43,6 +47,28 @@ Implemented devices: - OSPI controller - TRNG controller +Versal Gen 2 +"""""""""""" +Implemented CPU cores: + +- 8 ACPUs (ARM Cortex-A78AE) with their GICv3 and ITS +- 10 RCPUs (ARM Cortex-R52) with their GICv3 (one per cluster) + +Implemented devices: + +- 2 UARTs (ARM PL011) +- An RTC (Versal built-in) +- 3 GEMs (Cadence MACB Ethernet MACs) +- 8 ADMA (Xilinx zDMA) channels +- 2 SD Controllers +- OCM (256KB of On Chip Memory) +- DDR memory +- BBRAM (36 bytes of Battery-backed RAM) +- 2 CANFDs +- 2 USB controllers +- OSPI controller +- TRNG controller + QEMU does not yet model any other devices, including the PL and the AI Engine. Other differences between the hardware and the QEMU model: @@ -51,8 +77,8 @@ Other differences between the hardware and the QEMU model: ``-m`` argument. If a DTB is provided on the command line then QEMU will edit it to include suitable entries describing the Versal DDR memory ranges. -- QEMU provides 8 virtio-mmio virtio transports; these start at - address ``0xa0000000`` and have IRQs from 111 and upwards. +- QEMU provides 8 virtio-mmio virtio transports. They use reserved memory + regions and IRQ pins to avoid conflicts with real SoC peripherals. Running """"""" @@ -214,6 +240,11 @@ To use a different index value, N, from default of 0, add: eFUSE File Backend """""""""""""""""" + +.. note:: + The eFUSE device is not implemented in the Versal Gen 2 QEMU model + yet. + eFUSE can have an optional file backend, which must be a seekable binary file with a size of 3072 bytes or larger. A file with all binary 0s is a 'blank'. @@ -271,3 +302,7 @@ To connect CANFD0 and CANFD1 to host machine's CAN interface can0: -object can-bus,id=canbus -machine canbus0=canbus -machine canbus1=canbus -object can-host-socketcan,id=canhost0,if=can0,canbus=canbus + +.. note:: + Versal Gen 2 has 4 CAN controllers. ``canbus0`` to ``canbus3`` can + be specified on the command line. diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 14c2d5cc9243e..149b448546ee2 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -1,5 +1,5 @@ /* - * AMD/Xilinx Versal Virtual board. + * AMD/Xilinx Versal family Virtual board. * * Copyright (c) 2018 Xilinx Inc. * Copyright (c) 2025 Advanced Micro Devices, Inc. @@ -29,6 +29,7 @@ OBJECT_DECLARE_TYPE(VersalVirt, VersalVirtClass, XLNX_VERSAL_VIRT_BASE_MACHINE) #define TYPE_XLNX_VERSAL_VIRT_MACHINE MACHINE_TYPE_NAME("amd-versal-virt") +#define TYPE_XLNX_VERSAL2_VIRT_MACHINE MACHINE_TYPE_NAME("amd-versal2-virt") #define XLNX_VERSAL_NUM_OSPI_FLASH 4 @@ -57,7 +58,9 @@ struct VersalVirtClass { static void fdt_create(VersalVirt *s) { MachineClass *mc = MACHINE_GET_CLASS(s); + VersalVirtClass *vvc = XLNX_VERSAL_VIRT_BASE_MACHINE_GET_CLASS(s); const char versal_compat[] = "amd-versal-virt\0xlnx-versal-virt"; + const char versal2_compat[] = "amd-versal2-virt"; s->fdt = create_device_tree(&s->fdt_size); if (!s->fdt) { @@ -71,8 +74,18 @@ static void fdt_create(VersalVirt *s) /* Header */ qemu_fdt_setprop_string(s->fdt, "/", "model", mc->desc); - qemu_fdt_setprop(s->fdt, "/", "compatible", versal_compat, - sizeof(versal_compat)); + + switch (vvc->version) { + case VERSAL_VER_VERSAL: + qemu_fdt_setprop(s->fdt, "/", "compatible", versal_compat, + sizeof(versal_compat)); + break; + + case VERSAL_VER_VERSAL2: + qemu_fdt_setprop(s->fdt, "/", "compatible", versal2_compat, + sizeof(versal2_compat)); + break; + } } static void fdt_nop_memory_nodes(void *fdt, Error **errp) @@ -363,6 +376,17 @@ static void versal_virt_machine_class_init(ObjectClass *oc, const void *data) versal_virt_machine_class_init_common(oc); } +static void versal2_virt_machine_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + VersalVirtClass *vvc = XLNX_VERSAL_VIRT_BASE_MACHINE_CLASS(oc); + + mc->desc = "AMD Versal Gen 2 Virtual development board"; + vvc->version = VERSAL_VER_VERSAL2; + + versal_virt_machine_class_init_common(oc); +} + static const TypeInfo versal_virt_base_machine_init_typeinfo = { .name = TYPE_XLNX_VERSAL_VIRT_BASE_MACHINE, .parent = TYPE_MACHINE, @@ -379,10 +403,17 @@ static const TypeInfo versal_virt_machine_init_typeinfo = { .class_init = versal_virt_machine_class_init, }; +static const TypeInfo versal2_virt_machine_init_typeinfo = { + .name = TYPE_XLNX_VERSAL2_VIRT_MACHINE, + .parent = TYPE_XLNX_VERSAL_VIRT_BASE_MACHINE, + .class_init = versal2_virt_machine_class_init, +}; + static void versal_virt_machine_init_register_types(void) { type_register_static(&versal_virt_base_machine_init_typeinfo); type_register_static(&versal_virt_machine_init_typeinfo); + type_register_static(&versal2_virt_machine_init_typeinfo); } type_init(versal_virt_machine_init_register_types) From f1924af144115a159f854500f134c3769ec388df Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 26 Sep 2025 09:08:05 +0200 Subject: [PATCH 1380/1794] tests/functional/test_aarch64_xlnx_versal: test the versal2 machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test for the amd-versal2-virt machine using the same command line, kernel, initrd than the ones used for amd-versal-virt. Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250926070806.292065-48-luc.michel@amd.com Signed-off-by: Peter Maydell --- tests/functional/aarch64/test_xlnx_versal.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/functional/aarch64/test_xlnx_versal.py b/tests/functional/aarch64/test_xlnx_versal.py index 95e5c44771fd6..45aa6e1b88183 100755 --- a/tests/functional/aarch64/test_xlnx_versal.py +++ b/tests/functional/aarch64/test_xlnx_versal.py @@ -20,8 +20,8 @@ class AmdVersalVirtMachine(LinuxKernelTest): '/ubuntu-installer/arm64/initrd.gz'), 'e7a5e716b6f516d8be315c06e7331aaf16994fe4222e0e7cfb34bc015698929e') - def test_aarch64_amd_versal_virt(self): - self.set_machine('amd-versal-virt') + def common_aarch64_amd_versal_virt(self, machine): + self.set_machine(machine) kernel_path = self.ASSET_KERNEL.fetch() initrd_path = self.ASSET_INITRD.fetch() @@ -33,5 +33,11 @@ def test_aarch64_amd_versal_virt(self): self.vm.launch() self.wait_for_console_pattern('Checked W+X mappings: passed') + def test_aarch64_amd_versal_virt(self): + self.common_aarch64_amd_versal_virt('amd-versal-virt') + + def test_aarch64_amd_versal2_virt(self): + self.common_aarch64_amd_versal_virt('amd-versal2-virt') + if __name__ == '__main__': LinuxKernelTest.main() From b94246a252a3c04424269928a465d3fac02115e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chigot?= Date: Tue, 30 Sep 2025 13:57:16 +0200 Subject: [PATCH 1381/1794] hw/arm/xlnx-zynqmp: move GIC_NUM_SPI_INTR define in header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This define will be needed in a later patch in XlnxZynqMPState structure, hence move it within xlnx-zynqmp header. Add XLXN_ZYNQMP prefix as it's now public. Signed-off-by: Clément Chigot Reviewed-by: Edgar E. Iglesias Message-id: 20250930115718.437100-2-chigot@adacore.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-zynqmp.c | 11 +++++------ include/hw/arm/xlnx-zynqmp.h | 1 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index ec96a46eec3ce..d7adc070f8b7a 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -26,8 +26,6 @@ #include "target/arm/cpu-qom.h" #include "target/arm/gtimer.h" -#define GIC_NUM_SPI_INTR 160 - #define ARM_PHYS_TIMER_PPI 30 #define ARM_VIRT_TIMER_PPI 27 #define ARM_HYP_TIMER_PPI 26 @@ -206,7 +204,7 @@ static const XlnxZynqMPGICRegion xlnx_zynqmp_gic_regions[] = { static inline int arm_gic_ppi_index(int cpu_nr, int ppi_index) { - return GIC_NUM_SPI_INTR + cpu_nr * GIC_INTERNAL + ppi_index; + return XLNX_ZYNQMP_GIC_NUM_SPI_INTR + cpu_nr * GIC_INTERNAL + ppi_index; } static void xlnx_zynqmp_create_rpu(MachineState *ms, XlnxZynqMPState *s, @@ -454,7 +452,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) int num_apus = MIN(ms->smp.cpus, XLNX_ZYNQMP_NUM_APU_CPUS); const char *boot_cpu = s->boot_cpu ? s->boot_cpu : "apu-cpu[0]"; ram_addr_t ddr_low_size, ddr_high_size; - qemu_irq gic_spi[GIC_NUM_SPI_INTR]; + qemu_irq gic_spi[XLNX_ZYNQMP_GIC_NUM_SPI_INTR]; Error *err = NULL; ram_size = memory_region_size(s->ddr_ram); @@ -502,7 +500,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) g_free(ocm_name); } - qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", GIC_NUM_SPI_INTR + 32); + qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", + XLNX_ZYNQMP_GIC_NUM_SPI_INTR + 32); qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2); qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", num_apus); qdev_prop_set_bit(DEVICE(&s->gic), "has-security-extensions", s->secure); @@ -613,7 +612,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) return; } - for (i = 0; i < GIC_NUM_SPI_INTR; i++) { + for (i = 0; i < XLNX_ZYNQMP_GIC_NUM_SPI_INTR; i++) { gic_spi[i] = qdev_get_gpio_in(DEVICE(&s->gic), i); } diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index c137ac59e85fa..6a407c2962482 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -67,6 +67,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP) #define XLNX_ZYNQMP_OCM_RAM_SIZE 0x10000 #define XLNX_ZYNQMP_GIC_REGIONS 6 +#define XLNX_ZYNQMP_GIC_NUM_SPI_INTR 160 /* * ZynqMP maps the ARM GIC regions (GICC, GICD ...) at consecutive 64k offsets From f51ad36255516a5d41e05734e918b32885923151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chigot?= Date: Tue, 30 Sep 2025 13:57:17 +0200 Subject: [PATCH 1382/1794] hw/arm/xlnx-zynqmp: introduce helper to compute RPU number MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helper will avoid repeating the MIN/MAX formula everytime the number of RPUs available is requested. Signed-off-by: Clément Chigot Reviewed-by: Edgar E. Iglesias Message-id: 20250930115718.437100-3-chigot@adacore.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-zynqmp.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index d7adc070f8b7a..3d8c46986ebc0 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -207,14 +207,23 @@ static inline int arm_gic_ppi_index(int cpu_nr, int ppi_index) return XLNX_ZYNQMP_GIC_NUM_SPI_INTR + cpu_nr * GIC_INTERNAL + ppi_index; } +static unsigned int xlnx_zynqmp_get_rpu_number(MachineState *ms) +{ + /* + * RPUs will be created only if "-smp" is higher than the maximum + * of APUs. Round it up to 0 to avoid dealing with negative values. + */ + return MAX(0, MIN((int)(ms->smp.cpus - XLNX_ZYNQMP_NUM_APU_CPUS), + XLNX_ZYNQMP_NUM_RPU_CPUS)); +} + static void xlnx_zynqmp_create_rpu(MachineState *ms, XlnxZynqMPState *s, const char *boot_cpu, Error **errp) { int i; - int num_rpus = MIN((int)(ms->smp.cpus - XLNX_ZYNQMP_NUM_APU_CPUS), - XLNX_ZYNQMP_NUM_RPU_CPUS); + int num_rpus = xlnx_zynqmp_get_rpu_number(ms); - if (num_rpus <= 0) { + if (!num_rpus) { /* Don't create rpu-cluster object if there's nothing to put in it */ return; } From e836211f7df4b7102f571a33bf01722c9b9bd8b8 Mon Sep 17 00:00:00 2001 From: Frederic Konrad Date: Tue, 30 Sep 2025 13:57:18 +0200 Subject: [PATCH 1383/1794] hw/arm/xlnx-zynqmp: wire a second GIC for the Cortex-R5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This wires a second GIC for the Cortex-R5, all the IRQs are split when there is an RPU instanciated. Signed-off-by: Clément Chigot Acked-by: Edgar E. Iglesias Message-id: 20250930115718.437100-4-chigot@adacore.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-zynqmp.c | 77 +++++++++++++++++++++++++++++++++++- include/hw/arm/xlnx-zynqmp.h | 4 ++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 3d8c46986ebc0..ffed6e5126ed0 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -384,6 +384,7 @@ static void xlnx_zynqmp_init(Object *obj) XlnxZynqMPState *s = XLNX_ZYNQMP(obj); int i; int num_apus = MIN(ms->smp.cpus, XLNX_ZYNQMP_NUM_APU_CPUS); + int num_rpus = xlnx_zynqmp_get_rpu_number(ms); object_initialize_child(obj, "apu-cluster", &s->apu_cluster, TYPE_CPU_CLUSTER); @@ -397,6 +398,12 @@ static void xlnx_zynqmp_init(Object *obj) object_initialize_child(obj, "gic", &s->gic, gic_class_name()); + if (num_rpus) { + /* Do not create the rpu_gic if we don't have rpus */ + object_initialize_child(obj, "rpu_gic", &s->rpu_gic, + gic_class_name()); + } + for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) { object_initialize_child(obj, "gem[*]", &s->gem[i], TYPE_CADENCE_GEM); object_initialize_child(obj, "gem-irq-orgate[*]", @@ -446,6 +453,15 @@ static void xlnx_zynqmp_init(Object *obj) object_initialize_child(obj, "qspi-irq-orgate", &s->qspi_irq_orgate, TYPE_OR_IRQ); + if (num_rpus) { + for (i = 0; i < ARRAY_SIZE(s->splitter); i++) { + g_autofree char *name = g_strdup_printf("irq-splitter%d", i); + object_initialize_child(obj, name, &s->splitter[i], TYPE_SPLIT_IRQ); + } + } + + + for (i = 0; i < XLNX_ZYNQMP_NUM_USB; i++) { object_initialize_child(obj, "usb[*]", &s->usb[i], TYPE_USB_DWC3); } @@ -459,6 +475,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) uint8_t i; uint64_t ram_size; int num_apus = MIN(ms->smp.cpus, XLNX_ZYNQMP_NUM_APU_CPUS); + int num_rpus = xlnx_zynqmp_get_rpu_number(ms); const char *boot_cpu = s->boot_cpu ? s->boot_cpu : "apu-cpu[0]"; ram_addr_t ddr_low_size, ddr_high_size; qemu_irq gic_spi[XLNX_ZYNQMP_GIC_NUM_SPI_INTR]; @@ -517,6 +534,14 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) qdev_prop_set_bit(DEVICE(&s->gic), "has-virtualization-extensions", s->virt); + if (num_rpus) { + qdev_prop_set_uint32(DEVICE(&s->rpu_gic), "num-irq", + XLNX_ZYNQMP_GIC_NUM_SPI_INTR + 32); + qdev_prop_set_uint32(DEVICE(&s->rpu_gic), "revision", 1); + qdev_prop_set_uint32(DEVICE(&s->rpu_gic), "num-cpu", num_rpus); + qdev_prop_set_uint32(DEVICE(&s->rpu_gic), "first-cpu-index", 4); + } + qdev_realize(DEVICE(&s->apu_cluster), NULL, &error_fatal); /* Realize APUs before realizing the GIC. KVM requires this. */ @@ -616,13 +641,63 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) return; } + if (num_rpus) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s->rpu_gic), errp)) { + return; + } + + for (i = 0; i < num_rpus; i++) { + qemu_irq irq; + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->rpu_gic), i + 1, + GIC_BASE_ADDR + i * 0x1000); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rpu_gic), i, + qdev_get_gpio_in(DEVICE(&s->rpu_cpu[i]), + ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rpu_gic), i + num_rpus, + qdev_get_gpio_in(DEVICE(&s->rpu_cpu[i]), + ARM_CPU_FIQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rpu_gic), i + num_rpus * 2, + qdev_get_gpio_in(DEVICE(&s->rpu_cpu[i]), + ARM_CPU_VIRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rpu_gic), i + num_rpus * 3, + qdev_get_gpio_in(DEVICE(&s->rpu_cpu[i]), + ARM_CPU_VFIQ)); + irq = qdev_get_gpio_in(DEVICE(&s->rpu_gic), + arm_gic_ppi_index(i, ARM_PHYS_TIMER_PPI)); + qdev_connect_gpio_out(DEVICE(&s->rpu_cpu[i]), GTIMER_PHYS, irq); + irq = qdev_get_gpio_in(DEVICE(&s->rpu_gic), + arm_gic_ppi_index(i, ARM_VIRT_TIMER_PPI)); + qdev_connect_gpio_out(DEVICE(&s->rpu_cpu[i]), GTIMER_VIRT, irq); + irq = qdev_get_gpio_in(DEVICE(&s->rpu_gic), + arm_gic_ppi_index(i, ARM_HYP_TIMER_PPI)); + qdev_connect_gpio_out(DEVICE(&s->rpu_cpu[i]), GTIMER_HYP, irq); + irq = qdev_get_gpio_in(DEVICE(&s->rpu_gic), + arm_gic_ppi_index(i, ARM_SEC_TIMER_PPI)); + qdev_connect_gpio_out(DEVICE(&s->rpu_cpu[i]), GTIMER_SEC, irq); + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->rpu_gic), 0, GIC_BASE_ADDR); + } + if (!s->boot_cpu_ptr) { error_setg(errp, "ZynqMP Boot cpu %s not found", boot_cpu); return; } for (i = 0; i < XLNX_ZYNQMP_GIC_NUM_SPI_INTR; i++) { - gic_spi[i] = qdev_get_gpio_in(DEVICE(&s->gic), i); + if (num_rpus) { + DeviceState *splitter = DEVICE(&s->splitter[i]); + qdev_prop_set_uint16(splitter, "num-lines", 2); + qdev_realize(splitter, NULL, &error_abort); + gic_spi[i] = qdev_get_gpio_in(splitter, 0); + qdev_connect_gpio_out(splitter, 0, + qdev_get_gpio_in(DEVICE(&s->gic), i)); + qdev_connect_gpio_out(splitter, 1, + qdev_get_gpio_in(DEVICE(&s->rpu_gic), i)); + } else { + gic_spi[i] = qdev_get_gpio_in(DEVICE(&s->gic), i); + } } for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) { diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index 6a407c2962482..a3117bd6c50b3 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -42,6 +42,7 @@ #include "hw/misc/xlnx-zynqmp-crf.h" #include "hw/timer/cadence_ttc.h" #include "hw/usb/hcd-dwc3.h" +#include "hw/core/split-irq.h" #define TYPE_XLNX_ZYNQMP "xlnx-zynqmp" OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP) @@ -106,6 +107,9 @@ struct XlnxZynqMPState { GICState gic; MemoryRegion gic_mr[XLNX_ZYNQMP_GIC_REGIONS][XLNX_ZYNQMP_GIC_ALIASES]; + GICState rpu_gic; + SplitIRQ splitter[XLNX_ZYNQMP_GIC_NUM_SPI_INTR]; + MemoryRegion ocm_ram[XLNX_ZYNQMP_NUM_OCM_BANKS]; MemoryRegion *ddr_ram; From cedbe3b24a875c2abd84d1925abf3d288f753e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Oct 2025 10:40:47 +0200 Subject: [PATCH 1384/1794] hw/arm: Remove sl_bootparam_write() and 'hw/arm/sharpsl.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When removing the spitz and tosa board, commit b62151489ae ("hw/arm: Remove deprecated akita, borzoi spitz, terrier, tosa boards") removed the last calls to sl_bootparam_write(). Remove it, along with the "hw/arm/sharpsl.h" header. Signed-off-by: Philippe Mathieu-Daudé Message-id: 20251001084047.67423-1-philmd@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- MAINTAINERS | 1 - hw/gpio/zaurus.c | 42 ---------------------------------------- include/hw/arm/sharpsl.h | 17 ---------------- 3 files changed, 60 deletions(-) delete mode 100644 include/hw/arm/sharpsl.h diff --git a/MAINTAINERS b/MAINTAINERS index 75e1fa5c307ea..bb7fa76c324cb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1006,7 +1006,6 @@ S: Odd Fixes F: hw/arm/collie.c F: hw/arm/strongarm* F: hw/gpio/zaurus.c -F: include/hw/arm/sharpsl.h F: docs/system/arm/collie.rst F: tests/functional/arm/test_collie.py diff --git a/hw/gpio/zaurus.c b/hw/gpio/zaurus.c index b8d27f5973839..590ffde89d129 100644 --- a/hw/gpio/zaurus.c +++ b/hw/gpio/zaurus.c @@ -18,7 +18,6 @@ #include "qemu/osdep.h" #include "hw/irq.h" -#include "hw/arm/sharpsl.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/module.h" @@ -265,44 +264,3 @@ static void scoop_register_types(void) } type_init(scoop_register_types) - -/* Write the bootloader parameters memory area. */ - -#define MAGIC_CHG(a, b, c, d) ((d << 24) | (c << 16) | (b << 8) | a) - -static struct QEMU_PACKED sl_param_info { - uint32_t comadj_keyword; - int32_t comadj; - - uint32_t uuid_keyword; - char uuid[16]; - - uint32_t touch_keyword; - int32_t touch_xp; - int32_t touch_yp; - int32_t touch_xd; - int32_t touch_yd; - - uint32_t adadj_keyword; - int32_t adadj; - - uint32_t phad_keyword; - int32_t phadadj; -} zaurus_bootparam = { - .comadj_keyword = MAGIC_CHG('C', 'M', 'A', 'D'), - .comadj = 125, - .uuid_keyword = MAGIC_CHG('U', 'U', 'I', 'D'), - .uuid = { -1 }, - .touch_keyword = MAGIC_CHG('T', 'U', 'C', 'H'), - .touch_xp = -1, - .adadj_keyword = MAGIC_CHG('B', 'V', 'A', 'D'), - .adadj = -1, - .phad_keyword = MAGIC_CHG('P', 'H', 'A', 'D'), - .phadadj = 0x01, -}; - -void sl_bootparam_write(hwaddr ptr) -{ - cpu_physical_memory_write(ptr, &zaurus_bootparam, - sizeof(struct sl_param_info)); -} diff --git a/include/hw/arm/sharpsl.h b/include/hw/arm/sharpsl.h deleted file mode 100644 index 1e3992fcd0014..0000000000000 --- a/include/hw/arm/sharpsl.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Common declarations for the Zaurii. - * - * This file is licensed under the GNU GPL. - */ - -#ifndef QEMU_SHARPSL_H -#define QEMU_SHARPSL_H - -#include "exec/hwaddr.h" - -/* zaurus.c */ - -#define SL_PXA_PARAM_BASE 0xa0000a00 -void sl_bootparam_write(hwaddr ptr); - -#endif From 0407192ae6a06d649d693a9be5e864243e6de702 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 25 Sep 2025 17:11:25 -0700 Subject: [PATCH 1385/1794] target/arm: Add isar feature test for FEAT_RME_GPC2 Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-id: 20250926001134.295547-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 602f6a88e532a..f59c18b6ef6cd 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -1091,6 +1091,11 @@ static inline bool isar_feature_aa64_rme(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64PFR0, RME) != 0; } +static inline bool isar_feature_aa64_rme_gpc2(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64PFR0, RME) >= 2; +} + static inline bool isar_feature_aa64_dit(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64PFR0, DIT) != 0; From e2c25b123d48a9bb2d723f19a595914690e348b0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 25 Sep 2025 17:11:26 -0700 Subject: [PATCH 1386/1794] target/arm: Add GPCCR fields from ARM revision L.b Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-id: 20250926001134.295547-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 2b9585dc80a63..41414ac22b87b 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1995,13 +1995,19 @@ FIELD(V7M_VPR, MASK01, 16, 4) FIELD(V7M_VPR, MASK23, 20, 4) FIELD(GPCCR, PPS, 0, 3) +FIELD(GPCCR, RLPAD, 5, 1) +FIELD(GPCCR, NSPAD, 6, 1) +FIELD(GPCCR, SPAD, 7, 1) FIELD(GPCCR, IRGN, 8, 2) FIELD(GPCCR, ORGN, 10, 2) FIELD(GPCCR, SH, 12, 2) FIELD(GPCCR, PGS, 14, 2) FIELD(GPCCR, GPC, 16, 1) FIELD(GPCCR, GPCP, 17, 1) +FIELD(GPCCR, TBGPCD, 18, 1) +FIELD(GPCCR, NSO, 19, 1) FIELD(GPCCR, L0GPTSZ, 20, 4) +FIELD(GPCCR, APPSAA, 24, 1) FIELD(MFAR, FPA, 12, 40) FIELD(MFAR, NSE, 62, 1) From af95e2aaa0b00615728834a79de5c827158b3d9a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 25 Sep 2025 17:11:27 -0700 Subject: [PATCH 1387/1794] target/arm: Enable FEAT_RME_GPC2 bits in gpccr_write Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-id: 20250926001134.295547-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index 792a47a9c50dc..b7bf45afc13c0 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4933,6 +4933,11 @@ static void gpccr_write(CPUARMState *env, const ARMCPRegInfo *ri, R_GPCCR_ORGN_MASK | R_GPCCR_SH_MASK | R_GPCCR_PGS_MASK | R_GPCCR_GPC_MASK | R_GPCCR_GPCP_MASK; + if (cpu_isar_feature(aa64_rme_gpc2, env_archcpu(env))) { + rw_mask |= R_GPCCR_APPSAA_MASK | R_GPCCR_NSO_MASK | + R_GPCCR_SPAD_MASK | R_GPCCR_NSPAD_MASK | R_GPCCR_RLPAD_MASK; + } + env->cp15.gpccr_el3 = (value & rw_mask) | (env->cp15.gpccr_el3 & ~rw_mask); } From bd08319bb52cd009f86e9a04af8810f6aa540d8c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 25 Sep 2025 17:11:28 -0700 Subject: [PATCH 1388/1794] target/arm: Add cur_space to S1Translate We've been updating in_space and then using hacks to access the original space. Instead, update cur_space and leave in_space unchanged. Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-id: 20250926001134.295547-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 6344971fa6425..1cafe8f4f7bd6 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -36,8 +36,6 @@ typedef struct S1Translate { /* * in_space: the security space for this walk. This plus * the in_mmu_idx specify the architectural translation regime. - * If a Secure ptw is "downgraded" to NonSecure by an NSTable bit, - * this field is updated accordingly. * * Note that the security space for the in_ptw_idx may be different * from that for the in_mmu_idx. We do not need to explicitly track @@ -52,6 +50,11 @@ typedef struct S1Translate { * value being Stage2 vs Stage2_S distinguishes those. */ ARMSecuritySpace in_space; + /* + * Like in_space, except this may be "downgraded" to NonSecure + * by an NSTable bit. + */ + ARMSecuritySpace cur_space; /* * in_debug: is this a QEMU debug access (gdbstub, etc)? Debug * accesses will not update the guest page table access flags @@ -587,7 +590,8 @@ static bool S1_ptw_translate(CPUARMState *env, S1Translate *ptw, * From gdbstub, do not use softmmu so that we don't modify the * state of the cpu at all, including softmmu tlb contents. */ - ARMSecuritySpace s2_space = S2_security_space(ptw->in_space, s2_mmu_idx); + ARMSecuritySpace s2_space + = S2_security_space(ptw->cur_space, s2_mmu_idx); S1Translate s2ptw = { .in_mmu_idx = s2_mmu_idx, .in_ptw_idx = ptw_idx_for_stage_2(env, s2_mmu_idx), @@ -630,7 +634,7 @@ static bool S1_ptw_translate(CPUARMState *env, S1Translate *ptw, } if (regime_is_stage2(s2_mmu_idx)) { - uint64_t hcr = arm_hcr_el2_eff_secstate(env, ptw->in_space); + uint64_t hcr = arm_hcr_el2_eff_secstate(env, ptw->cur_space); if ((hcr & HCR_PTW) && S2_attrs_are_device(hcr, pte_attrs)) { /* @@ -641,7 +645,7 @@ static bool S1_ptw_translate(CPUARMState *env, S1Translate *ptw, fi->s2addr = addr; fi->stage2 = true; fi->s1ptw = true; - fi->s1ns = fault_s1ns(ptw->in_space, s2_mmu_idx); + fi->s1ns = fault_s1ns(ptw->cur_space, s2_mmu_idx); return false; } } @@ -657,7 +661,7 @@ static bool S1_ptw_translate(CPUARMState *env, S1Translate *ptw, fi->s2addr = addr; fi->stage2 = regime_is_stage2(s2_mmu_idx); fi->s1ptw = fi->stage2; - fi->s1ns = fault_s1ns(ptw->in_space, s2_mmu_idx); + fi->s1ns = fault_s1ns(ptw->cur_space, s2_mmu_idx); return false; } @@ -844,7 +848,7 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val, fi->s2addr = ptw->out_virt; fi->stage2 = true; fi->s1ptw = true; - fi->s1ns = fault_s1ns(ptw->in_space, ptw->in_ptw_idx); + fi->s1ns = fault_s1ns(ptw->cur_space, ptw->in_ptw_idx); return 0; } @@ -1224,7 +1228,7 @@ static bool get_phys_addr_v6(CPUARMState *env, S1Translate *ptw, g_assert_not_reached(); } } - out_space = ptw->in_space; + out_space = ptw->cur_space; if (ns) { /* * The NS bit will (as required by the architecture) have no effect if @@ -1254,7 +1258,7 @@ static bool get_phys_addr_v6(CPUARMState *env, S1Translate *ptw, } result->f.prot = get_S1prot(env, mmu_idx, false, user_rw, prot_rw, - xn, pxn, result->f.attrs.space, out_space); + xn, pxn, ptw->in_space, out_space); if (ptw->in_prot_check & ~result->f.prot) { /* Access permission fault. */ fi->type = ARMFault_Permission; @@ -1857,7 +1861,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, * NonSecure. With RME, the EL3 translation regime does not change * from Root to NonSecure. */ - if (ptw->in_space == ARMSS_Secure + if (ptw->cur_space == ARMSS_Secure && !regime_is_stage2(mmu_idx) && extract32(tableattrs, 4, 1)) { /* @@ -1867,7 +1871,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, QEMU_BUILD_BUG_ON(ARMMMUIdx_Phys_S + 1 != ARMMMUIdx_Phys_NS); QEMU_BUILD_BUG_ON(ARMMMUIdx_Stage2_S + 1 != ARMMMUIdx_Stage2); ptw->in_ptw_idx += 1; - ptw->in_space = ARMSS_NonSecure; + ptw->cur_space = ARMSS_NonSecure; } if (!S1_ptw_translate(env, ptw, descaddr, fi)) { @@ -1991,7 +1995,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, } ap = extract32(attrs, 6, 2); - out_space = ptw->in_space; + out_space = ptw->cur_space; if (regime_is_stage2(mmu_idx)) { /* * R_GYNXY: For stage2 in Realm security state, bit 55 is NS. @@ -2089,12 +2093,8 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, user_rw = simple_ap_to_rw_prot_is_user(ap, true); prot_rw = simple_ap_to_rw_prot_is_user(ap, false); - /* - * Note that we modified ptw->in_space earlier for NSTable, but - * result->f.attrs retains a copy of the original security space. - */ result->f.prot = get_S1prot(env, mmu_idx, aarch64, user_rw, prot_rw, - xn, pxn, result->f.attrs.space, out_space); + xn, pxn, ptw->in_space, out_space); /* Index into MAIR registers for cache attributes */ attrindx = extract32(attrs, 2, 3); @@ -2192,7 +2192,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, fi->level = level; fi->stage2 = regime_is_stage2(mmu_idx); } - fi->s1ns = fault_s1ns(ptw->in_space, mmu_idx); + fi->s1ns = fault_s1ns(ptw->cur_space, mmu_idx); return true; } @@ -3413,6 +3413,7 @@ static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw, * cannot upgrade a NonSecure translation regime's attributes * to Secure or Realm. */ + ptw->cur_space = ptw->in_space; result->f.attrs.space = ptw->in_space; result->f.attrs.secure = arm_space_is_secure(ptw->in_space); From 11dee1cb2f075185ed93b38ad964995290630041 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 25 Sep 2025 17:11:29 -0700 Subject: [PATCH 1389/1794] target/arm: GPT_Secure is reserved without FEAT_SEL2 For GPT_Secure, if SEL2 is not enabled, raise a GPCF_Walk exception. Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-id: 20250926001134.295547-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 1cafe8f4f7bd6..3df5d4da12f1d 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -478,10 +478,14 @@ static bool granule_protection_check(CPUARMState *env, uint64_t paddress, break; case 0b1111: /* all access */ return true; - case 0b1000: - case 0b1001: - case 0b1010: - case 0b1011: + case 0b1000: /* secure */ + if (!cpu_isar_feature(aa64_sel2, cpu)) { + goto fault_walk; + } + /* fall through */ + case 0b1001: /* non-secure */ + case 0b1010: /* root */ + case 0b1011: /* realm */ if (pspace == (gpi & 3)) { return true; } From ee45f4b4e9e58be886bb48b31ca58c2273f3630a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 25 Sep 2025 17:11:30 -0700 Subject: [PATCH 1390/1794] target/arm: Implement GPT_NonSecureOnly Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-id: 20250926001134.295547-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 3df5d4da12f1d..56a3cd8fa0ffa 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -318,6 +318,7 @@ static bool regime_translation_disabled(CPUARMState *env, ARMMMUIdx mmu_idx, static bool granule_protection_check(CPUARMState *env, uint64_t paddress, ARMSecuritySpace pspace, + ARMSecuritySpace ss, ARMMMUFaultInfo *fi) { MemTxAttrs attrs = { @@ -490,6 +491,13 @@ static bool granule_protection_check(CPUARMState *env, uint64_t paddress, return true; } break; + case 0b1101: /* non-secure only */ + /* aa64_rme_gpc2 was checked in gpccr_write */ + if (FIELD_EX64(gpccr, GPCCR, NSO)) { + return (pspace == ARMSS_NonSecure && + (ss == ARMSS_NonSecure || ss == ARMSS_Root)); + } + goto fault_walk; default: goto fault_walk; /* reserved */ } @@ -3553,7 +3561,7 @@ static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw, return true; } if (!granule_protection_check(env, result->f.phys_addr, - result->f.attrs.space, fi)) { + result->f.attrs.space, ptw->in_space, fi)) { fi->type = ARMFault_GPCFOnOutput; return true; } From 59bcd13b64bf54b35acac8a364b569964d7e0461 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 25 Sep 2025 17:11:31 -0700 Subject: [PATCH 1391/1794] target/arm: Implement SPAD, NSPAD, RLPAD These bits disable all access to a particular address space. Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-id: 20250926001134.295547-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 56a3cd8fa0ffa..36917be83e3fe 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -387,7 +387,25 @@ static bool granule_protection_check(CPUARMState *env, uint64_t paddress, l0gptsz = 30 + FIELD_EX64(gpccr, GPCCR, L0GPTSZ); /* - * GPC Priority 2: Secure, Realm or Root address exceeds PPS. + * GPC Priority 2: Access to Secure, NonSecure or Realm is prevented + * by one of the GPCCR_EL3 address space disable bits (R_TCWMD). + * All of these bits are checked vs aa64_rme_gpc2 in gpccr_write. + */ + { + static const uint8_t disable_masks[4] = { + [ARMSS_Secure] = R_GPCCR_SPAD_MASK, + [ARMSS_NonSecure] = R_GPCCR_NSPAD_MASK, + [ARMSS_Root] = 0, + [ARMSS_Realm] = R_GPCCR_RLPAD_MASK, + }; + + if (gpccr & disable_masks[pspace]) { + goto fault_fail; + } + } + + /* + * GPC Priority 3: Secure, Realm or Root address exceeds PPS. * R_CPDSB: A NonSecure physical address input exceeding PPS * does not experience any fault. */ @@ -398,7 +416,7 @@ static bool granule_protection_check(CPUARMState *env, uint64_t paddress, goto fault_size; } - /* GPC Priority 3: the base address of GPTBR_EL3 exceeds PPS. */ + /* GPC Priority 4: the base address of GPTBR_EL3 exceeds PPS. */ tableaddr = env->cp15.gptbr_el3 << 12; if (tableaddr & ~pps_mask) { goto fault_size; @@ -502,6 +520,7 @@ static bool granule_protection_check(CPUARMState *env, uint64_t paddress, goto fault_walk; /* reserved */ } + fault_fail: fi->gpcf = GPCF_Fail; goto fault_common; fault_eabt: From a0f1bb0dfeb2c8cd806349aa5938d9aab6ef1d61 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 25 Sep 2025 17:11:32 -0700 Subject: [PATCH 1392/1794] target/arm: Fix GPT fault type for address outside PPS The GPT address size fault is for the table itself. The physical address being checked gets Granule protection fault at Level 0 (R_JFFHB). Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-id: 20250926001134.295547-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 36917be83e3fe..236c3a9569b2f 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -413,7 +413,7 @@ static bool granule_protection_check(CPUARMState *env, uint64_t paddress, if (pspace == ARMSS_NonSecure) { return true; } - goto fault_size; + goto fault_fail; } /* GPC Priority 4: the base address of GPTBR_EL3 exceeds PPS. */ From 663f9c253eb31d4deacacf5b9de70bfa4674371f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 25 Sep 2025 17:11:33 -0700 Subject: [PATCH 1393/1794] target/arm: Implement APPSAA This bit allows all spaces to access memory above PPS. Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-id: 20250926001134.295547-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 236c3a9569b2f..e03657f309e94 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -408,9 +408,10 @@ static bool granule_protection_check(CPUARMState *env, uint64_t paddress, * GPC Priority 3: Secure, Realm or Root address exceeds PPS. * R_CPDSB: A NonSecure physical address input exceeding PPS * does not experience any fault. + * R_PBPSH: Other address spaces have fault suppressed by APPSAA. */ if (paddress & ~pps_mask) { - if (pspace == ARMSS_NonSecure) { + if (pspace == ARMSS_NonSecure || FIELD_EX64(gpccr, GPCCR, APPSAA)) { return true; } goto fault_fail; From 932cac41ca633f24f192a69770bf91b55c4d27bb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 25 Sep 2025 17:11:34 -0700 Subject: [PATCH 1394/1794] target/arm: Enable FEAT_RME_GPC2 for -cpu max with x-rme Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-id: 20250926001134.295547-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + target/arm/tcg/cpu64.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 6b04c96c8c466..1aa0a6e4c3915 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -120,6 +120,7 @@ the following architecture extensions: - FEAT_RASv1p1 (RAS Extension v1.1) - FEAT_RDM (Advanced SIMD rounding double multiply accumulate instructions) - FEAT_RME (Realm Management Extension) (NB: support status in QEMU is experimental) +- FEAT_RME_GPC2 (RME Granule Protection Check 2 Extension) - FEAT_RNG (Random number generator) - FEAT_RPRES (Increased precision of FRECPE and FRSQRTE) - FEAT_S2FWB (Stage 2 forced Write-Back) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 90b6c0ebb0e62..8c617fe37b2f6 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -159,7 +159,8 @@ static void cpu_arm_set_rme(Object *obj, bool value, Error **errp) { ARMCPU *cpu = ARM_CPU(obj); - FIELD_DP64_IDREG(&cpu->isar, ID_AA64PFR0, RME, value); + /* Enable FEAT_RME_GPC2 */ + FIELD_DP64_IDREG(&cpu->isar, ID_AA64PFR0, RME, value ? 2 : 0); } static void cpu_max_set_l0gptsz(Object *obj, Visitor *v, const char *name, From 2438f18ee01108ead65be81be056e5a5da69120c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 7 Oct 2025 17:45:58 +0400 Subject: [PATCH 1395/1794] build-sys: default to host vendor for rust target triple MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes docker-test@alpine, which uses "alpine" vendor. Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20251007134558.251670-1-marcandre.lureau@redhat.com Signed-off-by: Paolo Bonzini --- configure | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/configure b/configure index 78445cbb4b374..a467f3a2e05a1 100755 --- a/configure +++ b/configure @@ -1216,8 +1216,9 @@ fi if test "$rust" != disabled && test -z "$rust_target_triple"; then # arch and os generally matches between meson and rust rust_arch=$host_arch + # default to host vendor + rust_vendor=$(echo "$rust_host_triple" | cut -d'-' -f2) rust_os=$host_os - rust_machine=unknown rust_osvariant= # tweak rust_os if needed; also, machine and variant depend on the OS @@ -1225,7 +1226,7 @@ if test "$rust" != disabled && test -z "$rust_target_triple"; then case "$host_os" in darwin) # e.g. aarch64-apple-darwin - rust_machine=apple + rust_vendor=apple ;; linux) @@ -1273,13 +1274,13 @@ EOF ;; sunos) - rust_machine=pc + rust_vendor=pc rust_os=solaris ;; windows) # e.g. aarch64-pc-windows-gnullvm, x86_64-pc-windows-gnu (MSVC not supported) - rust_machine=pc + rust_vendor=pc if test "$host_arch" = aarch64; then rust_osvariant=gnullvm else @@ -1310,7 +1311,7 @@ EOF sparc64) if test "$rust_os" = solaris; then rust_arch=sparcv9 - rust_machine=sun + rust_vendor=sun fi ;; @@ -1324,7 +1325,7 @@ EOF # e.g. aarch64-linux-android rust_target_triple=$rust_arch-$rust_os-$rust_osvariant else - rust_target_triple=$rust_arch-$rust_machine-$rust_os${rust_osvariant:+-$rust_osvariant} + rust_target_triple=$rust_arch-$rust_vendor-$rust_os${rust_osvariant:+-$rust_osvariant} fi fi From e9efa4a77168ac2816bf9471f878252ce6224710 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 23 Sep 2025 12:22:54 +0200 Subject: [PATCH 1396/1794] target/i386: add compatibility property for arch_capabilities Prior to v10.1, if requested by user, arch-capabilities is always on despite the fact that CPUID advertises it to be off/unvailable. This causes a migration issue for VMs that are run on a machine without arch-capabilities and expect this feature to be present on the destination host with QEMU 10.1. Add a compatibility property to restore the legacy behavior for all machines with version prior to 10.1. To preserve the functionality (added by 10.1) of turning off ARCH_CAPABILITIES where Windows does not like it, use directly the guest CPU vendor: x86_cpu_get_supported_feature_word is not KVM-specific and therefore should not necessarily use the host CPUID. Co-authored-by: Hector Cao Signed-off-by: Hector Cao Fixes: d3a24134e37 ("target/i386: do not expose ARCH_CAPABILITIES on AMD CPU", 2025-07-17) Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 1 + target/i386/cpu.c | 17 +++++++++++++++++ target/i386/cpu.h | 6 ++++++ target/i386/kvm/kvm.c | 6 +----- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index bc048a6d13743..d7f48150fdd43 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -87,6 +87,7 @@ const size_t pc_compat_10_1_len = G_N_ELEMENTS(pc_compat_10_1); GlobalProperty pc_compat_10_0[] = { { TYPE_X86_CPU, "x-consistent-cache", "false" }, { TYPE_X86_CPU, "x-vendor-cpuid-only-v2", "false" }, + { TYPE_X86_CPU, "x-arch-cap-always-on", "true" }, }; const size_t pc_compat_10_0_len = G_N_ELEMENTS(pc_compat_10_0); diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 6d85149e6e1b5..fe369bb128486 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7539,6 +7539,20 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w) #endif break; + case FEAT_7_0_EDX: + /* + * Windows does not like ARCH_CAPABILITIES on AMD machines at all. + * Do not show the fake ARCH_CAPABILITIES MSR that KVM sets up, + * except if needed for migration. + * + * When arch_cap_always_on is removed, this tweak can move to + * kvm_arch_get_supported_cpuid. + */ + if (cpu && IS_AMD_CPU(&cpu->env) && !cpu->arch_cap_always_on) { + unavail = CPUID_7_0_EDX_ARCH_CAPABILITIES; + } + break; + default: break; } @@ -10004,6 +10018,9 @@ static const Property x86_cpu_properties[] = { true), DEFINE_PROP_BOOL("x-l1-cache-per-thread", X86CPU, l1_cache_per_core, true), DEFINE_PROP_BOOL("x-force-cpuid-0x1f", X86CPU, force_cpuid_0x1f, false), + + DEFINE_PROP_BOOL("x-arch-cap-always-on", X86CPU, + arch_cap_always_on, false), }; #ifndef CONFIG_USER_ONLY diff --git a/target/i386/cpu.h b/target/i386/cpu.h index e0be7a7406850..414ca968e8490 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2314,6 +2314,12 @@ struct ArchCPU { /* Forcefully disable KVM PV features not exposed in guest CPUIDs */ bool kvm_pv_enforce_cpuid; + /* + * Expose arch-capabilities unconditionally even on AMD models, for backwards + * compatibility with QEMU <10.1. + */ + bool arch_cap_always_on; + /* Number of physical address bits supported */ uint32_t phys_bits; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 6a3a1c1ed8ea8..db40caa3412f9 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -503,12 +503,8 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, * Linux v4.17-v4.20 incorrectly return ARCH_CAPABILITIES on SVM hosts. * We can detect the bug by checking if MSR_IA32_ARCH_CAPABILITIES is * returned by KVM_GET_MSR_INDEX_LIST. - * - * But also, because Windows does not like ARCH_CAPABILITIES on AMD - * mcahines at all, do not show the fake ARCH_CAPABILITIES MSR that - * KVM sets up. */ - if (!has_msr_arch_capabs || !(edx & CPUID_7_0_EDX_ARCH_CAPABILITIES)) { + if (!has_msr_arch_capabs) { ret &= ~CPUID_7_0_EDX_ARCH_CAPABILITIES; } } else if (function == 7 && index == 1 && reg == R_EAX) { From 6529f31e0dccadb532c80b36e3efe7aef83f9cad Mon Sep 17 00:00:00 2001 From: Hector Cao Date: Tue, 23 Sep 2025 12:16:41 +0200 Subject: [PATCH 1397/1794] target/i386: add compatibility property for pdcm feature The pdcm feature is supposed to be disabled when PMU is not available. Up until v10.1, pdcm feature is enabled even when PMU is off. This behavior has been fixed but this change breaks the migration of VMs that are run with QEMU < 10.0 and expect the pdcm feature to be enabled on the destination host. This commit restores the legacy behavior for machines with version prior to 10.1 to allow the migration from older QEMU to QEMU 10.1. Signed-off-by: Hector Cao Link: https://lore.kernel.org/r/20250910115733.21149-3-hector.cao@canonical.com Fixes: e68ec298090 ("i386/cpu: Move adjustment of CPUID_EXT_PDCM before feature_dependencies[] check", 2025-06-20) [Move property from migration object to CPU. - Paolo] Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 1 + target/i386/cpu.c | 15 ++++++++++++--- target/i386/cpu.h | 6 ++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index d7f48150fdd43..4668918746ecb 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -88,6 +88,7 @@ GlobalProperty pc_compat_10_0[] = { { TYPE_X86_CPU, "x-consistent-cache", "false" }, { TYPE_X86_CPU, "x-vendor-cpuid-only-v2", "false" }, { TYPE_X86_CPU, "x-arch-cap-always-on", "true" }, + { TYPE_X86_CPU, "x-pdcm-on-even-without-pmu", "true" }, }; const size_t pc_compat_10_0_len = G_N_ELEMENTS(pc_compat_10_0); diff --git a/target/i386/cpu.c b/target/i386/cpu.c index fe369bb128486..ab18de894e40a 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7908,6 +7908,11 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, /* Fixup overflow: max value for bits 23-16 is 255. */ *ebx |= MIN(num, 255) << 16; } + if (cpu->pdcm_on_even_without_pmu) { + if (!cpu->enable_pmu) { + *ecx &= ~CPUID_EXT_PDCM; + } + } break; case 2: { /* cache info: needed for Pentium Pro compatibility */ const CPUCaches *caches; @@ -8958,9 +8963,11 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) } } - /* PDCM is fixed1 bit for TDX */ - if (!cpu->enable_pmu && !is_tdx_vm()) { - env->features[FEAT_1_ECX] &= ~CPUID_EXT_PDCM; + if (!cpu->pdcm_on_even_without_pmu) { + /* PDCM is fixed1 bit for TDX */ + if (!cpu->enable_pmu && !is_tdx_vm()) { + env->features[FEAT_1_ECX] &= ~CPUID_EXT_PDCM; + } } for (i = 0; i < ARRAY_SIZE(feature_dependencies); i++) { @@ -10021,6 +10028,8 @@ static const Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("x-arch-cap-always-on", X86CPU, arch_cap_always_on, false), + DEFINE_PROP_BOOL("x-pdcm-on-even-without-pmu", X86CPU, + pdcm_on_even_without_pmu, false), }; #ifndef CONFIG_USER_ONLY diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 414ca968e8490..42168f1d6d8b8 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2320,6 +2320,12 @@ struct ArchCPU { */ bool arch_cap_always_on; + /* + * Backwards compatibility with QEMU <10.1. The PDCM feature is now disabled when + * PMU is not available, but prior to 10.1 it was enabled even if PMU is off. + */ + bool pdcm_on_even_without_pmu; + /* Number of physical address bits supported */ uint32_t phys_bits; From 37e12da5df8eb74042f11e9e7bec8a50b8090adb Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:21 +0200 Subject: [PATCH 1398/1794] accel: Add Meson and config support for MSHV accelerator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a Meson feature option and default-config entry to allow building QEMU with MSHV (Microsoft Hypervisor) acceleration support. This is the first step toward implementing an MSHV backend in QEMU. Signed-off-by: Magnus Kulke Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/r/20250916164847.77883-2-magnuskulke@linux.microsoft.com [Add error for unavailable accelerator. - Paolo] Signed-off-by: Paolo Bonzini --- accel/Kconfig | 3 +++ meson.build | 13 +++++++++++++ meson_options.txt | 2 ++ scripts/meson-buildoptions.sh | 3 +++ 4 files changed, 21 insertions(+) diff --git a/accel/Kconfig b/accel/Kconfig index 4263cab72272e..a60f1149238bc 100644 --- a/accel/Kconfig +++ b/accel/Kconfig @@ -13,6 +13,9 @@ config TCG config KVM bool +config MSHV + bool + config XEN bool select FSDEV_9P if VIRTFS diff --git a/meson.build b/meson.build index 62766c0f19cc9..167021ed621bd 100644 --- a/meson.build +++ b/meson.build @@ -334,6 +334,7 @@ elif cpu == 'x86_64' 'CONFIG_HVF': ['x86_64-softmmu'], 'CONFIG_NVMM': ['i386-softmmu', 'x86_64-softmmu'], 'CONFIG_WHPX': ['i386-softmmu', 'x86_64-softmmu'], + 'CONFIG_MSHV': ['x86_64-softmmu'], } endif @@ -883,6 +884,14 @@ accelerators = [] if get_option('kvm').allowed() and host_os == 'linux' accelerators += 'CONFIG_KVM' endif + +if get_option('mshv').allowed() and host_os == 'linux' + if get_option('mshv').enabled() and host_machine.cpu() != 'x86_64' + error('mshv accelerator requires x64_64 host') + endif + accelerators += 'CONFIG_MSHV' +endif + if get_option('whpx').allowed() and host_os == 'windows' if get_option('whpx').enabled() and host_machine.cpu() != 'x86_64' error('WHPX requires 64-bit host') @@ -952,6 +961,9 @@ endif if 'CONFIG_WHPX' not in accelerators and get_option('whpx').enabled() error('WHPX not available on this platform') endif +if 'CONFIG_MSHV' not in accelerators and get_option('mshv').enabled() + error('mshv not available on this platform') +endif xen = not_found if get_option('xen').enabled() or (get_option('xen').auto() and have_system) @@ -4827,6 +4839,7 @@ if have_system summary_info += {'HVF support': config_all_accel.has_key('CONFIG_HVF')} summary_info += {'WHPX support': config_all_accel.has_key('CONFIG_WHPX')} summary_info += {'NVMM support': config_all_accel.has_key('CONFIG_NVMM')} + summary_info += {'MSHV support': config_all_accel.has_key('CONFIG_MSHV')} summary_info += {'Xen support': xen.found()} if xen.found() summary_info += {'xen ctrl version': xen.version()} diff --git a/meson_options.txt b/meson_options.txt index fff1521e580de..1be7d61efddb8 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -71,6 +71,8 @@ option('malloc', type : 'combo', choices : ['system', 'tcmalloc', 'jemalloc'], option('kvm', type: 'feature', value: 'auto', description: 'KVM acceleration support') +option('mshv', type: 'feature', value: 'auto', + description: 'MSHV acceleration support') option('whpx', type: 'feature', value: 'auto', description: 'WHPX acceleration support') option('hvf', type: 'feature', value: 'auto', diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 0ebe6bc52a6b9..d3d8f48bbbef8 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -154,6 +154,7 @@ meson_options_help() { printf "%s\n" ' membarrier membarrier system call (for Linux 4.14+ or Windows' printf "%s\n" ' modules modules support (non Windows)' printf "%s\n" ' mpath Multipath persistent reservation passthrough' + printf "%s\n" ' mshv MSHV acceleration support' printf "%s\n" ' multiprocess Out of process device emulation support' printf "%s\n" ' netmap netmap network backend support' printf "%s\n" ' nettle nettle cryptography support' @@ -408,6 +409,8 @@ _meson_option_parse() { --disable-modules) printf "%s" -Dmodules=disabled ;; --enable-mpath) printf "%s" -Dmpath=enabled ;; --disable-mpath) printf "%s" -Dmpath=disabled ;; + --enable-mshv) printf "%s" -Dmshv=enabled ;; + --disable-mshv) printf "%s" -Dmshv=disabled ;; --enable-multiprocess) printf "%s" -Dmultiprocess=enabled ;; --disable-multiprocess) printf "%s" -Dmultiprocess=disabled ;; --enable-netmap) printf "%s" -Dnetmap=enabled ;; From 1e25327b244a217078e3fa5df18322c70932f478 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:22 +0200 Subject: [PATCH 1399/1794] target/i386/emulate: Allow instruction decoding from stream Introduce a new helper function to decode x86 instructions from a raw instruction byte stream. MSHV delivers an instruction stream in a buffer of the vm_exit message. It can be used to speed up MMIO emulation, since instructions do not have to be fetched and translated. Added "fetch_instruction()" op to x86_emul_ops() to improve traceability. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-3-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/emulate/x86_decode.c | 27 +++++++++++++++++++++++---- target/i386/emulate/x86_decode.h | 9 +++++++++ target/i386/emulate/x86_emu.c | 3 ++- target/i386/emulate/x86_emu.h | 2 ++ 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/target/i386/emulate/x86_decode.c b/target/i386/emulate/x86_decode.c index 2eca39802e36d..97bd6f1a3be94 100644 --- a/target/i386/emulate/x86_decode.c +++ b/target/i386/emulate/x86_decode.c @@ -71,10 +71,16 @@ static inline uint64_t decode_bytes(CPUX86State *env, struct x86_decode *decode, VM_PANIC_EX("%s invalid size %d\n", __func__, size); break; } - target_ulong va = linear_rip(env_cpu(env), env->eip) + decode->len; - emul_ops->read_mem(env_cpu(env), &val, va, size); + + /* copy the bytes from the instruction stream, if available */ + if (decode->stream && decode->len + size <= decode->stream->len) { + memcpy(&val, decode->stream->bytes + decode->len, size); + } else { + target_ulong va = linear_rip(env_cpu(env), env->eip) + decode->len; + emul_ops->fetch_instruction(env_cpu(env), &val, va, size); + } decode->len += size; - + return val; } @@ -2076,9 +2082,10 @@ static void decode_opcodes(CPUX86State *env, struct x86_decode *decode) } } -uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode) +static uint32_t decode_opcode(CPUX86State *env, struct x86_decode *decode) { memset(decode, 0, sizeof(*decode)); + decode_prefix(env, decode); set_addressing_size(env, decode); set_operand_size(env, decode); @@ -2088,6 +2095,18 @@ uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode) return decode->len; } +uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode) +{ + return decode_opcode(env, decode); +} + +uint32_t decode_instruction_stream(CPUX86State *env, struct x86_decode *decode, + struct x86_insn_stream *stream) +{ + decode->stream = stream; + return decode_opcode(env, decode); +} + void init_decoder(void) { int i; diff --git a/target/i386/emulate/x86_decode.h b/target/i386/emulate/x86_decode.h index 927645af1a30c..1cadf3694f02b 100644 --- a/target/i386/emulate/x86_decode.h +++ b/target/i386/emulate/x86_decode.h @@ -272,6 +272,11 @@ typedef struct x86_decode_op { }; } x86_decode_op; +typedef struct x86_insn_stream { + const uint8_t *bytes; + size_t len; +} x86_insn_stream; + typedef struct x86_decode { int len; uint8_t opcode[4]; @@ -298,11 +303,15 @@ typedef struct x86_decode { struct x86_modrm modrm; struct x86_decode_op op[4]; bool is_fpu; + + x86_insn_stream *stream; } x86_decode; uint64_t sign(uint64_t val, int size); uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode); +uint32_t decode_instruction_stream(CPUX86State *env, struct x86_decode *decode, + struct x86_insn_stream *stream); void *get_reg_ref(CPUX86State *env, int reg, int rex_present, int is_extended, int size); diff --git a/target/i386/emulate/x86_emu.c b/target/i386/emulate/x86_emu.c index db7a7f7437da7..4409f7bc134bf 100644 --- a/target/i386/emulate/x86_emu.c +++ b/target/i386/emulate/x86_emu.c @@ -1246,7 +1246,8 @@ static void init_cmd_handler(void) bool exec_instruction(CPUX86State *env, struct x86_decode *ins) { if (!_cmd_handler[ins->cmd].handler) { - printf("Unimplemented handler (" TARGET_FMT_lx ") for %d (%x %x) \n", env->eip, + printf("Unimplemented handler (" TARGET_FMT_lx ") for %d (%x %x)\n", + env->eip, ins->cmd, ins->opcode[0], ins->opcode_len > 1 ? ins->opcode[1] : 0); env->eip += ins->len; diff --git a/target/i386/emulate/x86_emu.h b/target/i386/emulate/x86_emu.h index a1a961284b2d4..05686b162f640 100644 --- a/target/i386/emulate/x86_emu.h +++ b/target/i386/emulate/x86_emu.h @@ -24,6 +24,8 @@ #include "cpu.h" struct x86_emul_ops { + void (*fetch_instruction)(CPUState *cpu, void *data, target_ulong addr, + int bytes); void (*read_mem)(CPUState *cpu, void *data, target_ulong addr, int bytes); void (*write_mem)(CPUState *cpu, void *data, target_ulong addr, int bytes); void (*read_segment_descriptor)(CPUState *cpu, struct x86_segment_descriptor *desc, From 0daf817c80b57e58168309420abf0a8a3d2a60f6 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:23 +0200 Subject: [PATCH 1400/1794] target/i386/mshv: Add x86 decoder/emu implementation The MSHV accelerator requires a x86 decoder/emulator in userland to emulate MMIO instructions. This change contains the implementations for the generalized i386 instruction decoder/emulator. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-4-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- include/system/mshv.h | 25 +++ target/i386/cpu.h | 2 +- target/i386/emulate/meson.build | 7 +- target/i386/meson.build | 2 + target/i386/mshv/meson.build | 7 + target/i386/mshv/x86.c | 297 ++++++++++++++++++++++++++++++++ 6 files changed, 337 insertions(+), 3 deletions(-) create mode 100644 include/system/mshv.h create mode 100644 target/i386/mshv/meson.build create mode 100644 target/i386/mshv/x86.c diff --git a/include/system/mshv.h b/include/system/mshv.h new file mode 100644 index 0000000000000..342f1ef6a9819 --- /dev/null +++ b/include/system/mshv.h @@ -0,0 +1,25 @@ +/* + * QEMU MSHV support + * + * Copyright Microsoft, Corp. 2025 + * + * Authors: Ziqiao Zhou + * Magnus Kulke + * Jinank Jain + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ + +#ifndef QEMU_MSHV_H +#define QEMU_MSHV_H + +#ifdef COMPILING_PER_TARGET +#ifdef CONFIG_MSHV +#define CONFIG_MSHV_IS_POSSIBLE +#endif +#else +#define CONFIG_MSHV_IS_POSSIBLE +#endif + +#endif diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 42168f1d6d8b8..3aec8fd41c822 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2126,7 +2126,7 @@ typedef struct CPUArchState { QEMUTimer *xen_periodic_timer; QemuMutex xen_timers_lock; #endif -#if defined(CONFIG_HVF) +#if defined(CONFIG_HVF) || defined(CONFIG_MSHV) void *emu_mmio_buf; #endif diff --git a/target/i386/emulate/meson.build b/target/i386/emulate/meson.build index 4edd4f462fc74..b6dafb6a5be68 100644 --- a/target/i386/emulate/meson.build +++ b/target/i386/emulate/meson.build @@ -1,5 +1,8 @@ -i386_system_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: files( +emulator_files = files( 'x86_decode.c', 'x86_emu.c', 'x86_flags.c', -)) +) + +i386_system_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: emulator_files) +i386_system_ss.add(when: 'CONFIG_MSHV', if_true: emulator_files) diff --git a/target/i386/meson.build b/target/i386/meson.build index 092af34e2d859..89ba4912aaeb1 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build @@ -13,6 +13,7 @@ i386_ss.add(when: 'CONFIG_KVM', if_true: files('host-cpu.c')) i386_ss.add(when: 'CONFIG_HVF', if_true: files('host-cpu.c')) i386_ss.add(when: 'CONFIG_WHPX', if_true: files('host-cpu.c')) i386_ss.add(when: 'CONFIG_NVMM', if_true: files('host-cpu.c')) +i386_ss.add(when: 'CONFIG_MSHV', if_true: files('host-cpu.c')) i386_system_ss = ss.source_set() i386_system_ss.add(files( @@ -34,6 +35,7 @@ subdir('nvmm') subdir('hvf') subdir('tcg') subdir('emulate') +subdir('mshv') target_arch += {'i386': i386_ss} target_system_arch += {'i386': i386_system_ss} diff --git a/target/i386/mshv/meson.build b/target/i386/mshv/meson.build new file mode 100644 index 0000000000000..8ddaa7c11dc7f --- /dev/null +++ b/target/i386/mshv/meson.build @@ -0,0 +1,7 @@ +i386_mshv_ss = ss.source_set() + +i386_mshv_ss.add(files( + 'x86.c', +)) + +i386_system_ss.add_all(when: 'CONFIG_MSHV', if_true: i386_mshv_ss) diff --git a/target/i386/mshv/x86.c b/target/i386/mshv/x86.c new file mode 100644 index 0000000000000..d574b3bc52fe1 --- /dev/null +++ b/target/i386/mshv/x86.c @@ -0,0 +1,297 @@ +/* + * QEMU MSHV support + * + * Copyright Microsoft, Corp. 2025 + * + * Authors: Magnus Kulke + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" + +#include "cpu.h" +#include "emulate/x86_decode.h" +#include "emulate/x86_emu.h" +#include "qemu/typedefs.h" +#include "qemu/error-report.h" +#include "system/mshv.h" + +/* RW or Exec segment */ +static const uint8_t RWRX_SEGMENT_TYPE = 0x2; +static const uint8_t CODE_SEGMENT_TYPE = 0x8; +static const uint8_t EXPAND_DOWN_SEGMENT_TYPE = 0x4; + +typedef enum CpuMode { + REAL_MODE, + PROTECTED_MODE, + LONG_MODE, +} CpuMode; + +static CpuMode cpu_mode(CPUState *cpu) +{ + enum CpuMode m = REAL_MODE; + + if (x86_is_protected(cpu)) { + m = PROTECTED_MODE; + + if (x86_is_long_mode(cpu)) { + m = LONG_MODE; + } + } + + return m; +} + +static bool segment_type_ro(const SegmentCache *seg) +{ + uint32_t type_ = (seg->flags >> DESC_TYPE_SHIFT) & 15; + return (type_ & (~RWRX_SEGMENT_TYPE)) == 0; +} + +static bool segment_type_code(const SegmentCache *seg) +{ + uint32_t type_ = (seg->flags >> DESC_TYPE_SHIFT) & 15; + return (type_ & CODE_SEGMENT_TYPE) != 0; +} + +static bool segment_expands_down(const SegmentCache *seg) +{ + uint32_t type_ = (seg->flags >> DESC_TYPE_SHIFT) & 15; + + if (segment_type_code(seg)) { + return false; + } + + return (type_ & EXPAND_DOWN_SEGMENT_TYPE) != 0; +} + +static uint32_t segment_limit(const SegmentCache *seg) +{ + uint32_t limit = seg->limit; + uint32_t granularity = (seg->flags & DESC_G_MASK) != 0; + + if (granularity != 0) { + limit = (limit << 12) | 0xFFF; + } + + return limit; +} + +static uint8_t segment_db(const SegmentCache *seg) +{ + return (seg->flags >> DESC_B_SHIFT) & 1; +} + +static uint32_t segment_max_limit(const SegmentCache *seg) +{ + if (segment_db(seg) != 0) { + return 0xFFFFFFFF; + } + return 0xFFFF; +} + +static int linearize(CPUState *cpu, + target_ulong logical_addr, target_ulong *linear_addr, + X86Seg seg_idx) +{ + enum CpuMode mode; + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + SegmentCache *seg = &env->segs[seg_idx]; + target_ulong base = seg->base; + target_ulong logical_addr_32b; + uint32_t limit; + /* TODO: the emulator will not pass us "write" indicator yet */ + bool write = false; + + mode = cpu_mode(cpu); + + switch (mode) { + case LONG_MODE: + if (__builtin_add_overflow(logical_addr, base, linear_addr)) { + error_report("Address overflow"); + return -1; + } + break; + case PROTECTED_MODE: + case REAL_MODE: + if (segment_type_ro(seg) && write) { + error_report("Cannot write to read-only segment"); + return -1; + } + + logical_addr_32b = logical_addr & 0xFFFFFFFF; + limit = segment_limit(seg); + + if (segment_expands_down(seg)) { + if (logical_addr_32b >= limit) { + error_report("Address exceeds limit (expands down)"); + return -1; + } + + limit = segment_max_limit(seg); + } + + if (logical_addr_32b > limit) { + error_report("Address exceeds limit %u", limit); + return -1; + } + *linear_addr = logical_addr_32b + base; + break; + default: + error_report("Unknown cpu mode: %d", mode); + return -1; + } + + return 0; +} + +bool x86_read_segment_descriptor(CPUState *cpu, + struct x86_segment_descriptor *desc, + x86_segment_selector sel) +{ + target_ulong base; + uint32_t limit; + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + target_ulong gva; + + memset(desc, 0, sizeof(*desc)); + + /* valid gdt descriptors start from index 1 */ + if (!sel.index && GDT_SEL == sel.ti) { + return false; + } + + if (GDT_SEL == sel.ti) { + base = env->gdt.base; + limit = env->gdt.limit; + } else { + base = env->ldt.base; + limit = env->ldt.limit; + } + + if (sel.index * 8 >= limit) { + return false; + } + + gva = base + sel.index * 8; + emul_ops->read_mem(cpu, desc, gva, sizeof(*desc)); + + return true; +} + +bool x86_read_call_gate(CPUState *cpu, struct x86_call_gate *idt_desc, + int gate) +{ + target_ulong base; + uint32_t limit; + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + target_ulong gva; + + base = env->idt.base; + limit = env->idt.limit; + + memset(idt_desc, 0, sizeof(*idt_desc)); + if (gate * 8 >= limit) { + perror("call gate exceeds idt limit"); + return false; + } + + gva = base + gate * 8; + emul_ops->read_mem(cpu, idt_desc, gva, sizeof(*idt_desc)); + + return true; +} + +bool x86_is_protected(CPUState *cpu) +{ + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + uint64_t cr0 = env->cr[0]; + + return cr0 & CR0_PE_MASK; +} + +bool x86_is_real(CPUState *cpu) +{ + return !x86_is_protected(cpu); +} + +bool x86_is_v8086(CPUState *cpu) +{ + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + return x86_is_protected(cpu) && (env->eflags & VM_MASK); +} + +bool x86_is_long_mode(CPUState *cpu) +{ + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + uint64_t efer = env->efer; + uint64_t lme_lma = (MSR_EFER_LME | MSR_EFER_LMA); + + return ((efer & lme_lma) == lme_lma); +} + +bool x86_is_long64_mode(CPUState *cpu) +{ + error_report("unimplemented: is_long64_mode()"); + abort(); +} + +bool x86_is_paging_mode(CPUState *cpu) +{ + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + uint64_t cr0 = env->cr[0]; + + return cr0 & CR0_PG_MASK; +} + +bool x86_is_pae_enabled(CPUState *cpu) +{ + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + uint64_t cr4 = env->cr[4]; + + return cr4 & CR4_PAE_MASK; +} + +target_ulong linear_addr(CPUState *cpu, target_ulong addr, X86Seg seg) +{ + int ret; + target_ulong linear_addr; + + ret = linearize(cpu, addr, &linear_addr, seg); + if (ret < 0) { + error_report("failed to linearize address"); + abort(); + } + + return linear_addr; +} + +target_ulong linear_addr_size(CPUState *cpu, target_ulong addr, int size, + X86Seg seg) +{ + switch (size) { + case 2: + addr = (uint16_t)addr; + break; + case 4: + addr = (uint32_t)addr; + break; + default: + break; + } + return linear_addr(cpu, addr, seg); +} + +target_ulong linear_rip(CPUState *cpu, target_ulong rip) +{ + return linear_addr(cpu, rip, R_CS); +} From 638ac1c78457dd93ccd795b9c6c2673af8c7dd21 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:24 +0200 Subject: [PATCH 1401/1794] hw/intc: Generalize APIC helper names from kvm_* to accel_* Rename APIC helper functions to use an accel_* prefix instead of kvm_* to support use by accelerators other than KVM. This is a preparatory step for integrating MSHV support with common APIC logic. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-5-magnuskulke@linux.microsoft.com [Remove dead definition of mshv_msi_via_irqfd_enabled. - Paolo] Signed-off-by: Paolo Bonzini --- accel/accel-irq.c | 106 +++++++++++++++++++++++++++++++++++++ accel/meson.build | 2 +- hw/intc/ioapic.c | 20 ++++--- hw/virtio/virtio-pci.c | 21 ++++---- include/system/accel-irq.h | 37 +++++++++++++ include/system/mshv.h | 17 ++++++ 6 files changed, 185 insertions(+), 18 deletions(-) create mode 100644 accel/accel-irq.c create mode 100644 include/system/accel-irq.h diff --git a/accel/accel-irq.c b/accel/accel-irq.c new file mode 100644 index 0000000000000..7f864e35c4e71 --- /dev/null +++ b/accel/accel-irq.c @@ -0,0 +1,106 @@ +/* + * Accelerated irqchip abstraction + * + * Copyright Microsoft, Corp. 2025 + * + * Authors: Ziqiao Zhou + * Magnus Kulke + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/pci/msi.h" + +#include "system/kvm.h" +#include "system/mshv.h" +#include "system/accel-irq.h" + +int accel_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev) +{ +#ifdef CONFIG_MSHV_IS_POSSIBLE + if (mshv_msi_via_irqfd_enabled()) { + return mshv_irqchip_add_msi_route(vector, dev); + } +#endif + if (kvm_enabled()) { + return kvm_irqchip_add_msi_route(c, vector, dev); + } + return -ENOSYS; +} + +int accel_irqchip_update_msi_route(int vector, MSIMessage msg, PCIDevice *dev) +{ +#ifdef CONFIG_MSHV_IS_POSSIBLE + if (mshv_msi_via_irqfd_enabled()) { + return mshv_irqchip_update_msi_route(vector, msg, dev); + } +#endif + if (kvm_enabled()) { + return kvm_irqchip_update_msi_route(kvm_state, vector, msg, dev); + } + return -ENOSYS; +} + +void accel_irqchip_commit_route_changes(KVMRouteChange *c) +{ +#ifdef CONFIG_MSHV_IS_POSSIBLE + if (mshv_msi_via_irqfd_enabled()) { + mshv_irqchip_commit_routes(); + } +#endif + if (kvm_enabled()) { + kvm_irqchip_commit_route_changes(c); + } +} + +void accel_irqchip_commit_routes(void) +{ +#ifdef CONFIG_MSHV_IS_POSSIBLE + if (mshv_msi_via_irqfd_enabled()) { + mshv_irqchip_commit_routes(); + } +#endif + if (kvm_enabled()) { + kvm_irqchip_commit_routes(kvm_state); + } +} + +void accel_irqchip_release_virq(int virq) +{ +#ifdef CONFIG_MSHV_IS_POSSIBLE + if (mshv_msi_via_irqfd_enabled()) { + mshv_irqchip_release_virq(virq); + } +#endif + if (kvm_enabled()) { + kvm_irqchip_release_virq(kvm_state, virq); + } +} + +int accel_irqchip_add_irqfd_notifier_gsi(EventNotifier *n, EventNotifier *rn, + int virq) +{ +#ifdef CONFIG_MSHV_IS_POSSIBLE + if (mshv_msi_via_irqfd_enabled()) { + return mshv_irqchip_add_irqfd_notifier_gsi(n, rn, virq); + } +#endif + if (kvm_enabled()) { + return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, rn, virq); + } + return -ENOSYS; +} + +int accel_irqchip_remove_irqfd_notifier_gsi(EventNotifier *n, int virq) +{ +#ifdef CONFIG_MSHV_IS_POSSIBLE + if (mshv_msi_via_irqfd_enabled()) { + return mshv_irqchip_remove_irqfd_notifier_gsi(n, virq); + } +#endif + if (kvm_enabled()) { + return kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, virq); + } + return -ENOSYS; +} diff --git a/accel/meson.build b/accel/meson.build index 25b0f100b5121..6349efe682f29 100644 --- a/accel/meson.build +++ b/accel/meson.build @@ -1,6 +1,6 @@ common_ss.add(files('accel-common.c')) specific_ss.add(files('accel-target.c')) -system_ss.add(files('accel-system.c', 'accel-blocker.c', 'accel-qmp.c')) +system_ss.add(files('accel-system.c', 'accel-blocker.c', 'accel-qmp.c', 'accel-irq.c')) user_ss.add(files('accel-user.c')) subdir('tcg') diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index 133bef852d1e0..e431d00311768 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -30,12 +30,18 @@ #include "hw/intc/ioapic_internal.h" #include "hw/pci/msi.h" #include "hw/qdev-properties.h" +#include "system/accel-irq.h" #include "system/kvm.h" #include "system/system.h" #include "hw/i386/apic-msidef.h" #include "hw/i386/x86-iommu.h" #include "trace.h" + +#if defined(CONFIG_KVM) || defined(CONFIG_MSHV) +#define ACCEL_GSI_IRQFD_POSSIBLE +#endif + #define APIC_DELIVERY_MODE_SHIFT 8 #define APIC_POLARITY_SHIFT 14 #define APIC_TRIG_MODE_SHIFT 15 @@ -191,10 +197,10 @@ static void ioapic_set_irq(void *opaque, int vector, int level) static void ioapic_update_kvm_routes(IOAPICCommonState *s) { -#ifdef CONFIG_KVM +#ifdef ACCEL_GSI_IRQFD_POSSIBLE int i; - if (kvm_irqchip_is_split()) { + if (accel_irqchip_is_split()) { for (i = 0; i < IOAPIC_NUM_PINS; i++) { MSIMessage msg; struct ioapic_entry_info info; @@ -202,15 +208,15 @@ static void ioapic_update_kvm_routes(IOAPICCommonState *s) if (!info.masked) { msg.address = info.addr; msg.data = info.data; - kvm_irqchip_update_msi_route(kvm_state, i, msg, NULL); + accel_irqchip_update_msi_route(i, msg, NULL); } } - kvm_irqchip_commit_routes(kvm_state); + accel_irqchip_commit_routes(); } #endif } -#ifdef CONFIG_KVM +#ifdef ACCEL_KERNEL_GSI_IRQFD_POSSIBLE static void ioapic_iec_notifier(void *private, bool global, uint32_t index, uint32_t mask) { @@ -428,11 +434,11 @@ static const MemoryRegionOps ioapic_io_ops = { static void ioapic_machine_done_notify(Notifier *notifier, void *data) { -#ifdef CONFIG_KVM +#ifdef ACCEL_KERNEL_GSI_IRQFD_POSSIBLE IOAPICCommonState *s = container_of(notifier, IOAPICCommonState, machine_done); - if (kvm_irqchip_is_split()) { + if (accel_irqchip_is_split()) { X86IOMMUState *iommu = x86_iommu_get_default(); if (iommu) { /* Register this IOAPIC with IOMMU IEC notifier, so that diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 767216d795998..0cdc16217ffa9 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -34,6 +34,7 @@ #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/loader.h" +#include "system/accel-irq.h" #include "system/kvm.h" #include "hw/virtio/virtio-pci.h" #include "qemu/range.h" @@ -825,11 +826,11 @@ static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy, if (irqfd->users == 0) { KVMRouteChange c = kvm_irqchip_begin_route_changes(kvm_state); - ret = kvm_irqchip_add_msi_route(&c, vector, &proxy->pci_dev); + ret = accel_irqchip_add_msi_route(&c, vector, &proxy->pci_dev); if (ret < 0) { return ret; } - kvm_irqchip_commit_route_changes(&c); + accel_irqchip_commit_route_changes(&c); irqfd->virq = ret; } irqfd->users++; @@ -841,7 +842,7 @@ static void kvm_virtio_pci_vq_vector_release(VirtIOPCIProxy *proxy, { VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; if (--irqfd->users == 0) { - kvm_irqchip_release_virq(kvm_state, irqfd->virq); + accel_irqchip_release_virq(irqfd->virq); } } @@ -850,7 +851,7 @@ static int kvm_virtio_pci_irqfd_use(VirtIOPCIProxy *proxy, unsigned int vector) { VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; - return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, irqfd->virq); + return accel_irqchip_add_irqfd_notifier_gsi(n, NULL, irqfd->virq); } static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy, @@ -860,7 +861,7 @@ static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy, VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; int ret; - ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, irqfd->virq); + ret = accel_irqchip_remove_irqfd_notifier_gsi(n, irqfd->virq); assert(ret == 0); } static int virtio_pci_get_notifier(VirtIOPCIProxy *proxy, int queue_no, @@ -995,12 +996,12 @@ static int virtio_pci_one_vector_unmask(VirtIOPCIProxy *proxy, if (proxy->vector_irqfd) { irqfd = &proxy->vector_irqfd[vector]; if (irqfd->msg.data != msg.data || irqfd->msg.address != msg.address) { - ret = kvm_irqchip_update_msi_route(kvm_state, irqfd->virq, msg, - &proxy->pci_dev); + ret = accel_irqchip_update_msi_route(irqfd->virq, msg, + &proxy->pci_dev); if (ret < 0) { return ret; } - kvm_irqchip_commit_routes(kvm_state); + accel_irqchip_commit_routes(); } } @@ -1229,7 +1230,7 @@ static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign) VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); int r, n; bool with_irqfd = msix_enabled(&proxy->pci_dev) && - kvm_msi_via_irqfd_enabled(); + accel_msi_via_irqfd_enabled() ; nvqs = MIN(nvqs, VIRTIO_QUEUE_MAX); @@ -1433,7 +1434,7 @@ static void virtio_pci_set_vector(VirtIODevice *vdev, uint16_t new_vector) { bool kvm_irqfd = (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) && - msix_enabled(&proxy->pci_dev) && kvm_msi_via_irqfd_enabled(); + msix_enabled(&proxy->pci_dev) && accel_msi_via_irqfd_enabled(); if (new_vector == old_vector) { return; diff --git a/include/system/accel-irq.h b/include/system/accel-irq.h new file mode 100644 index 0000000000000..671fb7dfdbbc0 --- /dev/null +++ b/include/system/accel-irq.h @@ -0,0 +1,37 @@ +/* + * Accelerated irqchip abstraction + * + * Copyright Microsoft, Corp. 2025 + * + * Authors: Ziqiao Zhou + * Magnus Kulke + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SYSTEM_ACCEL_IRQ_H +#define SYSTEM_ACCEL_IRQ_H +#include "hw/pci/msi.h" +#include "qemu/osdep.h" +#include "system/kvm.h" +#include "system/mshv.h" + +static inline bool accel_msi_via_irqfd_enabled(void) +{ + return mshv_msi_via_irqfd_enabled() || kvm_msi_via_irqfd_enabled(); +} + +static inline bool accel_irqchip_is_split(void) +{ + return mshv_msi_via_irqfd_enabled() || kvm_irqchip_is_split(); +} + +int accel_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev); +int accel_irqchip_update_msi_route(int vector, MSIMessage msg, PCIDevice *dev); +void accel_irqchip_commit_route_changes(KVMRouteChange *c); +void accel_irqchip_commit_routes(void); +void accel_irqchip_release_virq(int virq); +int accel_irqchip_add_irqfd_notifier_gsi(EventNotifier *n, EventNotifier *rn, + int virq); +int accel_irqchip_remove_irqfd_notifier_gsi(EventNotifier *n, int virq); +#endif diff --git a/include/system/mshv.h b/include/system/mshv.h index 342f1ef6a9819..2a504ed81f24a 100644 --- a/include/system/mshv.h +++ b/include/system/mshv.h @@ -22,4 +22,21 @@ #define CONFIG_MSHV_IS_POSSIBLE #endif +#ifdef CONFIG_MSHV_IS_POSSIBLE +extern bool mshv_allowed; +#define mshv_enabled() (mshv_allowed) +#else /* CONFIG_MSHV_IS_POSSIBLE */ +#define mshv_enabled() false +#endif +#define mshv_msi_via_irqfd_enabled() false + +/* interrupt */ +int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev); +int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev); +void mshv_irqchip_commit_routes(void); +void mshv_irqchip_release_virq(int virq); +int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *n, + const EventNotifier *rn, int virq); +int mshv_irqchip_remove_irqfd_notifier_gsi(const EventNotifier *n, int virq); + #endif From 7db6086287502ad5a015ccc31e85ec9cac7e2716 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:25 +0200 Subject: [PATCH 1402/1794] include/hw/hyperv: Add MSHV ABI header definitions Introduce headers for the Microsoft Hypervisor (MSHV) userspace ABI, including IOCTLs and structures used to interface with the hypervisor. These definitions are based on the upstream Linux MSHV interface and will be used by the MSHV accelerator backend in later patches. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-6-magnuskulke@linux.microsoft.com [Do not use __uN types. - Paolo] Signed-off-by: Paolo Bonzini --- include/hw/hyperv/hvgdk.h | 20 + include/hw/hyperv/hvgdk_mini.h | 817 ++++++++++++++++++++++++++++++++ include/hw/hyperv/hvhdk.h | 249 ++++++++++ include/hw/hyperv/hvhdk_mini.h | 102 ++++ scripts/update-linux-headers.sh | 2 +- 5 files changed, 1189 insertions(+), 1 deletion(-) create mode 100644 include/hw/hyperv/hvgdk.h create mode 100644 include/hw/hyperv/hvgdk_mini.h create mode 100644 include/hw/hyperv/hvhdk.h create mode 100644 include/hw/hyperv/hvhdk_mini.h diff --git a/include/hw/hyperv/hvgdk.h b/include/hw/hyperv/hvgdk.h new file mode 100644 index 0000000000000..71161f477c49b --- /dev/null +++ b/include/hw/hyperv/hvgdk.h @@ -0,0 +1,20 @@ +/* + * Type definitions for the mshv guest interface. + * + * Copyright Microsoft, Corp. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_HYPERV_HVGDK_H +#define HW_HYPERV_HVGDK_H + +#define HVGDK_H_VERSION (25125) + +enum hv_unimplemented_msr_action { + HV_UNIMPLEMENTED_MSR_ACTION_FAULT = 0, + HV_UNIMPLEMENTED_MSR_ACTION_IGNORE_WRITE_READ_ZERO = 1, + HV_UNIMPLEMENTED_MSR_ACTION_COUNT = 2, +}; + +#endif /* HW_HYPERV_HVGDK_H */ diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h new file mode 100644 index 0000000000000..d89315f5452fd --- /dev/null +++ b/include/hw/hyperv/hvgdk_mini.h @@ -0,0 +1,817 @@ +/* + * Userspace interfaces for /dev/mshv* devices and derived fds + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_HYPERV_HVGDK_MINI_H +#define HW_HYPERV_HVGDK_MINI_H + +#define MSHV_IOCTL 0xB8 + +typedef enum hv_register_name { + /* Pending Interruption Register */ + HV_REGISTER_PENDING_INTERRUPTION = 0x00010002, + + /* X64 User-Mode Registers */ + HV_X64_REGISTER_RAX = 0x00020000, + HV_X64_REGISTER_RCX = 0x00020001, + HV_X64_REGISTER_RDX = 0x00020002, + HV_X64_REGISTER_RBX = 0x00020003, + HV_X64_REGISTER_RSP = 0x00020004, + HV_X64_REGISTER_RBP = 0x00020005, + HV_X64_REGISTER_RSI = 0x00020006, + HV_X64_REGISTER_RDI = 0x00020007, + HV_X64_REGISTER_R8 = 0x00020008, + HV_X64_REGISTER_R9 = 0x00020009, + HV_X64_REGISTER_R10 = 0x0002000A, + HV_X64_REGISTER_R11 = 0x0002000B, + HV_X64_REGISTER_R12 = 0x0002000C, + HV_X64_REGISTER_R13 = 0x0002000D, + HV_X64_REGISTER_R14 = 0x0002000E, + HV_X64_REGISTER_R15 = 0x0002000F, + HV_X64_REGISTER_RIP = 0x00020010, + HV_X64_REGISTER_RFLAGS = 0x00020011, + + /* X64 Floating Point and Vector Registers */ + HV_X64_REGISTER_XMM0 = 0x00030000, + HV_X64_REGISTER_XMM1 = 0x00030001, + HV_X64_REGISTER_XMM2 = 0x00030002, + HV_X64_REGISTER_XMM3 = 0x00030003, + HV_X64_REGISTER_XMM4 = 0x00030004, + HV_X64_REGISTER_XMM5 = 0x00030005, + HV_X64_REGISTER_XMM6 = 0x00030006, + HV_X64_REGISTER_XMM7 = 0x00030007, + HV_X64_REGISTER_XMM8 = 0x00030008, + HV_X64_REGISTER_XMM9 = 0x00030009, + HV_X64_REGISTER_XMM10 = 0x0003000A, + HV_X64_REGISTER_XMM11 = 0x0003000B, + HV_X64_REGISTER_XMM12 = 0x0003000C, + HV_X64_REGISTER_XMM13 = 0x0003000D, + HV_X64_REGISTER_XMM14 = 0x0003000E, + HV_X64_REGISTER_XMM15 = 0x0003000F, + HV_X64_REGISTER_FP_MMX0 = 0x00030010, + HV_X64_REGISTER_FP_MMX1 = 0x00030011, + HV_X64_REGISTER_FP_MMX2 = 0x00030012, + HV_X64_REGISTER_FP_MMX3 = 0x00030013, + HV_X64_REGISTER_FP_MMX4 = 0x00030014, + HV_X64_REGISTER_FP_MMX5 = 0x00030015, + HV_X64_REGISTER_FP_MMX6 = 0x00030016, + HV_X64_REGISTER_FP_MMX7 = 0x00030017, + HV_X64_REGISTER_FP_CONTROL_STATUS = 0x00030018, + HV_X64_REGISTER_XMM_CONTROL_STATUS = 0x00030019, + + /* X64 Control Registers */ + HV_X64_REGISTER_CR0 = 0x00040000, + HV_X64_REGISTER_CR2 = 0x00040001, + HV_X64_REGISTER_CR3 = 0x00040002, + HV_X64_REGISTER_CR4 = 0x00040003, + HV_X64_REGISTER_CR8 = 0x00040004, + HV_X64_REGISTER_XFEM = 0x00040005, + + /* X64 Segment Registers */ + HV_X64_REGISTER_ES = 0x00060000, + HV_X64_REGISTER_CS = 0x00060001, + HV_X64_REGISTER_SS = 0x00060002, + HV_X64_REGISTER_DS = 0x00060003, + HV_X64_REGISTER_FS = 0x00060004, + HV_X64_REGISTER_GS = 0x00060005, + HV_X64_REGISTER_LDTR = 0x00060006, + HV_X64_REGISTER_TR = 0x00060007, + + /* X64 Table Registers */ + HV_X64_REGISTER_IDTR = 0x00070000, + HV_X64_REGISTER_GDTR = 0x00070001, + + /* X64 Virtualized MSRs */ + HV_X64_REGISTER_TSC = 0x00080000, + HV_X64_REGISTER_EFER = 0x00080001, + HV_X64_REGISTER_KERNEL_GS_BASE = 0x00080002, + HV_X64_REGISTER_APIC_BASE = 0x00080003, + HV_X64_REGISTER_PAT = 0x00080004, + HV_X64_REGISTER_SYSENTER_CS = 0x00080005, + HV_X64_REGISTER_SYSENTER_EIP = 0x00080006, + HV_X64_REGISTER_SYSENTER_ESP = 0x00080007, + HV_X64_REGISTER_STAR = 0x00080008, + HV_X64_REGISTER_LSTAR = 0x00080009, + HV_X64_REGISTER_CSTAR = 0x0008000A, + HV_X64_REGISTER_SFMASK = 0x0008000B, + HV_X64_REGISTER_INITIAL_APIC_ID = 0x0008000C, + + /* X64 Cache control MSRs */ + HV_X64_REGISTER_MSR_MTRR_CAP = 0x0008000D, + HV_X64_REGISTER_MSR_MTRR_DEF_TYPE = 0x0008000E, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE0 = 0x00080010, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE1 = 0x00080011, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE2 = 0x00080012, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE3 = 0x00080013, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE4 = 0x00080014, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE5 = 0x00080015, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE6 = 0x00080016, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE7 = 0x00080017, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE8 = 0x00080018, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE9 = 0x00080019, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASEA = 0x0008001A, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASEB = 0x0008001B, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASEC = 0x0008001C, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASED = 0x0008001D, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASEE = 0x0008001E, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASEF = 0x0008001F, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK0 = 0x00080040, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK1 = 0x00080041, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK2 = 0x00080042, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK3 = 0x00080043, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK4 = 0x00080044, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK5 = 0x00080045, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK6 = 0x00080046, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK7 = 0x00080047, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK8 = 0x00080048, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK9 = 0x00080049, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASKA = 0x0008004A, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASKB = 0x0008004B, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASKC = 0x0008004C, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASKD = 0x0008004D, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASKE = 0x0008004E, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASKF = 0x0008004F, + HV_X64_REGISTER_MSR_MTRR_FIX64K00000 = 0x00080070, + HV_X64_REGISTER_MSR_MTRR_FIX16K80000 = 0x00080071, + HV_X64_REGISTER_MSR_MTRR_FIX16KA0000 = 0x00080072, + HV_X64_REGISTER_MSR_MTRR_FIX4KC0000 = 0x00080073, + HV_X64_REGISTER_MSR_MTRR_FIX4KC8000 = 0x00080074, + HV_X64_REGISTER_MSR_MTRR_FIX4KD0000 = 0x00080075, + HV_X64_REGISTER_MSR_MTRR_FIX4KD8000 = 0x00080076, + HV_X64_REGISTER_MSR_MTRR_FIX4KE0000 = 0x00080077, + HV_X64_REGISTER_MSR_MTRR_FIX4KE8000 = 0x00080078, + HV_X64_REGISTER_MSR_MTRR_FIX4KF0000 = 0x00080079, + HV_X64_REGISTER_MSR_MTRR_FIX4KF8000 = 0x0008007A, + + HV_X64_REGISTER_TSC_AUX = 0x0008007B, + HV_X64_REGISTER_BNDCFGS = 0x0008007C, + HV_X64_REGISTER_DEBUG_CTL = 0x0008007D, + + /* Available */ + + HV_X64_REGISTER_SPEC_CTRL = 0x00080084, + HV_X64_REGISTER_TSC_ADJUST = 0x00080096, + + /* Other MSRs */ + HV_X64_REGISTER_MSR_IA32_MISC_ENABLE = 0x000800A0, + + /* Misc */ + HV_REGISTER_GUEST_OS_ID = 0x00090002, + HV_REGISTER_REFERENCE_TSC = 0x00090017, + + /* Hypervisor-defined Registers (Synic) */ + HV_REGISTER_SINT0 = 0x000A0000, + HV_REGISTER_SINT1 = 0x000A0001, + HV_REGISTER_SINT2 = 0x000A0002, + HV_REGISTER_SINT3 = 0x000A0003, + HV_REGISTER_SINT4 = 0x000A0004, + HV_REGISTER_SINT5 = 0x000A0005, + HV_REGISTER_SINT6 = 0x000A0006, + HV_REGISTER_SINT7 = 0x000A0007, + HV_REGISTER_SINT8 = 0x000A0008, + HV_REGISTER_SINT9 = 0x000A0009, + HV_REGISTER_SINT10 = 0x000A000A, + HV_REGISTER_SINT11 = 0x000A000B, + HV_REGISTER_SINT12 = 0x000A000C, + HV_REGISTER_SINT13 = 0x000A000D, + HV_REGISTER_SINT14 = 0x000A000E, + HV_REGISTER_SINT15 = 0x000A000F, + HV_REGISTER_SCONTROL = 0x000A0010, + HV_REGISTER_SVERSION = 0x000A0011, + HV_REGISTER_SIEFP = 0x000A0012, + HV_REGISTER_SIMP = 0x000A0013, + HV_REGISTER_EOM = 0x000A0014, + HV_REGISTER_SIRBP = 0x000A0015, +} hv_register_name; + +enum hv_intercept_type { + HV_INTERCEPT_TYPE_X64_IO_PORT = 0X00000000, + HV_INTERCEPT_TYPE_X64_MSR = 0X00000001, + HV_INTERCEPT_TYPE_X64_CPUID = 0X00000002, + HV_INTERCEPT_TYPE_EXCEPTION = 0X00000003, + + /* Used to be HV_INTERCEPT_TYPE_REGISTER */ + HV_INTERCEPT_TYPE_RESERVED0 = 0X00000004, + HV_INTERCEPT_TYPE_MMIO = 0X00000005, + HV_INTERCEPT_TYPE_X64_GLOBAL_CPUID = 0X00000006, + HV_INTERCEPT_TYPE_X64_APIC_SMI = 0X00000007, + HV_INTERCEPT_TYPE_HYPERCALL = 0X00000008, + + HV_INTERCEPT_TYPE_X64_APIC_INIT_SIPI = 0X00000009, + HV_INTERCEPT_MC_UPDATE_PATCH_LEVEL_MSR_READ = 0X0000000A, + + HV_INTERCEPT_TYPE_X64_APIC_WRITE = 0X0000000B, + HV_INTERCEPT_TYPE_X64_MSR_INDEX = 0X0000000C, + HV_INTERCEPT_TYPE_MAX, + HV_INTERCEPT_TYPE_INVALID = 0XFFFFFFFF, +}; + +struct hv_u128 { + uint64_t low_part; + uint64_t high_part; +}; + +union hv_x64_xmm_control_status_register { + struct hv_u128 as_uint128; + struct { + union { + /* long mode */ + uint64_t last_fp_rdp; + /* 32 bit mode */ + struct { + uint32_t last_fp_dp; + uint16_t last_fp_ds; + uint16_t padding; + }; + }; + uint32_t xmm_status_control; + uint32_t xmm_status_control_mask; + }; +}; + +union hv_x64_fp_register { + struct hv_u128 as_uint128; + struct { + uint64_t mantissa; + uint64_t biased_exponent:15; + uint64_t sign:1; + uint64_t reserved:48; + }; +}; + +union hv_x64_pending_exception_event { + uint64_t as_uint64[2]; + struct { + uint32_t event_pending:1; + uint32_t event_type:3; + uint32_t reserved0:4; + uint32_t deliver_error_code:1; + uint32_t reserved1:7; + uint32_t vector:16; + uint32_t error_code; + uint64_t exception_parameter; + }; +}; + +union hv_x64_pending_virtualization_fault_event { + uint64_t as_uint64[2]; + struct { + uint32_t event_pending:1; + uint32_t event_type:3; + uint32_t reserved0:4; + uint32_t reserved1:8; + uint32_t parameter0:16; + uint32_t code; + uint64_t parameter1; + }; +}; + +union hv_x64_pending_interruption_register { + uint64_t as_uint64; + struct { + uint32_t interruption_pending:1; + uint32_t interruption_type:3; + uint32_t deliver_error_code:1; + uint32_t instruction_length:4; + uint32_t nested_event:1; + uint32_t reserved:6; + uint32_t interruption_vector:16; + uint32_t error_code; + }; +}; + +union hv_x64_register_sev_control { + uint64_t as_uint64; + struct { + uint64_t enable_encrypted_state:1; + uint64_t reserved_z:11; + uint64_t vmsa_gpa_page_number:52; + }; +}; + +union hv_x64_msr_npiep_config_contents { + uint64_t as_uint64; + struct { + /* + * These bits enable instruction execution prevention for + * specific instructions. + */ + uint64_t prevents_gdt:1; + uint64_t prevents_idt:1; + uint64_t prevents_ldt:1; + uint64_t prevents_tr:1; + + /* The reserved bits must always be 0. */ + uint64_t reserved:60; + }; +}; + +typedef struct hv_x64_segment_register { + uint64_t base; + uint32_t limit; + uint16_t selector; + union { + struct { + uint16_t segment_type:4; + uint16_t non_system_segment:1; + uint16_t descriptor_privilege_level:2; + uint16_t present:1; + uint16_t reserved:4; + uint16_t available:1; + uint16_t _long:1; + uint16_t _default:1; + uint16_t granularity:1; + }; + uint16_t attributes; + }; +} hv_x64_segment_register; + +typedef struct hv_x64_table_register { + uint16_t pad[3]; + uint16_t limit; + uint64_t base; +} hv_x64_table_register; + +union hv_x64_fp_control_status_register { + struct hv_u128 as_uint128; + struct { + uint16_t fp_control; + uint16_t fp_status; + uint8_t fp_tag; + uint8_t reserved; + uint16_t last_fp_op; + union { + /* long mode */ + uint64_t last_fp_rip; + /* 32 bit mode */ + struct { + uint32_t last_fp_eip; + uint16_t last_fp_cs; + uint16_t padding; + }; + }; + }; +}; + +/* General Hypervisor Register Content Definitions */ + +union hv_explicit_suspend_register { + uint64_t as_uint64; + struct { + uint64_t suspended:1; + uint64_t reserved:63; + }; +}; + +union hv_internal_activity_register { + uint64_t as_uint64; + + struct { + uint64_t startup_suspend:1; + uint64_t halt_suspend:1; + uint64_t idle_suspend:1; + uint64_t rsvd_z:61; + }; +}; + +union hv_x64_interrupt_state_register { + uint64_t as_uint64; + struct { + uint64_t interrupt_shadow:1; + uint64_t nmi_masked:1; + uint64_t reserved:62; + }; +}; + +union hv_intercept_suspend_register { + uint64_t as_uint64; + struct { + uint64_t suspended:1; + uint64_t reserved:63; + }; +}; + +typedef union hv_register_value { + struct hv_u128 reg128; + uint64_t reg64; + uint32_t reg32; + uint16_t reg16; + uint8_t reg8; + union hv_x64_fp_register fp; + union hv_x64_fp_control_status_register fp_control_status; + union hv_x64_xmm_control_status_register xmm_control_status; + struct hv_x64_segment_register segment; + struct hv_x64_table_register table; + union hv_explicit_suspend_register explicit_suspend; + union hv_intercept_suspend_register intercept_suspend; + union hv_internal_activity_register internal_activity; + union hv_x64_interrupt_state_register interrupt_state; + union hv_x64_pending_interruption_register pending_interruption; + union hv_x64_msr_npiep_config_contents npiep_config; + union hv_x64_pending_exception_event pending_exception_event; + union hv_x64_pending_virtualization_fault_event + pending_virtualization_fault_event; + union hv_x64_register_sev_control sev_control; +} hv_register_value; + +typedef struct hv_register_assoc { + uint32_t name; /* enum hv_register_name */ + uint32_t reserved1; + uint64_t reserved2; + union hv_register_value value; +} hv_register_assoc; + +union hv_input_vtl { + uint8_t as_uint8; + struct { + uint8_t target_vtl:4; + uint8_t use_target_vtl:1; + uint8_t reserved_z:3; + }; +}; + +typedef struct hv_input_get_vp_registers { + uint64_t partition_id; + uint32_t vp_index; + union hv_input_vtl input_vtl; + uint8_t rsvd_z8; + uint16_t rsvd_z16; + uint32_t names[]; +} hv_input_get_vp_registers; + +typedef struct hv_input_set_vp_registers { + uint64_t partition_id; + uint32_t vp_index; + union hv_input_vtl input_vtl; + uint8_t rsvd_z8; + uint16_t rsvd_z16; + struct hv_register_assoc elements[]; +} hv_input_set_vp_registers; + +#define MSHV_VP_MAX_REGISTERS 128 + +struct mshv_vp_registers { + int count; /* at most MSHV_VP_MAX_REGISTERS */ + struct hv_register_assoc *regs; +}; + +union hv_interrupt_control { + uint64_t as_uint64; + struct { + uint32_t interrupt_type; /* enum hv_interrupt type */ + uint32_t level_triggered:1; + uint32_t logical_dest_mode:1; + uint32_t rsvd:30; + }; +}; + +struct hv_input_assert_virtual_interrupt { + uint64_t partition_id; + union hv_interrupt_control control; + uint64_t dest_addr; /* cpu's apic id */ + uint32_t vector; + uint8_t target_vtl; + uint8_t rsvd_z0; + uint16_t rsvd_z1; +}; + +/* /dev/mshv */ +#define MSHV_CREATE_PARTITION _IOW(MSHV_IOCTL, 0x00, struct mshv_create_partition) +#define MSHV_CREATE_VP _IOW(MSHV_IOCTL, 0x01, struct mshv_create_vp) + +/* Partition fds created with MSHV_CREATE_PARTITION */ +#define MSHV_INITIALIZE_PARTITION _IO(MSHV_IOCTL, 0x00) +#define MSHV_SET_GUEST_MEMORY _IOW(MSHV_IOCTL, 0x02, struct mshv_user_mem_region) +#define MSHV_IRQFD _IOW(MSHV_IOCTL, 0x03, struct mshv_user_irqfd) +#define MSHV_IOEVENTFD _IOW(MSHV_IOCTL, 0x04, struct mshv_user_ioeventfd) +#define MSHV_SET_MSI_ROUTING _IOW(MSHV_IOCTL, 0x05, struct mshv_user_irq_table) + +/* + ******************************** + * VP APIs for child partitions * + ******************************** + */ + +struct hv_local_interrupt_controller_state { + /* HV_X64_INTERRUPT_CONTROLLER_STATE */ + uint32_t apic_id; + uint32_t apic_version; + uint32_t apic_ldr; + uint32_t apic_dfr; + uint32_t apic_spurious; + uint32_t apic_isr[8]; + uint32_t apic_tmr[8]; + uint32_t apic_irr[8]; + uint32_t apic_esr; + uint32_t apic_icr_high; + uint32_t apic_icr_low; + uint32_t apic_lvt_timer; + uint32_t apic_lvt_thermal; + uint32_t apic_lvt_perfmon; + uint32_t apic_lvt_lint0; + uint32_t apic_lvt_lint1; + uint32_t apic_lvt_error; + uint32_t apic_lvt_cmci; + uint32_t apic_error_status; + uint32_t apic_initial_count; + uint32_t apic_counter_value; + uint32_t apic_divide_configuration; + uint32_t apic_remote_read; +}; + +/* Generic hypercall */ +#define MSHV_ROOT_HVCALL _IOWR(MSHV_IOCTL, 0x07, struct mshv_root_hvcall) + +/* From hvgdk_mini.h */ + +#define HV_X64_MSR_GUEST_OS_ID 0x40000000 +#define HV_X64_MSR_SINT0 0x40000090 +#define HV_X64_MSR_SINT1 0x40000091 +#define HV_X64_MSR_SINT2 0x40000092 +#define HV_X64_MSR_SINT3 0x40000093 +#define HV_X64_MSR_SINT4 0x40000094 +#define HV_X64_MSR_SINT5 0x40000095 +#define HV_X64_MSR_SINT6 0x40000096 +#define HV_X64_MSR_SINT7 0x40000097 +#define HV_X64_MSR_SINT8 0x40000098 +#define HV_X64_MSR_SINT9 0x40000099 +#define HV_X64_MSR_SINT10 0x4000009A +#define HV_X64_MSR_SINT11 0x4000009B +#define HV_X64_MSR_SINT12 0x4000009C +#define HV_X64_MSR_SINT13 0x4000009D +#define HV_X64_MSR_SINT14 0x4000009E +#define HV_X64_MSR_SINT15 0x4000009F +#define HV_X64_MSR_SCONTROL 0x40000080 +#define HV_X64_MSR_SIEFP 0x40000082 +#define HV_X64_MSR_SIMP 0x40000083 +#define HV_X64_MSR_REFERENCE_TSC 0x40000021 +#define HV_X64_MSR_EOM 0x40000084 + +/* Define port identifier type. */ +union hv_port_id { + uint32_t asuint32_t; + struct { + uint32_t id:24; + uint32_t reserved:8; + }; +}; + +#define HV_MESSAGE_SIZE (256) +#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240) +#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30) + +/* Define hypervisor message types. */ +enum hv_message_type { + HVMSG_NONE = 0x00000000, + + /* Memory access messages. */ + HVMSG_UNMAPPED_GPA = 0x80000000, + HVMSG_GPA_INTERCEPT = 0x80000001, + HVMSG_UNACCEPTED_GPA = 0x80000003, + HVMSG_GPA_ATTRIBUTE_INTERCEPT = 0x80000004, + + /* Timer notification messages. */ + HVMSG_TIMER_EXPIRED = 0x80000010, + + /* Error messages. */ + HVMSG_INVALID_VP_REGISTER_VALUE = 0x80000020, + HVMSG_UNRECOVERABLE_EXCEPTION = 0x80000021, + HVMSG_UNSUPPORTED_FEATURE = 0x80000022, + + /* + * Opaque intercept message. The original intercept message is only + * accessible from the mapped intercept message page. + */ + HVMSG_OPAQUE_INTERCEPT = 0x8000003F, + + /* Trace buffer complete messages. */ + HVMSG_EVENTLOG_BUFFERCOMPLETE = 0x80000040, + + /* Hypercall intercept */ + HVMSG_HYPERCALL_INTERCEPT = 0x80000050, + + /* SynIC intercepts */ + HVMSG_SYNIC_EVENT_INTERCEPT = 0x80000060, + HVMSG_SYNIC_SINT_INTERCEPT = 0x80000061, + HVMSG_SYNIC_SINT_DELIVERABLE = 0x80000062, + + /* Async call completion intercept */ + HVMSG_ASYNC_CALL_COMPLETION = 0x80000070, + + /* Root scheduler messages */ + HVMSG_SCHEDULER_VP_SIGNAL_BITSE = 0x80000100, + HVMSG_SCHEDULER_VP_SIGNAL_PAIR = 0x80000101, + + /* Platform-specific processor intercept messages. */ + HVMSG_X64_IO_PORT_INTERCEPT = 0x80010000, + HVMSG_X64_MSR_INTERCEPT = 0x80010001, + HVMSG_X64_CPUID_INTERCEPT = 0x80010002, + HVMSG_X64_EXCEPTION_INTERCEPT = 0x80010003, + HVMSG_X64_APIC_EOI = 0x80010004, + HVMSG_X64_LEGACY_FP_ERROR = 0x80010005, + HVMSG_X64_IOMMU_PRQ = 0x80010006, + HVMSG_X64_HALT = 0x80010007, + HVMSG_X64_INTERRUPTION_DELIVERABLE = 0x80010008, + HVMSG_X64_SIPI_INTERCEPT = 0x80010009, + HVMSG_X64_SEV_VMGEXIT_INTERCEPT = 0x80010013, +}; + +union hv_x64_vp_execution_state { + uint16_t as_uint16; + struct { + uint16_t cpl:2; + uint16_t cr0_pe:1; + uint16_t cr0_am:1; + uint16_t efer_lma:1; + uint16_t debug_active:1; + uint16_t interruption_pending:1; + uint16_t vtl:4; + uint16_t enclave_mode:1; + uint16_t interrupt_shadow:1; + uint16_t virtualization_fault_active:1; + uint16_t reserved:2; + }; +}; + +/* From openvmm::hvdef */ +enum hv_x64_intercept_access_type { + HV_X64_INTERCEPT_ACCESS_TYPE_READ = 0, + HV_X64_INTERCEPT_ACCESS_TYPE_WRITE = 1, + HV_X64_INTERCEPT_ACCESS_TYPE_EXECUTE = 2, +}; + +struct hv_x64_intercept_message_header { + uint32_t vp_index; + uint8_t instruction_length:4; + uint8_t cr8:4; /* Only set for exo partitions */ + uint8_t intercept_access_type; + union hv_x64_vp_execution_state execution_state; + struct hv_x64_segment_register cs_segment; + uint64_t rip; + uint64_t rflags; +}; + +union hv_x64_io_port_access_info { + uint8_t as_uint8; + struct { + uint8_t access_size:3; + uint8_t string_op:1; + uint8_t rep_prefix:1; + uint8_t reserved:3; + }; +}; + +typedef struct hv_x64_io_port_intercept_message { + struct hv_x64_intercept_message_header header; + uint16_t port_number; + union hv_x64_io_port_access_info access_info; + uint8_t instruction_byte_count; + uint32_t reserved; + uint64_t rax; + uint8_t instruction_bytes[16]; + struct hv_x64_segment_register ds_segment; + struct hv_x64_segment_register es_segment; + uint64_t rcx; + uint64_t rsi; + uint64_t rdi; +} hv_x64_io_port_intercept_message; + +union hv_x64_memory_access_info { + uint8_t as_uint8; + struct { + uint8_t gva_valid:1; + uint8_t gva_gpa_valid:1; + uint8_t hypercall_output_pending:1; + uint8_t tlb_locked_no_overlay:1; + uint8_t reserved:4; + }; +}; + +struct hv_x64_memory_intercept_message { + struct hv_x64_intercept_message_header header; + uint32_t cache_type; /* enum hv_cache_type */ + uint8_t instruction_byte_count; + union hv_x64_memory_access_info memory_access_info; + uint8_t tpr_priority; + uint8_t reserved1; + uint64_t guest_virtual_address; + uint64_t guest_physical_address; + uint8_t instruction_bytes[16]; +}; + +union hv_message_flags { + uint8_t asu8; + struct { + uint8_t msg_pending:1; + uint8_t reserved:7; + }; +}; + +struct hv_message_header { + uint32_t message_type; + uint8_t payload_size; + union hv_message_flags message_flags; + uint8_t reserved[2]; + union { + uint64_t sender; + union hv_port_id port; + }; +}; + +struct hv_message { + struct hv_message_header header; + union { + uint64_t payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; + } u; +}; + +/* From github.com/rust-vmm/mshv-bindings/src/x86_64/regs.rs */ + +struct hv_cpuid_entry { + uint32_t function; + uint32_t index; + uint32_t flags; + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t padding[3]; +}; + +struct hv_cpuid { + uint32_t nent; + uint32_t padding; + struct hv_cpuid_entry entries[0]; +}; + +#define IA32_MSR_TSC 0x00000010 +#define IA32_MSR_EFER 0xC0000080 +#define IA32_MSR_KERNEL_GS_BASE 0xC0000102 +#define IA32_MSR_APIC_BASE 0x0000001B +#define IA32_MSR_PAT 0x0277 +#define IA32_MSR_SYSENTER_CS 0x00000174 +#define IA32_MSR_SYSENTER_ESP 0x00000175 +#define IA32_MSR_SYSENTER_EIP 0x00000176 +#define IA32_MSR_STAR 0xC0000081 +#define IA32_MSR_LSTAR 0xC0000082 +#define IA32_MSR_CSTAR 0xC0000083 +#define IA32_MSR_SFMASK 0xC0000084 + +#define IA32_MSR_MTRR_CAP 0x00FE +#define IA32_MSR_MTRR_DEF_TYPE 0x02FF +#define IA32_MSR_MTRR_PHYSBASE0 0x0200 +#define IA32_MSR_MTRR_PHYSMASK0 0x0201 +#define IA32_MSR_MTRR_PHYSBASE1 0x0202 +#define IA32_MSR_MTRR_PHYSMASK1 0x0203 +#define IA32_MSR_MTRR_PHYSBASE2 0x0204 +#define IA32_MSR_MTRR_PHYSMASK2 0x0205 +#define IA32_MSR_MTRR_PHYSBASE3 0x0206 +#define IA32_MSR_MTRR_PHYSMASK3 0x0207 +#define IA32_MSR_MTRR_PHYSBASE4 0x0208 +#define IA32_MSR_MTRR_PHYSMASK4 0x0209 +#define IA32_MSR_MTRR_PHYSBASE5 0x020A +#define IA32_MSR_MTRR_PHYSMASK5 0x020B +#define IA32_MSR_MTRR_PHYSBASE6 0x020C +#define IA32_MSR_MTRR_PHYSMASK6 0x020D +#define IA32_MSR_MTRR_PHYSBASE7 0x020E +#define IA32_MSR_MTRR_PHYSMASK7 0x020F + +#define IA32_MSR_MTRR_FIX64K_00000 0x0250 +#define IA32_MSR_MTRR_FIX16K_80000 0x0258 +#define IA32_MSR_MTRR_FIX16K_A0000 0x0259 +#define IA32_MSR_MTRR_FIX4K_C0000 0x0268 +#define IA32_MSR_MTRR_FIX4K_C8000 0x0269 +#define IA32_MSR_MTRR_FIX4K_D0000 0x026A +#define IA32_MSR_MTRR_FIX4K_D8000 0x026B +#define IA32_MSR_MTRR_FIX4K_E0000 0x026C +#define IA32_MSR_MTRR_FIX4K_E8000 0x026D +#define IA32_MSR_MTRR_FIX4K_F0000 0x026E +#define IA32_MSR_MTRR_FIX4K_F8000 0x026F + +#define IA32_MSR_TSC_AUX 0xC0000103 +#define IA32_MSR_BNDCFGS 0x00000d90 +#define IA32_MSR_DEBUG_CTL 0x1D9 +#define IA32_MSR_SPEC_CTRL 0x00000048 +#define IA32_MSR_TSC_ADJUST 0x0000003b + +#define IA32_MSR_MISC_ENABLE 0x000001a0 + +#define HV_TRANSLATE_GVA_VALIDATE_READ (0x0001) +#define HV_TRANSLATE_GVA_VALIDATE_WRITE (0x0002) +#define HV_TRANSLATE_GVA_VALIDATE_EXECUTE (0x0004) + +#define HV_HYP_PAGE_SHIFT 12 +#define HV_HYP_PAGE_SIZE BIT(HV_HYP_PAGE_SHIFT) +#define HV_HYP_PAGE_MASK (~(HV_HYP_PAGE_SIZE - 1)) + +#define HVCALL_GET_PARTITION_PROPERTY 0x0044 +#define HVCALL_SET_PARTITION_PROPERTY 0x0045 +#define HVCALL_GET_VP_REGISTERS 0x0050 +#define HVCALL_SET_VP_REGISTERS 0x0051 +#define HVCALL_TRANSLATE_VIRTUAL_ADDRESS 0x0052 +#define HVCALL_REGISTER_INTERCEPT_RESULT 0x0091 +#define HVCALL_ASSERT_VIRTUAL_INTERRUPT 0x0094 + +#endif /* HW_HYPERV_HVGDK_MINI_H */ diff --git a/include/hw/hyperv/hvhdk.h b/include/hw/hyperv/hvhdk.h new file mode 100644 index 0000000000000..866c8211bfeff --- /dev/null +++ b/include/hw/hyperv/hvhdk.h @@ -0,0 +1,249 @@ +/* + * Type definitions for the mshv host. + * + * Copyright Microsoft, Corp. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_HYPERV_HVHDK_H +#define HW_HYPERV_HVHDK_H + +#define HV_PARTITION_SYNTHETIC_PROCESSOR_FEATURES_BANKS 1 + +struct hv_input_set_partition_property { + uint64_t partition_id; + uint32_t property_code; /* enum hv_partition_property_code */ + uint32_t padding; + uint64_t property_value; +}; + +union hv_partition_synthetic_processor_features { + uint64_t as_uint64[HV_PARTITION_SYNTHETIC_PROCESSOR_FEATURES_BANKS]; + + struct { + /* + * Report a hypervisor is present. CPUID leaves + * 0x40000000 and 0x40000001 are supported. + */ + uint64_t hypervisor_present:1; + + /* + * Features associated with HV#1: + */ + + /* Report support for Hv1 (CPUID leaves 0x40000000 - 0x40000006). */ + uint64_t hv1:1; + + /* + * Access to HV_X64_MSR_VP_RUNTIME. + * Corresponds to access_vp_run_time_reg privilege. + */ + uint64_t access_vp_run_time_reg:1; + + /* + * Access to HV_X64_MSR_TIME_REF_COUNT. + * Corresponds to access_partition_reference_counter privilege. + */ + uint64_t access_partition_reference_counter:1; + + /* + * Access to SINT-related registers (HV_X64_MSR_SCONTROL through + * HV_X64_MSR_EOM and HV_X64_MSR_SINT0 through HV_X64_MSR_SINT15). + * Corresponds to access_synic_regs privilege. + */ + uint64_t access_synic_regs:1; + + /* + * Access to synthetic timers and associated MSRs + * (HV_X64_MSR_STIMER0_CONFIG through HV_X64_MSR_STIMER3_COUNT). + * Corresponds to access_synthetic_timer_regs privilege. + */ + uint64_t access_synthetic_timer_regs:1; + + /* + * Access to APIC MSRs (HV_X64_MSR_EOI, HV_X64_MSR_ICR and + * HV_X64_MSR_TPR) as well as the VP assist page. + * Corresponds to access_intr_ctrl_regs privilege. + */ + uint64_t access_intr_ctrl_regs:1; + + /* + * Access to registers associated with hypercalls + * (HV_X64_MSR_GUEST_OS_ID and HV_X64_MSR_HYPERCALL). + * Corresponds to access_hypercall_msrs privilege. + */ + uint64_t access_hypercall_regs:1; + + /* VP index can be queried. corresponds to access_vp_index privilege. */ + uint64_t access_vp_index:1; + + /* + * Access to the reference TSC. Corresponds to + * access_partition_reference_tsc privilege. + */ + uint64_t access_partition_reference_tsc:1; + + /* + * Partition has access to the guest idle reg. Corresponds to + * access_guest_idle_reg privilege. + */ + uint64_t access_guest_idle_reg:1; + + /* + * Partition has access to frequency regs. corresponds to + * access_frequency_regs privilege. + */ + uint64_t access_frequency_regs:1; + + uint64_t reserved_z12:1; /* Reserved for access_reenlightenment_controls */ + uint64_t reserved_z13:1; /* Reserved for access_root_scheduler_reg */ + uint64_t reserved_z14:1; /* Reserved for access_tsc_invariant_controls */ + + /* + * Extended GVA ranges for HvCallFlushVirtualAddressList hypercall. + * Corresponds to privilege. + */ + uint64_t enable_extended_gva_ranges_for_flush_virtual_address_list:1; + + uint64_t reserved_z16:1; /* Reserved for access_vsm. */ + uint64_t reserved_z17:1; /* Reserved for access_vp_registers. */ + + /* Use fast hypercall output. Corresponds to privilege. */ + uint64_t fast_hypercall_output:1; + + uint64_t reserved_z19:1; /* Reserved for enable_extended_hypercalls. */ + + /* + * HvStartVirtualProcessor can be used to start virtual processors. + * Corresponds to privilege. + */ + uint64_t start_virtual_processor:1; + + uint64_t reserved_z21:1; /* Reserved for Isolation. */ + + /* Synthetic timers in direct mode. */ + uint64_t direct_synthetic_timers:1; + + uint64_t reserved_z23:1; /* Reserved for synthetic time unhalted timer */ + + /* Use extended processor masks. */ + uint64_t extended_processor_masks:1; + + /* + * HvCallFlushVirtualAddressSpace / HvCallFlushVirtualAddressList are + * supported. + */ + uint64_t tb_flush_hypercalls:1; + + /* HvCallSendSyntheticClusterIpi is supported. */ + uint64_t synthetic_cluster_ipi:1; + + /* HvCallNotifyLongSpinWait is supported. */ + uint64_t notify_long_spin_wait:1; + + /* HvCallQueryNumaDistance is supported. */ + uint64_t query_numa_distance:1; + + /* HvCallSignalEvent is supported. Corresponds to privilege. */ + uint64_t signal_events:1; + + /* HvCallRetargetDeviceInterrupt is supported. */ + uint64_t retarget_device_interrupt:1; + + /* HvCallRestorePartitionTime is supported. */ + uint64_t restore_time:1; + + /* EnlightenedVmcs nested enlightenment is supported. */ + uint64_t enlightened_vmcs:1; + + uint64_t reserved:30; + }; +}; + +enum hv_translate_gva_result_code { + HV_TRANSLATE_GVA_SUCCESS = 0, + + /* Translation failures. */ + HV_TRANSLATE_GVA_PAGE_NOT_PRESENT = 1, + HV_TRANSLATE_GVA_PRIVILEGE_VIOLATION = 2, + HV_TRANSLATE_GVA_INVALIDE_PAGE_TABLE_FLAGS = 3, + + /* GPA access failures. */ + HV_TRANSLATE_GVA_GPA_UNMAPPED = 4, + HV_TRANSLATE_GVA_GPA_NO_READ_ACCESS = 5, + HV_TRANSLATE_GVA_GPA_NO_WRITE_ACCESS = 6, + HV_TRANSLATE_GVA_GPA_ILLEGAL_OVERLAY_ACCESS = 7, + + /* + * Intercept for memory access by either + * - a higher VTL + * - a nested hypervisor (due to a violation of the nested page table) + */ + HV_TRANSLATE_GVA_INTERCEPT = 8, + + HV_TRANSLATE_GVA_GPA_UNACCEPTED = 9, +}; + +union hv_translate_gva_result { + uint64_t as_uint64; + struct { + uint32_t result_code; /* enum hv_translate_hva_result_code */ + uint32_t cache_type:8; + uint32_t overlay_page:1; + uint32_t reserved:23; + }; +}; + +typedef struct hv_input_translate_virtual_address { + uint64_t partition_id; + uint32_t vp_index; + uint32_t padding; + uint64_t control_flags; + uint64_t gva_page; +} hv_input_translate_virtual_address; + +typedef struct hv_output_translate_virtual_address { + union hv_translate_gva_result translation_result; + uint64_t gpa_page; +} hv_output_translate_virtual_address; + +typedef struct hv_register_x64_cpuid_result_parameters { + struct { + uint32_t eax; + uint32_t ecx; + uint8_t subleaf_specific; + uint8_t always_override; + uint16_t padding; + } input; + struct { + uint32_t eax; + uint32_t eax_mask; + uint32_t ebx; + uint32_t ebx_mask; + uint32_t ecx; + uint32_t ecx_mask; + uint32_t edx; + uint32_t edx_mask; + } result; +} hv_register_x64_cpuid_result_parameters; + +typedef struct hv_register_x64_msr_result_parameters { + uint32_t msr_index; + uint32_t access_type; + uint32_t action; /* enum hv_unimplemented_msr_action */ +} hv_register_x64_msr_result_parameters; + +union hv_register_intercept_result_parameters { + struct hv_register_x64_cpuid_result_parameters cpuid; + struct hv_register_x64_msr_result_parameters msr; +}; + +typedef struct hv_input_register_intercept_result { + uint64_t partition_id; + uint32_t vp_index; + uint32_t intercept_type; /* enum hv_intercept_type */ + union hv_register_intercept_result_parameters parameters; +} hv_input_register_intercept_result; + +#endif /* HW_HYPERV_HVHDK_H */ diff --git a/include/hw/hyperv/hvhdk_mini.h b/include/hw/hyperv/hvhdk_mini.h new file mode 100644 index 0000000000000..9c2f3cf5aeec8 --- /dev/null +++ b/include/hw/hyperv/hvhdk_mini.h @@ -0,0 +1,102 @@ +/* + * Type definitions for the mshv host interface. + * + * Copyright Microsoft, Corp. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_HYPERV_HVHDK_MINI_H +#define HW_HYPERV_HVHDK_MINI_H + +#define HVHVK_MINI_VERSION (25294) + +/* Each generic set contains 64 elements */ +#define HV_GENERIC_SET_SHIFT (6) +#define HV_GENERIC_SET_MASK (63) + +enum hv_generic_set_format { + HV_GENERIC_SET_SPARSE_4K, + HV_GENERIC_SET_ALL, +}; + +enum hv_partition_property_code { + /* Privilege properties */ + HV_PARTITION_PROPERTY_PRIVILEGE_FLAGS = 0x00010000, + HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES = 0x00010001, + + /* Scheduling properties */ + HV_PARTITION_PROPERTY_SUSPEND = 0x00020000, + HV_PARTITION_PROPERTY_CPU_RESERVE = 0x00020001, + HV_PARTITION_PROPERTY_CPU_CAP = 0x00020002, + HV_PARTITION_PROPERTY_CPU_WEIGHT = 0x00020003, + HV_PARTITION_PROPERTY_CPU_GROUP_ID = 0x00020004, + + /* Time properties */ + HV_PARTITION_PROPERTY_TIME_FREEZE = 0x00030003, + HV_PARTITION_PROPERTY_REFERENCE_TIME = 0x00030005, + + /* Debugging properties */ + HV_PARTITION_PROPERTY_DEBUG_CHANNEL_ID = 0x00040000, + + /* Resource properties */ + HV_PARTITION_PROPERTY_VIRTUAL_TLB_PAGE_COUNT = 0x00050000, + HV_PARTITION_PROPERTY_VSM_CONFIG = 0x00050001, + HV_PARTITION_PROPERTY_ZERO_MEMORY_ON_RESET = 0x00050002, + HV_PARTITION_PROPERTY_PROCESSORS_PER_SOCKET = 0x00050003, + HV_PARTITION_PROPERTY_NESTED_TLB_SIZE = 0x00050004, + HV_PARTITION_PROPERTY_GPA_PAGE_ACCESS_TRACKING = 0x00050005, + HV_PARTITION_PROPERTY_VSM_PERMISSIONS_DIRTY_SINCE_LAST_QUERY = 0x00050006, + HV_PARTITION_PROPERTY_SGX_LAUNCH_CONTROL_CONFIG = 0x00050007, + HV_PARTITION_PROPERTY_DEFAULT_SGX_LAUNCH_CONTROL0 = 0x00050008, + HV_PARTITION_PROPERTY_DEFAULT_SGX_LAUNCH_CONTROL1 = 0x00050009, + HV_PARTITION_PROPERTY_DEFAULT_SGX_LAUNCH_CONTROL2 = 0x0005000a, + HV_PARTITION_PROPERTY_DEFAULT_SGX_LAUNCH_CONTROL3 = 0x0005000b, + HV_PARTITION_PROPERTY_ISOLATION_STATE = 0x0005000c, + HV_PARTITION_PROPERTY_ISOLATION_CONTROL = 0x0005000d, + HV_PARTITION_PROPERTY_ALLOCATION_ID = 0x0005000e, + HV_PARTITION_PROPERTY_MONITORING_ID = 0x0005000f, + HV_PARTITION_PROPERTY_IMPLEMENTED_PHYSICAL_ADDRESS_BITS = 0x00050010, + HV_PARTITION_PROPERTY_NON_ARCHITECTURAL_CORE_SHARING = 0x00050011, + HV_PARTITION_PROPERTY_HYPERCALL_DOORBELL_PAGE = 0x00050012, + HV_PARTITION_PROPERTY_ISOLATION_POLICY = 0x00050014, + HV_PARTITION_PROPERTY_UNIMPLEMENTED_MSR_ACTION = 0x00050017, + HV_PARTITION_PROPERTY_SEV_VMGEXIT_OFFLOADS = 0x00050022, + + /* Compatibility properties */ + HV_PARTITION_PROPERTY_PROCESSOR_VENDOR = 0x00060000, + HV_PARTITION_PROPERTY_PROCESSOR_FEATURES_DEPRECATED = 0x00060001, + HV_PARTITION_PROPERTY_PROCESSOR_XSAVE_FEATURES = 0x00060002, + HV_PARTITION_PROPERTY_PROCESSOR_CL_FLUSH_SIZE = 0x00060003, + HV_PARTITION_PROPERTY_ENLIGHTENMENT_MODIFICATIONS = 0x00060004, + HV_PARTITION_PROPERTY_COMPATIBILITY_VERSION = 0x00060005, + HV_PARTITION_PROPERTY_PHYSICAL_ADDRESS_WIDTH = 0x00060006, + HV_PARTITION_PROPERTY_XSAVE_STATES = 0x00060007, + HV_PARTITION_PROPERTY_MAX_XSAVE_DATA_SIZE = 0x00060008, + HV_PARTITION_PROPERTY_PROCESSOR_CLOCK_FREQUENCY = 0x00060009, + HV_PARTITION_PROPERTY_PROCESSOR_FEATURES0 = 0x0006000a, + HV_PARTITION_PROPERTY_PROCESSOR_FEATURES1 = 0x0006000b, + + /* Guest software properties */ + HV_PARTITION_PROPERTY_GUEST_OS_ID = 0x00070000, + + /* Nested virtualization properties */ + HV_PARTITION_PROPERTY_PROCESSOR_VIRTUALIZATION_FEATURES = 0x00080000, +}; + +/* HV Map GPA (Guest Physical Address) Flags */ +#define HV_MAP_GPA_PERMISSIONS_NONE 0x0 +#define HV_MAP_GPA_READABLE 0x1 +#define HV_MAP_GPA_WRITABLE 0x2 +#define HV_MAP_GPA_KERNEL_EXECUTABLE 0x4 +#define HV_MAP_GPA_USER_EXECUTABLE 0x8 +#define HV_MAP_GPA_EXECUTABLE 0xC +#define HV_MAP_GPA_PERMISSIONS_MASK 0xF +#define HV_MAP_GPA_ADJUSTABLE 0x8000 +#define HV_MAP_GPA_NO_ACCESS 0x10000 +#define HV_MAP_GPA_NOT_CACHED 0x200000 +#define HV_MAP_GPA_LARGE_PAGE 0x80000000 + +#define HV_PFN_RNG_PAGEBITS 24 /* HV_SPA_PAGE_RANGE_ADDITIONAL_PAGES_BITS */ + +#endif /* HW_HYPERV_HVHDK_MINI_H */ diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 717c379f9e095..828a7809f75de 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -195,7 +195,7 @@ rm -rf "$output/linux-headers/linux" mkdir -p "$output/linux-headers/linux" for header in const.h stddef.h kvm.h vfio.h vfio_ccw.h vfio_zdev.h vhost.h \ psci.h psp-sev.h userfaultfd.h memfd.h mman.h nvme_ioctl.h \ - vduse.h iommufd.h bits.h; do + vduse.h iommufd.h bits.h mshv.h; do cp "$hdrdir/include/linux/$header" "$output/linux-headers/linux" done From a6d6878650a0f4982918e8cd9d7ea6c3c3c681f7 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:26 +0200 Subject: [PATCH 1403/1794] linux-headers/linux: Add mshv.h headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This file has been added to the tree by running `update-linux-header.sh` on linux v6.16. Signed-off-by: Magnus Kulke Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/r/20250916164847.77883-7-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- linux-headers/linux/mshv.h | 291 +++++++++++++++++++++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 linux-headers/linux/mshv.h diff --git a/linux-headers/linux/mshv.h b/linux-headers/linux/mshv.h new file mode 100644 index 0000000000000..5bc83db6a3254 --- /dev/null +++ b/linux-headers/linux/mshv.h @@ -0,0 +1,291 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Userspace interfaces for /dev/mshv* devices and derived fds + * + * This file is divided into sections containing data structures and IOCTLs for + * a particular set of related devices or derived file descriptors. + * + * The IOCTL definitions are at the end of each section. They are grouped by + * device/fd, so that new IOCTLs can easily be added with a monotonically + * increasing number. + */ +#ifndef _LINUX_MSHV_H +#define _LINUX_MSHV_H + +#include + +#define MSHV_IOCTL 0xB8 + +/* + ******************************************* + * Entry point to main VMM APIs: /dev/mshv * + ******************************************* + */ + +enum { + MSHV_PT_BIT_LAPIC, + MSHV_PT_BIT_X2APIC, + MSHV_PT_BIT_GPA_SUPER_PAGES, + MSHV_PT_BIT_COUNT, +}; + +#define MSHV_PT_FLAGS_MASK ((1 << MSHV_PT_BIT_COUNT) - 1) + +enum { + MSHV_PT_ISOLATION_NONE, + MSHV_PT_ISOLATION_COUNT, +}; + +/** + * struct mshv_create_partition - arguments for MSHV_CREATE_PARTITION + * @pt_flags: Bitmask of 1 << MSHV_PT_BIT_* + * @pt_isolation: MSHV_PT_ISOLATION_* + * + * Returns a file descriptor to act as a handle to a guest partition. + * At this point the partition is not yet initialized in the hypervisor. + * Some operations must be done with the partition in this state, e.g. setting + * so-called "early" partition properties. The partition can then be + * initialized with MSHV_INITIALIZE_PARTITION. + */ +struct mshv_create_partition { + __u64 pt_flags; + __u64 pt_isolation; +}; + +/* /dev/mshv */ +#define MSHV_CREATE_PARTITION _IOW(MSHV_IOCTL, 0x00, struct mshv_create_partition) + +/* + ************************ + * Child partition APIs * + ************************ + */ + +struct mshv_create_vp { + __u32 vp_index; +}; + +enum { + MSHV_SET_MEM_BIT_WRITABLE, + MSHV_SET_MEM_BIT_EXECUTABLE, + MSHV_SET_MEM_BIT_UNMAP, + MSHV_SET_MEM_BIT_COUNT +}; + +#define MSHV_SET_MEM_FLAGS_MASK ((1 << MSHV_SET_MEM_BIT_COUNT) - 1) + +/* The hypervisor's "native" page size */ +#define MSHV_HV_PAGE_SIZE 0x1000 + +/** + * struct mshv_user_mem_region - arguments for MSHV_SET_GUEST_MEMORY + * @size: Size of the memory region (bytes). Must be aligned to + * MSHV_HV_PAGE_SIZE + * @guest_pfn: Base guest page number to map + * @userspace_addr: Base address of userspace memory. Must be aligned to + * MSHV_HV_PAGE_SIZE + * @flags: Bitmask of 1 << MSHV_SET_MEM_BIT_*. If (1 << MSHV_SET_MEM_BIT_UNMAP) + * is set, ignore other bits. + * @rsvd: MBZ + * + * Map or unmap a region of userspace memory to Guest Physical Addresses (GPA). + * Mappings can't overlap in GPA space or userspace. + * To unmap, these fields must match an existing mapping. + */ +struct mshv_user_mem_region { + __u64 size; + __u64 guest_pfn; + __u64 userspace_addr; + __u8 flags; + __u8 rsvd[7]; +}; + +enum { + MSHV_IRQFD_BIT_DEASSIGN, + MSHV_IRQFD_BIT_RESAMPLE, + MSHV_IRQFD_BIT_COUNT, +}; + +#define MSHV_IRQFD_FLAGS_MASK ((1 << MSHV_IRQFD_BIT_COUNT) - 1) + +struct mshv_user_irqfd { + __s32 fd; + __s32 resamplefd; + __u32 gsi; + __u32 flags; +}; + +enum { + MSHV_IOEVENTFD_BIT_DATAMATCH, + MSHV_IOEVENTFD_BIT_PIO, + MSHV_IOEVENTFD_BIT_DEASSIGN, + MSHV_IOEVENTFD_BIT_COUNT, +}; + +#define MSHV_IOEVENTFD_FLAGS_MASK ((1 << MSHV_IOEVENTFD_BIT_COUNT) - 1) + +struct mshv_user_ioeventfd { + __u64 datamatch; + __u64 addr; /* legal pio/mmio address */ + __u32 len; /* 1, 2, 4, or 8 bytes */ + __s32 fd; + __u32 flags; + __u8 rsvd[4]; +}; + +struct mshv_user_irq_entry { + __u32 gsi; + __u32 address_lo; + __u32 address_hi; + __u32 data; +}; + +struct mshv_user_irq_table { + __u32 nr; + __u32 rsvd; /* MBZ */ + struct mshv_user_irq_entry entries[]; +}; + +enum { + MSHV_GPAP_ACCESS_TYPE_ACCESSED, + MSHV_GPAP_ACCESS_TYPE_DIRTY, + MSHV_GPAP_ACCESS_TYPE_COUNT /* Count of enum members */ +}; + +enum { + MSHV_GPAP_ACCESS_OP_NOOP, + MSHV_GPAP_ACCESS_OP_CLEAR, + MSHV_GPAP_ACCESS_OP_SET, + MSHV_GPAP_ACCESS_OP_COUNT /* Count of enum members */ +}; + +/** + * struct mshv_gpap_access_bitmap - arguments for MSHV_GET_GPAP_ACCESS_BITMAP + * @access_type: MSHV_GPAP_ACCESS_TYPE_* - The type of access to record in the + * bitmap + * @access_op: MSHV_GPAP_ACCESS_OP_* - Allows an optional clear or set of all + * the access states in the range, after retrieving the current + * states. + * @rsvd: MBZ + * @page_count: Number of pages + * @gpap_base: Base gpa page number + * @bitmap_ptr: Output buffer for bitmap, at least (page_count + 7) / 8 bytes + * + * Retrieve a bitmap of either ACCESSED or DIRTY bits for a given range of guest + * memory, and optionally clear or set the bits. + */ +struct mshv_gpap_access_bitmap { + __u8 access_type; + __u8 access_op; + __u8 rsvd[6]; + __u64 page_count; + __u64 gpap_base; + __u64 bitmap_ptr; +}; + +/** + * struct mshv_root_hvcall - arguments for MSHV_ROOT_HVCALL + * @code: Hypercall code (HVCALL_*) + * @reps: in: Rep count ('repcount') + * out: Reps completed ('repcomp'). MBZ unless rep hvcall + * @in_sz: Size of input incl rep data. <= MSHV_HV_PAGE_SIZE + * @out_sz: Size of output buffer. <= MSHV_HV_PAGE_SIZE. MBZ if out_ptr is 0 + * @status: in: MBZ + * out: HV_STATUS_* from hypercall + * @rsvd: MBZ + * @in_ptr: Input data buffer (struct hv_input_*). If used with partition or + * vp fd, partition id field is populated by kernel. + * @out_ptr: Output data buffer (optional) + */ +struct mshv_root_hvcall { + __u16 code; + __u16 reps; + __u16 in_sz; + __u16 out_sz; + __u16 status; + __u8 rsvd[6]; + __u64 in_ptr; + __u64 out_ptr; +}; + +/* Partition fds created with MSHV_CREATE_PARTITION */ +#define MSHV_INITIALIZE_PARTITION _IO(MSHV_IOCTL, 0x00) +#define MSHV_CREATE_VP _IOW(MSHV_IOCTL, 0x01, struct mshv_create_vp) +#define MSHV_SET_GUEST_MEMORY _IOW(MSHV_IOCTL, 0x02, struct mshv_user_mem_region) +#define MSHV_IRQFD _IOW(MSHV_IOCTL, 0x03, struct mshv_user_irqfd) +#define MSHV_IOEVENTFD _IOW(MSHV_IOCTL, 0x04, struct mshv_user_ioeventfd) +#define MSHV_SET_MSI_ROUTING _IOW(MSHV_IOCTL, 0x05, struct mshv_user_irq_table) +#define MSHV_GET_GPAP_ACCESS_BITMAP _IOWR(MSHV_IOCTL, 0x06, struct mshv_gpap_access_bitmap) +/* Generic hypercall */ +#define MSHV_ROOT_HVCALL _IOWR(MSHV_IOCTL, 0x07, struct mshv_root_hvcall) + +/* + ******************************** + * VP APIs for child partitions * + ******************************** + */ + +#define MSHV_RUN_VP_BUF_SZ 256 + +/* + * VP state pages may be mapped to userspace via mmap(). + * To specify which state page, use MSHV_VP_MMAP_OFFSET_ values multiplied by + * the system page size. + * e.g. + * long page_size = sysconf(_SC_PAGE_SIZE); + * void *reg_page = mmap(NULL, MSHV_HV_PAGE_SIZE, PROT_READ|PROT_WRITE, + * MAP_SHARED, vp_fd, + * MSHV_VP_MMAP_OFFSET_REGISTERS * page_size); + */ +enum { + MSHV_VP_MMAP_OFFSET_REGISTERS, + MSHV_VP_MMAP_OFFSET_INTERCEPT_MESSAGE, + MSHV_VP_MMAP_OFFSET_GHCB, + MSHV_VP_MMAP_OFFSET_COUNT +}; + +/** + * struct mshv_run_vp - argument for MSHV_RUN_VP + * @msg_buf: On success, the intercept message is copied here. It can be + * interpreted using the relevant hypervisor definitions. + */ +struct mshv_run_vp { + __u8 msg_buf[MSHV_RUN_VP_BUF_SZ]; +}; + +enum { + MSHV_VP_STATE_LAPIC, /* Local interrupt controller state (either arch) */ + MSHV_VP_STATE_XSAVE, /* XSAVE data in compacted form (x86_64) */ + MSHV_VP_STATE_SIMP, + MSHV_VP_STATE_SIEFP, + MSHV_VP_STATE_SYNTHETIC_TIMERS, + MSHV_VP_STATE_COUNT, +}; + +/** + * struct mshv_get_set_vp_state - arguments for MSHV_[GET,SET]_VP_STATE + * @type: MSHV_VP_STATE_* + * @rsvd: MBZ + * @buf_sz: in: 4k page-aligned size of buffer + * out: Actual size of data (on EINVAL, check this to see if buffer + * was too small) + * @buf_ptr: 4k page-aligned data buffer + */ +struct mshv_get_set_vp_state { + __u8 type; + __u8 rsvd[3]; + __u32 buf_sz; + __u64 buf_ptr; +}; + +/* VP fds created with MSHV_CREATE_VP */ +#define MSHV_RUN_VP _IOR(MSHV_IOCTL, 0x00, struct mshv_run_vp) +#define MSHV_GET_VP_STATE _IOWR(MSHV_IOCTL, 0x01, struct mshv_get_set_vp_state) +#define MSHV_SET_VP_STATE _IOWR(MSHV_IOCTL, 0x02, struct mshv_get_set_vp_state) +/* + * Generic hypercall + * Defined above in partition IOCTLs, avoid redefining it here + * #define MSHV_ROOT_HVCALL _IOWR(MSHV_IOCTL, 0x07, struct mshv_root_hvcall) + */ + +#endif From d0d2918f968c55628e17e2733b799fcefb50f16b Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Thu, 2 Oct 2025 18:25:02 +0200 Subject: [PATCH 1404/1794] accel/mshv: Add accelerator skeleton Introduce the initial scaffold for the MSHV (Microsoft Hypervisor) accelerator backend. This includes the basic directory structure and stub implementations needed to integrate with QEMU's accelerator framework. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-8-magnuskulke@linux.microsoft.com [Move include of linux/mshv.h in the per-target section; create include/system/mshv_int.h. - Paolo] Signed-off-by: Paolo Bonzini --- accel/meson.build | 1 + accel/mshv/meson.build | 6 ++ accel/mshv/mshv-all.c | 144 ++++++++++++++++++++++++++++++++++++++ include/system/mshv.h | 12 ++++ include/system/mshv_int.h | 41 +++++++++++ 5 files changed, 204 insertions(+) create mode 100644 accel/mshv/meson.build create mode 100644 accel/mshv/mshv-all.c create mode 100644 include/system/mshv_int.h diff --git a/accel/meson.build b/accel/meson.build index 6349efe682f29..983dfd0bd55e0 100644 --- a/accel/meson.build +++ b/accel/meson.build @@ -10,6 +10,7 @@ if have_system subdir('kvm') subdir('xen') subdir('stubs') + subdir('mshv') endif # qtest diff --git a/accel/mshv/meson.build b/accel/mshv/meson.build new file mode 100644 index 0000000000000..4c03ac792195b --- /dev/null +++ b/accel/mshv/meson.build @@ -0,0 +1,6 @@ +mshv_ss = ss.source_set() +mshv_ss.add(if_true: files( + 'mshv-all.c' +)) + +specific_ss.add_all(when: 'CONFIG_MSHV', if_true: mshv_ss) diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c new file mode 100644 index 0000000000000..ae12f0f58b93a --- /dev/null +++ b/accel/mshv/mshv-all.c @@ -0,0 +1,144 @@ +/* + * QEMU MSHV support + * + * Copyright Microsoft, Corp. 2025 + * + * Authors: + * Ziqiao Zhou + * Magnus Kulke + * Jinank Jain + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/event_notifier.h" +#include "qemu/module.h" +#include "qemu/main-loop.h" +#include "hw/boards.h" + +#include "hw/hyperv/hvhdk.h" +#include "hw/hyperv/hvhdk_mini.h" +#include "hw/hyperv/hvgdk.h" +#include "linux/mshv.h" + +#include "qemu/accel.h" +#include "qemu/guest-random.h" +#include "accel/accel-ops.h" +#include "accel/accel-cpu-ops.h" +#include "system/cpus.h" +#include "system/runstate.h" +#include "system/accel-blocker.h" +#include "system/address-spaces.h" +#include "system/mshv.h" +#include "system/mshv_int.h" +#include "system/reset.h" +#include "trace.h" +#include +#include +#include + +#define TYPE_MSHV_ACCEL ACCEL_CLASS_NAME("mshv") + +DECLARE_INSTANCE_CHECKER(MshvState, MSHV_STATE, TYPE_MSHV_ACCEL) + +bool mshv_allowed; + +MshvState *mshv_state; + +static int mshv_init(AccelState *as, MachineState *ms) +{ + error_report("unimplemented"); + abort(); +} + +static void mshv_start_vcpu_thread(CPUState *cpu) +{ + error_report("unimplemented"); + abort(); +} + +static void mshv_cpu_synchronize_post_init(CPUState *cpu) +{ + error_report("unimplemented"); + abort(); +} + +static void mshv_cpu_synchronize_post_reset(CPUState *cpu) +{ + error_report("unimplemented"); + abort(); +} + +static void mshv_cpu_synchronize_pre_loadvm(CPUState *cpu) +{ + error_report("unimplemented"); + abort(); +} + +static void mshv_cpu_synchronize(CPUState *cpu) +{ + error_report("unimplemented"); + abort(); +} + +static bool mshv_cpus_are_resettable(void) +{ + error_report("unimplemented"); + abort(); +} + +static void mshv_accel_class_init(ObjectClass *oc, const void *data) +{ + AccelClass *ac = ACCEL_CLASS(oc); + + ac->name = "MSHV"; + ac->init_machine = mshv_init; + ac->allowed = &mshv_allowed; +} + +static void mshv_accel_instance_init(Object *obj) +{ + MshvState *s = MSHV_STATE(obj); + + s->vm = 0; +} + +static const TypeInfo mshv_accel_type = { + .name = TYPE_MSHV_ACCEL, + .parent = TYPE_ACCEL, + .instance_init = mshv_accel_instance_init, + .class_init = mshv_accel_class_init, + .instance_size = sizeof(MshvState), +}; + +static void mshv_accel_ops_class_init(ObjectClass *oc, const void *data) +{ + AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); + + ops->create_vcpu_thread = mshv_start_vcpu_thread; + ops->synchronize_post_init = mshv_cpu_synchronize_post_init; + ops->synchronize_post_reset = mshv_cpu_synchronize_post_reset; + ops->synchronize_state = mshv_cpu_synchronize; + ops->synchronize_pre_loadvm = mshv_cpu_synchronize_pre_loadvm; + ops->cpus_are_resettable = mshv_cpus_are_resettable; + ops->handle_interrupt = generic_handle_interrupt; +} + +static const TypeInfo mshv_accel_ops_type = { + .name = ACCEL_OPS_NAME("mshv"), + .parent = TYPE_ACCEL_OPS, + .class_init = mshv_accel_ops_class_init, + .abstract = true, +}; + +static void mshv_type_init(void) +{ + type_register_static(&mshv_accel_type); + type_register_static(&mshv_accel_ops_type); +} + +type_init(mshv_type_init); diff --git a/include/system/mshv.h b/include/system/mshv.h index 2a504ed81f24a..434ea9682e1b9 100644 --- a/include/system/mshv.h +++ b/include/system/mshv.h @@ -14,8 +14,17 @@ #ifndef QEMU_MSHV_H #define QEMU_MSHV_H +#include "qemu/osdep.h" +#include "qemu/accel.h" +#include "hw/hyperv/hyperv-proto.h" +#include "hw/hyperv/hvhdk.h" +#include "qapi/qapi-types-common.h" +#include "system/memory.h" +#include "accel/accel-ops.h" + #ifdef COMPILING_PER_TARGET #ifdef CONFIG_MSHV +#include #define CONFIG_MSHV_IS_POSSIBLE #endif #else @@ -30,6 +39,9 @@ extern bool mshv_allowed; #endif #define mshv_msi_via_irqfd_enabled() false +typedef struct MshvState MshvState; +extern MshvState *mshv_state; + /* interrupt */ int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev); int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev); diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h new file mode 100644 index 0000000000000..132491b599d96 --- /dev/null +++ b/include/system/mshv_int.h @@ -0,0 +1,41 @@ +/* + * QEMU MSHV support + * + * Copyright Microsoft, Corp. 2025 + * + * Authors: Ziqiao Zhou + * Magnus Kulke + * Jinank Jain + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ + +#ifndef QEMU_MSHV_INT_H +#define QEMU_MSHV_INT_H + +struct AccelCPUState { + int cpufd; + bool dirty; +}; + +typedef struct MshvMemoryListener { + MemoryListener listener; + int as_id; +} MshvMemoryListener; + +typedef struct MshvAddressSpace { + MshvMemoryListener *ml; + AddressSpace *as; +} MshvAddressSpace; + +struct MshvState { + AccelState parent_obj; + int vm; + MshvMemoryListener memory_listener; + /* number of listeners */ + int nr_as; + MshvAddressSpace *as; +}; + +#endif From 5006ea1344d134356a9b2e1afd521cf8df0c6a85 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:28 +0200 Subject: [PATCH 1405/1794] accel/mshv: Register memory region listeners Add memory listener hooks for the MSHV accelerator to track guest memory regions. This enables the backend to respond to region additions, removals and will be used to manage guest memory mappings inside the hypervisor. Actually registering physical memory in the hypervisor is still stubbed out. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-9-magnuskulke@linux.microsoft.com [mshv.h/mshv_int.h split. - Paolo] Signed-off-by: Paolo Bonzini --- accel/mshv/mem.c | 25 +++++++++++++++ accel/mshv/meson.build | 1 + accel/mshv/mshv-all.c | 67 +++++++++++++++++++++++++++++++++++++-- include/system/mshv_int.h | 4 +++ 4 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 accel/mshv/mem.c diff --git a/accel/mshv/mem.c b/accel/mshv/mem.c new file mode 100644 index 0000000000000..9889918c31f2d --- /dev/null +++ b/accel/mshv/mem.c @@ -0,0 +1,25 @@ +/* + * QEMU MSHV support + * + * Copyright Microsoft, Corp. 2025 + * + * Authors: + * Magnus Kulke + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "system/address-spaces.h" +#include "system/mshv.h" +#include "system/mshv_int.h" + +void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section, + bool add) +{ + error_report("unimplemented"); + abort(); +} + diff --git a/accel/mshv/meson.build b/accel/mshv/meson.build index 4c03ac792195b..8a6beb3fb1e82 100644 --- a/accel/mshv/meson.build +++ b/accel/mshv/meson.build @@ -1,5 +1,6 @@ mshv_ss = ss.source_set() mshv_ss.add(if_true: files( + 'mem.c', 'mshv-all.c' )) diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c index ae12f0f58b93a..a684a366775f9 100644 --- a/accel/mshv/mshv-all.c +++ b/accel/mshv/mshv-all.c @@ -49,10 +49,73 @@ bool mshv_allowed; MshvState *mshv_state; +static void mem_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + MshvMemoryListener *mml; + mml = container_of(listener, MshvMemoryListener, listener); + memory_region_ref(section->mr); + mshv_set_phys_mem(mml, section, true); +} + +static void mem_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + MshvMemoryListener *mml; + mml = container_of(listener, MshvMemoryListener, listener); + mshv_set_phys_mem(mml, section, false); + memory_region_unref(section->mr); +} + +static MemoryListener mshv_memory_listener = { + .name = "mshv", + .priority = MEMORY_LISTENER_PRIORITY_ACCEL, + .region_add = mem_region_add, + .region_del = mem_region_del, +}; + +static MemoryListener mshv_io_listener = { + .name = "mshv", .priority = MEMORY_LISTENER_PRIORITY_DEV_BACKEND, + /* MSHV does not support PIO eventfd */ +}; + +static void register_mshv_memory_listener(MshvState *s, MshvMemoryListener *mml, + AddressSpace *as, int as_id, + const char *name) +{ + int i; + + mml->listener = mshv_memory_listener; + mml->listener.name = name; + memory_listener_register(&mml->listener, as); + for (i = 0; i < s->nr_as; ++i) { + if (!s->as[i].as) { + s->as[i].as = as; + s->as[i].ml = mml; + break; + } + } +} + static int mshv_init(AccelState *as, MachineState *ms) { - error_report("unimplemented"); - abort(); + MshvState *s; + s = MSHV_STATE(as); + + accel_blocker_init(); + + s->vm = 0; + + s->nr_as = 1; + s->as = g_new0(MshvAddressSpace, s->nr_as); + + mshv_state = s; + + register_mshv_memory_listener(s, &s->memory_listener, &address_space_memory, + 0, "mshv-memory"); + memory_listener_register(&mshv_io_listener, &address_space_io); + + return 0; } static void mshv_start_vcpu_thread(CPUState *cpu) diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index 132491b599d96..cfa177ff72f9e 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -38,4 +38,8 @@ struct MshvState { MshvAddressSpace *as; }; +/* memory */ +void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section, + bool add); + #endif From c5f23bccde416aab52fe14dec6f6716a85f0cd04 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Thu, 2 Oct 2025 18:28:16 +0200 Subject: [PATCH 1406/1794] accel/mshv: Initialize VM partition Create the MSHV virtual machine by opening a partition and issuing the necessary ioctl to initialize it. This sets up the basic VM structure and initial configuration used by MSHV to manage guest state. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-10-magnuskulke@linux.microsoft.com [Add stubs; fix format strings for trace-events; make mshv_hvcall available only in per-target files; mshv.h/mshv_int.h split. - Paolo] Signed-off-by: Paolo Bonzini --- accel/mshv/irq.c | 399 +++++++++++++++++++++++++++++++++++ accel/mshv/mem.c | 129 ++++++++++- accel/mshv/meson.build | 1 + accel/mshv/mshv-all.c | 326 ++++++++++++++++++++++++++++ accel/mshv/trace-events | 26 +++ accel/mshv/trace.h | 14 ++ accel/stubs/meson.build | 1 + accel/stubs/mshv-stub.c | 44 ++++ hw/intc/apic.c | 8 + include/system/mshv.h | 11 +- include/system/mshv_int.h | 25 +++ meson.build | 1 + target/i386/mshv/meson.build | 1 + target/i386/mshv/mshv-cpu.c | 72 +++++++ 14 files changed, 1054 insertions(+), 4 deletions(-) create mode 100644 accel/mshv/irq.c create mode 100644 accel/mshv/trace-events create mode 100644 accel/mshv/trace.h create mode 100644 accel/stubs/mshv-stub.c create mode 100644 target/i386/mshv/mshv-cpu.c diff --git a/accel/mshv/irq.c b/accel/mshv/irq.c new file mode 100644 index 0000000000000..adf8f337d9c93 --- /dev/null +++ b/accel/mshv/irq.c @@ -0,0 +1,399 @@ +/* + * QEMU MSHV support + * + * Copyright Microsoft, Corp. 2025 + * + * Authors: Ziqiao Zhou + * Magnus Kulke + * Stanislav Kinsburskii + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "linux/mshv.h" +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "hw/hyperv/hvhdk_mini.h" +#include "hw/hyperv/hvgdk_mini.h" +#include "hw/intc/ioapic.h" +#include "hw/pci/msi.h" +#include "system/mshv.h" +#include "system/mshv_int.h" +#include "trace.h" +#include +#include + +#define MSHV_IRQFD_RESAMPLE_FLAG (1 << MSHV_IRQFD_BIT_RESAMPLE) +#define MSHV_IRQFD_BIT_DEASSIGN_FLAG (1 << MSHV_IRQFD_BIT_DEASSIGN) + +static MshvMsiControl *msi_control; +static QemuMutex msi_control_mutex; + +void mshv_init_msicontrol(void) +{ + qemu_mutex_init(&msi_control_mutex); + msi_control = g_new0(MshvMsiControl, 1); + msi_control->gsi_routes = g_hash_table_new(g_direct_hash, g_direct_equal); + msi_control->updated = false; +} + +static int set_msi_routing(uint32_t gsi, uint64_t addr, uint32_t data) +{ + struct mshv_user_irq_entry *entry; + uint32_t high_addr = addr >> 32; + uint32_t low_addr = addr & 0xFFFFFFFF; + GHashTable *gsi_routes; + + trace_mshv_set_msi_routing(gsi, addr, data); + + if (gsi >= MSHV_MAX_MSI_ROUTES) { + error_report("gsi >= MSHV_MAX_MSI_ROUTES"); + return -1; + } + + assert(msi_control); + + WITH_QEMU_LOCK_GUARD(&msi_control_mutex) { + gsi_routes = msi_control->gsi_routes; + entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi)); + + if (entry + && entry->address_hi == high_addr + && entry->address_lo == low_addr + && entry->data == data) + { + /* nothing to update */ + return 0; + } + + /* free old entry */ + g_free(entry); + + /* create new entry */ + entry = g_new0(struct mshv_user_irq_entry, 1); + entry->gsi = gsi; + entry->address_hi = high_addr; + entry->address_lo = low_addr; + entry->data = data; + + g_hash_table_insert(gsi_routes, GINT_TO_POINTER(gsi), entry); + msi_control->updated = true; + } + + return 0; +} + +static int add_msi_routing(uint64_t addr, uint32_t data) +{ + struct mshv_user_irq_entry *route_entry; + uint32_t high_addr = addr >> 32; + uint32_t low_addr = addr & 0xFFFFFFFF; + int gsi; + GHashTable *gsi_routes; + + trace_mshv_add_msi_routing(addr, data); + + assert(msi_control); + + WITH_QEMU_LOCK_GUARD(&msi_control_mutex) { + /* find an empty slot */ + gsi = 0; + gsi_routes = msi_control->gsi_routes; + while (gsi < MSHV_MAX_MSI_ROUTES) { + route_entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi)); + if (!route_entry) { + break; + } + gsi++; + } + if (gsi >= MSHV_MAX_MSI_ROUTES) { + error_report("No empty gsi slot available"); + return -1; + } + + /* create new entry */ + route_entry = g_new0(struct mshv_user_irq_entry, 1); + route_entry->gsi = gsi; + route_entry->address_hi = high_addr; + route_entry->address_lo = low_addr; + route_entry->data = data; + + g_hash_table_insert(gsi_routes, GINT_TO_POINTER(gsi), route_entry); + msi_control->updated = true; + } + + return gsi; +} + +static int commit_msi_routing_table(int vm_fd) +{ + guint len; + int i, ret; + size_t table_size; + struct mshv_user_irq_table *table; + GHashTableIter iter; + gpointer key, value; + + assert(msi_control); + + WITH_QEMU_LOCK_GUARD(&msi_control_mutex) { + if (!msi_control->updated) { + /* nothing to update */ + return 0; + } + + /* Calculate the size of the table */ + len = g_hash_table_size(msi_control->gsi_routes); + table_size = sizeof(struct mshv_user_irq_table) + + len * sizeof(struct mshv_user_irq_entry); + table = g_malloc0(table_size); + + g_hash_table_iter_init(&iter, msi_control->gsi_routes); + i = 0; + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct mshv_user_irq_entry *entry = value; + table->entries[i] = *entry; + i++; + } + table->nr = i; + + trace_mshv_commit_msi_routing_table(vm_fd, len); + + ret = ioctl(vm_fd, MSHV_SET_MSI_ROUTING, table); + g_free(table); + if (ret < 0) { + error_report("Failed to commit msi routing table"); + return -1; + } + msi_control->updated = false; + } + return 0; +} + +static int remove_msi_routing(uint32_t gsi) +{ + struct mshv_user_irq_entry *route_entry; + GHashTable *gsi_routes; + + trace_mshv_remove_msi_routing(gsi); + + if (gsi >= MSHV_MAX_MSI_ROUTES) { + error_report("Invalid GSI: %u", gsi); + return -1; + } + + assert(msi_control); + + WITH_QEMU_LOCK_GUARD(&msi_control_mutex) { + gsi_routes = msi_control->gsi_routes; + route_entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi)); + if (route_entry) { + g_hash_table_remove(gsi_routes, GINT_TO_POINTER(gsi)); + g_free(route_entry); + msi_control->updated = true; + } + } + + return 0; +} + +/* Pass an eventfd which is to be used for injecting interrupts from userland */ +static int irqfd(int vm_fd, int fd, int resample_fd, uint32_t gsi, + uint32_t flags) +{ + int ret; + struct mshv_user_irqfd arg = { + .fd = fd, + .resamplefd = resample_fd, + .gsi = gsi, + .flags = flags, + }; + + ret = ioctl(vm_fd, MSHV_IRQFD, &arg); + if (ret < 0) { + error_report("Failed to set irqfd: gsi=%u, fd=%d", gsi, fd); + return -1; + } + return ret; +} + +static int register_irqfd(int vm_fd, int event_fd, uint32_t gsi) +{ + int ret; + + trace_mshv_register_irqfd(vm_fd, event_fd, gsi); + + ret = irqfd(vm_fd, event_fd, 0, gsi, 0); + if (ret < 0) { + error_report("Failed to register irqfd: gsi=%u", gsi); + return -1; + } + return 0; +} + +static int register_irqfd_with_resample(int vm_fd, int event_fd, + int resample_fd, uint32_t gsi) +{ + int ret; + uint32_t flags = MSHV_IRQFD_RESAMPLE_FLAG; + + ret = irqfd(vm_fd, event_fd, resample_fd, gsi, flags); + if (ret < 0) { + error_report("Failed to register irqfd with resample: gsi=%u", gsi); + return -errno; + } + return 0; +} + +static int unregister_irqfd(int vm_fd, int event_fd, uint32_t gsi) +{ + int ret; + uint32_t flags = MSHV_IRQFD_BIT_DEASSIGN_FLAG; + + ret = irqfd(vm_fd, event_fd, 0, gsi, flags); + if (ret < 0) { + error_report("Failed to unregister irqfd: gsi=%u", gsi); + return -errno; + } + return 0; +} + +static int irqchip_update_irqfd_notifier_gsi(const EventNotifier *event, + const EventNotifier *resample, + int virq, bool add) +{ + int fd = event_notifier_get_fd(event); + int rfd = resample ? event_notifier_get_fd(resample) : -1; + int vm_fd = mshv_state->vm; + + trace_mshv_irqchip_update_irqfd_notifier_gsi(fd, rfd, virq, add); + + if (!add) { + return unregister_irqfd(vm_fd, fd, virq); + } + + if (rfd > 0) { + return register_irqfd_with_resample(vm_fd, fd, rfd, virq); + } + + return register_irqfd(vm_fd, fd, virq); +} + + +int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev) +{ + MSIMessage msg = { 0, 0 }; + int virq = 0; + + if (pci_available && dev) { + msg = pci_get_msi_message(dev, vector); + virq = add_msi_routing(msg.address, le32_to_cpu(msg.data)); + } + + return virq; +} + +void mshv_irqchip_release_virq(int virq) +{ + remove_msi_routing(virq); +} + +int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev) +{ + int ret; + + ret = set_msi_routing(virq, msg.address, le32_to_cpu(msg.data)); + if (ret < 0) { + error_report("Failed to set msi routing"); + return -1; + } + + return 0; +} + +int mshv_request_interrupt(MshvState *mshv_state, uint32_t interrupt_type, uint32_t vector, + uint32_t vp_index, bool logical_dest_mode, + bool level_triggered) +{ + int ret; + int vm_fd = mshv_state->vm; + + if (vector == 0) { + warn_report("Ignoring request for interrupt vector 0"); + return 0; + } + + union hv_interrupt_control control = { + .interrupt_type = interrupt_type, + .level_triggered = level_triggered, + .logical_dest_mode = logical_dest_mode, + .rsvd = 0, + }; + + struct hv_input_assert_virtual_interrupt arg = {0}; + arg.control = control; + arg.dest_addr = (uint64_t)vp_index; + arg.vector = vector; + + struct mshv_root_hvcall args = {0}; + args.code = HVCALL_ASSERT_VIRTUAL_INTERRUPT; + args.in_sz = sizeof(arg); + args.in_ptr = (uint64_t)&arg; + + ret = mshv_hvcall(vm_fd, &args); + if (ret < 0) { + error_report("Failed to request interrupt"); + return -errno; + } + return 0; +} + +void mshv_irqchip_commit_routes(void) +{ + int ret; + int vm_fd = mshv_state->vm; + + ret = commit_msi_routing_table(vm_fd); + if (ret < 0) { + error_report("Failed to commit msi routing table"); + abort(); + } +} + +int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *event, + const EventNotifier *resample, + int virq) +{ + return irqchip_update_irqfd_notifier_gsi(event, resample, virq, true); +} + +int mshv_irqchip_remove_irqfd_notifier_gsi(const EventNotifier *event, + int virq) +{ + return irqchip_update_irqfd_notifier_gsi(event, NULL, virq, false); +} + +int mshv_reserve_ioapic_msi_routes(int vm_fd) +{ + int ret, gsi; + + /* + * Reserve GSI 0-23 for IOAPIC pins, to avoid conflicts of legacy + * peripherals with MSI-X devices + */ + for (gsi = 0; gsi < IOAPIC_NUM_PINS; gsi++) { + ret = add_msi_routing(0, 0); + if (ret < 0) { + error_report("Failed to reserve GSI %d", gsi); + return -1; + } + } + + ret = commit_msi_routing_table(vm_fd); + if (ret < 0) { + error_report("Failed to commit reserved IOAPIC MSI routes"); + return -1; + } + + return 0; +} diff --git a/accel/mshv/mem.c b/accel/mshv/mem.c index 9889918c31f2d..a0a40eb3333c6 100644 --- a/accel/mshv/mem.c +++ b/accel/mshv/mem.c @@ -12,14 +12,137 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "linux/mshv.h" #include "system/address-spaces.h" #include "system/mshv.h" #include "system/mshv_int.h" +#include "exec/memattrs.h" +#include +#include "trace.h" + +static int set_guest_memory(int vm_fd, + const struct mshv_user_mem_region *region) +{ + int ret; + + ret = ioctl(vm_fd, MSHV_SET_GUEST_MEMORY, region); + if (ret < 0) { + error_report("failed to set guest memory"); + return -errno; + } + + return 0; +} + +static int map_or_unmap(int vm_fd, const MshvMemoryRegion *mr, bool map) +{ + struct mshv_user_mem_region region = {0}; + + region.guest_pfn = mr->guest_phys_addr >> MSHV_PAGE_SHIFT; + region.size = mr->memory_size; + region.userspace_addr = mr->userspace_addr; + + if (!map) { + region.flags |= (1 << MSHV_SET_MEM_BIT_UNMAP); + trace_mshv_unmap_memory(mr->userspace_addr, mr->guest_phys_addr, + mr->memory_size); + return set_guest_memory(vm_fd, ®ion); + } + + region.flags = BIT(MSHV_SET_MEM_BIT_EXECUTABLE); + if (!mr->readonly) { + region.flags |= BIT(MSHV_SET_MEM_BIT_WRITABLE); + } + + trace_mshv_map_memory(mr->userspace_addr, mr->guest_phys_addr, + mr->memory_size); + return set_guest_memory(vm_fd, ®ion); +} + +static int set_memory(const MshvMemoryRegion *mshv_mr, bool add) +{ + int ret = 0; + + if (!mshv_mr) { + error_report("Invalid mshv_mr"); + return -1; + } + + trace_mshv_set_memory(add, mshv_mr->guest_phys_addr, + mshv_mr->memory_size, + mshv_mr->userspace_addr, mshv_mr->readonly, + ret); + return map_or_unmap(mshv_state->vm, mshv_mr, add); +} + +/* + * Calculate and align the start address and the size of the section. + * Return the size. If the size is 0, the aligned section is empty. + */ +static hwaddr align_section(MemoryRegionSection *section, hwaddr *start) +{ + hwaddr size = int128_get64(section->size); + hwaddr delta, aligned; + + /* + * works in page size chunks, but the function may be called + * with sub-page size and unaligned start address. Pad the start + * address to next and truncate size to previous page boundary. + */ + aligned = ROUND_UP(section->offset_within_address_space, + qemu_real_host_page_size()); + delta = aligned - section->offset_within_address_space; + *start = aligned; + if (delta > size) { + return 0; + } + + return (size - delta) & qemu_real_host_page_mask(); +} void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section, bool add) { - error_report("unimplemented"); - abort(); -} + int ret = 0; + MemoryRegion *area = section->mr; + bool writable = !area->readonly && !area->rom_device; + hwaddr start_addr, mr_offset, size; + void *ram; + MshvMemoryRegion mshv_mr = {0}; + + size = align_section(section, &start_addr); + trace_mshv_set_phys_mem(add, section->mr->name, start_addr); + + /* + * If the memory device is a writable non-ram area, we do not + * want to map it into the guest memory. If it is not a ROM device, + * we want to remove mshv memory mapping, so accesses will trap. + */ + if (!memory_region_is_ram(area)) { + if (writable) { + return; + } else if (!area->romd_mode) { + add = false; + } + } + + if (!size) { + return; + } + mr_offset = section->offset_within_region + start_addr - + section->offset_within_address_space; + + ram = memory_region_get_ram_ptr(area) + mr_offset; + + mshv_mr.guest_phys_addr = start_addr; + mshv_mr.memory_size = size; + mshv_mr.readonly = !writable; + mshv_mr.userspace_addr = (uint64_t)ram; + + ret = set_memory(&mshv_mr, add); + if (ret < 0) { + error_report("Failed to set memory region"); + abort(); + } +} diff --git a/accel/mshv/meson.build b/accel/mshv/meson.build index 8a6beb3fb1e82..f88fc8678c3d6 100644 --- a/accel/mshv/meson.build +++ b/accel/mshv/meson.build @@ -1,5 +1,6 @@ mshv_ss = ss.source_set() mshv_ss.add(if_true: files( + 'irq.c', 'mem.c', 'mshv-all.c' )) diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c index a684a366775f9..653195c57c996 100644 --- a/accel/mshv/mshv-all.c +++ b/accel/mshv/mshv-all.c @@ -7,6 +7,7 @@ * Ziqiao Zhou * Magnus Kulke * Jinank Jain + * Wei Liu * * SPDX-License-Identifier: GPL-2.0-or-later * @@ -23,6 +24,7 @@ #include "hw/hyperv/hvhdk.h" #include "hw/hyperv/hvhdk_mini.h" #include "hw/hyperv/hvgdk.h" +#include "hw/hyperv/hvgdk_mini.h" #include "linux/mshv.h" #include "qemu/accel.h" @@ -49,6 +51,175 @@ bool mshv_allowed; MshvState *mshv_state; +static int init_mshv(int *mshv_fd) +{ + int fd = open("/dev/mshv", O_RDWR | O_CLOEXEC); + if (fd < 0) { + error_report("Failed to open /dev/mshv: %s", strerror(errno)); + return -1; + } + *mshv_fd = fd; + return 0; +} + +/* freeze 1 to pause, 0 to resume */ +static int set_time_freeze(int vm_fd, int freeze) +{ + int ret; + struct hv_input_set_partition_property in = {0}; + in.property_code = HV_PARTITION_PROPERTY_TIME_FREEZE; + in.property_value = freeze; + + struct mshv_root_hvcall args = {0}; + args.code = HVCALL_SET_PARTITION_PROPERTY; + args.in_sz = sizeof(in); + args.in_ptr = (uint64_t)∈ + + ret = mshv_hvcall(vm_fd, &args); + if (ret < 0) { + error_report("Failed to set time freeze"); + return -1; + } + + return 0; +} + +static int pause_vm(int vm_fd) +{ + int ret; + + ret = set_time_freeze(vm_fd, 1); + if (ret < 0) { + error_report("Failed to pause partition: %s", strerror(errno)); + return -1; + } + + return 0; +} + +static int resume_vm(int vm_fd) +{ + int ret; + + ret = set_time_freeze(vm_fd, 0); + if (ret < 0) { + error_report("Failed to resume partition: %s", strerror(errno)); + return -1; + } + + return 0; +} + +static int create_partition(int mshv_fd, int *vm_fd) +{ + int ret; + struct mshv_create_partition args = {0}; + + /* Initialize pt_flags with the desired features */ + uint64_t pt_flags = (1ULL << MSHV_PT_BIT_LAPIC) | + (1ULL << MSHV_PT_BIT_X2APIC) | + (1ULL << MSHV_PT_BIT_GPA_SUPER_PAGES); + + /* Set default isolation type */ + uint64_t pt_isolation = MSHV_PT_ISOLATION_NONE; + + args.pt_flags = pt_flags; + args.pt_isolation = pt_isolation; + + ret = ioctl(mshv_fd, MSHV_CREATE_PARTITION, &args); + if (ret < 0) { + error_report("Failed to create partition: %s", strerror(errno)); + return -1; + } + + *vm_fd = ret; + return 0; +} + +static int set_synthetic_proc_features(int vm_fd) +{ + int ret; + struct hv_input_set_partition_property in = {0}; + union hv_partition_synthetic_processor_features features = {0}; + + /* Access the bitfield and set the desired features */ + features.hypervisor_present = 1; + features.hv1 = 1; + features.access_partition_reference_counter = 1; + features.access_synic_regs = 1; + features.access_synthetic_timer_regs = 1; + features.access_partition_reference_tsc = 1; + features.access_frequency_regs = 1; + features.access_intr_ctrl_regs = 1; + features.access_vp_index = 1; + features.access_hypercall_regs = 1; + features.tb_flush_hypercalls = 1; + features.synthetic_cluster_ipi = 1; + features.direct_synthetic_timers = 1; + + mshv_arch_amend_proc_features(&features); + + in.property_code = HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES; + in.property_value = features.as_uint64[0]; + + struct mshv_root_hvcall args = {0}; + args.code = HVCALL_SET_PARTITION_PROPERTY; + args.in_sz = sizeof(in); + args.in_ptr = (uint64_t)∈ + + trace_mshv_hvcall_args("synthetic_proc_features", args.code, args.in_sz); + + ret = mshv_hvcall(vm_fd, &args); + if (ret < 0) { + error_report("Failed to set synthethic proc features"); + return -errno; + } + return 0; +} + +static int initialize_vm(int vm_fd) +{ + int ret = ioctl(vm_fd, MSHV_INITIALIZE_PARTITION); + if (ret < 0) { + error_report("Failed to initialize partition: %s", strerror(errno)); + return -1; + } + return 0; +} + +static int create_vm(int mshv_fd, int *vm_fd) +{ + int ret = create_partition(mshv_fd, vm_fd); + if (ret < 0) { + return -1; + } + + ret = set_synthetic_proc_features(*vm_fd); + if (ret < 0) { + return -1; + } + + ret = initialize_vm(*vm_fd); + if (ret < 0) { + return -1; + } + + ret = mshv_reserve_ioapic_msi_routes(*vm_fd); + if (ret < 0) { + return -1; + } + + ret = mshv_arch_post_init_vm(*vm_fd); + if (ret < 0) { + return -1; + } + + /* Always create a frozen partition */ + pause_vm(*vm_fd); + + return 0; +} + static void mem_region_add(MemoryListener *listener, MemoryRegionSection *section) { @@ -67,11 +238,124 @@ static void mem_region_del(MemoryListener *listener, memory_region_unref(section->mr); } +typedef enum { + DATAMATCH_NONE, + DATAMATCH_U32, + DATAMATCH_U64, +} DatamatchTag; + +typedef struct { + DatamatchTag tag; + union { + uint32_t u32; + uint64_t u64; + } value; +} Datamatch; + +/* flags: determine whether to de/assign */ +static int ioeventfd(int vm_fd, int event_fd, uint64_t addr, Datamatch dm, + uint32_t flags) +{ + struct mshv_user_ioeventfd args = {0}; + args.fd = event_fd; + args.addr = addr; + args.flags = flags; + + if (dm.tag == DATAMATCH_NONE) { + args.datamatch = 0; + } else { + flags |= BIT(MSHV_IOEVENTFD_BIT_DATAMATCH); + args.flags = flags; + if (dm.tag == DATAMATCH_U64) { + args.len = sizeof(uint64_t); + args.datamatch = dm.value.u64; + } else { + args.len = sizeof(uint32_t); + args.datamatch = dm.value.u32; + } + } + + return ioctl(vm_fd, MSHV_IOEVENTFD, &args); +} + +static int unregister_ioevent(int vm_fd, int event_fd, uint64_t mmio_addr) +{ + uint32_t flags = 0; + Datamatch dm = {0}; + + flags |= BIT(MSHV_IOEVENTFD_BIT_DEASSIGN); + dm.tag = DATAMATCH_NONE; + + return ioeventfd(vm_fd, event_fd, mmio_addr, dm, flags); +} + +static int register_ioevent(int vm_fd, int event_fd, uint64_t mmio_addr, + uint64_t val, bool is_64bit, bool is_datamatch) +{ + uint32_t flags = 0; + Datamatch dm = {0}; + + if (!is_datamatch) { + dm.tag = DATAMATCH_NONE; + } else if (is_64bit) { + dm.tag = DATAMATCH_U64; + dm.value.u64 = val; + } else { + dm.tag = DATAMATCH_U32; + dm.value.u32 = val; + } + + return ioeventfd(vm_fd, event_fd, mmio_addr, dm, flags); +} + +static void mem_ioeventfd_add(MemoryListener *listener, + MemoryRegionSection *section, + bool match_data, uint64_t data, + EventNotifier *e) +{ + int fd = event_notifier_get_fd(e); + int ret; + bool is_64 = int128_get64(section->size) == 8; + uint64_t addr = section->offset_within_address_space; + + trace_mshv_mem_ioeventfd_add(addr, int128_get64(section->size), data); + + ret = register_ioevent(mshv_state->vm, fd, addr, data, is_64, match_data); + + if (ret < 0) { + error_report("Failed to register ioeventfd: %s (%d)", strerror(-ret), + -ret); + abort(); + } +} + +static void mem_ioeventfd_del(MemoryListener *listener, + MemoryRegionSection *section, + bool match_data, uint64_t data, + EventNotifier *e) +{ + int fd = event_notifier_get_fd(e); + int ret; + uint64_t addr = section->offset_within_address_space; + + trace_mshv_mem_ioeventfd_del(section->offset_within_address_space, + int128_get64(section->size), data); + + ret = unregister_ioevent(mshv_state->vm, fd, addr); + if (ret < 0) { + error_report("Failed to unregister ioeventfd: %s (%d)", strerror(-ret), + -ret); + abort(); + } +} + static MemoryListener mshv_memory_listener = { .name = "mshv", .priority = MEMORY_LISTENER_PRIORITY_ACCEL, .region_add = mem_region_add, .region_del = mem_region_del, + .eventfd_add = mem_ioeventfd_add, + .eventfd_del = mem_ioeventfd_del, }; static MemoryListener mshv_io_listener = { @@ -97,15 +381,57 @@ static void register_mshv_memory_listener(MshvState *s, MshvMemoryListener *mml, } } +int mshv_hvcall(int fd, const struct mshv_root_hvcall *args) +{ + int ret = 0; + + ret = ioctl(fd, MSHV_ROOT_HVCALL, args); + if (ret < 0) { + error_report("Failed to perform hvcall: %s", strerror(errno)); + return -1; + } + return ret; +} + + static int mshv_init(AccelState *as, MachineState *ms) { MshvState *s; + int mshv_fd, vm_fd, ret; + + if (mshv_state) { + warn_report("MSHV accelerator already initialized"); + return 0; + } + s = MSHV_STATE(as); accel_blocker_init(); s->vm = 0; + ret = init_mshv(&mshv_fd); + if (ret < 0) { + return -1; + } + + mshv_init_msicontrol(); + + ret = create_vm(mshv_fd, &vm_fd); + if (ret < 0) { + close(mshv_fd); + return -1; + } + + ret = resume_vm(vm_fd); + if (ret < 0) { + close(mshv_fd); + close(vm_fd); + return -1; + } + + s->vm = vm_fd; + s->fd = mshv_fd; s->nr_as = 1; s->as = g_new0(MshvAddressSpace, s->nr_as); diff --git a/accel/mshv/trace-events b/accel/mshv/trace-events new file mode 100644 index 0000000000000..6130c4abf8af8 --- /dev/null +++ b/accel/mshv/trace-events @@ -0,0 +1,26 @@ +# Authors: Ziqiao Zhou +# Magnus Kulke +# +# SPDX-License-Identifier: GPL-2.0-or-later + +mshv_set_memory(bool add, uint64_t gpa, uint64_t size, uint64_t user_addr, bool readonly, int ret) "add=%d gpa=0x%" PRIx64 " size=0x%" PRIx64 " user=0x%" PRIx64 " readonly=%d result=%d" +mshv_mem_ioeventfd_add(uint64_t addr, uint32_t size, uint32_t data) "addr=0x%" PRIx64 " size=%d data=0x%x" +mshv_mem_ioeventfd_del(uint64_t addr, uint32_t size, uint32_t data) "addr=0x%" PRIx64 " size=%d data=0x%x" + +mshv_hvcall_args(const char* hvcall, uint16_t code, uint16_t in_sz) "built args for '%s' code: %d in_sz: %d" + +mshv_handle_interrupt(uint32_t cpu, int mask) "cpu_index=%d mask=0x%x" +mshv_set_msi_routing(uint32_t gsi, uint64_t addr, uint32_t data) "gsi=%d addr=0x%" PRIx64 " data=0x%x" +mshv_remove_msi_routing(uint32_t gsi) "gsi=%d" +mshv_add_msi_routing(uint64_t addr, uint32_t data) "addr=0x%" PRIx64 " data=0x%x" +mshv_commit_msi_routing_table(int vm_fd, int len) "vm_fd=%d table_size=%d" +mshv_register_irqfd(int vm_fd, int event_fd, uint32_t gsi) "vm_fd=%d event_fd=%d gsi=%d" +mshv_irqchip_update_irqfd_notifier_gsi(int event_fd, int resample_fd, int virq, bool add) "event_fd=%d resample_fd=%d virq=%d add=%d" + +mshv_insn_fetch(uint64_t addr, size_t size) "gpa=0x%" PRIx64 " size=%zu" +mshv_mem_write(uint64_t addr, size_t size) "\tgpa=0x%" PRIx64 " size=%zu" +mshv_mem_read(uint64_t addr, size_t size) "\tgpa=0x%" PRIx64 " size=%zu" +mshv_map_memory(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64 +mshv_unmap_memory(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64 +mshv_set_phys_mem(bool add, const char *name, uint64_t gpa) "\tadd=%d name=%s gpa=0x%010" PRIx64 +mshv_handle_mmio(uint64_t gva, uint64_t gpa, uint64_t size, uint8_t access_type) "\tgva=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%" PRIx64 " access_type=%d" diff --git a/accel/mshv/trace.h b/accel/mshv/trace.h new file mode 100644 index 0000000000000..0dca48f9179f1 --- /dev/null +++ b/accel/mshv/trace.h @@ -0,0 +1,14 @@ +/* + * QEMU MSHV support + * + * Copyright Microsoft, Corp. 2025 + * + * Authors: + * Ziqiao Zhou + * Magnus Kulke + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ + +#include "trace/trace-accel_mshv.h" diff --git a/accel/stubs/meson.build b/accel/stubs/meson.build index 9dfc4f9ddaf5b..48eccd1b86170 100644 --- a/accel/stubs/meson.build +++ b/accel/stubs/meson.build @@ -5,5 +5,6 @@ system_stubs_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c')) system_stubs_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c')) system_stubs_ss.add(when: 'CONFIG_NVMM', if_false: files('nvmm-stub.c')) system_stubs_ss.add(when: 'CONFIG_WHPX', if_false: files('whpx-stub.c')) +system_stubs_ss.add(when: 'CONFIG_MSHV', if_false: files('mshv-stub.c')) specific_ss.add_all(when: ['CONFIG_SYSTEM_ONLY'], if_true: system_stubs_ss) diff --git a/accel/stubs/mshv-stub.c b/accel/stubs/mshv-stub.c new file mode 100644 index 0000000000000..e499b199d9dd5 --- /dev/null +++ b/accel/stubs/mshv-stub.c @@ -0,0 +1,44 @@ +/* + * QEMU MSHV stub + * + * Copyright Red Hat, Inc. 2025 + * + * Author: Paolo Bonzini + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/pci/msi.h" +#include "system/mshv.h" + +bool mshv_allowed; + +int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev) +{ + return -ENOSYS; +} + +void mshv_irqchip_release_virq(int virq) +{ +} + +int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev) +{ + return -ENOSYS; +} + +void mshv_irqchip_commit_routes(void) +{ +} + +int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *n, + const EventNotifier *rn, int virq) +{ + return -ENOSYS; +} + +int mshv_irqchip_remove_irqfd_notifier_gsi(const EventNotifier *n, int virq) +{ + return -ENOSYS; +} diff --git a/hw/intc/apic.c b/hw/intc/apic.c index bcb103560c726..6d7859640c275 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -27,6 +27,7 @@ #include "hw/pci/msi.h" #include "qemu/host-utils.h" #include "system/kvm.h" +#include "system/mshv.h" #include "trace.h" #include "hw/i386/apic-msidef.h" #include "qapi/error.h" @@ -932,6 +933,13 @@ static void apic_send_msi(MSIMessage *msi) uint8_t trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; uint8_t delivery = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7; /* XXX: Ignore redirection hint. */ +#ifdef CONFIG_MSHV + if (mshv_enabled()) { + mshv_request_interrupt(mshv_state, delivery, vector, dest, + dest_mode, trigger_mode); + return; + } +#endif apic_deliver_irq(dest, dest_mode, delivery, vector, trigger_mode); } diff --git a/include/system/mshv.h b/include/system/mshv.h index 434ea9682e1b9..1011e81df478a 100644 --- a/include/system/mshv.h +++ b/include/system/mshv.h @@ -31,18 +31,27 @@ #define CONFIG_MSHV_IS_POSSIBLE #endif +#define MSHV_MAX_MSI_ROUTES 4096 + +#define MSHV_PAGE_SHIFT 12 + #ifdef CONFIG_MSHV_IS_POSSIBLE extern bool mshv_allowed; #define mshv_enabled() (mshv_allowed) +#define mshv_msi_via_irqfd_enabled() mshv_enabled() #else /* CONFIG_MSHV_IS_POSSIBLE */ #define mshv_enabled() false -#endif #define mshv_msi_via_irqfd_enabled() false +#endif typedef struct MshvState MshvState; extern MshvState *mshv_state; /* interrupt */ +int mshv_request_interrupt(MshvState *mshv_state, uint32_t interrupt_type, uint32_t vector, + uint32_t vp_index, bool logical_destination_mode, + bool level_triggered); + int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev); int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev); void mshv_irqchip_commit_routes(void); diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index cfa177ff72f9e..b36124a0ea703 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -36,10 +36,35 @@ struct MshvState { /* number of listeners */ int nr_as; MshvAddressSpace *as; + int fd; }; +typedef struct MshvMsiControl { + bool updated; + GHashTable *gsi_routes; +} MshvMsiControl; + +void mshv_arch_amend_proc_features( + union hv_partition_synthetic_processor_features *features); +int mshv_arch_post_init_vm(int vm_fd); + +#if defined COMPILING_PER_TARGET && defined CONFIG_MSHV_IS_POSSIBLE +int mshv_hvcall(int fd, const struct mshv_root_hvcall *args); +#endif + /* memory */ +typedef struct MshvMemoryRegion { + uint64_t guest_phys_addr; + uint64_t memory_size; + uint64_t userspace_addr; + bool readonly; +} MshvMemoryRegion; + void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section, bool add); +/* interrupt */ +void mshv_init_msicontrol(void); +int mshv_reserve_ioapic_msi_routes(int vm_fd); + #endif diff --git a/meson.build b/meson.build index 167021ed621bd..afaefa0172224 100644 --- a/meson.build +++ b/meson.build @@ -3668,6 +3668,7 @@ if have_system trace_events_subdirs += [ 'accel/hvf', 'accel/kvm', + 'accel/mshv', 'audio', 'backends', 'backends/tpm', diff --git a/target/i386/mshv/meson.build b/target/i386/mshv/meson.build index 8ddaa7c11dc7f..647e5dafb77f7 100644 --- a/target/i386/mshv/meson.build +++ b/target/i386/mshv/meson.build @@ -1,6 +1,7 @@ i386_mshv_ss = ss.source_set() i386_mshv_ss.add(files( + 'mshv-cpu.c', 'x86.c', )) diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c new file mode 100644 index 0000000000000..de0c26bc6c66c --- /dev/null +++ b/target/i386/mshv/mshv-cpu.c @@ -0,0 +1,72 @@ +/* + * QEMU MSHV support + * + * Copyright Microsoft, Corp. 2025 + * + * Authors: Ziqiao Zhou + * Magnus Kulke + * Jinank Jain + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/typedefs.h" + +#include "system/mshv.h" +#include "system/mshv_int.h" +#include "system/address-spaces.h" +#include "linux/mshv.h" +#include "hw/hyperv/hvgdk.h" +#include "hw/hyperv/hvgdk_mini.h" +#include "hw/hyperv/hvhdk_mini.h" + +#include "trace-accel_mshv.h" +#include "trace.h" + +void mshv_arch_amend_proc_features( + union hv_partition_synthetic_processor_features *features) +{ + features->access_guest_idle_reg = 1; +} + +/* + * Default Microsoft Hypervisor behavior for unimplemented MSR is to send a + * fault to the guest if it tries to access it. It is possible to override + * this behavior with a more suitable option i.e., ignore writes from the guest + * and return zero in attempt to read unimplemented. + */ +static int set_unimplemented_msr_action(int vm_fd) +{ + struct hv_input_set_partition_property in = {0}; + struct mshv_root_hvcall args = {0}; + + in.property_code = HV_PARTITION_PROPERTY_UNIMPLEMENTED_MSR_ACTION; + in.property_value = HV_UNIMPLEMENTED_MSR_ACTION_IGNORE_WRITE_READ_ZERO; + + args.code = HVCALL_SET_PARTITION_PROPERTY; + args.in_sz = sizeof(in); + args.in_ptr = (uint64_t)∈ + + trace_mshv_hvcall_args("unimplemented_msr_action", args.code, args.in_sz); + + int ret = mshv_hvcall(vm_fd, &args); + if (ret < 0) { + error_report("Failed to set unimplemented MSR action"); + return -1; + } + return 0; +} + +int mshv_arch_post_init_vm(int vm_fd) +{ + int ret; + + ret = set_unimplemented_msr_action(vm_fd); + if (ret < 0) { + error_report("Failed to set unimplemented MSR action"); + } + + return ret; +} From 4dc5d4257259764b6fcd035870517fc4140d8962 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:30 +0200 Subject: [PATCH 1407/1794] accel/mshv: Add vCPU creation and execution loop Create MSHV vCPUs using MSHV_CREATE_VP and initialize their state. Register the MSHV CPU execution loop loop with the QEMU accelerator framework to enable guest code execution. The target/i386 functionality is still mostly stubbed out and will be populated in a later commit in this series. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-11-magnuskulke@linux.microsoft.com [Fix g_free/g_clear_pointer confusion; rename qemu_wait_io_event; mshv.h/mshv_int.h split. - Paolo] Signed-off-by: Paolo Bonzini --- accel/mshv/mshv-all.c | 186 +++++++++++++++++++++++++++++++++--- accel/mshv/trace-events | 2 + include/system/mshv.h | 2 +- include/system/mshv_int.h | 20 ++++ target/i386/mshv/mshv-cpu.c | 63 ++++++++++++ 5 files changed, 260 insertions(+), 13 deletions(-) diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c index 653195c57c996..e02421d79d410 100644 --- a/accel/mshv/mshv-all.c +++ b/accel/mshv/mshv-all.c @@ -393,6 +393,24 @@ int mshv_hvcall(int fd, const struct mshv_root_hvcall *args) return ret; } +static int mshv_init_vcpu(CPUState *cpu) +{ + int vm_fd = mshv_state->vm; + uint8_t vp_index = cpu->cpu_index; + int ret; + + mshv_arch_init_vcpu(cpu); + cpu->accel = g_new0(AccelCPUState, 1); + + ret = mshv_create_vcpu(vm_fd, vp_index, &cpu->accel->cpufd); + if (ret < 0) { + return -1; + } + + cpu->accel->dirty = true; + + return 0; +} static int mshv_init(AccelState *as, MachineState *ms) { @@ -415,6 +433,8 @@ static int mshv_init(AccelState *as, MachineState *ms) return -1; } + mshv_init_mmio_emu(); + mshv_init_msicontrol(); ret = create_vm(mshv_fd, &vm_fd); @@ -444,40 +464,182 @@ static int mshv_init(AccelState *as, MachineState *ms) return 0; } +static int mshv_destroy_vcpu(CPUState *cpu) +{ + int cpu_fd = mshv_vcpufd(cpu); + int vm_fd = mshv_state->vm; + + mshv_remove_vcpu(vm_fd, cpu_fd); + mshv_vcpufd(cpu) = 0; + + mshv_arch_destroy_vcpu(cpu); + g_clear_pointer(&cpu->accel, g_free); + return 0; +} + +static int mshv_cpu_exec(CPUState *cpu) +{ + hv_message mshv_msg; + enum MshvVmExit exit_reason; + int ret = 0; + + bql_unlock(); + cpu_exec_start(cpu); + + do { + if (cpu->accel->dirty) { + ret = mshv_arch_put_registers(cpu); + if (ret) { + error_report("Failed to put registers after init: %s", + strerror(-ret)); + ret = -1; + break; + } + cpu->accel->dirty = false; + } + + ret = mshv_run_vcpu(mshv_state->vm, cpu, &mshv_msg, &exit_reason); + if (ret < 0) { + error_report("Failed to run on vcpu %d", cpu->cpu_index); + abort(); + } + + switch (exit_reason) { + case MshvVmExitIgnore: + break; + default: + ret = EXCP_INTERRUPT; + break; + } + } while (ret == 0); + + cpu_exec_end(cpu); + bql_lock(); + + if (ret < 0) { + cpu_dump_state(cpu, stderr, CPU_DUMP_CODE); + vm_stop(RUN_STATE_INTERNAL_ERROR); + } + + return ret; +} + +static void *mshv_vcpu_thread(void *arg) +{ + CPUState *cpu = arg; + int ret; + + rcu_register_thread(); + + bql_lock(); + qemu_thread_get_self(cpu->thread); + cpu->thread_id = qemu_get_thread_id(); + current_cpu = cpu; + ret = mshv_init_vcpu(cpu); + if (ret < 0) { + error_report("Failed to init vcpu %d", cpu->cpu_index); + goto cleanup; + } + + /* signal CPU creation */ + cpu_thread_signal_created(cpu); + qemu_guest_random_seed_thread_part2(cpu->random_seed); + + do { + qemu_process_cpu_events(cpu); + if (cpu_can_run(cpu)) { + mshv_cpu_exec(cpu); + } + } while (!cpu->unplug || cpu_can_run(cpu)); + + mshv_destroy_vcpu(cpu); +cleanup: + cpu_thread_signal_destroyed(cpu); + bql_unlock(); + rcu_unregister_thread(); + return NULL; +} + static void mshv_start_vcpu_thread(CPUState *cpu) { - error_report("unimplemented"); - abort(); + char thread_name[VCPU_THREAD_NAME_SIZE]; + + cpu->thread = g_malloc0(sizeof(QemuThread)); + cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + + qemu_cond_init(cpu->halt_cond); + + trace_mshv_start_vcpu_thread(thread_name, cpu->cpu_index); + qemu_thread_create(cpu->thread, thread_name, mshv_vcpu_thread, cpu, + QEMU_THREAD_JOINABLE); +} + +static void do_mshv_cpu_synchronize_post_init(CPUState *cpu, + run_on_cpu_data arg) +{ + int ret = mshv_arch_put_registers(cpu); + if (ret < 0) { + error_report("Failed to put registers after init: %s", strerror(-ret)); + abort(); + } + + cpu->accel->dirty = false; } static void mshv_cpu_synchronize_post_init(CPUState *cpu) { - error_report("unimplemented"); - abort(); + run_on_cpu(cpu, do_mshv_cpu_synchronize_post_init, RUN_ON_CPU_NULL); } static void mshv_cpu_synchronize_post_reset(CPUState *cpu) { - error_report("unimplemented"); - abort(); + int ret = mshv_arch_put_registers(cpu); + if (ret) { + error_report("Failed to put registers after reset: %s", + strerror(-ret)); + cpu_dump_state(cpu, stderr, CPU_DUMP_CODE); + vm_stop(RUN_STATE_INTERNAL_ERROR); + } + cpu->accel->dirty = false; +} + +static void do_mshv_cpu_synchronize_pre_loadvm(CPUState *cpu, + run_on_cpu_data arg) +{ + cpu->accel->dirty = true; } static void mshv_cpu_synchronize_pre_loadvm(CPUState *cpu) { - error_report("unimplemented"); - abort(); + run_on_cpu(cpu, do_mshv_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL); +} + +static void do_mshv_cpu_synchronize(CPUState *cpu, run_on_cpu_data arg) +{ + if (!cpu->accel->dirty) { + int ret = mshv_load_regs(cpu); + if (ret < 0) { + error_report("Failed to load registers for vcpu %d", + cpu->cpu_index); + + cpu_dump_state(cpu, stderr, CPU_DUMP_CODE); + vm_stop(RUN_STATE_INTERNAL_ERROR); + } + + cpu->accel->dirty = true; + } } static void mshv_cpu_synchronize(CPUState *cpu) { - error_report("unimplemented"); - abort(); + if (!cpu->accel->dirty) { + run_on_cpu(cpu, do_mshv_cpu_synchronize, RUN_ON_CPU_NULL); + } } static bool mshv_cpus_are_resettable(void) { - error_report("unimplemented"); - abort(); + return false; } static void mshv_accel_class_init(ObjectClass *oc, const void *data) diff --git a/accel/mshv/trace-events b/accel/mshv/trace-events index 6130c4abf8af8..a4dffeb24a327 100644 --- a/accel/mshv/trace-events +++ b/accel/mshv/trace-events @@ -3,6 +3,8 @@ # # SPDX-License-Identifier: GPL-2.0-or-later +mshv_start_vcpu_thread(const char* thread, uint32_t cpu) "thread=%s cpu_index=%d" + mshv_set_memory(bool add, uint64_t gpa, uint64_t size, uint64_t user_addr, bool readonly, int ret) "add=%d gpa=0x%" PRIx64 " size=0x%" PRIx64 " user=0x%" PRIx64 " readonly=%d result=%d" mshv_mem_ioeventfd_add(uint64_t addr, uint32_t size, uint32_t data) "addr=0x%" PRIx64 " size=%d data=0x%x" mshv_mem_ioeventfd_del(uint64_t addr, uint32_t size, uint32_t data) "addr=0x%" PRIx64 " size=%d data=0x%x" diff --git a/include/system/mshv.h b/include/system/mshv.h index 1011e81df478a..bbc42f4dc3aec 100644 --- a/include/system/mshv.h +++ b/include/system/mshv.h @@ -41,7 +41,7 @@ extern bool mshv_allowed; #define mshv_msi_via_irqfd_enabled() mshv_enabled() #else /* CONFIG_MSHV_IS_POSSIBLE */ #define mshv_enabled() false -#define mshv_msi_via_irqfd_enabled() false +#define mshv_msi_via_irqfd_enabled() mshv_enabled() #endif typedef struct MshvState MshvState; diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index b36124a0ea703..fb80f69772ba0 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -14,6 +14,8 @@ #ifndef QEMU_MSHV_INT_H #define QEMU_MSHV_INT_H +typedef struct hyperv_message hv_message; + struct AccelCPUState { int cpufd; bool dirty; @@ -44,6 +46,24 @@ typedef struct MshvMsiControl { GHashTable *gsi_routes; } MshvMsiControl; +#define mshv_vcpufd(cpu) (cpu->accel->cpufd) + +/* cpu */ +typedef enum MshvVmExit { + MshvVmExitIgnore = 0, + MshvVmExitShutdown = 1, + MshvVmExitSpecial = 2, +} MshvVmExit; + +void mshv_init_mmio_emu(void); +int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd); +void mshv_remove_vcpu(int vm_fd, int cpu_fd); +int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit); +int mshv_load_regs(CPUState *cpu); +int mshv_store_regs(CPUState *cpu); +int mshv_arch_put_registers(const CPUState *cpu); +void mshv_arch_init_vcpu(CPUState *cpu); +void mshv_arch_destroy_vcpu(CPUState *cpu); void mshv_arch_amend_proc_features( union hv_partition_synthetic_processor_features *features); int mshv_arch_post_init_vm(int vm_fd); diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index de0c26bc6c66c..02d71ebc14ab6 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -22,15 +22,78 @@ #include "hw/hyperv/hvgdk_mini.h" #include "hw/hyperv/hvhdk_mini.h" +#include "cpu.h" +#include "emulate/x86_decode.h" +#include "emulate/x86_emu.h" +#include "emulate/x86_flags.h" + #include "trace-accel_mshv.h" #include "trace.h" +int mshv_store_regs(CPUState *cpu) +{ + error_report("unimplemented"); + abort(); +} + +int mshv_load_regs(CPUState *cpu) +{ + error_report("unimplemented"); + abort(); +} + +int mshv_arch_put_registers(const CPUState *cpu) +{ + error_report("unimplemented"); + abort(); +} + void mshv_arch_amend_proc_features( union hv_partition_synthetic_processor_features *features) { features->access_guest_idle_reg = 1; } +int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit) +{ + error_report("unimplemented"); + abort(); +} + +void mshv_remove_vcpu(int vm_fd, int cpu_fd) +{ + error_report("unimplemented"); + abort(); +} + +int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd) +{ + error_report("unimplemented"); + abort(); +} + +void mshv_init_mmio_emu(void) +{ + error_report("unimplemented"); + abort(); +} + +void mshv_arch_init_vcpu(CPUState *cpu) +{ + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + + env->emu_mmio_buf = g_new(char, 4096); +} + +void mshv_arch_destroy_vcpu(CPUState *cpu) +{ + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + + g_clear_pointer(&env->emu_mmio_buf, g_free); +} + /* * Default Microsoft Hypervisor behavior for unimplemented MSR is to send a * fault to the guest if it tries to access it. It is possible to override From 575df4df54e060661db45e6f80293b29ea8901c1 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:31 +0200 Subject: [PATCH 1408/1794] accel/mshv: Add vCPU signal handling Implement signal handling for MSHV vCPUs to support asynchronous interrupts from the main thread. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-12-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- accel/mshv/mshv-all.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c index e02421d79d410..fa1f8f35bdb43 100644 --- a/accel/mshv/mshv-all.c +++ b/accel/mshv/mshv-all.c @@ -524,6 +524,35 @@ static int mshv_cpu_exec(CPUState *cpu) return ret; } +/* + * The signal handler is triggered when QEMU's main thread receives a SIG_IPI + * (SIGUSR1). This signal causes the current CPU thread to be kicked, forcing a + * VM exit on the CPU. The VM exit generates an exit reason that breaks the loop + * (see mshv_cpu_exec). If the exit is due to a Ctrl+A+x command, the system + * will shut down. For other cases, the system will continue running. + */ +static void sa_ipi_handler(int sig) +{ + /* TODO: call IOCTL to set_immediate_exit, once implemented. */ + + qemu_cpu_kick_self(); +} + +static void init_signal(CPUState *cpu) +{ + /* init cpu signals */ + struct sigaction sigact; + sigset_t set; + + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_handler = sa_ipi_handler; + sigaction(SIG_IPI, &sigact, NULL); + + pthread_sigmask(SIG_BLOCK, NULL, &set); + sigdelset(&set, SIG_IPI); + pthread_sigmask(SIG_SETMASK, &set, NULL); +} + static void *mshv_vcpu_thread(void *arg) { CPUState *cpu = arg; @@ -540,6 +569,7 @@ static void *mshv_vcpu_thread(void *arg) error_report("Failed to init vcpu %d", cpu->cpu_index); goto cleanup; } + init_signal(cpu); /* signal CPU creation */ cpu_thread_signal_created(cpu); From 4ed605c06e743b2ec0bfe35954d88b8c64dd3767 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:32 +0200 Subject: [PATCH 1409/1794] target/i386/mshv: Add CPU create and remove logic Implement MSHV-specific hooks for vCPU creation and teardown in the i386 target. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-13-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/mshv/mshv-cpu.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 02d71ebc14ab6..5069ab7a22f30 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -30,6 +30,8 @@ #include "trace-accel_mshv.h" #include "trace.h" +#include + int mshv_store_regs(CPUState *cpu) { error_report("unimplemented"); @@ -62,20 +64,29 @@ int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit) void mshv_remove_vcpu(int vm_fd, int cpu_fd) { - error_report("unimplemented"); - abort(); + close(cpu_fd); } + int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd) { - error_report("unimplemented"); - abort(); + int ret; + struct mshv_create_vp vp_arg = { + .vp_index = vp_index, + }; + ret = ioctl(vm_fd, MSHV_CREATE_VP, &vp_arg); + if (ret < 0) { + error_report("failed to create mshv vcpu: %s", strerror(errno)); + return -1; + } + + *cpu_fd = ret; + + return 0; } void mshv_init_mmio_emu(void) { - error_report("unimplemented"); - abort(); } void mshv_arch_init_vcpu(CPUState *cpu) From 2bd7a6aa4743ee85c77c0520e8b422b3bfeab386 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Thu, 2 Oct 2025 18:13:31 +0200 Subject: [PATCH 1410/1794] target/i386/mshv: Implement mshv_store_regs() Add support for writing general-purpose registers to MSHV vCPUs during initialization or migration using the MSHV register interface. A generic set_register call is introduced to abstract the HV call over the various register types. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-14-magnuskulke@linux.microsoft.com [mshv.h/mshv_int.h split. - Paolo] Signed-off-by: Paolo Bonzini --- include/system/mshv.h | 1 + include/system/mshv_int.h | 2 + target/i386/mshv/mshv-cpu.c | 116 +++++++++++++++++++++++++++++++++++- 3 files changed, 117 insertions(+), 2 deletions(-) diff --git a/include/system/mshv.h b/include/system/mshv.h index bbc42f4dc3aec..8b1fc20c80ddf 100644 --- a/include/system/mshv.h +++ b/include/system/mshv.h @@ -18,6 +18,7 @@ #include "qemu/accel.h" #include "hw/hyperv/hyperv-proto.h" #include "hw/hyperv/hvhdk.h" +#include "hw/hyperv/hvgdk_mini.h" #include "qapi/qapi-types-common.h" #include "system/memory.h" #include "accel/accel-ops.h" diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index fb80f69772ba0..731841af92935 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -61,6 +61,8 @@ void mshv_remove_vcpu(int vm_fd, int cpu_fd); int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit); int mshv_load_regs(CPUState *cpu); int mshv_store_regs(CPUState *cpu); +int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs, + size_t n_regs); int mshv_arch_put_registers(const CPUState *cpu); void mshv_arch_init_vcpu(CPUState *cpu); void mshv_arch_destroy_vcpu(CPUState *cpu); diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 5069ab7a22f30..9ead03ca2d80c 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -32,12 +32,124 @@ #include +static enum hv_register_name STANDARD_REGISTER_NAMES[18] = { + HV_X64_REGISTER_RAX, + HV_X64_REGISTER_RBX, + HV_X64_REGISTER_RCX, + HV_X64_REGISTER_RDX, + HV_X64_REGISTER_RSI, + HV_X64_REGISTER_RDI, + HV_X64_REGISTER_RSP, + HV_X64_REGISTER_RBP, + HV_X64_REGISTER_R8, + HV_X64_REGISTER_R9, + HV_X64_REGISTER_R10, + HV_X64_REGISTER_R11, + HV_X64_REGISTER_R12, + HV_X64_REGISTER_R13, + HV_X64_REGISTER_R14, + HV_X64_REGISTER_R15, + HV_X64_REGISTER_RIP, + HV_X64_REGISTER_RFLAGS, +}; + +int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs, + size_t n_regs) +{ + int cpu_fd = mshv_vcpufd(cpu); + int vp_index = cpu->cpu_index; + size_t in_sz, assocs_sz; + hv_input_set_vp_registers *in; + struct mshv_root_hvcall args = {0}; + int ret; + + /* find out the size of the struct w/ a flexible array at the tail */ + assocs_sz = n_regs * sizeof(hv_register_assoc); + in_sz = sizeof(hv_input_set_vp_registers) + assocs_sz; + + /* fill the input struct */ + in = g_malloc0(in_sz); + in->vp_index = vp_index; + memcpy(in->elements, assocs, assocs_sz); + + /* create the hvcall envelope */ + args.code = HVCALL_SET_VP_REGISTERS; + args.in_sz = in_sz; + args.in_ptr = (uint64_t) in; + args.reps = (uint16_t) n_regs; + + /* perform the call */ + ret = mshv_hvcall(cpu_fd, &args); + g_free(in); + if (ret < 0) { + error_report("Failed to set registers"); + return -1; + } + + /* assert we set all registers */ + if (args.reps != n_regs) { + error_report("Failed to set registers: expected %zu elements" + ", got %u", n_regs, args.reps); + return -1; + } + + return 0; +} + +static int set_standard_regs(const CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + hv_register_assoc assocs[ARRAY_SIZE(STANDARD_REGISTER_NAMES)]; + int ret; + size_t n_regs = ARRAY_SIZE(STANDARD_REGISTER_NAMES); + + /* set names */ + for (size_t i = 0; i < ARRAY_SIZE(STANDARD_REGISTER_NAMES); i++) { + assocs[i].name = STANDARD_REGISTER_NAMES[i]; + } + assocs[0].value.reg64 = env->regs[R_EAX]; + assocs[1].value.reg64 = env->regs[R_EBX]; + assocs[2].value.reg64 = env->regs[R_ECX]; + assocs[3].value.reg64 = env->regs[R_EDX]; + assocs[4].value.reg64 = env->regs[R_ESI]; + assocs[5].value.reg64 = env->regs[R_EDI]; + assocs[6].value.reg64 = env->regs[R_ESP]; + assocs[7].value.reg64 = env->regs[R_EBP]; + assocs[8].value.reg64 = env->regs[R_R8]; + assocs[9].value.reg64 = env->regs[R_R9]; + assocs[10].value.reg64 = env->regs[R_R10]; + assocs[11].value.reg64 = env->regs[R_R11]; + assocs[12].value.reg64 = env->regs[R_R12]; + assocs[13].value.reg64 = env->regs[R_R13]; + assocs[14].value.reg64 = env->regs[R_R14]; + assocs[15].value.reg64 = env->regs[R_R15]; + assocs[16].value.reg64 = env->eip; + lflags_to_rflags(env); + assocs[17].value.reg64 = env->eflags; + + ret = mshv_set_generic_regs(cpu, assocs, n_regs); + if (ret < 0) { + error_report("failed to set standard registers"); + return -errno; + } + return 0; +} + int mshv_store_regs(CPUState *cpu) { - error_report("unimplemented"); - abort(); + int ret; + + ret = set_standard_regs(cpu); + if (ret < 0) { + error_report("Failed to store standard registers"); + return -1; + } + + return 0; } + int mshv_load_regs(CPUState *cpu) { error_report("unimplemented"); From 66480a048a01fc833ba153410282609f26166141 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:34 +0200 Subject: [PATCH 1411/1794] target/i386/mshv: Implement mshv_get_standard_regs() Fetch standard register state from MSHV vCPUs to support debugging, migration, and other introspection features in QEMU. Fetch standard register state from a MHSV vCPU's. A generic get_regs() function and a mapper to map the different register representations are introduced. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-15-magnuskulke@linux.microsoft.com [mshv.h/mshv_int.h split. - Paolo] Signed-off-by: Paolo Bonzini --- include/system/mshv_int.h | 1 + target/i386/mshv/mshv-cpu.c | 116 +++++++++++++++++++++++++++++++++++- 2 files changed, 115 insertions(+), 2 deletions(-) diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index 731841af92935..b0a79296ad9b4 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -58,6 +58,7 @@ typedef enum MshvVmExit { void mshv_init_mmio_emu(void); int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd); void mshv_remove_vcpu(int vm_fd, int cpu_fd); +int mshv_get_standard_regs(CPUState *cpu); int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit); int mshv_load_regs(CPUState *cpu); int mshv_store_regs(CPUState *cpu); diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 9ead03ca2d80c..4e3eee113bbb7 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -96,6 +96,66 @@ int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs, return 0; } +static int get_generic_regs(CPUState *cpu, hv_register_assoc *assocs, + size_t n_regs) +{ + int cpu_fd = mshv_vcpufd(cpu); + int vp_index = cpu->cpu_index; + hv_input_get_vp_registers *in; + hv_register_value *values; + size_t in_sz, names_sz, values_sz; + int i, ret; + struct mshv_root_hvcall args = {0}; + + /* find out the size of the struct w/ a flexible array at the tail */ + names_sz = n_regs * sizeof(hv_register_name); + in_sz = sizeof(hv_input_get_vp_registers) + names_sz; + + /* fill the input struct */ + in = g_malloc0(in_sz); + in->vp_index = vp_index; + for (i = 0; i < n_regs; i++) { + in->names[i] = assocs[i].name; + } + + /* allocate value output buffer */ + values_sz = n_regs * sizeof(union hv_register_value); + values = g_malloc0(values_sz); + + /* create the hvcall envelope */ + args.code = HVCALL_GET_VP_REGISTERS; + args.in_sz = in_sz; + args.in_ptr = (uint64_t) in; + args.out_sz = values_sz; + args.out_ptr = (uint64_t) values; + args.reps = (uint16_t) n_regs; + + /* perform the call */ + ret = mshv_hvcall(cpu_fd, &args); + g_free(in); + if (ret < 0) { + g_free(values); + error_report("Failed to retrieve registers"); + return -1; + } + + /* assert we got all registers */ + if (args.reps != n_regs) { + g_free(values); + error_report("Failed to retrieve registers: expected %zu elements" + ", got %u", n_regs, args.reps); + return -1; + } + + /* copy values into assoc */ + for (i = 0; i < n_regs; i++) { + assocs[i].value = values[i]; + } + g_free(values); + + return 0; +} + static int set_standard_regs(const CPUState *cpu) { X86CPU *x86cpu = X86_CPU(cpu); @@ -149,11 +209,63 @@ int mshv_store_regs(CPUState *cpu) return 0; } +static void populate_standard_regs(const hv_register_assoc *assocs, + CPUX86State *env) +{ + env->regs[R_EAX] = assocs[0].value.reg64; + env->regs[R_EBX] = assocs[1].value.reg64; + env->regs[R_ECX] = assocs[2].value.reg64; + env->regs[R_EDX] = assocs[3].value.reg64; + env->regs[R_ESI] = assocs[4].value.reg64; + env->regs[R_EDI] = assocs[5].value.reg64; + env->regs[R_ESP] = assocs[6].value.reg64; + env->regs[R_EBP] = assocs[7].value.reg64; + env->regs[R_R8] = assocs[8].value.reg64; + env->regs[R_R9] = assocs[9].value.reg64; + env->regs[R_R10] = assocs[10].value.reg64; + env->regs[R_R11] = assocs[11].value.reg64; + env->regs[R_R12] = assocs[12].value.reg64; + env->regs[R_R13] = assocs[13].value.reg64; + env->regs[R_R14] = assocs[14].value.reg64; + env->regs[R_R15] = assocs[15].value.reg64; + + env->eip = assocs[16].value.reg64; + env->eflags = assocs[17].value.reg64; + rflags_to_lflags(env); +} + +int mshv_get_standard_regs(CPUState *cpu) +{ + struct hv_register_assoc assocs[ARRAY_SIZE(STANDARD_REGISTER_NAMES)]; + int ret; + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + size_t n_regs = ARRAY_SIZE(STANDARD_REGISTER_NAMES); + + for (size_t i = 0; i < n_regs; i++) { + assocs[i].name = STANDARD_REGISTER_NAMES[i]; + } + ret = get_generic_regs(cpu, assocs, n_regs); + if (ret < 0) { + error_report("failed to get standard registers"); + return -1; + } + + populate_standard_regs(assocs, env); + return 0; +} int mshv_load_regs(CPUState *cpu) { - error_report("unimplemented"); - abort(); + int ret; + + ret = mshv_get_standard_regs(cpu); + if (ret < 0) { + error_report("Failed to load standard registers"); + return -1; + } + + return 0; } int mshv_arch_put_registers(const CPUState *cpu) From 0382c2c8544da66619d16988caaab8e3d9e881d4 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:35 +0200 Subject: [PATCH 1412/1794] target/i386/mshv: Implement mshv_get_special_regs() Retrieve special registers (e.g. segment, control, and descriptor table registers) from MSHV vCPUs. Various helper functions to map register state representations between Qemu and MSHV are introduced. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-16-magnuskulke@linux.microsoft.com [mshv.h/mshv_int.h split. - Paolo] Signed-off-by: Paolo Bonzini --- include/system/mshv_int.h | 1 + target/i386/mshv/mshv-cpu.c | 104 ++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index b0a79296ad9b4..c6e6e8af30787 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -59,6 +59,7 @@ void mshv_init_mmio_emu(void); int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd); void mshv_remove_vcpu(int vm_fd, int cpu_fd); int mshv_get_standard_regs(CPUState *cpu); +int mshv_get_special_regs(CPUState *cpu); int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit); int mshv_load_regs(CPUState *cpu); int mshv_store_regs(CPUState *cpu); diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 4e3eee113bbb7..bc75686f8282b 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -53,6 +53,26 @@ static enum hv_register_name STANDARD_REGISTER_NAMES[18] = { HV_X64_REGISTER_RFLAGS, }; +static enum hv_register_name SPECIAL_REGISTER_NAMES[17] = { + HV_X64_REGISTER_CS, + HV_X64_REGISTER_DS, + HV_X64_REGISTER_ES, + HV_X64_REGISTER_FS, + HV_X64_REGISTER_GS, + HV_X64_REGISTER_SS, + HV_X64_REGISTER_TR, + HV_X64_REGISTER_LDTR, + HV_X64_REGISTER_GDTR, + HV_X64_REGISTER_IDTR, + HV_X64_REGISTER_CR0, + HV_X64_REGISTER_CR2, + HV_X64_REGISTER_CR3, + HV_X64_REGISTER_CR4, + HV_X64_REGISTER_CR8, + HV_X64_REGISTER_EFER, + HV_X64_REGISTER_APIC_BASE, +}; + int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs, size_t n_regs) { @@ -255,6 +275,84 @@ int mshv_get_standard_regs(CPUState *cpu) return 0; } +static inline void populate_segment_reg(const hv_x64_segment_register *hv_seg, + SegmentCache *seg) +{ + memset(seg, 0, sizeof(SegmentCache)); + + seg->base = hv_seg->base; + seg->limit = hv_seg->limit; + seg->selector = hv_seg->selector; + + seg->flags = (hv_seg->segment_type << DESC_TYPE_SHIFT) + | (hv_seg->present * DESC_P_MASK) + | (hv_seg->descriptor_privilege_level << DESC_DPL_SHIFT) + | (hv_seg->_default << DESC_B_SHIFT) + | (hv_seg->non_system_segment * DESC_S_MASK) + | (hv_seg->_long << DESC_L_SHIFT) + | (hv_seg->granularity * DESC_G_MASK) + | (hv_seg->available * DESC_AVL_MASK); + +} + +static inline void populate_table_reg(const hv_x64_table_register *hv_seg, + SegmentCache *tbl) +{ + memset(tbl, 0, sizeof(SegmentCache)); + + tbl->base = hv_seg->base; + tbl->limit = hv_seg->limit; +} + +static void populate_special_regs(const hv_register_assoc *assocs, + X86CPU *x86cpu) +{ + CPUX86State *env = &x86cpu->env; + + populate_segment_reg(&assocs[0].value.segment, &env->segs[R_CS]); + populate_segment_reg(&assocs[1].value.segment, &env->segs[R_DS]); + populate_segment_reg(&assocs[2].value.segment, &env->segs[R_ES]); + populate_segment_reg(&assocs[3].value.segment, &env->segs[R_FS]); + populate_segment_reg(&assocs[4].value.segment, &env->segs[R_GS]); + populate_segment_reg(&assocs[5].value.segment, &env->segs[R_SS]); + + populate_segment_reg(&assocs[6].value.segment, &env->tr); + populate_segment_reg(&assocs[7].value.segment, &env->ldt); + + populate_table_reg(&assocs[8].value.table, &env->gdt); + populate_table_reg(&assocs[9].value.table, &env->idt); + + env->cr[0] = assocs[10].value.reg64; + env->cr[2] = assocs[11].value.reg64; + env->cr[3] = assocs[12].value.reg64; + env->cr[4] = assocs[13].value.reg64; + + cpu_set_apic_tpr(x86cpu->apic_state, assocs[14].value.reg64); + env->efer = assocs[15].value.reg64; + cpu_set_apic_base(x86cpu->apic_state, assocs[16].value.reg64); +} + + +int mshv_get_special_regs(CPUState *cpu) +{ + struct hv_register_assoc assocs[ARRAY_SIZE(SPECIAL_REGISTER_NAMES)]; + int ret; + X86CPU *x86cpu = X86_CPU(cpu); + size_t n_regs = ARRAY_SIZE(SPECIAL_REGISTER_NAMES); + + for (size_t i = 0; i < n_regs; i++) { + assocs[i].name = SPECIAL_REGISTER_NAMES[i]; + } + ret = get_generic_regs(cpu, assocs, n_regs); + if (ret < 0) { + error_report("failed to get special registers"); + return -errno; + } + + populate_special_regs(assocs, x86cpu); + return 0; +} + int mshv_load_regs(CPUState *cpu) { int ret; @@ -265,6 +363,12 @@ int mshv_load_regs(CPUState *cpu) return -1; } + ret = mshv_get_special_regs(cpu); + if (ret < 0) { + error_report("Failed to load special registers"); + return -1; + } + return 0; } From 25a1d871e0f19bbf8fee471af5fefec52465bb09 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:36 +0200 Subject: [PATCH 1413/1794] target/i386/mshv: Implement mshv_arch_put_registers() Write CPU register state to MSHV vCPUs. Various mapping functions to prepare the payload for the HV call have been implemented. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-17-magnuskulke@linux.microsoft.com [mshv.h/mshv_int.h split. - Paolo] Signed-off-by: Paolo Bonzini --- include/system/mshv_int.h | 15 +++ target/i386/mshv/mshv-cpu.c | 237 ++++++++++++++++++++++++++++++++++++ 2 files changed, 252 insertions(+) diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index c6e6e8af30787..0ea8d504fa519 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -49,6 +49,20 @@ typedef struct MshvMsiControl { #define mshv_vcpufd(cpu) (cpu->accel->cpufd) /* cpu */ +typedef struct MshvFPU { + uint8_t fpr[8][16]; + uint16_t fcw; + uint16_t fsw; + uint8_t ftwx; + uint8_t pad1; + uint16_t last_opcode; + uint64_t last_ip; + uint64_t last_dp; + uint8_t xmm[16][16]; + uint32_t mxcsr; + uint32_t pad2; +} MshvFPU; + typedef enum MshvVmExit { MshvVmExitIgnore = 0, MshvVmExitShutdown = 1, @@ -58,6 +72,7 @@ typedef enum MshvVmExit { void mshv_init_mmio_emu(void); int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd); void mshv_remove_vcpu(int vm_fd, int cpu_fd); +int mshv_configure_vcpu(const CPUState *cpu, const MshvFPU *fpu, uint64_t xcr0); int mshv_get_standard_regs(CPUState *cpu); int mshv_get_special_regs(CPUState *cpu); int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit); diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index bc75686f8282b..8b10c79e54783 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -73,6 +73,35 @@ static enum hv_register_name SPECIAL_REGISTER_NAMES[17] = { HV_X64_REGISTER_APIC_BASE, }; +static enum hv_register_name FPU_REGISTER_NAMES[26] = { + HV_X64_REGISTER_XMM0, + HV_X64_REGISTER_XMM1, + HV_X64_REGISTER_XMM2, + HV_X64_REGISTER_XMM3, + HV_X64_REGISTER_XMM4, + HV_X64_REGISTER_XMM5, + HV_X64_REGISTER_XMM6, + HV_X64_REGISTER_XMM7, + HV_X64_REGISTER_XMM8, + HV_X64_REGISTER_XMM9, + HV_X64_REGISTER_XMM10, + HV_X64_REGISTER_XMM11, + HV_X64_REGISTER_XMM12, + HV_X64_REGISTER_XMM13, + HV_X64_REGISTER_XMM14, + HV_X64_REGISTER_XMM15, + HV_X64_REGISTER_FP_MMX0, + HV_X64_REGISTER_FP_MMX1, + HV_X64_REGISTER_FP_MMX2, + HV_X64_REGISTER_FP_MMX3, + HV_X64_REGISTER_FP_MMX4, + HV_X64_REGISTER_FP_MMX5, + HV_X64_REGISTER_FP_MMX6, + HV_X64_REGISTER_FP_MMX7, + HV_X64_REGISTER_FP_CONTROL_STATUS, + HV_X64_REGISTER_XMM_CONTROL_STATUS, +}; + int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs, size_t n_regs) { @@ -372,8 +401,216 @@ int mshv_load_regs(CPUState *cpu) return 0; } +static inline void populate_hv_segment_reg(SegmentCache *seg, + hv_x64_segment_register *hv_reg) +{ + uint32_t flags = seg->flags; + + hv_reg->base = seg->base; + hv_reg->limit = seg->limit; + hv_reg->selector = seg->selector; + hv_reg->segment_type = (flags >> DESC_TYPE_SHIFT) & 0xF; + hv_reg->non_system_segment = (flags & DESC_S_MASK) != 0; + hv_reg->descriptor_privilege_level = (flags >> DESC_DPL_SHIFT) & 0x3; + hv_reg->present = (flags & DESC_P_MASK) != 0; + hv_reg->reserved = 0; + hv_reg->available = (flags & DESC_AVL_MASK) != 0; + hv_reg->_long = (flags >> DESC_L_SHIFT) & 0x1; + hv_reg->_default = (flags >> DESC_B_SHIFT) & 0x1; + hv_reg->granularity = (flags & DESC_G_MASK) != 0; +} + +static inline void populate_hv_table_reg(const struct SegmentCache *seg, + hv_x64_table_register *hv_reg) +{ + memset(hv_reg, 0, sizeof(*hv_reg)); + + hv_reg->base = seg->base; + hv_reg->limit = seg->limit; +} + +static int set_special_regs(const CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + struct hv_register_assoc assocs[ARRAY_SIZE(SPECIAL_REGISTER_NAMES)]; + size_t n_regs = ARRAY_SIZE(SPECIAL_REGISTER_NAMES); + int ret; + + /* set names */ + for (size_t i = 0; i < n_regs; i++) { + assocs[i].name = SPECIAL_REGISTER_NAMES[i]; + } + populate_hv_segment_reg(&env->segs[R_CS], &assocs[0].value.segment); + populate_hv_segment_reg(&env->segs[R_DS], &assocs[1].value.segment); + populate_hv_segment_reg(&env->segs[R_ES], &assocs[2].value.segment); + populate_hv_segment_reg(&env->segs[R_FS], &assocs[3].value.segment); + populate_hv_segment_reg(&env->segs[R_GS], &assocs[4].value.segment); + populate_hv_segment_reg(&env->segs[R_SS], &assocs[5].value.segment); + populate_hv_segment_reg(&env->tr, &assocs[6].value.segment); + populate_hv_segment_reg(&env->ldt, &assocs[7].value.segment); + + populate_hv_table_reg(&env->gdt, &assocs[8].value.table); + populate_hv_table_reg(&env->idt, &assocs[9].value.table); + + assocs[10].value.reg64 = env->cr[0]; + assocs[11].value.reg64 = env->cr[2]; + assocs[12].value.reg64 = env->cr[3]; + assocs[13].value.reg64 = env->cr[4]; + assocs[14].value.reg64 = cpu_get_apic_tpr(x86cpu->apic_state); + assocs[15].value.reg64 = env->efer; + assocs[16].value.reg64 = cpu_get_apic_base(x86cpu->apic_state); + + ret = mshv_set_generic_regs(cpu, assocs, n_regs); + if (ret < 0) { + error_report("failed to set special registers"); + return -1; + } + + return 0; +} + +static int set_fpu(const CPUState *cpu, const struct MshvFPU *regs) +{ + struct hv_register_assoc assocs[ARRAY_SIZE(FPU_REGISTER_NAMES)]; + union hv_register_value *value; + size_t fp_i; + union hv_x64_fp_control_status_register *ctrl_status; + union hv_x64_xmm_control_status_register *xmm_ctrl_status; + int ret; + size_t n_regs = ARRAY_SIZE(FPU_REGISTER_NAMES); + + /* first 16 registers are xmm0-xmm15 */ + for (size_t i = 0; i < 16; i++) { + assocs[i].name = FPU_REGISTER_NAMES[i]; + value = &assocs[i].value; + memcpy(&value->reg128, ®s->xmm[i], 16); + } + + /* next 8 registers are fp_mmx0-fp_mmx7 */ + for (size_t i = 16; i < 24; i++) { + assocs[i].name = FPU_REGISTER_NAMES[i]; + fp_i = (i - 16); + value = &assocs[i].value; + memcpy(&value->reg128, ®s->fpr[fp_i], 16); + } + + /* last two registers are fp_control_status and xmm_control_status */ + assocs[24].name = FPU_REGISTER_NAMES[24]; + value = &assocs[24].value; + ctrl_status = &value->fp_control_status; + ctrl_status->fp_control = regs->fcw; + ctrl_status->fp_status = regs->fsw; + ctrl_status->fp_tag = regs->ftwx; + ctrl_status->reserved = 0; + ctrl_status->last_fp_op = regs->last_opcode; + ctrl_status->last_fp_rip = regs->last_ip; + + assocs[25].name = FPU_REGISTER_NAMES[25]; + value = &assocs[25].value; + xmm_ctrl_status = &value->xmm_control_status; + xmm_ctrl_status->xmm_status_control = regs->mxcsr; + xmm_ctrl_status->xmm_status_control_mask = 0; + xmm_ctrl_status->last_fp_rdp = regs->last_dp; + + ret = mshv_set_generic_regs(cpu, assocs, n_regs); + if (ret < 0) { + error_report("failed to set fpu registers"); + return -1; + } + + return 0; +} + +static int set_xc_reg(const CPUState *cpu, uint64_t xcr0) +{ + int ret; + struct hv_register_assoc assoc = { + .name = HV_X64_REGISTER_XFEM, + .value.reg64 = xcr0, + }; + + ret = mshv_set_generic_regs(cpu, &assoc, 1); + if (ret < 0) { + error_report("failed to set xcr0"); + return -errno; + } + return 0; +} + +static int set_cpu_state(const CPUState *cpu, const MshvFPU *fpu_regs, + uint64_t xcr0) +{ + int ret; + + ret = set_standard_regs(cpu); + if (ret < 0) { + return ret; + } + ret = set_special_regs(cpu); + if (ret < 0) { + return ret; + } + ret = set_fpu(cpu, fpu_regs); + if (ret < 0) { + return ret; + } + ret = set_xc_reg(cpu, xcr0); + if (ret < 0) { + return ret; + } + return 0; +} + +/* + * TODO: populate topology info: + * + * X86CPU *x86cpu = X86_CPU(cpu); + * CPUX86State *env = &x86cpu->env; + * X86CPUTopoInfo *topo_info = &env->topo_info; + */ +int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu, + uint64_t xcr0) +{ + int ret; + + ret = set_cpu_state(cpu, fpu, xcr0); + if (ret < 0) { + error_report("failed to set cpu state"); + return -1; + } + + return 0; +} + +static int put_regs(const CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + MshvFPU fpu = {0}; + int ret; + + memset(&fpu, 0, sizeof(fpu)); + + ret = mshv_configure_vcpu(cpu, &fpu, env->xcr0); + if (ret < 0) { + error_report("failed to configure vcpu"); + return ret; + } + + return 0; +} + int mshv_arch_put_registers(const CPUState *cpu) { + int ret; + + ret = put_regs(cpu); + if (ret < 0) { + error_report("Failed to put registers"); + return -1; + } + error_report("unimplemented"); abort(); } From ca20d46fa94887a6e42061646a71ee44cbc9adb0 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:37 +0200 Subject: [PATCH 1414/1794] target/i386/mshv: Set local interrupt controller state To set the local interrupt controller state, perform hv calls retrieving partition state from the hypervisor. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-18-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/mshv/mshv-cpu.c | 117 ++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 8b10c79e54783..0fe3cbb48d8c7 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "qemu/memalign.h" #include "qemu/typedefs.h" #include "system/mshv.h" @@ -21,6 +22,7 @@ #include "hw/hyperv/hvgdk.h" #include "hw/hyperv/hvgdk_mini.h" #include "hw/hyperv/hvhdk_mini.h" +#include "hw/i386/apic_internal.h" #include "cpu.h" #include "emulate/x86_decode.h" @@ -562,6 +564,114 @@ static int set_cpu_state(const CPUState *cpu, const MshvFPU *fpu_regs, return 0; } +static int get_vp_state(int cpu_fd, struct mshv_get_set_vp_state *state) +{ + int ret; + + ret = ioctl(cpu_fd, MSHV_GET_VP_STATE, state); + if (ret < 0) { + error_report("failed to get partition state: %s", strerror(errno)); + return -1; + } + + return 0; +} + +static int get_lapic(int cpu_fd, + struct hv_local_interrupt_controller_state *state) +{ + int ret; + size_t size = 4096; + /* buffer aligned to 4k, as *state requires that */ + void *buffer = qemu_memalign(size, size); + struct mshv_get_set_vp_state mshv_state = { 0 }; + + mshv_state.buf_ptr = (uint64_t) buffer; + mshv_state.buf_sz = size; + mshv_state.type = MSHV_VP_STATE_LAPIC; + + ret = get_vp_state(cpu_fd, &mshv_state); + if (ret == 0) { + memcpy(state, buffer, sizeof(*state)); + } + qemu_vfree(buffer); + if (ret < 0) { + error_report("failed to get lapic"); + return -1; + } + + return 0; +} + +static uint32_t set_apic_delivery_mode(uint32_t reg, uint32_t mode) +{ + return ((reg) & ~0x700) | ((mode) << 8); +} + +static int set_vp_state(int cpu_fd, const struct mshv_get_set_vp_state *state) +{ + int ret; + + ret = ioctl(cpu_fd, MSHV_SET_VP_STATE, state); + if (ret < 0) { + error_report("failed to set partition state: %s", strerror(errno)); + return -1; + } + + return 0; +} + +static int set_lapic(int cpu_fd, + const struct hv_local_interrupt_controller_state *state) +{ + int ret; + size_t size = 4096; + /* buffer aligned to 4k, as *state requires that */ + void *buffer = qemu_memalign(size, size); + struct mshv_get_set_vp_state mshv_state = { 0 }; + + if (!state) { + error_report("lapic state is NULL"); + return -1; + } + memcpy(buffer, state, sizeof(*state)); + + mshv_state.buf_ptr = (uint64_t) buffer; + mshv_state.buf_sz = size; + mshv_state.type = MSHV_VP_STATE_LAPIC; + + ret = set_vp_state(cpu_fd, &mshv_state); + qemu_vfree(buffer); + if (ret < 0) { + error_report("failed to set lapic: %s", strerror(errno)); + return -1; + } + + return 0; +} + +static int set_lint(int cpu_fd) +{ + int ret; + uint32_t *lvt_lint0, *lvt_lint1; + + struct hv_local_interrupt_controller_state lapic_state = { 0 }; + ret = get_lapic(cpu_fd, &lapic_state); + if (ret < 0) { + return ret; + } + + lvt_lint0 = &lapic_state.apic_lvt_lint0; + *lvt_lint0 = set_apic_delivery_mode(*lvt_lint0, APIC_DM_EXTINT); + + lvt_lint1 = &lapic_state.apic_lvt_lint1; + *lvt_lint1 = set_apic_delivery_mode(*lvt_lint1, APIC_DM_NMI); + + /* TODO: should we skip setting lapic if the values are the same? */ + + return set_lapic(cpu_fd, &lapic_state); +} + /* * TODO: populate topology info: * @@ -573,6 +683,7 @@ int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu, uint64_t xcr0) { int ret; + int cpu_fd = mshv_vcpufd(cpu); ret = set_cpu_state(cpu, fpu, xcr0); if (ret < 0) { @@ -580,6 +691,12 @@ int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu, return -1; } + ret = set_lint(cpu_fd); + if (ret < 0) { + error_report("failed to set lpic int"); + return -1; + } + return 0; } From 4fa04dd16216e266564606b7da582e5fce07bced Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:38 +0200 Subject: [PATCH 1415/1794] target/i386/mshv: Register CPUID entries with MSHV Convert the guest CPU's CPUID model into MSHV's format and register it with the hypervisor. This ensures that the guest observes the correct CPU feature set during CPUID instructions. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-19-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/mshv/mshv-cpu.c | 206 ++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 0fe3cbb48d8c7..2b7a81274b3b4 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -403,6 +403,206 @@ int mshv_load_regs(CPUState *cpu) return 0; } +static void add_cpuid_entry(GList *cpuid_entries, + uint32_t function, uint32_t index, + uint32_t eax, uint32_t ebx, + uint32_t ecx, uint32_t edx) +{ + struct hv_cpuid_entry *entry; + + entry = g_malloc0(sizeof(struct hv_cpuid_entry)); + entry->function = function; + entry->index = index; + entry->eax = eax; + entry->ebx = ebx; + entry->ecx = ecx; + entry->edx = edx; + + cpuid_entries = g_list_append(cpuid_entries, entry); +} + +static void collect_cpuid_entries(const CPUState *cpu, GList *cpuid_entries) +{ + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + uint32_t eax, ebx, ecx, edx; + uint32_t leaf, subleaf; + size_t max_leaf = 0x1F; + size_t max_subleaf = 0x20; + + uint32_t leaves_with_subleaves[] = {0x4, 0x7, 0xD, 0xF, 0x10}; + int n_subleaf_leaves = ARRAY_SIZE(leaves_with_subleaves); + + /* Regular leaves without subleaves */ + for (leaf = 0; leaf <= max_leaf; leaf++) { + bool has_subleaves = false; + for (int i = 0; i < n_subleaf_leaves; i++) { + if (leaf == leaves_with_subleaves[i]) { + has_subleaves = true; + break; + } + } + + if (!has_subleaves) { + cpu_x86_cpuid(env, leaf, 0, &eax, &ebx, &ecx, &edx); + if (eax == 0 && ebx == 0 && ecx == 0 && edx == 0) { + /* all zeroes indicates no more leaves */ + continue; + } + + add_cpuid_entry(cpuid_entries, leaf, 0, eax, ebx, ecx, edx); + continue; + } + + subleaf = 0; + while (subleaf < max_subleaf) { + cpu_x86_cpuid(env, leaf, subleaf, &eax, &ebx, &ecx, &edx); + + if (eax == 0 && ebx == 0 && ecx == 0 && edx == 0) { + /* all zeroes indicates no more leaves */ + break; + } + add_cpuid_entry(cpuid_entries, leaf, 0, eax, ebx, ecx, edx); + subleaf++; + } + } +} + +static int register_intercept_result_cpuid_entry(const CPUState *cpu, + uint8_t subleaf_specific, + uint8_t always_override, + struct hv_cpuid_entry *entry) +{ + int ret; + int vp_index = cpu->cpu_index; + int cpu_fd = mshv_vcpufd(cpu); + + struct hv_register_x64_cpuid_result_parameters cpuid_params = { + .input.eax = entry->function, + .input.ecx = entry->index, + .input.subleaf_specific = subleaf_specific, + .input.always_override = always_override, + .input.padding = 0, + /* + * With regard to masks - these are to specify bits to be overwritten + * The current CpuidEntry structure wouldn't allow to carry the masks + * in addition to the actual register values. For this reason, the + * masks are set to the exact values of the corresponding register bits + * to be registered for an overwrite. To view resulting values the + * hypervisor would return, HvCallGetVpCpuidValues hypercall can be + * used. + */ + .result.eax = entry->eax, + .result.eax_mask = entry->eax, + .result.ebx = entry->ebx, + .result.ebx_mask = entry->ebx, + .result.ecx = entry->ecx, + .result.ecx_mask = entry->ecx, + .result.edx = entry->edx, + .result.edx_mask = entry->edx, + }; + union hv_register_intercept_result_parameters parameters = { + .cpuid = cpuid_params, + }; + + hv_input_register_intercept_result in = {0}; + in.vp_index = vp_index; + in.intercept_type = HV_INTERCEPT_TYPE_X64_CPUID; + in.parameters = parameters; + + struct mshv_root_hvcall args = {0}; + args.code = HVCALL_REGISTER_INTERCEPT_RESULT; + args.in_sz = sizeof(in); + args.in_ptr = (uint64_t)∈ + + ret = mshv_hvcall(cpu_fd, &args); + if (ret < 0) { + error_report("failed to register intercept result for cpuid"); + return -1; + } + + return 0; +} + +static int register_intercept_result_cpuid(const CPUState *cpu, + struct hv_cpuid *cpuid) +{ + int ret = 0, entry_ret; + struct hv_cpuid_entry *entry; + uint8_t subleaf_specific, always_override; + + for (size_t i = 0; i < cpuid->nent; i++) { + entry = &cpuid->entries[i]; + + /* set defaults */ + subleaf_specific = 0; + always_override = 1; + + /* Intel */ + /* 0xb - Extended Topology Enumeration Leaf */ + /* 0x1f - V2 Extended Topology Enumeration Leaf */ + /* AMD */ + /* 0x8000_001e - Processor Topology Information */ + /* 0x8000_0026 - Extended CPU Topology */ + if (entry->function == 0xb + || entry->function == 0x1f + || entry->function == 0x8000001e + || entry->function == 0x80000026) { + subleaf_specific = 1; + always_override = 1; + } else if (entry->function == 0x00000001 + || entry->function == 0x80000000 + || entry->function == 0x80000001 + || entry->function == 0x80000008) { + subleaf_specific = 0; + always_override = 1; + } + + entry_ret = register_intercept_result_cpuid_entry(cpu, subleaf_specific, + always_override, + entry); + if ((entry_ret < 0) && (ret == 0)) { + ret = entry_ret; + } + } + + return ret; +} + +static int set_cpuid2(const CPUState *cpu) +{ + int ret; + size_t n_entries, cpuid_size; + struct hv_cpuid *cpuid; + struct hv_cpuid_entry *entry; + GList *entries = NULL; + + collect_cpuid_entries(cpu, entries); + n_entries = g_list_length(entries); + + cpuid_size = sizeof(struct hv_cpuid) + + n_entries * sizeof(struct hv_cpuid_entry); + + cpuid = g_malloc0(cpuid_size); + cpuid->nent = n_entries; + cpuid->padding = 0; + + for (size_t i = 0; i < n_entries; i++) { + entry = g_list_nth_data(entries, i); + cpuid->entries[i] = *entry; + g_free(entry); + } + g_list_free(entries); + + ret = register_intercept_result_cpuid(cpu, cpuid); + g_free(cpuid); + if (ret < 0) { + return ret; + } + + return 0; +} + static inline void populate_hv_segment_reg(SegmentCache *seg, hv_x64_segment_register *hv_reg) { @@ -685,6 +885,12 @@ int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu, int ret; int cpu_fd = mshv_vcpufd(cpu); + ret = set_cpuid2(cpu); + if (ret < 0) { + error_report("failed to set cpuid"); + return -1; + } + ret = set_cpu_state(cpu, fpu, xcr0); if (ret < 0) { error_report("failed to set cpu state"); From f38e2a63e541730e114f6ed09e5f8719e388c8db Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Thu, 2 Oct 2025 18:19:22 +0200 Subject: [PATCH 1416/1794] target/i386/mshv: Register MSRs with MSHV Build and register the guest vCPU's model-specific registers using the MSHV interface. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-20-magnuskulke@linux.microsoft.com [mshv.h/mshv_int.h split. - Paolo] Signed-off-by: Paolo Bonzini --- accel/mshv/meson.build | 1 + accel/mshv/msr.c | 375 ++++++++++++++++++++++++++++++++++++ include/system/mshv_int.h | 17 ++ target/i386/cpu.h | 2 + target/i386/mshv/mshv-cpu.c | 33 ++++ 5 files changed, 428 insertions(+) create mode 100644 accel/mshv/msr.c diff --git a/accel/mshv/meson.build b/accel/mshv/meson.build index f88fc8678c3d6..d3a2b32581122 100644 --- a/accel/mshv/meson.build +++ b/accel/mshv/meson.build @@ -2,6 +2,7 @@ mshv_ss = ss.source_set() mshv_ss.add(if_true: files( 'irq.c', 'mem.c', + 'msr.c', 'mshv-all.c' )) diff --git a/accel/mshv/msr.c b/accel/mshv/msr.c new file mode 100644 index 0000000000000..e6e5baef507bf --- /dev/null +++ b/accel/mshv/msr.c @@ -0,0 +1,375 @@ +/* + * QEMU MSHV support + * + * Copyright Microsoft, Corp. 2025 + * + * Authors: Magnus Kulke + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/mshv.h" +#include "system/mshv_int.h" +#include "hw/hyperv/hvgdk_mini.h" +#include "linux/mshv.h" +#include "qemu/error-report.h" + +static uint32_t supported_msrs[64] = { + IA32_MSR_TSC, + IA32_MSR_EFER, + IA32_MSR_KERNEL_GS_BASE, + IA32_MSR_APIC_BASE, + IA32_MSR_PAT, + IA32_MSR_SYSENTER_CS, + IA32_MSR_SYSENTER_ESP, + IA32_MSR_SYSENTER_EIP, + IA32_MSR_STAR, + IA32_MSR_LSTAR, + IA32_MSR_CSTAR, + IA32_MSR_SFMASK, + IA32_MSR_MTRR_DEF_TYPE, + IA32_MSR_MTRR_PHYSBASE0, + IA32_MSR_MTRR_PHYSMASK0, + IA32_MSR_MTRR_PHYSBASE1, + IA32_MSR_MTRR_PHYSMASK1, + IA32_MSR_MTRR_PHYSBASE2, + IA32_MSR_MTRR_PHYSMASK2, + IA32_MSR_MTRR_PHYSBASE3, + IA32_MSR_MTRR_PHYSMASK3, + IA32_MSR_MTRR_PHYSBASE4, + IA32_MSR_MTRR_PHYSMASK4, + IA32_MSR_MTRR_PHYSBASE5, + IA32_MSR_MTRR_PHYSMASK5, + IA32_MSR_MTRR_PHYSBASE6, + IA32_MSR_MTRR_PHYSMASK6, + IA32_MSR_MTRR_PHYSBASE7, + IA32_MSR_MTRR_PHYSMASK7, + IA32_MSR_MTRR_FIX64K_00000, + IA32_MSR_MTRR_FIX16K_80000, + IA32_MSR_MTRR_FIX16K_A0000, + IA32_MSR_MTRR_FIX4K_C0000, + IA32_MSR_MTRR_FIX4K_C8000, + IA32_MSR_MTRR_FIX4K_D0000, + IA32_MSR_MTRR_FIX4K_D8000, + IA32_MSR_MTRR_FIX4K_E0000, + IA32_MSR_MTRR_FIX4K_E8000, + IA32_MSR_MTRR_FIX4K_F0000, + IA32_MSR_MTRR_FIX4K_F8000, + IA32_MSR_TSC_AUX, + IA32_MSR_DEBUG_CTL, + HV_X64_MSR_GUEST_OS_ID, + HV_X64_MSR_SINT0, + HV_X64_MSR_SINT1, + HV_X64_MSR_SINT2, + HV_X64_MSR_SINT3, + HV_X64_MSR_SINT4, + HV_X64_MSR_SINT5, + HV_X64_MSR_SINT6, + HV_X64_MSR_SINT7, + HV_X64_MSR_SINT8, + HV_X64_MSR_SINT9, + HV_X64_MSR_SINT10, + HV_X64_MSR_SINT11, + HV_X64_MSR_SINT12, + HV_X64_MSR_SINT13, + HV_X64_MSR_SINT14, + HV_X64_MSR_SINT15, + HV_X64_MSR_SCONTROL, + HV_X64_MSR_SIEFP, + HV_X64_MSR_SIMP, + HV_X64_MSR_REFERENCE_TSC, + HV_X64_MSR_EOM, +}; +static const size_t msr_count = ARRAY_SIZE(supported_msrs); + +static int compare_msr_index(const void *a, const void *b) +{ + return *(uint32_t *)a - *(uint32_t *)b; +} + +__attribute__((constructor)) +static void init_sorted_msr_map(void) +{ + qsort(supported_msrs, msr_count, sizeof(uint32_t), compare_msr_index); +} + +static int mshv_is_supported_msr(uint32_t msr) +{ + return bsearch(&msr, supported_msrs, msr_count, sizeof(uint32_t), + compare_msr_index) != NULL; +} + +static int mshv_msr_to_hv_reg_name(uint32_t msr, uint32_t *hv_reg) +{ + switch (msr) { + case IA32_MSR_TSC: + *hv_reg = HV_X64_REGISTER_TSC; + return 0; + case IA32_MSR_EFER: + *hv_reg = HV_X64_REGISTER_EFER; + return 0; + case IA32_MSR_KERNEL_GS_BASE: + *hv_reg = HV_X64_REGISTER_KERNEL_GS_BASE; + return 0; + case IA32_MSR_APIC_BASE: + *hv_reg = HV_X64_REGISTER_APIC_BASE; + return 0; + case IA32_MSR_PAT: + *hv_reg = HV_X64_REGISTER_PAT; + return 0; + case IA32_MSR_SYSENTER_CS: + *hv_reg = HV_X64_REGISTER_SYSENTER_CS; + return 0; + case IA32_MSR_SYSENTER_ESP: + *hv_reg = HV_X64_REGISTER_SYSENTER_ESP; + return 0; + case IA32_MSR_SYSENTER_EIP: + *hv_reg = HV_X64_REGISTER_SYSENTER_EIP; + return 0; + case IA32_MSR_STAR: + *hv_reg = HV_X64_REGISTER_STAR; + return 0; + case IA32_MSR_LSTAR: + *hv_reg = HV_X64_REGISTER_LSTAR; + return 0; + case IA32_MSR_CSTAR: + *hv_reg = HV_X64_REGISTER_CSTAR; + return 0; + case IA32_MSR_SFMASK: + *hv_reg = HV_X64_REGISTER_SFMASK; + return 0; + case IA32_MSR_MTRR_CAP: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_CAP; + return 0; + case IA32_MSR_MTRR_DEF_TYPE: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_DEF_TYPE; + return 0; + case IA32_MSR_MTRR_PHYSBASE0: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE0; + return 0; + case IA32_MSR_MTRR_PHYSMASK0: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK0; + return 0; + case IA32_MSR_MTRR_PHYSBASE1: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE1; + return 0; + case IA32_MSR_MTRR_PHYSMASK1: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK1; + return 0; + case IA32_MSR_MTRR_PHYSBASE2: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE2; + return 0; + case IA32_MSR_MTRR_PHYSMASK2: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK2; + return 0; + case IA32_MSR_MTRR_PHYSBASE3: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE3; + return 0; + case IA32_MSR_MTRR_PHYSMASK3: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK3; + return 0; + case IA32_MSR_MTRR_PHYSBASE4: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE4; + return 0; + case IA32_MSR_MTRR_PHYSMASK4: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK4; + return 0; + case IA32_MSR_MTRR_PHYSBASE5: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE5; + return 0; + case IA32_MSR_MTRR_PHYSMASK5: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK5; + return 0; + case IA32_MSR_MTRR_PHYSBASE6: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE6; + return 0; + case IA32_MSR_MTRR_PHYSMASK6: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK6; + return 0; + case IA32_MSR_MTRR_PHYSBASE7: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE7; + return 0; + case IA32_MSR_MTRR_PHYSMASK7: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK7; + return 0; + case IA32_MSR_MTRR_FIX64K_00000: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX64K00000; + return 0; + case IA32_MSR_MTRR_FIX16K_80000: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX16K80000; + return 0; + case IA32_MSR_MTRR_FIX16K_A0000: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX16KA0000; + return 0; + case IA32_MSR_MTRR_FIX4K_C0000: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KC0000; + return 0; + case IA32_MSR_MTRR_FIX4K_C8000: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KC8000; + return 0; + case IA32_MSR_MTRR_FIX4K_D0000: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KD0000; + return 0; + case IA32_MSR_MTRR_FIX4K_D8000: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KD8000; + return 0; + case IA32_MSR_MTRR_FIX4K_E0000: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KE0000; + return 0; + case IA32_MSR_MTRR_FIX4K_E8000: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KE8000; + return 0; + case IA32_MSR_MTRR_FIX4K_F0000: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KF0000; + return 0; + case IA32_MSR_MTRR_FIX4K_F8000: + *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KF8000; + return 0; + case IA32_MSR_TSC_AUX: + *hv_reg = HV_X64_REGISTER_TSC_AUX; + return 0; + case IA32_MSR_BNDCFGS: + *hv_reg = HV_X64_REGISTER_BNDCFGS; + return 0; + case IA32_MSR_DEBUG_CTL: + *hv_reg = HV_X64_REGISTER_DEBUG_CTL; + return 0; + case IA32_MSR_TSC_ADJUST: + *hv_reg = HV_X64_REGISTER_TSC_ADJUST; + return 0; + case IA32_MSR_SPEC_CTRL: + *hv_reg = HV_X64_REGISTER_SPEC_CTRL; + return 0; + case HV_X64_MSR_GUEST_OS_ID: + *hv_reg = HV_REGISTER_GUEST_OS_ID; + return 0; + case HV_X64_MSR_SINT0: + *hv_reg = HV_REGISTER_SINT0; + return 0; + case HV_X64_MSR_SINT1: + *hv_reg = HV_REGISTER_SINT1; + return 0; + case HV_X64_MSR_SINT2: + *hv_reg = HV_REGISTER_SINT2; + return 0; + case HV_X64_MSR_SINT3: + *hv_reg = HV_REGISTER_SINT3; + return 0; + case HV_X64_MSR_SINT4: + *hv_reg = HV_REGISTER_SINT4; + return 0; + case HV_X64_MSR_SINT5: + *hv_reg = HV_REGISTER_SINT5; + return 0; + case HV_X64_MSR_SINT6: + *hv_reg = HV_REGISTER_SINT6; + return 0; + case HV_X64_MSR_SINT7: + *hv_reg = HV_REGISTER_SINT7; + return 0; + case HV_X64_MSR_SINT8: + *hv_reg = HV_REGISTER_SINT8; + return 0; + case HV_X64_MSR_SINT9: + *hv_reg = HV_REGISTER_SINT9; + return 0; + case HV_X64_MSR_SINT10: + *hv_reg = HV_REGISTER_SINT10; + return 0; + case HV_X64_MSR_SINT11: + *hv_reg = HV_REGISTER_SINT11; + return 0; + case HV_X64_MSR_SINT12: + *hv_reg = HV_REGISTER_SINT12; + return 0; + case HV_X64_MSR_SINT13: + *hv_reg = HV_REGISTER_SINT13; + return 0; + case HV_X64_MSR_SINT14: + *hv_reg = HV_REGISTER_SINT14; + return 0; + case HV_X64_MSR_SINT15: + *hv_reg = HV_REGISTER_SINT15; + return 0; + case IA32_MSR_MISC_ENABLE: + *hv_reg = HV_X64_REGISTER_MSR_IA32_MISC_ENABLE; + return 0; + case HV_X64_MSR_SCONTROL: + *hv_reg = HV_REGISTER_SCONTROL; + return 0; + case HV_X64_MSR_SIEFP: + *hv_reg = HV_REGISTER_SIEFP; + return 0; + case HV_X64_MSR_SIMP: + *hv_reg = HV_REGISTER_SIMP; + return 0; + case HV_X64_MSR_REFERENCE_TSC: + *hv_reg = HV_REGISTER_REFERENCE_TSC; + return 0; + case HV_X64_MSR_EOM: + *hv_reg = HV_REGISTER_EOM; + return 0; + default: + error_report("failed to map MSR %u to HV register name", msr); + return -1; + } +} + +static int set_msrs(const CPUState *cpu, GList *msrs) +{ + size_t n_msrs; + GList *entries; + MshvMsrEntry *entry; + enum hv_register_name name; + struct hv_register_assoc *assoc; + int ret; + size_t i = 0; + + n_msrs = g_list_length(msrs); + hv_register_assoc *assocs = g_new0(hv_register_assoc, n_msrs); + + entries = msrs; + for (const GList *elem = entries; elem != NULL; elem = elem->next) { + entry = elem->data; + ret = mshv_msr_to_hv_reg_name(entry->index, &name); + if (ret < 0) { + g_free(assocs); + return ret; + } + assoc = &assocs[i]; + assoc->name = name; + /* the union has been initialized to 0 */ + assoc->value.reg64 = entry->data; + i++; + } + ret = mshv_set_generic_regs(cpu, assocs, n_msrs); + g_free(assocs); + if (ret < 0) { + error_report("failed to set msrs"); + return -1; + } + return 0; +} + + +int mshv_configure_msr(const CPUState *cpu, const MshvMsrEntry *msrs, + size_t n_msrs) +{ + GList *valid_msrs = NULL; + uint32_t msr_index; + int ret; + + for (size_t i = 0; i < n_msrs; i++) { + msr_index = msrs[i].index; + /* check whether index of msrs is in SUPPORTED_MSRS */ + if (mshv_is_supported_msr(msr_index)) { + valid_msrs = g_list_append(valid_msrs, (void *) &msrs[i]); + } + } + + ret = set_msrs(cpu, valid_msrs); + g_list_free(valid_msrs); + + return ret; +} diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index 0ea8d504fa519..6649438313432 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -14,6 +14,8 @@ #ifndef QEMU_MSHV_INT_H #define QEMU_MSHV_INT_H +#define MSHV_MSR_ENTRIES_COUNT 64 + typedef struct hyperv_message hv_message; struct AccelCPUState { @@ -102,6 +104,21 @@ typedef struct MshvMemoryRegion { void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section, bool add); +/* msr */ +typedef struct MshvMsrEntry { + uint32_t index; + uint32_t reserved; + uint64_t data; +} MshvMsrEntry; + +typedef struct MshvMsrEntries { + MshvMsrEntry entries[MSHV_MSR_ENTRIES_COUNT]; + uint32_t nmsrs; +} MshvMsrEntries; + +int mshv_configure_msr(const CPUState *cpu, const MshvMsrEntry *msrs, + size_t n_msrs); + /* interrupt */ void mshv_init_msicontrol(void); int mshv_reserve_ioapic_msi_routes(int vm_fd); diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 3aec8fd41c822..8b7c173838e51 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -435,9 +435,11 @@ typedef enum X86Seg { #define MSR_SMI_COUNT 0x34 #define MSR_CORE_THREAD_COUNT 0x35 #define MSR_MTRRcap 0xfe +#define MSR_MTRR_MEM_TYPE_WB 0x06 #define MSR_MTRRcap_VCNT 8 #define MSR_MTRRcap_FIXRANGE_SUPPORT (1 << 8) #define MSR_MTRRcap_WC_SUPPORTED (1 << 10) +#define MSR_MTRR_ENABLE (1 << 11) #define MSR_IA32_SYSENTER_CS 0x174 #define MSR_IA32_SYSENTER_ESP 0x175 diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 2b7a81274b3b4..1f43dfc58ac55 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -872,6 +872,33 @@ static int set_lint(int cpu_fd) return set_lapic(cpu_fd, &lapic_state); } +static int setup_msrs(const CPUState *cpu) +{ + int ret; + uint64_t default_type = MSR_MTRR_ENABLE | MSR_MTRR_MEM_TYPE_WB; + + /* boot msr entries */ + MshvMsrEntry msrs[9] = { + { .index = IA32_MSR_SYSENTER_CS, .data = 0x0, }, + { .index = IA32_MSR_SYSENTER_ESP, .data = 0x0, }, + { .index = IA32_MSR_SYSENTER_EIP, .data = 0x0, }, + { .index = IA32_MSR_STAR, .data = 0x0, }, + { .index = IA32_MSR_CSTAR, .data = 0x0, }, + { .index = IA32_MSR_LSTAR, .data = 0x0, }, + { .index = IA32_MSR_KERNEL_GS_BASE, .data = 0x0, }, + { .index = IA32_MSR_SFMASK, .data = 0x0, }, + { .index = IA32_MSR_MTRR_DEF_TYPE, .data = default_type, }, + }; + + ret = mshv_configure_msr(cpu, msrs, 9); + if (ret < 0) { + error_report("failed to setup msrs"); + return -1; + } + + return 0; +} + /* * TODO: populate topology info: * @@ -891,6 +918,12 @@ int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu, return -1; } + ret = setup_msrs(cpu); + if (ret < 0) { + error_report("failed to setup msrs"); + return -1; + } + ret = set_cpu_state(cpu, fpu, xcr0); if (ret < 0) { error_report("failed to set cpu state"); From 9bc6a1d29605a13c541629a651e41787af65a963 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:40 +0200 Subject: [PATCH 1417/1794] target/i386/mshv: Integrate x86 instruction decoder/emulator Connect the x86 instruction decoder and emulator to the MSHV backend to handle intercepted instructions. This enables software emulation of MMIO operations in MSHV guests. MSHV has a translate_gva hypercall that is used to accessing the physical guest memory. A guest might read from unmapped memory regions (e.g. OVMF will probe 0xfed40000 for a vTPM). In those cases 0xFF bytes is returned instead of aborting the execution. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-21-magnuskulke@linux.microsoft.com [mshv.h/mshv_int.h split. - Paolo] Signed-off-by: Paolo Bonzini --- accel/mshv/mem.c | 65 +++++++++++++++++ include/system/mshv_int.h | 4 ++ target/i386/mshv/mshv-cpu.c | 135 ++++++++++++++++++++++++++++++++++++ 3 files changed, 204 insertions(+) diff --git a/accel/mshv/mem.c b/accel/mshv/mem.c index a0a40eb3333c6..e55c38d4db9f1 100644 --- a/accel/mshv/mem.c +++ b/accel/mshv/mem.c @@ -59,6 +59,71 @@ static int map_or_unmap(int vm_fd, const MshvMemoryRegion *mr, bool map) return set_guest_memory(vm_fd, ®ion); } +static int handle_unmapped_mmio_region_read(uint64_t gpa, uint64_t size, + uint8_t *data) +{ + warn_report("read from unmapped mmio region gpa=0x%lx size=%lu", gpa, size); + + if (size == 0 || size > 8) { + error_report("invalid size %lu for reading from unmapped mmio region", + size); + return -1; + } + + memset(data, 0xFF, size); + + return 0; +} + +int mshv_guest_mem_read(uint64_t gpa, uint8_t *data, uintptr_t size, + bool is_secure_mode, bool instruction_fetch) +{ + int ret; + MemTxAttrs memattr = { .secure = is_secure_mode }; + + if (instruction_fetch) { + trace_mshv_insn_fetch(gpa, size); + } else { + trace_mshv_mem_read(gpa, size); + } + + ret = address_space_rw(&address_space_memory, gpa, memattr, (void *)data, + size, false); + if (ret == MEMTX_OK) { + return 0; + } + + if (ret == MEMTX_DECODE_ERROR) { + return handle_unmapped_mmio_region_read(gpa, size, data); + } + + error_report("failed to read guest memory at 0x%lx", gpa); + return -1; +} + +int mshv_guest_mem_write(uint64_t gpa, const uint8_t *data, uintptr_t size, + bool is_secure_mode) +{ + int ret; + MemTxAttrs memattr = { .secure = is_secure_mode }; + + trace_mshv_mem_write(gpa, size); + ret = address_space_rw(&address_space_memory, gpa, memattr, (void *)data, + size, true); + if (ret == MEMTX_OK) { + return 0; + } + + if (ret == MEMTX_DECODE_ERROR) { + warn_report("write to unmapped mmio region gpa=0x%lx size=%lu", gpa, + size); + return 0; + } + + error_report("Failed to write guest memory"); + return -1; +} + static int set_memory(const MshvMemoryRegion *mshv_mr, bool add) { int ret = 0; diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index 6649438313432..b29d39911dbab 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -101,6 +101,10 @@ typedef struct MshvMemoryRegion { bool readonly; } MshvMemoryRegion; +int mshv_guest_mem_read(uint64_t gpa, uint8_t *data, uintptr_t size, + bool is_secure_mode, bool instruction_fetch); +int mshv_guest_mem_write(uint64_t gpa, const uint8_t *data, uintptr_t size, + bool is_secure_mode); void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section, bool add); diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 1f43dfc58ac55..424ebdb1228e1 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -104,6 +104,47 @@ static enum hv_register_name FPU_REGISTER_NAMES[26] = { HV_X64_REGISTER_XMM_CONTROL_STATUS, }; +static int translate_gva(const CPUState *cpu, uint64_t gva, uint64_t *gpa, + uint64_t flags) +{ + int ret; + int cpu_fd = mshv_vcpufd(cpu); + int vp_index = cpu->cpu_index; + + hv_input_translate_virtual_address in = { 0 }; + hv_output_translate_virtual_address out = { 0 }; + struct mshv_root_hvcall args = {0}; + uint64_t gva_page = gva >> HV_HYP_PAGE_SHIFT; + + in.vp_index = vp_index; + in.control_flags = flags; + in.gva_page = gva_page; + + /* create the hvcall envelope */ + args.code = HVCALL_TRANSLATE_VIRTUAL_ADDRESS; + args.in_sz = sizeof(in); + args.in_ptr = (uint64_t) ∈ + args.out_sz = sizeof(out); + args.out_ptr = (uint64_t) &out; + + /* perform the call */ + ret = mshv_hvcall(cpu_fd, &args); + if (ret < 0) { + error_report("Failed to invoke gva->gpa translation"); + return -errno; + } + + if (out.translation_result.result_code != HV_TRANSLATE_GVA_SUCCESS) { + error_report("Failed to translate gva (" TARGET_FMT_lx ") to gpa", gva); + return -1; + } + + *gpa = ((out.gpa_page << HV_HYP_PAGE_SHIFT) + | (gva & ~(uint64_t)HV_HYP_PAGE_MASK)); + + return 0; +} + int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs, size_t n_regs) { @@ -1006,8 +1047,102 @@ int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd) return 0; } +static int guest_mem_read_with_gva(const CPUState *cpu, uint64_t gva, + uint8_t *data, uintptr_t size, + bool fetch_instruction) +{ + int ret; + uint64_t gpa, flags; + + flags = HV_TRANSLATE_GVA_VALIDATE_READ; + ret = translate_gva(cpu, gva, &gpa, flags); + if (ret < 0) { + error_report("failed to translate gva to gpa"); + return -1; + } + + ret = mshv_guest_mem_read(gpa, data, size, false, fetch_instruction); + if (ret < 0) { + error_report("failed to read from guest memory"); + return -1; + } + + return 0; +} + +static int guest_mem_write_with_gva(const CPUState *cpu, uint64_t gva, + const uint8_t *data, uintptr_t size) +{ + int ret; + uint64_t gpa, flags; + + flags = HV_TRANSLATE_GVA_VALIDATE_WRITE; + ret = translate_gva(cpu, gva, &gpa, flags); + if (ret < 0) { + error_report("failed to translate gva to gpa"); + return -1; + } + ret = mshv_guest_mem_write(gpa, data, size, false); + if (ret < 0) { + error_report("failed to write to guest memory"); + return -1; + } + return 0; +} + +static void write_mem(CPUState *cpu, void *data, target_ulong addr, int bytes) +{ + if (guest_mem_write_with_gva(cpu, addr, data, bytes) < 0) { + error_report("failed to write memory"); + abort(); + } +} + +static void fetch_instruction(CPUState *cpu, void *data, + target_ulong addr, int bytes) +{ + if (guest_mem_read_with_gva(cpu, addr, data, bytes, true) < 0) { + error_report("failed to fetch instruction"); + abort(); + } +} + +static void read_mem(CPUState *cpu, void *data, target_ulong addr, int bytes) +{ + if (guest_mem_read_with_gva(cpu, addr, data, bytes, false) < 0) { + error_report("failed to read memory"); + abort(); + } +} + +static void read_segment_descriptor(CPUState *cpu, + struct x86_segment_descriptor *desc, + enum X86Seg seg_idx) +{ + bool ret; + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + SegmentCache *seg = &env->segs[seg_idx]; + x86_segment_selector sel = { .sel = seg->selector & 0xFFFF }; + + ret = x86_read_segment_descriptor(cpu, desc, sel); + if (ret == false) { + error_report("failed to read segment descriptor"); + abort(); + } +} + +static const struct x86_emul_ops mshv_x86_emul_ops = { + .fetch_instruction = fetch_instruction, + .read_mem = read_mem, + .write_mem = write_mem, + .read_segment_descriptor = read_segment_descriptor, +}; + void mshv_init_mmio_emu(void) { + init_decoder(); + init_emu(&mshv_x86_emul_ops); } void mshv_arch_init_vcpu(CPUState *cpu) From 64118f452cbd97cd9fa790b1c15b65b435f136d2 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:41 +0200 Subject: [PATCH 1418/1794] target/i386/mshv: Write MSRs to the hypervisor Push current model-specific register (MSR) values to MSHV's vCPUs as part of setting state to the hypervisor. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-22-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/mshv/mshv-cpu.c | 68 +++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 424ebdb1228e1..33a3ce8b1105d 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -998,6 +998,65 @@ static int put_regs(const CPUState *cpu) return 0; } +struct MsrPair { + uint32_t index; + uint64_t value; +}; + +static int put_msrs(const CPUState *cpu) +{ + int ret = 0; + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + MshvMsrEntries *msrs = g_malloc0(sizeof(MshvMsrEntries)); + + struct MsrPair pairs[] = { + { MSR_IA32_SYSENTER_CS, env->sysenter_cs }, + { MSR_IA32_SYSENTER_ESP, env->sysenter_esp }, + { MSR_IA32_SYSENTER_EIP, env->sysenter_eip }, + { MSR_EFER, env->efer }, + { MSR_PAT, env->pat }, + { MSR_STAR, env->star }, + { MSR_CSTAR, env->cstar }, + { MSR_LSTAR, env->lstar }, + { MSR_KERNELGSBASE, env->kernelgsbase }, + { MSR_FMASK, env->fmask }, + { MSR_MTRRdefType, env->mtrr_deftype }, + { MSR_VM_HSAVE_PA, env->vm_hsave }, + { MSR_SMI_COUNT, env->msr_smi_count }, + { MSR_IA32_PKRS, env->pkrs }, + { MSR_IA32_BNDCFGS, env->msr_bndcfgs }, + { MSR_IA32_XSS, env->xss }, + { MSR_IA32_UMWAIT_CONTROL, env->umwait }, + { MSR_IA32_TSX_CTRL, env->tsx_ctrl }, + { MSR_AMD64_TSC_RATIO, env->amd_tsc_scale_msr }, + { MSR_TSC_AUX, env->tsc_aux }, + { MSR_TSC_ADJUST, env->tsc_adjust }, + { MSR_IA32_SMBASE, env->smbase }, + { MSR_IA32_SPEC_CTRL, env->spec_ctrl }, + { MSR_VIRT_SSBD, env->virt_ssbd }, + }; + + if (ARRAY_SIZE(pairs) > MSHV_MSR_ENTRIES_COUNT) { + error_report("MSR entries exceed maximum size"); + g_free(msrs); + return -1; + } + + for (size_t i = 0; i < ARRAY_SIZE(pairs); i++) { + MshvMsrEntry *entry = &msrs->entries[i]; + entry->index = pairs[i].index; + entry->reserved = 0; + entry->data = pairs[i].value; + msrs->nmsrs++; + } + + ret = mshv_configure_msr(cpu, &msrs->entries[0], msrs->nmsrs); + g_free(msrs); + return ret; +} + + int mshv_arch_put_registers(const CPUState *cpu) { int ret; @@ -1008,8 +1067,13 @@ int mshv_arch_put_registers(const CPUState *cpu) return -1; } - error_report("unimplemented"); - abort(); + ret = put_msrs(cpu); + if (ret < 0) { + error_report("Failed to put msrs"); + return -1; + } + + return 0; } void mshv_arch_amend_proc_features( From 6dec60528c419781f1f79cd9837d5f26415a3fd7 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:42 +0200 Subject: [PATCH 1419/1794] target/i386/mshv: Implement mshv_vcpu_run() Add the main vCPU execution loop for MSHV using the MSHV_RUN_VP ioctl. The execution loop handles guest entry and VM exits. There are handlers for memory r/w, PIO and MMIO to which the exit events are dispatched. In case of MMIO the i386 instruction decoder/emulator is invoked to perform the operation in user space. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-23-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/mshv/mshv-cpu.c | 444 +++++++++++++++++++++++++++++++++++- 1 file changed, 442 insertions(+), 2 deletions(-) diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 33a3ce8b1105d..7edc032cea3d3 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -1082,10 +1082,450 @@ void mshv_arch_amend_proc_features( features->access_guest_idle_reg = 1; } +static int set_memory_info(const struct hyperv_message *msg, + struct hv_x64_memory_intercept_message *info) +{ + if (msg->header.message_type != HVMSG_GPA_INTERCEPT + && msg->header.message_type != HVMSG_UNMAPPED_GPA + && msg->header.message_type != HVMSG_UNACCEPTED_GPA) { + error_report("invalid message type"); + return -1; + } + memcpy(info, msg->payload, sizeof(*info)); + + return 0; +} + +static int emulate_instruction(CPUState *cpu, + const uint8_t *insn_bytes, size_t insn_len, + uint64_t gva, uint64_t gpa) +{ + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + struct x86_decode decode = { 0 }; + int ret; + x86_insn_stream stream = { .bytes = insn_bytes, .len = insn_len }; + + ret = mshv_load_regs(cpu); + if (ret < 0) { + error_report("failed to load registers"); + return -1; + } + + decode_instruction_stream(env, &decode, &stream); + exec_instruction(env, &decode); + + ret = mshv_store_regs(cpu); + if (ret < 0) { + error_report("failed to store registers"); + return -1; + } + + return 0; +} + +static int handle_mmio(CPUState *cpu, const struct hyperv_message *msg, + MshvVmExit *exit_reason) +{ + struct hv_x64_memory_intercept_message info = { 0 }; + size_t insn_len; + uint8_t access_type; + uint8_t *instruction_bytes; + int ret; + + ret = set_memory_info(msg, &info); + if (ret < 0) { + error_report("failed to convert message to memory info"); + return -1; + } + insn_len = info.instruction_byte_count; + access_type = info.header.intercept_access_type; + + if (access_type == HV_X64_INTERCEPT_ACCESS_TYPE_EXECUTE) { + error_report("invalid intercept access type: execute"); + return -1; + } + + if (insn_len > 16) { + error_report("invalid mmio instruction length: %zu", insn_len); + return -1; + } + + trace_mshv_handle_mmio(info.guest_virtual_address, + info.guest_physical_address, + info.instruction_byte_count, access_type); + + instruction_bytes = info.instruction_bytes; + + ret = emulate_instruction(cpu, instruction_bytes, insn_len, + info.guest_virtual_address, + info.guest_physical_address); + if (ret < 0) { + error_report("failed to emulate mmio"); + return -1; + } + + *exit_reason = MshvVmExitIgnore; + + return 0; +} + +static int set_ioport_info(const struct hyperv_message *msg, + hv_x64_io_port_intercept_message *info) +{ + if (msg->header.message_type != HVMSG_X64_IO_PORT_INTERCEPT) { + error_report("Invalid message type"); + return -1; + } + memcpy(info, msg->payload, sizeof(*info)); + + return 0; +} + +static int set_x64_registers(const CPUState *cpu, const uint32_t *names, + const uint64_t *values) +{ + + hv_register_assoc assocs[2]; + int ret; + + for (size_t i = 0; i < ARRAY_SIZE(assocs); i++) { + assocs[i].name = names[i]; + assocs[i].value.reg64 = values[i]; + } + + ret = mshv_set_generic_regs(cpu, assocs, ARRAY_SIZE(assocs)); + if (ret < 0) { + error_report("failed to set x64 registers"); + return -1; + } + + return 0; +} + +static inline MemTxAttrs get_mem_attrs(bool is_secure_mode) +{ + MemTxAttrs memattr = {0}; + memattr.secure = is_secure_mode; + return memattr; +} + +static void pio_read(uint64_t port, uint8_t *data, uintptr_t size, + bool is_secure_mode) +{ + int ret = 0; + MemTxAttrs memattr = get_mem_attrs(is_secure_mode); + ret = address_space_rw(&address_space_io, port, memattr, (void *)data, size, + false); + if (ret != MEMTX_OK) { + error_report("Failed to read from port %lx: %d", port, ret); + abort(); + } +} + +static int pio_write(uint64_t port, const uint8_t *data, uintptr_t size, + bool is_secure_mode) +{ + int ret = 0; + MemTxAttrs memattr = get_mem_attrs(is_secure_mode); + ret = address_space_rw(&address_space_io, port, memattr, (void *)data, size, + true); + return ret; +} + +static int handle_pio_non_str(const CPUState *cpu, + hv_x64_io_port_intercept_message *info) +{ + size_t len = info->access_info.access_size; + uint8_t access_type = info->header.intercept_access_type; + int ret; + uint32_t val, eax; + const uint32_t eax_mask = 0xffffffffu >> (32 - len * 8); + size_t insn_len; + uint64_t rip, rax; + uint32_t reg_names[2]; + uint64_t reg_values[2]; + uint16_t port = info->port_number; + + if (access_type == HV_X64_INTERCEPT_ACCESS_TYPE_WRITE) { + union { + uint32_t u32; + uint8_t bytes[4]; + } conv; + + /* convert the first 4 bytes of rax to bytes */ + conv.u32 = (uint32_t)info->rax; + /* secure mode is set to false */ + ret = pio_write(port, conv.bytes, len, false); + if (ret < 0) { + error_report("Failed to write to io port"); + return -1; + } + } else { + uint8_t data[4] = { 0 }; + /* secure mode is set to false */ + pio_read(info->port_number, data, len, false); + + /* Preserve high bits in EAX, but clear out high bits in RAX */ + val = *(uint32_t *)data; + eax = (((uint32_t)info->rax) & ~eax_mask) | (val & eax_mask); + info->rax = (uint64_t)eax; + } + + insn_len = info->header.instruction_length; + + /* Advance RIP and update RAX */ + rip = info->header.rip + insn_len; + rax = info->rax; + + reg_names[0] = HV_X64_REGISTER_RIP; + reg_values[0] = rip; + reg_names[1] = HV_X64_REGISTER_RAX; + reg_values[1] = rax; + + ret = set_x64_registers(cpu, reg_names, reg_values); + if (ret < 0) { + error_report("Failed to set x64 registers"); + return -1; + } + + cpu->accel->dirty = false; + + return 0; +} + +static int fetch_guest_state(CPUState *cpu) +{ + int ret; + + ret = mshv_get_standard_regs(cpu); + if (ret < 0) { + error_report("Failed to get standard registers"); + return -1; + } + + ret = mshv_get_special_regs(cpu); + if (ret < 0) { + error_report("Failed to get special registers"); + return -1; + } + + return 0; +} + +static int read_memory(const CPUState *cpu, uint64_t initial_gva, + uint64_t initial_gpa, uint64_t gva, uint8_t *data, + size_t len) +{ + int ret; + uint64_t gpa, flags; + + if (gva == initial_gva) { + gpa = initial_gpa; + } else { + flags = HV_TRANSLATE_GVA_VALIDATE_READ; + ret = translate_gva(cpu, gva, &gpa, flags); + if (ret < 0) { + return -1; + } + + ret = mshv_guest_mem_read(gpa, data, len, false, false); + if (ret < 0) { + error_report("failed to read guest mem"); + return -1; + } + } + + return 0; +} + +static int write_memory(const CPUState *cpu, uint64_t initial_gva, + uint64_t initial_gpa, uint64_t gva, const uint8_t *data, + size_t len) +{ + int ret; + uint64_t gpa, flags; + + if (gva == initial_gva) { + gpa = initial_gpa; + } else { + flags = HV_TRANSLATE_GVA_VALIDATE_WRITE; + ret = translate_gva(cpu, gva, &gpa, flags); + if (ret < 0) { + error_report("failed to translate gva to gpa"); + return -1; + } + } + ret = mshv_guest_mem_write(gpa, data, len, false); + if (ret != MEMTX_OK) { + error_report("failed to write to mmio"); + return -1; + } + + return 0; +} + +static int handle_pio_str_write(CPUState *cpu, + hv_x64_io_port_intercept_message *info, + size_t repeat, uint16_t port, + bool direction_flag) +{ + int ret; + uint64_t src; + uint8_t data[4] = { 0 }; + size_t len = info->access_info.access_size; + + src = linear_addr(cpu, info->rsi, R_DS); + + for (size_t i = 0; i < repeat; i++) { + ret = read_memory(cpu, 0, 0, src, data, len); + if (ret < 0) { + error_report("Failed to read memory"); + return -1; + } + ret = pio_write(port, data, len, false); + if (ret < 0) { + error_report("Failed to write to io port"); + return -1; + } + src += direction_flag ? -len : len; + info->rsi += direction_flag ? -len : len; + } + + return 0; +} + +static int handle_pio_str_read(CPUState *cpu, + hv_x64_io_port_intercept_message *info, + size_t repeat, uint16_t port, + bool direction_flag) +{ + int ret; + uint64_t dst; + size_t len = info->access_info.access_size; + uint8_t data[4] = { 0 }; + + dst = linear_addr(cpu, info->rdi, R_ES); + + for (size_t i = 0; i < repeat; i++) { + pio_read(port, data, len, false); + + ret = write_memory(cpu, 0, 0, dst, data, len); + if (ret < 0) { + error_report("Failed to write memory"); + return -1; + } + dst += direction_flag ? -len : len; + info->rdi += direction_flag ? -len : len; + } + + return 0; +} + +static int handle_pio_str(CPUState *cpu, hv_x64_io_port_intercept_message *info) +{ + uint8_t access_type = info->header.intercept_access_type; + uint16_t port = info->port_number; + bool repop = info->access_info.rep_prefix == 1; + size_t repeat = repop ? info->rcx : 1; + size_t insn_len = info->header.instruction_length; + bool direction_flag; + uint32_t reg_names[3]; + uint64_t reg_values[3]; + int ret; + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + + ret = fetch_guest_state(cpu); + if (ret < 0) { + error_report("Failed to fetch guest state"); + return -1; + } + + direction_flag = (env->eflags & DESC_E_MASK) != 0; + + if (access_type == HV_X64_INTERCEPT_ACCESS_TYPE_WRITE) { + ret = handle_pio_str_write(cpu, info, repeat, port, direction_flag); + if (ret < 0) { + error_report("Failed to handle pio str write"); + return -1; + } + reg_names[0] = HV_X64_REGISTER_RSI; + reg_values[0] = info->rsi; + } else { + ret = handle_pio_str_read(cpu, info, repeat, port, direction_flag); + reg_names[0] = HV_X64_REGISTER_RDI; + reg_values[0] = info->rdi; + } + + reg_names[1] = HV_X64_REGISTER_RIP; + reg_values[1] = info->header.rip + insn_len; + reg_names[2] = HV_X64_REGISTER_RAX; + reg_values[2] = info->rax; + + ret = set_x64_registers(cpu, reg_names, reg_values); + if (ret < 0) { + error_report("Failed to set x64 registers"); + return -1; + } + + cpu->accel->dirty = false; + + return 0; +} + +static int handle_pio(CPUState *cpu, const struct hyperv_message *msg) +{ + struct hv_x64_io_port_intercept_message info = { 0 }; + int ret; + + ret = set_ioport_info(msg, &info); + if (ret < 0) { + error_report("Failed to convert message to ioport info"); + return -1; + } + + if (info.access_info.string_op) { + return handle_pio_str(cpu, &info); + } + + return handle_pio_non_str(cpu, &info); +} + int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit) { - error_report("unimplemented"); - abort(); + int ret; + enum MshvVmExit exit_reason; + int cpu_fd = mshv_vcpufd(cpu); + + ret = ioctl(cpu_fd, MSHV_RUN_VP, msg); + if (ret < 0) { + return MshvVmExitShutdown; + } + + switch (msg->header.message_type) { + case HVMSG_UNRECOVERABLE_EXCEPTION: + return MshvVmExitShutdown; + case HVMSG_UNMAPPED_GPA: + case HVMSG_GPA_INTERCEPT: + ret = handle_mmio(cpu, msg, &exit_reason); + if (ret < 0) { + error_report("failed to handle mmio"); + return -1; + } + return exit_reason; + case HVMSG_X64_IO_PORT_INTERCEPT: + ret = handle_pio(cpu, msg); + if (ret < 0) { + return MshvVmExitSpecial; + } + return MshvVmExitIgnore; + default: + break; + } + + *exit = MshvVmExitIgnore; + return 0; } void mshv_remove_vcpu(int vm_fd, int cpu_fd) From efc4093358511a58846a409b965213aa1bb9f31a Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:43 +0200 Subject: [PATCH 1420/1794] accel/mshv: Handle overlapping mem mappings QEMU maps certain regions into the guest multiple times, as seen in the trace below. Currently the MSHV kernel driver will reject those mappings. To workaround this, a record is kept (a static global list of "slots", inspired by what the HVF accelerator has implemented). An overlapping region is not registered at the hypervisor, and marked as mapped=false. If there is an UNMAPPED_GPA exit, we can look for a slot that is unmapped and would cover the GPA. In this case we map out the conflicting slot and map in the requested region. mshv_set_phys_mem add=1 name=pc.bios mshv_map_memory => u_a=7ffff4e00000 gpa=00fffc0000 size=00040000 mshv_set_phys_mem add=1 name=ioapic mshv_set_phys_mem add=1 name=hpet mshv_set_phys_mem add=0 name=pc.ram mshv_unmap_memory u_a=7fff67e00000 gpa=0000000000 size=80000000 mshv_set_phys_mem add=1 name=pc.ram mshv_map_memory u_a=7fff67e00000 gpa=0000000000 size=000c0000 mshv_set_phys_mem add=1 name=pc.rom mshv_map_memory u_a=7ffff4c00000 gpa=00000c0000 size=00020000 mshv_set_phys_mem add=1 name=pc.bios mshv_remap_attempt => u_a=7ffff4e20000 gpa=00000e0000 size=00020000 The mapping table is guarded by a mutex for concurrent modification and RCU mechanisms for concurrent reads. Writes occur rarely, but we'll have to verify whether an unmapped region exist for each UNMAPPED_GPA exit, which happens frequently. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-24-magnuskulke@linux.microsoft.com [Fix format strings for trace-events; mshv.h/mshv_int.h split. - Paolo] Signed-off-by: Paolo Bonzini --- accel/mshv/mem.c | 406 +++++++++++++++++++++++++++++++++--- accel/mshv/mshv-all.c | 2 + accel/mshv/trace-events | 5 + include/system/mshv_int.h | 22 +- target/i386/mshv/mshv-cpu.c | 43 ++++ 5 files changed, 448 insertions(+), 30 deletions(-) diff --git a/accel/mshv/mem.c b/accel/mshv/mem.c index e55c38d4db9f1..0e2164af3ee3a 100644 --- a/accel/mshv/mem.c +++ b/accel/mshv/mem.c @@ -11,7 +11,9 @@ */ #include "qemu/osdep.h" +#include "qemu/lockable.h" #include "qemu/error-report.h" +#include "qemu/rcu.h" #include "linux/mshv.h" #include "system/address-spaces.h" #include "system/mshv.h" @@ -20,6 +22,137 @@ #include #include "trace.h" +typedef struct SlotsRCUReclaim { + struct rcu_head rcu; + GList *old_head; + MshvMemorySlot *removed_slot; +} SlotsRCUReclaim; + +static void rcu_reclaim_slotlist(struct rcu_head *rcu) +{ + SlotsRCUReclaim *r = container_of(rcu, SlotsRCUReclaim, rcu); + g_list_free(r->old_head); + g_free(r->removed_slot); + g_free(r); +} + +static void publish_slots(GList *new_head, GList *old_head, + MshvMemorySlot *removed_slot) +{ + MshvMemorySlotManager *manager = &mshv_state->msm; + + assert(manager); + qatomic_store_release(&manager->slots, new_head); + + SlotsRCUReclaim *r = g_new(SlotsRCUReclaim, 1); + r->old_head = old_head; + r->removed_slot = removed_slot; + + call_rcu1(&r->rcu, rcu_reclaim_slotlist); +} + +/* Needs to be called with mshv_state->msm.mutex held */ +static int remove_slot(MshvMemorySlot *slot) +{ + GList *old_head, *new_head; + MshvMemorySlotManager *manager = &mshv_state->msm; + + assert(manager); + old_head = qatomic_load_acquire(&manager->slots); + + if (!g_list_find(old_head, slot)) { + error_report("slot requested for removal not found"); + return -1; + } + + new_head = g_list_copy(old_head); + new_head = g_list_remove(new_head, slot); + manager->n_slots--; + + publish_slots(new_head, old_head, slot); + + return 0; +} + +/* Needs to be called with mshv_state->msm.mutex held */ +static MshvMemorySlot *append_slot(uint64_t gpa, uint64_t userspace_addr, + uint64_t size, bool readonly) +{ + GList *old_head, *new_head; + MshvMemorySlot *slot; + MshvMemorySlotManager *manager = &mshv_state->msm; + + assert(manager); + + old_head = qatomic_load_acquire(&manager->slots); + + if (manager->n_slots >= MSHV_MAX_MEM_SLOTS) { + error_report("no free memory slots available"); + return NULL; + } + + slot = g_new0(MshvMemorySlot, 1); + slot->guest_phys_addr = gpa; + slot->userspace_addr = userspace_addr; + slot->memory_size = size; + slot->readonly = readonly; + + new_head = g_list_copy(old_head); + new_head = g_list_append(new_head, slot); + manager->n_slots++; + + publish_slots(new_head, old_head, NULL); + + return slot; +} + +static int slot_overlaps(const MshvMemorySlot *slot1, + const MshvMemorySlot *slot2) +{ + uint64_t start_1 = slot1->userspace_addr, + start_2 = slot2->userspace_addr; + size_t len_1 = slot1->memory_size, + len_2 = slot2->memory_size; + + if (slot1 == slot2) { + return -1; + } + + return ranges_overlap(start_1, len_1, start_2, len_2) ? 0 : -1; +} + +static bool is_mapped(MshvMemorySlot *slot) +{ + /* Subsequent reads of mapped field see a fully-initialized slot */ + return qatomic_load_acquire(&slot->mapped); +} + +/* + * Find slot that is: + * - overlapping in userspace + * - currently mapped in the guest + * + * Needs to be called with mshv_state->msm.mutex or RCU read lock held. + */ +static MshvMemorySlot *find_overlap_mem_slot(GList *head, MshvMemorySlot *slot) +{ + GList *found; + MshvMemorySlot *overlap_slot; + + found = g_list_find_custom(head, slot, (GCompareFunc) slot_overlaps); + + if (!found) { + return NULL; + } + + overlap_slot = found->data; + if (!overlap_slot || !is_mapped(overlap_slot)) { + return NULL; + } + + return overlap_slot; +} + static int set_guest_memory(int vm_fd, const struct mshv_user_mem_region *region) { @@ -27,38 +160,169 @@ static int set_guest_memory(int vm_fd, ret = ioctl(vm_fd, MSHV_SET_GUEST_MEMORY, region); if (ret < 0) { - error_report("failed to set guest memory"); - return -errno; + error_report("failed to set guest memory: %s", strerror(errno)); + return -1; } return 0; } -static int map_or_unmap(int vm_fd, const MshvMemoryRegion *mr, bool map) +static int map_or_unmap(int vm_fd, const MshvMemorySlot *slot, bool map) { struct mshv_user_mem_region region = {0}; - region.guest_pfn = mr->guest_phys_addr >> MSHV_PAGE_SHIFT; - region.size = mr->memory_size; - region.userspace_addr = mr->userspace_addr; + region.guest_pfn = slot->guest_phys_addr >> MSHV_PAGE_SHIFT; + region.size = slot->memory_size; + region.userspace_addr = slot->userspace_addr; if (!map) { region.flags |= (1 << MSHV_SET_MEM_BIT_UNMAP); - trace_mshv_unmap_memory(mr->userspace_addr, mr->guest_phys_addr, - mr->memory_size); + trace_mshv_unmap_memory(slot->userspace_addr, slot->guest_phys_addr, + slot->memory_size); return set_guest_memory(vm_fd, ®ion); } region.flags = BIT(MSHV_SET_MEM_BIT_EXECUTABLE); - if (!mr->readonly) { + if (!slot->readonly) { region.flags |= BIT(MSHV_SET_MEM_BIT_WRITABLE); } - trace_mshv_map_memory(mr->userspace_addr, mr->guest_phys_addr, - mr->memory_size); + trace_mshv_map_memory(slot->userspace_addr, slot->guest_phys_addr, + slot->memory_size); return set_guest_memory(vm_fd, ®ion); } +static int slot_matches_region(const MshvMemorySlot *slot1, + const MshvMemorySlot *slot2) +{ + return (slot1->guest_phys_addr == slot2->guest_phys_addr && + slot1->userspace_addr == slot2->userspace_addr && + slot1->memory_size == slot2->memory_size) ? 0 : -1; +} + +/* Needs to be called with mshv_state->msm.mutex held */ +static MshvMemorySlot *find_mem_slot_by_region(uint64_t gpa, uint64_t size, + uint64_t userspace_addr) +{ + MshvMemorySlot ref_slot = { + .guest_phys_addr = gpa, + .userspace_addr = userspace_addr, + .memory_size = size, + }; + GList *found; + MshvMemorySlotManager *manager = &mshv_state->msm; + + assert(manager); + found = g_list_find_custom(manager->slots, &ref_slot, + (GCompareFunc) slot_matches_region); + + return found ? found->data : NULL; +} + +static int slot_covers_gpa(const MshvMemorySlot *slot, uint64_t *gpa_p) +{ + uint64_t gpa_offset, gpa = *gpa_p; + + gpa_offset = gpa - slot->guest_phys_addr; + return (slot->guest_phys_addr <= gpa && gpa_offset < slot->memory_size) + ? 0 : -1; +} + +/* Needs to be called with mshv_state->msm.mutex or RCU read lock held */ +static MshvMemorySlot *find_mem_slot_by_gpa(GList *head, uint64_t gpa) +{ + GList *found; + MshvMemorySlot *slot; + + trace_mshv_find_slot_by_gpa(gpa); + + found = g_list_find_custom(head, &gpa, (GCompareFunc) slot_covers_gpa); + if (found) { + slot = found->data; + trace_mshv_found_slot(slot->userspace_addr, slot->guest_phys_addr, + slot->memory_size); + return slot; + } + + return NULL; +} + +/* Needs to be called with mshv_state->msm.mutex held */ +static void set_mapped(MshvMemorySlot *slot, bool mapped) +{ + /* prior writes to mapped field becomes visible before readers see slot */ + qatomic_store_release(&slot->mapped, mapped); +} + +MshvRemapResult mshv_remap_overlap_region(int vm_fd, uint64_t gpa) +{ + MshvMemorySlot *gpa_slot, *overlap_slot; + GList *head; + int ret; + MshvMemorySlotManager *manager = &mshv_state->msm; + + /* fast path, called often by unmapped_gpa vm exit */ + WITH_RCU_READ_LOCK_GUARD() { + assert(manager); + head = qatomic_load_acquire(&manager->slots); + /* return early if no slot is found */ + gpa_slot = find_mem_slot_by_gpa(head, gpa); + if (gpa_slot == NULL) { + return MshvRemapNoMapping; + } + + /* return early if no overlapping slot is found */ + overlap_slot = find_overlap_mem_slot(head, gpa_slot); + if (overlap_slot == NULL) { + return MshvRemapNoOverlap; + } + } + + /* + * We'll modify the mapping list, so we need to upgrade to mutex and + * recheck. + */ + assert(manager); + QEMU_LOCK_GUARD(&manager->mutex); + + /* return early if no slot is found */ + gpa_slot = find_mem_slot_by_gpa(manager->slots, gpa); + if (gpa_slot == NULL) { + return MshvRemapNoMapping; + } + + /* return early if no overlapping slot is found */ + overlap_slot = find_overlap_mem_slot(manager->slots, gpa_slot); + if (overlap_slot == NULL) { + return MshvRemapNoOverlap; + } + + /* unmap overlapping slot */ + ret = map_or_unmap(vm_fd, overlap_slot, false); + if (ret < 0) { + error_report("failed to unmap overlap region"); + abort(); + } + set_mapped(overlap_slot, false); + warn_report("mapped out userspace_addr=0x%016lx gpa=0x%010lx size=0x%lx", + overlap_slot->userspace_addr, + overlap_slot->guest_phys_addr, + overlap_slot->memory_size); + + /* map region for gpa */ + ret = map_or_unmap(vm_fd, gpa_slot, true); + if (ret < 0) { + error_report("failed to map new region"); + abort(); + } + set_mapped(gpa_slot, true); + warn_report("mapped in userspace_addr=0x%016lx gpa=0x%010lx size=0x%lx", + gpa_slot->userspace_addr, gpa_slot->guest_phys_addr, + gpa_slot->memory_size); + + return MshvRemapOk; +} + static int handle_unmapped_mmio_region_read(uint64_t gpa, uint64_t size, uint8_t *data) { @@ -124,20 +388,97 @@ int mshv_guest_mem_write(uint64_t gpa, const uint8_t *data, uintptr_t size, return -1; } -static int set_memory(const MshvMemoryRegion *mshv_mr, bool add) +static int tracked_unmap(int vm_fd, uint64_t gpa, uint64_t size, + uint64_t userspace_addr) { - int ret = 0; + int ret; + MshvMemorySlot *slot; + MshvMemorySlotManager *manager = &mshv_state->msm; + + assert(manager); + + QEMU_LOCK_GUARD(&manager->mutex); + + slot = find_mem_slot_by_region(gpa, size, userspace_addr); + if (!slot) { + trace_mshv_skip_unset_mem(userspace_addr, gpa, size); + /* no work to do */ + return 0; + } + + if (!is_mapped(slot)) { + /* remove slot, no need to unmap */ + return remove_slot(slot); + } - if (!mshv_mr) { - error_report("Invalid mshv_mr"); + ret = map_or_unmap(vm_fd, slot, false); + if (ret < 0) { + error_report("failed to unmap memory region"); + return ret; + } + return remove_slot(slot); +} + +static int tracked_map(int vm_fd, uint64_t gpa, uint64_t size, bool readonly, + uint64_t userspace_addr) +{ + MshvMemorySlot *slot, *overlap_slot; + int ret; + MshvMemorySlotManager *manager = &mshv_state->msm; + + assert(manager); + + QEMU_LOCK_GUARD(&manager->mutex); + + slot = find_mem_slot_by_region(gpa, size, userspace_addr); + if (slot) { + error_report("memory region already mapped at gpa=0x%lx, " + "userspace_addr=0x%lx, size=0x%lx", + slot->guest_phys_addr, slot->userspace_addr, + slot->memory_size); return -1; } - trace_mshv_set_memory(add, mshv_mr->guest_phys_addr, - mshv_mr->memory_size, - mshv_mr->userspace_addr, mshv_mr->readonly, - ret); - return map_or_unmap(mshv_state->vm, mshv_mr, add); + slot = append_slot(gpa, userspace_addr, size, readonly); + + overlap_slot = find_overlap_mem_slot(manager->slots, slot); + if (overlap_slot) { + trace_mshv_remap_attempt(slot->userspace_addr, + slot->guest_phys_addr, + slot->memory_size); + warn_report("attempt to map region [0x%lx-0x%lx], while " + "[0x%lx-0x%lx] is already mapped in the guest", + userspace_addr, userspace_addr + size - 1, + overlap_slot->userspace_addr, + overlap_slot->userspace_addr + + overlap_slot->memory_size - 1); + + /* do not register mem slot in hv, but record for later swap-in */ + set_mapped(slot, false); + + return 0; + } + + ret = map_or_unmap(vm_fd, slot, true); + if (ret < 0) { + error_report("failed to map memory region"); + return -1; + } + set_mapped(slot, true); + + return 0; +} + +static int set_memory(uint64_t gpa, uint64_t size, bool readonly, + uint64_t userspace_addr, bool add) +{ + int vm_fd = mshv_state->vm; + + if (add) { + return tracked_map(vm_fd, gpa, size, readonly, userspace_addr); + } + + return tracked_unmap(vm_fd, gpa, size, userspace_addr); } /* @@ -173,7 +514,9 @@ void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section, bool writable = !area->readonly && !area->rom_device; hwaddr start_addr, mr_offset, size; void *ram; - MshvMemoryRegion mshv_mr = {0}; + + size = align_section(section, &start_addr); + trace_mshv_set_phys_mem(add, section->mr->name, start_addr); size = align_section(section, &start_addr); trace_mshv_set_phys_mem(add, section->mr->name, start_addr); @@ -200,14 +543,21 @@ void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section, ram = memory_region_get_ram_ptr(area) + mr_offset; - mshv_mr.guest_phys_addr = start_addr; - mshv_mr.memory_size = size; - mshv_mr.readonly = !writable; - mshv_mr.userspace_addr = (uint64_t)ram; - - ret = set_memory(&mshv_mr, add); + ret = set_memory(start_addr, size, !writable, (uint64_t)ram, add); if (ret < 0) { - error_report("Failed to set memory region"); + error_report("failed to set memory region"); abort(); } } + +void mshv_init_memory_slot_manager(MshvState *mshv_state) +{ + MshvMemorySlotManager *manager; + + assert(mshv_state); + manager = &mshv_state->msm; + + manager->n_slots = 0; + manager->slots = NULL; + qemu_mutex_init(&manager->mutex); +} diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c index fa1f8f35bdb43..5edfcbad9d2e8 100644 --- a/accel/mshv/mshv-all.c +++ b/accel/mshv/mshv-all.c @@ -437,6 +437,8 @@ static int mshv_init(AccelState *as, MachineState *ms) mshv_init_msicontrol(); + mshv_init_memory_slot_manager(s); + ret = create_vm(mshv_fd, &vm_fd); if (ret < 0) { close(mshv_fd); diff --git a/accel/mshv/trace-events b/accel/mshv/trace-events index a4dffeb24a327..36f0d59b38556 100644 --- a/accel/mshv/trace-events +++ b/accel/mshv/trace-events @@ -26,3 +26,8 @@ mshv_map_memory(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x% mshv_unmap_memory(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64 mshv_set_phys_mem(bool add, const char *name, uint64_t gpa) "\tadd=%d name=%s gpa=0x%010" PRIx64 mshv_handle_mmio(uint64_t gva, uint64_t gpa, uint64_t size, uint8_t access_type) "\tgva=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%" PRIx64 " access_type=%d" + +mshv_found_slot(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64 +mshv_skip_unset_mem(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64 +mshv_remap_attempt(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64 +mshv_find_slot_by_gpa(uint64_t gpa) "\tgpa=0x%010" PRIx64 diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index b29d39911dbab..6350c69e9d34e 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -16,6 +16,8 @@ #define MSHV_MSR_ENTRIES_COUNT 64 +#define MSHV_MAX_MEM_SLOTS 32 + typedef struct hyperv_message hv_message; struct AccelCPUState { @@ -33,6 +35,12 @@ typedef struct MshvAddressSpace { AddressSpace *as; } MshvAddressSpace; +typedef struct MshvMemorySlotManager { + size_t n_slots; + GList *slots; + QemuMutex mutex; +} MshvMemorySlotManager; + struct MshvState { AccelState parent_obj; int vm; @@ -41,6 +49,7 @@ struct MshvState { int nr_as; MshvAddressSpace *as; int fd; + MshvMemorySlotManager msm; }; typedef struct MshvMsiControl { @@ -71,6 +80,12 @@ typedef enum MshvVmExit { MshvVmExitSpecial = 2, } MshvVmExit; +typedef enum MshvRemapResult { + MshvRemapOk = 0, + MshvRemapNoMapping = 1, + MshvRemapNoOverlap = 2, +} MshvRemapResult; + void mshv_init_mmio_emu(void); int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd); void mshv_remove_vcpu(int vm_fd, int cpu_fd); @@ -94,19 +109,22 @@ int mshv_hvcall(int fd, const struct mshv_root_hvcall *args); #endif /* memory */ -typedef struct MshvMemoryRegion { +typedef struct MshvMemorySlot { uint64_t guest_phys_addr; uint64_t memory_size; uint64_t userspace_addr; bool readonly; -} MshvMemoryRegion; + bool mapped; +} MshvMemorySlot; +MshvRemapResult mshv_remap_overlap_region(int vm_fd, uint64_t gpa); int mshv_guest_mem_read(uint64_t gpa, uint8_t *data, uintptr_t size, bool is_secure_mode, bool instruction_fetch); int mshv_guest_mem_write(uint64_t gpa, const uint8_t *data, uintptr_t size, bool is_secure_mode); void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section, bool add); +void mshv_init_memory_slot_manager(MshvState *mshv_state); /* msr */ typedef struct MshvMsrEntry { diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 7edc032cea3d3..de87142bffcb1 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -1170,6 +1170,43 @@ static int handle_mmio(CPUState *cpu, const struct hyperv_message *msg, return 0; } +static int handle_unmapped_mem(int vm_fd, CPUState *cpu, + const struct hyperv_message *msg, + MshvVmExit *exit_reason) +{ + struct hv_x64_memory_intercept_message info = { 0 }; + uint64_t gpa; + int ret; + enum MshvRemapResult remap_result; + + ret = set_memory_info(msg, &info); + if (ret < 0) { + error_report("failed to convert message to memory info"); + return -1; + } + + gpa = info.guest_physical_address; + + /* attempt to remap the region, in case of overlapping userspace mappings */ + remap_result = mshv_remap_overlap_region(vm_fd, gpa); + *exit_reason = MshvVmExitIgnore; + + switch (remap_result) { + case MshvRemapNoMapping: + /* if we didn't find a mapping, it is probably mmio */ + return handle_mmio(cpu, msg, exit_reason); + case MshvRemapOk: + break; + case MshvRemapNoOverlap: + /* This should not happen, but we are forgiving it */ + warn_report("found no overlap for unmapped region"); + *exit_reason = MshvVmExitSpecial; + break; + } + + return 0; +} + static int set_ioport_info(const struct hyperv_message *msg, hv_x64_io_port_intercept_message *info) { @@ -1507,6 +1544,12 @@ int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit) case HVMSG_UNRECOVERABLE_EXCEPTION: return MshvVmExitShutdown; case HVMSG_UNMAPPED_GPA: + ret = handle_unmapped_mem(vm_fd, cpu, msg, &exit_reason); + if (ret < 0) { + error_report("failed to handle unmapped memory"); + return -1; + } + return exit_reason; case HVMSG_GPA_INTERCEPT: ret = handle_mmio(cpu, msg, &exit_reason); if (ret < 0) { From e7b08dfb902430b4f8226d23d7cf9b2762b6fc83 Mon Sep 17 00:00:00 2001 From: Praveen K Paladugu Date: Tue, 16 Sep 2025 18:48:44 +0200 Subject: [PATCH 1421/1794] qapi/accel: Allow to query mshv capabilities Allow to query mshv capabilities via query-mshv QMP and info mshv HMP commands. Signed-off-by: Magnus Kulke Acked-by: Dr. David Alan Gilbert Link: https://lore.kernel.org/r/20250916164847.77883-25-magnuskulke@linux.microsoft.com [Fix "since" version. - Paolo] Signed-off-by: Paolo Bonzini --- hmp-commands-info.hx | 13 +++++++++++++ hw/core/machine-hmp-cmds.c | 15 +++++++++++++++ hw/core/machine-qmp-cmds.c | 14 ++++++++++++++ include/monitor/hmp.h | 1 + include/system/hw_accel.h | 1 + qapi/accelerator.json | 29 +++++++++++++++++++++++++++++ 6 files changed, 73 insertions(+) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 6142f60e7b164..eaaa880c1b303 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -307,6 +307,19 @@ SRST Show KVM information. ERST + { + .name = "mshv", + .args_type = "", + .params = "", + .help = "show MSHV information", + .cmd = hmp_info_mshv, + }, + +SRST + ``info mshv`` + Show MSHV information. +ERST + { .name = "numa", .args_type = "", diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index 3a612e2232d90..682ed9f49b836 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -163,6 +163,21 @@ void hmp_info_kvm(Monitor *mon, const QDict *qdict) qapi_free_KvmInfo(info); } +void hmp_info_mshv(Monitor *mon, const QDict *qdict) +{ + MshvInfo *info; + + info = qmp_query_mshv(NULL); + monitor_printf(mon, "mshv support: "); + if (info->present) { + monitor_printf(mon, "%s\n", info->enabled ? "enabled" : "disabled"); + } else { + monitor_printf(mon, "not compiled\n"); + } + + qapi_free_MshvInfo(info); +} + void hmp_info_uuid(Monitor *mon, const QDict *qdict) { UuidInfo *info; diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index 6aca1a626e60e..e24bf0d97bfc5 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -28,6 +28,20 @@ #include "system/runstate.h" #include "system/system.h" #include "hw/s390x/storage-keys.h" +#include + +/* + * QMP query for MSHV + */ +MshvInfo *qmp_query_mshv(Error **errp) +{ + MshvInfo *info = g_malloc0(sizeof(*info)); + + info->enabled = mshv_enabled(); + info->present = accel_find("mshv"); + + return info; +} /* * fast means: we NEVER interrupt vCPU threads to retrieve diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index ae116d9804a3e..31bd812e5f418 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -24,6 +24,7 @@ strList *hmp_split_at_comma(const char *str); void hmp_info_name(Monitor *mon, const QDict *qdict); void hmp_info_version(Monitor *mon, const QDict *qdict); void hmp_info_kvm(Monitor *mon, const QDict *qdict); +void hmp_info_mshv(Monitor *mon, const QDict *qdict); void hmp_info_status(Monitor *mon, const QDict *qdict); void hmp_info_uuid(Monitor *mon, const QDict *qdict); void hmp_info_chardev(Monitor *mon, const QDict *qdict); diff --git a/include/system/hw_accel.h b/include/system/hw_accel.h index fa9228d5d2dca..55497edc2936d 100644 --- a/include/system/hw_accel.h +++ b/include/system/hw_accel.h @@ -14,6 +14,7 @@ #include "hw/core/cpu.h" #include "system/kvm.h" #include "system/hvf.h" +#include "system/mshv.h" #include "system/whpx.h" #include "system/nvmm.h" diff --git a/qapi/accelerator.json b/qapi/accelerator.json index fb28c8d920aec..664e02724655a 100644 --- a/qapi/accelerator.json +++ b/qapi/accelerator.json @@ -54,3 +54,32 @@ { 'command': 'x-accel-stats', 'returns': 'HumanReadableText', 'features': [ 'unstable' ] } + +## +# @MshvInfo: +# +# Information about support for MSHV acceleration +# +# @enabled: true if MSHV acceleration is active +# +# @present: true if MSHV acceleration is built into this executable +# +# Since: 10.2.0 +## +{ 'struct': 'MshvInfo', 'data': {'enabled': 'bool', 'present': 'bool'} } + +## +# @query-mshv: +# +# Return information about MSHV acceleration +# +# Returns: @MshvInfo +# +# Since: 10.0.92 +# +# .. qmp-example:: +# +# -> { "execute": "query-mshv" } +# <- { "return": { "enabled": true, "present": true } } +## +{ 'command': 'query-mshv', 'returns': 'MshvInfo' } From e4a20afce59937073298d716cc829bdc026542dc Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Thu, 2 Oct 2025 09:50:12 +0200 Subject: [PATCH 1422/1794] target/i386/mshv: Use preallocated page for hvcall There are hvcalls that are invoked during MMIO exits, the payload is of dynamic size. To avoid heap allocations we can use preallocated pages as in/out buffer for those calls. A page is reserved per vCPU and used for set/get register hv calls. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-26-magnuskulke@linux.microsoft.com [Use standard MAX_CONST macro; mshv.h/mshv_int.h split. - Paolo] Signed-off-by: Paolo Bonzini --- accel/mshv/mshv-all.c | 2 +- include/system/mshv_int.h | 7 +++++++ target/i386/mshv/mshv-cpu.c | 38 +++++++++++++++++++++++++------------ 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c index 5edfcbad9d2e8..45174f7c4eba0 100644 --- a/accel/mshv/mshv-all.c +++ b/accel/mshv/mshv-all.c @@ -399,8 +399,8 @@ static int mshv_init_vcpu(CPUState *cpu) uint8_t vp_index = cpu->cpu_index; int ret; - mshv_arch_init_vcpu(cpu); cpu->accel = g_new0(AccelCPUState, 1); + mshv_arch_init_vcpu(cpu); ret = mshv_create_vcpu(vm_fd, vp_index, &cpu->accel->cpufd); if (ret < 0) { diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index 6350c69e9d34e..490563c1ab294 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -20,9 +20,16 @@ typedef struct hyperv_message hv_message; +typedef struct MshvHvCallArgs { + void *base; + void *input_page; + void *output_page; +} MshvHvCallArgs; + struct AccelCPUState { int cpufd; bool dirty; + MshvHvCallArgs hvcall_args; }; typedef struct MshvMemoryListener { diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index de87142bffcb1..1f7b9cb37ec23 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -34,6 +34,10 @@ #include +#define MAX_REGISTER_COUNT (MAX_CONST(ARRAY_SIZE(STANDARD_REGISTER_NAMES), \ + MAX_CONST(ARRAY_SIZE(SPECIAL_REGISTER_NAMES), \ + ARRAY_SIZE(FPU_REGISTER_NAMES)))) + static enum hv_register_name STANDARD_REGISTER_NAMES[18] = { HV_X64_REGISTER_RAX, HV_X64_REGISTER_RBX, @@ -151,7 +155,7 @@ int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs, int cpu_fd = mshv_vcpufd(cpu); int vp_index = cpu->cpu_index; size_t in_sz, assocs_sz; - hv_input_set_vp_registers *in; + hv_input_set_vp_registers *in = cpu->accel->hvcall_args.input_page; struct mshv_root_hvcall args = {0}; int ret; @@ -160,7 +164,7 @@ int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs, in_sz = sizeof(hv_input_set_vp_registers) + assocs_sz; /* fill the input struct */ - in = g_malloc0(in_sz); + memset(in, 0, sizeof(hv_input_set_vp_registers)); in->vp_index = vp_index; memcpy(in->elements, assocs, assocs_sz); @@ -172,7 +176,6 @@ int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs, /* perform the call */ ret = mshv_hvcall(cpu_fd, &args); - g_free(in); if (ret < 0) { error_report("Failed to set registers"); return -1; @@ -193,8 +196,8 @@ static int get_generic_regs(CPUState *cpu, hv_register_assoc *assocs, { int cpu_fd = mshv_vcpufd(cpu); int vp_index = cpu->cpu_index; - hv_input_get_vp_registers *in; - hv_register_value *values; + hv_input_get_vp_registers *in = cpu->accel->hvcall_args.input_page; + hv_register_value *values = cpu->accel->hvcall_args.output_page; size_t in_sz, names_sz, values_sz; int i, ret; struct mshv_root_hvcall args = {0}; @@ -204,15 +207,14 @@ static int get_generic_regs(CPUState *cpu, hv_register_assoc *assocs, in_sz = sizeof(hv_input_get_vp_registers) + names_sz; /* fill the input struct */ - in = g_malloc0(in_sz); + memset(in, 0, sizeof(hv_input_get_vp_registers)); in->vp_index = vp_index; for (i = 0; i < n_regs; i++) { in->names[i] = assocs[i].name; } - /* allocate value output buffer */ + /* determine size of value output buffer */ values_sz = n_regs * sizeof(union hv_register_value); - values = g_malloc0(values_sz); /* create the hvcall envelope */ args.code = HVCALL_GET_VP_REGISTERS; @@ -224,16 +226,13 @@ static int get_generic_regs(CPUState *cpu, hv_register_assoc *assocs, /* perform the call */ ret = mshv_hvcall(cpu_fd, &args); - g_free(in); if (ret < 0) { - g_free(values); error_report("Failed to retrieve registers"); return -1; } /* assert we got all registers */ if (args.reps != n_regs) { - g_free(values); error_report("Failed to retrieve registers: expected %zu elements" ", got %u", n_regs, args.reps); return -1; @@ -243,7 +242,6 @@ static int get_generic_regs(CPUState *cpu, hv_register_assoc *assocs, for (i = 0; i < n_regs; i++) { assocs[i].value = values[i]; } - g_free(values); return 0; } @@ -1696,6 +1694,19 @@ void mshv_arch_init_vcpu(CPUState *cpu) { X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; + AccelCPUState *state = cpu->accel; + size_t page = HV_HYP_PAGE_SIZE; + void *mem = qemu_memalign(page, 2 * page); + + /* sanity check, to make sure we don't overflow the page */ + QEMU_BUILD_BUG_ON((MAX_REGISTER_COUNT + * sizeof(hv_register_assoc) + + sizeof(hv_input_get_vp_registers) + > HV_HYP_PAGE_SIZE)); + + state->hvcall_args.base = mem; + state->hvcall_args.input_page = mem; + state->hvcall_args.output_page = (uint8_t *)mem + page; env->emu_mmio_buf = g_new(char, 4096); } @@ -1704,7 +1715,10 @@ void mshv_arch_destroy_vcpu(CPUState *cpu) { X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; + AccelCPUState *state = cpu->accel; + g_free(state->hvcall_args.base); + state->hvcall_args = (MshvHvCallArgs){0}; g_clear_pointer(&env->emu_mmio_buf, g_free); } From 3af71a1a6a7a497df7d3026239d6136b56e3d5ab Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:46 +0200 Subject: [PATCH 1423/1794] docs: Add mshv to documentation Added mshv to the list of accelerators in doc text. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-27-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- docs/about/build-platforms.rst | 2 +- docs/devel/codebase.rst | 2 +- docs/glossary.rst | 7 +++---- docs/system/introduction.rst | 3 +++ qemu-options.hx | 16 ++++++++-------- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index 798cb4631dfc1..fc2743658d4a8 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -53,7 +53,7 @@ Those hosts are officially supported, with various accelerators: * - SPARC - tcg * - x86 - - hvf (64 bit only), kvm, nvmm, tcg, whpx (64 bit only), xen + - hvf (64 bit only), mshv (64 bit only), kvm, nvmm, tcg, whpx (64 bit only), xen Other host architectures are not supported. It is possible to build QEMU system emulation on an unsupported host architecture using the configure diff --git a/docs/devel/codebase.rst b/docs/devel/codebase.rst index 2a3143787a6ce..69d88271178bf 100644 --- a/docs/devel/codebase.rst +++ b/docs/devel/codebase.rst @@ -48,7 +48,7 @@ yet, so sometimes the source code is all you have. * `accel `_: Infrastructure and architecture agnostic code related to the various `accelerators ` supported by QEMU - (TCG, KVM, hvf, whpx, xen, nvmm). + (TCG, KVM, hvf, whpx, xen, nvmm, mshv). Contains interfaces for operations that will be implemented per `target `_. * `audio `_: diff --git a/docs/glossary.rst b/docs/glossary.rst index 4fa044bfb6ee7..2857731bc4466 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -12,7 +12,7 @@ Accelerator A specific API used to accelerate execution of guest instructions. It can be hardware-based, through a virtualization API provided by the host OS (kvm, hvf, -whpx, ...), or software-based (tcg). See this description of `supported +whpx, mshv, ...), or software-based (tcg). See this description of `supported accelerators`. Board @@ -101,9 +101,8 @@ manage a virtual machine. QEMU is a virtualizer, that interacts with various hypervisors. In the context of QEMU, an hypervisor is an API, provided by the Host OS, -allowing to execute virtual machines. Linux implementation is KVM (and supports -Xen as well). For MacOS, it's HVF. Windows defines WHPX. And NetBSD provides -NVMM. +allowing to execute virtual machines. Linux provides a choice of KVM, Xen +or MSHV; MacOS provides HVF; Windows provides WHPX; NetBSD provides NVMM. .. _machine: diff --git a/docs/system/introduction.rst b/docs/system/introduction.rst index 4cd46b5b8f958..9c57523b6c288 100644 --- a/docs/system/introduction.rst +++ b/docs/system/introduction.rst @@ -23,6 +23,9 @@ Tiny Code Generator (TCG) capable of emulating many CPUs. * - Xen - Linux (as dom0) - Arm, x86 + * - MSHV + - Linux (as dom0) + - x86 * - Hypervisor Framework (hvf) - MacOS - x86 (64 bit only), Arm (64 bit only) diff --git a/qemu-options.hx b/qemu-options.hx index 075f4be2e3e67..56db4bf9e5efd 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -28,7 +28,7 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ "-machine [type=]name[,prop[=value][,...]]\n" " selects emulated machine ('-machine help' for list)\n" " property accel=accel1[:accel2[:...]] selects accelerator\n" - " supported accelerators are kvm, xen, hvf, nvmm, whpx or tcg (default: tcg)\n" + " supported accelerators are kvm, xen, hvf, nvmm, whpx, mshv or tcg (default: tcg)\n" " vmport=on|off|auto controls emulation of vmport (default: auto)\n" " dump-guest-core=on|off include guest memory in a core dump (default=on)\n" " mem-merge=on|off controls memory merge support (default: on)\n" @@ -66,10 +66,10 @@ SRST ``accel=accels1[:accels2[:...]]`` This is used to enable an accelerator. Depending on the target - architecture, kvm, xen, hvf, nvmm, whpx or tcg can be available. - By default, tcg is used. If there is more than one accelerator - specified, the next one is used if the previous one fails to - initialize. + architecture, kvm, xen, hvf, nvmm, whpx, mshv or tcg can be + available. By default, tcg is used. If there is more than one + accelerator specified, the next one is used if the previous one + fails to initialize. ``vmport=on|off|auto`` Enables emulation of VMWare IO port, for vmmouse etc. auto says @@ -226,7 +226,7 @@ ERST DEF("accel", HAS_ARG, QEMU_OPTION_accel, "-accel [accel=]accelerator[,prop[=value][,...]]\n" - " select accelerator (kvm, xen, hvf, nvmm, whpx or tcg; use 'help' for a list)\n" + " select accelerator (kvm, xen, hvf, nvmm, whpx, mshv or tcg; use 'help' for a list)\n" " igd-passthru=on|off (enable Xen integrated Intel graphics passthrough, default=off)\n" " kernel-irqchip=on|off|split controls accelerated irqchip support (default=on)\n" " kvm-shadow-mem=size of KVM shadow MMU in bytes\n" @@ -241,8 +241,8 @@ DEF("accel", HAS_ARG, QEMU_OPTION_accel, SRST ``-accel name[,prop=value[,...]]`` This is used to enable an accelerator. Depending on the target - architecture, kvm, xen, hvf, nvmm, whpx or tcg can be available. By - default, tcg is used. If there is more than one accelerator + architecture, kvm, xen, hvf, nvmm, whpx, mshv or tcg can be available. + By default, tcg is used. If there is more than one accelerator specified, the next one is used if the previous one fails to initialize. From 1872bd9f2dad5113b0c27d352ec49683e0953c7f Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 16 Sep 2025 18:48:47 +0200 Subject: [PATCH 1424/1794] MAINTAINERS: Add maintainers for mshv accelerator Adding Magnus Kulke and Wei Liu to the maintainers file for the respective folders/files. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250916164847.77883-28-magnuskulke@linux.microsoft.com [Rename "MAHV CPUs" to mention x86. - Paolo] Signed-off-by: Paolo Bonzini --- MAINTAINERS | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 406cef88f0ca8..645344c4faf17 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -551,6 +551,21 @@ F: target/i386/whpx/ F: accel/stubs/whpx-stub.c F: include/system/whpx.h +MSHV +M: Magnus Kulke +R: Wei Liu +S: Supported +F: accel/mshv/ +F: include/system/mshv.h +F: include/hw/hyperv/hvgdk*.h +F: include/hw/hyperv/hvhdk*.h + +X86 MSHV CPUs +M: Magnus Kulke +R: Wei Liu +S: Supported +F: target/i386/mshv/ + X86 Instruction Emulator M: Cameron Esfahani M: Roman Bolshakov From 1a583ca382f63be537977e76bf54a0670e57a2a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 7 Oct 2025 19:34:05 +0400 Subject: [PATCH 1425/1794] tests/docker: make --enable-rust overridable with EXTRA_CONFIGURE_OPTS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20251007153406.421032-1-marcandre.lureau@redhat.com Signed-off-by: Paolo Bonzini --- tests/docker/common.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker/common.rc b/tests/docker/common.rc index 752f4f3aed9a6..79d533ab2e500 100755 --- a/tests/docker/common.rc +++ b/tests/docker/common.rc @@ -53,8 +53,8 @@ configure_qemu() config_opts="--enable-werror \ ${TARGET_LIST:+--target-list=${TARGET_LIST}} \ --prefix=$INSTALL_DIR \ - $QEMU_CONFIGURE_OPTS $EXTRA_CONFIGURE_OPTS \ $enable_rust \ + $QEMU_CONFIGURE_OPTS $EXTRA_CONFIGURE_OPTS \ $@" echo "Configure options:" echo $config_opts From b9ef6198d709f431d893d1b5525cdf7fd7a3e11b Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 7 Oct 2025 15:44:27 -0400 Subject: [PATCH 1426/1794] rust: fix path to rust_root_crate.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generated Rust root crate source files contain the wrong path to the rust_root_crate.sh script. Signed-off-by: Stefan Hajnoczi Reviewed-by: Manos Pitsidianakis Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20251007194427.118871-1-stefanha@redhat.com Signed-off-by: Paolo Bonzini --- scripts/rust/rust_root_crate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/rust/rust_root_crate.sh b/scripts/rust/rust_root_crate.sh index 975bddf7f1a4c..f05b8d021081f 100755 --- a/scripts/rust/rust_root_crate.sh +++ b/scripts/rust/rust_root_crate.sh @@ -4,7 +4,7 @@ set -eu cat < Date: Tue, 23 Sep 2025 22:35:40 +0800 Subject: [PATCH 1427/1794] bios-tables-test-allowed-diff.h: Allow LoongArch DSDT.* Signed-off-by: Huacai Chen Reviewed-by: Message-ID: <20250923143542.2391576-2-chenhuacai@kernel.org> Signed-off-by: Song Gao --- tests/qtest/bios-tables-test-allowed-diff.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8bf45..2ed837faeec8f 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,5 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/loongarch64/virt/DSDT", +"tests/data/acpi/loongarch64/virt/DSDT.memhp", +"tests/data/acpi/loongarch64/virt/DSDT.numamem", +"tests/data/acpi/loongarch64/virt/DSDT.topology", From dd333bf0e3ab2096702844ae0d0cff8a8864e5f7 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Tue, 23 Sep 2025 22:35:41 +0800 Subject: [PATCH 1428/1794] hw/loongarch/virt: Align VIRT_GED_CPUHP_ADDR to 4 bytes Now VIRT_GED_CPUHP_ADDR is not aligned to 4 bytes, but if Linux kernel is built with ACPI_MISALIGNMENT_NOT_SUPPORTED, it assumes the alignment, otherwise we get ACPI errors at boot phase: ACPI Error: AE_AML_ALIGNMENT, Returned by Handler for [SystemMemory] (20250404/evregion-301) ACPI Error: Aborting method \_SB.CPUS.CSTA due to previous error (AE_AML_ALIGNMENT) (20250404/psparse-529) ACPI Error: Aborting method \_SB.CPUS.C000._STA due to previous error (AE_AML_ALIGNMENT) (20250404/psparse-529) ACPI Error: Method execution failed \_SB.CPUS.C000._STA due to previous error (AE_AML_ALIGNMENT) (20250404/uteval-68) VIRT_GED_MEM_ADDR and VIRT_GED_REG_ADDR are already aligned now, but use QEMU_ALIGN_UP() to explicitly align them can make code more robust. Reported-by: Nathan Chancellor Suggested-by: WANG Rui Signed-off-by: Huacai Chen Reviewed-by: Bibo Mao Message-ID: <20250923143542.2391576-3-chenhuacai@kernel.org> Signed-off-by: Song Gao --- include/hw/loongarch/virt.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index cd97bdfb8d45a..76fa57cd070cf 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -49,9 +49,9 @@ #define VIRT_LOWMEM_SIZE 0x10000000 #define VIRT_HIGHMEM_BASE 0x80000000 #define VIRT_GED_EVT_ADDR 0x100e0000 -#define VIRT_GED_MEM_ADDR (VIRT_GED_EVT_ADDR + ACPI_GED_EVT_SEL_LEN) -#define VIRT_GED_REG_ADDR (VIRT_GED_MEM_ADDR + MEMORY_HOTPLUG_IO_LEN) -#define VIRT_GED_CPUHP_ADDR (VIRT_GED_REG_ADDR + ACPI_GED_REG_COUNT) +#define VIRT_GED_MEM_ADDR QEMU_ALIGN_UP(VIRT_GED_EVT_ADDR + ACPI_GED_EVT_SEL_LEN, 4) +#define VIRT_GED_REG_ADDR QEMU_ALIGN_UP(VIRT_GED_MEM_ADDR + MEMORY_HOTPLUG_IO_LEN, 4) +#define VIRT_GED_CPUHP_ADDR QEMU_ALIGN_UP(VIRT_GED_REG_ADDR + ACPI_GED_REG_COUNT, 4) #define COMMAND_LINE_SIZE 512 From 68e8df8cfa4fcf053e306f24edeaea59d90b0b3d Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Tue, 23 Sep 2025 22:35:42 +0800 Subject: [PATCH 1429/1794] tests/data/acpi/loongarch64: Update expected DSDT.* DSDT diffs from "iasl -d": @@ -11,7 +11,7 @@ * Signature "DSDT" * Length 0x000011FB (4603) * Revision 0x01 **** 32-bit table (V1), no 64-bit math support - * Checksum 0x5D + * Checksum 0x5B * OEM ID "BOCHS " * OEM Table ID "BXPC " * OEM Revision 0x00000001 (1) @@ -1426,11 +1426,11 @@ Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { Memory32Fixed (ReadWrite, - 0x100E001F, // Address Base + 0x100E0020, // Address Base 0x0000000C, // Address Length ) }) - OperationRegion (PRST, SystemMemory, 0x100E001F, 0x0C) + OperationRegion (PRST, SystemMemory, 0x100E0020, 0x0C) Field (PRST, ByteAcc, NoLock, WriteAsZeros) { Offset (0x04), Signed-off-by: Huacai Chen Acked-by: Michael S. Tsirkin Message-ID: <20250923143542.2391576-4-chenhuacai@kernel.org> Signed-off-by: Song Gao --- tests/data/acpi/loongarch64/virt/DSDT | Bin 4603 -> 4603 bytes tests/data/acpi/loongarch64/virt/DSDT.memhp | Bin 5824 -> 5824 bytes tests/data/acpi/loongarch64/virt/DSDT.numamem | Bin 4609 -> 4609 bytes .../data/acpi/loongarch64/virt/DSDT.topology | Bin 4905 -> 4905 bytes tests/qtest/bios-tables-test-allowed-diff.h | 4 ---- 5 files changed, 4 deletions(-) diff --git a/tests/data/acpi/loongarch64/virt/DSDT b/tests/data/acpi/loongarch64/virt/DSDT index b31841aec6ed296f10ea1695a67ead38f45424d5..55aa34f988d6ef69293e91c5fe45bee0a02bc5f1 100644 GIT binary patch delta 44 zcmeyZ{9BpJCD2415AS3=9mF4ABh%LBSymJPMm1@=CD)02#3h APXGV_ delta 44 zcmeyZ{9BpJCD2415AS3=9mF4ABh%LBSymJPMm1@_MiW0PuGT Ag8%>k delta 44 zcmZ3fwo;ADCDk diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 2ed837faeec8f..dfb8523c8bf45 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,5 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/loongarch64/virt/DSDT", -"tests/data/acpi/loongarch64/virt/DSDT.memhp", -"tests/data/acpi/loongarch64/virt/DSDT.numamem", -"tests/data/acpi/loongarch64/virt/DSDT.topology", From 410dfbf620aef6361a4fdb40f7a7ee32bf5581b4 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 29 Sep 2025 11:53:36 +0800 Subject: [PATCH 1430/1794] target/loongarch: Move TCG specified functions to tcg_cpu.c New file target/loongarch/tcg/tcg_cpu.c is created, and move TCG specified functions to here from file target/loongarch/cpu.c It is only code movement and there is no any function change. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson Message-ID: <20250929035338.2320419-2-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/cpu.c | 262 +------------------------- target/loongarch/internals.h | 2 + target/loongarch/tcg/meson.build | 1 + target/loongarch/tcg/tcg_cpu.c | 266 +++++++++++++++++++++++++++ target/loongarch/tcg/tcg_loongarch.h | 1 + 5 files changed, 272 insertions(+), 260 deletions(-) create mode 100644 target/loongarch/tcg/tcg_cpu.c diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 993602fb8cb7d..245c6d3ab9dd2 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -28,11 +28,6 @@ #ifdef CONFIG_KVM #include #endif -#ifdef CONFIG_TCG -#include "accel/tcg/cpu-ldst.h" -#include "accel/tcg/cpu-ops.h" -#include "tcg/tcg.h" -#endif #include "tcg/tcg_loongarch.h" const char * const regnames[32] = { @@ -140,18 +135,8 @@ void loongarch_cpu_set_irq(void *opaque, int irq, int level) } } -static inline bool cpu_loongarch_hw_interrupts_enabled(CPULoongArchState *env) -{ - bool ret = 0; - - ret = (FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE) && - !(FIELD_EX64(env->CSR_DBG, CSR_DBG, DST))); - - return ret; -} - /* Check if there is pending and not masked out interrupt */ -static inline bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env) +bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env) { uint32_t pending; uint32_t status; @@ -163,217 +148,8 @@ static inline bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env) } #endif -#ifdef CONFIG_TCG -#ifndef CONFIG_USER_ONLY -static void loongarch_cpu_do_interrupt(CPUState *cs) -{ - CPULoongArchState *env = cpu_env(cs); - bool update_badinstr = 1; - int cause = -1; - bool tlbfill = FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR); - uint32_t vec_size = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, VS); - - if (cs->exception_index != EXCCODE_INT) { - qemu_log_mask(CPU_LOG_INT, - "%s enter: pc " TARGET_FMT_lx " ERA " TARGET_FMT_lx - " TLBRERA " TARGET_FMT_lx " exception: %d (%s)\n", - __func__, env->pc, env->CSR_ERA, env->CSR_TLBRERA, - cs->exception_index, - loongarch_exception_name(cs->exception_index)); - } - - switch (cs->exception_index) { - case EXCCODE_DBP: - env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DCL, 1); - env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, ECODE, 0xC); - goto set_DERA; - set_DERA: - env->CSR_DERA = env->pc; - env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DST, 1); - set_pc(env, env->CSR_EENTRY + 0x480); - break; - case EXCCODE_INT: - if (FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) { - env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DEI, 1); - goto set_DERA; - } - QEMU_FALLTHROUGH; - case EXCCODE_PIF: - case EXCCODE_ADEF: - cause = cs->exception_index; - update_badinstr = 0; - break; - case EXCCODE_SYS: - case EXCCODE_BRK: - case EXCCODE_INE: - case EXCCODE_IPE: - case EXCCODE_FPD: - case EXCCODE_FPE: - case EXCCODE_SXD: - case EXCCODE_ASXD: - env->CSR_BADV = env->pc; - QEMU_FALLTHROUGH; - case EXCCODE_BCE: - case EXCCODE_ADEM: - case EXCCODE_PIL: - case EXCCODE_PIS: - case EXCCODE_PME: - case EXCCODE_PNR: - case EXCCODE_PNX: - case EXCCODE_PPI: - cause = cs->exception_index; - break; - default: - qemu_log("Error: exception(%d) has not been supported\n", - cs->exception_index); - abort(); - } - - if (update_badinstr) { - env->CSR_BADI = cpu_ldl_code(env, env->pc); - } - - /* Save PLV and IE */ - if (tlbfill) { - env->CSR_TLBRPRMD = FIELD_DP64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV, - FIELD_EX64(env->CSR_CRMD, - CSR_CRMD, PLV)); - env->CSR_TLBRPRMD = FIELD_DP64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE, - FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE)); - /* set the DA mode */ - env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 1); - env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 0); - env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, - PC, (env->pc >> 2)); - } else { - env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, ECODE, - EXCODE_MCODE(cause)); - env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, ESUBCODE, - EXCODE_SUBCODE(cause)); - env->CSR_PRMD = FIELD_DP64(env->CSR_PRMD, CSR_PRMD, PPLV, - FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV)); - env->CSR_PRMD = FIELD_DP64(env->CSR_PRMD, CSR_PRMD, PIE, - FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE)); - env->CSR_ERA = env->pc; - } - - env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, 0); - env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, 0); - - if (vec_size) { - vec_size = (1 << vec_size) * 4; - } - - if (cs->exception_index == EXCCODE_INT) { - /* Interrupt */ - uint32_t vector = 0; - uint32_t pending = FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS); - pending &= FIELD_EX64(env->CSR_ECFG, CSR_ECFG, LIE); - - /* Find the highest-priority interrupt. */ - vector = 31 - clz32(pending); - set_pc(env, env->CSR_EENTRY + \ - (EXCCODE_EXTERNAL_INT + vector) * vec_size); - qemu_log_mask(CPU_LOG_INT, - "%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx - " cause %d\n" " A " TARGET_FMT_lx " D " - TARGET_FMT_lx " vector = %d ExC " TARGET_FMT_lx "ExS" - TARGET_FMT_lx "\n", - __func__, env->pc, env->CSR_ERA, - cause, env->CSR_BADV, env->CSR_DERA, vector, - env->CSR_ECFG, env->CSR_ESTAT); - } else { - if (tlbfill) { - set_pc(env, env->CSR_TLBRENTRY); - } else { - set_pc(env, env->CSR_EENTRY + EXCODE_MCODE(cause) * vec_size); - } - qemu_log_mask(CPU_LOG_INT, - "%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx - " cause %d%s\n, ESTAT " TARGET_FMT_lx - " EXCFG " TARGET_FMT_lx " BADVA " TARGET_FMT_lx - "BADI " TARGET_FMT_lx " SYS_NUM " TARGET_FMT_lu - " cpu %d asid " TARGET_FMT_lx "\n", __func__, env->pc, - tlbfill ? env->CSR_TLBRERA : env->CSR_ERA, - cause, tlbfill ? "(refill)" : "", env->CSR_ESTAT, - env->CSR_ECFG, - tlbfill ? env->CSR_TLBRBADV : env->CSR_BADV, - env->CSR_BADI, env->gpr[11], cs->cpu_index, - env->CSR_ASID); - } - cs->exception_index = -1; -} - -static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, - vaddr addr, unsigned size, - MMUAccessType access_type, - int mmu_idx, MemTxAttrs attrs, - MemTxResult response, - uintptr_t retaddr) -{ - CPULoongArchState *env = cpu_env(cs); - - if (access_type == MMU_INST_FETCH) { - do_raise_exception(env, EXCCODE_ADEF, retaddr); - } else { - do_raise_exception(env, EXCCODE_ADEM, retaddr); - } -} - -static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request) -{ - if (interrupt_request & CPU_INTERRUPT_HARD) { - CPULoongArchState *env = cpu_env(cs); - - if (cpu_loongarch_hw_interrupts_enabled(env) && - cpu_loongarch_hw_interrupts_pending(env)) { - /* Raise it */ - cs->exception_index = EXCCODE_INT; - loongarch_cpu_do_interrupt(cs); - return true; - } - } - return false; -} - -static vaddr loongarch_pointer_wrap(CPUState *cs, int mmu_idx, - vaddr result, vaddr base) -{ - return is_va32(cpu_env(cs)) ? (uint32_t)result : result; -} -#endif - -static TCGTBCPUState loongarch_get_tb_cpu_state(CPUState *cs) -{ - CPULoongArchState *env = cpu_env(cs); - uint32_t flags; - - flags = env->CSR_CRMD & (R_CSR_CRMD_PLV_MASK | R_CSR_CRMD_PG_MASK); - flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, FPE) * HW_FLAGS_EUEN_FPE; - flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE) * HW_FLAGS_EUEN_SXE; - flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE) * HW_FLAGS_EUEN_ASXE; - flags |= is_va32(env) * HW_FLAGS_VA32; - - return (TCGTBCPUState){ .pc = env->pc, .flags = flags }; -} - -static void loongarch_cpu_synchronize_from_tb(CPUState *cs, - const TranslationBlock *tb) -{ - tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); - set_pc(cpu_env(cs), tb->pc); -} - -static void loongarch_restore_state_to_opc(CPUState *cs, - const TranslationBlock *tb, - const uint64_t *data) -{ - set_pc(cpu_env(cs), data[0]); -} -#endif /* CONFIG_TCG */ - #ifndef CONFIG_USER_ONLY -static bool loongarch_cpu_has_work(CPUState *cs) +bool loongarch_cpu_has_work(CPUState *cs) { bool has_work = false; @@ -386,16 +162,6 @@ static bool loongarch_cpu_has_work(CPUState *cs) } #endif /* !CONFIG_USER_ONLY */ -static int loongarch_cpu_mmu_index(CPUState *cs, bool ifetch) -{ - CPULoongArchState *env = cpu_env(cs); - - if (FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG)) { - return FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV); - } - return MMU_DA_IDX; -} - static void loongarch_la464_init_csr(Object *obj) { #ifndef CONFIG_USER_ONLY @@ -911,30 +677,6 @@ static void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) } } -#ifdef CONFIG_TCG -static const TCGCPUOps loongarch_tcg_ops = { - .guest_default_memory_order = 0, - .mttcg_supported = true, - - .initialize = loongarch_translate_init, - .translate_code = loongarch_translate_code, - .get_tb_cpu_state = loongarch_get_tb_cpu_state, - .synchronize_from_tb = loongarch_cpu_synchronize_from_tb, - .restore_state_to_opc = loongarch_restore_state_to_opc, - .mmu_index = loongarch_cpu_mmu_index, - -#ifndef CONFIG_USER_ONLY - .tlb_fill = loongarch_cpu_tlb_fill, - .pointer_wrap = loongarch_pointer_wrap, - .cpu_exec_interrupt = loongarch_cpu_exec_interrupt, - .cpu_exec_halt = loongarch_cpu_has_work, - .cpu_exec_reset = cpu_reset, - .do_interrupt = loongarch_cpu_do_interrupt, - .do_transaction_failed = loongarch_cpu_do_transaction_failed, -#endif -}; -#endif /* CONFIG_TCG */ - #ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index e50d109767d49..e994f5a3d361f 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -41,6 +41,8 @@ uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu); uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu); void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu, uint64_t value); +bool loongarch_cpu_has_work(CPUState *cs); +bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env); #endif /* !CONFIG_USER_ONLY */ uint64_t read_fcc(CPULoongArchState *env); diff --git a/target/loongarch/tcg/meson.build b/target/loongarch/tcg/meson.build index bdf34f9673b68..b7adfe46f1dbf 100644 --- a/target/loongarch/tcg/meson.build +++ b/target/loongarch/tcg/meson.build @@ -7,6 +7,7 @@ loongarch_ss.add([zlib, gen]) loongarch_ss.add(files( 'fpu_helper.c', 'op_helper.c', + 'tcg_cpu.c', 'translate.c', 'vec_helper.c', )) diff --git a/target/loongarch/tcg/tcg_cpu.c b/target/loongarch/tcg/tcg_cpu.c new file mode 100644 index 0000000000000..c7f49838e3078 --- /dev/null +++ b/target/loongarch/tcg/tcg_cpu.c @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch CPU parameters for QEMU. + * + * Copyright (c) 2025 Loongson Technology Corporation Limited + */ +#include "qemu/osdep.h" +#include "qemu/accel.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "accel/accel-cpu-target.h" +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/cpu-ops.h" +#include "exec/translation-block.h" +#include "exec/target_page.h" +#include "tcg_loongarch.h" +#include "internals.h" + +#ifndef CONFIG_USER_ONLY +static void loongarch_cpu_do_interrupt(CPUState *cs) +{ + CPULoongArchState *env = cpu_env(cs); + bool update_badinstr = 1; + int cause = -1; + bool tlbfill = FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR); + uint32_t vec_size = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, VS); + + if (cs->exception_index != EXCCODE_INT) { + qemu_log_mask(CPU_LOG_INT, + "%s enter: pc " TARGET_FMT_lx " ERA " TARGET_FMT_lx + " TLBRERA " TARGET_FMT_lx " exception: %d (%s)\n", + __func__, env->pc, env->CSR_ERA, env->CSR_TLBRERA, + cs->exception_index, + loongarch_exception_name(cs->exception_index)); + } + + switch (cs->exception_index) { + case EXCCODE_DBP: + env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DCL, 1); + env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, ECODE, 0xC); + goto set_DERA; + set_DERA: + env->CSR_DERA = env->pc; + env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DST, 1); + set_pc(env, env->CSR_EENTRY + 0x480); + break; + case EXCCODE_INT: + if (FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) { + env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DEI, 1); + goto set_DERA; + } + QEMU_FALLTHROUGH; + case EXCCODE_PIF: + case EXCCODE_ADEF: + cause = cs->exception_index; + update_badinstr = 0; + break; + case EXCCODE_SYS: + case EXCCODE_BRK: + case EXCCODE_INE: + case EXCCODE_IPE: + case EXCCODE_FPD: + case EXCCODE_FPE: + case EXCCODE_SXD: + case EXCCODE_ASXD: + env->CSR_BADV = env->pc; + QEMU_FALLTHROUGH; + case EXCCODE_BCE: + case EXCCODE_ADEM: + case EXCCODE_PIL: + case EXCCODE_PIS: + case EXCCODE_PME: + case EXCCODE_PNR: + case EXCCODE_PNX: + case EXCCODE_PPI: + cause = cs->exception_index; + break; + default: + qemu_log("Error: exception(%d) has not been supported\n", + cs->exception_index); + abort(); + } + + if (update_badinstr) { + env->CSR_BADI = cpu_ldl_code(env, env->pc); + } + + /* Save PLV and IE */ + if (tlbfill) { + env->CSR_TLBRPRMD = FIELD_DP64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV, + FIELD_EX64(env->CSR_CRMD, + CSR_CRMD, PLV)); + env->CSR_TLBRPRMD = FIELD_DP64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE, + FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE)); + /* set the DA mode */ + env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 1); + env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 0); + env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, + PC, (env->pc >> 2)); + } else { + env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, ECODE, + EXCODE_MCODE(cause)); + env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, ESUBCODE, + EXCODE_SUBCODE(cause)); + env->CSR_PRMD = FIELD_DP64(env->CSR_PRMD, CSR_PRMD, PPLV, + FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV)); + env->CSR_PRMD = FIELD_DP64(env->CSR_PRMD, CSR_PRMD, PIE, + FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE)); + env->CSR_ERA = env->pc; + } + + env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, 0); + env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, 0); + + if (vec_size) { + vec_size = (1 << vec_size) * 4; + } + + if (cs->exception_index == EXCCODE_INT) { + /* Interrupt */ + uint32_t vector = 0; + uint32_t pending = FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS); + pending &= FIELD_EX64(env->CSR_ECFG, CSR_ECFG, LIE); + + /* Find the highest-priority interrupt. */ + vector = 31 - clz32(pending); + set_pc(env, env->CSR_EENTRY + \ + (EXCCODE_EXTERNAL_INT + vector) * vec_size); + qemu_log_mask(CPU_LOG_INT, + "%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx + " cause %d\n" " A " TARGET_FMT_lx " D " + TARGET_FMT_lx " vector = %d ExC " TARGET_FMT_lx "ExS" + TARGET_FMT_lx "\n", + __func__, env->pc, env->CSR_ERA, + cause, env->CSR_BADV, env->CSR_DERA, vector, + env->CSR_ECFG, env->CSR_ESTAT); + } else { + if (tlbfill) { + set_pc(env, env->CSR_TLBRENTRY); + } else { + set_pc(env, env->CSR_EENTRY + EXCODE_MCODE(cause) * vec_size); + } + qemu_log_mask(CPU_LOG_INT, + "%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx + " cause %d%s\n, ESTAT " TARGET_FMT_lx + " EXCFG " TARGET_FMT_lx " BADVA " TARGET_FMT_lx + "BADI " TARGET_FMT_lx " SYS_NUM " TARGET_FMT_lu + " cpu %d asid " TARGET_FMT_lx "\n", __func__, env->pc, + tlbfill ? env->CSR_TLBRERA : env->CSR_ERA, + cause, tlbfill ? "(refill)" : "", env->CSR_ESTAT, + env->CSR_ECFG, + tlbfill ? env->CSR_TLBRBADV : env->CSR_BADV, + env->CSR_BADI, env->gpr[11], cs->cpu_index, + env->CSR_ASID); + } + cs->exception_index = -1; +} + +static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, + vaddr addr, unsigned size, + MMUAccessType access_type, + int mmu_idx, MemTxAttrs attrs, + MemTxResult response, + uintptr_t retaddr) +{ + CPULoongArchState *env = cpu_env(cs); + + if (access_type == MMU_INST_FETCH) { + do_raise_exception(env, EXCCODE_ADEF, retaddr); + } else { + do_raise_exception(env, EXCCODE_ADEM, retaddr); + } +} + +static inline bool cpu_loongarch_hw_interrupts_enabled(CPULoongArchState *env) +{ + bool ret = 0; + + ret = (FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE) && + !(FIELD_EX64(env->CSR_DBG, CSR_DBG, DST))); + + return ret; +} + +static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + if (interrupt_request & CPU_INTERRUPT_HARD) { + CPULoongArchState *env = cpu_env(cs); + + if (cpu_loongarch_hw_interrupts_enabled(env) && + cpu_loongarch_hw_interrupts_pending(env)) { + /* Raise it */ + cs->exception_index = EXCCODE_INT; + loongarch_cpu_do_interrupt(cs); + return true; + } + } + return false; +} + +static vaddr loongarch_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + return is_va32(cpu_env(cs)) ? (uint32_t)result : result; +} +#endif + +static TCGTBCPUState loongarch_get_tb_cpu_state(CPUState *cs) +{ + CPULoongArchState *env = cpu_env(cs); + uint32_t flags; + + flags = env->CSR_CRMD & (R_CSR_CRMD_PLV_MASK | R_CSR_CRMD_PG_MASK); + flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, FPE) * HW_FLAGS_EUEN_FPE; + flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE) * HW_FLAGS_EUEN_SXE; + flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE) * HW_FLAGS_EUEN_ASXE; + flags |= is_va32(env) * HW_FLAGS_VA32; + + return (TCGTBCPUState){ .pc = env->pc, .flags = flags }; +} + +static void loongarch_cpu_synchronize_from_tb(CPUState *cs, + const TranslationBlock *tb) +{ + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); + set_pc(cpu_env(cs), tb->pc); +} + +static void loongarch_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + set_pc(cpu_env(cs), data[0]); +} + +static int loongarch_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + CPULoongArchState *env = cpu_env(cs); + + if (FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG)) { + return FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV); + } + return MMU_DA_IDX; +} + +const TCGCPUOps loongarch_tcg_ops = { + .guest_default_memory_order = 0, + .mttcg_supported = true, + + .initialize = loongarch_translate_init, + .translate_code = loongarch_translate_code, + .get_tb_cpu_state = loongarch_get_tb_cpu_state, + .synchronize_from_tb = loongarch_cpu_synchronize_from_tb, + .restore_state_to_opc = loongarch_restore_state_to_opc, + .mmu_index = loongarch_cpu_mmu_index, + +#ifndef CONFIG_USER_ONLY + .tlb_fill = loongarch_cpu_tlb_fill, + .pointer_wrap = loongarch_pointer_wrap, + .cpu_exec_interrupt = loongarch_cpu_exec_interrupt, + .cpu_exec_halt = loongarch_cpu_has_work, + .cpu_exec_reset = cpu_reset, + .do_interrupt = loongarch_cpu_do_interrupt, + .do_transaction_failed = loongarch_cpu_do_transaction_failed, +#endif +}; diff --git a/target/loongarch/tcg/tcg_loongarch.h b/target/loongarch/tcg/tcg_loongarch.h index 47702893e3248..7fb627f2d6303 100644 --- a/target/loongarch/tcg/tcg_loongarch.h +++ b/target/loongarch/tcg/tcg_loongarch.h @@ -9,6 +9,7 @@ #include "cpu.h" #include "cpu-mmu.h" +extern const TCGCPUOps loongarch_tcg_ops; void loongarch_csr_translate_init(void); bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, From 29332340e14738998a7ebcb62c5c9a310e8fb455 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 29 Sep 2025 11:53:37 +0800 Subject: [PATCH 1431/1794] target/loongarch: Move function do_raise_exception() to tcg_cpu.c Function do_raise_exception() is specified with TCG mode, so move it to file target/loongarch/tcg/tcg_cpu.c It is only code movement and there is no any function change. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson Message-ID: <20250929035338.2320419-3-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/cpu.c | 56 ---------------------------------- target/loongarch/tcg/tcg_cpu.c | 56 ++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 245c6d3ab9dd2..86490e0f7285d 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -44,62 +44,6 @@ const char * const fregnames[32] = { "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", }; -struct TypeExcp { - int32_t exccode; - const char * const name; -}; - -static const struct TypeExcp excp_names[] = { - {EXCCODE_INT, "Interrupt"}, - {EXCCODE_PIL, "Page invalid exception for load"}, - {EXCCODE_PIS, "Page invalid exception for store"}, - {EXCCODE_PIF, "Page invalid exception for fetch"}, - {EXCCODE_PME, "Page modified exception"}, - {EXCCODE_PNR, "Page Not Readable exception"}, - {EXCCODE_PNX, "Page Not Executable exception"}, - {EXCCODE_PPI, "Page Privilege error"}, - {EXCCODE_ADEF, "Address error for instruction fetch"}, - {EXCCODE_ADEM, "Address error for Memory access"}, - {EXCCODE_SYS, "Syscall"}, - {EXCCODE_BRK, "Break"}, - {EXCCODE_INE, "Instruction Non-Existent"}, - {EXCCODE_IPE, "Instruction privilege error"}, - {EXCCODE_FPD, "Floating Point Disabled"}, - {EXCCODE_FPE, "Floating Point Exception"}, - {EXCCODE_DBP, "Debug breakpoint"}, - {EXCCODE_BCE, "Bound Check Exception"}, - {EXCCODE_SXD, "128 bit vector instructions Disable exception"}, - {EXCCODE_ASXD, "256 bit vector instructions Disable exception"}, - {EXCP_HLT, "EXCP_HLT"}, -}; - -const char *loongarch_exception_name(int32_t exception) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(excp_names); i++) { - if (excp_names[i].exccode == exception) { - return excp_names[i].name; - } - } - return "Unknown"; -} - -void G_NORETURN do_raise_exception(CPULoongArchState *env, - uint32_t exception, - uintptr_t pc) -{ - CPUState *cs = env_cpu(env); - - qemu_log_mask(CPU_LOG_INT, "%s: exception: %d (%s)\n", - __func__, - exception, - loongarch_exception_name(exception)); - cs->exception_index = exception; - - cpu_loop_exit_restore(cs, pc); -} - static void loongarch_cpu_set_pc(CPUState *cs, vaddr value) { set_pc(cpu_env(cs), value); diff --git a/target/loongarch/tcg/tcg_cpu.c b/target/loongarch/tcg/tcg_cpu.c index c7f49838e3078..59b5800ecfead 100644 --- a/target/loongarch/tcg/tcg_cpu.c +++ b/target/loongarch/tcg/tcg_cpu.c @@ -16,6 +16,62 @@ #include "tcg_loongarch.h" #include "internals.h" +struct TypeExcp { + int32_t exccode; + const char * const name; +}; + +static const struct TypeExcp excp_names[] = { + {EXCCODE_INT, "Interrupt"}, + {EXCCODE_PIL, "Page invalid exception for load"}, + {EXCCODE_PIS, "Page invalid exception for store"}, + {EXCCODE_PIF, "Page invalid exception for fetch"}, + {EXCCODE_PME, "Page modified exception"}, + {EXCCODE_PNR, "Page Not Readable exception"}, + {EXCCODE_PNX, "Page Not Executable exception"}, + {EXCCODE_PPI, "Page Privilege error"}, + {EXCCODE_ADEF, "Address error for instruction fetch"}, + {EXCCODE_ADEM, "Address error for Memory access"}, + {EXCCODE_SYS, "Syscall"}, + {EXCCODE_BRK, "Break"}, + {EXCCODE_INE, "Instruction Non-Existent"}, + {EXCCODE_IPE, "Instruction privilege error"}, + {EXCCODE_FPD, "Floating Point Disabled"}, + {EXCCODE_FPE, "Floating Point Exception"}, + {EXCCODE_DBP, "Debug breakpoint"}, + {EXCCODE_BCE, "Bound Check Exception"}, + {EXCCODE_SXD, "128 bit vector instructions Disable exception"}, + {EXCCODE_ASXD, "256 bit vector instructions Disable exception"}, + {EXCP_HLT, "EXCP_HLT"}, +}; + +const char *loongarch_exception_name(int32_t exception) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(excp_names); i++) { + if (excp_names[i].exccode == exception) { + return excp_names[i].name; + } + } + return "Unknown"; +} + +void G_NORETURN do_raise_exception(CPULoongArchState *env, + uint32_t exception, + uintptr_t pc) +{ + CPUState *cs = env_cpu(env); + + qemu_log_mask(CPU_LOG_INT, "%s: exception: %d (%s)\n", + __func__, + exception, + loongarch_exception_name(exception)); + cs->exception_index = exception; + + cpu_loop_exit_restore(cs, pc); +} + #ifndef CONFIG_USER_ONLY static void loongarch_cpu_do_interrupt(CPUState *cs) { From fa6af7f6bf6dbc4c83595905d2572ad86358aa67 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 29 Sep 2025 11:53:38 +0800 Subject: [PATCH 1432/1794] target/loongarch: Define loongarch_exception_name() as static Function loongarch_exception_name() is only called in defined file target/loongarch/tcg/tcg_cpu.c, set this function as static. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson Message-ID: <20250929035338.2320419-4-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/internals.h | 2 -- target/loongarch/tcg/tcg_cpu.c | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index e994f5a3d361f..8793bd9df65a6 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -24,8 +24,6 @@ void G_NORETURN do_raise_exception(CPULoongArchState *env, uint32_t exception, uintptr_t pc); -const char *loongarch_exception_name(int32_t exception); - #ifdef CONFIG_TCG int ieee_ex_to_loongarch(int xcpt); void restore_fp_status(CPULoongArchState *env); diff --git a/target/loongarch/tcg/tcg_cpu.c b/target/loongarch/tcg/tcg_cpu.c index 59b5800ecfead..82b54e6dc3cc3 100644 --- a/target/loongarch/tcg/tcg_cpu.c +++ b/target/loongarch/tcg/tcg_cpu.c @@ -45,7 +45,7 @@ static const struct TypeExcp excp_names[] = { {EXCP_HLT, "EXCP_HLT"}, }; -const char *loongarch_exception_name(int32_t exception) +static const char *loongarch_exception_name(int32_t exception) { int i; From 18beee5c6bb875ec5a1455e5e7118338701e2a16 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 Oct 2025 23:05:08 +0200 Subject: [PATCH 1433/1794] rust: pl011: fix warning with new clippy Newer versions of clippy are able to see that all the variants in the PL011 word length enum end with "Bits", and complain about it. Allow it. Reported-by: Richard Henderson Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/registers.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs index 0c3a4d7d214e5..fa572811b29c0 100644 --- a/rust/hw/char/pl011/src/registers.rs +++ b/rust/hw/char/pl011/src/registers.rs @@ -255,6 +255,7 @@ pub enum Mode { #[bitsize(2)] #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] +#[allow(clippy::enum_variant_names)] /// `WLEN` Word length, field of [Line Control register](LineControl). /// /// These bits indicate the number of data bits transmitted or received in a From 314a31ea0ba06160a54972ecf08ae56bc0c6b094 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 6 Oct 2025 19:03:04 +0200 Subject: [PATCH 1434/1794] tests/functional: Drop the "Attempting to cache ..." log text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fetch() function already either prints "Using cached asset ..." or "Downloading ... to ..." with the same file name to the log, so the "Attempting to cache ..." message does not provide any additional valuable information. Thus let's drop it to limit the length of the logging output to a more reasonable size. Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth Message-ID: <20251006170304.197387-1-thuth@redhat.com> --- tests/functional/qemu_test/asset.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index 2971a989d1ec8..f666125bfaf9e 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -225,7 +225,6 @@ def precache_test(test): log.addHandler(handler) for name, asset in vars(test.__class__).items(): if name.startswith("ASSET_") and type(asset) == Asset: - log.info("Attempting to cache '%s'" % asset) try: asset.fetch() except AssetError as e: From 8dd912b042f47bb0468f097df708cf2fab2af614 Mon Sep 17 00:00:00 2001 From: Jaehoon Kim Date: Wed, 1 Oct 2025 10:39:38 -0500 Subject: [PATCH 1435/1794] s390x/pci: fix interrupt blocking by returning only the device's summary bit Previously, set_ind_atomic() returned the entire byte containing multiple summary bits. This meant that if any other summary bit in the byte was set, interrupt injection could be incorrectly blocked, even when the current device's summary bit was not set. As a result, the guest could remain blocked after I/O completion during FIO tests. This patch replaces set_ind_atomic() with set_ind_bit_atomic(), which returns true if the bit was set by this function, and false if it was already set or mapping failed. Interrupts are now blocked only when the device's own summary bit was not previously set, avoiding unintended blocking when multiple PCI summary bits exist within the same byte. Signed-off-by: Jaehoon Kim Reviewed-by: Halil Pasic Reviewed-by: Eric Farman Reviewed-by: Christian Borntraeger Reviewed-by: Matthew Rosato Message-ID: <20251001154004.71917-1-jhkim@linux.ibm.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-pci-bus.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index f87d2748b6378..e8e41c8a9a10a 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -652,7 +652,16 @@ static const PCIIOMMUOps s390_iommu_ops = { .get_address_space = s390_pci_dma_iommu, }; -static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set) +/** + * set_ind_bit_atomic - Atomically set a bit in an indicator + * + * @ind_loc: Address of the indicator + * @to_be_set: Bit to set + * + * Returns true if the bit was set by this function, false if it was + * already set or mapping failed. + */ +static bool set_ind_bit_atomic(uint64_t ind_loc, uint8_t to_be_set) { uint8_t expected, actual; hwaddr len = 1; @@ -662,7 +671,7 @@ static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set) ind_addr = cpu_physical_memory_map(ind_loc, &len, true); if (!ind_addr) { s390_pci_generate_error_event(ERR_EVENT_AIRERR, 0, 0, 0, 0); - return -1; + return false; } actual = *ind_addr; do { @@ -671,7 +680,7 @@ static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set) } while (actual != expected); cpu_physical_memory_unmap((void *)ind_addr, len, 1, len); - return actual; + return (actual & to_be_set) ? false : true; } static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data, @@ -693,10 +702,10 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data, ind_bit = pbdev->routes.adapter.ind_offset; sum_bit = pbdev->routes.adapter.summary_offset; - set_ind_atomic(pbdev->routes.adapter.ind_addr + (ind_bit + vec) / 8, + set_ind_bit_atomic(pbdev->routes.adapter.ind_addr + (ind_bit + vec) / 8, 0x80 >> ((ind_bit + vec) % 8)); - if (!set_ind_atomic(pbdev->routes.adapter.summary_addr + sum_bit / 8, - 0x80 >> (sum_bit % 8))) { + if (set_ind_bit_atomic(pbdev->routes.adapter.summary_addr + sum_bit / 8, + 0x80 >> (sum_bit % 8))) { css_adapter_interrupt(CSS_IO_ADAPTER_PCI, pbdev->isc); } } From d24ac20c6a86b32d64b90a90693c97086eba0f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 2 Oct 2025 11:11:27 +0200 Subject: [PATCH 1436/1794] target/s390x: Replace legacy cpu_physical_memory_[un]map() calls (1/3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit b7ecba0f6f6 ("docs/devel/loads-stores.rst: Document our various load and store APIs") mentioned cpu_physical_memory_*() methods are legacy, the replacement being address_space_*(). Replace the *_map() / *_unmap() methods in mchk_store_vregs(). No behavioral change expected. Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20251002091132.65703-5-philmd@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/tcg/excp_helper.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index 4c7faeee82b10..5e1d4dc9583f2 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -30,6 +30,7 @@ #ifndef CONFIG_USER_ONLY #include "qemu/timer.h" #include "system/address-spaces.h" +#include "system/memory.h" #include "hw/s390x/ioinst.h" #include "hw/s390x/s390_flic.h" #include "hw/boards.h" @@ -418,16 +419,18 @@ QEMU_BUILD_BUG_ON(sizeof(MchkExtSaveArea) != 1024); static int mchk_store_vregs(CPUS390XState *env, uint64_t mcesao) { + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; + AddressSpace *as = env_cpu(env)->as; hwaddr len = sizeof(MchkExtSaveArea); MchkExtSaveArea *sa; int i; - sa = cpu_physical_memory_map(mcesao, &len, true); + sa = address_space_map(as, mcesao, &len, true, attrs); if (!sa) { return -EFAULT; } if (len != sizeof(MchkExtSaveArea)) { - cpu_physical_memory_unmap(sa, len, 1, 0); + address_space_unmap(as, sa, len, true, 0); return -EFAULT; } @@ -436,7 +439,7 @@ static int mchk_store_vregs(CPUS390XState *env, uint64_t mcesao) sa->vregs[i][1] = cpu_to_be64(env->vregs[i][1]); } - cpu_physical_memory_unmap(sa, len, 1, len); + address_space_unmap(as, sa, len, true, len); return 0; } From c35b166c1dc2d0332d2199ea8f36cf4eaa9eda35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 2 Oct 2025 11:11:28 +0200 Subject: [PATCH 1437/1794] target/s390x: Propagate CPUS390XState to cpu_unmap_lowcore() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To be able to access the CPU state in cpu_unmap_lowcore() in the next commit, propagate it as argument. cpu_map_lowcore() already takes it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20251002091132.65703-6-philmd@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/helper.c | 4 ++-- target/s390x/s390x-internal.h | 2 +- target/s390x/tcg/excp_helper.c | 10 +++++----- target/s390x/tcg/misc_helper.c | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/target/s390x/helper.c b/target/s390x/helper.c index 5c127da1a6a89..a6d4a1ce05d5f 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -117,7 +117,7 @@ LowCore *cpu_map_lowcore(CPUS390XState *env) return lowcore; } -void cpu_unmap_lowcore(LowCore *lowcore) +void cpu_unmap_lowcore(CPUS390XState *env, LowCore *lowcore) { cpu_physical_memory_unmap(lowcore, sizeof(LowCore), 1, sizeof(LowCore)); } @@ -134,7 +134,7 @@ void do_restart_interrupt(CPUS390XState *env) mask = be64_to_cpu(lowcore->restart_new_psw.mask); addr = be64_to_cpu(lowcore->restart_new_psw.addr); - cpu_unmap_lowcore(lowcore); + cpu_unmap_lowcore(env, lowcore); env->pending_int &= ~INTERRUPT_RESTART; s390_cpu_set_psw(env, mask, addr); diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index 56cce2e7f5060..1fb752aa1d659 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -327,7 +327,7 @@ hwaddr s390_cpu_get_phys_addr_debug(CPUState *cpu, vaddr addr); int s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch); int s390_store_adtl_status(S390CPU *cpu, hwaddr addr, hwaddr len); LowCore *cpu_map_lowcore(CPUS390XState *env); -void cpu_unmap_lowcore(LowCore *lowcore); +void cpu_unmap_lowcore(CPUS390XState *env, LowCore *lowcore); #endif /* CONFIG_USER_ONLY */ diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index 5e1d4dc9583f2..0ae4e26606594 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -285,7 +285,7 @@ static void do_program_interrupt(CPUS390XState *env) addr = be64_to_cpu(lowcore->program_new_psw.addr); lowcore->per_breaking_event_addr = cpu_to_be64(env->gbea); - cpu_unmap_lowcore(lowcore); + cpu_unmap_lowcore(env, lowcore); s390_cpu_set_psw(env, mask, addr); } @@ -304,7 +304,7 @@ static void do_svc_interrupt(CPUS390XState *env) mask = be64_to_cpu(lowcore->svc_new_psw.mask); addr = be64_to_cpu(lowcore->svc_new_psw.addr); - cpu_unmap_lowcore(lowcore); + cpu_unmap_lowcore(env, lowcore); s390_cpu_set_psw(env, mask, addr); @@ -378,7 +378,7 @@ static void do_ext_interrupt(CPUS390XState *env) lowcore->external_old_psw.mask = cpu_to_be64(s390_cpu_get_psw_mask(env)); lowcore->external_old_psw.addr = cpu_to_be64(env->psw.addr); - cpu_unmap_lowcore(lowcore); + cpu_unmap_lowcore(env, lowcore); s390_cpu_set_psw(env, mask, addr); } @@ -405,7 +405,7 @@ static void do_io_interrupt(CPUS390XState *env) mask = be64_to_cpu(lowcore->io_new_psw.mask); addr = be64_to_cpu(lowcore->io_new_psw.addr); - cpu_unmap_lowcore(lowcore); + cpu_unmap_lowcore(env, lowcore); g_free(io); s390_cpu_set_psw(env, mask, addr); @@ -491,7 +491,7 @@ static void do_mchk_interrupt(CPUS390XState *env) mask = be64_to_cpu(lowcore->mcck_new_psw.mask); addr = be64_to_cpu(lowcore->mcck_new_psw.addr); - cpu_unmap_lowcore(lowcore); + cpu_unmap_lowcore(env, lowcore); s390_cpu_set_psw(env, mask, addr); } diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index f7101be5745c2..6d9d601d29aef 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -570,7 +570,7 @@ uint32_t HELPER(tpi)(CPUS390XState *env, uint64_t addr) lowcore->subchannel_nr = cpu_to_be16(io->nr); lowcore->io_int_parm = cpu_to_be32(io->parm); lowcore->io_int_word = cpu_to_be32(io->word); - cpu_unmap_lowcore(lowcore); + cpu_unmap_lowcore(env, lowcore); } g_free(io); @@ -700,7 +700,7 @@ void HELPER(stfl)(CPUS390XState *env) lowcore = cpu_map_lowcore(env); prepare_stfl(); memcpy(&lowcore->stfl_fac_list, stfl_bytes, sizeof(lowcore->stfl_fac_list)); - cpu_unmap_lowcore(lowcore); + cpu_unmap_lowcore(env, lowcore); } #endif From 64321858d0735191b6160ba78f2282a67df6d9d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 2 Oct 2025 11:11:29 +0200 Subject: [PATCH 1438/1794] target/s390x: Replace legacy cpu_physical_memory_[un]map() calls (2/3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit b7ecba0f6f6 ("docs/devel/loads-stores.rst: Document our various load and store APIs") mentioned cpu_physical_memory_*() methods are legacy, the replacement being address_space_*(). Replace the *_map() / *_unmap() methods in cpu_[un]map_lowcore(). No behavioral change expected. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20251002091132.65703-7-philmd@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/helper.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/target/s390x/helper.c b/target/s390x/helper.c index a6d4a1ce05d5f..b01b7d9bbbf25 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -26,6 +26,7 @@ #include "hw/s390x/ioinst.h" #include "target/s390x/kvm/pv.h" #include "system/hw_accel.h" +#include "system/memory.h" #include "system/runstate.h" #include "exec/target_page.h" #include "exec/watchpoint.h" @@ -107,11 +108,13 @@ LowCore *cpu_map_lowcore(CPUS390XState *env) { LowCore *lowcore; hwaddr len = sizeof(LowCore); + CPUState *cs = env_cpu(env); + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; - lowcore = cpu_physical_memory_map(env->psa, &len, true); + lowcore = address_space_map(cs->as, env->psa, &len, true, attrs); if (len < sizeof(LowCore)) { - cpu_abort(env_cpu(env), "Could not map lowcore\n"); + cpu_abort(cs, "Could not map lowcore\n"); } return lowcore; @@ -119,7 +122,9 @@ LowCore *cpu_map_lowcore(CPUS390XState *env) void cpu_unmap_lowcore(CPUS390XState *env, LowCore *lowcore) { - cpu_physical_memory_unmap(lowcore, sizeof(LowCore), 1, sizeof(LowCore)); + AddressSpace *as = env_cpu(env)->as; + + address_space_unmap(as, lowcore, sizeof(LowCore), true, sizeof(LowCore)); } void do_restart_interrupt(CPUS390XState *env) From 1819902285177c8781ab4ef92cc44e0882c0dec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 2 Oct 2025 11:11:30 +0200 Subject: [PATCH 1439/1794] target/s390x: Reduce s390_store_adtl_status() scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit s390_store_adtl_status() is only called within sigp.c, move it and the SigpAdtlSaveArea structure definition there where it belongs, with other SIGP handling code. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20251002091132.65703-8-philmd@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/helper.c | 40 ----------------------------------- target/s390x/s390x-internal.h | 1 - target/s390x/sigp.c | 40 +++++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 41 deletions(-) diff --git a/target/s390x/helper.c b/target/s390x/helper.c index b01b7d9bbbf25..84321e1d68e68 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -248,43 +248,3 @@ int s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch) return 0; } - -typedef struct SigpAdtlSaveArea { - uint64_t vregs[32][2]; /* 0x0000 */ - uint8_t pad_0x0200[0x0400 - 0x0200]; /* 0x0200 */ - uint64_t gscb[4]; /* 0x0400 */ - uint8_t pad_0x0420[0x1000 - 0x0420]; /* 0x0420 */ -} SigpAdtlSaveArea; -QEMU_BUILD_BUG_ON(sizeof(SigpAdtlSaveArea) != 4096); - -#define ADTL_GS_MIN_SIZE 2048 /* minimal size of adtl save area for GS */ -int s390_store_adtl_status(S390CPU *cpu, hwaddr addr, hwaddr len) -{ - SigpAdtlSaveArea *sa; - hwaddr save = len; - int i; - - sa = cpu_physical_memory_map(addr, &save, true); - if (!sa) { - return -EFAULT; - } - if (save != len) { - cpu_physical_memory_unmap(sa, len, 1, 0); - return -EFAULT; - } - - if (s390_has_feat(S390_FEAT_VECTOR)) { - for (i = 0; i < 32; i++) { - sa->vregs[i][0] = cpu_to_be64(cpu->env.vregs[i][0]); - sa->vregs[i][1] = cpu_to_be64(cpu->env.vregs[i][1]); - } - } - if (s390_has_feat(S390_FEAT_GUARDED_STORAGE) && len >= ADTL_GS_MIN_SIZE) { - for (i = 0; i < 4; i++) { - sa->gscb[i] = cpu_to_be64(cpu->env.gscb[i]); - } - } - - cpu_physical_memory_unmap(sa, len, 1, len); - return 0; -} diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index 1fb752aa1d659..a49dca94a75fe 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -325,7 +325,6 @@ hwaddr s390_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); hwaddr s390_cpu_get_phys_addr_debug(CPUState *cpu, vaddr addr); #define S390_STORE_STATUS_DEF_ADDR offsetof(LowCore, floating_pt_save_area) int s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch); -int s390_store_adtl_status(S390CPU *cpu, hwaddr addr, hwaddr len); LowCore *cpu_map_lowcore(CPUS390XState *env); void cpu_unmap_lowcore(CPUS390XState *env, LowCore *lowcore); #endif /* CONFIG_USER_ONLY */ diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index 5e95c4978f962..1464be76983d1 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -172,6 +172,46 @@ static void sigp_store_status_at_address(CPUState *cs, run_on_cpu_data arg) si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } +typedef struct SigpAdtlSaveArea { + uint64_t vregs[32][2]; /* 0x0000 */ + uint8_t pad_0x0200[0x0400 - 0x0200]; /* 0x0200 */ + uint64_t gscb[4]; /* 0x0400 */ + uint8_t pad_0x0420[0x1000 - 0x0420]; /* 0x0420 */ +} SigpAdtlSaveArea; +QEMU_BUILD_BUG_ON(sizeof(SigpAdtlSaveArea) != 4096); + +#define ADTL_GS_MIN_SIZE 2048 /* minimal size of adtl save area for GS */ +static int s390_store_adtl_status(S390CPU *cpu, hwaddr addr, hwaddr len) +{ + SigpAdtlSaveArea *sa; + hwaddr save = len; + int i; + + sa = cpu_physical_memory_map(addr, &save, true); + if (!sa) { + return -EFAULT; + } + if (save != len) { + cpu_physical_memory_unmap(sa, len, 1, 0); + return -EFAULT; + } + + if (s390_has_feat(S390_FEAT_VECTOR)) { + for (i = 0; i < 32; i++) { + sa->vregs[i][0] = cpu_to_be64(cpu->env.vregs[i][0]); + sa->vregs[i][1] = cpu_to_be64(cpu->env.vregs[i][1]); + } + } + if (s390_has_feat(S390_FEAT_GUARDED_STORAGE) && len >= ADTL_GS_MIN_SIZE) { + for (i = 0; i < 4; i++) { + sa->gscb[i] = cpu_to_be64(cpu->env.gscb[i]); + } + } + + cpu_physical_memory_unmap(sa, len, 1, len); + return 0; +} + #define ADTL_SAVE_LC_MASK 0xfUL static void sigp_store_adtl_status(CPUState *cs, run_on_cpu_data arg) { From db7721857b3033222a521acc2ace7d3ed9529cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 2 Oct 2025 11:11:31 +0200 Subject: [PATCH 1440/1794] target/s390x: Reduce s390_store_status() scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit s390_store_status() is only called within sigp.c, move it and the SigpSaveArea structure definition there where it belongs, with other SIGP handling code. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20251002091132.65703-9-philmd@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/helper.c | 67 ---------------------------------- target/s390x/s390x-internal.h | 2 -- target/s390x/sigp.c | 68 +++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 69 deletions(-) diff --git a/target/s390x/helper.c b/target/s390x/helper.c index 84321e1d68e68..184428c6d9de8 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -24,7 +24,6 @@ #include "gdbstub/helpers.h" #include "qemu/timer.h" #include "hw/s390x/ioinst.h" -#include "target/s390x/kvm/pv.h" #include "system/hw_accel.h" #include "system/memory.h" #include "system/runstate.h" @@ -182,69 +181,3 @@ void s390_cpu_recompute_watchpoints(CPUState *cs) wp_flags, NULL); } } - -typedef struct SigpSaveArea { - uint64_t fprs[16]; /* 0x0000 */ - uint64_t grs[16]; /* 0x0080 */ - PSW psw; /* 0x0100 */ - uint8_t pad_0x0110[0x0118 - 0x0110]; /* 0x0110 */ - uint32_t prefix; /* 0x0118 */ - uint32_t fpc; /* 0x011c */ - uint8_t pad_0x0120[0x0124 - 0x0120]; /* 0x0120 */ - uint32_t todpr; /* 0x0124 */ - uint64_t cputm; /* 0x0128 */ - uint64_t ckc; /* 0x0130 */ - uint8_t pad_0x0138[0x0140 - 0x0138]; /* 0x0138 */ - uint32_t ars[16]; /* 0x0140 */ - uint64_t crs[16]; /* 0x0384 */ -} SigpSaveArea; -QEMU_BUILD_BUG_ON(sizeof(SigpSaveArea) != 512); - -int s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch) -{ - static const uint8_t ar_id = 1; - SigpSaveArea *sa; - hwaddr len = sizeof(*sa); - int i; - - /* For PVMs storing will occur when this cpu enters SIE again */ - if (s390_is_pv()) { - return 0; - } - - sa = cpu_physical_memory_map(addr, &len, true); - if (!sa) { - return -EFAULT; - } - if (len != sizeof(*sa)) { - cpu_physical_memory_unmap(sa, len, 1, 0); - return -EFAULT; - } - - if (store_arch) { - cpu_physical_memory_write(offsetof(LowCore, ar_access_id), &ar_id, 1); - } - for (i = 0; i < 16; ++i) { - sa->fprs[i] = cpu_to_be64(*get_freg(&cpu->env, i)); - } - for (i = 0; i < 16; ++i) { - sa->grs[i] = cpu_to_be64(cpu->env.regs[i]); - } - sa->psw.addr = cpu_to_be64(cpu->env.psw.addr); - sa->psw.mask = cpu_to_be64(s390_cpu_get_psw_mask(&cpu->env)); - sa->prefix = cpu_to_be32(cpu->env.psa); - sa->fpc = cpu_to_be32(cpu->env.fpc); - sa->todpr = cpu_to_be32(cpu->env.todpr); - sa->cputm = cpu_to_be64(cpu->env.cputm); - sa->ckc = cpu_to_be64(cpu->env.ckc >> 8); - for (i = 0; i < 16; ++i) { - sa->ars[i] = cpu_to_be32(cpu->env.aregs[i]); - } - for (i = 0; i < 16; ++i) { - sa->crs[i] = cpu_to_be64(cpu->env.cregs[i]); - } - - cpu_physical_memory_unmap(sa, len, 1, len); - - return 0; -} diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index a49dca94a75fe..9691366ec916e 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -323,8 +323,6 @@ void s390x_cpu_timer(void *opaque); void s390_handle_wait(S390CPU *cpu); hwaddr s390_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); hwaddr s390_cpu_get_phys_addr_debug(CPUState *cpu, vaddr addr); -#define S390_STORE_STATUS_DEF_ADDR offsetof(LowCore, floating_pt_save_area) -int s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch); LowCore *cpu_map_lowcore(CPUS390XState *env); void cpu_unmap_lowcore(CPUS390XState *env, LowCore *lowcore); #endif /* CONFIG_USER_ONLY */ diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index 1464be76983d1..2487acdb49ef4 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -19,6 +19,7 @@ #include "system/tcg.h" #include "trace.h" #include "qapi/qapi-types-machine.h" +#include "target/s390x/kvm/pv.h" QemuMutex qemu_sigp_mutex; @@ -126,6 +127,73 @@ static void sigp_stop(CPUState *cs, run_on_cpu_data arg) si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } +typedef struct SigpSaveArea { + uint64_t fprs[16]; /* 0x0000 */ + uint64_t grs[16]; /* 0x0080 */ + PSW psw; /* 0x0100 */ + uint8_t pad_0x0110[0x0118 - 0x0110]; /* 0x0110 */ + uint32_t prefix; /* 0x0118 */ + uint32_t fpc; /* 0x011c */ + uint8_t pad_0x0120[0x0124 - 0x0120]; /* 0x0120 */ + uint32_t todpr; /* 0x0124 */ + uint64_t cputm; /* 0x0128 */ + uint64_t ckc; /* 0x0130 */ + uint8_t pad_0x0138[0x0140 - 0x0138]; /* 0x0138 */ + uint32_t ars[16]; /* 0x0140 */ + uint64_t crs[16]; /* 0x0384 */ +} SigpSaveArea; +QEMU_BUILD_BUG_ON(sizeof(SigpSaveArea) != 512); + +#define S390_STORE_STATUS_DEF_ADDR offsetof(LowCore, floating_pt_save_area) +static int s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch) +{ + static const uint8_t ar_id = 1; + SigpSaveArea *sa; + hwaddr len = sizeof(*sa); + int i; + + /* For PVMs storing will occur when this cpu enters SIE again */ + if (s390_is_pv()) { + return 0; + } + + sa = cpu_physical_memory_map(addr, &len, true); + if (!sa) { + return -EFAULT; + } + if (len != sizeof(*sa)) { + cpu_physical_memory_unmap(sa, len, 1, 0); + return -EFAULT; + } + + if (store_arch) { + cpu_physical_memory_write(offsetof(LowCore, ar_access_id), &ar_id, 1); + } + for (i = 0; i < 16; ++i) { + sa->fprs[i] = cpu_to_be64(*get_freg(&cpu->env, i)); + } + for (i = 0; i < 16; ++i) { + sa->grs[i] = cpu_to_be64(cpu->env.regs[i]); + } + sa->psw.addr = cpu_to_be64(cpu->env.psw.addr); + sa->psw.mask = cpu_to_be64(s390_cpu_get_psw_mask(&cpu->env)); + sa->prefix = cpu_to_be32(cpu->env.psa); + sa->fpc = cpu_to_be32(cpu->env.fpc); + sa->todpr = cpu_to_be32(cpu->env.todpr); + sa->cputm = cpu_to_be64(cpu->env.cputm); + sa->ckc = cpu_to_be64(cpu->env.ckc >> 8); + for (i = 0; i < 16; ++i) { + sa->ars[i] = cpu_to_be32(cpu->env.aregs[i]); + } + for (i = 0; i < 16; ++i) { + sa->crs[i] = cpu_to_be64(cpu->env.cregs[i]); + } + + cpu_physical_memory_unmap(sa, len, 1, len); + + return 0; +} + static void sigp_stop_and_store_status(CPUState *cs, run_on_cpu_data arg) { S390CPU *cpu = S390_CPU(cs); From 00457aed51a240d6478e0870cd17fdcaa5f6a0ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 2 Oct 2025 11:11:32 +0200 Subject: [PATCH 1441/1794] target/s390x: Replace legacy cpu_physical_memory_[un]map() calls (3/3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit b7ecba0f6f6 ("docs/devel/loads-stores.rst: Document our various load and store APIs") mentioned cpu_physical_memory_*() methods are legacy, the replacement being address_space_*(). Replace the *_map() / *_unmap() methods in s390_store_status() and s390_store_adtl_status(). In s390_store_status(), replace cpu_physical_memory_write() by address_space_stb(), restricting @ar_id scope. No behavioral change expected. Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20251002091132.65703-10-philmd@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/sigp.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index 2487acdb49ef4..f5d7bc0fa2256 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -13,6 +13,7 @@ #include "s390x-internal.h" #include "hw/boards.h" #include "system/hw_accel.h" +#include "system/memory.h" #include "system/runstate.h" #include "system/address-spaces.h" #include "exec/cputlb.h" @@ -147,7 +148,8 @@ QEMU_BUILD_BUG_ON(sizeof(SigpSaveArea) != 512); #define S390_STORE_STATUS_DEF_ADDR offsetof(LowCore, floating_pt_save_area) static int s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch) { - static const uint8_t ar_id = 1; + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; + AddressSpace *as = CPU(cpu)->as; SigpSaveArea *sa; hwaddr len = sizeof(*sa); int i; @@ -157,17 +159,21 @@ static int s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch) return 0; } - sa = cpu_physical_memory_map(addr, &len, true); + sa = address_space_map(as, addr, &len, true, attrs); if (!sa) { return -EFAULT; } if (len != sizeof(*sa)) { - cpu_physical_memory_unmap(sa, len, 1, 0); + address_space_unmap(as, sa, len, true, 0); return -EFAULT; } if (store_arch) { - cpu_physical_memory_write(offsetof(LowCore, ar_access_id), &ar_id, 1); + static const uint8_t ar_id = 1; + + address_space_stb(as, offsetof(LowCore, ar_access_id), + ar_id, attrs, NULL); + } for (i = 0; i < 16; ++i) { sa->fprs[i] = cpu_to_be64(*get_freg(&cpu->env, i)); @@ -189,7 +195,7 @@ static int s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch) sa->crs[i] = cpu_to_be64(cpu->env.cregs[i]); } - cpu_physical_memory_unmap(sa, len, 1, len); + address_space_unmap(as, sa, len, true, len); return 0; } @@ -251,16 +257,18 @@ QEMU_BUILD_BUG_ON(sizeof(SigpAdtlSaveArea) != 4096); #define ADTL_GS_MIN_SIZE 2048 /* minimal size of adtl save area for GS */ static int s390_store_adtl_status(S390CPU *cpu, hwaddr addr, hwaddr len) { + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; + AddressSpace *as = CPU(cpu)->as; SigpAdtlSaveArea *sa; hwaddr save = len; int i; - sa = cpu_physical_memory_map(addr, &save, true); + sa = address_space_map(as, addr, &save, true, attrs); if (!sa) { return -EFAULT; } if (save != len) { - cpu_physical_memory_unmap(sa, len, 1, 0); + address_space_unmap(as, sa, len, true, 0); return -EFAULT; } @@ -276,7 +284,8 @@ static int s390_store_adtl_status(S390CPU *cpu, hwaddr addr, hwaddr len) } } - cpu_physical_memory_unmap(sa, len, 1, len); + address_space_unmap(as, sa, len, true, len); + return 0; } From 47ea7263701e85ea03270d34c9a90f3971597e06 Mon Sep 17 00:00:00 2001 From: Matthew Rosato Date: Wed, 8 Oct 2025 16:33:50 -0400 Subject: [PATCH 1442/1794] s390x/pci: set kvm_msi_via_irqfd_allowed Allow irqfd to be used for virtio-pci on s390x if the kernel supports it. This improves s390x virtio-pci performance when using kvm acceleration by allowing kvm to deliver interrupts instead of QEMU. Signed-off-by: Matthew Rosato Reviewed-by: Farhan Ali Reviewed-by: Eric Farman Message-ID: <20251008203350.354121-1-mjrosato@linux.ibm.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-pci-bus.c | 1 + include/hw/s390x/s390-pci-kvm.h | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index e8e41c8a9a10a..52820894fa168 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -900,6 +900,7 @@ static void s390_pcihost_realize(DeviceState *dev, Error **errp) s390_pci_init_default_group(); css_register_io_adapters(CSS_IO_ADAPTER_PCI, true, false, S390_ADAPTER_SUPPRESSIBLE, errp); + s390_pcihost_kvm_realize(); } static void s390_pcihost_unrealize(DeviceState *dev) diff --git a/include/hw/s390x/s390-pci-kvm.h b/include/hw/s390x/s390-pci-kvm.h index 933814a4025bb..c33f2833a3cc3 100644 --- a/include/hw/s390x/s390-pci-kvm.h +++ b/include/hw/s390x/s390-pci-kvm.h @@ -14,12 +14,19 @@ #include "hw/s390x/s390-pci-bus.h" #include "hw/s390x/s390-pci-inst.h" +#include "system/kvm.h" #ifdef CONFIG_KVM +static inline void s390_pcihost_kvm_realize(void) +{ + kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled(); +} + bool s390_pci_kvm_interp_allowed(void); int s390_pci_kvm_aif_enable(S390PCIBusDevice *pbdev, ZpciFib *fib, bool assist); int s390_pci_kvm_aif_disable(S390PCIBusDevice *pbdev); #else +static inline void s390_pcihost_kvm_realize(void) {} static inline bool s390_pci_kvm_interp_allowed(void) { return false; From 5d7148ae8c0f881425badb9b624e2d44efcdc0be Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 8 Oct 2025 14:55:01 -0700 Subject: [PATCH 1443/1794] tests/functional: update tests using TF-A/TF-RMM to support FEAT_GCS Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- tests/functional/aarch64/test_device_passthrough.py | 4 ++-- tests/functional/aarch64/test_rme_sbsaref.py | 4 ++-- tests/functional/aarch64/test_rme_virt.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/functional/aarch64/test_device_passthrough.py b/tests/functional/aarch64/test_device_passthrough.py index 17437784bbe14..05a3f52d5e217 100755 --- a/tests/functional/aarch64/test_device_passthrough.py +++ b/tests/functional/aarch64/test_device_passthrough.py @@ -85,8 +85,8 @@ class Aarch64DevicePassthrough(QemuSystemTest): # https://docs.kernel.org/driver-api/vfio.html#vfio-device-cde ASSET_DEVICE_PASSTHROUGH_STACK = Asset( ('https://github.com/pbo-linaro/qemu-linux-stack/' - 'releases/download/build/device_passthrough-c3fb84a.tar.xz'), - '15ac2b02bed0c0ea8e3e007de0bcfdaf6fd51c1ba98213f841dc7d01d6f72f04') + 'releases/download/build/device_passthrough-a9612a2.tar.xz'), + 'f7d2f70912e7231986e6e293e1a2c4786dd02bec113a7acb6bfc619e96155455') # This tests the device passthrough implementation, by booting a VM # supporting it with two nvme disks attached, and launching a nested VM diff --git a/tests/functional/aarch64/test_rme_sbsaref.py b/tests/functional/aarch64/test_rme_sbsaref.py index ca892e0a8c9d4..6f92858397ad8 100755 --- a/tests/functional/aarch64/test_rme_sbsaref.py +++ b/tests/functional/aarch64/test_rme_sbsaref.py @@ -25,8 +25,8 @@ class Aarch64RMESbsaRefMachine(QemuSystemTest): # ./build.sh && ./archive_artifacts.sh out.tar.xz ASSET_RME_STACK_SBSA = Asset( ('https://github.com/pbo-linaro/qemu-linux-stack/' - 'releases/download/build/rme_sbsa_release-a7f02cf.tar.xz'), - '27d8400b11befb828d6db0cab97e7ae102d0992c928d3dfbf38b24b6cf6c324c') + 'releases/download/build/rme_sbsa_release-6a2dfc5.tar.xz'), + '5adba482aa069912292a8da746c6b21268224d9d81c97fe7c0bed690579ebdcb') # This tests the FEAT_RME cpu implementation, by booting a VM supporting it, # and launching a nested VM using it. diff --git a/tests/functional/aarch64/test_rme_virt.py b/tests/functional/aarch64/test_rme_virt.py index bb603aaa26c6f..5e23773f93d77 100755 --- a/tests/functional/aarch64/test_rme_virt.py +++ b/tests/functional/aarch64/test_rme_virt.py @@ -23,8 +23,8 @@ class Aarch64RMEVirtMachine(QemuSystemTest): # ./build.sh && ./archive_artifacts.sh out.tar.xz ASSET_RME_STACK_VIRT = Asset( ('https://github.com/pbo-linaro/qemu-linux-stack/' - 'releases/download/build/rme_release-86101e5.tar.xz'), - 'e42fef8439badb52a071ac446fc33cff4cb7d61314c7a28fdbe61a11e1faad3a') + 'releases/download/build/rme_release-56bc99e.tar.xz'), + '0e3dc6b8a4b828dbae09c951a40dcb710eded084b32432b50c69cf4173ffa4be') # This tests the FEAT_RME cpu implementation, by booting a VM supporting it, # and launching a nested VM using it. From c817b325c16bee80a130e30c756021da95745b9e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:02 -0700 Subject: [PATCH 1444/1794] target/arm: Add isar feature test for FEAT_S1PIE, FEAT_S2PIE Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index f59c18b6ef6cd..f3e90408f7b7a 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -1349,6 +1349,16 @@ static inline bool isar_feature_aa64_sctlr2(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64MMFR3, SCTLRX) != 0; } +static inline bool isar_feature_aa64_s1pie(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64MMFR3, S1PIE) != 0; +} + +static inline bool isar_feature_aa64_s2pie(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64MMFR3, S2PIE) != 0; +} + static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64DFR0, PMUVER) >= 4 && From 48669f526deae089f23f75638819e7bb90c57d32 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:03 -0700 Subject: [PATCH 1445/1794] target/arm: Enable TCR2_ELx.PIE Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index b7bf45afc13c0..c9ebdf144e341 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -5902,8 +5902,12 @@ static CPAccessResult tcr2_el1_access(CPUARMState *env, const ARMCPRegInfo *ri, static void tcr2_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { + ARMCPU *cpu = env_archcpu(env); uint64_t valid_mask = 0; + if (cpu_isar_feature(aa64_s1pie, cpu)) { + valid_mask |= TCR2_PIE; + } value &= valid_mask; raw_write(env, ri, value); } @@ -5911,8 +5915,12 @@ static void tcr2_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, static void tcr2_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { + ARMCPU *cpu = env_archcpu(env); uint64_t valid_mask = 0; + if (cpu_isar_feature(aa64_s1pie, cpu)) { + valid_mask |= TCR2_PIE; + } value &= valid_mask; raw_write(env, ri, value); } From 0277e0652b663770bb42113ed3fea1ea0dfeaf19 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:04 -0700 Subject: [PATCH 1446/1794] target/arm: Implement PIR_ELx, PIRE0_ELx, S2PIR_EL2 registers Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpregs.h | 2 ++ target/arm/cpu.c | 4 +++ target/arm/cpu.h | 4 +++ target/arm/helper.c | 69 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 57fde5f57ae30..f48c4df30fbac 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -781,6 +781,8 @@ typedef enum FGTBit { DO_BIT(HFGRTR, ERRIDR_EL1), DO_REV_BIT(HFGRTR, NSMPRI_EL1), DO_REV_BIT(HFGRTR, NTPIDR2_EL0), + DO_REV_BIT(HFGRTR, NPIRE0_EL1), + DO_REV_BIT(HFGRTR, NPIR_EL1), /* Trap bits in HDFGRTR_EL2 / HDFGWTR_EL2, starting from bit 0. */ DO_BIT(HDFGRTR, DBGBCRN_EL1), diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 30e29fd3153a2..9bca1b8eae511 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -641,6 +641,10 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el) if (cpu_isar_feature(aa64_sctlr2, cpu)) { env->cp15.scr_el3 |= SCR_SCTLR2EN; } + if (cpu_isar_feature(aa64_s1pie, cpu) || + cpu_isar_feature(aa64_s2pie, cpu)) { + env->cp15.scr_el3 |= SCR_PIEN; + } } if (target_el == 2) { diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 41414ac22b87b..c9ea160d03550 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -368,6 +368,9 @@ typedef struct CPUArchState { uint64_t tcr2_el[3]; uint64_t vtcr_el2; /* Virtualization Translation Control. */ uint64_t vstcr_el2; /* Secure Virtualization Translation Control. */ + uint64_t pir_el[4]; /* PIRE0_EL1, PIR_EL1, PIR_EL2, PIR_EL3 */ + uint64_t pire0_el2; + uint64_t s2pir_el2; uint32_t c2_data; /* MPU data cacheable bits. */ uint32_t c2_insn; /* MPU instruction cacheable bits. */ union { /* MMU domain access control register @@ -1720,6 +1723,7 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_ENTP2 (1ULL << 41) #define SCR_TCR2EN (1ULL << 43) #define SCR_SCTLR2EN (1ULL << 44) +#define SCR_PIEN (1ULL << 45) #define SCR_GPF (1ULL << 48) #define SCR_NSE (1ULL << 62) diff --git a/target/arm/helper.c b/target/arm/helper.c index c9ebdf144e341..20a189ef4f7a3 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -770,6 +770,10 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) if (cpu_isar_feature(aa64_sctlr2, cpu)) { valid_mask |= SCR_SCTLR2EN; } + if (cpu_isar_feature(aa64_s1pie, cpu) || + cpu_isar_feature(aa64_s2pie, cpu)) { + valid_mask |= SCR_PIEN; + } } else { valid_mask &= ~(SCR_RW | SCR_ST); if (cpu_isar_feature(aa32_ras, cpu)) { @@ -5941,6 +5945,64 @@ static const ARMCPRegInfo tcr2_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.tcr2_el[2]) }, }; +static CPAccessResult pien_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_feature(env, ARM_FEATURE_EL3) + && !(env->cp15.scr_el3 & SCR_PIEN) + && arm_current_el(env) < 3) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +static CPAccessResult pien_el1_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + CPAccessResult ret = access_tvm_trvm(env, ri, isread); + if (ret == CP_ACCESS_OK) { + ret = pien_access(env, ri, isread); + } + return ret; +} + +static const ARMCPRegInfo s1pie_reginfo[] = { + { .name = "PIR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .opc2 = 3, .crn = 10, .crm = 2, + .access = PL1_RW, .accessfn = pien_el1_access, + .fgt = FGT_NPIR_EL1, .nv2_redirect_offset = 0x2a0 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 10, 2, 3), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 10, 2, 3), + .fieldoffset = offsetof(CPUARMState, cp15.pir_el[1]) }, + { .name = "PIR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 3, .crn = 10, .crm = 2, + .access = PL2_RW, .accessfn = pien_access, + .fieldoffset = offsetof(CPUARMState, cp15.pir_el[2]) }, + { .name = "PIR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .opc2 = 3, .crn = 10, .crm = 2, + .access = PL3_RW, + .fieldoffset = offsetof(CPUARMState, cp15.pir_el[3]) }, + { .name = "PIRE0_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .opc2 = 2, .crn = 10, .crm = 2, + .access = PL1_RW, .accessfn = pien_el1_access, + .fgt = FGT_NPIRE0_EL1, .nv2_redirect_offset = 0x290 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 10, 2, 2), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 10, 2, 2), + .fieldoffset = offsetof(CPUARMState, cp15.pir_el[0]) }, + { .name = "PIRE0_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 2, .crn = 10, .crm = 2, + .access = PL2_RW, .accessfn = pien_access, + .fieldoffset = offsetof(CPUARMState, cp15.pire0_el2) }, +}; + +static const ARMCPRegInfo s2pie_reginfo[] = { + { .name = "S2PIR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 5, .crn = 10, .crm = 2, + .access = PL2_RW, .accessfn = pien_access, + .nv2_redirect_offset = 0x2b0, + .fieldoffset = offsetof(CPUARMState, cp15.s2pir_el2) }, +}; + void register_cp_regs_for_features(ARMCPU *cpu) { /* Register all the coprocessor registers based on feature bits */ @@ -7173,6 +7235,13 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, tcr2_reginfo); } + if (cpu_isar_feature(aa64_s1pie, cpu)) { + define_arm_cp_regs(cpu, s1pie_reginfo); + } + if (cpu_isar_feature(aa64_s2pie, cpu)) { + define_arm_cp_regs(cpu, s2pie_reginfo); + } + if (cpu_isar_feature(any_predinv, cpu)) { define_arm_cp_regs(cpu, predinv_reginfo); } From 26c7b95eb1342907c569cc6da7e27ded0959e4fe Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:05 -0700 Subject: [PATCH 1447/1794] target/arm: Force HPD for stage2 translations Stage2 translations do not have hierarchial permissions. Setting HPD means we can eliminate an extra check against regime_is_stage2. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 7 +++++-- target/arm/ptw.c | 24 +++++++++++++----------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 20a189ef4f7a3..b7bc27436f702 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9397,8 +9397,11 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, tsz = extract32(tcr, 0, 6); gran = tg0_to_gran_size(extract32(tcr, 14, 2)); if (stage2) { - /* VTCR_EL2 */ - hpd = false; + /* + * Stage2 does not have hierarchical permissions. + * Thus disabling them makes things easier during ptw. + */ + hpd = true; } else { hpd = extract32(tcr, 24, 1); } diff --git a/target/arm/ptw.c b/target/arm/ptw.c index e03657f309e94..26570231205c9 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -1507,8 +1507,12 @@ static ARMVAParameters aa32_va_parameters(CPUARMState *env, uint32_t va, } tsz = sextract32(tcr, 0, 4) + 8; select = 0; - hpd = false; epd = false; + /* + * Stage2 does not have hierarchical permissions. + * Thus disabling them makes things easier during ptw. + */ + hpd = true; } else if (el == 2) { /* HTCR */ tsz = extract32(tcr, 0, 3); @@ -2014,16 +2018,14 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, * except NSTable (which we have already handled). */ attrs = new_descriptor & (MAKE_64BIT_MASK(2, 10) | MAKE_64BIT_MASK(50, 14)); - if (!regime_is_stage2(mmu_idx)) { - if (!param.hpd) { - attrs |= extract64(tableattrs, 0, 2) << 53; /* XN, PXN */ - /* - * The sense of AP[1] vs APTable[0] is reversed, as APTable[0] == 1 - * means "force PL1 access only", which means forcing AP[1] to 0. - */ - attrs &= ~(extract64(tableattrs, 2, 1) << 6); /* !APT[0] => AP[1] */ - attrs |= extract32(tableattrs, 3, 1) << 7; /* APT[1] => AP[2] */ - } + if (!param.hpd) { + attrs |= extract64(tableattrs, 0, 2) << 53; /* XN, PXN */ + /* + * The sense of AP[1] vs APTable[0] is reversed, as APTable[0] == 1 + * means "force PL1 access only", which means forcing AP[1] to 0. + */ + attrs &= ~(extract64(tableattrs, 2, 1) << 6); /* !APT[0] => AP[1] */ + attrs |= extract32(tableattrs, 3, 1) << 7; /* APT[1] => AP[2] */ } ap = extract32(attrs, 6, 2); From 67f3eda009d9af5afb64886cbadc01454fd987a5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:06 -0700 Subject: [PATCH 1448/1794] target/arm: Cache NV1 early in get_phys_addr_lpae We were not using the correct security space in the existing call to nv_nv1_enabled, because it may have been modified for NSTable. Cache it early, as we will shortly need it elsewhere as well. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 26570231205c9..e1515675eb9c2 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -79,6 +79,8 @@ typedef struct S1Translate { * may be suppressed for debug or AT insns. */ uint8_t in_prot_check; + /* Cached EffectiveHCR_EL2_NVx() bit */ + bool in_nv1; bool out_rw; bool out_be; ARMSecuritySpace out_space; @@ -1677,12 +1679,6 @@ static bool lpae_block_desc_valid(ARMCPU *cpu, bool ds, } } -static bool nv_nv1_enabled(CPUARMState *env, S1Translate *ptw) -{ - uint64_t hcr = arm_hcr_el2_eff_secstate(env, ptw->in_space); - return (hcr & (HCR_NV | HCR_NV1)) == (HCR_NV | HCR_NV1); -} - /** * get_phys_addr_lpae: perform one stage of page table walk, LPAE format * @@ -1734,6 +1730,16 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, !arm_el_is_aa64(env, 1)); level = 0; + /* + * Cache NV1 before we adjust ptw->in_space for NSTable. + * Note that this is only relevant for EL1&0, and that + * computing it would assert for ARMSS_Root. + */ + if (el == 1) { + uint64_t hcr = arm_hcr_el2_eff_secstate(env, ptw->in_space); + ptw->in_nv1 = (hcr & (HCR_NV | HCR_NV1)) == (HCR_NV | HCR_NV1); + } + /* * If TxSZ is programmed to a value larger than the maximum, * or smaller than the effective minimum, it is IMPLEMENTATION @@ -2109,7 +2115,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, xn = extract64(attrs, 54, 1); pxn = extract64(attrs, 53, 1); - if (el == 1 && nv_nv1_enabled(env, ptw)) { + if (el == 1 && ptw->in_nv1) { /* * With FEAT_NV, when HCR_EL2.{NV,NV1} == {1,1}, the block/page * descriptor bit 54 holds PXN, 53 is RES0, and the effective value From 1952f58f984a36bb30b9780ed0257e6879252fcd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:07 -0700 Subject: [PATCH 1449/1794] target/arm: Populate PIE in aa64_va_parameters Select the PIE bit for the translation regime. With PIE, the PTE layout changes, forcing HPD. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 19 ++++++++++++++++++- target/arm/internals.h | 3 ++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index b7bc27436f702..37865bf70eb03 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9386,11 +9386,12 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, bool el1_is_aa32) { uint64_t tcr = regime_tcr(env, mmu_idx); - bool epd, hpd, tsz_oob, ds, ha, hd; + bool epd, hpd, tsz_oob, ds, ha, hd, pie = false; int select, tsz, tbi, max_tsz, min_tsz, ps, sh; ARMGranuleSize gran; ARMCPU *cpu = env_archcpu(env); bool stage2 = regime_is_stage2(mmu_idx); + int r_el = regime_el(env, mmu_idx); if (!regime_has_2_ranges(mmu_idx)) { select = 0; @@ -9402,8 +9403,17 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, * Thus disabling them makes things easier during ptw. */ hpd = true; + pie = extract64(tcr, 36, 1) && cpu_isar_feature(aa64_s2pie, cpu); } else { hpd = extract32(tcr, 24, 1); + if (r_el == 3) { + pie = (extract64(tcr, 35, 1) + && cpu_isar_feature(aa64_s1pie, cpu)); + } else { + pie = ((env->cp15.tcr2_el[2] & TCR2_PIE) + && (!arm_feature(env, ARM_FEATURE_EL3) + || (env->cp15.scr_el3 & SCR_TCR2EN))); + } } epd = false; sh = extract32(tcr, 12, 2); @@ -9443,7 +9453,13 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, regime_is_user(env, mmu_idx)) { epd = true; } + + pie = ((env->cp15.tcr2_el[r_el] & TCR2_PIE) + && (!arm_feature(env, ARM_FEATURE_EL3) + || (env->cp15.scr_el3 & SCR_TCR2EN)) + && (r_el == 2 || (arm_hcrx_el2_eff(env) & HCRX_TCR2EN))); } + hpd |= pie; gran = sanitize_gran_size(cpu, gran, stage2); @@ -9522,6 +9538,7 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, .ha = ha, .hd = ha && hd, .gran = gran, + .pie = pie, }; } diff --git a/target/arm/internals.h b/target/arm/internals.h index 1d958dbf685f8..face1019f52a4 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1491,7 +1491,7 @@ static inline int arm_granule_bits(ARMGranuleSize gran) /* * Parameters of a given virtual address, as extracted from the - * translation control register (TCR) for a given regime. + * translation controls for a given regime. */ typedef struct ARMVAParameters { unsigned tsz : 8; @@ -1506,6 +1506,7 @@ typedef struct ARMVAParameters { bool ha : 1; bool hd : 1; ARMGranuleSize gran : 2; + bool pie : 1; } ARMVAParameters; /** From 1cbaf63a109db8529589b0660df06ea69fd07b05 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:08 -0700 Subject: [PATCH 1450/1794] target/arm: Implement get_S1prot_indirect This approximately corresponds to AArch64.S1IndirectBasePermissions and the tail of AArch64.S1ComputePermissions which applies WXN. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 161 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 139 insertions(+), 22 deletions(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index e1515675eb9c2..5913556077694 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -1484,6 +1484,106 @@ static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64, return prot_rw | PAGE_EXEC; } +/* Extra page permission bits, during get_S1prot_indirect only. */ +#define PAGE_GCS (1 << 3) +#define PAGE_WXN (1 << 4) +#define PAGE_OVERLAY (1 << 5) +QEMU_BUILD_BUG_ON(PAGE_RWX & (PAGE_GCS | PAGE_WXN | PAGE_OVERLAY)); + +static int get_S1prot_indirect(CPUARMState *env, S1Translate *ptw, + ARMMMUIdx mmu_idx, int pi_index, int po_index, + ARMSecuritySpace in_pa, ARMSecuritySpace out_pa) +{ + static const uint8_t perm_table[16] = { + /* 0 */ PAGE_OVERLAY, /* no access */ + /* 1 */ PAGE_OVERLAY | PAGE_READ, + /* 2 */ PAGE_OVERLAY | PAGE_EXEC, + /* 3 */ PAGE_OVERLAY | PAGE_READ | PAGE_EXEC, + /* 4 */ PAGE_OVERLAY, /* reserved */ + /* 5 */ PAGE_OVERLAY | PAGE_READ | PAGE_WRITE, + /* 6 */ PAGE_OVERLAY | PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_WXN, + /* 7 */ PAGE_OVERLAY | PAGE_READ | PAGE_WRITE | PAGE_EXEC, + /* 8 */ PAGE_READ, + /* 9 */ PAGE_READ | PAGE_GCS, + /* A */ PAGE_READ | PAGE_EXEC, + /* B */ 0, /* reserved */ + /* C */ PAGE_READ | PAGE_WRITE, + /* D */ 0, /* reserved */ + /* E */ PAGE_READ | PAGE_WRITE | PAGE_EXEC, + /* F */ 0, /* reserved */ + }; + + uint32_t el = regime_el(env, mmu_idx); + uint64_t pir = env->cp15.pir_el[el]; + uint64_t pire0 = 0; + int perm; + + if (el < 3) { + if (arm_feature(env, ARM_FEATURE_EL3) + && !(env->cp15.scr_el3 & SCR_PIEN)) { + pir = 0; + } else if (el == 2) { + pire0 = env->cp15.pire0_el2; + } else if (!ptw->in_nv1) { + pire0 = env->cp15.pir_el[0]; + } + } + perm = perm_table[extract64(pir, pi_index * 4, 4)]; + + if (regime_has_2_ranges(mmu_idx)) { + int p_perm = perm; + int u_perm = perm_table[extract64(pire0, pi_index * 4, 4)]; + + if ((p_perm & (PAGE_EXEC | PAGE_GCS)) && + (u_perm & (PAGE_WRITE | PAGE_GCS))) { + p_perm &= ~(PAGE_RWX | PAGE_GCS); + u_perm &= ~(PAGE_RWX | PAGE_GCS); + } + if ((u_perm & (PAGE_RWX | PAGE_GCS)) && regime_is_pan(env, mmu_idx)) { + p_perm &= ~(PAGE_READ | PAGE_WRITE); + } + perm = regime_is_user(env, mmu_idx) ? u_perm : p_perm; + } + + if (in_pa != out_pa) { + switch (in_pa) { + case ARMSS_Root: + /* + * R_ZWRVD: permission fault for insn fetched from non-Root, + * I_WWBFB: SIF has no effect in EL3. + */ + perm &= ~(PAGE_EXEC | PAGE_GCS); + break; + case ARMSS_Realm: + /* + * R_PKTDS: permission fault for insn fetched from non-Realm, + * for Realm EL2 or EL2&0. The corresponding fault for EL1&0 + * happens during any stage2 translation. + */ + if (el == 2) { + perm &= ~(PAGE_EXEC | PAGE_GCS); + } + break; + case ARMSS_Secure: + if (env->cp15.scr_el3 & SCR_SIF) { + perm &= ~(PAGE_EXEC | PAGE_GCS); + } + break; + default: + /* Input NonSecure must have output NonSecure. */ + g_assert_not_reached(); + } + } + + if (perm & PAGE_WXN) { + perm &= ~PAGE_EXEC; + } + + /* TODO: FEAT_GCS */ + + return perm & PAGE_RWX; +} + static ARMVAParameters aa32_va_parameters(CPUARMState *env, uint32_t va, ARMMMUIdx mmu_idx) { @@ -1713,7 +1813,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, int32_t stride; int addrsize, inputsize, outputsize; uint64_t tcr = regime_tcr(env, mmu_idx); - int ap, xn, pxn; + int ap; uint32_t el = regime_el(env, mmu_idx); uint64_t descaddrmask; bool aarch64 = arm_el_is_aa64(env, el); @@ -2047,7 +2147,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, out_space = ARMSS_NonSecure; result->f.prot = get_S2prot_noexecute(ap); } else { - xn = extract64(attrs, 53, 2); + int xn = extract64(attrs, 53, 2); result->f.prot = get_S2prot(env, ap, xn, ptw->in_s1_is_el0); } @@ -2063,7 +2163,6 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, int nse, ns = extract32(attrs, 5, 1); uint8_t attrindx; uint64_t mair; - int user_rw, prot_rw; switch (out_space) { case ARMSS_Root: @@ -2112,29 +2211,47 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, default: g_assert_not_reached(); } - xn = extract64(attrs, 54, 1); - pxn = extract64(attrs, 53, 1); - if (el == 1 && ptw->in_nv1) { + if (param.pie) { + int pi = extract64(attrs, 6, 1) + | (extract64(attrs, 51, 1) << 1) + | (extract64(attrs, 53, 2) << 2); + int po = extract64(attrs, 60, 3); /* - * With FEAT_NV, when HCR_EL2.{NV,NV1} == {1,1}, the block/page - * descriptor bit 54 holds PXN, 53 is RES0, and the effective value - * of UXN is 0. Similarly for bits 59 and 60 in table descriptors - * (which we have already folded into bits 53 and 54 of attrs). - * AP[1] (descriptor bit 6, our ap bit 0) is treated as 0. - * Similarly, APTable[0] from the table descriptor is treated as 0; - * we already folded this into AP[1] and squashing that to 0 does - * the right thing. + * Note that we modified ptw->in_space earlier for NSTable, but + * result->f.attrs retains a copy of the original security space. */ - pxn = xn; - xn = 0; - ap &= ~1; - } + result->f.prot = get_S1prot_indirect(env, ptw, mmu_idx, pi, po, + result->f.attrs.space, + out_space); + } else { + int xn = extract64(attrs, 54, 1); + int pxn = extract64(attrs, 53, 1); + int user_rw, prot_rw; - user_rw = simple_ap_to_rw_prot_is_user(ap, true); - prot_rw = simple_ap_to_rw_prot_is_user(ap, false); - result->f.prot = get_S1prot(env, mmu_idx, aarch64, user_rw, prot_rw, - xn, pxn, ptw->in_space, out_space); + if (el == 1 && ptw->in_nv1) { + /* + * With FEAT_NV, when HCR_EL2.{NV,NV1} == {1,1}, + * the block/page descriptor bit 54 holds PXN, + * 53 is RES0, and the effective value of UXN is 0. + * Similarly for bits 59 and 60 in table descriptors + * (which we have already folded into bits 53 and 54 of attrs). + * AP[1] (descriptor bit 6, our ap bit 0) is treated as 0. + * Similarly, APTable[0] from the table descriptor is treated + * as 0; we already folded this into AP[1] and squashing + * that to 0 does the right thing. + */ + pxn = xn; + xn = 0; + ap &= ~1; + } + + user_rw = simple_ap_to_rw_prot_is_user(ap, true); + prot_rw = simple_ap_to_rw_prot_is_user(ap, false); + result->f.prot = get_S1prot(env, mmu_idx, aarch64, + user_rw, prot_rw, xn, pxn, + ptw->in_space, out_space); + } /* Index into MAIR registers for cache attributes */ attrindx = extract32(attrs, 2, 3); From a811c5dafb728087fa6f222ad1322ec26d080fdd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:09 -0700 Subject: [PATCH 1451/1794] target/arm: Implement get_S2prot_indirect Move the stage2 permissions for normal accesses to GetPhysAddrResult.s2prot. Put the stage2 permissions for page table walking in CPUTLBEntryFull.prot. This allows the permission checks in S1_ptw_translate and arm_casq_ptw to see the right permission. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/internals.h | 7 ++++ target/arm/ptw.c | 81 +++++++++++++++++++++++++++++++----------- 2 files changed, 68 insertions(+), 20 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index face1019f52a4..22947c4b78a71 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1577,6 +1577,13 @@ typedef struct ARMCacheAttrs { typedef struct GetPhysAddrResult { CPUTLBEntryFull f; ARMCacheAttrs cacheattrs; + /* + * For ARMMMUIdx_Stage2*, the protection installed into f.prot + * is the result for AccessType_TTW, i.e. the page table walk itself. + * The protection installed info s2prot is the one to be merged + * with the stage1 protection. + */ + int s2prot; } GetPhysAddrResult; /** diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 5913556077694..2e026a7c59771 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -1316,7 +1316,7 @@ static bool get_phys_addr_v6(CPUARMState *env, S1Translate *ptw, * @xn: XN (execute-never) bits * @s1_is_el0: true if this is S2 of an S1+2 walk for EL0 */ -static int get_S2prot_noexecute(int s2ap) +static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0) { int prot = 0; @@ -1326,12 +1326,6 @@ static int get_S2prot_noexecute(int s2ap) if (s2ap & 2) { prot |= PAGE_WRITE; } - return prot; -} - -static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0) -{ - int prot = get_S2prot_noexecute(s2ap); if (cpu_isar_feature(any_tts2uxn, env_archcpu(env))) { switch (xn) { @@ -1363,6 +1357,44 @@ static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0) return prot; } +static int get_S2prot_indirect(CPUARMState *env, GetPhysAddrResult *result, + int pi_index, int po_index, bool s1_is_el0) +{ + /* Last index is (priv, unpriv, ttw) */ + static const uint8_t perm_table[16][3] = { + /* 0 */ { 0, 0, 0 }, /* no access */ + /* 1 */ { 0, 0, 0 }, /* reserved */ + /* 2 */ { PAGE_READ, PAGE_READ, PAGE_READ | PAGE_WRITE }, + /* 3 */ { PAGE_READ, PAGE_READ, PAGE_READ | PAGE_WRITE }, + /* 4 */ { PAGE_WRITE, PAGE_WRITE, 0 }, + /* 5 */ { 0, 0, 0 }, /* reserved */ + /* 6 */ { PAGE_READ, PAGE_READ, PAGE_READ | PAGE_WRITE }, + /* 7 */ { PAGE_READ, PAGE_READ, PAGE_READ | PAGE_WRITE }, + /* 8 */ { PAGE_READ, PAGE_READ, PAGE_READ }, + /* 9 */ { PAGE_READ, PAGE_READ | PAGE_EXEC, PAGE_READ }, + /* A */ { PAGE_READ | PAGE_EXEC, PAGE_READ, PAGE_READ }, + /* B */ { PAGE_READ | PAGE_EXEC, PAGE_READ | PAGE_EXEC, PAGE_READ }, + /* C */ { PAGE_READ | PAGE_WRITE, + PAGE_READ | PAGE_WRITE, + PAGE_READ | PAGE_WRITE }, + /* D */ { PAGE_READ | PAGE_WRITE, + PAGE_READ | PAGE_WRITE | PAGE_EXEC, + PAGE_READ | PAGE_WRITE }, + /* E */ { PAGE_READ | PAGE_WRITE | PAGE_EXEC, + PAGE_READ | PAGE_WRITE, + PAGE_READ | PAGE_WRITE }, + /* F */ { PAGE_READ | PAGE_WRITE | PAGE_EXEC, + PAGE_READ | PAGE_WRITE | PAGE_EXEC, + PAGE_READ | PAGE_WRITE }, + }; + + uint64_t pir = (env->cp15.scr_el3 & SCR_PIEN ? env->cp15.s2pir_el2 : 0); + int s2pi = extract64(pir, pi_index * 4, 4); + + result->f.prot = perm_table[s2pi][2]; + return perm_table[s2pi][s1_is_el0]; +} + /* * Translate section/page access permissions to protection flags * @env: CPUARMState @@ -1813,7 +1845,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, int32_t stride; int addrsize, inputsize, outputsize; uint64_t tcr = regime_tcr(env, mmu_idx); - int ap; + int ap, prot; uint32_t el = regime_el(env, mmu_idx); uint64_t descaddrmask; bool aarch64 = arm_el_is_aa64(env, el); @@ -2137,6 +2169,18 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, ap = extract32(attrs, 6, 2); out_space = ptw->cur_space; if (regime_is_stage2(mmu_idx)) { + if (param.pie) { + int pi = extract64(attrs, 6, 1) + | (extract64(attrs, 51, 1) << 1) + | (extract64(attrs, 53, 2) << 2); + int po = extract64(attrs, 60, 3); + prot = get_S2prot_indirect(env, result, pi, po, ptw->in_s1_is_el0); + } else { + int xn = extract64(attrs, 53, 2); + prot = get_S2prot(env, ap, xn, ptw->in_s1_is_el0); + /* Install TTW permissions in f.prot. */ + result->f.prot = prot & (PAGE_READ | PAGE_WRITE); + } /* * R_GYNXY: For stage2 in Realm security state, bit 55 is NS. * The bit remains ignored for other security states. @@ -2145,11 +2189,9 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, */ if (out_space == ARMSS_Realm && extract64(attrs, 55, 1)) { out_space = ARMSS_NonSecure; - result->f.prot = get_S2prot_noexecute(ap); - } else { - int xn = extract64(attrs, 53, 2); - result->f.prot = get_S2prot(env, ap, xn, ptw->in_s1_is_el0); + prot &= ~PAGE_EXEC; } + result->s2prot = prot; result->cacheattrs.is_s2_format = true; result->cacheattrs.attrs = extract32(attrs, 2, 4); @@ -2221,9 +2263,8 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, * Note that we modified ptw->in_space earlier for NSTable, but * result->f.attrs retains a copy of the original security space. */ - result->f.prot = get_S1prot_indirect(env, ptw, mmu_idx, pi, po, - result->f.attrs.space, - out_space); + prot = get_S1prot_indirect(env, ptw, mmu_idx, pi, po, + result->f.attrs.space, out_space); } else { int xn = extract64(attrs, 54, 1); int pxn = extract64(attrs, 53, 1); @@ -2248,10 +2289,10 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, user_rw = simple_ap_to_rw_prot_is_user(ap, true); prot_rw = simple_ap_to_rw_prot_is_user(ap, false); - result->f.prot = get_S1prot(env, mmu_idx, aarch64, - user_rw, prot_rw, xn, pxn, - ptw->in_space, out_space); + prot = get_S1prot(env, mmu_idx, aarch64, user_rw, prot_rw, + xn, pxn, ptw->in_space, out_space); } + result->f.prot = prot; /* Index into MAIR registers for cache attributes */ attrindx = extract32(attrs, 2, 3); @@ -2297,7 +2338,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, result->f.tlb_fill_flags = 0; } - if (ptw->in_prot_check & ~result->f.prot) { + if (ptw->in_prot_check & ~prot) { fi->type = ARMFault_Permission; goto do_fault; } @@ -3495,7 +3536,7 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, fi->s2addr = ipa; /* Combine the S1 and S2 perms. */ - result->f.prot &= s1_prot; + result->f.prot = s1_prot & result->s2prot; /* If S2 fails, return early. */ if (ret) { From 5a3197bc73040e657017d08445918c88c542f7d4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:10 -0700 Subject: [PATCH 1452/1794] target/arm: Expand CPUARMState.exception.syndrome to 64 bits This will be used for storing the ISS2 portion of the ESR_ELx registers in aarch64 state. Re-order the fsr member to eliminate two structure holes. Drop the comment about "if we implement EL2" since we have already done so. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.h | 7 ++----- target/arm/helper.c | 2 +- target/arm/machine.c | 32 +++++++++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c9ea160d03550..04b57f1dc5abd 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -633,13 +633,10 @@ typedef struct CPUArchState { * entry process. */ struct { - uint32_t syndrome; /* AArch64 format syndrome register */ - uint32_t fsr; /* AArch32 format fault status register info */ + uint64_t syndrome; /* AArch64 format syndrome register */ uint64_t vaddress; /* virtual addr associated with exception, if any */ + uint32_t fsr; /* AArch32 format fault status register info */ uint32_t target_el; /* EL the exception should be targeted for */ - /* If we implement EL2 we will also need to store information - * about the intermediate physical address for stage 2 faults. - */ } exception; /* Information associated with an SError */ diff --git a/target/arm/helper.c b/target/arm/helper.c index 37865bf70eb03..bd5321348accb 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9196,7 +9196,7 @@ void arm_cpu_do_interrupt(CPUState *cs) new_el); if (qemu_loglevel_mask(CPU_LOG_INT) && !excp_is_internal(cs->exception_index)) { - qemu_log_mask(CPU_LOG_INT, "...with ESR 0x%x/0x%" PRIx32 "\n", + qemu_log_mask(CPU_LOG_INT, "...with ESR 0x%x/0x%" PRIx64 "\n", syn_get_ec(env->exception.syndrome), env->exception.syndrome); } diff --git a/target/arm/machine.c b/target/arm/machine.c index 6666a0c50c455..ce20b46f50f00 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -848,6 +848,23 @@ static const VMStateInfo vmstate_powered_off = { .put = put_power, }; +static bool syndrome64_needed(void *opaque) +{ + ARMCPU *cpu = opaque; + return cpu->env.exception.syndrome > UINT32_MAX; +} + +static const VMStateDescription vmstate_syndrome64 = { + .name = "cpu/syndrome64", + .version_id = 1, + .minimum_version_id = 1, + .needed = syndrome64_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(env.exception.syndrome, ARMCPU), + VMSTATE_END_OF_LIST() + }, +}; + static int cpu_pre_save(void *opaque) { ARMCPU *cpu = opaque; @@ -1065,7 +1082,19 @@ const VMStateDescription vmstate_arm_cpu = { VMSTATE_UINT64(env.exclusive_val, ARMCPU), VMSTATE_UINT64(env.exclusive_high, ARMCPU), VMSTATE_UNUSED(sizeof(uint64_t)), - VMSTATE_UINT32(env.exception.syndrome, ARMCPU), + /* + * If any bits are set in the upper 32 bits of syndrome, + * then the cpu/syndrome64 subsection will override this + * with the full 64 bit state. + */ + { + .name = "env.exception.syndrome", + .version_id = 0, + .size = sizeof(uint32_t), + .info = &vmstate_info_uint32, + .flags = VMS_SINGLE, + .offset = offsetoflow32(ARMCPU, env.exception.syndrome), + }, VMSTATE_UINT32(env.exception.fsr, ARMCPU), VMSTATE_UINT64(env.exception.vaddress, ARMCPU), VMSTATE_TIMER_PTR(gt_timer[GTIMER_PHYS], ARMCPU), @@ -1098,6 +1127,7 @@ const VMStateDescription vmstate_arm_cpu = { &vmstate_serror, &vmstate_irq_line_state, &vmstate_wfxt_timer, + &vmstate_syndrome64, NULL } }; From 9190d68e83dc13c08b50b538f6473894b1b7b024 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:11 -0700 Subject: [PATCH 1453/1794] target/arm: Expand syndrome parameter to raise_exception* Prepare for raising exceptions with 64-bit syndromes. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/internals.h | 6 +++--- target/arm/tcg-stubs.c | 2 +- target/arm/tcg/op_helper.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index 22947c4b78a71..b59650959ec6b 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -304,14 +304,14 @@ FIELD(CNTHCTL, CNTPMASK, 19, 1) * and never returns because we will longjump back up to the CPU main loop. */ G_NORETURN void raise_exception(CPUARMState *env, uint32_t excp, - uint32_t syndrome, uint32_t target_el); + uint64_t syndrome, uint32_t target_el); /* * Similarly, but also use unwinding to restore cpu state. */ G_NORETURN void raise_exception_ra(CPUARMState *env, uint32_t excp, - uint32_t syndrome, uint32_t target_el, - uintptr_t ra); + uint64_t syndrome, uint32_t target_el, + uintptr_t ra); /* * For AArch64, map a given EL to an index in the banked_spsr array. diff --git a/target/arm/tcg-stubs.c b/target/arm/tcg-stubs.c index aac99b2672a29..aeeede806619c 100644 --- a/target/arm/tcg-stubs.c +++ b/target/arm/tcg-stubs.c @@ -16,7 +16,7 @@ void write_v7m_exception(CPUARMState *env, uint32_t new_exc) g_assert_not_reached(); } -void raise_exception_ra(CPUARMState *env, uint32_t excp, uint32_t syndrome, +void raise_exception_ra(CPUARMState *env, uint32_t excp, uint64_t syndrome, uint32_t target_el, uintptr_t ra) { g_assert_not_reached(); diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index 5373e0e998c8d..dd3700dc6f228 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -46,7 +46,7 @@ int exception_target_el(CPUARMState *env) } void raise_exception(CPUARMState *env, uint32_t excp, - uint32_t syndrome, uint32_t target_el) + uint64_t syndrome, uint32_t target_el) { CPUState *cs = env_cpu(env); @@ -70,7 +70,7 @@ void raise_exception(CPUARMState *env, uint32_t excp, cpu_loop_exit(cs); } -void raise_exception_ra(CPUARMState *env, uint32_t excp, uint32_t syndrome, +void raise_exception_ra(CPUARMState *env, uint32_t excp, uint64_t syndrome, uint32_t target_el, uintptr_t ra) { CPUState *cs = env_cpu(env); From cfd363ca4c374ba34673e34467d973255ca009e5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:12 -0700 Subject: [PATCH 1454/1794] target/arm: Implement dirtybit check for PIE Both S1PIE and S2PIE have a bit to make software tracking of dirty pages easier. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-13-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/internals.h | 1 + target/arm/ptw.c | 16 ++++++++++++++++ target/arm/tcg/tlb_helper.c | 12 +++++++++--- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index b59650959ec6b..69eb1df6173ce 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -752,6 +752,7 @@ struct ARMMMUFaultInfo { bool s1ptw; bool s1ns; bool ea; + bool dirtybit; /* FEAT_S1PIE, FEAT_S2PIE */ }; /** diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 2e026a7c59771..21540a1ec3e32 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -2343,6 +2343,22 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, goto do_fault; } + /* S1PIE and S2PIE both have a bit for software dirty page tracking. */ + if (access_type == MMU_DATA_STORE && param.pie) { + /* + * For S1PIE, bit 7 is nDirty and both HA and HD are checked. + * For S2PIE, bit 7 is Dirty and only HD is checked. + */ + bool bit7 = extract64(attrs, 7, 1); + if (regime_is_stage2(mmu_idx) + ? !bit7 && !param.hd + : bit7 && !(param.ha && param.hd)) { + fi->type = ARMFault_Permission; + fi->dirtybit = true; + goto do_fault; + } + } + /* If FEAT_HAFDBS has made changes, update the PTE. */ if (new_descriptor != descriptor) { new_descriptor = arm_casq_ptw(env, descriptor, new_descriptor, ptw, fi); diff --git a/target/arm/tcg/tlb_helper.c b/target/arm/tcg/tlb_helper.c index 23c72a99f5c53..ae2acd67276df 100644 --- a/target/arm/tcg/tlb_helper.c +++ b/target/arm/tcg/tlb_helper.c @@ -24,13 +24,13 @@ bool arm_s1_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) return regime_using_lpae_format(env, mmu_idx); } -static inline uint32_t merge_syn_data_abort(uint32_t template_syn, +static inline uint64_t merge_syn_data_abort(uint32_t template_syn, ARMMMUFaultInfo *fi, unsigned int target_el, bool same_el, bool is_write, int fsc) { - uint32_t syn; + uint64_t syn; /* * ISV is only set for stage-2 data aborts routed to EL2 and @@ -75,6 +75,10 @@ static inline uint32_t merge_syn_data_abort(uint32_t template_syn, /* Merge the runtime syndrome with the template syndrome. */ syn |= template_syn; } + + /* Form ISS2 at the top of the syndrome. */ + syn |= (uint64_t)fi->dirtybit << 37; + return syn; } @@ -176,7 +180,9 @@ void arm_deliver_fault(ARMCPU *cpu, vaddr addr, int target_el = exception_target_el(env); int current_el = arm_current_el(env); bool same_el; - uint32_t syn, exc, fsr, fsc; + uint32_t exc, fsr, fsc; + uint64_t syn; + /* * We know this must be a data or insn abort, and that * env->exception.syndrome contains the template syndrome set From 4983e3ccc9c384c7ae12822945943a0d8bce6537 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:13 -0700 Subject: [PATCH 1455/1794] target/arm: Enable FEAT_S1PIE and FEAT_S2PIE on -cpu max Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-14-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 2 ++ target/arm/tcg/cpu64.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 1aa0a6e4c3915..c779a50570a19 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -123,6 +123,8 @@ the following architecture extensions: - FEAT_RME_GPC2 (RME Granule Protection Check 2 Extension) - FEAT_RNG (Random number generator) - FEAT_RPRES (Increased precision of FRECPE and FRSQRTE) +- FEAT_S1PIE (Stage 1 permission indirections) +- FEAT_S2PIE (Stage 2 permission indirections) - FEAT_S2FWB (Stage 2 forced Write-Back) - FEAT_SB (Speculation Barrier) - FEAT_SCTLR2 (Extension to SCTLR_ELx) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 8c617fe37b2f6..f024db1d29f20 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1327,6 +1327,8 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR3, TCRX, 1); /* FEAT_TCR2 */ t = FIELD_DP64(t, ID_AA64MMFR3, SCTLRX, 1); /* FEAT_SCTLR2 */ t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ + t = FIELD_DP64(t, ID_AA64MMFR3, S1PIE, 1); /* FEAT_S1PIE */ + t = FIELD_DP64(t, ID_AA64MMFR3, S2PIE, 1); /* FEAT_S2PIE */ SET_IDREG(isar, ID_AA64MMFR3, t); t = GET_IDREG(isar, ID_AA64ZFR0); From d182123974c4377757d50ce93108daef687a8951 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:14 -0700 Subject: [PATCH 1456/1794] include/exec/memopidx: Adjust for 32 mmu indexes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20251008215613.300150-15-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- include/exec/memopidx.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/exec/memopidx.h b/include/exec/memopidx.h index eb7f1591a3764..66d9c58b3afcd 100644 --- a/include/exec/memopidx.h +++ b/include/exec/memopidx.h @@ -25,9 +25,10 @@ typedef uint32_t MemOpIdx; static inline MemOpIdx make_memop_idx(MemOp op, unsigned idx) { #ifdef CONFIG_DEBUG_TCG - assert(idx <= 15); + assert(idx <= 31); + assert(clz32(op) >= 5); #endif - return (op << 4) | idx; + return (op << 5) | idx; } /** @@ -38,7 +39,7 @@ static inline MemOpIdx make_memop_idx(MemOp op, unsigned idx) */ static inline MemOp get_memop(MemOpIdx oi) { - return oi >> 4; + return oi >> 5; } /** @@ -49,7 +50,7 @@ static inline MemOp get_memop(MemOpIdx oi) */ static inline unsigned get_mmuidx(MemOpIdx oi) { - return oi & 15; + return oi & 31; } #endif From e1e2f08b431890c757e8dffc760c2719e9577f86 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:15 -0700 Subject: [PATCH 1457/1794] include/hw/core/cpu: Widen MMUIdxMap Widen MMUIdxMap to 32 bits. Do not yet expand NB_MMU_MODES, but widen the map type in preparation. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-16-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- accel/tcg/cputlb.c | 3 --- include/hw/core/cpu.h | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 7214d41cb5d2d..3010dd4f5df5f 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -90,9 +90,6 @@ */ QEMU_BUILD_BUG_ON(sizeof(vaddr) > sizeof(run_on_cpu_data)); -/* We currently can't handle more than 16 bits in the MMUIDX bitmask. - */ -QEMU_BUILD_BUG_ON(NB_MMU_MODES > 16); #define ALL_MMUIDX_BITS ((1 << NB_MMU_MODES) - 1) static inline size_t tlb_n_entries(CPUTLBDescFast *fast) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 4f7026a11918d..d175edb6f8a28 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -201,7 +201,7 @@ struct CPUClass { * Fix the number of mmu modes to 16. */ #define NB_MMU_MODES 16 -typedef uint16_t MMUIdxMap; +typedef uint32_t MMUIdxMap; /* Use a fully associative victim tlb of 8 entries. */ #define CPU_VTLB_SIZE 8 From e886dccc8276f22296582338c30c639a24e45ea3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:16 -0700 Subject: [PATCH 1458/1794] target/arm: Split out mmuidx.h from cpu.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-17-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.h | 207 +----------------------------------------- target/arm/mmuidx.h | 216 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+), 206 deletions(-) create mode 100644 target/arm/mmuidx.h diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 04b57f1dc5abd..6773676973c6a 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -33,6 +33,7 @@ #include "target/arm/multiprocessing.h" #include "target/arm/gtimer.h" #include "target/arm/cpu-sysregs.h" +#include "target/arm/mmuidx.h" #define EXCP_UDEF 1 /* undefined instruction */ #define EXCP_SWI 2 /* software interrupt */ @@ -2301,212 +2302,6 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync); #define TYPE_ARM_HOST_CPU "host-" TYPE_ARM_CPU -/* ARM has the following "translation regimes" (as the ARM ARM calls them): - * - * If EL3 is 64-bit: - * + NonSecure EL1 & 0 stage 1 - * + NonSecure EL1 & 0 stage 2 - * + NonSecure EL2 - * + NonSecure EL2 & 0 (ARMv8.1-VHE) - * + Secure EL1 & 0 stage 1 - * + Secure EL1 & 0 stage 2 (FEAT_SEL2) - * + Secure EL2 (FEAT_SEL2) - * + Secure EL2 & 0 (FEAT_SEL2) - * + Realm EL1 & 0 stage 1 (FEAT_RME) - * + Realm EL1 & 0 stage 2 (FEAT_RME) - * + Realm EL2 (FEAT_RME) - * + EL3 - * If EL3 is 32-bit: - * + NonSecure PL1 & 0 stage 1 - * + NonSecure PL1 & 0 stage 2 - * + NonSecure PL2 - * + Secure PL1 & 0 - * (reminder: for 32 bit EL3, Secure PL1 is *EL3*, not EL1.) - * - * For QEMU, an mmu_idx is not quite the same as a translation regime because: - * 1. we need to split the "EL1 & 0" and "EL2 & 0" regimes into two mmu_idxes, - * because they may differ in access permissions even if the VA->PA map is - * the same - * 2. we want to cache in our TLB the full VA->IPA->PA lookup for a stage 1+2 - * translation, which means that we have one mmu_idx that deals with two - * concatenated translation regimes [this sort of combined s1+2 TLB is - * architecturally permitted] - * 3. we don't need to allocate an mmu_idx to translations that we won't be - * handling via the TLB. The only way to do a stage 1 translation without - * the immediate stage 2 translation is via the ATS or AT system insns, - * which can be slow-pathed and always do a page table walk. - * The only use of stage 2 translations is either as part of an s1+2 - * lookup or when loading the descriptors during a stage 1 page table walk, - * and in both those cases we don't use the TLB. - * 4. we can also safely fold together the "32 bit EL3" and "64 bit EL3" - * translation regimes, because they map reasonably well to each other - * and they can't both be active at the same time. - * 5. we want to be able to use the TLB for accesses done as part of a - * stage1 page table walk, rather than having to walk the stage2 page - * table over and over. - * 6. we need separate EL1/EL2 mmu_idx for handling the Privileged Access - * Never (PAN) bit within PSTATE. - * 7. we fold together most secure and non-secure regimes for A-profile, - * because there are no banked system registers for aarch64, so the - * process of switching between secure and non-secure is - * already heavyweight. - * 8. we cannot fold together Stage 2 Secure and Stage 2 NonSecure, - * because both are in use simultaneously for Secure EL2. - * - * This gives us the following list of cases: - * - * EL0 EL1&0 stage 1+2 (aka NS PL0 PL1&0 stage 1+2) - * EL1 EL1&0 stage 1+2 (aka NS PL1 PL1&0 stage 1+2) - * EL1 EL1&0 stage 1+2 +PAN (aka NS PL1 P1&0 stage 1+2 +PAN) - * EL0 EL2&0 - * EL2 EL2&0 - * EL2 EL2&0 +PAN - * EL2 (aka NS PL2) - * EL3 (aka AArch32 S PL1 PL1&0) - * AArch32 S PL0 PL1&0 (we call this EL30_0) - * AArch32 S PL1 PL1&0 +PAN (we call this EL30_3_PAN) - * Stage2 Secure - * Stage2 NonSecure - * plus one TLB per Physical address space: S, NS, Realm, Root - * - * for a total of 16 different mmu_idx. - * - * R profile CPUs have an MPU, but can use the same set of MMU indexes - * as A profile. They only need to distinguish EL0 and EL1 (and - * EL2 for cores like the Cortex-R52). - * - * M profile CPUs are rather different as they do not have a true MMU. - * They have the following different MMU indexes: - * User - * Privileged - * User, execution priority negative (ie the MPU HFNMIENA bit may apply) - * Privileged, execution priority negative (ditto) - * If the CPU supports the v8M Security Extension then there are also: - * Secure User - * Secure Privileged - * Secure User, execution priority negative - * Secure Privileged, execution priority negative - * - * The ARMMMUIdx and the mmu index value used by the core QEMU TLB code - * are not quite the same -- different CPU types (most notably M profile - * vs A/R profile) would like to use MMU indexes with different semantics, - * but since we don't ever need to use all of those in a single CPU we - * can avoid having to set NB_MMU_MODES to "total number of A profile MMU - * modes + total number of M profile MMU modes". The lower bits of - * ARMMMUIdx are the core TLB mmu index, and the higher bits are always - * the same for any particular CPU. - * Variables of type ARMMUIdx are always full values, and the core - * index values are in variables of type 'int'. - * - * Our enumeration includes at the end some entries which are not "true" - * mmu_idx values in that they don't have corresponding TLBs and are only - * valid for doing slow path page table walks. - * - * The constant names here are patterned after the general style of the names - * of the AT/ATS operations. - * The values used are carefully arranged to make mmu_idx => EL lookup easy. - * For M profile we arrange them to have a bit for priv, a bit for negpri - * and a bit for secure. - */ -#define ARM_MMU_IDX_A 0x10 /* A profile */ -#define ARM_MMU_IDX_NOTLB 0x20 /* does not have a TLB */ -#define ARM_MMU_IDX_M 0x40 /* M profile */ - -/* Meanings of the bits for M profile mmu idx values */ -#define ARM_MMU_IDX_M_PRIV 0x1 -#define ARM_MMU_IDX_M_NEGPRI 0x2 -#define ARM_MMU_IDX_M_S 0x4 /* Secure */ - -#define ARM_MMU_IDX_TYPE_MASK \ - (ARM_MMU_IDX_A | ARM_MMU_IDX_M | ARM_MMU_IDX_NOTLB) -#define ARM_MMU_IDX_COREIDX_MASK 0xf - -typedef enum ARMMMUIdx { - /* - * A-profile. - */ - ARMMMUIdx_E10_0 = 0 | ARM_MMU_IDX_A, - ARMMMUIdx_E20_0 = 1 | ARM_MMU_IDX_A, - ARMMMUIdx_E10_1 = 2 | ARM_MMU_IDX_A, - ARMMMUIdx_E20_2 = 3 | ARM_MMU_IDX_A, - ARMMMUIdx_E10_1_PAN = 4 | ARM_MMU_IDX_A, - ARMMMUIdx_E20_2_PAN = 5 | ARM_MMU_IDX_A, - ARMMMUIdx_E2 = 6 | ARM_MMU_IDX_A, - ARMMMUIdx_E3 = 7 | ARM_MMU_IDX_A, - ARMMMUIdx_E30_0 = 8 | ARM_MMU_IDX_A, - ARMMMUIdx_E30_3_PAN = 9 | ARM_MMU_IDX_A, - - /* - * Used for second stage of an S12 page table walk, or for descriptor - * loads during first stage of an S1 page table walk. Note that both - * are in use simultaneously for SecureEL2: the security state for - * the S2 ptw is selected by the NS bit from the S1 ptw. - */ - ARMMMUIdx_Stage2_S = 10 | ARM_MMU_IDX_A, - ARMMMUIdx_Stage2 = 11 | ARM_MMU_IDX_A, - - /* TLBs with 1-1 mapping to the physical address spaces. */ - ARMMMUIdx_Phys_S = 12 | ARM_MMU_IDX_A, - ARMMMUIdx_Phys_NS = 13 | ARM_MMU_IDX_A, - ARMMMUIdx_Phys_Root = 14 | ARM_MMU_IDX_A, - ARMMMUIdx_Phys_Realm = 15 | ARM_MMU_IDX_A, - - /* - * These are not allocated TLBs and are used only for AT system - * instructions or for the first stage of an S12 page table walk. - */ - ARMMMUIdx_Stage1_E0 = 0 | ARM_MMU_IDX_NOTLB, - ARMMMUIdx_Stage1_E1 = 1 | ARM_MMU_IDX_NOTLB, - ARMMMUIdx_Stage1_E1_PAN = 2 | ARM_MMU_IDX_NOTLB, - - /* - * M-profile. - */ - ARMMMUIdx_MUser = ARM_MMU_IDX_M, - ARMMMUIdx_MPriv = ARM_MMU_IDX_M | ARM_MMU_IDX_M_PRIV, - ARMMMUIdx_MUserNegPri = ARMMMUIdx_MUser | ARM_MMU_IDX_M_NEGPRI, - ARMMMUIdx_MPrivNegPri = ARMMMUIdx_MPriv | ARM_MMU_IDX_M_NEGPRI, - ARMMMUIdx_MSUser = ARMMMUIdx_MUser | ARM_MMU_IDX_M_S, - ARMMMUIdx_MSPriv = ARMMMUIdx_MPriv | ARM_MMU_IDX_M_S, - ARMMMUIdx_MSUserNegPri = ARMMMUIdx_MUserNegPri | ARM_MMU_IDX_M_S, - ARMMMUIdx_MSPrivNegPri = ARMMMUIdx_MPrivNegPri | ARM_MMU_IDX_M_S, -} ARMMMUIdx; - -/* - * Bit macros for the core-mmu-index values for each index, - * for use when calling tlb_flush_by_mmuidx() and friends. - */ -#define TO_CORE_BIT(NAME) \ - ARMMMUIdxBit_##NAME = 1 << (ARMMMUIdx_##NAME & ARM_MMU_IDX_COREIDX_MASK) - -typedef enum ARMMMUIdxBit { - TO_CORE_BIT(E10_0), - TO_CORE_BIT(E20_0), - TO_CORE_BIT(E10_1), - TO_CORE_BIT(E10_1_PAN), - TO_CORE_BIT(E2), - TO_CORE_BIT(E20_2), - TO_CORE_BIT(E20_2_PAN), - TO_CORE_BIT(E3), - TO_CORE_BIT(E30_0), - TO_CORE_BIT(E30_3_PAN), - TO_CORE_BIT(Stage2), - TO_CORE_BIT(Stage2_S), - - TO_CORE_BIT(MUser), - TO_CORE_BIT(MPriv), - TO_CORE_BIT(MUserNegPri), - TO_CORE_BIT(MPrivNegPri), - TO_CORE_BIT(MSUser), - TO_CORE_BIT(MSPriv), - TO_CORE_BIT(MSUserNegPri), - TO_CORE_BIT(MSPrivNegPri), -} ARMMMUIdxBit; - -#undef TO_CORE_BIT - -#define MMU_USER_IDX 0 - /* Indexes used when registering address spaces with cpu_address_space_init */ typedef enum ARMASIdx { ARMASIdx_NS = 0, diff --git a/target/arm/mmuidx.h b/target/arm/mmuidx.h new file mode 100644 index 0000000000000..5b9b4bc84fd32 --- /dev/null +++ b/target/arm/mmuidx.h @@ -0,0 +1,216 @@ +/* + * QEMU Arm software mmu index definitions + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TARGET_ARM_MMUIDX_H +#define TARGET_ARM_MMUIDX_H + +/* + * Arm has the following "translation regimes" (as the Arm ARM calls them): + * + * If EL3 is 64-bit: + * + NonSecure EL1 & 0 stage 1 + * + NonSecure EL1 & 0 stage 2 + * + NonSecure EL2 + * + NonSecure EL2 & 0 (ARMv8.1-VHE) + * + Secure EL1 & 0 stage 1 + * + Secure EL1 & 0 stage 2 (FEAT_SEL2) + * + Secure EL2 (FEAT_SEL2) + * + Secure EL2 & 0 (FEAT_SEL2) + * + Realm EL1 & 0 stage 1 (FEAT_RME) + * + Realm EL1 & 0 stage 2 (FEAT_RME) + * + Realm EL2 (FEAT_RME) + * + EL3 + * If EL3 is 32-bit: + * + NonSecure PL1 & 0 stage 1 + * + NonSecure PL1 & 0 stage 2 + * + NonSecure PL2 + * + Secure PL1 & 0 + * (reminder: for 32 bit EL3, Secure PL1 is *EL3*, not EL1.) + * + * For QEMU, an mmu_idx is not quite the same as a translation regime because: + * 1. we need to split the "EL1 & 0" and "EL2 & 0" regimes into two mmu_idxes, + * because they may differ in access permissions even if the VA->PA map is + * the same + * 2. we want to cache in our TLB the full VA->IPA->PA lookup for a stage 1+2 + * translation, which means that we have one mmu_idx that deals with two + * concatenated translation regimes [this sort of combined s1+2 TLB is + * architecturally permitted] + * 3. we don't need to allocate an mmu_idx to translations that we won't be + * handling via the TLB. The only way to do a stage 1 translation without + * the immediate stage 2 translation is via the ATS or AT system insns, + * which can be slow-pathed and always do a page table walk. + * The only use of stage 2 translations is either as part of an s1+2 + * lookup or when loading the descriptors during a stage 1 page table walk, + * and in both those cases we don't use the TLB. + * 4. we can also safely fold together the "32 bit EL3" and "64 bit EL3" + * translation regimes, because they map reasonably well to each other + * and they can't both be active at the same time. + * 5. we want to be able to use the TLB for accesses done as part of a + * stage1 page table walk, rather than having to walk the stage2 page + * table over and over. + * 6. we need separate EL1/EL2 mmu_idx for handling the Privileged Access + * Never (PAN) bit within PSTATE. + * 7. we fold together most secure and non-secure regimes for A-profile, + * because there are no banked system registers for aarch64, so the + * process of switching between secure and non-secure is + * already heavyweight. + * 8. we cannot fold together Stage 2 Secure and Stage 2 NonSecure, + * because both are in use simultaneously for Secure EL2. + * + * This gives us the following list of cases: + * + * EL0 EL1&0 stage 1+2 (aka NS PL0 PL1&0 stage 1+2) + * EL1 EL1&0 stage 1+2 (aka NS PL1 PL1&0 stage 1+2) + * EL1 EL1&0 stage 1+2 +PAN (aka NS PL1 P1&0 stage 1+2 +PAN) + * EL0 EL2&0 + * EL2 EL2&0 + * EL2 EL2&0 +PAN + * EL2 (aka NS PL2) + * EL3 (aka AArch32 S PL1 PL1&0) + * AArch32 S PL0 PL1&0 (we call this EL30_0) + * AArch32 S PL1 PL1&0 +PAN (we call this EL30_3_PAN) + * Stage2 Secure + * Stage2 NonSecure + * plus one TLB per Physical address space: S, NS, Realm, Root + * + * for a total of 16 different mmu_idx. + * + * R profile CPUs have an MPU, but can use the same set of MMU indexes + * as A profile. They only need to distinguish EL0 and EL1 (and + * EL2 for cores like the Cortex-R52). + * + * M profile CPUs are rather different as they do not have a true MMU. + * They have the following different MMU indexes: + * User + * Privileged + * User, execution priority negative (ie the MPU HFNMIENA bit may apply) + * Privileged, execution priority negative (ditto) + * If the CPU supports the v8M Security Extension then there are also: + * Secure User + * Secure Privileged + * Secure User, execution priority negative + * Secure Privileged, execution priority negative + * + * The ARMMMUIdx and the mmu index value used by the core QEMU TLB code + * are not quite the same -- different CPU types (most notably M profile + * vs A/R profile) would like to use MMU indexes with different semantics, + * but since we don't ever need to use all of those in a single CPU we + * can avoid having to set NB_MMU_MODES to "total number of A profile MMU + * modes + total number of M profile MMU modes". The lower bits of + * ARMMMUIdx are the core TLB mmu index, and the higher bits are always + * the same for any particular CPU. + * Variables of type ARMMUIdx are always full values, and the core + * index values are in variables of type 'int'. + * + * Our enumeration includes at the end some entries which are not "true" + * mmu_idx values in that they don't have corresponding TLBs and are only + * valid for doing slow path page table walks. + * + * The constant names here are patterned after the general style of the names + * of the AT/ATS operations. + * The values used are carefully arranged to make mmu_idx => EL lookup easy. + * For M profile we arrange them to have a bit for priv, a bit for negpri + * and a bit for secure. + */ +#define ARM_MMU_IDX_A 0x10 /* A profile */ +#define ARM_MMU_IDX_NOTLB 0x20 /* does not have a TLB */ +#define ARM_MMU_IDX_M 0x40 /* M profile */ + +/* Meanings of the bits for M profile mmu idx values */ +#define ARM_MMU_IDX_M_PRIV 0x1 +#define ARM_MMU_IDX_M_NEGPRI 0x2 +#define ARM_MMU_IDX_M_S 0x4 /* Secure */ + +#define ARM_MMU_IDX_TYPE_MASK \ + (ARM_MMU_IDX_A | ARM_MMU_IDX_M | ARM_MMU_IDX_NOTLB) +#define ARM_MMU_IDX_COREIDX_MASK 0xf + +typedef enum ARMMMUIdx { + /* + * A-profile. + */ + ARMMMUIdx_E10_0 = 0 | ARM_MMU_IDX_A, + ARMMMUIdx_E20_0 = 1 | ARM_MMU_IDX_A, + ARMMMUIdx_E10_1 = 2 | ARM_MMU_IDX_A, + ARMMMUIdx_E20_2 = 3 | ARM_MMU_IDX_A, + ARMMMUIdx_E10_1_PAN = 4 | ARM_MMU_IDX_A, + ARMMMUIdx_E20_2_PAN = 5 | ARM_MMU_IDX_A, + ARMMMUIdx_E2 = 6 | ARM_MMU_IDX_A, + ARMMMUIdx_E3 = 7 | ARM_MMU_IDX_A, + ARMMMUIdx_E30_0 = 8 | ARM_MMU_IDX_A, + ARMMMUIdx_E30_3_PAN = 9 | ARM_MMU_IDX_A, + + /* + * Used for second stage of an S12 page table walk, or for descriptor + * loads during first stage of an S1 page table walk. Note that both + * are in use simultaneously for SecureEL2: the security state for + * the S2 ptw is selected by the NS bit from the S1 ptw. + */ + ARMMMUIdx_Stage2_S = 10 | ARM_MMU_IDX_A, + ARMMMUIdx_Stage2 = 11 | ARM_MMU_IDX_A, + + /* TLBs with 1-1 mapping to the physical address spaces. */ + ARMMMUIdx_Phys_S = 12 | ARM_MMU_IDX_A, + ARMMMUIdx_Phys_NS = 13 | ARM_MMU_IDX_A, + ARMMMUIdx_Phys_Root = 14 | ARM_MMU_IDX_A, + ARMMMUIdx_Phys_Realm = 15 | ARM_MMU_IDX_A, + + /* + * These are not allocated TLBs and are used only for AT system + * instructions or for the first stage of an S12 page table walk. + */ + ARMMMUIdx_Stage1_E0 = 0 | ARM_MMU_IDX_NOTLB, + ARMMMUIdx_Stage1_E1 = 1 | ARM_MMU_IDX_NOTLB, + ARMMMUIdx_Stage1_E1_PAN = 2 | ARM_MMU_IDX_NOTLB, + + /* + * M-profile. + */ + ARMMMUIdx_MUser = ARM_MMU_IDX_M, + ARMMMUIdx_MPriv = ARM_MMU_IDX_M | ARM_MMU_IDX_M_PRIV, + ARMMMUIdx_MUserNegPri = ARMMMUIdx_MUser | ARM_MMU_IDX_M_NEGPRI, + ARMMMUIdx_MPrivNegPri = ARMMMUIdx_MPriv | ARM_MMU_IDX_M_NEGPRI, + ARMMMUIdx_MSUser = ARMMMUIdx_MUser | ARM_MMU_IDX_M_S, + ARMMMUIdx_MSPriv = ARMMMUIdx_MPriv | ARM_MMU_IDX_M_S, + ARMMMUIdx_MSUserNegPri = ARMMMUIdx_MUserNegPri | ARM_MMU_IDX_M_S, + ARMMMUIdx_MSPrivNegPri = ARMMMUIdx_MPrivNegPri | ARM_MMU_IDX_M_S, +} ARMMMUIdx; + +/* + * Bit macros for the core-mmu-index values for each index, + * for use when calling tlb_flush_by_mmuidx() and friends. + */ +#define TO_CORE_BIT(NAME) \ + ARMMMUIdxBit_##NAME = 1 << (ARMMMUIdx_##NAME & ARM_MMU_IDX_COREIDX_MASK) + +typedef enum ARMMMUIdxBit { + TO_CORE_BIT(E10_0), + TO_CORE_BIT(E20_0), + TO_CORE_BIT(E10_1), + TO_CORE_BIT(E10_1_PAN), + TO_CORE_BIT(E2), + TO_CORE_BIT(E20_2), + TO_CORE_BIT(E20_2_PAN), + TO_CORE_BIT(E3), + TO_CORE_BIT(E30_0), + TO_CORE_BIT(E30_3_PAN), + TO_CORE_BIT(Stage2), + TO_CORE_BIT(Stage2_S), + + TO_CORE_BIT(MUser), + TO_CORE_BIT(MPriv), + TO_CORE_BIT(MUserNegPri), + TO_CORE_BIT(MPrivNegPri), + TO_CORE_BIT(MSUser), + TO_CORE_BIT(MSPriv), + TO_CORE_BIT(MSUserNegPri), + TO_CORE_BIT(MSPrivNegPri), +} ARMMMUIdxBit; + +#undef TO_CORE_BIT + +#define MMU_USER_IDX 0 + +#endif /* TARGET_ARM_MMUIDX_H */ From 65ef0f2b33621fcc24ca54936475a00604e9a62e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:17 -0700 Subject: [PATCH 1459/1794] target/arm: Convert arm_mmu_idx_to_el from switch to table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In an effort to keep all ARMMMUIdx data in one place, begin construction of an info table describing all of the properties of the mmu_idx. Begin with the access EL. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-18-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 27 ------------------------ target/arm/internals.h | 3 +-- target/arm/meson.build | 7 +++++- target/arm/mmuidx-internal.h | 29 +++++++++++++++++++++++++ target/arm/mmuidx.c | 41 ++++++++++++++++++++++++++++++++++++ 5 files changed, 77 insertions(+), 30 deletions(-) create mode 100644 target/arm/mmuidx-internal.h create mode 100644 target/arm/mmuidx.c diff --git a/target/arm/helper.c b/target/arm/helper.c index bd5321348accb..b1d68da1333f7 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9653,33 +9653,6 @@ int fp_exception_el(CPUARMState *env, int cur_el) return 0; } -/* Return the exception level we're running at if this is our mmu_idx */ -int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx) -{ - if (mmu_idx & ARM_MMU_IDX_M) { - return mmu_idx & ARM_MMU_IDX_M_PRIV; - } - - switch (mmu_idx) { - case ARMMMUIdx_E10_0: - case ARMMMUIdx_E20_0: - case ARMMMUIdx_E30_0: - return 0; - case ARMMMUIdx_E10_1: - case ARMMMUIdx_E10_1_PAN: - return 1; - case ARMMMUIdx_E2: - case ARMMMUIdx_E20_2: - case ARMMMUIdx_E20_2_PAN: - return 2; - case ARMMMUIdx_E3: - case ARMMMUIdx_E30_3_PAN: - return 3; - default: - g_assert_not_reached(); - } -} - #ifndef CONFIG_TCG ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate) { diff --git a/target/arm/internals.h b/target/arm/internals.h index 69eb1df6173ce..1a234d41c2d75 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -34,6 +34,7 @@ #include "system/memory.h" #include "syndrome.h" #include "cpu-features.h" +#include "mmuidx-internal.h" /* register banks for CPU modes */ #define BANK_USRSYS 0 @@ -984,8 +985,6 @@ static inline ARMMMUIdx core_to_aa64_mmu_idx(int mmu_idx) return mmu_idx | ARM_MMU_IDX_A; } -int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx); - /* Return the MMU index for a v7M CPU in the specified security state */ ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate); diff --git a/target/arm/meson.build b/target/arm/meson.build index 638ee62525fb7..f9f0beef05ecf 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -6,7 +6,12 @@ arm_ss.add(files( arm_ss.add(when: 'TARGET_AARCH64', if_true: files( 'cpu64.c', - 'gdbstub64.c')) + 'gdbstub64.c' +)) + +arm_common_ss.add(files( + 'mmuidx.c', +)) arm_system_ss = ss.source_set() arm_common_system_ss = ss.source_set() diff --git a/target/arm/mmuidx-internal.h b/target/arm/mmuidx-internal.h new file mode 100644 index 0000000000000..29bba4ecb5421 --- /dev/null +++ b/target/arm/mmuidx-internal.h @@ -0,0 +1,29 @@ +/* + * QEMU Arm software mmu index internal definitions + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TARGET_ARM_MMUIDX_INTERNAL_H +#define TARGET_ARM_MMUIDX_INTERNAL_H + +#include "mmuidx.h" +#include "tcg/debug-assert.h" +#include "hw/registerfields.h" + + +FIELD(MMUIDXINFO, EL, 0, 2) +FIELD(MMUIDXINFO, ELVALID, 2, 1) + +extern const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8]; + +#define arm_mmuidx_is_valid(x) ((unsigned)(x) < ARRAY_SIZE(arm_mmuidx_table)) + +/* Return the exception level associated with this mmu index. */ +static inline int arm_mmu_idx_to_el(ARMMMUIdx idx) +{ + tcg_debug_assert(arm_mmuidx_is_valid(idx)); + tcg_debug_assert(FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, ELVALID)); + return FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, EL); +} + +#endif /* TARGET_ARM_MMUIDX_INTERNAL_H */ diff --git a/target/arm/mmuidx.c b/target/arm/mmuidx.c new file mode 100644 index 0000000000000..309b1d68df5ea --- /dev/null +++ b/target/arm/mmuidx.c @@ -0,0 +1,41 @@ +/* + * QEMU Arm software mmu index definitions + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "mmuidx-internal.h" + + +#define EL(X) ((X << R_MMUIDXINFO_EL_SHIFT) | R_MMUIDXINFO_ELVALID_MASK) + +const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8] = { + /* + * A-profile. + */ + [ARMMMUIdx_E10_0] = EL(0), + [ARMMMUIdx_E10_1] = EL(1), + [ARMMMUIdx_E10_1_PAN] = EL(1), + + [ARMMMUIdx_E20_0] = EL(0), + [ARMMMUIdx_E20_2] = EL(2), + [ARMMMUIdx_E20_2_PAN] = EL(2), + + [ARMMMUIdx_E2] = EL(2), + + [ARMMMUIdx_E3] = EL(3), + [ARMMMUIdx_E30_0] = EL(0), + [ARMMMUIdx_E30_3_PAN] = EL(3), + + /* + * M-profile. + */ + [ARMMMUIdx_MUser] = EL(0), + [ARMMMUIdx_MPriv] = EL(1), + [ARMMMUIdx_MUserNegPri] = EL(0), + [ARMMMUIdx_MPrivNegPri] = EL(1), + [ARMMMUIdx_MSUser] = EL(0), + [ARMMMUIdx_MSPriv] = EL(1), + [ARMMMUIdx_MSUserNegPri] = EL(0), + [ARMMMUIdx_MSPrivNegPri] = EL(1), +}; From 5dccad96b1f089a2d97262565a26960cf04e1579 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:18 -0700 Subject: [PATCH 1460/1794] target/arm: Remove unused env argument from regime_el MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-19-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 2 +- target/arm/internals.h | 8 ++++---- target/arm/ptw.c | 34 +++++++++++++++++----------------- target/arm/tcg/mte_helper.c | 2 +- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index b1d68da1333f7..a55161ef40ee4 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9391,7 +9391,7 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, ARMGranuleSize gran; ARMCPU *cpu = env_archcpu(env); bool stage2 = regime_is_stage2(mmu_idx); - int r_el = regime_el(env, mmu_idx); + int r_el = regime_el(mmu_idx); if (!regime_has_2_ranges(mmu_idx)) { select = 0; diff --git a/target/arm/internals.h b/target/arm/internals.h index 1a234d41c2d75..bd7ea820674a5 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1074,7 +1074,7 @@ static inline bool regime_is_stage2(ARMMMUIdx mmu_idx) } /* Return the exception level which controls this address translation regime */ -static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) +static inline uint32_t regime_el(ARMMMUIdx mmu_idx) { switch (mmu_idx) { case ARMMMUIdx_E20_0: @@ -1128,7 +1128,7 @@ static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx) /* Return the SCTLR value which controls this address translation regime */ static inline uint64_t regime_sctlr(CPUARMState *env, ARMMMUIdx mmu_idx) { - return env->cp15.sctlr_el[regime_el(env, mmu_idx)]; + return env->cp15.sctlr_el[regime_el(mmu_idx)]; } /* @@ -1160,13 +1160,13 @@ static inline uint64_t regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx) v |= env->cp15.vtcr_el2 & VTCR_SHARED_FIELD_MASK; return v; } - return env->cp15.tcr_el[regime_el(env, mmu_idx)]; + return env->cp15.tcr_el[regime_el(mmu_idx)]; } /* Return true if the translation regime is using LPAE format page tables */ static inline bool regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) { - int el = regime_el(env, mmu_idx); + int el = regime_el(mmu_idx); if (el == 2 || arm_el_is_aa64(env, el)) { return true; } diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 21540a1ec3e32..9c89ffe8a5435 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -235,9 +235,9 @@ static uint64_t regime_ttbr(CPUARMState *env, ARMMMUIdx mmu_idx, int ttbrn) return env->cp15.vsttbr_el2; } if (ttbrn == 0) { - return env->cp15.ttbr0_el[regime_el(env, mmu_idx)]; + return env->cp15.ttbr0_el[regime_el(mmu_idx)]; } else { - return env->cp15.ttbr1_el[regime_el(env, mmu_idx)]; + return env->cp15.ttbr1_el[regime_el(mmu_idx)]; } } @@ -1059,7 +1059,7 @@ static bool get_phys_addr_v5(CPUARMState *env, S1Translate *ptw, } type = (desc & 3); domain = (desc >> 5) & 0x0f; - if (regime_el(env, ptw->in_mmu_idx) == 1) { + if (regime_el(ptw->in_mmu_idx) == 1) { dacr = env->cp15.dacr_ns; } else { dacr = env->cp15.dacr_s; @@ -1198,7 +1198,7 @@ static bool get_phys_addr_v6(CPUARMState *env, S1Translate *ptw, /* Page or Section. */ domain = (desc >> 5) & 0x0f; } - if (regime_el(env, mmu_idx) == 1) { + if (regime_el(mmu_idx) == 1) { dacr = env->cp15.dacr_ns; } else { dacr = env->cp15.dacr_s; @@ -1489,7 +1489,7 @@ static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64, xn = pxn || (user_rw & PAGE_WRITE); } } else if (arm_feature(env, ARM_FEATURE_V7)) { - switch (regime_el(env, mmu_idx)) { + switch (regime_el(mmu_idx)) { case 1: case 3: if (is_user) { @@ -1545,7 +1545,7 @@ static int get_S1prot_indirect(CPUARMState *env, S1Translate *ptw, /* F */ 0, /* reserved */ }; - uint32_t el = regime_el(env, mmu_idx); + uint32_t el = regime_el(mmu_idx); uint64_t pir = env->cp15.pir_el[el]; uint64_t pire0 = 0; int perm; @@ -1620,7 +1620,7 @@ static ARMVAParameters aa32_va_parameters(CPUARMState *env, uint32_t va, ARMMMUIdx mmu_idx) { uint64_t tcr = regime_tcr(env, mmu_idx); - uint32_t el = regime_el(env, mmu_idx); + uint32_t el = regime_el(mmu_idx); int select, tsz; bool epd, hpd; @@ -1846,7 +1846,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, int addrsize, inputsize, outputsize; uint64_t tcr = regime_tcr(env, mmu_idx); int ap, prot; - uint32_t el = regime_el(env, mmu_idx); + uint32_t el = regime_el(mmu_idx); uint64_t descaddrmask; bool aarch64 = arm_el_is_aa64(env, el); uint64_t descriptor, new_descriptor; @@ -2296,7 +2296,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, /* Index into MAIR registers for cache attributes */ attrindx = extract32(attrs, 2, 3); - mair = env->cp15.mair_el[regime_el(env, mmu_idx)]; + mair = env->cp15.mair_el[regime_el(mmu_idx)]; assert(attrindx <= 7); result->cacheattrs.is_s2_format = false; result->cacheattrs.attrs = extract64(mair, attrindx * 8, 8); @@ -2774,7 +2774,7 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, static uint32_t *regime_rbar(CPUARMState *env, ARMMMUIdx mmu_idx, uint32_t secure) { - if (regime_el(env, mmu_idx) == 2) { + if (regime_el(mmu_idx) == 2) { return env->pmsav8.hprbar; } else { return env->pmsav8.rbar[secure]; @@ -2784,7 +2784,7 @@ static uint32_t *regime_rbar(CPUARMState *env, ARMMMUIdx mmu_idx, static uint32_t *regime_rlar(CPUARMState *env, ARMMMUIdx mmu_idx, uint32_t secure) { - if (regime_el(env, mmu_idx) == 2) { + if (regime_el(mmu_idx) == 2) { return env->pmsav8.hprlar; } else { return env->pmsav8.rlar[secure]; @@ -2816,7 +2816,7 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, uint32_t addr_page_limit = addr_page_base + (TARGET_PAGE_SIZE - 1); int region_counter; - if (regime_el(env, mmu_idx) == 2) { + if (regime_el(mmu_idx) == 2) { region_counter = cpu->pmsav8r_hdregion; } else { region_counter = cpu->pmsav7_dregion; @@ -2942,7 +2942,7 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, xn = 1; } - if (regime_el(env, mmu_idx) == 2) { + if (regime_el(mmu_idx) == 2) { result->f.prot = simple_ap_to_rw_prot_is_user(ap, mmu_idx != ARMMMUIdx_E2); } else { @@ -2951,7 +2951,7 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, if (!arm_feature(env, ARM_FEATURE_M)) { uint8_t attrindx = extract32(matched_rlar, 1, 3); - uint64_t mair = env->cp15.mair_el[regime_el(env, mmu_idx)]; + uint64_t mair = env->cp15.mair_el[regime_el(mmu_idx)]; uint8_t sh = extract32(matched_rlar, 3, 2); if (regime_sctlr(env, mmu_idx) & SCTLR_WXN && @@ -2959,7 +2959,7 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, xn = 0x1; } - if ((regime_el(env, mmu_idx) == 1) && + if ((regime_el(mmu_idx) == 1) && regime_sctlr(env, mmu_idx) & SCTLR_UWXN && ap == 0x1) { pxn = 0x1; } @@ -3444,7 +3444,7 @@ static bool get_phys_addr_disabled(CPUARMState *env, break; default: - r_el = regime_el(env, mmu_idx); + r_el = regime_el(mmu_idx); if (arm_el_is_aa64(env, r_el)) { int pamax = arm_pamax(env_archcpu(env)); uint64_t tcr = env->cp15.tcr_el[r_el]; @@ -3697,7 +3697,7 @@ static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw, */ if (address < 0x02000000 && mmu_idx != ARMMMUIdx_Stage2 && !arm_feature(env, ARM_FEATURE_V8)) { - if (regime_el(env, mmu_idx) == 3) { + if (regime_el(mmu_idx) == 3) { address += env->cp15.fcseidr_s; } else { address += env->cp15.fcseidr_ns; diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index b96c953f809e9..bb48fe359b8c0 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -605,7 +605,7 @@ void mte_check_fail(CPUARMState *env, uint32_t desc, int el, reg_el, tcf; uint64_t sctlr; - reg_el = regime_el(env, arm_mmu_idx); + reg_el = regime_el(arm_mmu_idx); sctlr = env->cp15.sctlr_el[reg_el]; switch (arm_mmu_idx) { From 9fe4b77afef948acfee1681ae7c0c2860c48dd5d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:19 -0700 Subject: [PATCH 1461/1794] target/arm: Convert regime_el from switch to table Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-20-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/internals.h | 35 ---------------------------- target/arm/mmuidx-internal.h | 13 +++++++++++ target/arm/mmuidx.c | 44 +++++++++++++++++++++--------------- 3 files changed, 39 insertions(+), 53 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index bd7ea820674a5..319c39a4ac57e 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1073,41 +1073,6 @@ static inline bool regime_is_stage2(ARMMMUIdx mmu_idx) return mmu_idx == ARMMMUIdx_Stage2 || mmu_idx == ARMMMUIdx_Stage2_S; } -/* Return the exception level which controls this address translation regime */ -static inline uint32_t regime_el(ARMMMUIdx mmu_idx) -{ - switch (mmu_idx) { - case ARMMMUIdx_E20_0: - case ARMMMUIdx_E20_2: - case ARMMMUIdx_E20_2_PAN: - case ARMMMUIdx_Stage2: - case ARMMMUIdx_Stage2_S: - case ARMMMUIdx_E2: - return 2; - case ARMMMUIdx_E3: - case ARMMMUIdx_E30_0: - case ARMMMUIdx_E30_3_PAN: - return 3; - case ARMMMUIdx_E10_0: - case ARMMMUIdx_Stage1_E0: - case ARMMMUIdx_Stage1_E1: - case ARMMMUIdx_Stage1_E1_PAN: - case ARMMMUIdx_E10_1: - case ARMMMUIdx_E10_1_PAN: - case ARMMMUIdx_MPrivNegPri: - case ARMMMUIdx_MUserNegPri: - case ARMMMUIdx_MPriv: - case ARMMMUIdx_MUser: - case ARMMMUIdx_MSPrivNegPri: - case ARMMMUIdx_MSUserNegPri: - case ARMMMUIdx_MSPriv: - case ARMMMUIdx_MSUser: - return 1; - default: - g_assert_not_reached(); - } -} - static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx) { switch (mmu_idx) { diff --git a/target/arm/mmuidx-internal.h b/target/arm/mmuidx-internal.h index 29bba4ecb5421..d8d64a14d6204 100644 --- a/target/arm/mmuidx-internal.h +++ b/target/arm/mmuidx-internal.h @@ -13,6 +13,8 @@ FIELD(MMUIDXINFO, EL, 0, 2) FIELD(MMUIDXINFO, ELVALID, 2, 1) +FIELD(MMUIDXINFO, REL, 3, 2) +FIELD(MMUIDXINFO, RELVALID, 5, 1) extern const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8]; @@ -26,4 +28,15 @@ static inline int arm_mmu_idx_to_el(ARMMMUIdx idx) return FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, EL); } +/* + * Return the exception level for the address translation regime + * associated with this mmu index. + */ +static inline uint32_t regime_el(ARMMMUIdx idx) +{ + tcg_debug_assert(arm_mmuidx_is_valid(idx)); + tcg_debug_assert(FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, RELVALID)); + return FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, REL); +} + #endif /* TARGET_ARM_MMUIDX_INTERNAL_H */ diff --git a/target/arm/mmuidx.c b/target/arm/mmuidx.c index 309b1d68df5ea..6dfefa56c2443 100644 --- a/target/arm/mmuidx.c +++ b/target/arm/mmuidx.c @@ -8,34 +8,42 @@ #define EL(X) ((X << R_MMUIDXINFO_EL_SHIFT) | R_MMUIDXINFO_ELVALID_MASK) +#define REL(X) ((X << R_MMUIDXINFO_REL_SHIFT) | R_MMUIDXINFO_RELVALID_MASK) const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8] = { /* * A-profile. */ - [ARMMMUIdx_E10_0] = EL(0), - [ARMMMUIdx_E10_1] = EL(1), - [ARMMMUIdx_E10_1_PAN] = EL(1), + [ARMMMUIdx_E10_0] = EL(0) | REL(1), + [ARMMMUIdx_E10_1] = EL(1) | REL(1), + [ARMMMUIdx_E10_1_PAN] = EL(1) | REL(1), - [ARMMMUIdx_E20_0] = EL(0), - [ARMMMUIdx_E20_2] = EL(2), - [ARMMMUIdx_E20_2_PAN] = EL(2), + [ARMMMUIdx_E20_0] = EL(0) | REL(2), + [ARMMMUIdx_E20_2] = EL(2) | REL(2), + [ARMMMUIdx_E20_2_PAN] = EL(2) | REL(2), - [ARMMMUIdx_E2] = EL(2), + [ARMMMUIdx_E2] = EL(2) | REL(2), - [ARMMMUIdx_E3] = EL(3), - [ARMMMUIdx_E30_0] = EL(0), - [ARMMMUIdx_E30_3_PAN] = EL(3), + [ARMMMUIdx_E3] = EL(3) | REL(3), + [ARMMMUIdx_E30_0] = EL(0) | REL(3), + [ARMMMUIdx_E30_3_PAN] = EL(3) | REL(3), + + [ARMMMUIdx_Stage2_S] = REL(2), + [ARMMMUIdx_Stage2] = REL(2), + + [ARMMMUIdx_Stage1_E0] = REL(1), + [ARMMMUIdx_Stage1_E1] = REL(1), + [ARMMMUIdx_Stage1_E1_PAN] = REL(1), /* * M-profile. */ - [ARMMMUIdx_MUser] = EL(0), - [ARMMMUIdx_MPriv] = EL(1), - [ARMMMUIdx_MUserNegPri] = EL(0), - [ARMMMUIdx_MPrivNegPri] = EL(1), - [ARMMMUIdx_MSUser] = EL(0), - [ARMMMUIdx_MSPriv] = EL(1), - [ARMMMUIdx_MSUserNegPri] = EL(0), - [ARMMMUIdx_MSPrivNegPri] = EL(1), + [ARMMMUIdx_MUser] = EL(0) | REL(1), + [ARMMMUIdx_MPriv] = EL(1) | REL(1), + [ARMMMUIdx_MUserNegPri] = EL(0) | REL(1), + [ARMMMUIdx_MPrivNegPri] = EL(1) | REL(1), + [ARMMMUIdx_MSUser] = EL(0) | REL(1), + [ARMMMUIdx_MSPriv] = EL(1) | REL(1), + [ARMMMUIdx_MSUserNegPri] = EL(0) | REL(1), + [ARMMMUIdx_MSPrivNegPri] = EL(1) | REL(1), }; From bb39dd76d7f66eb4e5d513cdc2ed0179f5c1cc7f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:20 -0700 Subject: [PATCH 1462/1794] target/arm: Convert regime_has_2_ranges from switch to table Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-21-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/internals.h | 28 ---------------------------- target/arm/mmuidx-internal.h | 17 +++++++++++++++++ target/arm/mmuidx.c | 19 ++++++++++--------- 3 files changed, 27 insertions(+), 37 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index 319c39a4ac57e..061472a943910 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1027,34 +1027,6 @@ static inline void arm_call_el_change_hook(ARMCPU *cpu) } } -/* - * Return true if this address translation regime has two ranges. - * Note that this will not return the correct answer for AArch32 - * Secure PL1&0 (i.e. mmu indexes E3, E30_0, E30_3_PAN), but it is - * never called from a context where EL3 can be AArch32. (The - * correct return value for ARMMMUIdx_E3 would be different for - * that case, so we can't just make the function return the - * correct value anyway; we would need an extra "bool e3_is_aarch32" - * argument which all the current callsites would pass as 'false'.) - */ -static inline bool regime_has_2_ranges(ARMMMUIdx mmu_idx) -{ - switch (mmu_idx) { - case ARMMMUIdx_Stage1_E0: - case ARMMMUIdx_Stage1_E1: - case ARMMMUIdx_Stage1_E1_PAN: - case ARMMMUIdx_E10_0: - case ARMMMUIdx_E10_1: - case ARMMMUIdx_E10_1_PAN: - case ARMMMUIdx_E20_0: - case ARMMMUIdx_E20_2: - case ARMMMUIdx_E20_2_PAN: - return true; - default: - return false; - } -} - static inline bool regime_is_pan(CPUARMState *env, ARMMMUIdx mmu_idx) { switch (mmu_idx) { diff --git a/target/arm/mmuidx-internal.h b/target/arm/mmuidx-internal.h index d8d64a14d6204..f03a2ab94c264 100644 --- a/target/arm/mmuidx-internal.h +++ b/target/arm/mmuidx-internal.h @@ -15,6 +15,7 @@ FIELD(MMUIDXINFO, EL, 0, 2) FIELD(MMUIDXINFO, ELVALID, 2, 1) FIELD(MMUIDXINFO, REL, 3, 2) FIELD(MMUIDXINFO, RELVALID, 5, 1) +FIELD(MMUIDXINFO, 2RANGES, 6, 1) extern const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8]; @@ -39,4 +40,20 @@ static inline uint32_t regime_el(ARMMMUIdx idx) return FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, REL); } +/* + * Return true if this address translation regime has two ranges. + * Note that this will not return the correct answer for AArch32 + * Secure PL1&0 (i.e. mmu indexes E3, E30_0, E30_3_PAN), but it is + * never called from a context where EL3 can be AArch32. (The + * correct return value for ARMMMUIdx_E3 would be different for + * that case, so we can't just make the function return the + * correct value anyway; we would need an extra "bool e3_is_aarch32" + * argument which all the current callsites would pass as 'false'.) + */ +static inline bool regime_has_2_ranges(ARMMMUIdx idx) +{ + tcg_debug_assert(arm_mmuidx_is_valid(idx)); + return FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, 2RANGES); +} + #endif /* TARGET_ARM_MMUIDX_INTERNAL_H */ diff --git a/target/arm/mmuidx.c b/target/arm/mmuidx.c index 6dfefa56c2443..f880d216067ec 100644 --- a/target/arm/mmuidx.c +++ b/target/arm/mmuidx.c @@ -9,18 +9,19 @@ #define EL(X) ((X << R_MMUIDXINFO_EL_SHIFT) | R_MMUIDXINFO_ELVALID_MASK) #define REL(X) ((X << R_MMUIDXINFO_REL_SHIFT) | R_MMUIDXINFO_RELVALID_MASK) +#define R2 R_MMUIDXINFO_2RANGES_MASK const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8] = { /* * A-profile. */ - [ARMMMUIdx_E10_0] = EL(0) | REL(1), - [ARMMMUIdx_E10_1] = EL(1) | REL(1), - [ARMMMUIdx_E10_1_PAN] = EL(1) | REL(1), + [ARMMMUIdx_E10_0] = EL(0) | REL(1) | R2, + [ARMMMUIdx_E10_1] = EL(1) | REL(1) | R2, + [ARMMMUIdx_E10_1_PAN] = EL(1) | REL(1) | R2, - [ARMMMUIdx_E20_0] = EL(0) | REL(2), - [ARMMMUIdx_E20_2] = EL(2) | REL(2), - [ARMMMUIdx_E20_2_PAN] = EL(2) | REL(2), + [ARMMMUIdx_E20_0] = EL(0) | REL(2) | R2, + [ARMMMUIdx_E20_2] = EL(2) | REL(2) | R2, + [ARMMMUIdx_E20_2_PAN] = EL(2) | REL(2) | R2, [ARMMMUIdx_E2] = EL(2) | REL(2), @@ -31,9 +32,9 @@ const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8] = { [ARMMMUIdx_Stage2_S] = REL(2), [ARMMMUIdx_Stage2] = REL(2), - [ARMMMUIdx_Stage1_E0] = REL(1), - [ARMMMUIdx_Stage1_E1] = REL(1), - [ARMMMUIdx_Stage1_E1_PAN] = REL(1), + [ARMMMUIdx_Stage1_E0] = REL(1) | R2, + [ARMMMUIdx_Stage1_E1] = REL(1) | R2, + [ARMMMUIdx_Stage1_E1_PAN] = REL(1) | R2, /* * M-profile. From 59af12841b749c15d2a26a8f4222bc7627d00d01 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:21 -0700 Subject: [PATCH 1463/1794] target/arm: Remove unused env argument from regime_is_pan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-22-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/internals.h | 2 +- target/arm/ptw.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index 061472a943910..cb2ffeff59c5e 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1027,7 +1027,7 @@ static inline void arm_call_el_change_hook(ARMCPU *cpu) } } -static inline bool regime_is_pan(CPUARMState *env, ARMMMUIdx mmu_idx) +static inline bool regime_is_pan(ARMMMUIdx mmu_idx) { switch (mmu_idx) { case ARMMMUIdx_Stage1_E1_PAN: diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 9c89ffe8a5435..1080b6d69d4ae 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -1429,10 +1429,10 @@ static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64, * We make the IMPDEF choices that SCR_EL3.SIF and Realm EL2&0 * do not affect EPAN. */ - if (user_rw && regime_is_pan(env, mmu_idx)) { + if (user_rw && regime_is_pan(mmu_idx)) { prot_rw = 0; } else if (cpu_isar_feature(aa64_pan3, cpu) && is_aa64 && - regime_is_pan(env, mmu_idx) && + regime_is_pan(mmu_idx) && (regime_sctlr(env, mmu_idx) & SCTLR_EPAN) && !xn) { prot_rw = 0; } @@ -1571,7 +1571,7 @@ static int get_S1prot_indirect(CPUARMState *env, S1Translate *ptw, p_perm &= ~(PAGE_RWX | PAGE_GCS); u_perm &= ~(PAGE_RWX | PAGE_GCS); } - if ((u_perm & (PAGE_RWX | PAGE_GCS)) && regime_is_pan(env, mmu_idx)) { + if ((u_perm & (PAGE_RWX | PAGE_GCS)) && regime_is_pan(mmu_idx)) { p_perm &= ~(PAGE_READ | PAGE_WRITE); } perm = regime_is_user(env, mmu_idx) ? u_perm : p_perm; From ceab42c677484b9d6c97c91e59f8263685e891f5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:22 -0700 Subject: [PATCH 1464/1794] target/arm: Convert regime_is_pan from switch to table Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-23-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/internals.h | 13 ------------- target/arm/mmuidx-internal.h | 8 ++++++++ target/arm/mmuidx.c | 9 +++++---- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index cb2ffeff59c5e..819ada7a5ddfb 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1027,19 +1027,6 @@ static inline void arm_call_el_change_hook(ARMCPU *cpu) } } -static inline bool regime_is_pan(ARMMMUIdx mmu_idx) -{ - switch (mmu_idx) { - case ARMMMUIdx_Stage1_E1_PAN: - case ARMMMUIdx_E10_1_PAN: - case ARMMMUIdx_E20_2_PAN: - case ARMMMUIdx_E30_3_PAN: - return true; - default: - return false; - } -} - static inline bool regime_is_stage2(ARMMMUIdx mmu_idx) { return mmu_idx == ARMMMUIdx_Stage2 || mmu_idx == ARMMMUIdx_Stage2_S; diff --git a/target/arm/mmuidx-internal.h b/target/arm/mmuidx-internal.h index f03a2ab94c264..41baf1a003c07 100644 --- a/target/arm/mmuidx-internal.h +++ b/target/arm/mmuidx-internal.h @@ -16,6 +16,7 @@ FIELD(MMUIDXINFO, ELVALID, 2, 1) FIELD(MMUIDXINFO, REL, 3, 2) FIELD(MMUIDXINFO, RELVALID, 5, 1) FIELD(MMUIDXINFO, 2RANGES, 6, 1) +FIELD(MMUIDXINFO, PAN, 7, 1) extern const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8]; @@ -56,4 +57,11 @@ static inline bool regime_has_2_ranges(ARMMMUIdx idx) return FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, 2RANGES); } +/* Return true if Privileged Access Never is enabled for this mmu index. */ +static inline bool regime_is_pan(ARMMMUIdx idx) +{ + tcg_debug_assert(arm_mmuidx_is_valid(idx)); + return FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, PAN); +} + #endif /* TARGET_ARM_MMUIDX_INTERNAL_H */ diff --git a/target/arm/mmuidx.c b/target/arm/mmuidx.c index f880d216067ec..98db02b8e54de 100644 --- a/target/arm/mmuidx.c +++ b/target/arm/mmuidx.c @@ -10,6 +10,7 @@ #define EL(X) ((X << R_MMUIDXINFO_EL_SHIFT) | R_MMUIDXINFO_ELVALID_MASK) #define REL(X) ((X << R_MMUIDXINFO_REL_SHIFT) | R_MMUIDXINFO_RELVALID_MASK) #define R2 R_MMUIDXINFO_2RANGES_MASK +#define PAN R_MMUIDXINFO_PAN_MASK const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8] = { /* @@ -17,24 +18,24 @@ const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8] = { */ [ARMMMUIdx_E10_0] = EL(0) | REL(1) | R2, [ARMMMUIdx_E10_1] = EL(1) | REL(1) | R2, - [ARMMMUIdx_E10_1_PAN] = EL(1) | REL(1) | R2, + [ARMMMUIdx_E10_1_PAN] = EL(1) | REL(1) | R2 | PAN, [ARMMMUIdx_E20_0] = EL(0) | REL(2) | R2, [ARMMMUIdx_E20_2] = EL(2) | REL(2) | R2, - [ARMMMUIdx_E20_2_PAN] = EL(2) | REL(2) | R2, + [ARMMMUIdx_E20_2_PAN] = EL(2) | REL(2) | R2 | PAN, [ARMMMUIdx_E2] = EL(2) | REL(2), [ARMMMUIdx_E3] = EL(3) | REL(3), [ARMMMUIdx_E30_0] = EL(0) | REL(3), - [ARMMMUIdx_E30_3_PAN] = EL(3) | REL(3), + [ARMMMUIdx_E30_3_PAN] = EL(3) | REL(3) | PAN, [ARMMMUIdx_Stage2_S] = REL(2), [ARMMMUIdx_Stage2] = REL(2), [ARMMMUIdx_Stage1_E0] = REL(1) | R2, [ARMMMUIdx_Stage1_E1] = REL(1) | R2, - [ARMMMUIdx_Stage1_E1_PAN] = REL(1) | R2, + [ARMMMUIdx_Stage1_E1_PAN] = REL(1) | R2 | PAN, /* * M-profile. From b1af3bb87a808270bd6a0a48833e71a6ba506829 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:23 -0700 Subject: [PATCH 1465/1794] target/arm: Remove unused env argument from regime_is_user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-24-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 2 +- target/arm/internals.h | 2 +- target/arm/ptw.c | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index a55161ef40ee4..aed245fd868a4 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9450,7 +9450,7 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, ds = extract64(tcr, 59, 1); if (e0pd && cpu_isar_feature(aa64_e0pd, cpu) && - regime_is_user(env, mmu_idx)) { + regime_is_user(mmu_idx)) { epd = true; } diff --git a/target/arm/internals.h b/target/arm/internals.h index 819ada7a5ddfb..d0d976cbb083a 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1032,7 +1032,7 @@ static inline bool regime_is_stage2(ARMMMUIdx mmu_idx) return mmu_idx == ARMMMUIdx_Stage2 || mmu_idx == ARMMMUIdx_Stage2_S; } -static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx) +static inline bool regime_is_user(ARMMMUIdx mmu_idx) { switch (mmu_idx) { case ARMMMUIdx_E10_0: diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 1080b6d69d4ae..d6d4bf3b23b31 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -1000,7 +1000,7 @@ static int ap_to_rw_prot(CPUARMState *env, ARMMMUIdx mmu_idx, int ap, int domain_prot) { return ap_to_rw_prot_is_user(env, mmu_idx, ap, domain_prot, - regime_is_user(env, mmu_idx)); + regime_is_user(mmu_idx)); } /* @@ -1026,7 +1026,7 @@ static int simple_ap_to_rw_prot_is_user(int ap, bool is_user) static int simple_ap_to_rw_prot(CPUARMState *env, ARMMMUIdx mmu_idx, int ap) { - return simple_ap_to_rw_prot_is_user(ap, regime_is_user(env, mmu_idx)); + return simple_ap_to_rw_prot_is_user(ap, regime_is_user(mmu_idx)); } static bool get_phys_addr_v5(CPUARMState *env, S1Translate *ptw, @@ -1412,7 +1412,7 @@ static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64, ARMSecuritySpace in_pa, ARMSecuritySpace out_pa) { ARMCPU *cpu = env_archcpu(env); - bool is_user = regime_is_user(env, mmu_idx); + bool is_user = regime_is_user(mmu_idx); bool have_wxn; int wxn = 0; @@ -1574,7 +1574,7 @@ static int get_S1prot_indirect(CPUARMState *env, S1Translate *ptw, if ((u_perm & (PAGE_RWX | PAGE_GCS)) && regime_is_pan(mmu_idx)) { p_perm &= ~(PAGE_READ | PAGE_WRITE); } - perm = regime_is_user(env, mmu_idx) ? u_perm : p_perm; + perm = regime_is_user(mmu_idx) ? u_perm : p_perm; } if (in_pa != out_pa) { @@ -2421,7 +2421,7 @@ static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t mask; uint32_t base; ARMMMUIdx mmu_idx = ptw->in_mmu_idx; - bool is_user = regime_is_user(env, mmu_idx); + bool is_user = regime_is_user(mmu_idx); if (regime_translation_disabled(env, mmu_idx, ptw->in_space)) { /* MPU disabled. */ @@ -2588,7 +2588,7 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, ARMCPU *cpu = env_archcpu(env); int n; ARMMMUIdx mmu_idx = ptw->in_mmu_idx; - bool is_user = regime_is_user(env, mmu_idx); + bool is_user = regime_is_user(mmu_idx); bool secure = arm_space_is_secure(ptw->in_space); result->f.phys_addr = address; @@ -2808,7 +2808,7 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, * memory system to use a subpage. */ ARMCPU *cpu = env_archcpu(env); - bool is_user = regime_is_user(env, mmu_idx); + bool is_user = regime_is_user(mmu_idx); int n; int matchregion = -1; bool hit = false; @@ -3689,7 +3689,7 @@ static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw, break; } - result->f.attrs.user = regime_is_user(env, mmu_idx); + result->f.attrs.user = regime_is_user(mmu_idx); /* * Fast Context Switch Extension. This doesn't exist at all in v8. From 38e5e7018b9d3dbce30999b38d10cc5d7315da8c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:24 -0700 Subject: [PATCH 1466/1794] target/arm: Convert regime_is_user from switch to table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-25-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/internals.h | 17 ----------------- target/arm/mmuidx-internal.h | 12 ++++++++++++ target/arm/mmuidx.c | 6 ++++-- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index d0d976cbb083a..c4aef8b50e59e 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1032,23 +1032,6 @@ static inline bool regime_is_stage2(ARMMMUIdx mmu_idx) return mmu_idx == ARMMMUIdx_Stage2 || mmu_idx == ARMMMUIdx_Stage2_S; } -static inline bool regime_is_user(ARMMMUIdx mmu_idx) -{ - switch (mmu_idx) { - case ARMMMUIdx_E10_0: - case ARMMMUIdx_E20_0: - case ARMMMUIdx_E30_0: - case ARMMMUIdx_Stage1_E0: - case ARMMMUIdx_MUser: - case ARMMMUIdx_MSUser: - case ARMMMUIdx_MUserNegPri: - case ARMMMUIdx_MSUserNegPri: - return true; - default: - return false; - } -} - /* Return the SCTLR value which controls this address translation regime */ static inline uint64_t regime_sctlr(CPUARMState *env, ARMMMUIdx mmu_idx) { diff --git a/target/arm/mmuidx-internal.h b/target/arm/mmuidx-internal.h index 41baf1a003c07..3e51c0f57908a 100644 --- a/target/arm/mmuidx-internal.h +++ b/target/arm/mmuidx-internal.h @@ -17,6 +17,7 @@ FIELD(MMUIDXINFO, REL, 3, 2) FIELD(MMUIDXINFO, RELVALID, 5, 1) FIELD(MMUIDXINFO, 2RANGES, 6, 1) FIELD(MMUIDXINFO, PAN, 7, 1) +FIELD(MMUIDXINFO, USER, 8, 1) extern const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8]; @@ -64,4 +65,15 @@ static inline bool regime_is_pan(ARMMMUIdx idx) return FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, PAN); } +/* + * Return true if the exception level associated with this mmu index is 0. + * Differs from arm_mmu_idx_to_el(idx) == 0 in that this allows querying + * Stage1 and Stage2 mmu indexes. + */ +static inline bool regime_is_user(ARMMMUIdx idx) +{ + tcg_debug_assert(arm_mmuidx_is_valid(idx)); + return FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, USER); +} + #endif /* TARGET_ARM_MMUIDX_INTERNAL_H */ diff --git a/target/arm/mmuidx.c b/target/arm/mmuidx.c index 98db02b8e54de..1c1e062bfef5a 100644 --- a/target/arm/mmuidx.c +++ b/target/arm/mmuidx.c @@ -7,10 +7,12 @@ #include "mmuidx-internal.h" -#define EL(X) ((X << R_MMUIDXINFO_EL_SHIFT) | R_MMUIDXINFO_ELVALID_MASK) +#define EL(X) ((X << R_MMUIDXINFO_EL_SHIFT) | R_MMUIDXINFO_ELVALID_MASK | \ + ((X == 0) << R_MMUIDXINFO_USER_SHIFT)) #define REL(X) ((X << R_MMUIDXINFO_REL_SHIFT) | R_MMUIDXINFO_RELVALID_MASK) #define R2 R_MMUIDXINFO_2RANGES_MASK #define PAN R_MMUIDXINFO_PAN_MASK +#define USER R_MMUIDXINFO_USER_MASK const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8] = { /* @@ -33,7 +35,7 @@ const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8] = { [ARMMMUIdx_Stage2_S] = REL(2), [ARMMMUIdx_Stage2] = REL(2), - [ARMMMUIdx_Stage1_E0] = REL(1) | R2, + [ARMMMUIdx_Stage1_E0] = REL(1) | R2 | USER, [ARMMMUIdx_Stage1_E1] = REL(1) | R2, [ARMMMUIdx_Stage1_E1_PAN] = REL(1) | R2 | PAN, From 346feee01c87713230e5f3da7b1126da1b43027f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:25 -0700 Subject: [PATCH 1467/1794] target/arm: Convert arm_mmu_idx_is_stage1_of_2 from switch to table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-26-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/internals.h | 19 ------------------- target/arm/mmuidx-internal.h | 8 ++++++++ target/arm/mmuidx.c | 7 ++++--- 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index c4aef8b50e59e..92883b6c0e49f 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1285,25 +1285,6 @@ ARMMMUIdx stage_1_mmu_idx(ARMMMUIdx mmu_idx); ARMMMUIdx arm_stage1_mmu_idx(CPUARMState *env); #endif -/** - * arm_mmu_idx_is_stage1_of_2: - * @mmu_idx: The ARMMMUIdx to test - * - * Return true if @mmu_idx is a NOTLB mmu_idx that is the - * first stage of a two stage regime. - */ -static inline bool arm_mmu_idx_is_stage1_of_2(ARMMMUIdx mmu_idx) -{ - switch (mmu_idx) { - case ARMMMUIdx_Stage1_E0: - case ARMMMUIdx_Stage1_E1: - case ARMMMUIdx_Stage1_E1_PAN: - return true; - default: - return false; - } -} - static inline uint32_t aarch32_cpsr_valid_mask(uint64_t features, const ARMISARegisters *id) { diff --git a/target/arm/mmuidx-internal.h b/target/arm/mmuidx-internal.h index 3e51c0f57908a..55fba4aae26c2 100644 --- a/target/arm/mmuidx-internal.h +++ b/target/arm/mmuidx-internal.h @@ -18,6 +18,7 @@ FIELD(MMUIDXINFO, RELVALID, 5, 1) FIELD(MMUIDXINFO, 2RANGES, 6, 1) FIELD(MMUIDXINFO, PAN, 7, 1) FIELD(MMUIDXINFO, USER, 8, 1) +FIELD(MMUIDXINFO, STAGE1, 9, 1) extern const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8]; @@ -76,4 +77,11 @@ static inline bool regime_is_user(ARMMMUIdx idx) return FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, USER); } +/* Return true if this mmu index is stage 1 of a 2-stage translation. */ +static inline bool arm_mmu_idx_is_stage1_of_2(ARMMMUIdx idx) +{ + tcg_debug_assert(arm_mmuidx_is_valid(idx)); + return FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, STAGE1); +} + #endif /* TARGET_ARM_MMUIDX_INTERNAL_H */ diff --git a/target/arm/mmuidx.c b/target/arm/mmuidx.c index 1c1e062bfef5a..c5b43a593277b 100644 --- a/target/arm/mmuidx.c +++ b/target/arm/mmuidx.c @@ -13,6 +13,7 @@ #define R2 R_MMUIDXINFO_2RANGES_MASK #define PAN R_MMUIDXINFO_PAN_MASK #define USER R_MMUIDXINFO_USER_MASK +#define S1 R_MMUIDXINFO_STAGE1_MASK const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8] = { /* @@ -35,9 +36,9 @@ const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8] = { [ARMMMUIdx_Stage2_S] = REL(2), [ARMMMUIdx_Stage2] = REL(2), - [ARMMMUIdx_Stage1_E0] = REL(1) | R2 | USER, - [ARMMMUIdx_Stage1_E1] = REL(1) | R2, - [ARMMMUIdx_Stage1_E1_PAN] = REL(1) | R2 | PAN, + [ARMMMUIdx_Stage1_E0] = REL(1) | R2 | S1 | USER, + [ARMMMUIdx_Stage1_E1] = REL(1) | R2 | S1, + [ARMMMUIdx_Stage1_E1_PAN] = REL(1) | R2 | S1 | PAN, /* * M-profile. From 35ba9340114dc6122f5e7b65565a85795c2dc7d7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:26 -0700 Subject: [PATCH 1468/1794] target/arm: Convert regime_is_stage2 to table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This wasn't using a switch, but two comparisons. Convert it to arm_mmuidx_table for consistency. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-27-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/internals.h | 5 ----- target/arm/mmuidx-internal.h | 8 ++++++++ target/arm/mmuidx.c | 5 +++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index 92883b6c0e49f..591b509e68bd9 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1027,11 +1027,6 @@ static inline void arm_call_el_change_hook(ARMCPU *cpu) } } -static inline bool regime_is_stage2(ARMMMUIdx mmu_idx) -{ - return mmu_idx == ARMMMUIdx_Stage2 || mmu_idx == ARMMMUIdx_Stage2_S; -} - /* Return the SCTLR value which controls this address translation regime */ static inline uint64_t regime_sctlr(CPUARMState *env, ARMMMUIdx mmu_idx) { diff --git a/target/arm/mmuidx-internal.h b/target/arm/mmuidx-internal.h index 55fba4aae26c2..1d948aa6f4c37 100644 --- a/target/arm/mmuidx-internal.h +++ b/target/arm/mmuidx-internal.h @@ -19,6 +19,7 @@ FIELD(MMUIDXINFO, 2RANGES, 6, 1) FIELD(MMUIDXINFO, PAN, 7, 1) FIELD(MMUIDXINFO, USER, 8, 1) FIELD(MMUIDXINFO, STAGE1, 9, 1) +FIELD(MMUIDXINFO, STAGE2, 10, 1) extern const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8]; @@ -84,4 +85,11 @@ static inline bool arm_mmu_idx_is_stage1_of_2(ARMMMUIdx idx) return FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, STAGE1); } +/* Return true if this mmu index is stage 2 of a 2-stage translation. */ +static inline bool regime_is_stage2(ARMMMUIdx idx) +{ + tcg_debug_assert(arm_mmuidx_is_valid(idx)); + return FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, STAGE2); +} + #endif /* TARGET_ARM_MMUIDX_INTERNAL_H */ diff --git a/target/arm/mmuidx.c b/target/arm/mmuidx.c index c5b43a593277b..61a682e655dfb 100644 --- a/target/arm/mmuidx.c +++ b/target/arm/mmuidx.c @@ -14,6 +14,7 @@ #define PAN R_MMUIDXINFO_PAN_MASK #define USER R_MMUIDXINFO_USER_MASK #define S1 R_MMUIDXINFO_STAGE1_MASK +#define S2 R_MMUIDXINFO_STAGE2_MASK const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8] = { /* @@ -33,8 +34,8 @@ const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8] = { [ARMMMUIdx_E30_0] = EL(0) | REL(3), [ARMMMUIdx_E30_3_PAN] = EL(3) | REL(3) | PAN, - [ARMMMUIdx_Stage2_S] = REL(2), - [ARMMMUIdx_Stage2] = REL(2), + [ARMMMUIdx_Stage2_S] = REL(2) | S2, + [ARMMMUIdx_Stage2] = REL(2) | S2, [ARMMMUIdx_Stage1_E0] = REL(1) | R2 | S1 | USER, [ARMMMUIdx_Stage1_E1] = REL(1) | R2 | S1, From f76cee647cb4b2057d45d315ae73e89fa6d6f336 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:27 -0700 Subject: [PATCH 1469/1794] target/arm: Introduce mmu indexes for GCS Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-28-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- include/hw/core/cpu.h | 5 ++- target/arm/helper.c | 15 ++++++-- target/arm/mmuidx-internal.h | 8 ++++ target/arm/mmuidx.c | 9 +++++ target/arm/mmuidx.h | 71 ++++++++++++++++++++++++------------ target/arm/ptw.c | 20 ++++++++++ target/arm/tcg/tlb-insns.c | 47 ++++++++++++++++-------- 7 files changed, 132 insertions(+), 43 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index d175edb6f8a28..e79e8e0a8ee7b 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -198,9 +198,10 @@ struct CPUClass { }; /* - * Fix the number of mmu modes to 16. + * Fix the number of mmu modes across all targets. + * Current maximum is target/arm/. */ -#define NB_MMU_MODES 16 +#define NB_MMU_MODES 22 typedef uint32_t MMUIdxMap; /* Use a fully associative victim tlb of 8 entries. */ diff --git a/target/arm/helper.c b/target/arm/helper.c index aed245fd868a4..6642cae0cc436 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -420,7 +420,9 @@ int alle1_tlbmask(CPUARMState *env) */ return (ARMMMUIdxBit_E10_1 | ARMMMUIdxBit_E10_1_PAN | + ARMMMUIdxBit_E10_1_GCS | ARMMMUIdxBit_E10_0 | + ARMMMUIdxBit_E10_0_GCS | ARMMMUIdxBit_Stage2 | ARMMMUIdxBit_Stage2_S); } @@ -808,12 +810,17 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) */ if (changed & (SCR_NS | SCR_NSE)) { tlb_flush_by_mmuidx(env_cpu(env), (ARMMMUIdxBit_E10_0 | + ARMMMUIdxBit_E10_0_GCS | ARMMMUIdxBit_E20_0 | + ARMMMUIdxBit_E20_0_GCS | ARMMMUIdxBit_E10_1 | - ARMMMUIdxBit_E20_2 | ARMMMUIdxBit_E10_1_PAN | + ARMMMUIdxBit_E10_1_GCS | + ARMMMUIdxBit_E20_2 | ARMMMUIdxBit_E20_2_PAN | - ARMMMUIdxBit_E2)); + ARMMMUIdxBit_E20_2_GCS | + ARMMMUIdxBit_E2 | + ARMMMUIdxBit_E2_GCS)); } } @@ -2787,7 +2794,9 @@ static void vmsa_tcr_ttbr_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, (arm_hcr_el2_eff(env) & HCR_E2H)) { uint16_t mask = ARMMMUIdxBit_E20_2 | ARMMMUIdxBit_E20_2_PAN | - ARMMMUIdxBit_E20_0; + ARMMMUIdxBit_E20_2_GCS | + ARMMMUIdxBit_E20_0 | + ARMMMUIdxBit_E20_0_GCS; tlb_flush_by_mmuidx(env_cpu(env), mask); } raw_write(env, ri, value); diff --git a/target/arm/mmuidx-internal.h b/target/arm/mmuidx-internal.h index 1d948aa6f4c37..f494ec348d6ef 100644 --- a/target/arm/mmuidx-internal.h +++ b/target/arm/mmuidx-internal.h @@ -20,6 +20,7 @@ FIELD(MMUIDXINFO, PAN, 7, 1) FIELD(MMUIDXINFO, USER, 8, 1) FIELD(MMUIDXINFO, STAGE1, 9, 1) FIELD(MMUIDXINFO, STAGE2, 10, 1) +FIELD(MMUIDXINFO, GCS, 11, 1) extern const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8]; @@ -92,4 +93,11 @@ static inline bool regime_is_stage2(ARMMMUIdx idx) return FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, STAGE2); } +/* Return true if this mmu index implies AccessType_GCS. */ +static inline bool regime_is_gcs(ARMMMUIdx idx) +{ + tcg_debug_assert(arm_mmuidx_is_valid(idx)); + return FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, GCS); +} + #endif /* TARGET_ARM_MMUIDX_INTERNAL_H */ diff --git a/target/arm/mmuidx.c b/target/arm/mmuidx.c index 61a682e655dfb..42b003db9cb9d 100644 --- a/target/arm/mmuidx.c +++ b/target/arm/mmuidx.c @@ -15,22 +15,29 @@ #define USER R_MMUIDXINFO_USER_MASK #define S1 R_MMUIDXINFO_STAGE1_MASK #define S2 R_MMUIDXINFO_STAGE2_MASK +#define GCS R_MMUIDXINFO_GCS_MASK const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8] = { /* * A-profile. */ [ARMMMUIdx_E10_0] = EL(0) | REL(1) | R2, + [ARMMMUIdx_E10_0_GCS] = EL(0) | REL(1) | R2 | GCS, [ARMMMUIdx_E10_1] = EL(1) | REL(1) | R2, [ARMMMUIdx_E10_1_PAN] = EL(1) | REL(1) | R2 | PAN, + [ARMMMUIdx_E10_1_GCS] = EL(1) | REL(1) | R2 | GCS, [ARMMMUIdx_E20_0] = EL(0) | REL(2) | R2, + [ARMMMUIdx_E20_0_GCS] = EL(0) | REL(2) | R2 | GCS, [ARMMMUIdx_E20_2] = EL(2) | REL(2) | R2, [ARMMMUIdx_E20_2_PAN] = EL(2) | REL(2) | R2 | PAN, + [ARMMMUIdx_E20_2_GCS] = EL(2) | REL(2) | R2 | GCS, [ARMMMUIdx_E2] = EL(2) | REL(2), + [ARMMMUIdx_E2_GCS] = EL(2) | REL(2) | GCS, [ARMMMUIdx_E3] = EL(3) | REL(3), + [ARMMMUIdx_E3_GCS] = EL(3) | REL(3) | GCS, [ARMMMUIdx_E30_0] = EL(0) | REL(3), [ARMMMUIdx_E30_3_PAN] = EL(3) | REL(3) | PAN, @@ -38,8 +45,10 @@ const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8] = { [ARMMMUIdx_Stage2] = REL(2) | S2, [ARMMMUIdx_Stage1_E0] = REL(1) | R2 | S1 | USER, + [ARMMMUIdx_Stage1_E0_GCS] = REL(1) | R2 | S1 | USER | GCS, [ARMMMUIdx_Stage1_E1] = REL(1) | R2 | S1, [ARMMMUIdx_Stage1_E1_PAN] = REL(1) | R2 | S1 | PAN, + [ARMMMUIdx_Stage1_E1_GCS] = REL(1) | R2 | S1 | GCS, /* * M-profile. diff --git a/target/arm/mmuidx.h b/target/arm/mmuidx.h index 5b9b4bc84fd32..8d8d27337e0d1 100644 --- a/target/arm/mmuidx.h +++ b/target/arm/mmuidx.h @@ -58,24 +58,31 @@ * already heavyweight. * 8. we cannot fold together Stage 2 Secure and Stage 2 NonSecure, * because both are in use simultaneously for Secure EL2. + * 9. we need separate indexes for handling AccessType_GCS. * * This gives us the following list of cases: * * EL0 EL1&0 stage 1+2 (aka NS PL0 PL1&0 stage 1+2) + * EL0 EL1&0 stage 1+2 +GCS * EL1 EL1&0 stage 1+2 (aka NS PL1 PL1&0 stage 1+2) * EL1 EL1&0 stage 1+2 +PAN (aka NS PL1 P1&0 stage 1+2 +PAN) + * EL1 EL1&0 stage 1+2 +GCS * EL0 EL2&0 + * EL0 EL2&0 +GCS * EL2 EL2&0 * EL2 EL2&0 +PAN + * EL2 EL2&0 +GCS * EL2 (aka NS PL2) + * EL2 +GCS * EL3 (aka AArch32 S PL1 PL1&0) + * EL3 +GCS * AArch32 S PL0 PL1&0 (we call this EL30_0) * AArch32 S PL1 PL1&0 +PAN (we call this EL30_3_PAN) * Stage2 Secure * Stage2 NonSecure * plus one TLB per Physical address space: S, NS, Realm, Root * - * for a total of 16 different mmu_idx. + * for a total of 22 different mmu_idx. * * R profile CPUs have an MPU, but can use the same set of MMU indexes * as A profile. They only need to distinguish EL0 and EL1 (and @@ -114,9 +121,9 @@ * For M profile we arrange them to have a bit for priv, a bit for negpri * and a bit for secure. */ -#define ARM_MMU_IDX_A 0x10 /* A profile */ -#define ARM_MMU_IDX_NOTLB 0x20 /* does not have a TLB */ -#define ARM_MMU_IDX_M 0x40 /* M profile */ +#define ARM_MMU_IDX_A 0x20 /* A profile */ +#define ARM_MMU_IDX_NOTLB 0x40 /* does not have a TLB */ +#define ARM_MMU_IDX_M 0x80 /* M profile */ /* Meanings of the bits for M profile mmu idx values */ #define ARM_MMU_IDX_M_PRIV 0x1 @@ -125,22 +132,32 @@ #define ARM_MMU_IDX_TYPE_MASK \ (ARM_MMU_IDX_A | ARM_MMU_IDX_M | ARM_MMU_IDX_NOTLB) -#define ARM_MMU_IDX_COREIDX_MASK 0xf +#define ARM_MMU_IDX_COREIDX_MASK 0x1f typedef enum ARMMMUIdx { /* * A-profile. */ - ARMMMUIdx_E10_0 = 0 | ARM_MMU_IDX_A, - ARMMMUIdx_E20_0 = 1 | ARM_MMU_IDX_A, - ARMMMUIdx_E10_1 = 2 | ARM_MMU_IDX_A, - ARMMMUIdx_E20_2 = 3 | ARM_MMU_IDX_A, - ARMMMUIdx_E10_1_PAN = 4 | ARM_MMU_IDX_A, - ARMMMUIdx_E20_2_PAN = 5 | ARM_MMU_IDX_A, - ARMMMUIdx_E2 = 6 | ARM_MMU_IDX_A, - ARMMMUIdx_E3 = 7 | ARM_MMU_IDX_A, - ARMMMUIdx_E30_0 = 8 | ARM_MMU_IDX_A, - ARMMMUIdx_E30_3_PAN = 9 | ARM_MMU_IDX_A, + + ARMMMUIdx_E10_0 = 0 | ARM_MMU_IDX_A, + ARMMMUIdx_E10_0_GCS = 1 | ARM_MMU_IDX_A, + ARMMMUIdx_E10_1 = 2 | ARM_MMU_IDX_A, + ARMMMUIdx_E10_1_PAN = 3 | ARM_MMU_IDX_A, + ARMMMUIdx_E10_1_GCS = 4 | ARM_MMU_IDX_A, + + ARMMMUIdx_E20_0 = 5 | ARM_MMU_IDX_A, + ARMMMUIdx_E20_0_GCS = 6 | ARM_MMU_IDX_A, + ARMMMUIdx_E20_2 = 7 | ARM_MMU_IDX_A, + ARMMMUIdx_E20_2_PAN = 8 | ARM_MMU_IDX_A, + ARMMMUIdx_E20_2_GCS = 9 | ARM_MMU_IDX_A, + + ARMMMUIdx_E2 = 10 | ARM_MMU_IDX_A, + ARMMMUIdx_E2_GCS = 11 | ARM_MMU_IDX_A, + + ARMMMUIdx_E3 = 12 | ARM_MMU_IDX_A, + ARMMMUIdx_E3_GCS = 13 | ARM_MMU_IDX_A, + ARMMMUIdx_E30_0 = 14 | ARM_MMU_IDX_A, + ARMMMUIdx_E30_3_PAN = 15 | ARM_MMU_IDX_A, /* * Used for second stage of an S12 page table walk, or for descriptor @@ -148,14 +165,14 @@ typedef enum ARMMMUIdx { * are in use simultaneously for SecureEL2: the security state for * the S2 ptw is selected by the NS bit from the S1 ptw. */ - ARMMMUIdx_Stage2_S = 10 | ARM_MMU_IDX_A, - ARMMMUIdx_Stage2 = 11 | ARM_MMU_IDX_A, + ARMMMUIdx_Stage2_S = 16 | ARM_MMU_IDX_A, + ARMMMUIdx_Stage2 = 17 | ARM_MMU_IDX_A, /* TLBs with 1-1 mapping to the physical address spaces. */ - ARMMMUIdx_Phys_S = 12 | ARM_MMU_IDX_A, - ARMMMUIdx_Phys_NS = 13 | ARM_MMU_IDX_A, - ARMMMUIdx_Phys_Root = 14 | ARM_MMU_IDX_A, - ARMMMUIdx_Phys_Realm = 15 | ARM_MMU_IDX_A, + ARMMMUIdx_Phys_S = 18 | ARM_MMU_IDX_A, + ARMMMUIdx_Phys_NS = 19 | ARM_MMU_IDX_A, + ARMMMUIdx_Phys_Root = 20 | ARM_MMU_IDX_A, + ARMMMUIdx_Phys_Realm = 21 | ARM_MMU_IDX_A, /* * These are not allocated TLBs and are used only for AT system @@ -164,6 +181,8 @@ typedef enum ARMMMUIdx { ARMMMUIdx_Stage1_E0 = 0 | ARM_MMU_IDX_NOTLB, ARMMMUIdx_Stage1_E1 = 1 | ARM_MMU_IDX_NOTLB, ARMMMUIdx_Stage1_E1_PAN = 2 | ARM_MMU_IDX_NOTLB, + ARMMMUIdx_Stage1_E0_GCS = 3 | ARM_MMU_IDX_NOTLB, + ARMMMUIdx_Stage1_E1_GCS = 4 | ARM_MMU_IDX_NOTLB, /* * M-profile. @@ -187,13 +206,19 @@ typedef enum ARMMMUIdx { typedef enum ARMMMUIdxBit { TO_CORE_BIT(E10_0), - TO_CORE_BIT(E20_0), + TO_CORE_BIT(E10_0_GCS), TO_CORE_BIT(E10_1), TO_CORE_BIT(E10_1_PAN), - TO_CORE_BIT(E2), + TO_CORE_BIT(E10_1_GCS), + TO_CORE_BIT(E20_0), + TO_CORE_BIT(E20_0_GCS), TO_CORE_BIT(E20_2), TO_CORE_BIT(E20_2_PAN), + TO_CORE_BIT(E20_2_GCS), + TO_CORE_BIT(E2), + TO_CORE_BIT(E2_GCS), TO_CORE_BIT(E3), + TO_CORE_BIT(E3_GCS), TO_CORE_BIT(E30_0), TO_CORE_BIT(E30_3_PAN), TO_CORE_BIT(Stage2), diff --git a/target/arm/ptw.c b/target/arm/ptw.c index d6d4bf3b23b31..6c52ed1ad00f9 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -169,6 +169,10 @@ ARMMMUIdx stage_1_mmu_idx(ARMMMUIdx mmu_idx) return ARMMMUIdx_Stage1_E1; case ARMMMUIdx_E10_1_PAN: return ARMMMUIdx_Stage1_E1_PAN; + case ARMMMUIdx_E10_0_GCS: + return ARMMMUIdx_Stage1_E0_GCS; + case ARMMMUIdx_E10_1_GCS: + return ARMMMUIdx_Stage1_E1_GCS; default: return mmu_idx; } @@ -276,8 +280,10 @@ static bool regime_translation_disabled(CPUARMState *env, ARMMMUIdx mmu_idx, return (hcr_el2 & (HCR_DC | HCR_VM)) == 0; case ARMMMUIdx_E10_0: + case ARMMMUIdx_E10_0_GCS: case ARMMMUIdx_E10_1: case ARMMMUIdx_E10_1_PAN: + case ARMMMUIdx_E10_1_GCS: /* TGE means that EL0/1 act as if SCTLR_EL1.M is zero */ hcr_el2 = arm_hcr_el2_eff_secstate(env, space); if (hcr_el2 & HCR_TGE) { @@ -286,8 +292,10 @@ static bool regime_translation_disabled(CPUARMState *env, ARMMMUIdx mmu_idx, break; case ARMMMUIdx_Stage1_E0: + case ARMMMUIdx_Stage1_E0_GCS: case ARMMMUIdx_Stage1_E1: case ARMMMUIdx_Stage1_E1_PAN: + case ARMMMUIdx_Stage1_E1_GCS: /* HCR.DC means SCTLR_EL1.M behaves as 0 */ hcr_el2 = arm_hcr_el2_eff_secstate(env, space); if (hcr_el2 & HCR_DC) { @@ -296,10 +304,14 @@ static bool regime_translation_disabled(CPUARMState *env, ARMMMUIdx mmu_idx, break; case ARMMMUIdx_E20_0: + case ARMMMUIdx_E20_0_GCS: case ARMMMUIdx_E20_2: case ARMMMUIdx_E20_2_PAN: + case ARMMMUIdx_E20_2_GCS: case ARMMMUIdx_E2: + case ARMMMUIdx_E2_GCS: case ARMMMUIdx_E3: + case ARMMMUIdx_E3_GCS: case ARMMMUIdx_E30_0: case ARMMMUIdx_E30_3_PAN: break; @@ -3799,15 +3811,22 @@ arm_mmu_idx_to_security_space(CPUARMState *env, ARMMMUIdx mmu_idx) switch (mmu_idx) { case ARMMMUIdx_E10_0: + case ARMMMUIdx_E10_0_GCS: case ARMMMUIdx_E10_1: case ARMMMUIdx_E10_1_PAN: + case ARMMMUIdx_E10_1_GCS: case ARMMMUIdx_E20_0: + case ARMMMUIdx_E20_0_GCS: case ARMMMUIdx_E20_2: case ARMMMUIdx_E20_2_PAN: + case ARMMMUIdx_E20_2_GCS: case ARMMMUIdx_Stage1_E0: + case ARMMMUIdx_Stage1_E0_GCS: case ARMMMUIdx_Stage1_E1: case ARMMMUIdx_Stage1_E1_PAN: + case ARMMMUIdx_Stage1_E1_GCS: case ARMMMUIdx_E2: + case ARMMMUIdx_E2_GCS: ss = arm_security_space_below_el3(env); break; case ARMMMUIdx_Stage2: @@ -3836,6 +3855,7 @@ arm_mmu_idx_to_security_space(CPUARMState *env, ARMMMUIdx mmu_idx) ss = ARMSS_Secure; break; case ARMMMUIdx_E3: + case ARMMMUIdx_E3_GCS: case ARMMMUIdx_E30_0: case ARMMMUIdx_E30_3_PAN: if (arm_feature(env, ARM_FEATURE_AARCH64) && diff --git a/target/arm/tcg/tlb-insns.c b/target/arm/tcg/tlb-insns.c index 95c26c6d46384..1a0a332583641 100644 --- a/target/arm/tcg/tlb-insns.c +++ b/target/arm/tcg/tlb-insns.c @@ -149,7 +149,8 @@ static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, CPUState *cs = env_cpu(env); uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); - tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E2); + tlb_flush_page_by_mmuidx(cs, pageaddr, + ARMMMUIdxBit_E2 | ARMMMUIdxBit_E2_GCS); } static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -159,7 +160,8 @@ static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - ARMMMUIdxBit_E2); + ARMMMUIdxBit_E2 | + ARMMMUIdxBit_E2_GCS); } static void tlbiipas2_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -202,7 +204,7 @@ static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, { CPUState *cs = env_cpu(env); - tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E2); + tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E2 | ARMMMUIdxBit_E2_GCS); } static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -210,7 +212,8 @@ static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, { CPUState *cs = env_cpu(env); - tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E2); + tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E2 | + ARMMMUIdxBit_E2_GCS); } /* @@ -228,12 +231,16 @@ static int vae1_tlbmask(CPUARMState *env) if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { mask = ARMMMUIdxBit_E20_2 | ARMMMUIdxBit_E20_2_PAN | - ARMMMUIdxBit_E20_0; + ARMMMUIdxBit_E20_2_GCS | + ARMMMUIdxBit_E20_0 | + ARMMMUIdxBit_E20_0_GCS; } else { /* This is AArch64 only, so we don't need to touch the EL30_x TLBs */ mask = ARMMMUIdxBit_E10_1 | ARMMMUIdxBit_E10_1_PAN | - ARMMMUIdxBit_E10_0; + ARMMMUIdxBit_E10_1_GCS | + ARMMMUIdxBit_E10_0 | + ARMMMUIdxBit_E10_0_GCS; } return mask; } @@ -246,13 +253,20 @@ static int vae2_tlbmask(CPUARMState *env) if (hcr & HCR_E2H) { mask = ARMMMUIdxBit_E20_2 | ARMMMUIdxBit_E20_2_PAN | - ARMMMUIdxBit_E20_0; + ARMMMUIdxBit_E20_2_GCS | + ARMMMUIdxBit_E20_0 | + ARMMMUIdxBit_E20_0_GCS; } else { - mask = ARMMMUIdxBit_E2; + mask = ARMMMUIdxBit_E2 | ARMMMUIdxBit_E2_GCS; } return mask; } +static int vae3_tlbmask(void) +{ + return ARMMMUIdxBit_E3 | ARMMMUIdxBit_E3_GCS; +} + /* Return 56 if TBI is enabled, 64 otherwise. */ static int tlbbits_for_regime(CPUARMState *env, ARMMMUIdx mmu_idx, uint64_t addr) @@ -325,9 +339,12 @@ static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri, static int e2_tlbmask(CPUARMState *env) { return (ARMMMUIdxBit_E20_0 | + ARMMMUIdxBit_E20_0_GCS | ARMMMUIdxBit_E20_2 | ARMMMUIdxBit_E20_2_PAN | - ARMMMUIdxBit_E2); + ARMMMUIdxBit_E20_2_GCS | + ARMMMUIdxBit_E2 | + ARMMMUIdxBit_E2_GCS); } static void tlbi_aa64_alle1_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -354,7 +371,7 @@ static void tlbi_aa64_alle3_write(CPUARMState *env, const ARMCPRegInfo *ri, ARMCPU *cpu = env_archcpu(env); CPUState *cs = CPU(cpu); - tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E3); + tlb_flush_by_mmuidx(cs, vae3_tlbmask()); } static void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -380,7 +397,7 @@ static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, { CPUState *cs = env_cpu(env); - tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E3); + tlb_flush_by_mmuidx_all_cpus_synced(cs, vae3_tlbmask()); } static void tlbi_aa64_vae2_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -411,7 +428,7 @@ static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri, CPUState *cs = CPU(cpu); uint64_t pageaddr = sextract64(value << 12, 0, 56); - tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E3); + tlb_flush_page_by_mmuidx(cs, pageaddr, vae3_tlbmask()); } static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -465,7 +482,7 @@ static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, int bits = tlbbits_for_regime(env, ARMMMUIdx_E3, pageaddr); tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, - ARMMMUIdxBit_E3, bits); + vae3_tlbmask(), bits); } static int ipas2e1_tlbmask(CPUARMState *env, int64_t value) @@ -963,7 +980,7 @@ static void tlbi_aa64_rvae3_write(CPUARMState *env, * flush-last-level-only. */ - do_rvae_write(env, value, ARMMMUIdxBit_E3, tlb_force_broadcast(env)); + do_rvae_write(env, value, vae3_tlbmask(), tlb_force_broadcast(env)); } static void tlbi_aa64_rvae3is_write(CPUARMState *env, @@ -977,7 +994,7 @@ static void tlbi_aa64_rvae3is_write(CPUARMState *env, * flush-last-level-only or inner/outer specific flushes. */ - do_rvae_write(env, value, ARMMMUIdxBit_E3, true); + do_rvae_write(env, value, vae3_tlbmask(), true); } static void tlbi_aa64_ripas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, From 0fe292fadb4d1886ad57bb14abcfb4130901b0f6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:28 -0700 Subject: [PATCH 1470/1794] target/arm: Introduce regime_to_gcs Add a lookup from any a64 mmu index to the gcs mmu index within the same translation regime. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-29-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/mmuidx-internal.h | 10 ++++++++++ target/arm/mmuidx.c | 24 +++++++++++++----------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/target/arm/mmuidx-internal.h b/target/arm/mmuidx-internal.h index f494ec348d6ef..962b0538526e1 100644 --- a/target/arm/mmuidx-internal.h +++ b/target/arm/mmuidx-internal.h @@ -21,6 +21,7 @@ FIELD(MMUIDXINFO, USER, 8, 1) FIELD(MMUIDXINFO, STAGE1, 9, 1) FIELD(MMUIDXINFO, STAGE2, 10, 1) FIELD(MMUIDXINFO, GCS, 11, 1) +FIELD(MMUIDXINFO, TG, 12, 5) extern const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8]; @@ -100,4 +101,13 @@ static inline bool regime_is_gcs(ARMMMUIdx idx) return FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, GCS); } +/* Return the GCS MMUIdx for a given regime. */ +static inline ARMMMUIdx regime_to_gcs(ARMMMUIdx idx) +{ + tcg_debug_assert(arm_mmuidx_is_valid(idx)); + uint32_t core = FIELD_EX32(arm_mmuidx_table[idx], MMUIDXINFO, TG); + tcg_debug_assert(core != 0); /* core 0 is E10_0, not a GCS index */ + return core | ARM_MMU_IDX_A; +} + #endif /* TARGET_ARM_MMUIDX_INTERNAL_H */ diff --git a/target/arm/mmuidx.c b/target/arm/mmuidx.c index 42b003db9cb9d..a4663c8d87205 100644 --- a/target/arm/mmuidx.c +++ b/target/arm/mmuidx.c @@ -16,27 +16,29 @@ #define S1 R_MMUIDXINFO_STAGE1_MASK #define S2 R_MMUIDXINFO_STAGE2_MASK #define GCS R_MMUIDXINFO_GCS_MASK +#define TG(X) \ + ((ARMMMUIdx_##X##_GCS & ARM_MMU_IDX_COREIDX_MASK) << R_MMUIDXINFO_TG_SHIFT) const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8] = { /* * A-profile. */ - [ARMMMUIdx_E10_0] = EL(0) | REL(1) | R2, + [ARMMMUIdx_E10_0] = EL(0) | REL(1) | R2 | TG(E10_0), [ARMMMUIdx_E10_0_GCS] = EL(0) | REL(1) | R2 | GCS, - [ARMMMUIdx_E10_1] = EL(1) | REL(1) | R2, - [ARMMMUIdx_E10_1_PAN] = EL(1) | REL(1) | R2 | PAN, + [ARMMMUIdx_E10_1] = EL(1) | REL(1) | R2 | TG(E10_1), + [ARMMMUIdx_E10_1_PAN] = EL(1) | REL(1) | R2 | TG(E10_1) | PAN, [ARMMMUIdx_E10_1_GCS] = EL(1) | REL(1) | R2 | GCS, - [ARMMMUIdx_E20_0] = EL(0) | REL(2) | R2, + [ARMMMUIdx_E20_0] = EL(0) | REL(2) | R2 | TG(E20_0), [ARMMMUIdx_E20_0_GCS] = EL(0) | REL(2) | R2 | GCS, - [ARMMMUIdx_E20_2] = EL(2) | REL(2) | R2, - [ARMMMUIdx_E20_2_PAN] = EL(2) | REL(2) | R2 | PAN, + [ARMMMUIdx_E20_2] = EL(2) | REL(2) | R2 | TG(E20_2), + [ARMMMUIdx_E20_2_PAN] = EL(2) | REL(2) | R2 | TG(E20_2) | PAN, [ARMMMUIdx_E20_2_GCS] = EL(2) | REL(2) | R2 | GCS, - [ARMMMUIdx_E2] = EL(2) | REL(2), + [ARMMMUIdx_E2] = EL(2) | REL(2) | TG(E2), [ARMMMUIdx_E2_GCS] = EL(2) | REL(2) | GCS, - [ARMMMUIdx_E3] = EL(3) | REL(3), + [ARMMMUIdx_E3] = EL(3) | REL(3) | TG(E3), [ARMMMUIdx_E3_GCS] = EL(3) | REL(3) | GCS, [ARMMMUIdx_E30_0] = EL(0) | REL(3), [ARMMMUIdx_E30_3_PAN] = EL(3) | REL(3) | PAN, @@ -44,10 +46,10 @@ const uint32_t arm_mmuidx_table[ARM_MMU_IDX_M + 8] = { [ARMMMUIdx_Stage2_S] = REL(2) | S2, [ARMMMUIdx_Stage2] = REL(2) | S2, - [ARMMMUIdx_Stage1_E0] = REL(1) | R2 | S1 | USER, + [ARMMMUIdx_Stage1_E0] = REL(1) | R2 | S1 | USER | TG(Stage1_E0), [ARMMMUIdx_Stage1_E0_GCS] = REL(1) | R2 | S1 | USER | GCS, - [ARMMMUIdx_Stage1_E1] = REL(1) | R2 | S1, - [ARMMMUIdx_Stage1_E1_PAN] = REL(1) | R2 | S1 | PAN, + [ARMMMUIdx_Stage1_E1] = REL(1) | R2 | S1 | TG(Stage1_E1), + [ARMMMUIdx_Stage1_E1_PAN] = REL(1) | R2 | S1 | TG(Stage1_E1) | PAN, [ARMMMUIdx_Stage1_E1_GCS] = REL(1) | R2 | S1 | GCS, /* From 4c14265b8092a250edaef62891ee2d55fcfac92d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:29 -0700 Subject: [PATCH 1471/1794] target/arm: Support page protections for GCS mmu indexes Take read and write from the s1perms.gcs bit computed by the Arm pseudocode. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-30-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 6c52ed1ad00f9..d4386ede73e31 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -1619,12 +1619,16 @@ static int get_S1prot_indirect(CPUARMState *env, S1Translate *ptw, } } - if (perm & PAGE_WXN) { + if (regime_is_gcs(mmu_idx)) { + /* + * Note that the one s1perms.gcs bit controls both read and write + * access via AccessType_GCS. See AArch64.S1CheckPermissions. + */ + perm = (perm & PAGE_GCS ? PAGE_READ | PAGE_WRITE : 0); + } else if (perm & PAGE_WXN) { perm &= ~PAGE_EXEC; } - /* TODO: FEAT_GCS */ - return perm & PAGE_RWX; } @@ -2277,6 +2281,13 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, */ prot = get_S1prot_indirect(env, ptw, mmu_idx, pi, po, result->f.attrs.space, out_space); + } else if (regime_is_gcs(mmu_idx)) { + /* + * While one must use indirect permissions to successfully + * use GCS instructions, AArch64.S1DirectBasePermissions + * faithfully supplies s1perms.gcs = 0, Just In Case. + */ + prot = 0; } else { int xn = extract64(attrs, 54, 1); int pxn = extract64(attrs, 53, 1); From 8a0dda3e6b2394d0e96c16b80f44db97f9cc3e6e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:30 -0700 Subject: [PATCH 1472/1794] target/arm: Implement gcs bit for data abort Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-31-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/tlb_helper.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/tlb_helper.c b/target/arm/tcg/tlb_helper.c index ae2acd67276df..f1983a5732e65 100644 --- a/target/arm/tcg/tlb_helper.c +++ b/target/arm/tcg/tlb_helper.c @@ -28,7 +28,7 @@ static inline uint64_t merge_syn_data_abort(uint32_t template_syn, ARMMMUFaultInfo *fi, unsigned int target_el, bool same_el, bool is_write, - int fsc) + int fsc, bool gcs) { uint64_t syn; @@ -78,6 +78,7 @@ static inline uint64_t merge_syn_data_abort(uint32_t template_syn, /* Form ISS2 at the top of the syndrome. */ syn |= (uint64_t)fi->dirtybit << 37; + syn |= (uint64_t)gcs << 40; return syn; } @@ -252,9 +253,10 @@ void arm_deliver_fault(ARMCPU *cpu, vaddr addr, syn = syn_insn_abort(same_el, fi->ea, fi->s1ptw, fsc); exc = EXCP_PREFETCH_ABORT; } else { + bool gcs = regime_is_gcs(core_to_arm_mmu_idx(env, mmu_idx)); syn = merge_syn_data_abort(env->exception.syndrome, fi, target_el, same_el, access_type == MMU_DATA_STORE, - fsc); + fsc, gcs); if (access_type == MMU_DATA_STORE && arm_feature(env, ARM_FEATURE_V6)) { fsr |= (1 << 11); From d8bf1761efa7aa0cbda765c153357079b1e0bd3f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:31 -0700 Subject: [PATCH 1473/1794] target/arm: Add GCS cpregs Add isar_feature_aa64_gcs. Enable SCR_GCSEN in scr_write. Enable HCRX_GCSEN in hcrx_write. Default HCRX_GCSEN on if EL2 disabled. Add the GCSCR* and GCSPR* registers. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-32-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpregs-gcs.c | 95 +++++++++++++++++++++++++++++++++++++++ target/arm/cpregs.h | 2 + target/arm/cpu-features.h | 5 +++ target/arm/cpu.c | 3 ++ target/arm/cpu.h | 12 +++++ target/arm/helper.c | 10 +++++ target/arm/internals.h | 3 ++ target/arm/meson.build | 2 + 8 files changed, 132 insertions(+) create mode 100644 target/arm/cpregs-gcs.c diff --git a/target/arm/cpregs-gcs.c b/target/arm/cpregs-gcs.c new file mode 100644 index 0000000000000..1ff041811d213 --- /dev/null +++ b/target/arm/cpregs-gcs.c @@ -0,0 +1,95 @@ +/* + * QEMU ARM CP Register GCS regiters and instructions + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "exec/icount.h" +#include "hw/irq.h" +#include "cpu.h" +#include "cpu-features.h" +#include "cpregs.h" +#include "internals.h" + + +static CPAccessResult access_gcs(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) < 3 + && arm_feature(env, ARM_FEATURE_EL3) + && !(env->cp15.scr_el3 & SCR_GCSEN)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +static CPAccessResult access_gcs_el0(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 0 && !(env->cp15.gcscr_el[0] & GCSCRE0_NTR)) { + return CP_ACCESS_TRAP_EL1; + } + return access_gcs(env, ri, isread); +} + +static void gcspr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Bits [2:0] are RES0, so we might as well clear them now, + * rather than upon each usage a-la GetCurrentGCSPointer. + */ + raw_write(env, ri, value & ~7); +} + +static const ARMCPRegInfo gcs_reginfo[] = { + { .name = "GCSCRE0_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 5, .opc2 = 2, + .access = PL1_RW, .accessfn = access_gcs, .fgt = FGT_NGCS_EL0, + .fieldoffset = offsetof(CPUARMState, cp15.gcscr_el[0]) }, + { .name = "GCSCR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 5, .opc2 = 0, + .access = PL1_RW, .accessfn = access_gcs, .fgt = FGT_NGCS_EL1, + .nv2_redirect_offset = 0x8d0 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 2, 5, 0), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 2, 5, 0), + .fieldoffset = offsetof(CPUARMState, cp15.gcscr_el[1]) }, + { .name = "GCSCR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 5, .opc2 = 0, + .access = PL2_RW, .accessfn = access_gcs, + .fieldoffset = offsetof(CPUARMState, cp15.gcscr_el[2]) }, + { .name = "GCSCR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 5, .opc2 = 0, + .access = PL3_RW, + .fieldoffset = offsetof(CPUARMState, cp15.gcscr_el[3]) }, + + { .name = "GCSPR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 2, .crm = 5, .opc2 = 1, + .access = PL0_R | PL1_W, .accessfn = access_gcs_el0, + .fgt = FGT_NGCS_EL0, .writefn = gcspr_write, + .fieldoffset = offsetof(CPUARMState, cp15.gcspr_el[0]) }, + { .name = "GCSPR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 5, .opc2 = 1, + .access = PL1_RW, .accessfn = access_gcs, + .fgt = FGT_NGCS_EL1, .writefn = gcspr_write, + .nv2_redirect_offset = 0x8c0 | NV2_REDIR_NV1, + .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 2, 5, 1), + .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 2, 5, 1), + .fieldoffset = offsetof(CPUARMState, cp15.gcspr_el[1]) }, + { .name = "GCSPR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 5, .opc2 = 1, + .access = PL2_RW, .accessfn = access_gcs, .writefn = gcspr_write, + .fieldoffset = offsetof(CPUARMState, cp15.gcspr_el[2]) }, + { .name = "GCSPR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 5, .opc2 = 1, + .access = PL3_RW, .writefn = gcspr_write, + .fieldoffset = offsetof(CPUARMState, cp15.gcspr_el[2]) }, +}; + +void define_gcs_cpregs(ARMCPU *cpu) +{ + if (cpu_isar_feature(aa64_gcs, cpu)) { + define_arm_cp_regs(cpu, gcs_reginfo); + } +} diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index f48c4df30fbac..bd2121a336e27 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -779,6 +779,8 @@ typedef enum FGTBit { DO_BIT(HFGRTR, VBAR_EL1), DO_BIT(HFGRTR, ICC_IGRPENN_EL1), DO_BIT(HFGRTR, ERRIDR_EL1), + DO_REV_BIT(HFGRTR, NGCS_EL0), + DO_REV_BIT(HFGRTR, NGCS_EL1), DO_REV_BIT(HFGRTR, NSMPRI_EL1), DO_REV_BIT(HFGRTR, NTPIDR2_EL0), DO_REV_BIT(HFGRTR, NPIRE0_EL1), diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index f3e90408f7b7a..0f0a112c2134b 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -1149,6 +1149,11 @@ static inline bool isar_feature_aa64_nmi(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64PFR1, NMI) != 0; } +static inline bool isar_feature_aa64_gcs(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64PFR1, GCS) != 0; +} + static inline bool isar_feature_aa64_tgran4_lpa2(const ARMISARegisters *id) { return FIELD_SEX64_IDREG(id, ID_AA64MMFR0, TGRAN4) >= 1; diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 9bca1b8eae511..192acac1f5a13 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -635,6 +635,9 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el) if (cpu_isar_feature(aa64_fgt, cpu)) { env->cp15.scr_el3 |= SCR_FGTEN; } + if (cpu_isar_feature(aa64_gcs, cpu)) { + env->cp15.scr_el3 |= SCR_GCSEN; + } if (cpu_isar_feature(aa64_tcr2, cpu)) { env->cp15.scr_el3 |= SCR_TCR2EN; } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 6773676973c6a..e55524ae1075f 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -580,6 +580,9 @@ typedef struct CPUArchState { /* NV2 register */ uint64_t vncr_el2; + + uint64_t gcscr_el[4]; /* GCSCRE0_EL1, GCSCR_EL[123] */ + uint64_t gcspr_el[4]; /* GCSPR_EL[0123] */ } cp15; struct { @@ -1717,6 +1720,7 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_ENAS0 (1ULL << 36) #define SCR_ADEN (1ULL << 37) #define SCR_HXEN (1ULL << 38) +#define SCR_GCSEN (1ULL << 39) #define SCR_TRNDR (1ULL << 40) #define SCR_ENTP2 (1ULL << 41) #define SCR_TCR2EN (1ULL << 43) @@ -1725,6 +1729,14 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_GPF (1ULL << 48) #define SCR_NSE (1ULL << 62) +/* GCSCR_ELx fields */ +#define GCSCR_PCRSEL (1ULL << 0) +#define GCSCR_RVCHKEN (1ULL << 5) +#define GCSCR_EXLOCKEN (1ULL << 6) +#define GCSCR_PUSHMEN (1ULL << 8) +#define GCSCR_STREN (1ULL << 9) +#define GCSCRE0_NTR (1ULL << 10) + /* Return the current FPSCR value. */ uint32_t vfp_get_fpscr(CPUARMState *env); void vfp_set_fpscr(CPUARMState *env, uint32_t val); diff --git a/target/arm/helper.c b/target/arm/helper.c index 6642cae0cc436..64b6c21aef701 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -766,6 +766,9 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) if (cpu_isar_feature(aa64_ecv, cpu)) { valid_mask |= SCR_ECVEN; } + if (cpu_isar_feature(aa64_gcs, cpu)) { + valid_mask |= SCR_GCSEN; + } if (cpu_isar_feature(aa64_tcr2, cpu)) { valid_mask |= SCR_TCR2EN; } @@ -3953,6 +3956,9 @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri, if (cpu_isar_feature(aa64_sctlr2, cpu)) { valid_mask |= HCRX_SCTLR2EN; } + if (cpu_isar_feature(aa64_gcs, cpu)) { + valid_mask |= HCRX_GCSEN; + } /* Clear RES0 bits. */ env->cp15.hcrx_el2 = value & valid_mask; @@ -4023,6 +4029,9 @@ uint64_t arm_hcrx_el2_eff(CPUARMState *env) if (cpu_isar_feature(aa64_sctlr2, cpu)) { hcrx |= HCRX_SCTLR2EN; } + if (cpu_isar_feature(aa64_gcs, cpu)) { + hcrx |= HCRX_GCSEN; + } return hcrx; } if (arm_feature(env, ARM_FEATURE_EL3) && !(env->cp15.scr_el3 & SCR_HXEN)) { @@ -7260,6 +7269,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) } define_pm_cpregs(cpu); + define_gcs_cpregs(cpu); } /* diff --git a/target/arm/internals.h b/target/arm/internals.h index 591b509e68bd9..109aa104bfed8 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -251,6 +251,7 @@ FIELD(VSTCR, SA, 30, 1) #define HCRX_MSCEN (1ULL << 11) #define HCRX_TCR2EN (1ULL << 14) #define HCRX_SCTLR2EN (1ULL << 15) +#define HCRX_GCSEN (1ULL << 22) #define HPFAR_NS (1ULL << 63) @@ -1783,6 +1784,8 @@ void define_tlb_insn_regs(ARMCPU *cpu); void define_at_insn_regs(ARMCPU *cpu); /* Add the cpreg definitions for PM cpregs */ void define_pm_cpregs(ARMCPU *cpu); +/* Add the cpreg definitions for GCS cpregs */ +void define_gcs_cpregs(ARMCPU *cpu); /* Effective value of MDCR_EL2 */ static inline uint64_t arm_mdcr_el2_eff(CPUARMState *env) diff --git a/target/arm/meson.build b/target/arm/meson.build index f9f0beef05ecf..3df7e03654ee6 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -27,6 +27,7 @@ arm_user_ss.add(when: 'TARGET_AARCH64', if_false: files( 'cpu32-stubs.c', )) arm_user_ss.add(files( + 'cpregs-gcs.c', 'cpregs-pmu.c', 'debug_helper.c', 'helper.c', @@ -47,6 +48,7 @@ arm_common_system_ss.add(files( 'arch_dump.c', 'arm-powerctl.c', 'cortex-regs.c', + 'cpregs-gcs.c', 'cpregs-pmu.c', 'cpu-irq.c', 'debug_helper.c', From e5c79193ec92dca13e6eccc60c2a6a77bb8f2fb9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:32 -0700 Subject: [PATCH 1474/1794] target/arm: Add GCS enable and trap levels to DisasContext Pipe GCSEnabled, GCSReturnValueCheckEnabled, and CheckGCSSTREnabled through hflags to the translator. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-33-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.h | 3 +++ target/arm/tcg/hflags.c | 38 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-a64.c | 3 +++ target/arm/tcg/translate.h | 6 ++++++ 4 files changed, 50 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index e55524ae1075f..775219364f927 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2475,6 +2475,9 @@ FIELD(TBFLAG_A64, NV2_MEM_BE, 36, 1) FIELD(TBFLAG_A64, AH, 37, 1) /* FPCR.AH */ FIELD(TBFLAG_A64, NEP, 38, 1) /* FPCR.NEP */ FIELD(TBFLAG_A64, ZT0EXC_EL, 39, 2) +FIELD(TBFLAG_A64, GCS_EN, 41, 1) +FIELD(TBFLAG_A64, GCS_RVCEN, 42, 1) +FIELD(TBFLAG_A64, GCSSTR_EL, 43, 2) /* * Helpers for using the above. Note that only the A64 accessors use diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index 17f83f13a4002..5c9b9bec3b2c6 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -451,6 +451,44 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, DP_TBFLAG_A64(flags, TCMA, aa64_va_parameter_tcma(tcr, mmu_idx)); } + if (cpu_isar_feature(aa64_gcs, env_archcpu(env))) { + /* C.f. GCSEnabled */ + if (env->cp15.gcscr_el[el] & GCSCR_PCRSEL) { + switch (el) { + default: + if (!el_is_in_host(env, el) + && !(arm_hcrx_el2_eff(env) & HCRX_GCSEN)) { + break; + } + /* fall through */ + case 2: + if (arm_feature(env, ARM_FEATURE_EL3) + && !(env->cp15.scr_el3 & SCR_GCSEN)) { + break; + } + /* fall through */ + case 3: + DP_TBFLAG_A64(flags, GCS_EN, 1); + break; + } + } + + /* C.f. GCSReturnValueCheckEnabled */ + if (env->cp15.gcscr_el[el] & GCSCR_RVCHKEN) { + DP_TBFLAG_A64(flags, GCS_RVCEN, 1); + } + + /* C.f. CheckGCSSTREnabled */ + if (!(env->cp15.gcscr_el[el] & GCSCR_STREN)) { + DP_TBFLAG_A64(flags, GCSSTR_EL, el ? el : 1); + } else if (el == 1 + && EX_TBFLAG_ANY(flags, FGT_ACTIVE) + && !FIELD_EX64(env->cp15.fgt_exec[FGTREG_HFGITR], + HFGITR_EL2, NGCSSTR_EL1)) { + DP_TBFLAG_A64(flags, GCSSTR_EL, 2); + } + } + if (env->vfp.fpcr & FPCR_AH) { DP_TBFLAG_A64(flags, AH, 1); } diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index a0e3300231faa..693eab1c012bc 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -10344,6 +10344,9 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->nv2_mem_be = EX_TBFLAG_A64(tb_flags, NV2_MEM_BE); dc->fpcr_ah = EX_TBFLAG_A64(tb_flags, AH); dc->fpcr_nep = EX_TBFLAG_A64(tb_flags, NEP); + dc->gcs_en = EX_TBFLAG_A64(tb_flags, GCS_EN); + dc->gcs_rvcen = EX_TBFLAG_A64(tb_flags, GCS_RVCEN); + dc->gcsstr_el = EX_TBFLAG_A64(tb_flags, GCSSTR_EL); dc->vec_len = 0; dc->vec_stride = 0; dc->cp_regs = arm_cpu->cp_regs; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index f1a6e5e2b61fc..761edded52559 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -166,6 +166,12 @@ typedef struct DisasContext { bool fpcr_ah; /* True if FPCR.NEP is 1 (FEAT_AFP scalar upper-element result handling) */ bool fpcr_nep; + /* True if GCSEnabled. */ + bool gcs_en; + /* True if GCSReturnValueCheckEnabled. */ + bool gcs_rvcen; + /* GCSSTR exception EL or 0 if enabled */ + uint8_t gcsstr_el; /* * >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI. * < 0, set by the current instruction. From 2b6c55fb032967629d07f2ba096f66d3b83a14bd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:33 -0700 Subject: [PATCH 1475/1794] target/arm: Implement FEAT_CHK This feature contains only the CHKFEAT instruction. It has no ID enable, being back-allocated into the hint nop space. Reviewed-by: Gustavo Romero Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-34-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + target/arm/tcg/a64.decode | 1 + target/arm/tcg/translate-a64.c | 14 ++++++++++++++ 3 files changed, 16 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index c779a50570a19..68d9a4e734247 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -28,6 +28,7 @@ the following architecture extensions: - FEAT_BF16 (AArch64 BFloat16 instructions) - FEAT_BTI (Branch Target Identification) - FEAT_CCIDX (Extended cache index) +- FEAT_CHK (Check Feature Status) - FEAT_CMOW (Control for cache maintenance permission) - FEAT_CRC32 (CRC32 instructions) - FEAT_Crypto (Cryptographic Extension) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 55ff6c504f112..8283a9c83d949 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -256,6 +256,7 @@ ERETA 1101011 0100 11111 00001 m:1 11111 11111 &reta # ERETAA, ERETAB AUTIASP 1101 0101 0000 0011 0010 0011 101 11111 AUTIBZ 1101 0101 0000 0011 0010 0011 110 11111 AUTIBSP 1101 0101 0000 0011 0010 0011 111 11111 + CHKFEAT 1101 0101 0000 0011 0010 0101 000 11111 ] # The canonical NOP has CRm == op2 == 0, but all of the space # that isn't specifically allocated to an instruction must NOP diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 693eab1c012bc..78b2881a15569 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2124,6 +2124,20 @@ static bool trans_AUTIBSP(DisasContext *s, arg_AUTIBSP *a) return true; } +static bool trans_CHKFEAT(DisasContext *s, arg_CHKFEAT *a) +{ + uint64_t feat_en = 0; + + if (s->gcs_en) { + feat_en |= 1 << 0; + } + if (feat_en) { + TCGv_i64 x16 = cpu_reg(s, 16); + tcg_gen_andi_i64(x16, x16, ~feat_en); + } + return true; +} + static bool trans_CLREX(DisasContext *s, arg_CLREX *a) { tcg_gen_movi_i64(cpu_exclusive_addr, -1); From 3476d935c3f17f937c7939d91f79cd3bdffc6209 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:34 -0700 Subject: [PATCH 1476/1794] target/arm: Make helper_exception_return system-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20251008215613.300150-35-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-a64.c | 2 ++ target/arm/tcg/helper-a64.h | 5 ++++- target/arm/tcg/translate-a64.c | 8 ++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 71c6c44ee8ac2..6d77fd0113d2c 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -576,6 +576,7 @@ uint32_t HELPER(advsimd_rinth)(uint32_t x, float_status *fp_status) return ret; } +#ifndef CONFIG_USER_ONLY static int el_from_spsr(uint32_t spsr) { /* Return the exception level that this SPSR is requesting a return to, @@ -787,6 +788,7 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) qemu_log_mask(LOG_GUEST_ERROR, "Illegal exception return at EL%d: " "resuming execution at 0x%" PRIx64 "\n", cur_el, env->pc); } +#endif /* !CONFIG_USER_ONLY */ void HELPER(dc_zva)(CPUARMState *env, uint64_t vaddr_in) { diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index 85023465b7686..b6008b5a3ac4d 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -80,7 +80,6 @@ DEF_HELPER_3(vfp_ah_maxh, f16, f16, f16, fpst) DEF_HELPER_3(vfp_ah_maxs, f32, f32, f32, fpst) DEF_HELPER_3(vfp_ah_maxd, f64, f64, f64, fpst) -DEF_HELPER_2(exception_return, void, env, i64) DEF_HELPER_FLAGS_2(dc_zva, TCG_CALL_NO_WG, void, env, i64) DEF_HELPER_FLAGS_3(pacia, TCG_CALL_NO_WG, i64, env, i64, i64) @@ -145,3 +144,7 @@ DEF_HELPER_FLAGS_5(gvec_fmulx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32 DEF_HELPER_FLAGS_5(gvec_fmulx_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmulx_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmulx_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +#ifndef CONFIG_USER_ONLY +DEF_HELPER_2(exception_return, void, env, i64) +#endif diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 78b2881a15569..43c9bfef93fcf 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1870,6 +1870,9 @@ static bool trans_BLRA(DisasContext *s, arg_bra *a) static bool trans_ERET(DisasContext *s, arg_ERET *a) { +#ifdef CONFIG_USER_ONLY + return false; +#else TCGv_i64 dst; if (s->current_el == 0) { @@ -1889,10 +1892,14 @@ static bool trans_ERET(DisasContext *s, arg_ERET *a) /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; return true; +#endif } static bool trans_ERETA(DisasContext *s, arg_reta *a) { +#ifdef CONFIG_USER_ONLY + return false; +#else TCGv_i64 dst; if (!dc_isar_feature(aa64_pauth, s)) { @@ -1918,6 +1925,7 @@ static bool trans_ERETA(DisasContext *s, arg_reta *a) /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; return true; +#endif } static bool trans_NOP(DisasContext *s, arg_NOP *a) From 38f8b024d80d276429dad0adadc67789aa89730a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:35 -0700 Subject: [PATCH 1477/1794] target/arm: Export cpsr_{read_for, write_from}_spsr_elx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move cpsr_write_from_spsr_elx from tcg/helper-a64.c to helper.c, so that it's present with --disable-tcg. Declare both in internals.h. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20251008215613.300150-36-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 20 +++++++++++++++++++- target/arm/internals.h | 2 ++ target/arm/tcg/helper-a64.c | 20 -------------------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 64b6c21aef701..def4edf808cae 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -8896,7 +8896,7 @@ static int aarch64_regnum(CPUARMState *env, int aarch32_reg) } } -static uint32_t cpsr_read_for_spsr_elx(CPUARMState *env) +uint32_t cpsr_read_for_spsr_elx(CPUARMState *env) { uint32_t ret = cpsr_read(env); @@ -8911,6 +8911,24 @@ static uint32_t cpsr_read_for_spsr_elx(CPUARMState *env) return ret; } +void cpsr_write_from_spsr_elx(CPUARMState *env, uint32_t val) +{ + uint32_t mask; + + /* Save SPSR_ELx.SS into PSTATE. */ + env->pstate = (env->pstate & ~PSTATE_SS) | (val & PSTATE_SS); + val &= ~PSTATE_SS; + + /* Move DIT to the correct location for CPSR */ + if (val & PSTATE_DIT) { + val &= ~PSTATE_DIT; + val |= CPSR_DIT; + } + + mask = aarch32_cpsr_valid_mask(env->features, &env_archcpu(env)->isar); + cpsr_write(env, val, mask, CPSRWriteRaw); +} + static bool syndrome_is_sync_extabt(uint32_t syndrome) { /* Return true if this syndrome value is a synchronous external abort */ diff --git a/target/arm/internals.h b/target/arm/internals.h index 109aa104bfed8..2a85ab762d5d3 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1897,6 +1897,8 @@ void vfp_clear_float_status_exc_flags(CPUARMState *env); */ void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask); bool arm_pan_enabled(CPUARMState *env); +uint32_t cpsr_read_for_spsr_elx(CPUARMState *env); +void cpsr_write_from_spsr_elx(CPUARMState *env, uint32_t val); /* Compare uint64_t for qsort and bsearch. */ int compare_u64(const void *a, const void *b); diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 6d77fd0113d2c..eaea7859d3561 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -615,26 +615,6 @@ static int el_from_spsr(uint32_t spsr) } } -static void cpsr_write_from_spsr_elx(CPUARMState *env, - uint32_t val) -{ - uint32_t mask; - - /* Save SPSR_ELx.SS into PSTATE. */ - env->pstate = (env->pstate & ~PSTATE_SS) | (val & PSTATE_SS); - val &= ~PSTATE_SS; - - /* Move DIT to the correct location for CPSR */ - if (val & PSTATE_DIT) { - val &= ~PSTATE_DIT; - val |= CPSR_DIT; - } - - mask = aarch32_cpsr_valid_mask(env->features, \ - &env_archcpu(env)->isar); - cpsr_write(env, val, mask, CPSRWriteRaw); -} - void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) { ARMCPU *cpu = env_archcpu(env); From de83d60a84f25b9518a91bcb996f60a911d03e48 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:36 -0700 Subject: [PATCH 1478/1794] target/arm: Expand pstate to 64 bits The ARM now defines 36 bits in SPSR_ELx in aarch64 mode, so it's time to bite the bullet and extend PSTATE to match. Most changes are straightforward, adjusting printf formats, changing local variable types. More complex is migration, where to maintain backward compatibility a new pstate64 record is introduced, and only when one of the extensions that sets bits 32-35 are active. The fate of gdbstub is left undecided for the moment. Reviewed-by: Peter Maydell Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-37-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 6 +-- target/arm/cpu.h | 8 ++-- target/arm/gdbstub64.c | 2 + target/arm/helper.c | 11 ++--- target/arm/machine.c | 81 +++++++++++++++++++++++++++++++++++++ target/arm/tcg/helper-a64.c | 2 +- target/arm/tcg/translate.h | 20 ++++----- 7 files changed, 107 insertions(+), 23 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 192acac1f5a13..cbb00c3db336a 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -826,7 +826,7 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; - uint32_t psr = pstate_read(env); + uint64_t psr = pstate_read(env); int i, j; int el = arm_current_el(env); uint64_t hcr = arm_hcr_el2_eff(env); @@ -848,7 +848,7 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) } else { ns_status = ""; } - qemu_fprintf(f, "PSTATE=%08x %c%c%c%c %sEL%d%c", + qemu_fprintf(f, "PSTATE=%016" PRIx64 " %c%c%c%c %sEL%d%c", psr, psr & PSTATE_N ? 'N' : '-', psr & PSTATE_Z ? 'Z' : '-', @@ -865,7 +865,7 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) (FIELD_EX64(env->svcr, SVCR, SM) ? 'S' : '-')); } if (cpu_isar_feature(aa64_bti, cpu)) { - qemu_fprintf(f, " BTYPE=%d", (psr & PSTATE_BTYPE) >> 10); + qemu_fprintf(f, " BTYPE=%d", (int)(psr & PSTATE_BTYPE) >> 10); } qemu_fprintf(f, "%s%s%s", (hcr & HCR_NV) ? " NV" : "", diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 775219364f927..b8abfd827624c 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -268,7 +268,7 @@ typedef struct CPUArchState { uint64_t xregs[32]; uint64_t pc; /* PSTATE isn't an architectural register for ARMv8. However, it is - * convenient for us to assemble the underlying state into a 32 bit format + * convenient for us to assemble the underlying state into a 64 bit format * identical to the architectural format used for the SPSR. (This is also * what the Linux kernel's 'pstate' field in signal handlers and KVM's * 'pstate' register are.) Of the PSTATE bits: @@ -280,7 +280,7 @@ typedef struct CPUArchState { * SM and ZA are kept in env->svcr * all other bits are stored in their correct places in env->pstate */ - uint32_t pstate; + uint64_t pstate; bool aarch64; /* True if CPU is in aarch64 state; inverse of PSTATE.nRW */ bool thumb; /* True if CPU is in thumb mode; cpsr[5] */ @@ -1538,7 +1538,7 @@ static inline unsigned int aarch64_pstate_mode(unsigned int el, bool handler) * interprocessing, so we don't attempt to sync with the cpsr state used by * the 32 bit decoder. */ -static inline uint32_t pstate_read(CPUARMState *env) +static inline uint64_t pstate_read(CPUARMState *env) { int ZF; @@ -1548,7 +1548,7 @@ static inline uint32_t pstate_read(CPUARMState *env) | env->pstate | env->daif | (env->btype << 10); } -static inline void pstate_write(CPUARMState *env, uint32_t val) +static inline void pstate_write(CPUARMState *env, uint64_t val) { env->ZF = (~val) & PSTATE_Z; env->NF = val; diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index 3bccde2bf2595..65d6bbe65fb9b 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -47,6 +47,7 @@ int aarch64_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) case 32: return gdb_get_reg64(mem_buf, env->pc); case 33: + /* pstate is now a 64-bit value; can we simply adjust the xml? */ return gdb_get_reg32(mem_buf, pstate_read(env)); } /* Unknown register. */ @@ -75,6 +76,7 @@ int aarch64_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 8; case 33: /* CPSR */ + /* pstate is now a 64-bit value; can we simply adjust the xml? */ pstate_write(env, tmp); return 4; } diff --git a/target/arm/helper.c b/target/arm/helper.c index def4edf808cae..8e342b081184b 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -8961,8 +8961,8 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) CPUARMState *env = &cpu->env; unsigned int new_el = env->exception.target_el; vaddr addr = env->cp15.vbar_el[new_el]; - unsigned int new_mode = aarch64_pstate_mode(new_el, true); - unsigned int old_mode; + uint64_t new_mode = aarch64_pstate_mode(new_el, true); + uint64_t old_mode; unsigned int cur_el = arm_current_el(env); int rt; @@ -9110,7 +9110,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) * If NV2 is disabled, change SPSR when NV,NV1 == 1,0 (I_ZJRNN) * If NV2 is enabled, change SPSR when NV is 1 (I_DBTLM) */ - old_mode = deposit32(old_mode, 2, 2, 2); + old_mode = deposit64(old_mode, 2, 2, 2); } } } else { @@ -9123,7 +9123,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) } env->banked_spsr[aarch64_banked_spsr_index(new_el)] = old_mode; - qemu_log_mask(CPU_LOG_INT, "...with SPSR 0x%x\n", old_mode); + qemu_log_mask(CPU_LOG_INT, "...with SPSR 0x%" PRIx64 "\n", old_mode); qemu_log_mask(CPU_LOG_INT, "...with ELR 0x%" PRIx64 "\n", env->elr_el[new_el]); @@ -9177,7 +9177,8 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) env->pc = addr; - qemu_log_mask(CPU_LOG_INT, "...to EL%d PC 0x%" PRIx64 " PSTATE 0x%x\n", + qemu_log_mask(CPU_LOG_INT, "...to EL%d PC 0x%" PRIx64 + " PSTATE 0x%" PRIx64 "\n", new_el, env->pc, pstate_read(env)); } diff --git a/target/arm/machine.c b/target/arm/machine.c index ce20b46f50f00..44a0cf844b0bc 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -816,6 +816,80 @@ static const VMStateInfo vmstate_cpsr = { .put = put_cpsr, }; +static int get_pstate64(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + uint64_t val = qemu_get_be64(f); + + env->aarch64 = ((val & PSTATE_nRW) == 0); + if (is_a64(env)) { + pstate_write(env, val); + } else { + cpsr_write_from_spsr_elx(env, val); + } + return 0; +} + +static int put_pstate64(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + uint64_t val; + + if (is_a64(env)) { + val = pstate_read(env); + } else { + val = cpsr_read_for_spsr_elx(env); + } + qemu_put_be64(f, val); + return 0; +} + +static bool pstate64_needed(void *opaque) +{ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + uint64_t val; + + if (arm_feature(env, ARM_FEATURE_M)) { + return false; + } + if (is_a64(env)) { + val = pstate_read(env); + } else { + val = cpsr_read_for_spsr_elx(env); + if (val & PSTATE_SS) { + return true; + } + } + return val > UINT32_MAX; +} + +static const VMStateDescription vmstate_pstate64 = { + .name = "cpu/pstate64", + .version_id = 1, + .minimum_version_id = 1, + .needed = pstate64_needed, + .fields = (const VMStateField[]) { + { + .name = "pstate64", + .version_id = 0, + .size = sizeof(uint64_t), + .info = &(const VMStateInfo) { + .name = "pstate64", + .get = get_pstate64, + .put = put_pstate64, + }, + .flags = VMS_SINGLE, + .offset = 0, + }, + VMSTATE_END_OF_LIST() + }, +}; + static int get_power(QEMUFile *f, void *opaque, size_t size, const VMStateField *field) { @@ -1052,6 +1126,12 @@ const VMStateDescription vmstate_arm_cpu = { VMSTATE_UINT32_ARRAY(env.regs, ARMCPU, 16), VMSTATE_UINT64_ARRAY(env.xregs, ARMCPU, 32), VMSTATE_UINT64(env.pc, ARMCPU), + /* + * If any bits are set in the upper 32 bits of cpsr/pstate, + * or if the cpu is in aa32 mode and PSTATE.SS is set, then + * the cpu/pstate64 subsection will override this with the + * full 64 bit state. + */ { .name = "cpsr", .version_id = 0, @@ -1128,6 +1208,7 @@ const VMStateDescription vmstate_arm_cpu = { &vmstate_irq_line_state, &vmstate_wfxt_timer, &vmstate_syndrome64, + &vmstate_pstate64, NULL } }; diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index eaea7859d3561..08b7db7c46783 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -620,7 +620,7 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) ARMCPU *cpu = env_archcpu(env); int cur_el = arm_current_el(env); unsigned int spsr_idx = aarch64_banked_spsr_index(cur_el); - uint32_t spsr = env->banked_spsr[spsr_idx]; + uint64_t spsr = env->banked_spsr[spsr_idx]; int new_el; bool return_to_aa64 = (spsr & PSTATE_nRW) == 0; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 761edded52559..943dfd45fe7be 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -378,27 +378,27 @@ static inline TCGv_i32 get_ahp_flag(void) } /* Set bits within PSTATE. */ -static inline void set_pstate_bits(uint32_t bits) +static inline void set_pstate_bits(uint64_t bits) { - TCGv_i32 p = tcg_temp_new_i32(); + TCGv_i64 p = tcg_temp_new_i64(); tcg_debug_assert(!(bits & CACHED_PSTATE_BITS)); - tcg_gen_ld_i32(p, tcg_env, offsetof(CPUARMState, pstate)); - tcg_gen_ori_i32(p, p, bits); - tcg_gen_st_i32(p, tcg_env, offsetof(CPUARMState, pstate)); + tcg_gen_ld_i64(p, tcg_env, offsetof(CPUARMState, pstate)); + tcg_gen_ori_i64(p, p, bits); + tcg_gen_st_i64(p, tcg_env, offsetof(CPUARMState, pstate)); } /* Clear bits within PSTATE. */ -static inline void clear_pstate_bits(uint32_t bits) +static inline void clear_pstate_bits(uint64_t bits) { - TCGv_i32 p = tcg_temp_new_i32(); + TCGv_i64 p = tcg_temp_new_i64(); tcg_debug_assert(!(bits & CACHED_PSTATE_BITS)); - tcg_gen_ld_i32(p, tcg_env, offsetof(CPUARMState, pstate)); - tcg_gen_andi_i32(p, p, ~bits); - tcg_gen_st_i32(p, tcg_env, offsetof(CPUARMState, pstate)); + tcg_gen_ld_i64(p, tcg_env, offsetof(CPUARMState, pstate)); + tcg_gen_andi_i64(p, p, ~bits); + tcg_gen_st_i64(p, tcg_env, offsetof(CPUARMState, pstate)); } /* If the singlestep state is Active-not-pending, advance to Active-pending. */ From f0fdbfae973de21344706c5fdd9588b24e2395b7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:37 -0700 Subject: [PATCH 1479/1794] target/arm: Add syndrome data for EC_GCS Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-38-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/syndrome.h | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index c48d3b85871d8..bff61f052cc65 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -63,6 +63,7 @@ enum arm_exception_class { EC_MOP = 0x27, EC_AA32_FPTRAP = 0x28, EC_AA64_FPTRAP = 0x2c, + EC_GCS = 0x2d, EC_SERROR = 0x2f, EC_BREAKPOINT = 0x30, EC_BREAKPOINT_SAME_EL = 0x31, @@ -83,6 +84,23 @@ typedef enum { SME_ET_InaccessibleZT0, } SMEExceptionType; +typedef enum { + GCS_ET_DataCheck, + GCS_ET_EXLOCK, + GCS_ET_GCSSTR_GCSSTTR, +} GCSExceptionType; + +typedef enum { + GCS_IT_RET_nPauth = 0, + GCS_IT_GCSPOPM = 1, + GCS_IT_RET_PauthA = 2, + GCS_IT_RET_PauthB = 3, + GCS_IT_GCSSS1 = 4, + GCS_IT_GCSSS2 = 5, + GCS_IT_GCSPOPCX = 8, + GCS_IT_GCSPOPX = 9, +} GCSInstructionType; + #define ARM_EL_EC_LENGTH 6 #define ARM_EL_EC_SHIFT 26 #define ARM_EL_IL_SHIFT 25 @@ -351,6 +369,23 @@ static inline uint32_t syn_pcalignment(void) return (EC_PCALIGNMENT << ARM_EL_EC_SHIFT) | ARM_EL_IL; } +static inline uint32_t syn_gcs_data_check(GCSInstructionType it, int rn) +{ + return ((EC_GCS << ARM_EL_EC_SHIFT) | ARM_EL_IL | + (GCS_ET_DataCheck << 20) | (rn << 5) | it); +} + +static inline uint32_t syn_gcs_exlock(void) +{ + return (EC_GCS << ARM_EL_EC_SHIFT) | ARM_EL_IL | (GCS_ET_EXLOCK << 20); +} + +static inline uint32_t syn_gcs_gcsstr(int ra, int rn) +{ + return ((EC_GCS << ARM_EL_EC_SHIFT) | ARM_EL_IL | + (GCS_ET_GCSSTR_GCSSTTR << 20) | (ra << 10) | (rn << 5)); +} + static inline uint32_t syn_serror(uint32_t extra) { return (EC_SERROR << ARM_EL_EC_SHIFT) | ARM_EL_IL | extra; From 9e703a01f00a156f7944b8150bfb787ce273a4a0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:38 -0700 Subject: [PATCH 1480/1794] target/arm: Add arm_hcr_el2_nvx_eff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the pseudocode function EffectiveHCR_EL2_NVx. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20251008215613.300150-39-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.h | 1 + target/arm/helper.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index b8abfd827624c..54f3d7b1cca2c 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2235,6 +2235,7 @@ static inline bool arm_is_el2_enabled(CPUARMState *env) */ uint64_t arm_hcr_el2_eff_secstate(CPUARMState *env, ARMSecuritySpace space); uint64_t arm_hcr_el2_eff(CPUARMState *env); +uint64_t arm_hcr_el2_nvx_eff(CPUARMState *env); uint64_t arm_hcrx_el2_eff(CPUARMState *env); /* diff --git a/target/arm/helper.c b/target/arm/helper.c index 8e342b081184b..e397fe75bd136 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -3904,6 +3904,16 @@ uint64_t arm_hcr_el2_eff(CPUARMState *env) return arm_hcr_el2_eff_secstate(env, arm_security_space_below_el3(env)); } +uint64_t arm_hcr_el2_nvx_eff(CPUARMState *env) +{ + uint64_t hcr = arm_hcr_el2_eff(env); + + if (!(hcr & HCR_NV)) { + return 0; /* CONSTRAINED UNPREDICTABLE wrt NV1 */ + } + return hcr & (HCR_NV2 | HCR_NV1 | HCR_NV); +} + /* * Corresponds to ARM pseudocode function ELIsInHost(). */ From 3a6a957b89ffcdeed3a39ae9eb268dfbda926a63 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:39 -0700 Subject: [PATCH 1481/1794] target/arm: Use arm_hcr_el2_nvx_eff in access_nv1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20251008215613.300150-40-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index e397fe75bd136..2875f0ddd2e6a 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -3427,7 +3427,7 @@ static CPAccessResult access_nv1(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { if (arm_current_el(env) == 1) { - uint64_t hcr_nv = arm_hcr_el2_eff(env) & (HCR_NV | HCR_NV1 | HCR_NV2); + uint64_t hcr_nv = arm_hcr_el2_nvx_eff(env); if (hcr_nv == (HCR_NV | HCR_NV1)) { return CP_ACCESS_TRAP_EL2; From 399d40b985032ec1364f9906ef6596264a53f2db Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:40 -0700 Subject: [PATCH 1482/1794] target/arm: Split out access_nv1_with_nvx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20251008215613.300150-41-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 2875f0ddd2e6a..5d40266d96b2b 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -3423,15 +3423,16 @@ static void mdcr_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, } } +static CPAccessResult access_nv1_with_nvx(uint64_t hcr_nv) +{ + return hcr_nv == (HCR_NV | HCR_NV1) ? CP_ACCESS_TRAP_EL2 : CP_ACCESS_OK; +} + static CPAccessResult access_nv1(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { if (arm_current_el(env) == 1) { - uint64_t hcr_nv = arm_hcr_el2_nvx_eff(env); - - if (hcr_nv == (HCR_NV | HCR_NV1)) { - return CP_ACCESS_TRAP_EL2; - } + return access_nv1_with_nvx(arm_hcr_el2_nvx_eff(env)); } return CP_ACCESS_OK; } From 766c99beabd5a15ec2f4d2b1372926d8e1b03717 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:41 -0700 Subject: [PATCH 1483/1794] target/arm: Implement EXLOCKException for ELR_ELx and SPSR_ELx If PSTATE.EXLOCK is set, and the GCS EXLOCK enable bit is set, and nested virt is in the appropriate state, then we need to raise an EXLOCK exception. Since PSTATE.EXLOCK cannot be set without GCS being present and enabled, no explicit check for GCS is required. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-42-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpregs.h | 8 +++++ target/arm/cpu.h | 1 + target/arm/helper.c | 67 ++++++++++++++++++++++++++++++++++---- target/arm/tcg/op_helper.c | 7 ++++ 4 files changed, 77 insertions(+), 6 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index bd2121a336e27..a79f00351c8bb 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -351,6 +351,14 @@ typedef enum CPAccessResult { * specified target EL. */ CP_ACCESS_UNDEFINED = (2 << 2), + + /* + * Access fails with EXLOCK, a GCS exception syndrome. + * These traps are always to the current execution EL, + * which is the same as the usual target EL because + * they cannot occur from EL0. + */ + CP_ACCESS_EXLOCK = (3 << 2), } CPAccessResult; /* Indexes into fgt_read[] */ diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 54f3d7b1cca2c..91a851dac1765 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1502,6 +1502,7 @@ void pmu_init(ARMCPU *cpu); #define PSTATE_C (1U << 29) #define PSTATE_Z (1U << 30) #define PSTATE_N (1U << 31) +#define PSTATE_EXLOCK (1ULL << 34) #define PSTATE_NZCV (PSTATE_N | PSTATE_Z | PSTATE_C | PSTATE_V) #define PSTATE_DAIF (PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F) #define CACHED_PSTATE_BITS (PSTATE_NZCV | PSTATE_DAIF | PSTATE_BTYPE) diff --git a/target/arm/helper.c b/target/arm/helper.c index 5d40266d96b2b..1aa0a157b7295 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -3437,6 +3437,61 @@ static CPAccessResult access_nv1(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } +static CPAccessResult access_nv1_or_exlock_el1(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1) { + uint64_t nvx = arm_hcr_el2_nvx_eff(env); + + if (!isread && + (env->pstate & PSTATE_EXLOCK) && + (env->cp15.gcscr_el[1] & GCSCR_EXLOCKEN) && + !(nvx & HCR_NV1)) { + return CP_ACCESS_EXLOCK; + } + return access_nv1_with_nvx(nvx); + } + + /* + * At EL2, since VHE redirection is done at translation time, + * el_is_in_host is always false here, so EXLOCK does not apply. + */ + return CP_ACCESS_OK; +} + +static CPAccessResult access_exlock_el2(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + int el = arm_current_el(env); + + if (el == 3) { + return CP_ACCESS_OK; + } + + /* + * Access to the EL2 register from EL1 means NV is set, and + * EXLOCK has priority over an NV1 trap to EL2. + */ + if (!isread && + (env->pstate & PSTATE_EXLOCK) && + (env->cp15.gcscr_el[el] & GCSCR_EXLOCKEN)) { + return CP_ACCESS_EXLOCK; + } + return CP_ACCESS_OK; +} + +static CPAccessResult access_exlock_el3(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + if (!isread && + (env->pstate & PSTATE_EXLOCK) && + (env->cp15.gcscr_el[3] & GCSCR_EXLOCKEN)) { + return CP_ACCESS_EXLOCK; + } + return CP_ACCESS_OK; +} + #ifdef CONFIG_USER_ONLY /* * `IC IVAU` is handled to improve compatibility with JITs that dual-map their @@ -3608,7 +3663,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "ELR_EL1", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 1, - .access = PL1_RW, .accessfn = access_nv1, + .access = PL1_RW, .accessfn = access_nv1_or_exlock_el1, .nv2_redirect_offset = 0x230 | NV2_REDIR_NV1, .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 4, 0, 1), .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 4, 0, 1), @@ -3616,7 +3671,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "SPSR_EL1", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 0, - .access = PL1_RW, .accessfn = access_nv1, + .access = PL1_RW, .accessfn = access_nv1_or_exlock_el1, .nv2_redirect_offset = 0x160 | NV2_REDIR_NV1, .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 4, 0, 0), .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 4, 0, 0), @@ -4100,7 +4155,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "ELR_EL2", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS | ARM_CP_NV2_REDIRECT, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1, - .access = PL2_RW, + .access = PL2_RW, .accessfn = access_exlock_el2, .fieldoffset = offsetof(CPUARMState, elr_el[2]) }, { .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH, .type = ARM_CP_NV2_REDIRECT, @@ -4118,7 +4173,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "SPSR_EL2", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS | ARM_CP_NV2_REDIRECT, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 0, - .access = PL2_RW, + .access = PL2_RW, .accessfn = access_exlock_el2, .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_HYP]) }, { .name = "VBAR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0, @@ -4400,7 +4455,7 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { { .name = "ELR_EL3", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 6, .crn = 4, .crm = 0, .opc2 = 1, - .access = PL3_RW, + .access = PL3_RW, .accessfn = access_exlock_el3, .fieldoffset = offsetof(CPUARMState, elr_el[3]) }, { .name = "ESR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 5, .crm = 2, .opc2 = 0, @@ -4411,7 +4466,7 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { { .name = "SPSR_EL3", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 6, .crn = 4, .crm = 0, .opc2 = 0, - .access = PL3_RW, + .access = PL3_RW, .accessfn = access_exlock_el3, .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_MON]) }, { .name = "VBAR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 12, .crm = 0, .opc2 = 0, diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index dd3700dc6f228..4fbd219555df1 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -881,6 +881,13 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, } syndrome = syn_uncategorized(); break; + case CP_ACCESS_EXLOCK: + /* + * CP_ACCESS_EXLOCK is always directed to the current EL, + * which is going to be the same as the usual target EL. + */ + syndrome = syn_gcs_exlock(); + break; default: g_assert_not_reached(); } From 49b49214f346da81a79961e7f89225200da274e6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:42 -0700 Subject: [PATCH 1484/1794] target/arm: Split {full,core}_a64_user_mem_index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Separate get_a64_user_mem_index into two separate functions, one which returns the full ARMMMUIdx and one which returns the core mmu_idx. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20251008215613.300150-43-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 43c9bfef93fcf..1337e16a96be5 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -93,7 +93,7 @@ void a64_translate_init(void) } /* - * Return the core mmu_idx to use for A64 load/store insns which + * Return the full arm 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 @@ -103,7 +103,7 @@ void a64_translate_init(void) * normal encoding (in which case we will return the same * thing as get_mem_index(). */ -static int get_a64_user_mem_index(DisasContext *s, bool unpriv) +static ARMMMUIdx full_a64_user_mem_index(DisasContext *s, bool unpriv) { /* * If AccType_UNPRIV is not used, the insn uses AccType_NORMAL, @@ -130,7 +130,13 @@ static int get_a64_user_mem_index(DisasContext *s, bool unpriv) g_assert_not_reached(); } } - return arm_to_core_mmu_idx(useridx); + return useridx; +} + +/* Return the core mmu_idx per above. */ +static int core_a64_user_mem_index(DisasContext *s, bool unpriv) +{ + return arm_to_core_mmu_idx(full_a64_user_mem_index(s, unpriv)); } static void set_btype_raw(int val) @@ -3577,7 +3583,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 = get_a64_user_mem_index(s, a->unpriv); + memidx = core_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); @@ -3598,7 +3604,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 = get_a64_user_mem_index(s, a->unpriv); + int memidx = core_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); @@ -3616,7 +3622,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 = get_a64_user_mem_index(s, a->unpriv); + int memidx = core_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); @@ -4514,7 +4520,7 @@ static bool do_SET(DisasContext *s, arg_set *a, bool is_epilogue, return false; } - memidx = get_a64_user_mem_index(s, a->unpriv); + memidx = core_a64_user_mem_index(s, a->unpriv); /* * We pass option_a == true, matching our implementation; @@ -4568,8 +4574,8 @@ static bool do_CPY(DisasContext *s, arg_cpy *a, bool is_epilogue, CpyFn fn) return false; } - rmemidx = get_a64_user_mem_index(s, runpriv); - wmemidx = get_a64_user_mem_index(s, wunpriv); + rmemidx = core_a64_user_mem_index(s, runpriv); + wmemidx = core_a64_user_mem_index(s, wunpriv); /* * We pass option_a == true, matching our implementation; From 609f5d7692d97ea260eee9236d5741c46d07ff8d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:43 -0700 Subject: [PATCH 1485/1794] target/arm: Introduce delay_exception{_el} Add infrastructure to raise an exception out of line. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-44-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 2 ++ target/arm/tcg/translate.c | 53 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate.h | 20 +++++++++++++ 3 files changed, 75 insertions(+) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1337e16a96be5..c037119cdf924 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -10601,6 +10601,8 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) break; } } + + emit_delayed_exceptions(dc); } const TranslatorOps aarch64_translator_ops = { diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index e62dcc5d85d5a..78d26aac043de 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -1088,6 +1088,57 @@ void gen_exception_insn(DisasContext *s, target_long pc_diff, s->base.is_jmp = DISAS_NORETURN; } +TCGLabel *delay_exception_el(DisasContext *s, int excp, + uint32_t syn, uint32_t target_el) +{ + /* Use tcg_malloc for automatic release on longjmp out of translation. */ + DisasDelayException *e = tcg_malloc(sizeof(DisasDelayException)); + + memset(e, 0, sizeof(*e)); + + /* Save enough of the current state to satisfy gen_exception_insn. */ + e->pc_curr = s->pc_curr; + e->pc_save = s->pc_save; + if (!s->aarch64) { + e->condexec_cond = s->condexec_cond; + e->condexec_mask = s->condexec_mask; + } + + e->excp = excp; + e->syn = syn; + e->target_el = target_el; + + e->next = s->delay_excp_list; + s->delay_excp_list = e; + + e->lab = gen_new_label(); + return e->lab; +} + +TCGLabel *delay_exception(DisasContext *s, int excp, uint32_t syn) +{ + return delay_exception_el(s, excp, syn, 0); +} + +void emit_delayed_exceptions(DisasContext *s) +{ + for (DisasDelayException *e = s->delay_excp_list; e ; e = e->next) { + gen_set_label(e->lab); + + /* Restore the insn state to satisfy gen_exception_insn. */ + s->pc_curr = e->pc_curr; + s->pc_save = e->pc_save; + s->condexec_cond = e->condexec_cond; + s->condexec_mask = e->condexec_mask; + + if (e->target_el) { + gen_exception_insn_el(s, 0, e->excp, e->syn, e->target_el); + } else { + gen_exception_insn(s, 0, e->excp, e->syn); + } + } +} + static void gen_exception_bkpt_insn(DisasContext *s, uint32_t syn) { gen_set_condexec(s); @@ -6791,6 +6842,8 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) gen_goto_tb(dc, 1, curr_insn_len(dc)); } } + + emit_delayed_exceptions(dc); } static const TranslatorOps arm_translator_ops = { diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 943dfd45fe7be..9a85ea74dbb6a 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -21,9 +21,25 @@ typedef struct DisasLabel { target_ulong pc_save; } DisasLabel; +/* + * Emit an exception call out of line. + */ +typedef struct DisasDelayException { + struct DisasDelayException *next; + TCGLabel *lab; + target_long pc_curr; + target_long pc_save; + int condexec_mask; + int condexec_cond; + uint32_t excp; + uint32_t syn; + uint32_t target_el; +} DisasDelayException; + typedef struct DisasContext { DisasContextBase base; const ARMISARegisters *isar; + DisasDelayException *delay_excp_list; /* The address of the current instruction being translated. */ target_ulong pc_curr; @@ -365,6 +381,10 @@ void gen_exception_insn_el(DisasContext *s, target_long pc_diff, int excp, uint32_t syn, uint32_t target_el); void gen_exception_insn(DisasContext *s, target_long pc_diff, int excp, uint32_t syn); +TCGLabel *delay_exception_el(DisasContext *s, int excp, + uint32_t syn, uint32_t target_el); +TCGLabel *delay_exception(DisasContext *s, int excp, uint32_t syn); +void emit_delayed_exceptions(DisasContext *s); /* Return state of Alternate Half-precision flag, caller frees result */ static inline TCGv_i32 get_ahp_flag(void) From 923e7c432ec0fdee97b746f529f238dde2ba78f0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:44 -0700 Subject: [PATCH 1486/1794] target/arm: Emit HSTR trap exception out of line Use delay_exception_el to move the exception out of line. Use TCG_COND_TSTNE instead of separate AND+NE. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-45-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 78d26aac043de..57bc8ea4c54c1 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -1774,21 +1774,11 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, if (maskbit != 4 && maskbit != 14) { /* T4 and T14 are RES0 so never cause traps */ - TCGv_i32 t; - DisasLabel over = gen_disas_label(s); + TCGLabel *fail = delay_exception_el(s, EXCP_UDEF, syndrome, 2); + TCGv_i32 t = + load_cpu_offset(offsetoflow32(CPUARMState, cp15.hstr_el2)); - t = load_cpu_offset(offsetoflow32(CPUARMState, cp15.hstr_el2)); - tcg_gen_andi_i32(t, t, 1u << maskbit); - tcg_gen_brcondi_i32(TCG_COND_EQ, t, 0, over.label); - - gen_exception_insn_el(s, 0, EXCP_UDEF, syndrome, 2); - /* - * gen_exception_insn() will set is_jmp to DISAS_NORETURN, - * but since we're conditionally branching over it, we want - * to assume continue-to-next-instruction. - */ - s->base.is_jmp = DISAS_NEXT; - set_disas_label(s, over); + tcg_gen_brcondi_i32(TCG_COND_TSTNE, t, 1u << maskbit, fail); } } From 0e15a9a53c56f8ddd6a8c6f691a0507429387d91 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:45 -0700 Subject: [PATCH 1487/1794] target/arm: Emit v7m LTPSIZE exception out of line Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-46-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 57bc8ea4c54c1..3df0bbcb7f800 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -5598,11 +5598,10 @@ static bool trans_LE(DisasContext *s, arg_LE *a) if (!a->tp && dc_isar_feature(aa32_mve, s) && fpu_active) { /* Need to do a runtime check for LTPSIZE != 4 */ - DisasLabel skipexc = gen_disas_label(s); + TCGLabel *fail = delay_exception(s, EXCP_INVSTATE, syn_uncategorized()); + tmp = load_cpu_field(v7m.ltpsize); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 4, skipexc.label); - gen_exception_insn(s, 0, EXCP_INVSTATE, syn_uncategorized()); - set_disas_label(s, skipexc); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 4, fail); } if (a->f) { From 6a5596bad37e62800e9d8ed8d35cb6a1af1030ba Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:46 -0700 Subject: [PATCH 1488/1794] target/arm: Implement GCSSTR, GCSSTTR Note that CreateAccDescGCS() does not enable tagchecked, and Data Aborts from GCS instructions do not set iss.isv. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-47-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 3 +++ target/arm/tcg/translate-a64.c | 42 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 8283a9c83d949..2ae73f443a6a8 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -571,6 +571,9 @@ LDAPR_i 10 011001 10 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext LDAPR_i 00 011001 11 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext=1 sz=0 LDAPR_i 01 011001 11 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext=1 sz=1 +# GCSSTR, GCSSTTR +GCSSTR 11011001 000 11111 000 unpriv:1 11 rn:5 rt:5 + # Load/store multiple structures # The 4-bit opcode in [15:12] encodes repeat count and structure elements &ldst_mult rm rn rt sz q p rpt selem diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index c037119cdf924..b72aa968cd2fc 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -139,6 +139,12 @@ static int core_a64_user_mem_index(DisasContext *s, bool unpriv) return arm_to_core_mmu_idx(full_a64_user_mem_index(s, unpriv)); } +/* For a given translation regime, return the core mmu_idx for gcs access. */ +static int core_gcs_mem_index(ARMMMUIdx armidx) +{ + return arm_to_core_mmu_idx(regime_to_gcs(armidx)); +} + static void set_btype_raw(int val) { tcg_gen_st_i32(tcg_constant_i32(val), tcg_env, @@ -3989,6 +3995,42 @@ static bool trans_STLR_i(DisasContext *s, arg_ldapr_stlr_i *a) return true; } +static bool trans_GCSSTR(DisasContext *s, arg_GCSSTR *a) +{ + ARMMMUIdx armidx; + + if (!dc_isar_feature(aa64_gcs, s)) { + return false; + } + + /* + * The pseudocode for GCSSTTR is + * + * effective_el = AArch64.IsUnprivAccessPriv() ? PSTATE.EL : EL0; + * if (effective_el == PSTATE.EL) CheckGCSSTREnabled(); + * + * We have cached the result of IsUnprivAccessPriv in DisasContext, + * but since we need the result of full_a64_user_mem_index anyway, + * use the mmu_idx test as a proxy for the effective_el test. + */ + armidx = full_a64_user_mem_index(s, a->unpriv); + if (armidx == s->mmu_idx && s->gcsstr_el != 0) { + gen_exception_insn_el(s, 0, EXCP_UDEF, + syn_gcs_gcsstr(a->rn, a->rt), + s->gcsstr_el); + return true; + } + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + tcg_gen_qemu_st_i64(cpu_reg(s, a->rt), + clean_data_tbi(s, cpu_reg_sp(s, a->rn)), + core_gcs_mem_index(armidx), + finalize_memop(s, MO_64 | MO_ALIGN)); + return true; +} + static bool trans_LD_mult(DisasContext *s, arg_ldst_mult *a) { TCGv_i64 clean_addr, tcg_rn, tcg_ebytes; From d383c43d6d510638adc69043a92572bec3840515 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:47 -0700 Subject: [PATCH 1489/1794] target/arm: Implement GCSB Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-48-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 1 + target/arm/tcg/translate-a64.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 2ae73f443a6a8..01b1b3e38be06 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -248,6 +248,7 @@ ERETA 1101011 0100 11111 00001 m:1 11111 11111 &reta # ERETAA, ERETAB AUTIA1716 1101 0101 0000 0011 0010 0001 100 11111 AUTIB1716 1101 0101 0000 0011 0010 0001 110 11111 ESB 1101 0101 0000 0011 0010 0010 000 11111 + GCSB 1101 0101 0000 0011 0010 0010 011 11111 PACIAZ 1101 0101 0000 0011 0010 0011 000 11111 PACIASP 1101 0101 0000 0011 0010 0011 001 11111 PACIBZ 1101 0101 0000 0011 0010 0011 010 11111 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index b72aa968cd2fc..38a1f51ed5e29 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2080,6 +2080,14 @@ static bool trans_ESB(DisasContext *s, arg_ESB *a) return true; } +static bool trans_GCSB(DisasContext *s, arg_GCSB *a) +{ + if (dc_isar_feature(aa64_gcs, s)) { + tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); + } + return true; +} + static bool trans_PACIAZ(DisasContext *s, arg_PACIAZ *a) { if (s->pauth_active) { From 3afe6037e728f74be092027b4d24201a6ddc092f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:48 -0700 Subject: [PATCH 1490/1794] target/arm: Implement GCSPUSHM Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-49-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpregs-gcs.c | 15 +++++++++++++++ target/arm/cpregs.h | 3 +++ target/arm/tcg/translate-a64.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/target/arm/cpregs-gcs.c b/target/arm/cpregs-gcs.c index 1ff041811d213..cc5a0b86e4b87 100644 --- a/target/arm/cpregs-gcs.c +++ b/target/arm/cpregs-gcs.c @@ -43,6 +43,16 @@ static void gcspr_write(CPUARMState *env, const ARMCPRegInfo *ri, raw_write(env, ri, value & ~7); } +static CPAccessResult access_gcspushm(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + if (!(env->cp15.gcscr_el[el] & GCSCR_PUSHMEN)) { + return CP_ACCESS_TRAP_BIT | (el ? el : 1); + } + return CP_ACCESS_OK; +} + static const ARMCPRegInfo gcs_reginfo[] = { { .name = "GCSCRE0_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 5, .opc2 = 2, @@ -85,6 +95,11 @@ static const ARMCPRegInfo gcs_reginfo[] = { .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 5, .opc2 = 1, .access = PL3_RW, .writefn = gcspr_write, .fieldoffset = offsetof(CPUARMState, cp15.gcspr_el[2]) }, + + { .name = "GCSPUSHM", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 7, .opc2 = 0, + .access = PL0_W, .accessfn = access_gcspushm, + .fgt = FGT_NGCSPUSHM_EL1, .type = ARM_CP_GCSPUSHM }, }; void define_gcs_cpregs(ARMCPU *cpu) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index a79f00351c8bb..d22ae383cdb1e 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -47,6 +47,8 @@ enum { ARM_CP_DC_ZVA = 0x0005, ARM_CP_DC_GVA = 0x0006, ARM_CP_DC_GZVA = 0x0007, + /* Special: gcs instructions */ + ARM_CP_GCSPUSHM = 0x0008, /* Flag: reads produce resetvalue; writes ignored. */ ARM_CP_CONST = 1 << 4, @@ -871,6 +873,7 @@ typedef enum FGTBit { DO_BIT(HFGITR, DVPRCTX), DO_BIT(HFGITR, CPPRCTX), DO_BIT(HFGITR, DCCVAC), + DO_REV_BIT(HFGITR, NGCSPUSHM_EL1), DO_BIT(HFGITR, ATS1E1A), } FGTBit; diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 38a1f51ed5e29..625563d95be3f 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -26,6 +26,7 @@ #include "cpregs.h" static TCGv_i64 cpu_X[32]; +static TCGv_i64 cpu_gcspr[4]; static TCGv_i64 cpu_pc; /* Load/store exclusive handling */ @@ -77,6 +78,10 @@ static int scale_by_log2_tag_granule(DisasContext *s, int x) /* initialize TCG globals. */ void a64_translate_init(void) { + static const char gcspr_names[4][12] = { + "gcspr_el0", "gcspr_el1", "gcspr_el2", "gcspr_el3" + }; + int i; cpu_pc = tcg_global_mem_new_i64(tcg_env, @@ -90,6 +95,13 @@ void a64_translate_init(void) cpu_exclusive_high = tcg_global_mem_new_i64(tcg_env, offsetof(CPUARMState, exclusive_high), "exclusive_high"); + + for (i = 0; i < 4; i++) { + cpu_gcspr[i] = + tcg_global_mem_new_i64(tcg_env, + offsetof(CPUARMState, cp15.gcspr_el[i]), + gcspr_names[i]); + } } /* @@ -420,6 +432,18 @@ static MemOp check_ordered_align(DisasContext *s, int rn, int imm, return finalize_memop(s, mop); } +static void gen_add_gcs_record(DisasContext *s, TCGv_i64 value) +{ + TCGv_i64 addr = tcg_temp_new_i64(); + TCGv_i64 gcspr = cpu_gcspr[s->current_el]; + int mmuidx = core_gcs_mem_index(s->mmu_idx); + MemOp mop = finalize_memop(s, MO_64 | MO_ALIGN); + + tcg_gen_addi_i64(addr, gcspr, -8); + tcg_gen_qemu_st_i64(value, clean_data_tbi(s, addr), mmuidx, mop); + tcg_gen_mov_i64(gcspr, addr); +} + typedef struct DisasCompare64 { TCGCond cond; TCGv_i64 value; @@ -2803,6 +2827,11 @@ static void handle_sys(DisasContext *s, bool isread, } } return; + case ARM_CP_GCSPUSHM: + if (s->gcs_en) { + gen_add_gcs_record(s, cpu_reg(s, rt)); + } + return; default: g_assert_not_reached(); } From 0982f58db61277ff3babf1ac36c5eaceaadcb598 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:49 -0700 Subject: [PATCH 1491/1794] target/arm: Implement GCSPOPM Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-50-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpregs-gcs.c | 3 +++ target/arm/cpregs.h | 1 + target/arm/tcg/translate-a64.c | 24 ++++++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/target/arm/cpregs-gcs.c b/target/arm/cpregs-gcs.c index cc5a0b86e4b87..15d383b2a4150 100644 --- a/target/arm/cpregs-gcs.c +++ b/target/arm/cpregs-gcs.c @@ -100,6 +100,9 @@ static const ARMCPRegInfo gcs_reginfo[] = { .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 7, .opc2 = 0, .access = PL0_W, .accessfn = access_gcspushm, .fgt = FGT_NGCSPUSHM_EL1, .type = ARM_CP_GCSPUSHM }, + { .name = "GCSPOPM", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 7, .opc2 = 1, + .access = PL0_R, .type = ARM_CP_GCSPOPM }, }; void define_gcs_cpregs(ARMCPU *cpu) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index d22ae383cdb1e..81d8f0e32b1ea 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -49,6 +49,7 @@ enum { ARM_CP_DC_GZVA = 0x0007, /* Special: gcs instructions */ ARM_CP_GCSPUSHM = 0x0008, + ARM_CP_GCSPOPM = 0x0009, /* Flag: reads produce resetvalue; writes ignored. */ ARM_CP_CONST = 1 << 4, diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 625563d95be3f..7783273cc1dea 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2521,6 +2521,24 @@ static void gen_sysreg_undef(DisasContext *s, bool isread, gen_exception_insn(s, 0, EXCP_UDEF, syndrome); } +static void gen_gcspopm(DisasContext *s, int rt) +{ + TCGv_i64 gcspr = cpu_gcspr[s->current_el]; + int mmuidx = core_gcs_mem_index(s->mmu_idx); + MemOp mop = finalize_memop(s, MO_64 | MO_ALIGN); + TCGv_i64 value = tcg_temp_new_i64(); + TCGLabel *fail_label = + delay_exception(s, EXCP_UDEF, syn_gcs_data_check(GCS_IT_GCSPOPM, rt)); + + /* The value at top-of-stack must have low 2 bits clear. */ + tcg_gen_qemu_ld_i64(value, clean_data_tbi(s, gcspr), mmuidx, mop); + tcg_gen_brcondi_i64(TCG_COND_TSTNE, value, 3, fail_label); + + /* Complete the pop and return the value. */ + tcg_gen_addi_i64(gcspr, gcspr, 8); + tcg_gen_mov_i64(cpu_reg(s, rt), value); +} + /* * Look up @key, returning the cpreg, which must exist. * Additionally, the new cpreg must also be accessible. @@ -2832,6 +2850,12 @@ static void handle_sys(DisasContext *s, bool isread, gen_add_gcs_record(s, cpu_reg(s, rt)); } return; + case ARM_CP_GCSPOPM: + /* Note that X[rt] is unchanged if !GCSEnabled. */ + if (s->gcs_en) { + gen_gcspopm(s, rt); + } + return; default: g_assert_not_reached(); } From 6fb1678f90d028b0252699c66e99d6f6ff8127c8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:50 -0700 Subject: [PATCH 1492/1794] target/arm: Implement GCSPUSHX Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-51-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpregs-gcs.c | 17 ++++++++++++++++ target/arm/cpregs.h | 2 ++ target/arm/tcg/translate-a64.c | 37 ++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/target/arm/cpregs-gcs.c b/target/arm/cpregs-gcs.c index 15d383b2a4150..e6c7025d02a68 100644 --- a/target/arm/cpregs-gcs.c +++ b/target/arm/cpregs-gcs.c @@ -53,6 +53,19 @@ static CPAccessResult access_gcspushm(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } +static CPAccessResult access_gcspushx(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + /* Trap if lock taken, and enabled. */ + if (!(env->pstate & PSTATE_EXLOCK)) { + int el = arm_current_el(env); + if (env->cp15.gcscr_el[el] & GCSCR_EXLOCKEN) { + return CP_ACCESS_EXLOCK; + } + } + return CP_ACCESS_OK; +} + static const ARMCPRegInfo gcs_reginfo[] = { { .name = "GCSCRE0_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 5, .opc2 = 2, @@ -103,6 +116,10 @@ static const ARMCPRegInfo gcs_reginfo[] = { { .name = "GCSPOPM", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 7, .opc2 = 1, .access = PL0_R, .type = ARM_CP_GCSPOPM }, + { .name = "GCSPUSHX", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 7, .opc2 = 4, + .access = PL1_W, .accessfn = access_gcspushx, .fgt = FGT_NGCSEPP, + .type = ARM_CP_GCSPUSHX }, }; void define_gcs_cpregs(ARMCPU *cpu) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 81d8f0e32b1ea..909916b7fd7f0 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -50,6 +50,7 @@ enum { /* Special: gcs instructions */ ARM_CP_GCSPUSHM = 0x0008, ARM_CP_GCSPOPM = 0x0009, + ARM_CP_GCSPUSHX = 0x000a, /* Flag: reads produce resetvalue; writes ignored. */ ARM_CP_CONST = 1 << 4, @@ -875,6 +876,7 @@ typedef enum FGTBit { DO_BIT(HFGITR, CPPRCTX), DO_BIT(HFGITR, DCCVAC), DO_REV_BIT(HFGITR, NGCSPUSHM_EL1), + DO_REV_BIT(HFGITR, NGCSEPP), DO_BIT(HFGITR, ATS1E1A), } FGTBit; diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 7783273cc1dea..0df1916d28a0a 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2539,6 +2539,35 @@ static void gen_gcspopm(DisasContext *s, int rt) tcg_gen_mov_i64(cpu_reg(s, rt), value); } +static void gen_gcspushx(DisasContext *s) +{ + TCGv_i64 gcspr = cpu_gcspr[s->current_el]; + int spsr_idx = aarch64_banked_spsr_index(s->current_el); + int spsr_off = offsetof(CPUARMState, banked_spsr[spsr_idx]); + int elr_off = offsetof(CPUARMState, elr_el[s->current_el]); + int mmuidx = core_gcs_mem_index(s->mmu_idx); + MemOp mop = finalize_memop(s, MO_64 | MO_ALIGN); + TCGv_i64 addr = tcg_temp_new_i64(); + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_addi_i64(addr, gcspr, -8); + tcg_gen_qemu_st_i64(cpu_reg(s, 30), addr, mmuidx, mop); + + tcg_gen_ld_i64(tmp, tcg_env, spsr_off); + tcg_gen_addi_i64(addr, addr, -8); + tcg_gen_qemu_st_i64(tmp, addr, mmuidx, mop); + + tcg_gen_ld_i64(tmp, tcg_env, elr_off); + tcg_gen_addi_i64(addr, addr, -8); + tcg_gen_qemu_st_i64(tmp, addr, mmuidx, mop); + + tcg_gen_addi_i64(addr, addr, -8); + tcg_gen_qemu_st_i64(tcg_constant_i64(0b1001), addr, mmuidx, mop); + + tcg_gen_mov_i64(gcspr, addr); + clear_pstate_bits(PSTATE_EXLOCK); +} + /* * Look up @key, returning the cpreg, which must exist. * Additionally, the new cpreg must also be accessible. @@ -2856,6 +2885,14 @@ static void handle_sys(DisasContext *s, bool isread, gen_gcspopm(s, rt); } return; + case ARM_CP_GCSPUSHX: + /* Choose the CONSTRAINED UNPREDICTABLE for UNDEF. */ + if (rt != 31) { + unallocated_encoding(s); + } else if (s->gcs_en) { + gen_gcspushx(s); + } + return; default: g_assert_not_reached(); } From 75c8d645b970ab083ce2ee06aa31c90028441983 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:51 -0700 Subject: [PATCH 1493/1794] target/arm: Implement GCSPOPX Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-52-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpregs-gcs.c | 3 +++ target/arm/cpregs.h | 1 + target/arm/tcg/translate-a64.c | 35 ++++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/target/arm/cpregs-gcs.c b/target/arm/cpregs-gcs.c index e6c7025d02a68..5b5b895a091e7 100644 --- a/target/arm/cpregs-gcs.c +++ b/target/arm/cpregs-gcs.c @@ -120,6 +120,9 @@ static const ARMCPRegInfo gcs_reginfo[] = { .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 7, .opc2 = 4, .access = PL1_W, .accessfn = access_gcspushx, .fgt = FGT_NGCSEPP, .type = ARM_CP_GCSPUSHX }, + { .name = "GCSPOPX", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 7, .opc2 = 6, + .access = PL1_W, .type = ARM_CP_GCSPOPX }, }; void define_gcs_cpregs(ARMCPU *cpu) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 909916b7fd7f0..ccf45fd136d35 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -51,6 +51,7 @@ enum { ARM_CP_GCSPUSHM = 0x0008, ARM_CP_GCSPOPM = 0x0009, ARM_CP_GCSPUSHX = 0x000a, + ARM_CP_GCSPOPX = 0x000b, /* Flag: reads produce resetvalue; writes ignored. */ ARM_CP_CONST = 1 << 4, diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 0df1916d28a0a..72b912a6050ff 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2568,6 +2568,33 @@ static void gen_gcspushx(DisasContext *s) clear_pstate_bits(PSTATE_EXLOCK); } +static void gen_gcspopx(DisasContext *s) +{ + TCGv_i64 gcspr = cpu_gcspr[s->current_el]; + int mmuidx = core_gcs_mem_index(s->mmu_idx); + MemOp mop = finalize_memop(s, MO_64 | MO_ALIGN); + TCGv_i64 addr = tcg_temp_new_i64(); + TCGv_i64 tmp = tcg_temp_new_i64(); + TCGLabel *fail_label = + delay_exception(s, EXCP_UDEF, syn_gcs_data_check(GCS_IT_GCSPOPX, 31)); + + /* The value at top-of-stack must be an exception token. */ + tcg_gen_qemu_ld_i64(tmp, gcspr, mmuidx, mop); + tcg_gen_brcondi_i64(TCG_COND_NE, tmp, 0b1001, fail_label); + + /* + * The other three values in the exception return record + * are ignored, but are loaded anyway to raise faults. + */ + tcg_gen_addi_i64(addr, gcspr, 8); + tcg_gen_qemu_ld_i64(tmp, addr, mmuidx, mop); + tcg_gen_addi_i64(addr, addr, 8); + tcg_gen_qemu_ld_i64(tmp, addr, mmuidx, mop); + tcg_gen_addi_i64(addr, addr, 8); + tcg_gen_qemu_ld_i64(tmp, addr, mmuidx, mop); + tcg_gen_addi_i64(gcspr, addr, 8); +} + /* * Look up @key, returning the cpreg, which must exist. * Additionally, the new cpreg must also be accessible. @@ -2893,6 +2920,14 @@ static void handle_sys(DisasContext *s, bool isread, gen_gcspushx(s); } return; + case ARM_CP_GCSPOPX: + /* Choose the CONSTRAINED UNPREDICTABLE for UNDEF. */ + if (rt != 31) { + unallocated_encoding(s); + } else if (s->gcs_en) { + gen_gcspopx(s); + } + return; default: g_assert_not_reached(); } From a94b6aab8e522825b745dfedc4f4b641d7f22be0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:52 -0700 Subject: [PATCH 1494/1794] target/arm: Implement GCSPOPCX Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-53-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpregs-gcs.c | 17 +++++++++++ target/arm/cpregs.h | 1 + target/arm/tcg/translate-a64.c | 56 ++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/target/arm/cpregs-gcs.c b/target/arm/cpregs-gcs.c index 5b5b895a091e7..3795bf7f3633a 100644 --- a/target/arm/cpregs-gcs.c +++ b/target/arm/cpregs-gcs.c @@ -66,6 +66,19 @@ static CPAccessResult access_gcspushx(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } +static CPAccessResult access_gcspopcx(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + /* Trap if lock not taken, and enabled. */ + if (env->pstate & PSTATE_EXLOCK) { + int el = arm_current_el(env); + if (env->cp15.gcscr_el[el] & GCSCR_EXLOCKEN) { + return CP_ACCESS_EXLOCK; + } + } + return CP_ACCESS_OK; +} + static const ARMCPRegInfo gcs_reginfo[] = { { .name = "GCSCRE0_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 5, .opc2 = 2, @@ -120,6 +133,10 @@ static const ARMCPRegInfo gcs_reginfo[] = { .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 7, .opc2 = 4, .access = PL1_W, .accessfn = access_gcspushx, .fgt = FGT_NGCSEPP, .type = ARM_CP_GCSPUSHX }, + { .name = "GCSPOPCX", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 7, .opc2 = 5, + .access = PL1_W, .accessfn = access_gcspopcx, .fgt = FGT_NGCSEPP, + .type = ARM_CP_GCSPOPCX }, { .name = "GCSPOPX", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 7, .opc2 = 6, .access = PL1_W, .type = ARM_CP_GCSPOPX }, diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index ccf45fd136d35..6d9145109fe42 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -52,6 +52,7 @@ enum { ARM_CP_GCSPOPM = 0x0009, ARM_CP_GCSPUSHX = 0x000a, ARM_CP_GCSPOPX = 0x000b, + ARM_CP_GCSPOPCX = 0x000c, /* Flag: reads produce resetvalue; writes ignored. */ ARM_CP_CONST = 1 << 4, diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 72b912a6050ff..bb8ffba5868e4 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2568,6 +2568,54 @@ static void gen_gcspushx(DisasContext *s) clear_pstate_bits(PSTATE_EXLOCK); } +static void gen_gcspopcx(DisasContext *s) +{ + TCGv_i64 gcspr = cpu_gcspr[s->current_el]; + int spsr_idx = aarch64_banked_spsr_index(s->current_el); + int spsr_off = offsetof(CPUARMState, banked_spsr[spsr_idx]); + int elr_off = offsetof(CPUARMState, elr_el[s->current_el]); + int gcscr_off = offsetof(CPUARMState, cp15.gcscr_el[s->current_el]); + int pstate_off = offsetof(CPUARMState, pstate); + int mmuidx = core_gcs_mem_index(s->mmu_idx); + MemOp mop = finalize_memop(s, MO_64 | MO_ALIGN); + TCGv_i64 addr = tcg_temp_new_i64(); + TCGv_i64 tmp1 = tcg_temp_new_i64(); + TCGv_i64 tmp2 = tcg_temp_new_i64(); + TCGLabel *fail_label = + delay_exception(s, EXCP_UDEF, syn_gcs_data_check(GCS_IT_GCSPOPCX, 31)); + + /* The value at top-of-stack must be an exception token. */ + tcg_gen_qemu_ld_i64(tmp1, gcspr, mmuidx, mop); + tcg_gen_brcondi_i64(TCG_COND_NE, tmp1, 0b1001, fail_label); + + /* Validate in turn, ELR ... */ + tcg_gen_addi_i64(addr, gcspr, 8); + tcg_gen_qemu_ld_i64(tmp1, addr, mmuidx, mop); + tcg_gen_ld_i64(tmp2, tcg_env, elr_off); + tcg_gen_brcond_i64(TCG_COND_NE, tmp1, tmp2, fail_label); + + /* ... SPSR ... */ + tcg_gen_addi_i64(addr, addr, 8); + tcg_gen_qemu_ld_i64(tmp1, addr, mmuidx, mop); + tcg_gen_ld_i64(tmp2, tcg_env, spsr_off); + tcg_gen_brcond_i64(TCG_COND_NE, tmp1, tmp2, fail_label); + + /* ... and LR. */ + tcg_gen_addi_i64(addr, addr, 8); + tcg_gen_qemu_ld_i64(tmp1, addr, mmuidx, mop); + tcg_gen_brcond_i64(TCG_COND_NE, tmp1, cpu_reg(s, 30), fail_label); + + /* Writeback stack pointer after pop. */ + tcg_gen_addi_i64(gcspr, addr, 8); + + /* PSTATE.EXLOCK = GetCurrentEXLOCKEN(). */ + tcg_gen_ld_i64(tmp1, tcg_env, gcscr_off); + tcg_gen_ld_i64(tmp2, tcg_env, pstate_off); + tcg_gen_shri_i64(tmp1, tmp1, ctz64(GCSCR_EXLOCKEN)); + tcg_gen_deposit_i64(tmp2, tmp2, tmp1, ctz64(PSTATE_EXLOCK), 1); + tcg_gen_st_i64(tmp2, tcg_env, pstate_off); +} + static void gen_gcspopx(DisasContext *s) { TCGv_i64 gcspr = cpu_gcspr[s->current_el]; @@ -2920,6 +2968,14 @@ static void handle_sys(DisasContext *s, bool isread, gen_gcspushx(s); } return; + case ARM_CP_GCSPOPCX: + /* Choose the CONSTRAINED UNPREDICTABLE for UNDEF. */ + if (rt != 31) { + unallocated_encoding(s); + } else if (s->gcs_en) { + gen_gcspopcx(s); + } + return; case ARM_CP_GCSPOPX: /* Choose the CONSTRAINED UNPREDICTABLE for UNDEF. */ if (rt != 31) { From 22d20e38044bdebd217ad0339d3765933ef16143 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:53 -0700 Subject: [PATCH 1495/1794] target/arm: Implement GCSSS1 Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-54-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpregs-gcs.c | 3 +++ target/arm/cpregs.h | 1 + target/arm/tcg/translate-a64.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/target/arm/cpregs-gcs.c b/target/arm/cpregs-gcs.c index 3795bf7f3633a..eda5c498d73e1 100644 --- a/target/arm/cpregs-gcs.c +++ b/target/arm/cpregs-gcs.c @@ -129,6 +129,9 @@ static const ARMCPRegInfo gcs_reginfo[] = { { .name = "GCSPOPM", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 7, .opc2 = 1, .access = PL0_R, .type = ARM_CP_GCSPOPM }, + { .name = "GCSSS1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 7, .opc2 = 2, + .access = PL0_W, .type = ARM_CP_GCSSS1 }, { .name = "GCSPUSHX", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 7, .opc2 = 4, .access = PL1_W, .accessfn = access_gcspushx, .fgt = FGT_NGCSEPP, diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 6d9145109fe42..e7e7050880c95 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -53,6 +53,7 @@ enum { ARM_CP_GCSPUSHX = 0x000a, ARM_CP_GCSPOPX = 0x000b, ARM_CP_GCSPOPCX = 0x000c, + ARM_CP_GCSSS1 = 0x000d, /* Flag: reads produce resetvalue; writes ignored. */ ARM_CP_CONST = 1 << 4, diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index bb8ffba5868e4..82ddf4e1dc5c3 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2643,6 +2643,31 @@ static void gen_gcspopx(DisasContext *s) tcg_gen_addi_i64(gcspr, addr, 8); } +static void gen_gcsss1(DisasContext *s, int rt) +{ + TCGv_i64 gcspr = cpu_gcspr[s->current_el]; + int mmuidx = core_gcs_mem_index(s->mmu_idx); + MemOp mop = finalize_memop(s, MO_64 | MO_ALIGN); + TCGv_i64 inptr = cpu_reg(s, rt); + TCGv_i64 cmp = tcg_temp_new_i64(); + TCGv_i64 new = tcg_temp_new_i64(); + TCGv_i64 old = tcg_temp_new_i64(); + TCGLabel *fail_label = + delay_exception(s, EXCP_UDEF, syn_gcs_data_check(GCS_IT_GCSSS1, rt)); + + /* Compute the valid cap entry that the new stack must have. */ + tcg_gen_deposit_i64(cmp, inptr, tcg_constant_i64(1), 0, 12); + /* Compute the in-progress cap entry for the old stack. */ + tcg_gen_deposit_i64(new, gcspr, tcg_constant_i64(5), 0, 3); + + /* Swap the valid cap the with the in-progress cap. */ + tcg_gen_atomic_cmpxchg_i64(old, inptr, cmp, new, mmuidx, mop); + tcg_gen_brcond_i64(TCG_COND_NE, old, cmp, fail_label); + + /* The new stack had a valid cap: change gcspr. */ + tcg_gen_andi_i64(gcspr, inptr, ~7); +} + /* * Look up @key, returning the cpreg, which must exist. * Additionally, the new cpreg must also be accessible. @@ -2984,6 +3009,11 @@ static void handle_sys(DisasContext *s, bool isread, gen_gcspopx(s); } return; + case ARM_CP_GCSSS1: + if (s->gcs_en) { + gen_gcsss1(s, rt); + } + return; default: g_assert_not_reached(); } From e85fc2c62321201222d3e30191f376760978ccca Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:54 -0700 Subject: [PATCH 1496/1794] target/arm: Implement GCSSS2 Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-55-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpregs-gcs.c | 3 +++ target/arm/cpregs.h | 1 + target/arm/tcg/translate-a64.c | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/target/arm/cpregs-gcs.c b/target/arm/cpregs-gcs.c index eda5c498d73e1..1ed52a211a66b 100644 --- a/target/arm/cpregs-gcs.c +++ b/target/arm/cpregs-gcs.c @@ -132,6 +132,9 @@ static const ARMCPRegInfo gcs_reginfo[] = { { .name = "GCSSS1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 7, .opc2 = 2, .access = PL0_W, .type = ARM_CP_GCSSS1 }, + { .name = "GCSSS2", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 7, .opc2 = 3, + .access = PL0_R, .type = ARM_CP_GCSSS2 }, { .name = "GCSPUSHX", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 7, .opc2 = 4, .access = PL1_W, .accessfn = access_gcspushx, .fgt = FGT_NGCSEPP, diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index e7e7050880c95..732c07506d97d 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -54,6 +54,7 @@ enum { ARM_CP_GCSPOPX = 0x000b, ARM_CP_GCSPOPCX = 0x000c, ARM_CP_GCSSS1 = 0x000d, + ARM_CP_GCSSS2 = 0x000e, /* Flag: reads produce resetvalue; writes ignored. */ ARM_CP_CONST = 1 << 4, diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 82ddf4e1dc5c3..d58257be40ec2 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2668,6 +2668,35 @@ static void gen_gcsss1(DisasContext *s, int rt) tcg_gen_andi_i64(gcspr, inptr, ~7); } +static void gen_gcsss2(DisasContext *s, int rt) +{ + TCGv_i64 gcspr = cpu_gcspr[s->current_el]; + int mmuidx = core_gcs_mem_index(s->mmu_idx); + MemOp mop = finalize_memop(s, MO_64 | MO_ALIGN); + TCGv_i64 outptr = tcg_temp_new_i64(); + TCGv_i64 tmp = tcg_temp_new_i64(); + TCGLabel *fail_label = + delay_exception(s, EXCP_UDEF, syn_gcs_data_check(GCS_IT_GCSSS2, rt)); + + /* Validate that the new stack has an in-progress cap. */ + tcg_gen_qemu_ld_i64(outptr, gcspr, mmuidx, mop); + tcg_gen_andi_i64(tmp, outptr, 7); + tcg_gen_brcondi_i64(TCG_COND_NE, tmp, 5, fail_label); + + /* Push a valid cap to the old stack. */ + tcg_gen_andi_i64(outptr, outptr, ~7); + tcg_gen_addi_i64(outptr, outptr, -8); + tcg_gen_deposit_i64(tmp, outptr, tcg_constant_i64(1), 0, 12); + tcg_gen_qemu_st_i64(tmp, outptr, mmuidx, mop); + tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); + + /* Pop the in-progress cap from the new stack. */ + tcg_gen_addi_i64(gcspr, gcspr, 8); + + /* Return a pointer to the old stack cap. */ + tcg_gen_mov_i64(cpu_reg(s, rt), outptr); +} + /* * Look up @key, returning the cpreg, which must exist. * Additionally, the new cpreg must also be accessible. @@ -3014,6 +3043,11 @@ static void handle_sys(DisasContext *s, bool isread, gen_gcsss1(s, rt); } return; + case ARM_CP_GCSSS2: + if (s->gcs_en) { + gen_gcsss2(s, rt); + } + return; default: g_assert_not_reached(); } From a664ba1fc65ee84c9353cb2f0d2187f580b8e6bb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:55 -0700 Subject: [PATCH 1497/1794] target/arm: Add gcs record for BL Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-56-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d58257be40ec2..9a564339fa4dc 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1678,7 +1678,14 @@ static bool trans_B(DisasContext *s, arg_i *a) static bool trans_BL(DisasContext *s, arg_i *a) { - gen_pc_plus_diff(s, cpu_reg(s, 30), curr_insn_len(s)); + TCGv_i64 link = tcg_temp_new_i64(); + + gen_pc_plus_diff(s, link, 4); + if (s->gcs_en) { + gen_add_gcs_record(s, link); + } + tcg_gen_mov_i64(cpu_reg(s, 30), link); + reset_btype(s); gen_goto_tb(s, 0, a->imm); return true; From 978bad65f84aa4780ed586ef588ddb68a5d0532f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:56 -0700 Subject: [PATCH 1498/1794] target/arm: Add gcs record for BLR Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-57-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 9a564339fa4dc..fcfa4223f5c4b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1782,15 +1782,15 @@ static bool trans_BR(DisasContext *s, arg_r *a) static bool trans_BLR(DisasContext *s, arg_r *a) { - TCGv_i64 dst = cpu_reg(s, a->rn); - TCGv_i64 lr = cpu_reg(s, 30); - if (dst == lr) { - TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_mov_i64(tmp, dst); - dst = tmp; + TCGv_i64 link = tcg_temp_new_i64(); + + gen_pc_plus_diff(s, link, 4); + if (s->gcs_en) { + gen_add_gcs_record(s, link); } - gen_pc_plus_diff(s, lr, curr_insn_len(s)); - gen_a64_set_pc(s, dst); + gen_a64_set_pc(s, cpu_reg(s, a->rn)); + tcg_gen_mov_i64(cpu_reg(s, 30), link); + set_btype_for_blr(s); s->base.is_jmp = DISAS_JUMP; return true; From d28bb06dc8ad5667c26d00765c442ca9cf7f1aa1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:57 -0700 Subject: [PATCH 1499/1794] target/arm: Add gcs record for BLR with PAuth Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-58-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index fcfa4223f5c4b..9c3de0cf71771 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1842,21 +1842,21 @@ static bool trans_BRAZ(DisasContext *s, arg_braz *a) static bool trans_BLRAZ(DisasContext *s, arg_braz *a) { - TCGv_i64 dst, lr; + TCGv_i64 dst, link; if (!dc_isar_feature(aa64_pauth, s)) { return false; } - dst = auth_branch_target(s, cpu_reg(s, a->rn), tcg_constant_i64(0), !a->m); - lr = cpu_reg(s, 30); - if (dst == lr) { - TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_mov_i64(tmp, dst); - dst = tmp; + + link = tcg_temp_new_i64(); + gen_pc_plus_diff(s, link, 4); + if (s->gcs_en) { + gen_add_gcs_record(s, link); } - gen_pc_plus_diff(s, lr, curr_insn_len(s)); gen_a64_set_pc(s, dst); + tcg_gen_mov_i64(cpu_reg(s, 30), link); + set_btype_for_blr(s); s->base.is_jmp = DISAS_JUMP; return true; @@ -1892,20 +1892,21 @@ static bool trans_BRA(DisasContext *s, arg_bra *a) static bool trans_BLRA(DisasContext *s, arg_bra *a) { - TCGv_i64 dst, lr; + TCGv_i64 dst, link; if (!dc_isar_feature(aa64_pauth, s)) { return false; } dst = auth_branch_target(s, cpu_reg(s, a->rn), cpu_reg_sp(s, a->rm), !a->m); - lr = cpu_reg(s, 30); - if (dst == lr) { - TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_mov_i64(tmp, dst); - dst = tmp; + + link = tcg_temp_new_i64(); + gen_pc_plus_diff(s, link, 4); + if (s->gcs_en) { + gen_add_gcs_record(s, link); } - gen_pc_plus_diff(s, lr, curr_insn_len(s)); gen_a64_set_pc(s, dst); + tcg_gen_mov_i64(cpu_reg(s, 30), link); + set_btype_for_blr(s); s->base.is_jmp = DISAS_JUMP; return true; From 488fab6fc9470515309ca405ece3c8ae4bf8c37c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:58 -0700 Subject: [PATCH 1500/1794] target/arm: Load gcs record for RET Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-59-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 9c3de0cf71771..dfd5b17831759 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -444,6 +444,27 @@ static void gen_add_gcs_record(DisasContext *s, TCGv_i64 value) tcg_gen_mov_i64(gcspr, addr); } +static void gen_load_check_gcs_record(DisasContext *s, TCGv_i64 target, + GCSInstructionType it, int rt) +{ + TCGv_i64 gcspr = cpu_gcspr[s->current_el]; + int mmuidx = core_gcs_mem_index(s->mmu_idx); + MemOp mop = finalize_memop(s, MO_64 | MO_ALIGN); + TCGv_i64 rec_va = tcg_temp_new_i64(); + + tcg_gen_qemu_ld_i64(rec_va, clean_data_tbi(s, gcspr), mmuidx, mop); + + if (s->gcs_rvcen) { + TCGLabel *fail_label = + delay_exception(s, EXCP_UDEF, syn_gcs_data_check(it, rt)); + + tcg_gen_brcond_i64(TCG_COND_NE, rec_va, target, fail_label); + } + + gen_a64_set_pc(s, rec_va); + tcg_gen_addi_i64(gcspr, gcspr, 8); +} + typedef struct DisasCompare64 { TCGCond cond; TCGv_i64 value; @@ -1798,7 +1819,13 @@ static bool trans_BLR(DisasContext *s, arg_r *a) static bool trans_RET(DisasContext *s, arg_r *a) { - gen_a64_set_pc(s, cpu_reg(s, a->rn)); + TCGv_i64 target = cpu_reg(s, a->rn); + + if (s->gcs_en) { + gen_load_check_gcs_record(s, target, GCS_IT_RET_nPauth, a->rn); + } else { + gen_a64_set_pc(s, target); + } s->base.is_jmp = DISAS_JUMP; return true; } From 3d366bb67ff2eab40052bc09983328b06c394e12 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:55:59 -0700 Subject: [PATCH 1501/1794] target/arm: Load gcs record for RET with PAuth Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-60-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index dfd5b17831759..918d5ed112007 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1898,7 +1898,12 @@ static bool trans_RETA(DisasContext *s, arg_reta *a) } dst = auth_branch_target(s, cpu_reg(s, 30), cpu_X[31], !a->m); - gen_a64_set_pc(s, dst); + if (s->gcs_en) { + GCSInstructionType it = a->m ? GCS_IT_RET_PauthB : GCS_IT_RET_PauthA; + gen_load_check_gcs_record(s, dst, it, 30); + } else { + gen_a64_set_pc(s, dst); + } s->base.is_jmp = DISAS_JUMP; return true; } From d77ed960164490d4daeaa3e1a3532ea53477538a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:56:00 -0700 Subject: [PATCH 1502/1794] target/arm: Copy EXLOCKEn to EXLOCK on exception to the same EL Per R_WTXBY, PSTATE.EXLOCK is 0 on an exception to a higher EL, and copied from EXLOCKEn otherwise. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-61-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 1aa0a157b7295..0077788e1e84a 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9071,8 +9071,13 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) } else { addr += 0x600; } - } else if (pstate_read(env) & PSTATE_SP) { - addr += 0x200; + } else { + if (pstate_read(env) & PSTATE_SP) { + addr += 0x200; + } + if (is_a64(env) && (env->cp15.gcscr_el[new_el] & GCSCR_EXLOCKEN)) { + new_mode |= PSTATE_EXLOCK; + } } switch (cs->exception_index) { From 70440a58589baa9d77428573f146a0ceaa5aa3ef Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:56:01 -0700 Subject: [PATCH 1503/1794] target/arm: Implement EXLOCK check during exception return Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-62-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-a64.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 08b7db7c46783..ba1d775d818eb 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -675,6 +675,17 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) goto illegal_return; } + /* + * If GetCurrentEXLOCKEN, the exception return path must use GCSPOPCX, + * which will set PSTATE.EXLOCK. We need not explicitly check FEAT_GCS, + * because GCSCR_ELx cannot be set without it. + */ + if (new_el == cur_el && + (env->cp15.gcscr_el[cur_el] & GCSCR_EXLOCKEN) && + !(env->pstate & PSTATE_EXLOCK)) { + goto illegal_return; + } + bql_lock(); arm_call_pre_el_change_hook(cpu); bql_unlock(); From 9c5dfe8b568b1b5265044f9f1dbc57345656e342 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:56:02 -0700 Subject: [PATCH 1504/1794] target/arm: Enable FEAT_GCS with -cpu max MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested-by: Thiago Jung Bauermann Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20251008215613.300150-63-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + target/arm/tcg/cpu64.c | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 68d9a4e734247..4496178c48e8a 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -73,6 +73,7 @@ the following architecture extensions: - FEAT_FRINTTS (Floating-point to integer instructions) - FEAT_FlagM (Flag manipulation instructions v2) - FEAT_FlagM2 (Enhancements to flag manipulation instructions) +- FEAT_GCS (Guarded Control Stack Extension) - FEAT_GTG (Guest translation granule size) - FEAT_HAFDBS (Hardware management of the access flag and dirty bit state) - FEAT_HBC (Hinted conditional branches) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index f024db1d29f20..cc42dfdf561f7 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1280,6 +1280,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64PFR1, SME, 2); /* FEAT_SME2 */ t = FIELD_DP64(t, ID_AA64PFR1, CSV2_FRAC, 0); /* FEAT_CSV2_3 */ t = FIELD_DP64(t, ID_AA64PFR1, NMI, 1); /* FEAT_NMI */ + t = FIELD_DP64(t, ID_AA64PFR1, GCS, 1); /* FEAT_GCS */ SET_IDREG(isar, ID_AA64PFR1, t); t = GET_IDREG(isar, ID_AA64MMFR0); From d0e16dcad9b7a95367be91ddd2e74779c47ebf8a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:56:03 -0700 Subject: [PATCH 1505/1794] linux-user/aarch64: Implement prctls for GCS This is PR_GET_SHADOW_STACK_STATUS, PR_SET_SHADOW_STACK_STATUS, and PR_LOCK_SHADOW_STACK_STATUS. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-64-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/aarch64/gcs-internal.h | 38 ++++++++++++ linux-user/aarch64/target_prctl.h | 96 +++++++++++++++++++++++++++++++ linux-user/qemu.h | 5 ++ linux-user/syscall.c | 29 ++++++++++ 4 files changed, 168 insertions(+) create mode 100644 linux-user/aarch64/gcs-internal.h diff --git a/linux-user/aarch64/gcs-internal.h b/linux-user/aarch64/gcs-internal.h new file mode 100644 index 0000000000000..e586c7e80e223 --- /dev/null +++ b/linux-user/aarch64/gcs-internal.h @@ -0,0 +1,38 @@ +/* + * AArch64 gcs functions for linux-user + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef AARCH64_GCS_INTERNAL_H +#define AARCH64_GCS_INTERNAL_H + +#ifndef PR_SHADOW_STACK_ENABLE +# define PR_SHADOW_STACK_ENABLE (1U << 0) +# define PR_SHADOW_STACK_WRITE (1U << 1) +# define PR_SHADOW_STACK_PUSH (1U << 2) +#endif + +static inline uint64_t gcs_get_el0_mode(CPUArchState *env) +{ + uint64_t cr = env->cp15.gcscr_el[0]; + abi_ulong flags = 0; + + flags |= cr & GCSCR_PCRSEL ? PR_SHADOW_STACK_ENABLE : 0; + flags |= cr & GCSCR_STREN ? PR_SHADOW_STACK_WRITE : 0; + flags |= cr & GCSCR_PUSHMEN ? PR_SHADOW_STACK_PUSH : 0; + + return flags; +} + +static inline void gcs_set_el0_mode(CPUArchState *env, uint64_t flags) +{ + uint64_t cr = GCSCRE0_NTR; + + cr |= flags & PR_SHADOW_STACK_ENABLE ? GCSCR_RVCHKEN | GCSCR_PCRSEL : 0; + cr |= flags & PR_SHADOW_STACK_WRITE ? GCSCR_STREN : 0; + cr |= flags & PR_SHADOW_STACK_PUSH ? GCSCR_PUSHMEN : 0; + + env->cp15.gcscr_el[0] = cr; +} + +#endif diff --git a/linux-user/aarch64/target_prctl.h b/linux-user/aarch64/target_prctl.h index ed75b9e4b5ae8..621be5727fc3a 100644 --- a/linux-user/aarch64/target_prctl.h +++ b/linux-user/aarch64/target_prctl.h @@ -6,8 +6,10 @@ #ifndef AARCH64_TARGET_PRCTL_H #define AARCH64_TARGET_PRCTL_H +#include "qemu/units.h" #include "target/arm/cpu-features.h" #include "mte_user_helper.h" +#include "gcs-internal.h" static abi_long do_prctl_sve_get_vl(CPUArchState *env) { @@ -206,4 +208,98 @@ static abi_long do_prctl_get_tagged_addr_ctrl(CPUArchState *env) } #define do_prctl_get_tagged_addr_ctrl do_prctl_get_tagged_addr_ctrl +static abi_long do_prctl_get_shadow_stack_status(CPUArchState *env, + abi_long arg2) +{ + ARMCPU *cpu = env_archcpu(env); + + if (!cpu_isar_feature(aa64_gcs, cpu)) { + return -TARGET_EINVAL; + } + return put_user_ual(gcs_get_el0_mode(env), arg2); +} +#define do_prctl_get_shadow_stack_status do_prctl_get_shadow_stack_status + +static abi_long gcs_alloc(abi_ulong hint, abi_ulong size) +{ + /* + * Without softmmu, we cannot protect GCS memory properly. + * Make do with normal read/write permissions. This at least allows + * emulation of correct programs which don't access the gcs stack + * with normal instructions. + */ + return target_mmap(hint, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | + (hint ? MAP_FIXED_NOREPLACE : 0), -1, 0); +} + +static abi_ulong gcs_new_stack(TaskState *ts) +{ + /* Use guest_stack_size as a proxy for RLIMIT_STACK. */ + abi_ulong size = MIN(MAX(guest_stack_size / 2, TARGET_PAGE_SIZE), 2 * GiB); + abi_ulong base = gcs_alloc(0, size); + + if (base == -1) { + return -1; + } + + ts->gcs_base = base; + ts->gcs_size = size; + return base + size - 8; +} + +static abi_long do_prctl_set_shadow_stack_status(CPUArchState *env, + abi_long new_mode) +{ + ARMCPU *cpu = env_archcpu(env); + TaskState *ts = get_task_state(env_cpu(env)); + abi_long cur_mode; + + if (!cpu_isar_feature(aa64_gcs, cpu)) { + return -TARGET_EINVAL; + } + if (new_mode & ~(PR_SHADOW_STACK_ENABLE | + PR_SHADOW_STACK_WRITE | + PR_SHADOW_STACK_PUSH)) { + return -TARGET_EINVAL; + } + + cur_mode = gcs_get_el0_mode(env); + if ((new_mode ^ cur_mode) & ts->gcs_el0_locked) { + return -TARGET_EBUSY; + } + + if (new_mode & ~cur_mode & PR_SHADOW_STACK_ENABLE) { + abi_long gcspr; + + if (ts->gcs_base || env->cp15.gcspr_el[0]) { + return -EINVAL; + } + gcspr = gcs_new_stack(ts); + if (gcspr == -1) { + return -TARGET_ENOMEM; + } + env->cp15.gcspr_el[0] = gcspr; + } + + gcs_set_el0_mode(env, new_mode); + arm_rebuild_hflags(env); + return 0; +} +#define do_prctl_set_shadow_stack_status do_prctl_set_shadow_stack_status + +static abi_long do_prctl_lock_shadow_stack_status(CPUArchState *env, + abi_long arg2) +{ + ARMCPU *cpu = env_archcpu(env); + TaskState *ts = get_task_state(env_cpu(env)); + + if (!cpu_isar_feature(aa64_gcs, cpu)) { + return -EINVAL; + } + ts->gcs_el0_locked |= arg2; + return 0; +} +#define do_prctl_lock_shadow_stack_status do_prctl_lock_shadow_stack_status + #endif /* AARCH64_TARGET_PRCTL_H */ diff --git a/linux-user/qemu.h b/linux-user/qemu.h index cabb7bd6a8bad..85e68eff7b34a 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -121,6 +121,11 @@ struct TaskState { abi_ulong child_tidptr; #ifdef TARGET_M68K abi_ulong tp_value; +#endif +#if defined(TARGET_AARCH64) + vaddr gcs_base; + abi_ulong gcs_size; + abi_ulong gcs_el0_locked; #endif int used; /* non zero if used */ struct image_info *info; diff --git a/linux-user/syscall.c b/linux-user/syscall.c index d78b2029fa17a..56695de680fec 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6353,6 +6353,11 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) # define PR_SME_VL_LEN_MASK 0xffff # define PR_SME_VL_INHERIT (1 << 17) #endif +#ifndef PR_GET_SHADOW_STACK_STATUS +# define PR_GET_SHADOW_STACK_STATUS 74 +# define PR_SET_SHADOW_STACK_STATUS 75 +# define PR_LOCK_SHADOW_STACK_STATUS 76 +#endif #include "target_prctl.h" @@ -6399,6 +6404,15 @@ static abi_long do_prctl_inval1(CPUArchState *env, abi_long arg2) #ifndef do_prctl_sme_set_vl #define do_prctl_sme_set_vl do_prctl_inval1 #endif +#ifndef do_prctl_get_shadow_stack_status +#define do_prctl_get_shadow_stack_status do_prctl_inval1 +#endif +#ifndef do_prctl_set_shadow_stack_status +#define do_prctl_set_shadow_stack_status do_prctl_inval1 +#endif +#ifndef do_prctl_lock_shadow_stack_status +#define do_prctl_lock_shadow_stack_status do_prctl_inval1 +#endif static abi_long do_prctl_syscall_user_dispatch(CPUArchState *env, abi_ulong arg2, abi_ulong arg3, @@ -6499,6 +6513,21 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2, return -TARGET_EINVAL; } return do_prctl_get_tagged_addr_ctrl(env); + case PR_GET_SHADOW_STACK_STATUS: + if (arg3 || arg4 || arg5) { + return -TARGET_EINVAL; + } + return do_prctl_get_shadow_stack_status(env, arg2); + case PR_SET_SHADOW_STACK_STATUS: + if (arg3 || arg4 || arg5) { + return -TARGET_EINVAL; + } + return do_prctl_set_shadow_stack_status(env, arg2); + case PR_LOCK_SHADOW_STACK_STATUS: + if (arg3 || arg4 || arg5) { + return -TARGET_EINVAL; + } + return do_prctl_lock_shadow_stack_status(env, arg2); case PR_GET_UNALIGN: return do_prctl_get_unalign(env, arg2); From f57359b3f0c85482c35f6668b0815c350ff5d451 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:56:04 -0700 Subject: [PATCH 1506/1794] linux-user/aarch64: Allocate new gcs stack on clone Allocate the new stack early, so that error reporting need not clean up other objects. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-65-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/syscall.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 56695de680fec..dffe6c2016064 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6686,6 +6686,21 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, ts = g_new0(TaskState, 1); init_task_state(ts); +#ifdef TARGET_AARCH64 + /* + * If GCS is enabled in the parent thread, it is also enabled + * in the child thread, but with a newly allocated stack. + */ + abi_long new_gcspr = 0; + if (env->cp15.gcscr_el[0] & GCSCR_PCRSEL) { + new_gcspr = gcs_new_stack(ts); + if (new_gcspr == -1) { + g_free(ts); + return -TARGET_ENOMEM; + } + } +#endif + /* Grab a mutex so that thread setup appears atomic. */ pthread_mutex_lock(&clone_lock); @@ -6707,6 +6722,11 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, ts->info = parent_ts->info; ts->signal_mask = parent_ts->signal_mask; +#ifdef TARGET_AARCH64 + ts->gcs_el0_locked = parent_ts->gcs_el0_locked; + new_env->cp15.gcspr_el[0] = new_gcspr; +#endif + if (flags & CLONE_CHILD_CLEARTID) { ts->child_tidptr = child_tidptr; } From 1c9448037758277eb3dae71f98be3bbfdc91f467 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:56:05 -0700 Subject: [PATCH 1507/1794] linux-user/aarch64: Release gcs stack on thread exit Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-66-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/syscall.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index dffe6c2016064..47a6b58cf5eaa 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -9429,6 +9429,12 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, FUTEX_WAKE, INT_MAX, NULL, NULL, 0); } +#ifdef TARGET_AARCH64 + if (ts->gcs_base) { + target_munmap(ts->gcs_base, ts->gcs_size); + } +#endif + object_unparent(OBJECT(cpu)); object_unref(OBJECT(cpu)); /* From ad1afe433fa18691306451cf0f79bd2568ef9709 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:56:06 -0700 Subject: [PATCH 1508/1794] linux-user/aarch64: Implement map_shadow_stack syscall Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-67-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/syscall.c | 59 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 47a6b58cf5eaa..8546f48a05b05 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6358,6 +6358,12 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) # define PR_SET_SHADOW_STACK_STATUS 75 # define PR_LOCK_SHADOW_STACK_STATUS 76 #endif +#ifndef SHADOW_STACK_SET_TOKEN +# define SHADOW_STACK_SET_TOKEN (1u << 0) +#endif +#ifndef SHADOW_STACK_SET_MARKER +# define SHADOW_STACK_SET_MARKER (1u << 1) +#endif #include "target_prctl.h" @@ -6605,6 +6611,54 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2, } } +#ifdef TARGET_AARCH64 +static abi_long do_map_shadow_stack(CPUArchState *env, abi_ulong addr, + abi_ulong size, abi_int flags) +{ + ARMCPU *cpu = env_archcpu(env); + abi_ulong alloc_size; + + if (!cpu_isar_feature(aa64_gcs, cpu)) { + return -TARGET_EOPNOTSUPP; + } + if (flags & ~(SHADOW_STACK_SET_TOKEN | SHADOW_STACK_SET_MARKER)) { + return -TARGET_EINVAL; + } + if (addr & ~TARGET_PAGE_MASK) { + return -TARGET_EINVAL; + } + if (size == 8 || !QEMU_IS_ALIGNED(size, 8)) { + return -TARGET_EINVAL; + } + + alloc_size = TARGET_PAGE_ALIGN(size); + if (alloc_size < size) { + return -TARGET_EOVERFLOW; + } + + mmap_lock(); + addr = gcs_alloc(addr, alloc_size); + if (addr != -1) { + if (flags & SHADOW_STACK_SET_TOKEN) { + abi_ptr cap_ptr = addr + size - 8; + uint64_t cap_val; + + if (flags & SHADOW_STACK_SET_MARKER) { + /* Leave an extra empty frame at top-of-stack. */ + cap_ptr -= 8; + } + cap_val = (cap_ptr & TARGET_PAGE_MASK) | 1; + if (put_user_u64(cap_val, cap_ptr)) { + /* Allocation succeeded above. */ + g_assert_not_reached(); + } + } + } + mmap_unlock(); + return get_errno(addr); +} +#endif + #define NEW_STACK_SIZE 0x40000 @@ -14065,6 +14119,11 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return do_riscv_hwprobe(cpu_env, arg1, arg2, arg3, arg4, arg5); #endif +#ifdef TARGET_AARCH64 + case TARGET_NR_map_shadow_stack: + return do_map_shadow_stack(cpu_env, arg1, arg2, arg3); +#endif + default: qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num); return -TARGET_ENOSYS; From 5149287e10c903f2c629f127ec90415b2d1703d5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:56:07 -0700 Subject: [PATCH 1509/1794] target/arm: Enable GCSPR_EL0 for read in user-mode Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-68-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index cbb00c3db336a..11e7c10cef577 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -311,6 +311,10 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) env->cp15.mdscr_el1 |= 1 << 12; /* Enable FEAT_MOPS */ env->cp15.sctlr_el[1] |= SCTLR_MSCEN; + /* For Linux, GCSPR_EL0 is always readable. */ + if (cpu_isar_feature(aa64_gcs, cpu)) { + env->cp15.gcscr_el[0] = GCSCRE0_NTR; + } #else /* Reset into the highest available EL */ if (arm_feature(env, ARM_FEATURE_EL3)) { From 37897b29b3a55fd60bb75ab12ff52d0b1bf72bef Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:56:08 -0700 Subject: [PATCH 1510/1794] linux-user/aarch64: Inject SIGSEGV for GCS faults Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-69-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/aarch64/cpu_loop.c | 5 +++++ linux-user/aarch64/target_signal.h | 1 + 2 files changed, 6 insertions(+) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 50a4c99535d3b..7f66a879ea98d 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -89,6 +89,11 @@ static void signal_for_exception(CPUARMState *env, vaddr addr) si_code = TARGET_ILL_ILLOPN; break; + case EC_GCS: + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_CPERR; + break; + case EC_MOP: /* * FIXME: The kernel fixes up wrong-option exceptions. diff --git a/linux-user/aarch64/target_signal.h b/linux-user/aarch64/target_signal.h index 6f66a50bfd2a6..e509ac10327b0 100644 --- a/linux-user/aarch64/target_signal.h +++ b/linux-user/aarch64/target_signal.h @@ -7,6 +7,7 @@ #define TARGET_SEGV_MTEAERR 8 /* Asynchronous ARM MTE error */ #define TARGET_SEGV_MTESERR 9 /* Synchronous ARM MTE exception */ +#define TARGET_SEGV_CPERR 10 /* Control protection fault */ #define TARGET_ARCH_HAS_SETUP_FRAME #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 From ef110c3070aff0994c3c5601a6e37c43ac793834 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:56:09 -0700 Subject: [PATCH 1511/1794] linux-user/aarch64: Generate GCS signal records Here we must push and pop a cap on the GCS stack as well as the gcs record on the normal stack. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-70-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/aarch64/signal.c | 138 ++++++++++++++++++++++++++++++++++-- 1 file changed, 132 insertions(+), 6 deletions(-) diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index ef97be3ac7bd3..f7edfa249e5d5 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -22,6 +22,7 @@ #include "signal-common.h" #include "linux-user/trace.h" #include "target/arm/cpu-features.h" +#include "gcs-internal.h" struct target_sigcontext { uint64_t fault_address; @@ -152,6 +153,16 @@ struct target_zt_context { QEMU_BUILD_BUG_ON(TARGET_ZT_SIG_REG_BYTES != \ sizeof_field(CPUARMState, za_state.zt0)); +#define TARGET_GCS_MAGIC 0x47435300 +#define GCS_SIGNAL_CAP(X) ((X) & TARGET_PAGE_MASK) + +struct target_gcs_context { + struct target_aarch64_ctx head; + uint64_t gcspr; + uint64_t features_enabled; + uint64_t reserved; +}; + struct target_rt_sigframe { struct target_siginfo info; struct target_ucontext uc; @@ -322,6 +333,35 @@ static void target_setup_zt_record(struct target_zt_context *zt, } } +static bool target_setup_gcs_record(struct target_gcs_context *ctx, + CPUARMState *env, uint64_t return_addr) +{ + uint64_t mode = gcs_get_el0_mode(env); + uint64_t gcspr = env->cp15.gcspr_el[0]; + + if (mode & PR_SHADOW_STACK_ENABLE) { + /* Push a cap for the signal frame. */ + gcspr -= 8; + if (put_user_u64(GCS_SIGNAL_CAP(gcspr), gcspr)) { + return false; + } + + /* Push a gcs entry for the trampoline. */ + if (put_user_u64(return_addr, gcspr - 8)) { + return false; + } + env->cp15.gcspr_el[0] = gcspr - 8; + } + + __put_user(TARGET_GCS_MAGIC, &ctx->head.magic); + __put_user(sizeof(*ctx), &ctx->head.size); + __put_user(gcspr, &ctx->gcspr); + __put_user(mode, &ctx->features_enabled); + __put_user(0, &ctx->reserved); + + return true; +} + static void target_restore_general_frame(CPUARMState *env, struct target_rt_sigframe *sf) { @@ -502,6 +542,64 @@ static bool target_restore_zt_record(CPUARMState *env, return true; } +static bool target_restore_gcs_record(CPUARMState *env, + struct target_gcs_context *ctx, + bool *rebuild_hflags) +{ + TaskState *ts = get_task_state(env_cpu(env)); + uint64_t cur_mode = gcs_get_el0_mode(env); + uint64_t new_mode, gcspr; + + __get_user(new_mode, &ctx->features_enabled); + __get_user(gcspr, &ctx->gcspr); + + /* + * The kernel pushes the value through the hw register: + * write_sysreg_s(gcspr, SYS_GCSPR_EL0) in restore_gcs_context, + * then read_sysreg_s(SYS_GCSPR_EL0) in gcs_restore_signal. + * Since the bottom 3 bits are RES0, this can (CONSTRAINED UNPREDICTABLE) + * force align the value. Mirror the choice from gcspr_write(). + */ + gcspr &= ~7; + + if (new_mode & ~(PR_SHADOW_STACK_ENABLE | + PR_SHADOW_STACK_WRITE | + PR_SHADOW_STACK_PUSH)) { + return false; + } + if ((new_mode ^ cur_mode) & ts->gcs_el0_locked) { + return false; + } + if (new_mode & ~cur_mode & PR_SHADOW_STACK_ENABLE) { + return false; + } + + if (new_mode & PR_SHADOW_STACK_ENABLE) { + uint64_t cap; + + /* Pop and clear the signal cap. */ + if (get_user_u64(cap, gcspr)) { + return false; + } + if (cap != GCS_SIGNAL_CAP(gcspr)) { + return false; + } + if (put_user_u64(0, gcspr)) { + return false; + } + gcspr += 8; + } else { + new_mode = 0; + } + + env->cp15.gcspr_el[0] = gcspr; + if (new_mode != cur_mode) { + *rebuild_hflags = true; + gcs_set_el0_mode(env, new_mode); + } + return true; +} + static int target_restore_sigframe(CPUARMState *env, struct target_rt_sigframe *sf) { @@ -511,8 +609,10 @@ static int target_restore_sigframe(CPUARMState *env, struct target_za_context *za = NULL; struct target_tpidr2_context *tpidr2 = NULL; struct target_zt_context *zt = NULL; + struct target_gcs_context *gcs = NULL; uint64_t extra_datap = 0; bool used_extra = false; + bool rebuild_hflags = false; int sve_size = 0; int za_size = 0; int zt_size = 0; @@ -582,6 +682,15 @@ static int target_restore_sigframe(CPUARMState *env, zt_size = size; break; + case TARGET_GCS_MAGIC: + if (gcs + || size != sizeof(struct target_gcs_context) + || !cpu_isar_feature(aa64_gcs, env_archcpu(env))) { + goto err; + } + gcs = (struct target_gcs_context *)ctx; + break; + case TARGET_EXTRA_MAGIC: if (extra || size != sizeof(struct target_extra_context)) { goto err; @@ -612,6 +721,10 @@ static int target_restore_sigframe(CPUARMState *env, goto err; } + if (gcs && !target_restore_gcs_record(env, gcs, &rebuild_hflags)) { + goto err; + } + /* SVE data, if present, overwrites FPSIMD data. */ if (sve && !target_restore_sve_record(env, sve, sve_size, &svcr)) { goto err; @@ -631,6 +744,9 @@ static int target_restore_sigframe(CPUARMState *env, } if (env->svcr != svcr) { env->svcr = svcr; + rebuild_hflags = true; + } + if (rebuild_hflags) { arm_rebuild_hflags(env); } unlock_user(extra, extra_datap, 0); @@ -701,7 +817,7 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, uc.tuc_mcontext.__reserved), }; int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0, tpidr2_ofs = 0; - int zt_ofs = 0, esr_ofs = 0; + int zt_ofs = 0, esr_ofs = 0, gcs_ofs = 0; int sve_size = 0, za_size = 0, tpidr2_size = 0, zt_size = 0; struct target_rt_sigframe *frame; struct target_rt_frame_record *fr; @@ -720,6 +836,11 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, &layout); } + if (env->cp15.gcspr_el[0]) { + gcs_ofs = alloc_sigframe_space(sizeof(struct target_gcs_context), + &layout); + } + /* SVE state needs saving only if it exists. */ if (cpu_isar_feature(aa64_sve, env_archcpu(env)) || cpu_isar_feature(aa64_sme, env_archcpu(env))) { @@ -779,6 +900,12 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, goto give_sigsegv; } + if (ka->sa_flags & TARGET_SA_RESTORER) { + return_addr = ka->sa_restorer; + } else { + return_addr = default_rt_sigreturn; + } + target_setup_general_frame(frame, env, set); target_setup_fpsimd_record((void *)frame + fpsimd_ofs, env); if (esr_ofs) { @@ -786,6 +913,10 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, /* Leave ESR_EL1 clear while it's not relevant. */ env->cp15.esr_el[1] = 0; } + if (gcs_ofs && + !target_setup_gcs_record((void *)frame + gcs_ofs, env, return_addr)) { + goto give_sigsegv; + } target_setup_end_record((void *)frame + layout.std_end_ofs); if (layout.extra_ofs) { target_setup_extra_record((void *)frame + layout.extra_ofs, @@ -811,11 +942,6 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, __put_user(env->xregs[29], &fr->fp); __put_user(env->xregs[30], &fr->lr); - if (ka->sa_flags & TARGET_SA_RESTORER) { - return_addr = ka->sa_restorer; - } else { - return_addr = default_rt_sigreturn; - } env->xregs[0] = usig; env->xregs[29] = frame_addr + fr_ofs; env->xregs[30] = return_addr; From d2687ad312f4ec776398d4badad287b421c8fbc0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:56:10 -0700 Subject: [PATCH 1512/1794] linux-user/aarch64: Enable GCS in HWCAP Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-71-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/aarch64/elfload.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 77d03b50e1b93..3af5a3777616a 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -169,6 +169,7 @@ abi_ulong get_elf_hwcap(CPUState *cs) GET_FEATURE_ID(aa64_dcpop, ARM_HWCAP_A64_DCPOP); GET_FEATURE_ID(aa64_rcpc_8_3, ARM_HWCAP_A64_LRCPC); GET_FEATURE_ID(aa64_rcpc_8_4, ARM_HWCAP_A64_ILRCPC); + GET_FEATURE_ID(aa64_gcs, ARM_HWCAP_A64_GCS); return hwcaps; } From 3f3cc39b7d263faf453b6a165f56425e46a2f44e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:56:11 -0700 Subject: [PATCH 1513/1794] tests/tcg/aarch64: Add gcsstr Add some infrastructure for testing gcs in userspace. Validate successful and trapped executions of GCSSTR. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-72-richard.henderson@linaro.org [PMM: fixed hardcoded tabs] Signed-off-by: Peter Maydell --- tests/tcg/aarch64/Makefile.target | 5 +++ tests/tcg/aarch64/gcs.h | 71 +++++++++++++++++++++++++++++++ tests/tcg/aarch64/gcsstr.c | 48 +++++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 tests/tcg/aarch64/gcs.h create mode 100644 tests/tcg/aarch64/gcsstr.c diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 1755874beed90..5e1b3a3385148 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -75,6 +75,11 @@ AARCH64_TESTS += $(SME_TESTS) $(SME_TESTS): CFLAGS += $(CROSS_AS_HAS_ARMV9_SME) endif +# GCS Tests +GCS_TESTS += gcsstr +AARCH64_TESTS += $(GCS_TESTS) +$(GCS_TESTS): gcs.h + # System Registers Tests AARCH64_TESTS += sysregs diff --git a/tests/tcg/aarch64/gcs.h b/tests/tcg/aarch64/gcs.h new file mode 100644 index 0000000000000..770cde6a85adc --- /dev/null +++ b/tests/tcg/aarch64/gcs.h @@ -0,0 +1,71 @@ +/* + * Linux kernel fallback API definitions for GCS and test helpers. + * + * Copyright (c) 2025 Linaro Ltd + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef PR_GET_SHADOW_STACK_STATUS +#define PR_GET_SHADOW_STACK_STATUS 74 +#endif +#ifndef PR_SET_SHADOW_STACK_STATUS +#define PR_SET_SHADOW_STACK_STATUS 75 +#endif +#ifndef PR_LOCK_SHADOW_STACK_STATUS +#define PR_LOCK_SHADOW_STACK_STATUS 76 +#endif +#ifndef PR_SHADOW_STACK_ENABLE +# define PR_SHADOW_STACK_ENABLE (1 << 0) +# define PR_SHADOW_STACK_WRITE (1 << 1) +# define PR_SHADOW_STACK_PUSH (1 << 2) +#endif +#ifndef SHADOW_STACK_SET_TOKEN +#define SHADOW_STACK_SET_TOKEN (1 << 0) +#endif +#ifndef SHADOW_STACK_SET_MARKER +#define SHADOW_STACK_SET_MARKER (1 << 1) +#endif +#ifndef SEGV_CPERR +#define SEGV_CPERR 10 +#endif +#ifndef __NR_map_shadow_stack +#define __NR_map_shadow_stack 453 +#endif + +/* + * Macros, and implement the syscall inline, lest we fail + * the checked return from any function call. + */ +#define enable_gcs(flags) \ + do { \ + register long num __asm__ ("x8") = __NR_prctl; \ + register long arg1 __asm__ ("x0") = PR_SET_SHADOW_STACK_STATUS; \ + register long arg2 __asm__ ("x1") = PR_SHADOW_STACK_ENABLE | flags; \ + register long arg3 __asm__ ("x2") = 0; \ + register long arg4 __asm__ ("x3") = 0; \ + register long arg5 __asm__ ("x4") = 0; \ + asm volatile("svc #0" \ + : "+r"(arg1) \ + : "r"(arg2), "r"(arg3), "r"(arg4), "r"(arg5), "r"(num) \ + : "memory", "cc"); \ + if (arg1) { \ + errno = -arg1; \ + perror("PR_SET_SHADOW_STACK_STATUS"); \ + exit(2); \ + } \ + } while (0) + +#define gcspr() \ + ({ uint64_t *r; asm volatile("mrs %0, s3_3_c2_c5_1" : "=r"(r)); r; }) diff --git a/tests/tcg/aarch64/gcsstr.c b/tests/tcg/aarch64/gcsstr.c new file mode 100644 index 0000000000000..b045aee925156 --- /dev/null +++ b/tests/tcg/aarch64/gcsstr.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gcs.h" + +/* + * A single garbage store to the gcs stack. + * The asm inside must be unique, so disallow inlining. + */ +void __attribute__((noinline)) +test_gcsstr(void) +{ + register uint64_t *ptr __asm__("x0") = gcspr(); + /* GCSSTR x1, x0 */ + __asm__("inst_gcsstr: .inst 0xd91f1c01" : : "r"(--ptr)); +} + +static void test_sigsegv(int sig, siginfo_t *info, void *vuc) +{ + ucontext_t *uc = vuc; + uint64_t inst_gcsstr; + + __asm__("adr %0, inst_gcsstr" : "=r"(inst_gcsstr)); + assert(uc->uc_mcontext.pc == inst_gcsstr); + assert(info->si_code == SEGV_CPERR); + /* TODO: Dig for ESR and verify syndrome. */ + exit(0); +} + +int main() +{ + struct sigaction sa = { + .sa_sigaction = test_sigsegv, + .sa_flags = SA_SIGINFO, + }; + + /* Enable GCSSTR and test the store succeeds. */ + enable_gcs(PR_SHADOW_STACK_WRITE); + test_gcsstr(); + + /* Disable GCSSTR and test the resulting sigsegv. */ + enable_gcs(0); + if (sigaction(SIGSEGV, &sa, NULL) < 0) { + perror("sigaction"); + exit(1); + } + test_gcsstr(); + abort(); +} From 8f278acdce11cce89e5599bde0de76692ae1b1d6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:56:12 -0700 Subject: [PATCH 1514/1794] tests/tcg/aarch64: Add gcspushm Validate successful and trapped executions of GCSPUSHM, GCSPOPM. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-73-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- tests/tcg/aarch64/Makefile.target | 2 +- tests/tcg/aarch64/gcspushm.c | 71 +++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/aarch64/gcspushm.c diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 5e1b3a3385148..fddb7bc9cdc4d 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -76,7 +76,7 @@ $(SME_TESTS): CFLAGS += $(CROSS_AS_HAS_ARMV9_SME) endif # GCS Tests -GCS_TESTS += gcsstr +GCS_TESTS += gcsstr gcspushm AARCH64_TESTS += $(GCS_TESTS) $(GCS_TESTS): gcs.h diff --git a/tests/tcg/aarch64/gcspushm.c b/tests/tcg/aarch64/gcspushm.c new file mode 100644 index 0000000000000..c330417a2fa37 --- /dev/null +++ b/tests/tcg/aarch64/gcspushm.c @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gcs.h" + + +#define GCSPUSHM "sys #3, c7, c7, #0, %[push]" +#define GCSPOPM "sysl %[pop], #3, c7, c7, #1" + +static void test_sigsegv(int sig, siginfo_t *info, void *vuc) +{ + ucontext_t *uc = vuc; + uint64_t inst_sigsegv; + + __asm__("adr %0, inst_sigsegv" : "=r"(inst_sigsegv)); + assert(uc->uc_mcontext.pc == inst_sigsegv); + assert(info->si_code == SEGV_CPERR); + /* TODO: Dig for ESR and verify syndrome. */ + uc->uc_mcontext.pc += 4; +} + +static void test_sigill(int sig, siginfo_t *info, void *vuc) +{ + ucontext_t *uc = vuc; + uint64_t inst_sigill; + + __asm__("adr %0, inst_sigill" : "=r"(inst_sigill)); + assert(uc->uc_mcontext.pc == inst_sigill); + assert(info->si_code == ILL_ILLOPC); + uc->uc_mcontext.pc += 4; +} + +int main() +{ + struct sigaction sa = { .sa_flags = SA_SIGINFO }; + uint64_t old, new; + + sa.sa_sigaction = test_sigsegv; + if (sigaction(SIGSEGV, &sa, NULL) < 0) { + perror("sigaction"); + exit(1); + } + + sa.sa_sigaction = test_sigill; + if (sigaction(SIGILL, &sa, NULL) < 0) { + perror("sigaction"); + exit(1); + } + + /* Pushm is disabled -- SIGILL via EC_SYSTEMREGISTERTRAP */ + asm volatile("inst_sigill:\t" GCSPUSHM + : : [push] "r" (1)); + + enable_gcs(PR_SHADOW_STACK_PUSH); + + /* Valid value -- low 2 bits clear */ + old = 0xdeadbeeffeedcaec; + asm volatile(GCSPUSHM "\n\t" GCSPOPM + : [pop] "=r" (new) + : [push] "r" (old) + : "memory"); + assert(old == new); + + /* Invalid value -- SIGSEGV via EC_GCS */ + asm volatile(GCSPUSHM "\n" + "inst_sigsegv:\t" GCSPOPM + : [pop] "=r" (new) + : [push] "r" (1) + : "memory"); + + exit(0); +} From af0bd678df72f6c5a00484ed6acccfcfc91577d4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Oct 2025 14:56:13 -0700 Subject: [PATCH 1515/1794] tests/tcg/aarch64: Add gcsss Validate stack switching and recursion depth. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20251008215613.300150-74-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- tests/tcg/aarch64/Makefile.target | 2 +- tests/tcg/aarch64/gcs.h | 9 ++++ tests/tcg/aarch64/gcsss.c | 74 +++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/aarch64/gcsss.c diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index fddb7bc9cdc4d..55ce34e45eeca 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -76,7 +76,7 @@ $(SME_TESTS): CFLAGS += $(CROSS_AS_HAS_ARMV9_SME) endif # GCS Tests -GCS_TESTS += gcsstr gcspushm +GCS_TESTS += gcsstr gcspushm gcsss AARCH64_TESTS += $(GCS_TESTS) $(GCS_TESTS): gcs.h diff --git a/tests/tcg/aarch64/gcs.h b/tests/tcg/aarch64/gcs.h index 770cde6a85adc..6f013d0f1e0d5 100644 --- a/tests/tcg/aarch64/gcs.h +++ b/tests/tcg/aarch64/gcs.h @@ -69,3 +69,12 @@ #define gcspr() \ ({ uint64_t *r; asm volatile("mrs %0, s3_3_c2_c5_1" : "=r"(r)); r; }) + +#define gcsss1(val) \ + do { \ + asm volatile("sys #3, c7, c7, #2, %0" : : "r"(val) : "memory"); \ + } while (0) + +#define gcsss2() \ + ({ uint64_t *r; \ + asm volatile("sysl %0, #3, c7, c7, #3" : "=r"(r) : : "memory"); r; }) diff --git a/tests/tcg/aarch64/gcsss.c b/tests/tcg/aarch64/gcsss.c new file mode 100644 index 0000000000000..9550c68e7e745 --- /dev/null +++ b/tests/tcg/aarch64/gcsss.c @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gcs.h" + +#define IN_PROGRESS(X) ((uint64_t)(X) | 5) +#define CAP(X) (((uint64_t)(X) & ~0xfff) + 1) + +static uint64_t * __attribute__((noinline)) recurse(size_t index) +{ + if (index == 0) { + return gcspr(); + } + return recurse(index - 1); +} + +int main() +{ + void *tmp; + uint64_t *alt_stack, *alt_cap; + uint64_t *orig_pr, *orig_cap; + uint64_t *bottom; + size_t pagesize = getpagesize(); + size_t words; + + enable_gcs(0); + orig_pr = gcspr(); + + /* Allocate a guard page before and after. */ + tmp = mmap(0, 3 * pagesize, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); + assert(tmp != MAP_FAILED); + + /* map_shadow_stack won't replace existing mappings */ + munmap(tmp + pagesize, pagesize); + + /* Allocate a new stack between the guards. */ + alt_stack = (uint64_t *) + syscall(__NR_map_shadow_stack, tmp + pagesize, pagesize, + SHADOW_STACK_SET_TOKEN); + assert(alt_stack == tmp + pagesize); + + words = pagesize / 8; + alt_cap = alt_stack + words - 1; + + /* SHADOW_STACK_SET_TOKEN set the cap. */ + assert(*alt_cap == CAP(alt_cap)); + + /* Swap to the alt stack, one step at a time. */ + gcsss1(alt_cap); + + assert(gcspr() == alt_cap); + assert(*alt_cap == IN_PROGRESS(orig_pr)); + + orig_cap = gcsss2(); + + assert(orig_cap == orig_pr - 1); + assert(*orig_cap == CAP(orig_cap)); + assert(gcspr() == alt_stack + words); + + /* We should be able to use the whole stack. */ + bottom = recurse(words - 1); + assert(bottom == alt_stack); + + /* We should be back where we started. */ + assert(gcspr() == alt_stack + words); + + /* Swap back to the original stack. */ + gcsss1(orig_cap); + tmp = gcsss2(); + + assert(gcspr() == orig_pr); + assert(tmp == alt_cap); + + exit(0); +} From 8f6dd8f78aa961dbc9a02b01b6abe316f37d2766 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Mon, 6 Oct 2025 00:10:16 +0000 Subject: [PATCH 1516/1794] target/arm: Add a cpreg flag to indicate no trap in NV Add a new flag, ARM_CP_NV_NO_TRAP, to indicate that a CP register, even though it has opc1 == 4 or 5, does not trap when nested virtualization is enabled (FEAT_NV/FEAT_NV2). Signed-off-by: Gustavo Romero Message-id: 20251006001018.219756-2-gustavo.romero@linaro.org [PMM: tweaked comment text] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpregs.h | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 732c07506d97d..763de5e051c3d 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -144,6 +144,11 @@ enum { * identically to the normal one, other than FGT trapping handling.) */ ARM_CP_ADD_TLBI_NXS = 1 << 21, + /* + * Flag: even though this sysreg has opc1 == 4 or 5, it + * should not trap to EL2 when HCR_EL2.NV is set. + */ + ARM_CP_NV_NO_TRAP = 1 << 22, }; /* @@ -1178,12 +1183,17 @@ static inline bool arm_cpreg_traps_in_nv(const ARMCPRegInfo *ri) * fragile to future new sysregs, but this seems the least likely * to break. * - * In particular, note that the released sysreg XML defines that - * the FEAT_MEC sysregs and instructions do not follow this FEAT_NV - * trapping rule, so we will need to add an ARM_CP_* flag to indicate - * "register does not trap on NV" to handle those if/when we implement - * FEAT_MEC. + * In particular, note that the FEAT_MEC sysregs and instructions + * are exceptions to this trapping rule, so they are marked as + * ARM_CP_NV_NO_TRAP to indicate that they should not be trapped + * to EL2. (They are an exception because the FEAT_MEC sysregs UNDEF + * unless in Realm, and Realm is not expected to be virtualized.) */ + + if (ri->type & ARM_CP_NV_NO_TRAP) { + return false; + } + return ri->opc1 == 4 || ri->opc1 == 5; } From 700f08d5829f3736fdfafe92d8223254ffc9c495 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Mon, 6 Oct 2025 00:10:17 +0000 Subject: [PATCH 1517/1794] target/arm: Implement FEAT_MEC registers Add all FEAT_MEC registers. Enable access to the registers via the SCTLR2 and TCR2 control bits. Add the two new cache management instructions, which are nops in QEMU because we do not model caches. Message-ID: <20250711140828.1714666-3-gustavo.romero@linaro.org> Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Signed-off-by: Gustavo Romero Message-id: 20251006001018.219756-3-gustavo.romero@linaro.org [rth: Squash 3 patches to add all registers at once.] Signed-off-by: Richard Henderson Signed-off-by: Gustavo Romero Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 5 ++ target/arm/cpu.c | 3 ++ target/arm/cpu.h | 10 ++++ target/arm/helper.c | 108 ++++++++++++++++++++++++++++++++++++++ target/arm/internals.h | 3 ++ 5 files changed, 129 insertions(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 0f0a112c2134b..37f1eca3af62d 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -1364,6 +1364,11 @@ static inline bool isar_feature_aa64_s2pie(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64MMFR3, S2PIE) != 0; } +static inline bool isar_feature_aa64_mec(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64MMFR3, MEC) != 0; +} + static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64DFR0, PMUVER) >= 4 && diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 11e7c10cef577..3b556f1404ed1 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -652,6 +652,9 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el) cpu_isar_feature(aa64_s2pie, cpu)) { env->cp15.scr_el3 |= SCR_PIEN; } + if (cpu_isar_feature(aa64_mec, cpu)) { + env->cp15.scr_el3 |= SCR_MECEN; + } } if (target_el == 2) { diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 91a851dac1765..1d4e13320c3e9 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -583,6 +583,15 @@ typedef struct CPUArchState { uint64_t gcscr_el[4]; /* GCSCRE0_EL1, GCSCR_EL[123] */ uint64_t gcspr_el[4]; /* GCSPR_EL[0123] */ + + /* MEC registers */ + uint64_t mecid_p0_el2; + uint64_t mecid_a0_el2; + uint64_t mecid_p1_el2; + uint64_t mecid_a1_el2; + uint64_t mecid_rl_a_el3; + uint64_t vmecid_p_el2; + uint64_t vmecid_a_el2; } cp15; struct { @@ -1728,6 +1737,7 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_SCTLR2EN (1ULL << 44) #define SCR_PIEN (1ULL << 45) #define SCR_GPF (1ULL << 48) +#define SCR_MECEN (1ULL << 49) #define SCR_NSE (1ULL << 62) /* GCSCR_ELx fields */ diff --git a/target/arm/helper.c b/target/arm/helper.c index 0077788e1e84a..167f2909b3fe0 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -779,6 +779,9 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) cpu_isar_feature(aa64_s2pie, cpu)) { valid_mask |= SCR_PIEN; } + if (cpu_isar_feature(aa64_mec, cpu)) { + valid_mask |= SCR_MECEN; + } } else { valid_mask &= ~(SCR_RW | SCR_ST); if (cpu_isar_feature(aa32_ras, cpu)) { @@ -5088,6 +5091,96 @@ static const ARMCPRegInfo nmi_reginfo[] = { .resetfn = arm_cp_reset_ignore }, }; +static CPAccessResult mecid_access(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + int el = arm_current_el(env); + + if (el == 2) { + if (arm_security_space(env) != ARMSS_Realm) { + return CP_ACCESS_UNDEFINED; + } + + if (!(env->cp15.scr_el3 & SCR_MECEN)) { + return CP_ACCESS_TRAP_EL3; + } + } + + return CP_ACCESS_OK; +} + +static void mecid_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + value = extract64(value, 0, MECID_WIDTH); + raw_write(env, ri, value); +} + +static CPAccessResult cipae_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + switch (arm_security_space(env)) { + case ARMSS_Root: /* EL3 */ + case ARMSS_Realm: /* Realm EL2 */ + return CP_ACCESS_OK; + default: + return CP_ACCESS_UNDEFINED; + } +} + +static const ARMCPRegInfo mec_reginfo[] = { + { .name = "MECIDR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 7, .crn = 10, .crm = 8, + .access = PL2_R, .type = ARM_CP_CONST | ARM_CP_NV_NO_TRAP, + .resetvalue = MECID_WIDTH - 1 }, + { .name = "MECID_P0_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 0, .crn = 10, .crm = 8, + .access = PL2_RW, .type = ARM_CP_NV_NO_TRAP, + .accessfn = mecid_access, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.mecid_p0_el2) }, + { .name = "MECID_A0_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 1, .crn = 10, .crm = 8, + .access = PL2_RW, .type = ARM_CP_NV_NO_TRAP, + .accessfn = mecid_access, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.mecid_a0_el2) }, + { .name = "MECID_P1_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 2, .crn = 10, .crm = 8, + .access = PL2_RW, .type = ARM_CP_NV_NO_TRAP, + .accessfn = mecid_access, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.mecid_p1_el2) }, + { .name = "MECID_A1_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 3, .crn = 10, .crm = 8, + .access = PL2_RW, .type = ARM_CP_NV_NO_TRAP, + .accessfn = mecid_access, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.mecid_a1_el2) }, + { .name = "MECID_RL_A_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .opc2 = 1, .crn = 10, .crm = 10, + .access = PL3_RW, .accessfn = mecid_access, + .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.mecid_rl_a_el3) }, + { .name = "VMECID_P_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 0, .crn = 10, .crm = 9, + .access = PL2_RW, .type = ARM_CP_NV_NO_TRAP, + .accessfn = mecid_access, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.vmecid_p_el2) }, + { .name = "VMECID_A_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 1, .crn = 10, .crm = 9, + .access = PL2_RW, .type = ARM_CP_NV_NO_TRAP, + .accessfn = mecid_access, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.vmecid_a_el2) }, + { .name = "DC_CIPAE", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 14, .opc2 = 0, + .access = PL2_W, .type = ARM_CP_NOP | ARM_CP_NV_NO_TRAP, + .accessfn = cipae_access }, +}; + +static const ARMCPRegInfo mec_mte_reginfo[] = { + { .name = "DC_CIGDPAE", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 14, .opc2 = 7, + .access = PL2_W, .type = ARM_CP_NOP | ARM_CP_NV_NO_TRAP, + .accessfn = cipae_access }, +}; + #ifndef CONFIG_USER_ONLY /* * We don't know until after realize whether there's a GICv3 @@ -5930,6 +6023,9 @@ static void sctlr2_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, { uint64_t valid_mask = 0; + if (cpu_isar_feature(aa64_mec, env_archcpu(env))) { + valid_mask |= SCTLR2_EMEC; + } value &= valid_mask; raw_write(env, ri, value); } @@ -5939,6 +6035,9 @@ static void sctlr2_el3_write(CPUARMState *env, const ARMCPRegInfo *ri, { uint64_t valid_mask = 0; + if (cpu_isar_feature(aa64_mec, env_archcpu(env))) { + valid_mask |= SCTLR2_EMEC; + } value &= valid_mask; raw_write(env, ri, value); } @@ -6009,6 +6108,9 @@ static void tcr2_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, if (cpu_isar_feature(aa64_s1pie, cpu)) { valid_mask |= TCR2_PIE; } + if (cpu_isar_feature(aa64_mec, env_archcpu(env))) { + valid_mask |= TCR2_AMEC0 | TCR2_AMEC1; + } value &= valid_mask; raw_write(env, ri, value); } @@ -7325,6 +7427,12 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (cpu_isar_feature(aa64_s2pie, cpu)) { define_arm_cp_regs(cpu, s2pie_reginfo); } + if (cpu_isar_feature(aa64_mec, cpu)) { + define_arm_cp_regs(cpu, mec_reginfo); + if (cpu_isar_feature(aa64_mte, cpu)) { + define_arm_cp_regs(cpu, mec_mte_reginfo); + } + } if (cpu_isar_feature(any_predinv, cpu)) { define_arm_cp_regs(cpu, predinv_reginfo); diff --git a/target/arm/internals.h b/target/arm/internals.h index 2a85ab762d5d3..f539bbe58e129 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1903,4 +1903,7 @@ void cpsr_write_from_spsr_elx(CPUARMState *env, uint32_t val); /* Compare uint64_t for qsort and bsearch. */ int compare_u64(const void *a, const void *b); +/* Used in FEAT_MEC to set the MECIDWidthm1 field in the MECIDR_EL2 register. */ +#define MECID_WIDTH 16 + #endif From 00936783abf77ebb47a78312a2e6500c6a13d938 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Mon, 6 Oct 2025 00:10:18 +0000 Subject: [PATCH 1518/1794] target/arm: Enable FEAT_MEC in -cpu max Advertise FEAT_MEC in AA64MMFR3 ID register for the Arm64 cpu max as a first step to fully support FEAT_MEC. The FEAT_MEC is an extension to FEAT_RME that implements multiple Memory Encryption Contexts (MEC) so the memory in a realm can be encrypted and accessing it from the wrong encryption context is not possible. An encryption context allow the selection of a memory encryption engine. At this point, no real memory encryption is supported, but software stacks that rely on FEAT_MEC should work properly. Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Signed-off-by: Gustavo Romero Message-id: 20251006001018.219756-4-gustavo.romero@linaro.org Message-ID: <20250711140828.1714666-7-gustavo.romero@linaro.org> Signed-off-by: Richard Henderson Signed-off-by: Gustavo Romero Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 3 +++ target/arm/tcg/cpu64.c | 1 + 2 files changed, 4 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 4496178c48e8a..bf81da124a088 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -94,6 +94,9 @@ the following architecture extensions: - FEAT_LSE2 (Large System Extensions v2) - FEAT_LSE128 (128-bit Atomics) - FEAT_LVA (Large Virtual Address space) +- FEAT_MEC (Memory Encryption Contexts) + + * This is a register-only implementation without encryption. - FEAT_MixedEnd (Mixed-endian support) - FEAT_MixedEndEL0 (Mixed-endian support at EL0) - FEAT_MOPS (Standardization of memory operations) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index cc42dfdf561f7..1bffe66e81c44 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1327,6 +1327,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = GET_IDREG(isar, ID_AA64MMFR3); t = FIELD_DP64(t, ID_AA64MMFR3, TCRX, 1); /* FEAT_TCR2 */ t = FIELD_DP64(t, ID_AA64MMFR3, SCTLRX, 1); /* FEAT_SCTLR2 */ + t = FIELD_DP64(t, ID_AA64MMFR3, MEC, 1); /* FEAT_MEC */ t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ t = FIELD_DP64(t, ID_AA64MMFR3, S1PIE, 1); /* FEAT_S1PIE */ t = FIELD_DP64(t, ID_AA64MMFR3, S2PIE, 1); /* FEAT_S2PIE */ From ed26056d90ddff21351f3efd2cb47fea4f0e1d45 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Thu, 9 Oct 2025 15:08:31 +0100 Subject: [PATCH 1519/1794] block/curl.c: Use explicit long constants in curl_easy_setopt calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit curl_easy_setopt takes a variable argument that depends on what CURLOPT you are setting. Some require a long constant. Passing a plain int constant is potentially wrong on some platforms. With warnings enabled, multiple warnings like this were printed: ../block/curl.c: In function ‘curl_init_state’: ../block/curl.c:474:13: warning: call to ‘_curl_easy_setopt_err_long’ declared with attribute warning: curl_easy_setopt expects a long argument [-Wattribute-warning] 474 | curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1) || | ^ Signed-off-by: Richard W.M. Jones Signed-off-by: Chenxi Mao Reviewed-by: Daniel P. Berrangé Reviewed-by: Akihiko Odaki Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20251009141026.4042021-2-rjones@redhat.com> --- block/curl.c | 10 +++++----- contrib/elf2dmp/download.c | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/block/curl.c b/block/curl.c index e0f98e035a45e..68cf83ce55fc5 100644 --- a/block/curl.c +++ b/block/curl.c @@ -471,11 +471,11 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state) (void *)curl_read_cb) || curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state) || curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state) || - curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1) || - curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1) || - curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1) || + curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1L) || + curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1L) || + curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1L) || curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg) || - curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1)) { + curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1L)) { goto err; } if (s->username) { @@ -800,7 +800,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, } s->accept_range = false; - if (curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1) || + if (curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1L) || curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION, curl_header_cb) || curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s)) { pstrcpy(state->errmsg, CURL_ERROR_SIZE, diff --git a/contrib/elf2dmp/download.c b/contrib/elf2dmp/download.c index 21306b3fd4c44..fa8da0f9a2d03 100644 --- a/contrib/elf2dmp/download.c +++ b/contrib/elf2dmp/download.c @@ -27,8 +27,8 @@ bool download_url(const char *name, const char *url) if (curl_easy_setopt(curl, CURLOPT_URL, url) != CURLE_OK || curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL) != CURLE_OK || curl_easy_setopt(curl, CURLOPT_WRITEDATA, file) != CURLE_OK - || curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1) != CURLE_OK - || curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0) != CURLE_OK + || curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK + || curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L) != CURLE_OK || curl_easy_perform(curl) != CURLE_OK) { unlink(name); fclose(file); From 2dc5298d31d7ee86b9204de0b3b9c9fda294c3ae Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 19 Sep 2025 17:30:13 +0800 Subject: [PATCH 1520/1794] tests/functional/aarch64/aspeed_ast2700: Add PCIe and network tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the AST2700 and AST2700fc functional tests with PCIe and network checks. This patch introduces a helper "do_ast2700_pcie_test()" that runs "lspci" on the emulated system and verifies the expected PCIe devices: - 0002:00:00.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge - 0002:01:00.0 Ethernet controller: Intel Corporation 82574L Gigabit Network Connection Additional changes: - Add `-device e1000e,netdev=net1,bus=pcie.2 -netdev user,id=net1` to the AST2700 and AST2700fc test machines. - In the AST2700 vbootrom test, assign an IP address to the e1000e interface and verify it using `ip addr`. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250919093017.338309-15-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- .../functional/aarch64/test_aspeed_ast2700.py | 21 +++++++++++++++++++ .../aarch64/test_aspeed_ast2700fc.py | 13 ++++++++++++ 2 files changed, 34 insertions(+) diff --git a/tests/functional/aarch64/test_aspeed_ast2700.py b/tests/functional/aarch64/test_aspeed_ast2700.py index a3db267294991..0973fce0e9950 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700.py +++ b/tests/functional/aarch64/test_aspeed_ast2700.py @@ -69,6 +69,16 @@ def do_ast2700_i2c_test(self): exec_command_and_wait_for_pattern(self, 'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '18000') + def do_ast2700_pcie_test(self): + exec_command_and_wait_for_pattern(self, + 'lspci -s 0002:00:00.0', + '0002:00:00.0 PCI bridge: ' + 'ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge') + exec_command_and_wait_for_pattern(self, + 'lspci -s 0002:01:00.0', + '0002:01:00.0 Ethernet controller: ' + 'Intel Corporation 82574L Gigabit Network Connection') + def start_ast2700_test(self, name): num_cpu = 4 uboot_size = os.path.getsize(self.scratch_file(name, @@ -125,20 +135,31 @@ def test_aarch64_ast2700a0_evb_sdk_v09_06(self): def test_aarch64_ast2700a1_evb_sdk_v09_06(self): self.set_machine('ast2700a1-evb') + self.require_netdev('user') self.archive_extract(self.ASSET_SDK_V906_AST2700A1) + self.vm.add_args('-device', 'e1000e,netdev=net1,bus=pcie.2') + self.vm.add_args('-netdev', 'user,id=net1') self.start_ast2700_test('ast2700-default') self.verify_openbmc_boot_and_login('ast2700-default') self.do_ast2700_i2c_test() + self.do_ast2700_pcie_test() def test_aarch64_ast2700a1_evb_sdk_vbootrom_v09_07(self): self.set_machine('ast2700a1-evb') + self.require_netdev('user') self.archive_extract(self.ASSET_SDK_V907_AST2700A1_VBOOROM) + self.vm.add_args('-device', 'e1000e,netdev=net1,bus=pcie.2') + self.vm.add_args('-netdev', 'user,id=net1') self.start_ast2700_test_vbootrom('ast2700-default') self.verify_vbootrom_firmware_flow() self.verify_openbmc_boot_and_login('ast2700-default') self.do_ast2700_i2c_test() + self.do_ast2700_pcie_test() + exec_command_and_wait_for_pattern(self, + 'ip addr show dev eth2', + 'inet 10.0.2.15/24') if __name__ == '__main__': QemuSystemTest.main() diff --git a/tests/functional/aarch64/test_aspeed_ast2700fc.py b/tests/functional/aarch64/test_aspeed_ast2700fc.py index b85370e182ea0..28b66614d970c 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700fc.py +++ b/tests/functional/aarch64/test_aspeed_ast2700fc.py @@ -20,6 +20,8 @@ def do_test_aarch64_aspeed_sdk_start(self, image): self.vm.set_console() self.vm.add_args('-device', 'tmp105,bus=aspeed.i2c.bus.1,address=0x4d,id=tmp-test') + self.vm.add_args('-device', 'e1000e,netdev=net1,bus=pcie.2') + self.vm.add_args('-netdev', 'user,id=net1') self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', '-net', 'nic', '-net', 'user', '-snapshot') @@ -49,6 +51,16 @@ def do_ast2700_i2c_test(self): exec_command_and_wait_for_pattern(self, 'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '18000') + def do_ast2700_pcie_test(self): + exec_command_and_wait_for_pattern(self, + 'lspci -s 0002:00:00.0', + '0002:00:00.0 PCI bridge: ' + 'ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge') + exec_command_and_wait_for_pattern(self, + 'lspci -s 0002:01:00.0', + '0002:01:00.0 Ethernet controller: ' + 'Intel Corporation 82574L Gigabit Network Connection') + def do_ast2700fc_ssp_test(self): self.vm.shutdown() self.vm.set_console(console_index=1) @@ -128,6 +140,7 @@ def test_aarch64_ast2700fc_sdk_v09_06(self): self.start_ast2700fc_test('ast2700-default') self.verify_openbmc_boot_and_login('ast2700-default') self.do_ast2700_i2c_test() + self.do_ast2700_pcie_test() self.do_ast2700fc_ssp_test() self.do_ast2700fc_tsp_test() From 4fcb15ebc262b767743ca97e42cc4041b00b9193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 3 Oct 2025 12:30:24 +0200 Subject: [PATCH 1521/1794] aspeed: Don't set 'auto_create_sdcard' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Aspeed machines inherited from a 'no_sdcard' attribute when first introduced in QEMU. This attribute was later renamed to 'auto_create_sdcard' by commit cdc8d7cadaac ("hw/boards: Rename no_sdcard -> auto_create_sdcard") and set to 'true'. This has the indesirable efect to automatically create SD cards at init time. Remove 'auto_create_sdcard' to avoid creating a SD card device. Cc: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20251003103024.1863551-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 6046ec0bb2a2d..58cfbc7137942 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1418,7 +1418,6 @@ static void aspeed_machine_palmetto_class_init(ObjectClass *oc, amc->spi_model = "mx25l25635f"; amc->num_cs = 1; amc->i2c_init = palmetto_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 256 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1436,7 +1435,6 @@ static void aspeed_machine_quanta_q71l_class_init(ObjectClass *oc, amc->spi_model = "mx25l25635e"; amc->num_cs = 1; amc->i2c_init = quanta_q71l_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 128 * MiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1455,7 +1453,6 @@ static void aspeed_machine_supermicrox11_bmc_class_init(ObjectClass *oc, amc->num_cs = 1; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = palmetto_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 256 * MiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1474,7 +1471,6 @@ static void aspeed_machine_supermicro_x11spi_bmc_class_init(ObjectClass *oc, amc->num_cs = 1; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = palmetto_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1492,7 +1488,6 @@ static void aspeed_machine_ast2500_evb_class_init(ObjectClass *oc, amc->spi_model = "mx25l25635f"; amc->num_cs = 1; amc->i2c_init = ast2500_evb_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1511,7 +1506,6 @@ static void aspeed_machine_yosemitev2_class_init(ObjectClass *oc, amc->spi_model = "mx25l25635e"; amc->num_cs = 2; amc->i2c_init = yosemitev2_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1529,7 +1523,6 @@ static void aspeed_machine_romulus_class_init(ObjectClass *oc, amc->spi_model = "mx66l1g45g"; amc->num_cs = 2; amc->i2c_init = romulus_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1548,7 +1541,6 @@ static void aspeed_machine_tiogapass_class_init(ObjectClass *oc, amc->spi_model = "mx25l25635e"; amc->num_cs = 2; amc->i2c_init = tiogapass_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1566,7 +1558,6 @@ static void aspeed_machine_sonorapass_class_init(ObjectClass *oc, amc->spi_model = "mx66l1g45g"; amc->num_cs = 2; amc->i2c_init = sonorapass_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1584,7 +1575,6 @@ static void aspeed_machine_witherspoon_class_init(ObjectClass *oc, amc->spi_model = "mx66l1g45g"; amc->num_cs = 2; amc->i2c_init = witherspoon_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1606,7 +1596,6 @@ static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, ASPEED_MAC3_ON; amc->sdhci_wp_inverted = true; amc->i2c_init = ast2600_evb_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); aspeed_machine_ast2600_class_emmc_init(oc); @@ -1625,7 +1614,6 @@ static void aspeed_machine_g220a_class_init(ObjectClass *oc, const void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = g220a_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 1024 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1644,7 +1632,6 @@ static void aspeed_machine_fp5280g2_class_init(ObjectClass *oc, amc->num_cs = 2; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = fp5280g2_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1663,7 +1650,6 @@ static void aspeed_machine_rainier_class_init(ObjectClass *oc, const void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = rainier_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); aspeed_machine_ast2600_class_emmc_init(oc); @@ -1686,7 +1672,6 @@ static void aspeed_machine_fuji_class_init(ObjectClass *oc, const void *data) amc->macs_mask = ASPEED_MAC3_ON; amc->i2c_init = fuji_bmc_i2c_init; amc->uart_default = ASPEED_DEV_UART1; - mc->auto_create_sdcard = true; mc->default_ram_size = FUJI_BMC_RAM_SIZE; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1708,7 +1693,6 @@ static void aspeed_machine_bletchley_class_init(ObjectClass *oc, amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON; amc->i2c_init = bletchley_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = BLETCHLEY_BMC_RAM_SIZE; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1728,7 +1712,6 @@ static void aspeed_machine_catalina_class_init(ObjectClass *oc, amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON; amc->i2c_init = catalina_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = CATALINA_BMC_RAM_SIZE; aspeed_machine_class_init_cpus_defaults(mc); aspeed_machine_ast2600_class_emmc_init(oc); @@ -1796,7 +1779,6 @@ static void aspeed_machine_fby35_class_init(ObjectClass *oc, const void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC3_ON; amc->i2c_init = fby35_i2c_init; - mc->auto_create_sdcard = true; /* FIXME: Replace this macro with something more general */ mc->default_ram_size = FUJI_BMC_RAM_SIZE; aspeed_machine_class_init_cpus_defaults(mc); @@ -1909,7 +1891,6 @@ static void aspeed_machine_ast2700a0_evb_class_init(ObjectClass *oc, amc->uart_default = ASPEED_DEV_UART12; amc->i2c_init = ast2700_evb_i2c_init; amc->vbootrom = true; - mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1932,7 +1913,6 @@ static void aspeed_machine_ast2700a1_evb_class_init(ObjectClass *oc, amc->uart_default = ASPEED_DEV_UART12; amc->i2c_init = ast2700_evb_i2c_init; amc->vbootrom = true; - mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1953,7 +1933,6 @@ static void aspeed_machine_qcom_dc_scm_v1_class_init(ObjectClass *oc, amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = qcom_dc_scm_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1973,7 +1952,6 @@ static void aspeed_machine_qcom_firework_class_init(ObjectClass *oc, amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = qcom_dc_scm_firework_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); }; From c0c05762804f33f3c5edbde77ecadbc315dfee3a Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 3 Oct 2025 15:21:02 +0800 Subject: [PATCH 1522/1794] tests/functional/arm/test_aspeed_ast1030: Update test ASPEED SDK v03.03 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251003072107.3530642-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/arm/test_aspeed_ast1030.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/functional/arm/test_aspeed_ast1030.py b/tests/functional/arm/test_aspeed_ast1030.py index e47b597d0bd97..60e2b0251c6c6 100755 --- a/tests/functional/arm/test_aspeed_ast1030.py +++ b/tests/functional/arm/test_aspeed_ast1030.py @@ -13,17 +13,17 @@ class AST1030Machine(AspeedTest): - ASSET_ZEPHYR_3_02 = Asset( + ASSET_ZEPHYR_3_03 = Asset( ('https://github.com/AspeedTech-BMC' - '/zephyr/releases/download/v00.03.02/ast1030-evb-demo.zip'), - '1ec83caab3ddd5d09481772801be7210e222cb015ce22ec6fffb8a76956dcd4f') + '/zephyr/releases/download/v00.03.03/ast1030-evb-demo.zip'), + '27cd73cdee6374bceb4ee58b3ace87989fa3f0684f4e612510804b588b24d4e0') - def test_arm_ast1030_zephyros_3_02(self): + def test_arm_ast1030_zephyros_3_03(self): self.set_machine('ast1030-evb') - kernel_name = "ast1030-evb-demo-3/zephyr.elf" + kernel_name = "ast1030-evb-demo/zephyr.elf" kernel_file = self.archive_extract( - self.ASSET_ZEPHYR_3_02, member=kernel_name) + self.ASSET_ZEPHYR_3_03, member=kernel_name) self.vm.set_console() self.vm.add_args('-kernel', kernel_file, '-nographic') @@ -72,8 +72,9 @@ def test_arm_ast1030_zephyros_1_07(self): def test_arm_ast1030_otp_blockdev_device(self): self.vm.set_machine("ast1030-evb") - kernel_name = "ast1030-evb-demo-3/zephyr.elf" - kernel_file = self.archive_extract(self.ASSET_ZEPHYR_3_02, member=kernel_name) + kernel_name = "ast1030-evb-demo/zephyr.elf" + kernel_file = self.archive_extract(self.ASSET_ZEPHYR_3_03, + member=kernel_name) otp_img = self.generate_otpmem_image() self.vm.set_console() From 47105ec4dca077d0c535a02adb40645205106da5 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 3 Oct 2025 15:21:03 +0800 Subject: [PATCH 1523/1794] tests/functional/arm/test_aspeed_ast2500: Update test ASPEED SDK v09.08 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251003072107.3530642-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/arm/test_aspeed_ast2500.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/arm/test_aspeed_ast2500.py b/tests/functional/arm/test_aspeed_ast2500.py index 4fdd81e2f95b7..5efd104c2b959 100755 --- a/tests/functional/arm/test_aspeed_ast2500.py +++ b/tests/functional/arm/test_aspeed_ast2500.py @@ -37,14 +37,14 @@ def test_arm_ast2500_evb_buildroot(self): self.do_test_arm_aspeed_buildroot_poweroff() - ASSET_SDK_V907_AST2500 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.07/ast2500-default-obmc.tar.gz', - 'd52bcc279a37c8d7679b3e4ef22cc77c36f0f6624c687b37334f798828afb077') + ASSET_SDK_V908_AST2500 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.08/ast2500-default-obmc.tar.gz', + 'c0a2ba169efd19be5eb77c50ec2a6afd9d826e196a0be3432f969fc72d4b7c0e') def test_arm_ast2500_evb_sdk(self): self.set_machine('ast2500-evb') - self.archive_extract(self.ASSET_SDK_V907_AST2500) + self.archive_extract(self.ASSET_SDK_V908_AST2500) self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2500-default", "image-bmc")) From c4a9ecac40b360d1ebfc381981052d9e5024df16 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 3 Oct 2025 15:21:04 +0800 Subject: [PATCH 1524/1794] tests/functional/arm/test_aspeed_ast2600: Update test ASPEED SDK v09.08 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251003072107.3530642-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/arm/test_aspeed_ast2600.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/functional/arm/test_aspeed_ast2600.py b/tests/functional/arm/test_aspeed_ast2600.py index f655c0ba0c7a2..0127913cfb658 100755 --- a/tests/functional/arm/test_aspeed_ast2600.py +++ b/tests/functional/arm/test_aspeed_ast2600.py @@ -97,9 +97,9 @@ def test_arm_ast2600_evb_buildroot_tpm(self): self.do_test_arm_aspeed_buildroot_poweroff() - ASSET_SDK_V907_AST2600 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.07/ast2600-default-obmc.tar.gz', - 'cb6c08595bcbba1672ce716b068ba4e48eda1ed9abe78a07b30392ba2278feba') + ASSET_SDK_V908_AST2600 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.08/ast2600-default-obmc.tar.gz', + 'a0414f14ad696550efe083c2156dbeda855c08cc9ae7f40fe1b41bf292295f82') def do_ast2600_pcie_test(self): exec_command_and_wait_for_pattern(self, @@ -122,7 +122,7 @@ def test_arm_ast2600_evb_sdk(self): self.set_machine('ast2600-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V907_AST2600) + self.archive_extract(self.ASSET_SDK_V908_AST2600) self.vm.add_args('-device', 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test') @@ -160,7 +160,7 @@ def test_arm_ast2600_evb_sdk(self): def test_arm_ast2600_otp_blockdev_device(self): self.vm.set_machine("ast2600-evb") - image_path = self.archive_extract(self.ASSET_SDK_V907_AST2600) + image_path = self.archive_extract(self.ASSET_SDK_V908_AST2600) otp_img = self.generate_otpmem_image() self.vm.set_console() From fecf7b8e4cb6c23f5c7f1a12d511c26fb61756bf Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 3 Oct 2025 15:21:05 +0800 Subject: [PATCH 1525/1794] tests/functional/aarch64/test_aspeed_ast2700: Update test ASPEED SDK v09.08 for A1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support for AST2700 A0 was dropped starting from SDK v09.07. The new SDK v09.08 only updates support for AST2700 A1. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251003072107.3530642-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- .../functional/aarch64/test_aspeed_ast2700.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/tests/functional/aarch64/test_aspeed_ast2700.py b/tests/functional/aarch64/test_aspeed_ast2700.py index 0973fce0e9950..0e9f10d991ea0 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700.py +++ b/tests/functional/aarch64/test_aspeed_ast2700.py @@ -50,13 +50,9 @@ def verify_openbmc_boot_and_login(self, name): 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2700-a0-default-obmc.tar.gz', '7247b6f19dbfb700686f8d9f723ac23f3eb229226c0589cb9b06b80d1b61f3cb') - ASSET_SDK_V906_AST2700A1 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2700-default-obmc.tar.gz', - 'f1d53e0be8a404ecce3e105f72bc50fa4e090ad13160ffa91b10a6e0233a9dc6') - - ASSET_SDK_V907_AST2700A1_VBOOROM = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.07/ast2700-default-obmc.tar.gz', - '6e9e0c4b13e0f26040eca3f4a7f17cf09fc0f5c37c820500ff79370cc3c44add') + ASSET_SDK_V908_AST2700A1 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.08/ast2700-default-obmc.tar.gz', + 'eac3dc409b7ea3cd4b03d4792d3cebd469792ad893cb51e1d15f0fc20bd1e2cd') def do_ast2700_i2c_test(self): exec_command_and_wait_for_pattern(self, @@ -133,11 +129,11 @@ def test_aarch64_ast2700a0_evb_sdk_v09_06(self): self.verify_openbmc_boot_and_login('ast2700-a0-default') self.do_ast2700_i2c_test() - def test_aarch64_ast2700a1_evb_sdk_v09_06(self): + def test_aarch64_ast2700a1_evb_sdk_v09_08(self): self.set_machine('ast2700a1-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V906_AST2700A1) + self.archive_extract(self.ASSET_SDK_V908_AST2700A1) self.vm.add_args('-device', 'e1000e,netdev=net1,bus=pcie.2') self.vm.add_args('-netdev', 'user,id=net1') self.start_ast2700_test('ast2700-default') @@ -145,11 +141,11 @@ def test_aarch64_ast2700a1_evb_sdk_v09_06(self): self.do_ast2700_i2c_test() self.do_ast2700_pcie_test() - def test_aarch64_ast2700a1_evb_sdk_vbootrom_v09_07(self): + def test_aarch64_ast2700a1_evb_sdk_vbootrom_v09_08(self): self.set_machine('ast2700a1-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V907_AST2700A1_VBOOROM) + self.archive_extract(self.ASSET_SDK_V908_AST2700A1) self.vm.add_args('-device', 'e1000e,netdev=net1,bus=pcie.2') self.vm.add_args('-netdev', 'user,id=net1') self.start_ast2700_test_vbootrom('ast2700-default') From 783559aece0c0e59c35a8c63954f841d89a661b5 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 3 Oct 2025 15:21:06 +0800 Subject: [PATCH 1526/1794] tests/functional/aarch64/test_aspeed_ast2700: Move eth2 IP check into common function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The eth2 IP address check was previously only performed in test_aarch64_ast2700a1_evb_sdk_vbootrom_v09_08. This patch moves the check into do_ast2700_pcie_test(), ensuring it is executed consistently across all AST2700 PCIe test runs. This avoids code duplication. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251003072107.3530642-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/aarch64/test_aspeed_ast2700.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/functional/aarch64/test_aspeed_ast2700.py b/tests/functional/aarch64/test_aspeed_ast2700.py index 0e9f10d991ea0..ef7ed522afc31 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700.py +++ b/tests/functional/aarch64/test_aspeed_ast2700.py @@ -74,6 +74,9 @@ def do_ast2700_pcie_test(self): 'lspci -s 0002:01:00.0', '0002:01:00.0 Ethernet controller: ' 'Intel Corporation 82574L Gigabit Network Connection') + exec_command_and_wait_for_pattern(self, + 'ip addr show dev eth2', + 'inet 10.0.2.15/24') def start_ast2700_test(self, name): num_cpu = 4 @@ -153,9 +156,6 @@ def test_aarch64_ast2700a1_evb_sdk_vbootrom_v09_08(self): self.verify_openbmc_boot_and_login('ast2700-default') self.do_ast2700_i2c_test() self.do_ast2700_pcie_test() - exec_command_and_wait_for_pattern(self, - 'ip addr show dev eth2', - 'inet 10.0.2.15/24') if __name__ == '__main__': QemuSystemTest.main() From 511ea9a865f7da820bea5dc22c9340e5d4fc32f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 7 Oct 2025 11:00:31 +0200 Subject: [PATCH 1527/1794] tests/functional/arm: Split the ast2600 tests in two files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ast2600 test file currently includes tests for both the Buildroot and SDK images. Since the SDK image tests can take long to run, split them into a separate file to clearly distinguish the two sets of tests, improve parallelism and allow for different CI timeouts. Reviewed-by: Thomas Huth Reviewed-by: Jamin Lin Link: https://lore.kernel.org/qemu-devel/20251007090031.679003-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/functional/arm/meson.build | 6 +- ...00.py => test_aspeed_ast2600_buildroot.py} | 74 --------------- .../functional/arm/test_aspeed_ast2600_sdk.py | 94 +++++++++++++++++++ 3 files changed, 98 insertions(+), 76 deletions(-) rename tests/functional/arm/{test_aspeed_ast2600.py => test_aspeed_ast2600_buildroot.py} (57%) create mode 100755 tests/functional/arm/test_aspeed_ast2600_sdk.py diff --git a/tests/functional/arm/meson.build b/tests/functional/arm/meson.build index e4e7dba8d087a..d1ed076a6aa8e 100644 --- a/tests/functional/arm/meson.build +++ b/tests/functional/arm/meson.build @@ -5,7 +5,8 @@ test_arm_timeouts = { 'aspeed_romulus' : 120, 'aspeed_witherspoon' : 120, 'aspeed_ast2500' : 720, - 'aspeed_ast2600' : 1200, + 'aspeed_ast2600_buildroot' : 720, + 'aspeed_ast2600_sdk' : 1200, 'aspeed_bletchley' : 480, 'aspeed_catalina' : 480, 'aspeed_gb200nvl_bmc' : 480, @@ -31,7 +32,8 @@ tests_arm_system_thorough = [ 'aspeed_romulus', 'aspeed_witherspoon', 'aspeed_ast2500', - 'aspeed_ast2600', + 'aspeed_ast2600_buildroot', + 'aspeed_ast2600_sdk', 'aspeed_bletchley', 'aspeed_catalina', 'aspeed_gb200nvl_bmc', diff --git a/tests/functional/arm/test_aspeed_ast2600.py b/tests/functional/arm/test_aspeed_ast2600_buildroot.py similarity index 57% rename from tests/functional/arm/test_aspeed_ast2600.py rename to tests/functional/arm/test_aspeed_ast2600_buildroot.py index 0127913cfb658..51f2676c9061f 100755 --- a/tests/functional/arm/test_aspeed_ast2600.py +++ b/tests/functional/arm/test_aspeed_ast2600_buildroot.py @@ -97,80 +97,6 @@ def test_arm_ast2600_evb_buildroot_tpm(self): self.do_test_arm_aspeed_buildroot_poweroff() - ASSET_SDK_V908_AST2600 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.08/ast2600-default-obmc.tar.gz', - 'a0414f14ad696550efe083c2156dbeda855c08cc9ae7f40fe1b41bf292295f82') - - def do_ast2600_pcie_test(self): - exec_command_and_wait_for_pattern(self, - 'lspci -s 80:00.0', - '80:00.0 Host bridge: ' - 'ASPEED Technology, Inc. Device 2600') - exec_command_and_wait_for_pattern(self, - 'lspci -s 80:08.0', - '80:08.0 PCI bridge: ' - 'ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge') - exec_command_and_wait_for_pattern(self, - 'lspci -s 81:00.0', - '81:00.0 Ethernet controller: ' - 'Intel Corporation 82574L Gigabit Network Connection') - exec_command_and_wait_for_pattern(self, - 'ip addr show dev eth4', - 'inet 10.0.2.15/24') - - def test_arm_ast2600_evb_sdk(self): - self.set_machine('ast2600-evb') - self.require_netdev('user') - - self.archive_extract(self.ASSET_SDK_V908_AST2600) - - self.vm.add_args('-device', - 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test') - self.vm.add_args('-device', - 'ds1338,bus=aspeed.i2c.bus.5,address=0x32') - self.vm.add_args('-device', 'e1000e,netdev=net1,bus=pcie.0') - self.vm.add_args('-netdev', 'user,id=net1') - self.do_test_arm_aspeed_sdk_start( - self.scratch_file("ast2600-default", "image-bmc")) - - self.wait_for_console_pattern('ast2600-default login:') - - exec_command_and_wait_for_pattern(self, 'root', 'Password:') - exec_command_and_wait_for_pattern(self, '0penBmc', - 'root@ast2600-default:~#') - - exec_command_and_wait_for_pattern(self, - 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-5/device/new_device', - 'i2c i2c-5: new_device: Instantiated device lm75 at 0x4d') - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon19/temp1_input', '0') - self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', - property='temperature', value=18000) - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon19/temp1_input', '18000') - - exec_command_and_wait_for_pattern(self, - 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-5/device/new_device', - 'i2c i2c-5: new_device: Instantiated device ds1307 at 0x32') - year = time.strftime("%Y") - exec_command_and_wait_for_pattern(self, - '/sbin/hwclock -f /dev/rtc1', year) - self.do_ast2600_pcie_test() - - def test_arm_ast2600_otp_blockdev_device(self): - self.vm.set_machine("ast2600-evb") - - image_path = self.archive_extract(self.ASSET_SDK_V908_AST2600) - otp_img = self.generate_otpmem_image() - - self.vm.set_console() - self.vm.add_args( - "-blockdev", f"driver=file,filename={otp_img},node-name=otp", - "-global", "aspeed-otp.drive=otp", - ) - self.do_test_arm_aspeed_sdk_start( - self.scratch_file("ast2600-default", "image-bmc")) - self.wait_for_console_pattern("ast2600-default login:") if __name__ == '__main__': AspeedTest.main() diff --git a/tests/functional/arm/test_aspeed_ast2600_sdk.py b/tests/functional/arm/test_aspeed_ast2600_sdk.py new file mode 100755 index 0000000000000..e3d4ed09e2eef --- /dev/null +++ b/tests/functional/arm/test_aspeed_ast2600_sdk.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import time + +from qemu_test import Asset +from aspeed import AspeedTest +from qemu_test import exec_command_and_wait_for_pattern + + +class AST2600Machine(AspeedTest): + + ASSET_SDK_V908_AST2600 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.08/ast2600-default-obmc.tar.gz', + 'a0414f14ad696550efe083c2156dbeda855c08cc9ae7f40fe1b41bf292295f82') + + def do_ast2600_pcie_test(self): + exec_command_and_wait_for_pattern(self, + 'lspci -s 80:00.0', + '80:00.0 Host bridge: ' + 'ASPEED Technology, Inc. Device 2600') + exec_command_and_wait_for_pattern(self, + 'lspci -s 80:08.0', + '80:08.0 PCI bridge: ' + 'ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge') + exec_command_and_wait_for_pattern(self, + 'lspci -s 81:00.0', + '81:00.0 Ethernet controller: ' + 'Intel Corporation 82574L Gigabit Network Connection') + exec_command_and_wait_for_pattern(self, + 'ip addr show dev eth4', + 'inet 10.0.2.15/24') + + def test_arm_ast2600_evb_sdk(self): + self.set_machine('ast2600-evb') + self.require_netdev('user') + + self.archive_extract(self.ASSET_SDK_V908_AST2600) + + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test') + self.vm.add_args('-device', + 'ds1338,bus=aspeed.i2c.bus.5,address=0x32') + self.vm.add_args('-device', 'e1000e,netdev=net1,bus=pcie.0') + self.vm.add_args('-netdev', 'user,id=net1') + self.do_test_arm_aspeed_sdk_start( + self.scratch_file("ast2600-default", "image-bmc")) + + self.wait_for_console_pattern('ast2600-default login:') + + exec_command_and_wait_for_pattern(self, 'root', 'Password:') + exec_command_and_wait_for_pattern(self, '0penBmc', + 'root@ast2600-default:~#') + + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-5/device/new_device', + 'i2c i2c-5: new_device: Instantiated device lm75 at 0x4d') + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon19/temp1_input', '0') + self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', + property='temperature', value=18000) + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon19/temp1_input', '18000') + + exec_command_and_wait_for_pattern(self, + 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-5/device/new_device', + 'i2c i2c-5: new_device: Instantiated device ds1307 at 0x32') + year = time.strftime("%Y") + exec_command_and_wait_for_pattern(self, + '/sbin/hwclock -f /dev/rtc1', year) + self.do_ast2600_pcie_test() + + def test_arm_ast2600_otp_blockdev_device(self): + self.vm.set_machine("ast2600-evb") + + image_path = self.archive_extract(self.ASSET_SDK_V908_AST2600) + otp_img = self.generate_otpmem_image() + + self.vm.set_console() + self.vm.add_args( + "-blockdev", f"driver=file,filename={otp_img},node-name=otp", + "-global", "aspeed-otp.drive=otp", + ) + self.do_test_arm_aspeed_sdk_start( + self.scratch_file("ast2600-default", "image-bmc")) + self.wait_for_console_pattern("ast2600-default login:") + + +if __name__ == '__main__': + AspeedTest.main() From acd290b01726294d12f93d64e9ff1dd14f524d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 7 Oct 2025 16:16:00 +0200 Subject: [PATCH 1528/1794] aspeed: Deprecate the sonorapass-bmc machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'sonorapass-bmc' machine represents a lab server that never entered production. There are no functional tests for this machine which makes harder to determine when something becomes deprecated or unused. Since the machine does not rely on any specific device models, it can be replaced by the 'ast2500-evb' machine using the 'fmc-model' option to specify the flash type. The I2C devices connected to the board can be defined via the QEMU command line. Cc: Patrick Williams Link: https://lore.kernel.org/qemu-devel/20251007141604.761686-2-clg@redhat.com Signed-off-by: Cédric Le Goater --- docs/about/deprecated.rst | 9 +++++++++ hw/arm/aspeed.c | 1 + 2 files changed, 10 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 67e527740c0e4..ce8fe9ac1be78 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -285,6 +285,15 @@ and serves as the initial engineering sample rather than a production version. A newer revision, A1, is now supported, and the ``ast2700a1-evb`` should replace the older A0 version. +Arm ``sonorapass-bmc`` machine (since 10.2) +''''''''''''''''''''''''''''''''''''''''''' + +The ``sonorapass-bmc`` machine represents a lab server that never +entered production. Since it does not rely on any specific device +models, it can be replaced by the ``ast2500-evb`` machine using the +``fmc-model`` option to specify the flash type. The I2C devices +connected to the board can be defined via the QEMU command line. + RISC-V default machine option (since 10.0) '''''''''''''''''''''''''''''''''''''''''' diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 58cfbc7137942..d3a0c81f454f2 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1552,6 +1552,7 @@ static void aspeed_machine_sonorapass_class_init(ObjectClass *oc, AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); mc->desc = "OCP SonoraPass BMC (ARM1176)"; + mc->deprecation_reason = "use 'ast2500-evb' instead"; amc->soc_name = "ast2500-a1"; amc->hw_strap1 = SONORAPASS_BMC_HW_STRAP1; amc->fmc_model = "mx66l1g45g"; From b3b3b5be9cca7741bdf195d6ea7d39e5926b8c5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 7 Oct 2025 16:16:01 +0200 Subject: [PATCH 1529/1794] aspeed: Deprecate the qcom-dc-scm-v1-bmc and qcom-firework-bmc machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are no functional tests for the 'qcom-dc-scm-v1-bmc' and 'qcom-firework-bmc' machines which makes harder to determine when something becomes deprecated or unused. Since the machines do not rely on any specific device models, they can be replaced by the 'ast2600-evb' machine using the 'fmc-model' option to specify the flash type. The I2C devices connected to the board can be defined via the QEMU command line. Cc: Jae Hyun Yoo Link: https://lore.kernel.org/qemu-devel/20251007141604.761686-3-clg@redhat.com Signed-off-by: Cédric Le Goater --- docs/about/deprecated.rst | 10 ++++++++++ hw/arm/aspeed.c | 2 ++ 2 files changed, 12 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index ce8fe9ac1be78..f6410037ead21 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -294,6 +294,16 @@ models, it can be replaced by the ``ast2500-evb`` machine using the ``fmc-model`` option to specify the flash type. The I2C devices connected to the board can be defined via the QEMU command line. +Arm ``qcom-dc-scm-v1-bmc`` and ``qcom-firework-bmc`` machine (since 10.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``qcom-dc-scm-v1-bmc`` and ``qcom-firework-bmc`` represent lab +servers that never entered production. Since they do not rely on any +specific device models, they can be replaced by the ``ast2600-evb`` +machine using the ``fmc-model`` option to specify the flash type. The +I2C devices connected to the board can be defined via the QEMU command +line. + RISC-V default machine option (since 10.0) '''''''''''''''''''''''''''''''''''''''''' diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index d3a0c81f454f2..0707a760fda69 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1926,6 +1926,7 @@ static void aspeed_machine_qcom_dc_scm_v1_class_init(ObjectClass *oc, AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); mc->desc = "Qualcomm DC-SCM V1 BMC (Cortex A7)"; + mc->deprecation_reason = "use 'ast2600-evb' instead"; amc->soc_name = "ast2600-a3"; amc->hw_strap1 = QCOM_DC_SCM_V1_BMC_HW_STRAP1; amc->hw_strap2 = QCOM_DC_SCM_V1_BMC_HW_STRAP2; @@ -1945,6 +1946,7 @@ static void aspeed_machine_qcom_firework_class_init(ObjectClass *oc, AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); mc->desc = "Qualcomm DC-SCM V1/Firework BMC (Cortex A7)"; + mc->deprecation_reason = "use 'ast2600-evb' instead"; amc->soc_name = "ast2600-a3"; amc->hw_strap1 = QCOM_DC_SCM_V1_BMC_HW_STRAP1; amc->hw_strap2 = QCOM_DC_SCM_V1_BMC_HW_STRAP2; From a7fd1fd7b3b19fb1a597d62d9c01edfc5ff483aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 7 Oct 2025 16:16:02 +0200 Subject: [PATCH 1530/1794] aspeed: Deprecate the fp5280g2-bmc machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are no functional tests for the 'fp5280g2-bmc' machine which makes harder to determine when something becomes deprecated or unused. Since the machine does not rely on any specific device models, it can be replaced by the 'ast2500-evb' machine using the 'fmc-model' option to specify the flash type. The I2C devices connected to the board can be defined via the QEMU command line. Cc: John Wang Link: https://lore.kernel.org/qemu-devel/20251007141604.761686-4-clg@redhat.com Signed-off-by: Cédric Le Goater --- docs/about/deprecated.rst | 8 ++++++++ hw/arm/aspeed.c | 1 + 2 files changed, 9 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index f6410037ead21..98361f5832dc5 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -304,6 +304,14 @@ machine using the ``fmc-model`` option to specify the flash type. The I2C devices connected to the board can be defined via the QEMU command line. +Arm ``fp5280g2-bmc`` machine (since 10.2) +''''''''''''''''''''''''''''''''''''''''''' + +The ``fp5280g2-bmc`` machine does not rely on any specific device +models, it can be replaced by the ``ast2500-evb`` machine using the +``fmc-model`` option to specify the flash type. The I2C devices +connected to the board can be defined via the QEMU command line. + RISC-V default machine option (since 10.0) '''''''''''''''''''''''''''''''''''''''''' diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 0707a760fda69..e73185eeb35c1 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1626,6 +1626,7 @@ static void aspeed_machine_fp5280g2_class_init(ObjectClass *oc, AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); mc->desc = "Inspur FP5280G2 BMC (ARM1176)"; + mc->deprecation_reason = "use 'ast2500-evb' instead"; amc->soc_name = "ast2500-a1"; amc->hw_strap1 = FP5280G2_BMC_HW_STRAP1; amc->fmc_model = "n25q512a"; From 3c17823e2940c0a7756246eb57b5971ecfe197b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 7 Oct 2025 16:16:03 +0200 Subject: [PATCH 1531/1794] test/functional/aarch64: Remove test for the ast2700a0-evb machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'ast2700a0-evb' machine was deprecated in commit 6888a4a9c860 and removal is scheduled in the QEMU 11.0 release. This change removes the corresponding tests ahead of time to save CI resources. Cc: Thomas Huth Reviewed-by: Thomas Huth Reviewed-by: Jamin Lin Link: https://lore.kernel.org/qemu-devel/20251007141604.761686-5-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/functional/aarch64/test_aspeed_ast2700.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/tests/functional/aarch64/test_aspeed_ast2700.py b/tests/functional/aarch64/test_aspeed_ast2700.py index ef7ed522afc31..a60dc1259f69c 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700.py +++ b/tests/functional/aarch64/test_aspeed_ast2700.py @@ -46,10 +46,6 @@ def verify_openbmc_boot_and_login(self, name): exec_command_and_wait_for_pattern(self, 'root', 'Password:') exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') - ASSET_SDK_V906_AST2700 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2700-a0-default-obmc.tar.gz', - '7247b6f19dbfb700686f8d9f723ac23f3eb229226c0589cb9b06b80d1b61f3cb') - ASSET_SDK_V908_AST2700A1 = Asset( 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.08/ast2700-default-obmc.tar.gz', 'eac3dc409b7ea3cd4b03d4792d3cebd469792ad893cb51e1d15f0fc20bd1e2cd') @@ -124,14 +120,6 @@ def start_ast2700_test_vbootrom(self, name): self.do_test_aarch64_aspeed_sdk_start( self.scratch_file(name, 'image-bmc')) - def test_aarch64_ast2700a0_evb_sdk_v09_06(self): - self.set_machine('ast2700a0-evb') - - self.archive_extract(self.ASSET_SDK_V906_AST2700) - self.start_ast2700_test('ast2700-a0-default') - self.verify_openbmc_boot_and_login('ast2700-a0-default') - self.do_ast2700_i2c_test() - def test_aarch64_ast2700a1_evb_sdk_v09_08(self): self.set_machine('ast2700a1-evb') self.require_netdev('user') From 1f993447cf432cf1d2bf12284e80b395518ea0bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 7 Oct 2025 16:16:04 +0200 Subject: [PATCH 1532/1794] test/functional/aarch64: Split the ast2700a1-evb OpenBMC boot test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'ast2700a1-evb' machine has two functional tests: one loading firmware components into memory and another using a vbootrom image. Both tests perform a full OpenBMC boot and run checks on I2C and PCIe devices, which is redundant and time-consuming. To save CI resources, the vbootrom test is refactored to focus on the firmware boot process only. The OpenBMC boot verification logic is split and a new verify_openbmc_boot_start() helper is introduced to only wait for the kernel to start. The vbootrom test now uses this function and the less essential I2C and PCIe checks have been removed from this test case. Cc: Thomas Huth Reviewed-by: Thomas Huth Reviewed-by: Jamin Lin Link: https://lore.kernel.org/qemu-devel/20251007141604.761686-6-clg@redhat.com [ clg: Changed pattern from 'Starting kernel ...' to 'Linux version ' ] Signed-off-by: Cédric Le Goater --- tests/functional/aarch64/test_aspeed_ast2700.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/functional/aarch64/test_aspeed_ast2700.py b/tests/functional/aarch64/test_aspeed_ast2700.py index a60dc1259f69c..0ced1a25021dc 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700.py +++ b/tests/functional/aarch64/test_aspeed_ast2700.py @@ -37,10 +37,13 @@ def verify_vbootrom_firmware_flow(self): wait_for_console_pattern(self, 'done') wait_for_console_pattern(self, 'Jumping to BL31 (Trusted Firmware-A)') - def verify_openbmc_boot_and_login(self, name): + def verify_openbmc_boot_start(self): wait_for_console_pattern(self, 'U-Boot 2023.10') wait_for_console_pattern(self, '## Loading kernel from FIT Image') - wait_for_console_pattern(self, 'Starting kernel ...') + wait_for_console_pattern(self, 'Linux version ') + + def verify_openbmc_boot_and_login(self, name): + self.verify_openbmc_boot_start() wait_for_console_pattern(self, f'{name} login:') exec_command_and_wait_for_pattern(self, 'root', 'Password:') @@ -141,9 +144,7 @@ def test_aarch64_ast2700a1_evb_sdk_vbootrom_v09_08(self): self.vm.add_args('-netdev', 'user,id=net1') self.start_ast2700_test_vbootrom('ast2700-default') self.verify_vbootrom_firmware_flow() - self.verify_openbmc_boot_and_login('ast2700-default') - self.do_ast2700_i2c_test() - self.do_ast2700_pcie_test() + self.verify_openbmc_boot_start() if __name__ == '__main__': QemuSystemTest.main() From 03c9c22f9019a98973227bc49db8819a35e78680 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Oct 2025 13:43:13 +0800 Subject: [PATCH 1533/1794] hw/arm/aspeed: Remove AspeedSoCState dependency from aspeed_uart_first() API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the aspeed_uart_first() helper to remove its dependency on AspeedSoCState and make the UART helper APIs more generic. The function now takes uarts_base as an integer parameter instead of requiring a full SoC class instance. Corresponding call sites in aspeed.c and aspeed_soc_common.c are updated accordingly. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013054334.955331-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 2 +- hw/arm/aspeed_soc_common.c | 2 +- include/hw/arm/aspeed_soc.h | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index e73185eeb35c1..49d8debf9988c 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1310,7 +1310,7 @@ static void aspeed_set_bmc_console(Object *obj, const char *value, Error **errp) AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(bmc); AspeedSoCClass *sc = ASPEED_SOC_CLASS(object_class_by_name(amc->soc_name)); int val; - int uart_first = aspeed_uart_first(sc); + int uart_first = aspeed_uart_first(sc->uarts_base); int uart_last = aspeed_uart_last(sc); if (sscanf(value, "uart%u", &val) != 1) { diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index bc70e864fba7c..a4e74acdce77b 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -62,7 +62,7 @@ bool aspeed_soc_uart_realize(AspeedSoCState *s, Error **errp) void aspeed_soc_uart_set_chr(AspeedSoCState *s, int dev, Chardev *chr) { AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - int uart_first = aspeed_uart_first(sc); + int uart_first = aspeed_uart_first(sc->uarts_base); int uart_index = aspeed_uart_index(dev); int i = uart_index - uart_first; diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index ed32efb543f44..5786fbbcbbe71 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -326,14 +326,14 @@ static inline int aspeed_uart_index(int uart_dev) return uart_dev - ASPEED_DEV_UART0; } -static inline int aspeed_uart_first(AspeedSoCClass *sc) +static inline int aspeed_uart_first(int uarts_base) { - return aspeed_uart_index(sc->uarts_base); + return aspeed_uart_index(uarts_base); } static inline int aspeed_uart_last(AspeedSoCClass *sc) { - return aspeed_uart_first(sc) + sc->uarts_num - 1; + return aspeed_uart_first(sc->uarts_base) + sc->uarts_num - 1; } #endif /* ASPEED_SOC_H */ From 37d2e4b5339ac3d584f8c9b0396c9060f52f12a0 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Oct 2025 13:43:14 +0800 Subject: [PATCH 1534/1794] hw/arm/aspeed: Remove AspeedSoCClass dependency from aspeed_uart_last() API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the aspeed_uart_last() helper to remove its dependency on AspeedSoCClass and make the UART helper APIs more generic. The function now takes uarts_base and uarts_num as integer parameters instead of requiring a full SoC class instance. All related call sites in aspeed.c are updated accordingly. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013054334.955331-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 2 +- include/hw/arm/aspeed_soc.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 49d8debf9988c..ad17471c8d613 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1311,7 +1311,7 @@ static void aspeed_set_bmc_console(Object *obj, const char *value, Error **errp) AspeedSoCClass *sc = ASPEED_SOC_CLASS(object_class_by_name(amc->soc_name)); int val; int uart_first = aspeed_uart_first(sc->uarts_base); - int uart_last = aspeed_uart_last(sc); + int uart_last = aspeed_uart_last(sc->uarts_base, sc->uarts_num); if (sscanf(value, "uart%u", &val) != 1) { error_setg(errp, "Bad value for \"uart\" property"); diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 5786fbbcbbe71..0162738f884e6 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -331,9 +331,9 @@ static inline int aspeed_uart_first(int uarts_base) return aspeed_uart_index(uarts_base); } -static inline int aspeed_uart_last(AspeedSoCClass *sc) +static inline int aspeed_uart_last(int uarts_base, int uarts_num) { - return aspeed_uart_first(sc->uarts_base) + sc->uarts_num - 1; + return aspeed_uart_first(uarts_base) + uarts_num - 1; } #endif /* ASPEED_SOC_H */ From 8f1ceec735cb3b44b68ee7dd129d8844f81e39c3 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Oct 2025 13:43:15 +0800 Subject: [PATCH 1535/1794] hw/arm/aspeed: Remove AspeedSoCState dependency from aspeed_soc_uart_set_chr() API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the aspeed_soc_uart_set_chr() helper to remove its dependency on AspeedSoCState and make the UART character device binding more generic. The function now takes SerialMM *uart, uarts_base, and uarts_num as arguments instead of relying on AspeedSoCState. All affected call sites in aspeed.c, aspeed_ast27x0-fc.c, and fby35.c are updated to use the new parameter format. This improves API flexibility and enables reuse across different Aspeed SoC variants without requiring access to internal SoC state. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013054334.955331-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 6 ++++-- hw/arm/aspeed_ast27x0-fc.c | 13 ++++++++++--- hw/arm/aspeed_soc_common.c | 10 +++++----- hw/arm/fby35.c | 10 ++++++++-- include/hw/arm/aspeed_soc.h | 3 ++- 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index ad17471c8d613..21ee62f75044d 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -299,12 +299,14 @@ static void connect_serial_hds_to_uarts(AspeedMachineState *bmc) AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); int uart_chosen = bmc->uart_chosen ? bmc->uart_chosen : amc->uart_default; - aspeed_soc_uart_set_chr(s, uart_chosen, serial_hd(0)); + aspeed_soc_uart_set_chr(s->uart, uart_chosen, sc->uarts_base, + sc->uarts_num, serial_hd(0)); for (int i = 1, uart = sc->uarts_base; i < sc->uarts_num; uart++) { if (uart == uart_chosen) { continue; } - aspeed_soc_uart_set_chr(s, uart, serial_hd(i++)); + aspeed_soc_uart_set_chr(s->uart, uart, sc->uarts_base, sc->uarts_num, + serial_hd(i++)); } } diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index 2e16a0340a7ba..e598f57ca228b 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -91,7 +91,8 @@ static bool ast2700fc_ca35_init(MachineState *machine, Error **errp) AST2700FC_HW_STRAP1, &error_abort); object_property_set_int(OBJECT(&s->ca35), "hw-strap2", AST2700FC_HW_STRAP2, &error_abort); - aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART12, serial_hd(0)); + aspeed_soc_uart_set_chr(soc->uart, ASPEED_DEV_UART12, sc->uarts_base, + sc->uarts_num, serial_hd(0)); if (!qdev_realize(DEVICE(&s->ca35), NULL, errp)) { return false; } @@ -115,6 +116,7 @@ static bool ast2700fc_ca35_init(MachineState *machine, Error **errp) static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) { AspeedSoCState *soc; + AspeedSoCClass *sc; Ast2700FCState *s = AST2700A1FC(machine); s->ssp_sysclk = clock_new(OBJECT(s), "SSP_SYSCLK"); clock_set_hz(s->ssp_sysclk, 200000000ULL); @@ -128,7 +130,9 @@ static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) OBJECT(&s->ssp_memory), &error_abort); soc = ASPEED_SOC(&s->ssp); - aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART4, serial_hd(1)); + sc = ASPEED_SOC_GET_CLASS(soc); + aspeed_soc_uart_set_chr(soc->uart, ASPEED_DEV_UART4, sc->uarts_base, + sc->uarts_num, serial_hd(1)); if (!qdev_realize(DEVICE(&s->ssp), NULL, errp)) { return false; } @@ -139,6 +143,7 @@ static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) { AspeedSoCState *soc; + AspeedSoCClass *sc; Ast2700FCState *s = AST2700A1FC(machine); s->tsp_sysclk = clock_new(OBJECT(s), "TSP_SYSCLK"); clock_set_hz(s->tsp_sysclk, 200000000ULL); @@ -152,7 +157,9 @@ static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) OBJECT(&s->tsp_memory), &error_abort); soc = ASPEED_SOC(&s->tsp); - aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART7, serial_hd(2)); + sc = ASPEED_SOC_GET_CLASS(soc); + aspeed_soc_uart_set_chr(soc->uart, ASPEED_DEV_UART7, sc->uarts_base, + sc->uarts_num, serial_hd(2)); if (!qdev_realize(DEVICE(&s->tsp), NULL, errp)) { return false; } diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index a4e74acdce77b..ddcbba0020ee8 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -59,15 +59,15 @@ bool aspeed_soc_uart_realize(AspeedSoCState *s, Error **errp) return true; } -void aspeed_soc_uart_set_chr(AspeedSoCState *s, int dev, Chardev *chr) +void aspeed_soc_uart_set_chr(SerialMM *uart, int dev, int uarts_base, + int uarts_num, Chardev *chr) { - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - int uart_first = aspeed_uart_first(sc->uarts_base); + int uart_first = aspeed_uart_first(uarts_base); int uart_index = aspeed_uart_index(dev); int i = uart_index - uart_first; - g_assert(0 <= i && i < ARRAY_SIZE(s->uart) && i < sc->uarts_num); - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); + g_assert(0 <= i && i < ASPEED_UARTS_NUM && i < uarts_num); + qdev_prop_set_chr(DEVICE(&uart[i]), "chardev", chr); } /* diff --git a/hw/arm/fby35.c b/hw/arm/fby35.c index c14fc2efe9bb7..5a94c847d365e 100644 --- a/hw/arm/fby35.c +++ b/hw/arm/fby35.c @@ -71,9 +71,11 @@ static void fby35_bmc_write_boot_rom(DriveInfo *dinfo, MemoryRegion *mr, static void fby35_bmc_init(Fby35State *s) { AspeedSoCState *soc; + AspeedSoCClass *sc; object_initialize_child(OBJECT(s), "bmc", &s->bmc, "ast2600-a3"); soc = ASPEED_SOC(&s->bmc); + sc = ASPEED_SOC_GET_CLASS(soc); memory_region_init(&s->bmc_memory, OBJECT(&s->bmc), "bmc-memory", UINT64_MAX); @@ -91,7 +93,8 @@ static void fby35_bmc_init(Fby35State *s) &error_abort); object_property_set_int(OBJECT(&s->bmc), "hw-strap2", 0x00000003, &error_abort); - aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART5, serial_hd(0)); + aspeed_soc_uart_set_chr(soc->uart, ASPEED_DEV_UART5, sc->uarts_base, + sc->uarts_num, serial_hd(0)); qdev_realize(DEVICE(&s->bmc), NULL, &error_abort); aspeed_board_init_flashes(&soc->fmc, "n25q00", 2, 0); @@ -118,12 +121,14 @@ static void fby35_bmc_init(Fby35State *s) static void fby35_bic_init(Fby35State *s) { AspeedSoCState *soc; + AspeedSoCClass *sc; s->bic_sysclk = clock_new(OBJECT(s), "SYSCLK"); clock_set_hz(s->bic_sysclk, 200000000ULL); object_initialize_child(OBJECT(s), "bic", &s->bic, "ast1030-a1"); soc = ASPEED_SOC(&s->bic); + sc = ASPEED_SOC_GET_CLASS(soc); memory_region_init(&s->bic_memory, OBJECT(&s->bic), "bic-memory", UINT64_MAX); @@ -131,7 +136,8 @@ static void fby35_bic_init(Fby35State *s) qdev_connect_clock_in(DEVICE(&s->bic), "sysclk", s->bic_sysclk); object_property_set_link(OBJECT(&s->bic), "memory", OBJECT(&s->bic_memory), &error_abort); - aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART5, serial_hd(1)); + aspeed_soc_uart_set_chr(soc->uart, ASPEED_DEV_UART5, sc->uarts_base, + sc->uarts_num, serial_hd(1)); qdev_realize(DEVICE(&s->bic), NULL, &error_abort); aspeed_board_init_flashes(&soc->fmc, "sst25vf032b", 2, 2); diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 0162738f884e6..c870bf55865e2 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -306,7 +306,8 @@ enum { qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev); bool aspeed_soc_uart_realize(AspeedSoCState *s, Error **errp); -void aspeed_soc_uart_set_chr(AspeedSoCState *s, int dev, Chardev *chr); +void aspeed_soc_uart_set_chr(SerialMM *uart, int dev, int uarts_base, + int uarts_num, Chardev *chr); bool aspeed_soc_dram_init(AspeedSoCState *s, Error **errp); void aspeed_mmio_map(AspeedSoCState *s, SysBusDevice *dev, int n, hwaddr addr); void aspeed_mmio_map_unimplemented(AspeedSoCState *s, SysBusDevice *dev, From bb3219345aa195b1ac3d0c91ab30d90d2fc85c16 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Oct 2025 13:43:16 +0800 Subject: [PATCH 1536/1794] hw/arm/aspeed: Remove AspeedSoCClass dependency from aspeed_soc_cpu_type() API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the aspeed_soc_cpu_type() helper to remove its dependency on AspeedSoCClass and make CPU type retrieval more generic. The function now takes valid_cpu_types as a const char * const * parameter instead of requiring a full AspeedSoCClass instance. All corresponding call sites in various Aspeed SoC initialization files (aspeed_ast10x0.c, aspeed_ast2400.c, aspeed_ast2600.c, aspeed_ast27x0.c, and related variants) are updated accordingly. This change simplifies the API, eliminates unnecessary type coupling, and improves code reusability across different SoC families. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013054334.955331-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast10x0.c | 3 ++- hw/arm/aspeed_ast2400.c | 2 +- hw/arm/aspeed_ast2600.c | 2 +- hw/arm/aspeed_ast27x0-ssp.c | 3 ++- hw/arm/aspeed_ast27x0-tsp.c | 3 ++- hw/arm/aspeed_ast27x0.c | 2 +- hw/arm/aspeed_soc_common.c | 10 +++++----- include/hw/arm/aspeed_soc.h | 3 +-- 8 files changed, 15 insertions(+), 13 deletions(-) diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index c446e70b24a9e..dab012aa953d5 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -211,7 +211,8 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) /* AST1030 CPU Core */ armv7m = DEVICE(&a->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 256); - qdev_prop_set_string(armv7m, "cpu-type", aspeed_soc_cpu_type(sc)); + qdev_prop_set_string(armv7m, "cpu-type", + aspeed_soc_cpu_type(sc->valid_cpu_types)); qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); object_property_set_link(OBJECT(&a->armv7m), "memory", OBJECT(s->memory), &error_abort); diff --git a/hw/arm/aspeed_ast2400.c b/hw/arm/aspeed_ast2400.c index c7b0f21887b5d..53c2a5156dd53 100644 --- a/hw/arm/aspeed_ast2400.c +++ b/hw/arm/aspeed_ast2400.c @@ -157,7 +157,7 @@ static void aspeed_ast2400_soc_init(Object *obj) for (i = 0; i < sc->num_cpus; i++) { object_initialize_child(obj, "cpu[*]", &a->cpu[i], - aspeed_soc_cpu_type(sc)); + aspeed_soc_cpu_type(sc->valid_cpu_types)); } snprintf(typename, sizeof(typename), "aspeed.scu-%s", socname); diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index 03e5df96bb4f3..0299d97929186 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -167,7 +167,7 @@ static void aspeed_soc_ast2600_init(Object *obj) for (i = 0; i < sc->num_cpus; i++) { object_initialize_child(obj, "cpu[*]", &a->cpu[i], - aspeed_soc_cpu_type(sc)); + aspeed_soc_cpu_type(sc->valid_cpu_types)); } snprintf(typename, sizeof(typename), "aspeed.scu-%s", socname); diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c index 80ec5996c1d1d..490e98b924df5 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -174,7 +174,8 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) /* AST27X0 SSP Core */ armv7m = DEVICE(&a->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 256); - qdev_prop_set_string(armv7m, "cpu-type", aspeed_soc_cpu_type(sc)); + qdev_prop_set_string(armv7m, "cpu-type", + aspeed_soc_cpu_type(sc->valid_cpu_types)); qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); object_property_set_link(OBJECT(&a->armv7m), "memory", OBJECT(s->memory), &error_abort); diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index 4e0efaef07c04..d83f90ef00cec 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -174,7 +174,8 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) /* AST27X0 TSP Core */ armv7m = DEVICE(&a->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 256); - qdev_prop_set_string(armv7m, "cpu-type", aspeed_soc_cpu_type(sc)); + qdev_prop_set_string(armv7m, "cpu-type", + aspeed_soc_cpu_type(sc->valid_cpu_types)); qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); object_property_set_link(OBJECT(&a->armv7m), "memory", OBJECT(s->memory), &error_abort); diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 853339119ff65..2f018e9e588af 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -436,7 +436,7 @@ static void aspeed_soc_ast2700_init(Object *obj) for (i = 0; i < sc->num_cpus; i++) { object_initialize_child(obj, "cpu[*]", &a->cpu[i], - aspeed_soc_cpu_type(sc)); + aspeed_soc_cpu_type(sc->valid_cpu_types)); } object_initialize_child(obj, "gic", &a->gic, gicv3_class_name()); diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index ddcbba0020ee8..16c7c4bb78d41 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -22,12 +22,12 @@ #include "qemu/datadir.h" -const char *aspeed_soc_cpu_type(AspeedSoCClass *sc) +const char *aspeed_soc_cpu_type(const char * const *valid_cpu_types) { - assert(sc->valid_cpu_types); - assert(sc->valid_cpu_types[0]); - assert(!sc->valid_cpu_types[1]); - return sc->valid_cpu_types[0]; + assert(valid_cpu_types); + assert(valid_cpu_types[0]); + assert(!valid_cpu_types[1]); + return valid_cpu_types[0]; } qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev) diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index c870bf55865e2..385b657b50960 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -202,8 +202,6 @@ struct AspeedSoCClass { bool (*boot_from_emmc)(AspeedSoCState *s); }; -const char *aspeed_soc_cpu_type(AspeedSoCClass *sc); - enum { ASPEED_DEV_VBOOTROM, ASPEED_DEV_SPI_BOOT, @@ -304,6 +302,7 @@ enum { ASPEED_DEV_IPC1, }; +const char *aspeed_soc_cpu_type(const char * const *valid_cpu_types); qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev); bool aspeed_soc_uart_realize(AspeedSoCState *s, Error **errp); void aspeed_soc_uart_set_chr(SerialMM *uart, int dev, int uarts_base, From 448c4502a50bc4fc056726d5953eb873a2794486 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Oct 2025 13:43:17 +0800 Subject: [PATCH 1537/1794] hw/arm/aspeed: Remove AspeedSoCState dependency from aspeed_mmio_map() API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor aspeed_mmio_map() to take MemoryRegion * instead of AspeedSoCState *, making the MMIO mapping helper more generic and decoupled from SoC state. Update all call sites to pass s->memory (or equivalent) explicitly. Touched files include: headers, aspeed_soc_common.c, and SoC realize paths in AST10x0/2400/2600/27x0 (SSP/TSP) and AST2700. This reduces coupling, improves reuse across variants, and clarifies the API boundary between SoC state and memory mapping. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013054334.955331-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast10x0.c | 37 ++++++++++++--------- hw/arm/aspeed_ast2400.c | 47 +++++++++++++++------------ hw/arm/aspeed_ast2600.c | 65 +++++++++++++++++++++---------------- hw/arm/aspeed_ast27x0-ssp.c | 7 ++-- hw/arm/aspeed_ast27x0-tsp.c | 7 ++-- hw/arm/aspeed_ast27x0.c | 60 +++++++++++++++++++--------------- hw/arm/aspeed_soc_common.c | 8 ++--- include/hw/arm/aspeed_soc.h | 3 +- 8 files changed, 133 insertions(+), 101 deletions(-) diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index dab012aa953d5..caa9feb667ec1 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -242,7 +242,8 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->scu), 0, + sc->memmap[ASPEED_DEV_SCU]); /* I2C */ @@ -251,7 +252,8 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->i2c), 0, + sc->memmap[ASPEED_DEV_I2C]); for (i = 0; i < ASPEED_I2C_GET_CLASS(&s->i2c)->num_busses; i++) { qemu_irq irq = qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[ASPEED_DEV_I2C] + i); @@ -263,7 +265,8 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->i3c), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i3c), 0, sc->memmap[ASPEED_DEV_I3C]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->i3c), 0, + sc->memmap[ASPEED_DEV_I3C]); for (i = 0; i < ASPEED_I3C_NR_DEVICES; i++) { qemu_irq irq = qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[ASPEED_DEV_I3C] + i); @@ -275,7 +278,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->peci), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->peci), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->peci), 0, sc->memmap[ASPEED_DEV_PECI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->peci), 0, aspeed_soc_get_irq(s, ASPEED_DEV_PECI)); @@ -284,7 +287,8 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->lpc), 0, + sc->memmap[ASPEED_DEV_LPC]); /* Connect the LPC IRQ to the GIC. It is otherwise unused. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 0, @@ -320,7 +324,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->timerctrl), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->timerctrl), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->timerctrl), 0, sc->memmap[ASPEED_DEV_TIMER1]); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); @@ -331,7 +335,8 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->adc), 0, + sc->memmap[ASPEED_DEV_ADC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); @@ -341,8 +346,9 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->fmc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 1, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->fmc), 0, + sc->memmap[ASPEED_DEV_FMC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->fmc), 1, ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); @@ -354,9 +360,9 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->spi[i]), 0, sc->memmap[ASPEED_DEV_SPI1 + i]); - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 1, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->spi[i]), 1, ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); } @@ -364,7 +370,8 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sbc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sbc), 0, sc->memmap[ASPEED_DEV_SBC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->sbc), 0, + sc->memmap[ASPEED_DEV_SBC]); /* HACE */ object_property_set_link(OBJECT(&s->hace), "dram", OBJECT(&s->sram), @@ -372,7 +379,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->hace), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->hace), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->hace), 0, sc->memmap[ASPEED_DEV_HACE]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); @@ -387,14 +394,14 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->wdt[i]), 0, wdt_offset); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->wdt[i]), 0, wdt_offset); } /* GPIO */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->gpio), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); diff --git a/hw/arm/aspeed_ast2400.c b/hw/arm/aspeed_ast2400.c index 53c2a5156dd53..669075221581a 100644 --- a/hw/arm/aspeed_ast2400.c +++ b/hw/arm/aspeed_ast2400.c @@ -289,13 +289,15 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->scu), 0, + sc->memmap[ASPEED_DEV_SCU]); /* VIC */ if (!sysbus_realize(SYS_BUS_DEVICE(&a->vic), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->vic), 0, sc->memmap[ASPEED_DEV_VIC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&a->vic), 0, + sc->memmap[ASPEED_DEV_VIC]); sysbus_connect_irq(SYS_BUS_DEVICE(&a->vic), 0, qdev_get_gpio_in(DEVICE(&a->cpu), ARM_CPU_IRQ)); sysbus_connect_irq(SYS_BUS_DEVICE(&a->vic), 1, @@ -305,7 +307,8 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->rtc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_DEV_RTC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->rtc), 0, + sc->memmap[ASPEED_DEV_RTC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_RTC)); @@ -315,7 +318,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->timerctrl), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->timerctrl), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->timerctrl), 0, sc->memmap[ASPEED_DEV_TIMER1]); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); @@ -326,7 +329,8 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->adc), 0, + sc->memmap[ASPEED_DEV_ADC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); @@ -341,7 +345,8 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->i2c), 0, + sc->memmap[ASPEED_DEV_I2C]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), 0, aspeed_soc_get_irq(s, ASPEED_DEV_I2C)); @@ -349,7 +354,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->peci), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->peci), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->peci), 0, sc->memmap[ASPEED_DEV_PECI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->peci), 0, aspeed_soc_get_irq(s, ASPEED_DEV_PECI)); @@ -360,8 +365,9 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->fmc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 1, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->fmc), 0, + sc->memmap[ASPEED_DEV_FMC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->fmc), 1, ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); @@ -377,9 +383,9 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->spi[i]), 0, sc->memmap[ASPEED_DEV_SPI1 + i]); - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 1, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->spi[i]), 1, ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); } @@ -388,7 +394,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->ehci[i]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->ehci[i]), 0, sc->memmap[ASPEED_DEV_EHCI1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, aspeed_soc_get_irq(s, ASPEED_DEV_EHCI1 + i)); @@ -398,7 +404,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdmc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdmc), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->sdmc), 0, sc->memmap[ASPEED_DEV_SDMC]); /* Watch dog */ @@ -411,7 +417,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->wdt[i]), 0, wdt_offset); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->wdt[i]), 0, wdt_offset); } /* RAM */ @@ -426,7 +432,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->ftgmac100[i]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, sc->memmap[ASPEED_DEV_ETH1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ETH1 + i)); @@ -436,7 +442,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->xdma), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->xdma), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->xdma), 0, sc->memmap[ASPEED_DEV_XDMA]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->xdma), 0, aspeed_soc_get_irq(s, ASPEED_DEV_XDMA)); @@ -445,7 +451,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->gpio), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); @@ -454,7 +460,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdhci), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->sdhci), 0, sc->memmap[ASPEED_DEV_SDHCI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, aspeed_soc_get_irq(s, ASPEED_DEV_SDHCI)); @@ -463,7 +469,8 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->lpc), 0, + sc->memmap[ASPEED_DEV_LPC]); /* Connect the LPC IRQ to the VIC */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 0, @@ -496,7 +503,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->hace), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->hace), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->hace), 0, sc->memmap[ASPEED_DEV_HACE]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index 0299d97929186..bf0ecde0514bb 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -324,7 +324,7 @@ static bool aspeed_soc_ast2600_pcie_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie_phy[0]), errp)) { return false; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->pcie_phy[0]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->pcie_phy[0]), 0, sc->memmap[ASPEED_DEV_PCIE_PHY1]); object_property_set_int(OBJECT(&s->pcie[0]), "dram-base", @@ -335,7 +335,7 @@ static bool aspeed_soc_ast2600_pcie_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie[0]), errp)) { return false; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->pcie[0]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->pcie[0]), 0, sc->memmap[ASPEED_DEV_PCIE0]); irq = qdev_get_gpio_in(DEVICE(&a->a7mpcore), @@ -414,7 +414,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) &error_abort); sysbus_realize(SYS_BUS_DEVICE(&a->a7mpcore), &error_abort); - aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->a7mpcore), 0, ASPEED_A7MPCORE_ADDR); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&a->a7mpcore), 0, + ASPEED_A7MPCORE_ADDR); for (i = 0; i < sc->num_cpus; i++) { SysBusDevice *sbd = SYS_BUS_DEVICE(&a->a7mpcore); @@ -448,13 +449,15 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->scu), 0, + sc->memmap[ASPEED_DEV_SCU]); /* RTC */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->rtc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_DEV_RTC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->rtc), 0, + sc->memmap[ASPEED_DEV_RTC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_RTC)); @@ -464,7 +467,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->timerctrl), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->timerctrl), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->timerctrl), 0, sc->memmap[ASPEED_DEV_TIMER1]); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); @@ -475,7 +478,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->adc), 0, + sc->memmap[ASPEED_DEV_ADC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); @@ -490,7 +494,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->i2c), 0, + sc->memmap[ASPEED_DEV_I2C]); for (i = 0; i < ASPEED_I2C_GET_CLASS(&s->i2c)->num_busses; i++) { irq = qdev_get_gpio_in(DEVICE(&a->a7mpcore), sc->irqmap[ASPEED_DEV_I2C] + i); @@ -502,7 +507,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->peci), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->peci), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->peci), 0, sc->memmap[ASPEED_DEV_PECI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->peci), 0, aspeed_soc_get_irq(s, ASPEED_DEV_PECI)); @@ -518,8 +523,9 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->fmc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 1, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->fmc), 0, + sc->memmap[ASPEED_DEV_FMC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->fmc), 1, ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); @@ -537,9 +543,9 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->spi[i]), 0, sc->memmap[ASPEED_DEV_SPI1 + i]); - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 1, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->spi[i]), 1, ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); } @@ -548,7 +554,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->ehci[i]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->ehci[i]), 0, sc->memmap[ASPEED_DEV_EHCI1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, aspeed_soc_get_irq(s, ASPEED_DEV_EHCI1 + i)); @@ -558,7 +564,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdmc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdmc), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->sdmc), 0, sc->memmap[ASPEED_DEV_SDMC]); /* Watch dog */ @@ -571,7 +577,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->wdt[i]), 0, wdt_offset); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->wdt[i]), 0, wdt_offset); } /* RAM */ @@ -586,7 +592,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->ftgmac100[i]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, sc->memmap[ASPEED_DEV_ETH1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ETH1 + i)); @@ -597,7 +603,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->mii[i]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->mii[i]), 0, sc->memmap[ASPEED_DEV_MII1 + i]); } @@ -605,7 +611,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->xdma), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->xdma), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->xdma), 0, sc->memmap[ASPEED_DEV_XDMA]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->xdma), 0, aspeed_soc_get_irq(s, ASPEED_DEV_XDMA)); @@ -614,7 +620,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->gpio), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); @@ -622,7 +628,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio_1_8v), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->gpio_1_8v), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->gpio_1_8v), 0, sc->memmap[ASPEED_DEV_GPIO_1_8V]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio_1_8v), 0, aspeed_soc_get_irq(s, ASPEED_DEV_GPIO_1_8V)); @@ -631,7 +637,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdhci), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->sdhci), 0, sc->memmap[ASPEED_DEV_SDHCI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, aspeed_soc_get_irq(s, ASPEED_DEV_SDHCI)); @@ -640,7 +646,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->emmc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->emmc), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->emmc), 0, sc->memmap[ASPEED_DEV_EMMC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_EMMC)); @@ -649,7 +655,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->lpc), 0, + sc->memmap[ASPEED_DEV_LPC]); /* Connect the LPC IRQ to the GIC. It is otherwise unused. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 0, @@ -685,7 +692,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->hace), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->hace), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->hace), 0, sc->memmap[ASPEED_DEV_HACE]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); @@ -694,7 +701,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->i3c), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i3c), 0, sc->memmap[ASPEED_DEV_I3C]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->i3c), 0, + sc->memmap[ASPEED_DEV_I3C]); for (i = 0; i < ASPEED_I3C_NR_DEVICES; i++) { irq = qdev_get_gpio_in(DEVICE(&a->a7mpcore), sc->irqmap[ASPEED_DEV_I3C] + i); @@ -706,14 +714,15 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sbc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sbc), 0, sc->memmap[ASPEED_DEV_SBC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->sbc), 0, + sc->memmap[ASPEED_DEV_SBC]); /* FSI */ for (i = 0; i < ASPEED_FSI_NUM; i++) { if (!sysbus_realize(SYS_BUS_DEVICE(&s->fsi[i]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fsi[i]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->fsi[i]), 0, sc->memmap[ASPEED_DEV_FSI1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fsi[i]), 0, aspeed_soc_get_irq(s, ASPEED_DEV_FSI1 + i)); diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c index 490e98b924df5..83cf3c14b6c3c 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -196,14 +196,15 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->scu), 0, + sc->memmap[ASPEED_DEV_SCU]); /* INTC */ if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[0]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[0]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&a->intc[0]), 0, sc->memmap[ASPEED_DEV_INTC]); /* INTCIO */ @@ -211,7 +212,7 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[1]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&a->intc[1]), 0, sc->memmap[ASPEED_DEV_INTCIO]); /* irq source orgates -> INTC0 */ diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index d83f90ef00cec..86aa565608985 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -196,14 +196,15 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->scu), 0, + sc->memmap[ASPEED_DEV_SCU]); /* INTC */ if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[0]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[0]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&a->intc[0]), 0, sc->memmap[ASPEED_DEV_INTC]); /* INTCIO */ @@ -211,7 +212,7 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[1]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&a->intc[1]), 0, sc->memmap[ASPEED_DEV_INTCIO]); /* irq source orgates -> INTC */ diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 2f018e9e588af..8db67dc806ac6 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -589,9 +589,9 @@ static bool aspeed_soc_ast2700_gic_realize(DeviceState *dev, Error **errp) return false; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->gic), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&a->gic), 0, sc->memmap[ASPEED_GIC_DIST]); - aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->gic), 1, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&a->gic), 1, sc->memmap[ASPEED_GIC_REDIST]); for (i = 0; i < sc->num_cpus; i++) { @@ -647,7 +647,7 @@ static bool aspeed_soc_ast2700_pcie_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie_phy[i]), errp)) { return false; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->pcie_phy[i]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->pcie_phy[i]), 0, sc->memmap[ASPEED_DEV_PCIE_PHY0 + i]); object_property_set_int(OBJECT(&s->pcie[i]), "dram-base", @@ -658,7 +658,7 @@ static bool aspeed_soc_ast2700_pcie_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie[i]), errp)) { return false; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->pcie[i]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->pcie[i]), 0, sc->memmap[ASPEED_DEV_PCIE0 + i]); irq = aspeed_soc_get_irq(s, ASPEED_DEV_PCIE0 + i); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie[i].rc), 0, irq); @@ -719,7 +719,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[0]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&a->intc[0]), 0, sc->memmap[ASPEED_DEV_INTC]); /* INTCIO */ @@ -727,7 +727,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[1]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&a->intc[1]), 0, sc->memmap[ASPEED_DEV_INTCIO]); /* irq sources -> orgates -> INTC */ @@ -777,13 +777,14 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->scu), 0, + sc->memmap[ASPEED_DEV_SCU]); /* SCU1 */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->scuio), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scuio), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->scuio), 0, sc->memmap[ASPEED_DEV_SCUIO]); /* UART */ @@ -800,8 +801,9 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->fmc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 1, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->fmc), 0, + sc->memmap[ASPEED_DEV_FMC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->fmc), 1, ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); @@ -819,9 +821,9 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->spi[i]), 0, sc->memmap[ASPEED_DEV_SPI0 + i]); - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 1, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->spi[i]), 1, ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); } @@ -830,7 +832,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->ehci[i]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->ehci[i]), 0, sc->memmap[ASPEED_DEV_EHCI1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, aspeed_soc_get_irq(s, ASPEED_DEV_EHCI1 + i)); @@ -848,7 +850,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdmc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdmc), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->sdmc), 0, sc->memmap[ASPEED_DEV_SDMC]); /* RAM */ @@ -865,7 +867,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->ftgmac100[i]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, sc->memmap[ASPEED_DEV_ETH1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ETH1 + i)); @@ -876,7 +878,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->mii[i]), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->mii[i]), 0, sc->memmap[ASPEED_DEV_MII1 + i]); } @@ -890,26 +892,28 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->wdt[i]), 0, wdt_offset); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->wdt[i]), 0, wdt_offset); } /* SLI */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->sli), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sli), 0, sc->memmap[ASPEED_DEV_SLI]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->sli), 0, + sc->memmap[ASPEED_DEV_SLI]); if (!sysbus_realize(SYS_BUS_DEVICE(&s->sliio), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sliio), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->sliio), 0, sc->memmap[ASPEED_DEV_SLIIO]); /* ADC */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->adc), 0, + sc->memmap[ASPEED_DEV_ADC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); @@ -919,7 +923,8 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->i2c), 0, + sc->memmap[ASPEED_DEV_I2C]); for (i = 0; i < ASPEED_I2C_GET_CLASS(&s->i2c)->num_busses; i++) { /* * The AST2700 I2C controller has one source INTC per bus. @@ -948,7 +953,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->gpio), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); @@ -957,7 +962,8 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->rtc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_DEV_RTC]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->rtc), 0, + sc->memmap[ASPEED_DEV_RTC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_RTC)); @@ -965,7 +971,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdhci), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->sdhci), 0, sc->memmap[ASPEED_DEV_SDHCI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, aspeed_soc_get_irq(s, ASPEED_DEV_SDHCI)); @@ -974,7 +980,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->emmc), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->emmc), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->emmc), 0, sc->memmap[ASPEED_DEV_EMMC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_EMMC)); @@ -985,7 +991,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->timerctrl), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->timerctrl), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->timerctrl), 0, sc->memmap[ASPEED_DEV_TIMER1]); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); @@ -998,7 +1004,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->hace), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->hace), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->hace), 0, sc->memmap[ASPEED_DEV_HACE]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index 16c7c4bb78d41..ca4e589dce85d 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -53,7 +53,7 @@ bool aspeed_soc_uart_realize(AspeedSoCState *s, Error **errp) } sysbus_connect_irq(SYS_BUS_DEVICE(smm), 0, aspeed_soc_get_irq(s, uart)); - aspeed_mmio_map(s, SYS_BUS_DEVICE(smm), 0, sc->memmap[uart]); + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(smm), 0, sc->memmap[uart]); } return true; @@ -111,10 +111,10 @@ bool aspeed_soc_dram_init(AspeedSoCState *s, Error **errp) return true; } -void aspeed_mmio_map(AspeedSoCState *s, SysBusDevice *dev, int n, hwaddr addr) +void aspeed_mmio_map(MemoryRegion *memory, SysBusDevice *dev, int n, + hwaddr addr) { - memory_region_add_subregion(s->memory, addr, - sysbus_mmio_get_region(dev, n)); + memory_region_add_subregion(memory, addr, sysbus_mmio_get_region(dev, n)); } void aspeed_mmio_map_unimplemented(AspeedSoCState *s, SysBusDevice *dev, diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 385b657b50960..606cf6bb61935 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -308,7 +308,8 @@ bool aspeed_soc_uart_realize(AspeedSoCState *s, Error **errp); void aspeed_soc_uart_set_chr(SerialMM *uart, int dev, int uarts_base, int uarts_num, Chardev *chr); bool aspeed_soc_dram_init(AspeedSoCState *s, Error **errp); -void aspeed_mmio_map(AspeedSoCState *s, SysBusDevice *dev, int n, hwaddr addr); +void aspeed_mmio_map(MemoryRegion *memory, SysBusDevice *dev, int n, + hwaddr addr); void aspeed_mmio_map_unimplemented(AspeedSoCState *s, SysBusDevice *dev, const char *name, hwaddr addr, uint64_t size); From 68f915b91cbf697b5f65a6aca72f260c6c29d00f Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Oct 2025 13:43:18 +0800 Subject: [PATCH 1538/1794] hw/arm/aspeed: Remove AspeedSoCState dependency from aspeed_mmio_map_unimplemented() API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor aspeed_mmio_map_unimplemented() to take MemoryRegion * instead of AspeedSoCState *, removing its dependency on SoC state and aligning it with the updated aspeed_mmio_map() interface. All related call sites are updated to explicitly pass s->memory. Affected files include headers, aspeed_soc_common.c, and SoC realize functions in AST10x0, AST2400, AST2600, AST27x0 (SSP/TSP), and AST2700. This change simplifies the MMIO mapping helpers, improves API consistency, and reduces coupling between SoC logic and memory operations. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013054334.955331-7-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast10x0.c | 24 ++++++++++++++++-------- hw/arm/aspeed_ast2400.c | 6 ++++-- hw/arm/aspeed_ast2600.c | 12 ++++++++---- hw/arm/aspeed_ast27x0-ssp.c | 8 ++++---- hw/arm/aspeed_ast27x0-tsp.c | 8 ++++---- hw/arm/aspeed_ast27x0.c | 10 +++++----- hw/arm/aspeed_soc_common.c | 4 ++-- include/hw/arm/aspeed_soc.h | 2 +- 8 files changed, 44 insertions(+), 30 deletions(-) diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index caa9feb667ec1..e861b6dad6992 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -201,10 +201,12 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) } /* General I/O memory space to catch all unimplemented device */ - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem), "aspeed.io", + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->iomem), + "aspeed.io", sc->memmap[ASPEED_DEV_IOMEM], ASPEED_SOC_IOMEM_SIZE); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->sbc_unimplemented), + aspeed_mmio_map_unimplemented(s->memory, + SYS_BUS_DEVICE(&s->sbc_unimplemented), "aspeed.sbc", sc->memmap[ASPEED_DEV_SBC], 0x40000); @@ -406,20 +408,26 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->pwm), "aspeed.pwm", + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->pwm), + "aspeed.pwm", sc->memmap[ASPEED_DEV_PWM], 0x100); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->espi), "aspeed.espi", + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->espi), + "aspeed.espi", sc->memmap[ASPEED_DEV_ESPI], 0x800); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->udc), "aspeed.udc", + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->udc), + "aspeed.udc", sc->memmap[ASPEED_DEV_UDC], 0x1000); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->sgpiom), "aspeed.sgpiom", + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->sgpiom), + "aspeed.sgpiom", sc->memmap[ASPEED_DEV_SGPIOM], 0x100); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->jtag[0]), "aspeed.jtag", + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->jtag[0]), + "aspeed.jtag", sc->memmap[ASPEED_DEV_JTAG0], 0x20); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->jtag[1]), "aspeed.jtag", + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->jtag[1]), + "aspeed.jtag", sc->memmap[ASPEED_DEV_JTAG1], 0x20); } diff --git a/hw/arm/aspeed_ast2400.c b/hw/arm/aspeed_ast2400.c index 669075221581a..e0604851a5586 100644 --- a/hw/arm/aspeed_ast2400.c +++ b/hw/arm/aspeed_ast2400.c @@ -259,12 +259,14 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) &s->spi_boot_container); /* IO space */ - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem), "aspeed.io", + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->iomem), + "aspeed.io", sc->memmap[ASPEED_DEV_IOMEM], ASPEED_SOC_IOMEM_SIZE); /* Video engine stub */ - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->video), "aspeed.video", + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->video), + "aspeed.video", sc->memmap[ASPEED_DEV_VIDEO], 0x1000); /* CPU */ diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index bf0ecde0514bb..ed0985a16e089 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -370,16 +370,19 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) &s->spi_boot_container); /* IO space */ - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem), "aspeed.io", + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->iomem), + "aspeed.io", sc->memmap[ASPEED_DEV_IOMEM], ASPEED_SOC_IOMEM_SIZE); /* Video engine stub */ - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->video), "aspeed.video", + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->video), + "aspeed.video", sc->memmap[ASPEED_DEV_VIDEO], 0x1000); /* eMMC Boot Controller stub */ - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->emmc_boot_controller), + aspeed_mmio_map_unimplemented(s->memory, + SYS_BUS_DEVICE(&s->emmc_boot_controller), "aspeed.emmc-boot-controller", sc->memmap[ASPEED_DEV_EMMC_BC], 0x1000); @@ -441,7 +444,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) sc->memmap[ASPEED_DEV_SRAM], &s->sram); /* DPMCU */ - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->dpmcu), "aspeed.dpmcu", + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->dpmcu), + "aspeed.dpmcu", sc->memmap[ASPEED_DEV_DPMCU], ASPEED_SOC_DPMCU_SIZE); diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c index 83cf3c14b6c3c..99a3de15b56cc 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -241,16 +241,16 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) return; } - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->timerctrl), + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->timerctrl), "aspeed.timerctrl", sc->memmap[ASPEED_DEV_TIMER1], 0x200); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->ipc[0]), + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&a->ipc[0]), "aspeed.ipc0", sc->memmap[ASPEED_DEV_IPC0], 0x1000); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->ipc[1]), + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&a->ipc[1]), "aspeed.ipc1", sc->memmap[ASPEED_DEV_IPC1], 0x1000); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->scuio), + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&a->scuio), "aspeed.scuio", sc->memmap[ASPEED_DEV_SCUIO], 0x1000); } diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index 86aa565608985..568d7555e26ed 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -241,16 +241,16 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) return; } - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->timerctrl), + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->timerctrl), "aspeed.timerctrl", sc->memmap[ASPEED_DEV_TIMER1], 0x200); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->ipc[0]), + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&a->ipc[0]), "aspeed.ipc0", sc->memmap[ASPEED_DEV_IPC0], 0x1000); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->ipc[1]), + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&a->ipc[1]), "aspeed.ipc1", sc->memmap[ASPEED_DEV_IPC1], 0x1000); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->scuio), + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&a->scuio), "aspeed.scuio", sc->memmap[ASPEED_DEV_SCUIO], 0x1000); } diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 8db67dc806ac6..9b645c6c55026 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -1014,23 +1014,23 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) return; } - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->dpmcu), + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->dpmcu), "aspeed.dpmcu", sc->memmap[ASPEED_DEV_DPMCU], AST2700_SOC_DPMCU_SIZE); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->ltpi), + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->ltpi), "aspeed.ltpi", sc->memmap[ASPEED_DEV_LTPI], AST2700_SOC_LTPI_SIZE); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem), + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->iomem), "aspeed.io", sc->memmap[ASPEED_DEV_IOMEM], AST2700_SOC_IO_SIZE); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem0), + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->iomem0), "aspeed.iomem0", sc->memmap[ASPEED_DEV_IOMEM0], AST2700_SOC_IOMEM_SIZE); - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem1), + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->iomem1), "aspeed.iomem1", sc->memmap[ASPEED_DEV_IOMEM1], AST2700_SOC_IOMEM_SIZE); diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index ca4e589dce85d..e7d0a9c2909a9 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -117,14 +117,14 @@ void aspeed_mmio_map(MemoryRegion *memory, SysBusDevice *dev, int n, memory_region_add_subregion(memory, addr, sysbus_mmio_get_region(dev, n)); } -void aspeed_mmio_map_unimplemented(AspeedSoCState *s, SysBusDevice *dev, +void aspeed_mmio_map_unimplemented(MemoryRegion *memory, SysBusDevice *dev, const char *name, hwaddr addr, uint64_t size) { qdev_prop_set_string(DEVICE(dev), "name", name); qdev_prop_set_uint64(DEVICE(dev), "size", size); sysbus_realize(dev, &error_abort); - memory_region_add_subregion_overlap(s->memory, addr, + memory_region_add_subregion_overlap(memory, addr, sysbus_mmio_get_region(dev, 0), -1000); } diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 606cf6bb61935..957362b88d06c 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -310,7 +310,7 @@ void aspeed_soc_uart_set_chr(SerialMM *uart, int dev, int uarts_base, bool aspeed_soc_dram_init(AspeedSoCState *s, Error **errp); void aspeed_mmio_map(MemoryRegion *memory, SysBusDevice *dev, int n, hwaddr addr); -void aspeed_mmio_map_unimplemented(AspeedSoCState *s, SysBusDevice *dev, +void aspeed_mmio_map_unimplemented(MemoryRegion *memory, SysBusDevice *dev, const char *name, hwaddr addr, uint64_t size); void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, From 15f26071bf424a564e177ab1ac746b85feda5286 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Oct 2025 13:43:19 +0800 Subject: [PATCH 1539/1794] hw/arm/aspeed: Remove AspeedSoCState dependency from aspeed_soc_uart_realize() API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor aspeed_soc_uart_realize() to take MemoryRegion *, SerialMM *, and MMIO base addr instead of AspeedSoCState *, decoupling the helper from SoC state and making it reusable per-UART. The helper now realizes a single UART instance and maps its MMIO. IRQ wiring and iteration over all UARTs are moved to callers. Update call sites in AST1030, AST2400, AST2600, AST27x0 SSP/TSP, and AST2700 to loop over UARTs, call the new helper, and connect IRQ via aspeed_soc_get_irq(). This simplifies the UART realize path and reduces cross-module coupling. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013054334.955331-8-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast10x0.c | 10 ++++++++-- hw/arm/aspeed_ast2400.c | 10 ++++++++-- hw/arm/aspeed_ast2600.c | 10 ++++++++-- hw/arm/aspeed_ast27x0-ssp.c | 10 ++++++++-- hw/arm/aspeed_ast27x0-tsp.c | 10 ++++++++-- hw/arm/aspeed_ast27x0.c | 10 ++++++++-- hw/arm/aspeed_soc_common.c | 28 ++++++++++------------------ include/hw/arm/aspeed_soc.h | 3 ++- 8 files changed, 60 insertions(+), 31 deletions(-) diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index e861b6dad6992..ff781379c10ea 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -192,6 +192,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); DeviceState *armv7m; Error *err = NULL; + int uart; int i; g_autofree char *sram_name = NULL; @@ -316,8 +317,13 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_4)); /* UART */ - if (!aspeed_soc_uart_realize(s, errp)) { - return; + for (i = 0, uart = sc->uarts_base; i < sc->uarts_num; i++, uart++) { + if (!aspeed_soc_uart_realize(s->memory, &s->uart[i], + sc->memmap[uart], errp)) { + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + aspeed_soc_get_irq(s, uart)); } /* Timer */ diff --git a/hw/arm/aspeed_ast2400.c b/hw/arm/aspeed_ast2400.c index e0604851a5586..8d4d6564c7ad4 100644 --- a/hw/arm/aspeed_ast2400.c +++ b/hw/arm/aspeed_ast2400.c @@ -251,6 +251,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) AspeedSoCState *s = ASPEED_SOC(dev); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); g_autofree char *sram_name = NULL; + int uart; /* Default boot region (SPI memory or ROMs) */ memory_region_init(&s->spi_boot_container, OBJECT(s), @@ -337,8 +338,13 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); /* UART */ - if (!aspeed_soc_uart_realize(s, errp)) { - return; + for (i = 0, uart = sc->uarts_base; i < sc->uarts_num; i++, uart++) { + if (!aspeed_soc_uart_realize(s->memory, &s->uart[i], + sc->memmap[uart], errp)) { + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + aspeed_soc_get_irq(s, uart)); } /* I2C */ diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index ed0985a16e089..f508bf53e71d1 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -362,6 +362,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); qemu_irq irq; g_autofree char *sram_name = NULL; + int uart; /* Default boot region (SPI memory or ROMs) */ memory_region_init(&s->spi_boot_container, OBJECT(s), @@ -488,8 +489,13 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); /* UART */ - if (!aspeed_soc_uart_realize(s, errp)) { - return; + for (i = 0, uart = sc->uarts_base; i < sc->uarts_num; i++, uart++) { + if (!aspeed_soc_uart_realize(s->memory, &s->uart[i], + sc->memmap[uart], errp)) { + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + aspeed_soc_get_irq(s, uart)); } /* I2C */ diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c index 99a3de15b56cc..7420ae04acb57 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -164,6 +164,7 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); DeviceState *armv7m; g_autofree char *sram_name = NULL; + int uart; int i; if (!clock_has_source(s->sysclk)) { @@ -237,8 +238,13 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) qdev_get_gpio_in(DEVICE(&a->intc[0].orgates[0]), i)); } /* UART */ - if (!aspeed_soc_uart_realize(s, errp)) { - return; + for (i = 0, uart = sc->uarts_base; i < sc->uarts_num; i++, uart++) { + if (!aspeed_soc_uart_realize(s->memory, &s->uart[i], + sc->memmap[uart], errp)) { + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + aspeed_soc_get_irq(s, uart)); } aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->timerctrl), diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index 568d7555e26ed..b764147a33139 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -164,6 +164,7 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); DeviceState *armv7m; g_autofree char *sram_name = NULL; + int uart; int i; if (!clock_has_source(s->sysclk)) { @@ -237,8 +238,13 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) qdev_get_gpio_in(DEVICE(&a->intc[0].orgates[0]), i)); } /* UART */ - if (!aspeed_soc_uart_realize(s, errp)) { - return; + for (i = 0, uart = sc->uarts_base; i < sc->uarts_num; i++, uart++) { + if (!aspeed_soc_uart_realize(s->memory, &s->uart[i], + sc->memmap[uart], errp)) { + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + aspeed_soc_get_irq(s, uart)); } aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->timerctrl), diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 9b645c6c55026..96882b8755488 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -687,6 +687,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) AspeedINTCClass *icio = ASPEED_INTC_GET_CLASS(&a->intc[1]); g_autofree char *name = NULL; qemu_irq irq; + int uart; /* Default boot region (SPI memory or ROMs) */ memory_region_init(&s->spi_boot_container, OBJECT(s), @@ -788,8 +789,13 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) sc->memmap[ASPEED_DEV_SCUIO]); /* UART */ - if (!aspeed_soc_uart_realize(s, errp)) { - return; + for (i = 0, uart = sc->uarts_base; i < sc->uarts_num; i++, uart++) { + if (!aspeed_soc_uart_realize(s->memory, &s->uart[i], + sc->memmap[uart], errp)) { + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + aspeed_soc_get_irq(s, uart)); } /* FMC, The number of CS is set at the board level */ diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index e7d0a9c2909a9..a785a50609e33 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -35,27 +35,19 @@ qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev) return ASPEED_SOC_GET_CLASS(s)->get_irq(s, dev); } -bool aspeed_soc_uart_realize(AspeedSoCState *s, Error **errp) +bool aspeed_soc_uart_realize(MemoryRegion *memory, SerialMM *smm, + const hwaddr addr, Error **errp) { - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - SerialMM *smm; - - for (int i = 0, uart = sc->uarts_base; i < sc->uarts_num; i++, uart++) { - smm = &s->uart[i]; - - /* Chardev property is set by the machine. */ - qdev_prop_set_uint8(DEVICE(smm), "regshift", 2); - qdev_prop_set_uint32(DEVICE(smm), "baudbase", 38400); - qdev_set_legacy_instance_id(DEVICE(smm), sc->memmap[uart], 2); - qdev_prop_set_uint8(DEVICE(smm), "endianness", DEVICE_LITTLE_ENDIAN); - if (!sysbus_realize(SYS_BUS_DEVICE(smm), errp)) { - return false; - } - - sysbus_connect_irq(SYS_BUS_DEVICE(smm), 0, aspeed_soc_get_irq(s, uart)); - aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(smm), 0, sc->memmap[uart]); + /* Chardev property is set by the machine. */ + qdev_prop_set_uint8(DEVICE(smm), "regshift", 2); + qdev_prop_set_uint32(DEVICE(smm), "baudbase", 38400); + qdev_set_legacy_instance_id(DEVICE(smm), addr, 2); + qdev_prop_set_uint8(DEVICE(smm), "endianness", DEVICE_LITTLE_ENDIAN); + if (!sysbus_realize(SYS_BUS_DEVICE(smm), errp)) { + return false; } + aspeed_mmio_map(memory, SYS_BUS_DEVICE(smm), 0, addr); return true; } diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 957362b88d06c..47341ea2fdbc3 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -304,7 +304,8 @@ enum { const char *aspeed_soc_cpu_type(const char * const *valid_cpu_types); qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev); -bool aspeed_soc_uart_realize(AspeedSoCState *s, Error **errp); +bool aspeed_soc_uart_realize(MemoryRegion *memory, SerialMM *smm, + const hwaddr addr, Error **errp); void aspeed_soc_uart_set_chr(SerialMM *uart, int dev, int uarts_base, int uarts_num, Chardev *chr); bool aspeed_soc_dram_init(AspeedSoCState *s, Error **errp); From 21b3898a6984d0bc08c5a8b3f498ddfe4ff95e44 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Oct 2025 13:43:20 +0800 Subject: [PATCH 1540/1794] hw/arm/aspeed: Remove the aspeed_soc_get_irq and class get_irq hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the the common aspeed_soc_get_irq. Call sites are updated to use the SoC-specific get_irq helpers directly (aspeed_soc_ast1030_get_irq(), _aspeed2400_get_irq(), _ast2600_get_irq(), _ast27x0ssp_get_irq(), _ast27x0tsp_get_irq(), and _ast2700_get_irq()) This makes the IRQ lookup explicit per-SoC and drops the exported API that depended on AspeedSoCState, reducing cross-module coupling in the common layer. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013054334.955331-9-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast10x0.c | 17 ++++++++--------- hw/arm/aspeed_ast2400.c | 31 +++++++++++++++---------------- hw/arm/aspeed_ast2600.c | 34 +++++++++++++++++----------------- hw/arm/aspeed_ast27x0-ssp.c | 3 +-- hw/arm/aspeed_ast27x0-tsp.c | 3 +-- hw/arm/aspeed_ast27x0.c | 27 +++++++++++++-------------- hw/arm/aspeed_soc_common.c | 5 ----- include/hw/arm/aspeed_soc.h | 2 -- 8 files changed, 55 insertions(+), 67 deletions(-) diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index ff781379c10ea..7f49c13391be0 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -284,7 +284,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->peci), 0, sc->memmap[ASPEED_DEV_PECI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->peci), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_PECI)); + aspeed_soc_ast1030_get_irq(s, ASPEED_DEV_PECI)); /* LPC */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { @@ -295,7 +295,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) /* Connect the LPC IRQ to the GIC. It is otherwise unused. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_LPC)); + aspeed_soc_ast1030_get_irq(s, ASPEED_DEV_LPC)); /* * On the AST1030 LPC subdevice IRQs are connected straight to the GIC. @@ -323,7 +323,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, - aspeed_soc_get_irq(s, uart)); + aspeed_soc_ast1030_get_irq(s, uart)); } /* Timer */ @@ -335,7 +335,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->timerctrl), 0, sc->memmap[ASPEED_DEV_TIMER1]); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { - qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); + qemu_irq irq = aspeed_soc_ast1030_get_irq(s, ASPEED_DEV_TIMER1 + i); sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); } @@ -346,7 +346,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); + aspeed_soc_ast1030_get_irq(s, ASPEED_DEV_ADC)); /* FMC, The number of CS is set at the board level */ object_property_set_link(OBJECT(&s->fmc), "dram", OBJECT(&s->sram), @@ -359,7 +359,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->fmc), 1, ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); + aspeed_soc_ast1030_get_irq(s, ASPEED_DEV_FMC)); /* SPI */ for (i = 0; i < sc->spis_num; i++) { @@ -390,7 +390,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->hace), 0, sc->memmap[ASPEED_DEV_HACE]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); + aspeed_soc_ast1030_get_irq(s, ASPEED_DEV_HACE)); /* Watch dog */ for (i = 0; i < sc->wdts_num; i++) { @@ -412,7 +412,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); + aspeed_soc_ast1030_get_irq(s, ASPEED_DEV_GPIO)); aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->pwm), "aspeed.pwm", @@ -463,7 +463,6 @@ static void aspeed_soc_ast1030_class_init(ObjectClass *klass, const void *data) sc->irqmap = aspeed_soc_ast1030_irqmap; sc->memmap = aspeed_soc_ast1030_memmap; sc->num_cpus = 1; - sc->get_irq = aspeed_soc_ast1030_get_irq; } static const TypeInfo aspeed_soc_ast10x0_types[] = { diff --git a/hw/arm/aspeed_ast2400.c b/hw/arm/aspeed_ast2400.c index 8d4d6564c7ad4..b1b826b7e0b13 100644 --- a/hw/arm/aspeed_ast2400.c +++ b/hw/arm/aspeed_ast2400.c @@ -313,7 +313,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_DEV_RTC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_RTC)); + aspeed_soc_ast2400_get_irq(s, ASPEED_DEV_RTC)); /* Timer */ object_property_set_link(OBJECT(&s->timerctrl), "scu", OBJECT(&s->scu), @@ -324,7 +324,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->timerctrl), 0, sc->memmap[ASPEED_DEV_TIMER1]); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { - qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); + qemu_irq irq = aspeed_soc_ast2400_get_irq(s, ASPEED_DEV_TIMER1 + i); sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); } @@ -335,7 +335,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); + aspeed_soc_ast2400_get_irq(s, ASPEED_DEV_ADC)); /* UART */ for (i = 0, uart = sc->uarts_base; i < sc->uarts_num; i++, uart++) { @@ -344,7 +344,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, - aspeed_soc_get_irq(s, uart)); + aspeed_soc_ast2400_get_irq(s, uart)); } /* I2C */ @@ -356,7 +356,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_I2C)); + aspeed_soc_ast2400_get_irq(s, ASPEED_DEV_I2C)); /* PECI */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->peci), errp)) { @@ -365,7 +365,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->peci), 0, sc->memmap[ASPEED_DEV_PECI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->peci), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_PECI)); + aspeed_soc_ast2400_get_irq(s, ASPEED_DEV_PECI)); /* FMC, The number of CS is set at the board level */ object_property_set_link(OBJECT(&s->fmc), "dram", OBJECT(s->dram_mr), @@ -378,7 +378,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->fmc), 1, ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); + aspeed_soc_ast2400_get_irq(s, ASPEED_DEV_FMC)); /* Set up an alias on the FMC CE0 region (boot default) */ MemoryRegion *fmc0_mmio = &s->fmc.flashes[0].mmio; @@ -405,7 +405,8 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->ehci[i]), 0, sc->memmap[ASPEED_DEV_EHCI1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_EHCI1 + i)); + aspeed_soc_ast2400_get_irq(s, + ASPEED_DEV_EHCI1 + i)); } /* SDMC - SDRAM Memory Controller */ @@ -443,7 +444,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, sc->memmap[ASPEED_DEV_ETH1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_ETH1 + i)); + aspeed_soc_ast2400_get_irq(s, ASPEED_DEV_ETH1 + i)); } /* XDMA */ @@ -453,7 +454,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->xdma), 0, sc->memmap[ASPEED_DEV_XDMA]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->xdma), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_XDMA)); + aspeed_soc_ast2400_get_irq(s, ASPEED_DEV_XDMA)); /* GPIO */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { @@ -462,7 +463,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); + aspeed_soc_ast2400_get_irq(s, ASPEED_DEV_GPIO)); /* SDHCI */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp)) { @@ -471,7 +472,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->sdhci), 0, sc->memmap[ASPEED_DEV_SDHCI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_SDHCI)); + aspeed_soc_ast2400_get_irq(s, ASPEED_DEV_SDHCI)); /* LPC */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { @@ -482,7 +483,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) /* Connect the LPC IRQ to the VIC */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_LPC)); + aspeed_soc_ast2400_get_irq(s, ASPEED_DEV_LPC)); /* * On the AST2400 and AST2500 the one LPC IRQ is shared between all of the @@ -514,7 +515,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->hace), 0, sc->memmap[ASPEED_DEV_HACE]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); + aspeed_soc_ast2400_get_irq(s, ASPEED_DEV_HACE)); } static void aspeed_soc_ast2400_class_init(ObjectClass *oc, const void *data) @@ -542,7 +543,6 @@ static void aspeed_soc_ast2400_class_init(ObjectClass *oc, const void *data) sc->irqmap = aspeed_soc_ast2400_irqmap; sc->memmap = aspeed_soc_ast2400_memmap; sc->num_cpus = 1; - sc->get_irq = aspeed_soc_ast2400_get_irq; } static void aspeed_soc_ast2500_class_init(ObjectClass *oc, const void *data) @@ -570,7 +570,6 @@ static void aspeed_soc_ast2500_class_init(ObjectClass *oc, const void *data) sc->irqmap = aspeed_soc_ast2500_irqmap; sc->memmap = aspeed_soc_ast2500_memmap; sc->num_cpus = 1; - sc->get_irq = aspeed_soc_ast2400_get_irq; } static const TypeInfo aspeed_soc_ast2400_types[] = { diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index f508bf53e71d1..498d1ecc078b7 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -464,7 +464,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_DEV_RTC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_RTC)); + aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_RTC)); /* Timer */ object_property_set_link(OBJECT(&s->timerctrl), "scu", OBJECT(&s->scu), @@ -475,7 +475,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->timerctrl), 0, sc->memmap[ASPEED_DEV_TIMER1]); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { - irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); + irq = aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_TIMER1 + i); sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); } @@ -486,7 +486,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); + aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_ADC)); /* UART */ for (i = 0, uart = sc->uarts_base; i < sc->uarts_num; i++, uart++) { @@ -495,7 +495,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, - aspeed_soc_get_irq(s, uart)); + aspeed_soc_ast2600_get_irq(s, uart)); } /* I2C */ @@ -520,7 +520,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->peci), 0, sc->memmap[ASPEED_DEV_PECI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->peci), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_PECI)); + aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_PECI)); /* PCIe Root Complex (RC) */ if (!aspeed_soc_ast2600_pcie_realize(dev, errp)) { @@ -538,7 +538,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->fmc), 1, ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); + aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_FMC)); /* Set up an alias on the FMC CE0 region (boot default) */ MemoryRegion *fmc0_mmio = &s->fmc.flashes[0].mmio; @@ -567,7 +567,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->ehci[i]), 0, sc->memmap[ASPEED_DEV_EHCI1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_EHCI1 + i)); + aspeed_soc_ast2600_get_irq(s, + ASPEED_DEV_EHCI1 + i)); } /* SDMC - SDRAM Memory Controller */ @@ -605,7 +606,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, sc->memmap[ASPEED_DEV_ETH1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_ETH1 + i)); + aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_ETH1 + i)); object_property_set_link(OBJECT(&s->mii[i]), "nic", OBJECT(&s->ftgmac100[i]), &error_abort); @@ -624,7 +625,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->xdma), 0, sc->memmap[ASPEED_DEV_XDMA]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->xdma), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_XDMA)); + aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_XDMA)); /* GPIO */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { @@ -633,7 +634,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); + aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_GPIO)); if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio_1_8v), errp)) { return; @@ -641,7 +642,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->gpio_1_8v), 0, sc->memmap[ASPEED_DEV_GPIO_1_8V]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio_1_8v), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_GPIO_1_8V)); + aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_GPIO_1_8V)); /* SDHCI */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp)) { @@ -650,7 +651,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->sdhci), 0, sc->memmap[ASPEED_DEV_SDHCI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_SDHCI)); + aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_SDHCI)); /* eMMC */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->emmc), errp)) { @@ -659,7 +660,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->emmc), 0, sc->memmap[ASPEED_DEV_EMMC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_EMMC)); + aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_EMMC)); /* LPC */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { @@ -670,7 +671,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) /* Connect the LPC IRQ to the GIC. It is otherwise unused. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_LPC)); + aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_LPC)); /* * On the AST2600 LPC subdevice IRQs are connected straight to the GIC. @@ -705,7 +706,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->hace), 0, sc->memmap[ASPEED_DEV_HACE]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); + aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_HACE)); /* I3C */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->i3c), errp)) { @@ -735,7 +736,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->fsi[i]), 0, sc->memmap[ASPEED_DEV_FSI1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fsi[i]), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_FSI1 + i)); + aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_FSI1 + i)); } } @@ -771,7 +772,6 @@ static void aspeed_soc_ast2600_class_init(ObjectClass *oc, const void *data) sc->irqmap = aspeed_soc_ast2600_irqmap; sc->memmap = aspeed_soc_ast2600_memmap; sc->num_cpus = 2; - sc->get_irq = aspeed_soc_ast2600_get_irq; sc->boot_from_emmc = aspeed_soc_ast2600_boot_from_emmc; } diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c index 7420ae04acb57..f90d14437291f 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -244,7 +244,7 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, - aspeed_soc_get_irq(s, uart)); + aspeed_soc_ast27x0ssp_get_irq(s, uart)); } aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->timerctrl), @@ -286,7 +286,6 @@ static void aspeed_soc_ast27x0ssp_class_init(ObjectClass *klass, const void *dat sc->irqmap = aspeed_soc_ast27x0ssp_irqmap; sc->memmap = aspeed_soc_ast27x0ssp_memmap; sc->num_cpus = 1; - sc->get_irq = aspeed_soc_ast27x0ssp_get_irq; } static const TypeInfo aspeed_soc_ast27x0ssp_types[] = { diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index b764147a33139..8643f8268347b 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -244,7 +244,7 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, - aspeed_soc_get_irq(s, uart)); + aspeed_soc_ast27x0tsp_get_irq(s, uart)); } aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->timerctrl), @@ -286,7 +286,6 @@ static void aspeed_soc_ast27x0tsp_class_init(ObjectClass *klass, const void *dat sc->irqmap = aspeed_soc_ast27x0tsp_irqmap; sc->memmap = aspeed_soc_ast27x0tsp_memmap; sc->num_cpus = 1; - sc->get_irq = aspeed_soc_ast27x0tsp_get_irq; } static const TypeInfo aspeed_soc_ast27x0tsp_types[] = { diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 96882b8755488..c484bcd4e22fb 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -660,7 +660,7 @@ static bool aspeed_soc_ast2700_pcie_realize(DeviceState *dev, Error **errp) } aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->pcie[i]), 0, sc->memmap[ASPEED_DEV_PCIE0 + i]); - irq = aspeed_soc_get_irq(s, ASPEED_DEV_PCIE0 + i); + irq = aspeed_soc_ast2700_get_irq(s, ASPEED_DEV_PCIE0 + i); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie[i].rc), 0, irq); mmio_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->pcie[i].rc), 1); @@ -795,7 +795,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, - aspeed_soc_get_irq(s, uart)); + aspeed_soc_ast2700_get_irq(s, uart)); } /* FMC, The number of CS is set at the board level */ @@ -812,7 +812,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->fmc), 1, ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); + aspeed_soc_ast2700_get_irq(s, ASPEED_DEV_FMC)); /* Set up an alias on the FMC CE0 region (boot default) */ MemoryRegion *fmc0_mmio = &s->fmc.flashes[0].mmio; @@ -841,7 +841,8 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->ehci[i]), 0, sc->memmap[ASPEED_DEV_EHCI1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_EHCI1 + i)); + aspeed_soc_ast2700_get_irq(s, + ASPEED_DEV_EHCI1 + i)); } /* @@ -876,7 +877,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, sc->memmap[ASPEED_DEV_ETH1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_ETH1 + i)); + aspeed_soc_ast2700_get_irq(s, ASPEED_DEV_ETH1 + i)); object_property_set_link(OBJECT(&s->mii[i]), "nic", OBJECT(&s->ftgmac100[i]), &error_abort); @@ -921,7 +922,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); + aspeed_soc_ast2700_get_irq(s, ASPEED_DEV_ADC)); /* I2C */ object_property_set_link(OBJECT(&s->i2c), "dram", OBJECT(s->dram_mr), @@ -962,7 +963,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); + aspeed_soc_ast2700_get_irq(s, ASPEED_DEV_GPIO)); /* RTC */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->rtc), errp)) { @@ -971,7 +972,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_DEV_RTC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_RTC)); + aspeed_soc_ast2700_get_irq(s, ASPEED_DEV_RTC)); /* SDHCI */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp)) { @@ -980,7 +981,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->sdhci), 0, sc->memmap[ASPEED_DEV_SDHCI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_SDHCI)); + aspeed_soc_ast2700_get_irq(s, ASPEED_DEV_SDHCI)); /* eMMC */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->emmc), errp)) { @@ -989,7 +990,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->emmc), 0, sc->memmap[ASPEED_DEV_EMMC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_EMMC)); + aspeed_soc_ast2700_get_irq(s, ASPEED_DEV_EMMC)); /* Timer */ object_property_set_link(OBJECT(&s->timerctrl), "scu", OBJECT(&s->scu), @@ -1000,7 +1001,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->timerctrl), 0, sc->memmap[ASPEED_DEV_TIMER1]); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { - irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); + irq = aspeed_soc_ast2700_get_irq(s, ASPEED_DEV_TIMER1 + i); sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); } @@ -1013,7 +1014,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->hace), 0, sc->memmap[ASPEED_DEV_HACE]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, - aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); + aspeed_soc_ast2700_get_irq(s, ASPEED_DEV_HACE)); /* PCIe Root Complex (RC) */ if (!aspeed_soc_ast2700_pcie_realize(dev, errp)) { @@ -1068,7 +1069,6 @@ static void aspeed_soc_ast2700a0_class_init(ObjectClass *oc, const void *data) sc->uarts_base = ASPEED_DEV_UART0; sc->irqmap = aspeed_soc_ast2700a0_irqmap; sc->memmap = aspeed_soc_ast2700_memmap; - sc->get_irq = aspeed_soc_ast2700_get_irq; } static void aspeed_soc_ast2700a1_class_init(ObjectClass *oc, const void *data) @@ -1097,7 +1097,6 @@ static void aspeed_soc_ast2700a1_class_init(ObjectClass *oc, const void *data) sc->uarts_base = ASPEED_DEV_UART0; sc->irqmap = aspeed_soc_ast2700a1_irqmap; sc->memmap = aspeed_soc_ast2700_memmap; - sc->get_irq = aspeed_soc_ast2700_get_irq; } static const TypeInfo aspeed_soc_ast27x0_types[] = { diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index a785a50609e33..78b6ae18f87ec 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -30,11 +30,6 @@ const char *aspeed_soc_cpu_type(const char * const *valid_cpu_types) return valid_cpu_types[0]; } -qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev) -{ - return ASPEED_SOC_GET_CLASS(s)->get_irq(s, dev); -} - bool aspeed_soc_uart_realize(MemoryRegion *memory, SerialMM *smm, const hwaddr addr, Error **errp) { diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 47341ea2fdbc3..0e07c079f0cf1 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -198,7 +198,6 @@ struct AspeedSoCClass { const int *irqmap; const hwaddr *memmap; uint32_t num_cpus; - qemu_irq (*get_irq)(AspeedSoCState *s, int dev); bool (*boot_from_emmc)(AspeedSoCState *s); }; @@ -303,7 +302,6 @@ enum { }; const char *aspeed_soc_cpu_type(const char * const *valid_cpu_types); -qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev); bool aspeed_soc_uart_realize(MemoryRegion *memory, SerialMM *smm, const hwaddr addr, Error **errp); void aspeed_soc_uart_set_chr(SerialMM *uart, int dev, int uarts_base, From d183ef19a80867b6a1906417eda00f4ef17ffb77 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Oct 2025 13:43:21 +0800 Subject: [PATCH 1541/1794] hw/arm/aspeed: Introduce AspeedCoprocessor class and base implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new AspeedCoprocessor class that defines the foundational structure for ASPEED coprocessor models. This class encapsulates a base DeviceState with links to system memory, clock, and peripheral components such as SCU, SCUIO, Timer Controller, and UARTs. Introduce the corresponding implementation file aspeed_coprocessor_common.c, which provides the aspeed_coprocessor_realize() method, property registration, and QOM type registration. The class is marked as abstract and intended to serve as a common base for specific coprocessor variants (e.g. SSP/TSP subsystems). This establishes a reusable and extensible framework for modeling ASPEED coprocessor devices. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013054334.955331-10-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_coprocessor_common.c | 49 +++++++++++++++++++++++++++++ hw/arm/meson.build | 3 +- include/hw/arm/aspeed_coprocessor.h | 44 ++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 hw/arm/aspeed_coprocessor_common.c create mode 100644 include/hw/arm/aspeed_coprocessor.h diff --git a/hw/arm/aspeed_coprocessor_common.c b/hw/arm/aspeed_coprocessor_common.c new file mode 100644 index 0000000000000..8a94b44f07f23 --- /dev/null +++ b/hw/arm/aspeed_coprocessor_common.c @@ -0,0 +1,49 @@ +/* + * ASPEED Coprocessor + * + * Copyright (C) 2025 ASPEED Technology Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "system/memory.h" +#include "hw/qdev-properties.h" +#include "hw/arm/aspeed_coprocessor.h" + +static void aspeed_coprocessor_realize(DeviceState *dev, Error **errp) +{ + AspeedCoprocessorState *s = ASPEED_COPROCESSOR(dev); + + if (!s->memory) { + error_setg(errp, "'memory' link is not set"); + return; + } +} + +static const Property aspeed_coprocessor_properties[] = { + DEFINE_PROP_LINK("memory", AspeedCoprocessorState, memory, + TYPE_MEMORY_REGION, MemoryRegion *), +}; + +static void aspeed_coprocessor_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = aspeed_coprocessor_realize; + device_class_set_props(dc, aspeed_coprocessor_properties); +} + +static const TypeInfo aspeed_coprocessor_types[] = { + { + .name = TYPE_ASPEED_COPROCESSOR, + .parent = TYPE_DEVICE, + .instance_size = sizeof(AspeedCoprocessorState), + .class_size = sizeof(AspeedCoprocessorClass), + .class_init = aspeed_coprocessor_class_init, + .abstract = true, + }, +}; + +DEFINE_TYPES(aspeed_coprocessor_types) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index dc68391305fe1..56bdb88b11758 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -52,7 +52,8 @@ arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'fby35.c')) arm_common_ss.add(when: ['CONFIG_ASPEED_SOC', 'TARGET_AARCH64'], if_true: files( 'aspeed_ast27x0.c', - 'aspeed_ast27x0-fc.c',)) + 'aspeed_ast27x0-fc.c', + 'aspeed_coprocessor_common.c')) arm_common_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2.c')) arm_common_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2-tz.c')) arm_common_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-soc.c')) diff --git a/include/hw/arm/aspeed_coprocessor.h b/include/hw/arm/aspeed_coprocessor.h new file mode 100644 index 0000000000000..793c7b1f8be96 --- /dev/null +++ b/include/hw/arm/aspeed_coprocessor.h @@ -0,0 +1,44 @@ +/* + * ASPEED Coprocessor + * + * Copyright (C) 2025 ASPEED Technology Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ASPEED_COPROCESSOR_H +#define ASPEED_COPROCESSOR_H + +#include "qom/object.h" +#include "hw/arm/aspeed_soc.h" + +struct AspeedCoprocessorState { + DeviceState parent; + + MemoryRegion *memory; + MemoryRegion sram; + Clock *sysclk; + + AspeedSCUState scu; + AspeedSCUState scuio; + AspeedTimerCtrlState timerctrl; + SerialMM uart[ASPEED_UARTS_NUM]; +}; + +#define TYPE_ASPEED_COPROCESSOR "aspeed-coprocessor" +OBJECT_DECLARE_TYPE(AspeedCoprocessorState, AspeedCoprocessorClass, + ASPEED_COPROCESSOR) + +struct AspeedCoprocessorClass { + DeviceClass parent_class; + + /** valid_cpu_types: NULL terminated array of a single CPU type. */ + const char * const *valid_cpu_types; + uint32_t silicon_rev; + const hwaddr *memmap; + const int *irqmap; + int uarts_base; + int uarts_num; +}; + +#endif /* ASPEED_COPROCESSOR_H */ From 860204de8a49bc687b11b98fff460a984d9321ce Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Oct 2025 13:43:22 +0800 Subject: [PATCH 1542/1794] hw/arm/aspeed_ast27x0-ssp: Make AST27x0 SSP inherit from AspeedCoprocessor instead of AspeedSoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the AST27x0 SSP implementation to derive from the newly introduced AspeedCoprocessor base class rather than AspeedSoC. The AspeedSoC class contains many SoC-level fields and behaviors that are not applicable to coprocessor subsystems like SSP, leading to unnecessary coupling and code size. This change moves the Aspeed27x0SSPSoCState structure definition into aspeed_coprocessor.h and updates related references in aspeed_ast27x0-ssp.c and aspeed_ast27x0-fc.c to use AspeedCoprocessorState and AspeedCoprocessorClass. Key updates include: - Replace inheritance from AspeedSoC -> AspeedCoprocessor. - Replace type casts and class access macros (ASPEED_SOC_*) with ASPEED_COPROCESSOR_*. This refactor improves modularity, reduces memory footprint, and prepares for future coprocessor variants to share a lighter-weight common base. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013054334.955331-11-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 10 +++++----- hw/arm/aspeed_ast27x0-ssp.c | 30 +++++++++++++---------------- hw/arm/meson.build | 2 +- include/hw/arm/aspeed_coprocessor.h | 12 ++++++++++++ include/hw/arm/aspeed_soc.h | 12 ------------ 5 files changed, 31 insertions(+), 35 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index e598f57ca228b..4315e8da98d20 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -21,7 +21,7 @@ #include "hw/loader.h" #include "hw/arm/boot.h" #include "hw/block/flash.h" - +#include "hw/arm/aspeed_coprocessor.h" #define TYPE_AST2700A1FC MACHINE_TYPE_NAME("ast2700fc") OBJECT_DECLARE_SIMPLE_TYPE(Ast2700FCState, AST2700A1FC); @@ -115,8 +115,8 @@ static bool ast2700fc_ca35_init(MachineState *machine, Error **errp) static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) { - AspeedSoCState *soc; - AspeedSoCClass *sc; + AspeedCoprocessorState *soc; + AspeedCoprocessorClass *sc; Ast2700FCState *s = AST2700A1FC(machine); s->ssp_sysclk = clock_new(OBJECT(s), "SSP_SYSCLK"); clock_set_hz(s->ssp_sysclk, 200000000ULL); @@ -129,8 +129,8 @@ static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) object_property_set_link(OBJECT(&s->ssp), "memory", OBJECT(&s->ssp_memory), &error_abort); - soc = ASPEED_SOC(&s->ssp); - sc = ASPEED_SOC_GET_CLASS(soc); + soc = ASPEED_COPROCESSOR(&s->ssp); + sc = ASPEED_COPROCESSOR_GET_CLASS(soc); aspeed_soc_uart_set_chr(soc->uart, ASPEED_DEV_UART4, sc->uarts_base, sc->uarts_num, serial_hd(1)); if (!qdev_realize(DEVICE(&s->ssp), NULL, errp)) { diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c index f90d14437291f..1ebf06299ebe7 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -14,6 +14,7 @@ #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" #include "hw/arm/aspeed_soc.h" +#include "hw/arm/aspeed_coprocessor.h" #define AST2700_SSP_RAM_SIZE (32 * MiB) @@ -104,10 +105,11 @@ static struct nvic_intc_irq_info ast2700_ssp_intcmap[] = { {136, 0, 9, NULL}, }; -static qemu_irq aspeed_soc_ast27x0ssp_get_irq(AspeedSoCState *s, int dev) +static qemu_irq aspeed_soc_ast27x0ssp_get_irq(AspeedCoprocessorState *s, + int dev) { Aspeed27x0SSPSoCState *a = ASPEED27X0SSP_SOC(s); - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); int or_idx; int idx; @@ -129,8 +131,8 @@ static qemu_irq aspeed_soc_ast27x0ssp_get_irq(AspeedSoCState *s, int dev) static void aspeed_soc_ast27x0ssp_init(Object *obj) { Aspeed27x0SSPSoCState *a = ASPEED27X0SSP_SOC(obj); - AspeedSoCState *s = ASPEED_SOC(obj); - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + AspeedCoprocessorState *s = ASPEED_COPROCESSOR(obj); + AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); int i; object_initialize_child(obj, "armv7m", &a->armv7m, TYPE_ARMV7M); @@ -160,8 +162,8 @@ static void aspeed_soc_ast27x0ssp_init(Object *obj) static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) { Aspeed27x0SSPSoCState *a = ASPEED27X0SSP_SOC(dev_soc); - AspeedSoCState *s = ASPEED_SOC(dev_soc); - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + AspeedCoprocessorState *s = ASPEED_COPROCESSOR(dev_soc); + AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); DeviceState *armv7m; g_autofree char *sram_name = NULL; int uart; @@ -185,8 +187,8 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) sram_name = g_strdup_printf("aspeed.dram.%d", CPU(a->armv7m.cpu)->cpu_index); - if (!memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size, - errp)) { + if (!memory_region_init_ram(&s->sram, OBJECT(s), sram_name, + AST2700_SSP_RAM_SIZE, errp)) { return; } memory_region_add_subregion(s->memory, @@ -268,30 +270,24 @@ static void aspeed_soc_ast27x0ssp_class_init(ObjectClass *klass, const void *dat NULL }; DeviceClass *dc = DEVICE_CLASS(klass); - AspeedSoCClass *sc = ASPEED_SOC_CLASS(dc); + AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_CLASS(dc); - /* Reason: The Aspeed SoC can only be instantiated from a board */ + /* Reason: The Aspeed Coprocessor can only be instantiated from a board */ dc->user_creatable = false; dc->realize = aspeed_soc_ast27x0ssp_realize; sc->valid_cpu_types = valid_cpu_types; sc->silicon_rev = AST2700_A1_SILICON_REV; - sc->sram_size = AST2700_SSP_RAM_SIZE; - sc->spis_num = 0; - sc->ehcis_num = 0; - sc->wdts_num = 0; - sc->macs_num = 0; sc->uarts_num = 13; sc->uarts_base = ASPEED_DEV_UART0; sc->irqmap = aspeed_soc_ast27x0ssp_irqmap; sc->memmap = aspeed_soc_ast27x0ssp_memmap; - sc->num_cpus = 1; } static const TypeInfo aspeed_soc_ast27x0ssp_types[] = { { .name = TYPE_ASPEED27X0SSP_SOC, - .parent = TYPE_ASPEED_SOC, + .parent = TYPE_ASPEED_COPROCESSOR, .instance_size = sizeof(Aspeed27x0SSPSoCState), .instance_init = aspeed_soc_ast27x0ssp_init, .class_init = aspeed_soc_ast27x0ssp_class_init, diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 56bdb88b11758..b9e02ace7f214 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -45,7 +45,6 @@ arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_soc_common.c', 'aspeed_ast2400.c', 'aspeed_ast2600.c', - 'aspeed_ast27x0-ssp.c', 'aspeed_ast27x0-tsp.c', 'aspeed_ast10x0.c', 'aspeed_eeprom.c', @@ -53,6 +52,7 @@ arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( arm_common_ss.add(when: ['CONFIG_ASPEED_SOC', 'TARGET_AARCH64'], if_true: files( 'aspeed_ast27x0.c', 'aspeed_ast27x0-fc.c', + 'aspeed_ast27x0-ssp.c', 'aspeed_coprocessor_common.c')) arm_common_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2.c')) arm_common_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2-tz.c')) diff --git a/include/hw/arm/aspeed_coprocessor.h b/include/hw/arm/aspeed_coprocessor.h index 793c7b1f8be96..901b8d8e249d0 100644 --- a/include/hw/arm/aspeed_coprocessor.h +++ b/include/hw/arm/aspeed_coprocessor.h @@ -41,4 +41,16 @@ struct AspeedCoprocessorClass { int uarts_num; }; +struct Aspeed27x0SSPSoCState { + AspeedCoprocessorState parent; + AspeedINTCState intc[2]; + UnimplementedDeviceState ipc[2]; + UnimplementedDeviceState scuio; + + ARMv7MState armv7m; +}; + +#define TYPE_ASPEED27X0SSP_SOC "aspeed27x0ssp-soc" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0SSPSoCState, ASPEED27X0SSP_SOC) + #endif /* ASPEED_COPROCESSOR_H */ diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 0e07c079f0cf1..a34ab986a9da6 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -153,18 +153,6 @@ struct Aspeed10x0SoCState { ARMv7MState armv7m; }; -struct Aspeed27x0SSPSoCState { - AspeedSoCState parent; - AspeedINTCState intc[2]; - UnimplementedDeviceState ipc[2]; - UnimplementedDeviceState scuio; - - ARMv7MState armv7m; -}; - -#define TYPE_ASPEED27X0SSP_SOC "aspeed27x0ssp-soc" -OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0SSPSoCState, ASPEED27X0SSP_SOC) - struct Aspeed27x0TSPSoCState { AspeedSoCState parent; AspeedINTCState intc[2]; From bbdd4167fa7ab8b27527e00214c3b903cf30a737 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Oct 2025 13:43:23 +0800 Subject: [PATCH 1543/1794] hw/arm/aspeed_ast27x0-tsp: Make AST27x0 TSP inherit from AspeedCoprocessor instead of AspeedSoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the AST27x0 TSP implementation to derive from the newly introduced AspeedCoprocessor base class rather than AspeedSoC. The AspeedSoC class includes SoC-level infrastructure and peripheral definitions that are not applicable to lightweight coprocessor subsystems such as TSP, resulting in unnecessary coupling and complexity. This change moves the Aspeed27x0TSPSoCState structure definition into aspeed_coprocessor.h and updates all related references in aspeed_ast27x0-tsp.c and aspeed_ast27x0-fc.c to use AspeedCoprocessorState and AspeedCoprocessorClass. Key updates include: - Replace inheritance from AspeedSoC -> AspeedCoprocessor. - Update type casts and macros from ASPEED_SOC_* to ASPEED_COPROCESSOR_* This refactor improves modularity, reduces memory footprint, and prepares for future coprocessor variants to share a lighter-weight common base. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013054334.955331-12-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 8 ++++---- hw/arm/aspeed_ast27x0-tsp.c | 30 +++++++++++++---------------- hw/arm/meson.build | 2 +- include/hw/arm/aspeed_coprocessor.h | 12 ++++++++++++ include/hw/arm/aspeed_soc.h | 12 ------------ 5 files changed, 30 insertions(+), 34 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index 4315e8da98d20..b34cd54e4e7f5 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -142,8 +142,8 @@ static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) { - AspeedSoCState *soc; - AspeedSoCClass *sc; + AspeedCoprocessorState *soc; + AspeedCoprocessorClass *sc; Ast2700FCState *s = AST2700A1FC(machine); s->tsp_sysclk = clock_new(OBJECT(s), "TSP_SYSCLK"); clock_set_hz(s->tsp_sysclk, 200000000ULL); @@ -156,8 +156,8 @@ static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) object_property_set_link(OBJECT(&s->tsp), "memory", OBJECT(&s->tsp_memory), &error_abort); - soc = ASPEED_SOC(&s->tsp); - sc = ASPEED_SOC_GET_CLASS(soc); + soc = ASPEED_COPROCESSOR(&s->tsp); + sc = ASPEED_COPROCESSOR_GET_CLASS(soc); aspeed_soc_uart_set_chr(soc->uart, ASPEED_DEV_UART7, sc->uarts_base, sc->uarts_num, serial_hd(2)); if (!qdev_realize(DEVICE(&s->tsp), NULL, errp)) { diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index 8643f8268347b..b77c5291a6d5b 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -14,6 +14,7 @@ #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" #include "hw/arm/aspeed_soc.h" +#include "hw/arm/aspeed_coprocessor.h" #define AST2700_TSP_RAM_SIZE (32 * MiB) @@ -104,10 +105,11 @@ static struct nvic_intc_irq_info ast2700_tsp_intcmap[] = { {136, 0, 9, NULL}, }; -static qemu_irq aspeed_soc_ast27x0tsp_get_irq(AspeedSoCState *s, int dev) +static qemu_irq aspeed_soc_ast27x0tsp_get_irq(AspeedCoprocessorState *s, + int dev) { Aspeed27x0TSPSoCState *a = ASPEED27X0TSP_SOC(s); - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); int or_idx; int idx; @@ -129,8 +131,8 @@ static qemu_irq aspeed_soc_ast27x0tsp_get_irq(AspeedSoCState *s, int dev) static void aspeed_soc_ast27x0tsp_init(Object *obj) { Aspeed27x0TSPSoCState *a = ASPEED27X0TSP_SOC(obj); - AspeedSoCState *s = ASPEED_SOC(obj); - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + AspeedCoprocessorState *s = ASPEED_COPROCESSOR(obj); + AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); int i; object_initialize_child(obj, "armv7m", &a->armv7m, TYPE_ARMV7M); @@ -160,8 +162,8 @@ static void aspeed_soc_ast27x0tsp_init(Object *obj) static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) { Aspeed27x0TSPSoCState *a = ASPEED27X0TSP_SOC(dev_soc); - AspeedSoCState *s = ASPEED_SOC(dev_soc); - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + AspeedCoprocessorState *s = ASPEED_COPROCESSOR(dev_soc); + AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); DeviceState *armv7m; g_autofree char *sram_name = NULL; int uart; @@ -185,8 +187,8 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) sram_name = g_strdup_printf("aspeed.dram.%d", CPU(a->armv7m.cpu)->cpu_index); - if (!memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size, - errp)) { + if (!memory_region_init_ram(&s->sram, OBJECT(s), sram_name, + AST2700_TSP_RAM_SIZE, errp)) { return; } memory_region_add_subregion(s->memory, @@ -268,30 +270,24 @@ static void aspeed_soc_ast27x0tsp_class_init(ObjectClass *klass, const void *dat NULL }; DeviceClass *dc = DEVICE_CLASS(klass); - AspeedSoCClass *sc = ASPEED_SOC_CLASS(dc); + AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_CLASS(dc); - /* Reason: The Aspeed SoC can only be instantiated from a board */ + /* Reason: The Aspeed Coprocessor can only be instantiated from a board */ dc->user_creatable = false; dc->realize = aspeed_soc_ast27x0tsp_realize; sc->valid_cpu_types = valid_cpu_types; sc->silicon_rev = AST2700_A1_SILICON_REV; - sc->sram_size = AST2700_TSP_RAM_SIZE; - sc->spis_num = 0; - sc->ehcis_num = 0; - sc->wdts_num = 0; - sc->macs_num = 0; sc->uarts_num = 13; sc->uarts_base = ASPEED_DEV_UART0; sc->irqmap = aspeed_soc_ast27x0tsp_irqmap; sc->memmap = aspeed_soc_ast27x0tsp_memmap; - sc->num_cpus = 1; } static const TypeInfo aspeed_soc_ast27x0tsp_types[] = { { .name = TYPE_ASPEED27X0TSP_SOC, - .parent = TYPE_ASPEED_SOC, + .parent = TYPE_ASPEED_COPROCESSOR, .instance_size = sizeof(Aspeed27x0TSPSoCState), .instance_init = aspeed_soc_ast27x0tsp_init, .class_init = aspeed_soc_ast27x0tsp_class_init, diff --git a/hw/arm/meson.build b/hw/arm/meson.build index b9e02ace7f214..b88b5b06d7ed0 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -45,7 +45,6 @@ arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_soc_common.c', 'aspeed_ast2400.c', 'aspeed_ast2600.c', - 'aspeed_ast27x0-tsp.c', 'aspeed_ast10x0.c', 'aspeed_eeprom.c', 'fby35.c')) @@ -53,6 +52,7 @@ arm_common_ss.add(when: ['CONFIG_ASPEED_SOC', 'TARGET_AARCH64'], if_true: files( 'aspeed_ast27x0.c', 'aspeed_ast27x0-fc.c', 'aspeed_ast27x0-ssp.c', + 'aspeed_ast27x0-tsp.c', 'aspeed_coprocessor_common.c')) arm_common_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2.c')) arm_common_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2-tz.c')) diff --git a/include/hw/arm/aspeed_coprocessor.h b/include/hw/arm/aspeed_coprocessor.h index 901b8d8e249d0..f09c2ed267b2e 100644 --- a/include/hw/arm/aspeed_coprocessor.h +++ b/include/hw/arm/aspeed_coprocessor.h @@ -53,4 +53,16 @@ struct Aspeed27x0SSPSoCState { #define TYPE_ASPEED27X0SSP_SOC "aspeed27x0ssp-soc" OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0SSPSoCState, ASPEED27X0SSP_SOC) +struct Aspeed27x0TSPSoCState { + AspeedCoprocessorState parent; + AspeedINTCState intc[2]; + UnimplementedDeviceState ipc[2]; + UnimplementedDeviceState scuio; + + ARMv7MState armv7m; +}; + +#define TYPE_ASPEED27X0TSP_SOC "aspeed27x0tsp-soc" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0TSPSoCState, ASPEED27X0TSP_SOC) + #endif /* ASPEED_COPROCESSOR_H */ diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index a34ab986a9da6..4b8e599f1a53b 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -153,18 +153,6 @@ struct Aspeed10x0SoCState { ARMv7MState armv7m; }; -struct Aspeed27x0TSPSoCState { - AspeedSoCState parent; - AspeedINTCState intc[2]; - UnimplementedDeviceState ipc[2]; - UnimplementedDeviceState scuio; - - ARMv7MState armv7m; -}; - -#define TYPE_ASPEED27X0TSP_SOC "aspeed27x0tsp-soc" -OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0TSPSoCState, ASPEED27X0TSP_SOC) - #define TYPE_ASPEED10X0_SOC "aspeed10x0-soc" OBJECT_DECLARE_SIMPLE_TYPE(Aspeed10x0SoCState, ASPEED10X0_SOC) From 9a255419e90c4ba4d68fd45ff4e962197a94304c Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Oct 2025 13:43:24 +0800 Subject: [PATCH 1544/1794] hw/arm/aspeed_ast27x0-ssp: Change to use Aspeed27x0CoprocessorState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the AST27x0 SSP implementation to use the unified Aspeed27x0CoprocessorState structure shared between SSP and TSP. Previously, SSP and TSP each defined separate state structures (Aspeed27x0SSPSoCState and Aspeed27x0TSPSoCState), which contained identical members and caused unnecessary code duplication. This change removes Aspeed27x0SSPSoCState and replaces it with Aspeed27x0CoprocessorState, consolidating shared coprocessor state fields into a single definition in aspeed_coprocessor.h. This refactor unifies SSP and TSP under the same coprocessor state type, improving code maintainability and consistency across Aspeed coprocessor implementations. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013054334.955331-13-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 2 +- hw/arm/aspeed_ast27x0-ssp.c | 8 ++++---- include/hw/arm/aspeed_coprocessor.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index b34cd54e4e7f5..cd09a2dcf0b9a 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -42,7 +42,7 @@ struct Ast2700FCState { Clock *tsp_sysclk; Aspeed27x0SoCState ca35; - Aspeed27x0SSPSoCState ssp; + Aspeed27x0CoprocessorState ssp; Aspeed27x0TSPSoCState tsp; bool mmio_exec; diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c index 1ebf06299ebe7..f8319c95fd410 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -108,7 +108,7 @@ static struct nvic_intc_irq_info ast2700_ssp_intcmap[] = { static qemu_irq aspeed_soc_ast27x0ssp_get_irq(AspeedCoprocessorState *s, int dev) { - Aspeed27x0SSPSoCState *a = ASPEED27X0SSP_SOC(s); + Aspeed27x0CoprocessorState *a = ASPEED27X0SSP_SOC(s); AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); int or_idx; @@ -130,7 +130,7 @@ static qemu_irq aspeed_soc_ast27x0ssp_get_irq(AspeedCoprocessorState *s, static void aspeed_soc_ast27x0ssp_init(Object *obj) { - Aspeed27x0SSPSoCState *a = ASPEED27X0SSP_SOC(obj); + Aspeed27x0CoprocessorState *a = ASPEED27X0SSP_SOC(obj); AspeedCoprocessorState *s = ASPEED_COPROCESSOR(obj); AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); int i; @@ -161,7 +161,7 @@ static void aspeed_soc_ast27x0ssp_init(Object *obj) static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) { - Aspeed27x0SSPSoCState *a = ASPEED27X0SSP_SOC(dev_soc); + Aspeed27x0CoprocessorState *a = ASPEED27X0SSP_SOC(dev_soc); AspeedCoprocessorState *s = ASPEED_COPROCESSOR(dev_soc); AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); DeviceState *armv7m; @@ -288,7 +288,7 @@ static const TypeInfo aspeed_soc_ast27x0ssp_types[] = { { .name = TYPE_ASPEED27X0SSP_SOC, .parent = TYPE_ASPEED_COPROCESSOR, - .instance_size = sizeof(Aspeed27x0SSPSoCState), + .instance_size = sizeof(Aspeed27x0CoprocessorState), .instance_init = aspeed_soc_ast27x0ssp_init, .class_init = aspeed_soc_ast27x0ssp_class_init, }, diff --git a/include/hw/arm/aspeed_coprocessor.h b/include/hw/arm/aspeed_coprocessor.h index f09c2ed267b2e..d799726635df3 100644 --- a/include/hw/arm/aspeed_coprocessor.h +++ b/include/hw/arm/aspeed_coprocessor.h @@ -41,7 +41,7 @@ struct AspeedCoprocessorClass { int uarts_num; }; -struct Aspeed27x0SSPSoCState { +struct Aspeed27x0CoprocessorState { AspeedCoprocessorState parent; AspeedINTCState intc[2]; UnimplementedDeviceState ipc[2]; @@ -51,7 +51,7 @@ struct Aspeed27x0SSPSoCState { }; #define TYPE_ASPEED27X0SSP_SOC "aspeed27x0ssp-soc" -OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0SSPSoCState, ASPEED27X0SSP_SOC) +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0CoprocessorState, ASPEED27X0SSP_SOC) struct Aspeed27x0TSPSoCState { AspeedCoprocessorState parent; From 012d952c20b254bad8cac51fb6b7f8d2af0c56db Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Oct 2025 13:43:25 +0800 Subject: [PATCH 1545/1794] hw/arm/aspeed_ast27x0-tsp: Change to use Aspeed27x0CoprocessorState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the AST27x0 TSP implementation to use the unified Aspeed27x0CoprocessorState, matching the prior SSP change and removing the duplicated Aspeed27x0TSPSoCState. Key updates: - Delete Aspeed27x0TSPSoCState and reuse Aspeed27x0CoprocessorState. Update Ast2700FCState to declare tsp as Aspeed27x0CoprocessorState. This aligns TSP with SSP on a single coprocessor state type, reducing code duplication and simplifying maintenance. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013054334.955331-14-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 2 +- hw/arm/aspeed_ast27x0-tsp.c | 8 ++++---- include/hw/arm/aspeed_coprocessor.h | 12 ++---------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index cd09a2dcf0b9a..c2fa8df33ce43 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -43,7 +43,7 @@ struct Ast2700FCState { Aspeed27x0SoCState ca35; Aspeed27x0CoprocessorState ssp; - Aspeed27x0TSPSoCState tsp; + Aspeed27x0CoprocessorState tsp; bool mmio_exec; }; diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index b77c5291a6d5b..e18c624361e27 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -108,7 +108,7 @@ static struct nvic_intc_irq_info ast2700_tsp_intcmap[] = { static qemu_irq aspeed_soc_ast27x0tsp_get_irq(AspeedCoprocessorState *s, int dev) { - Aspeed27x0TSPSoCState *a = ASPEED27X0TSP_SOC(s); + Aspeed27x0CoprocessorState *a = ASPEED27X0TSP_SOC(s); AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); int or_idx; @@ -130,7 +130,7 @@ static qemu_irq aspeed_soc_ast27x0tsp_get_irq(AspeedCoprocessorState *s, static void aspeed_soc_ast27x0tsp_init(Object *obj) { - Aspeed27x0TSPSoCState *a = ASPEED27X0TSP_SOC(obj); + Aspeed27x0CoprocessorState *a = ASPEED27X0TSP_SOC(obj); AspeedCoprocessorState *s = ASPEED_COPROCESSOR(obj); AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); int i; @@ -161,7 +161,7 @@ static void aspeed_soc_ast27x0tsp_init(Object *obj) static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) { - Aspeed27x0TSPSoCState *a = ASPEED27X0TSP_SOC(dev_soc); + Aspeed27x0CoprocessorState *a = ASPEED27X0TSP_SOC(dev_soc); AspeedCoprocessorState *s = ASPEED_COPROCESSOR(dev_soc); AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); DeviceState *armv7m; @@ -288,7 +288,7 @@ static const TypeInfo aspeed_soc_ast27x0tsp_types[] = { { .name = TYPE_ASPEED27X0TSP_SOC, .parent = TYPE_ASPEED_COPROCESSOR, - .instance_size = sizeof(Aspeed27x0TSPSoCState), + .instance_size = sizeof(Aspeed27x0CoprocessorState), .instance_init = aspeed_soc_ast27x0tsp_init, .class_init = aspeed_soc_ast27x0tsp_class_init, }, diff --git a/include/hw/arm/aspeed_coprocessor.h b/include/hw/arm/aspeed_coprocessor.h index d799726635df3..110e776c62020 100644 --- a/include/hw/arm/aspeed_coprocessor.h +++ b/include/hw/arm/aspeed_coprocessor.h @@ -53,16 +53,8 @@ struct Aspeed27x0CoprocessorState { #define TYPE_ASPEED27X0SSP_SOC "aspeed27x0ssp-soc" OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0CoprocessorState, ASPEED27X0SSP_SOC) -struct Aspeed27x0TSPSoCState { - AspeedCoprocessorState parent; - AspeedINTCState intc[2]; - UnimplementedDeviceState ipc[2]; - UnimplementedDeviceState scuio; - - ARMv7MState armv7m; -}; - #define TYPE_ASPEED27X0TSP_SOC "aspeed27x0tsp-soc" -OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0TSPSoCState, ASPEED27X0TSP_SOC) +DECLARE_OBJ_CHECKERS(Aspeed27x0CoprocessorState, AspeedCoprocessorClass, + ASPEED27X0TSP_SOC, TYPE_ASPEED27X0TSP_SOC) #endif /* ASPEED_COPROCESSOR_H */ From f063ec2a5f8d89099b8b8d084b32020e9cda9280 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Oct 2025 13:43:26 +0800 Subject: [PATCH 1546/1794] hw/arm/aspeed_ast27x0-ssp: Rename type to TYPE_ASPEED27X0SSP_COPROCESSOR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the AST27x0 SSP type from TYPE_ASPEED27X0SSP_SOC to TYPE_ASPEED27X0SSP_COPROCESSOR to better reflect its role as a coprocessor rather than a standalone SoC. This aligns naming conventions with the coprocessor-based design introduced in earlier refactors. This change improves naming consistency across SSP and TSP coprocessor implementations and clarifies their relationship to the unified Aspeed27x0CoprocessorState. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013054334.955331-15-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 3 ++- hw/arm/aspeed_ast27x0-ssp.c | 10 +++++----- include/hw/arm/aspeed_coprocessor.h | 5 +++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index c2fa8df33ce43..67982d2fa0736 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -121,7 +121,8 @@ static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) s->ssp_sysclk = clock_new(OBJECT(s), "SSP_SYSCLK"); clock_set_hz(s->ssp_sysclk, 200000000ULL); - object_initialize_child(OBJECT(s), "ssp", &s->ssp, TYPE_ASPEED27X0SSP_SOC); + object_initialize_child(OBJECT(s), "ssp", &s->ssp, + TYPE_ASPEED27X0SSP_COPROCESSOR); memory_region_init(&s->ssp_memory, OBJECT(&s->ssp), "ssp-memory", UINT64_MAX); diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c index f8319c95fd410..d27be8b92558e 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -1,5 +1,5 @@ /* - * ASPEED Ast27x0 SSP SoC + * ASPEED Ast27x0 SSP Coprocessor * * Copyright (C) 2025 ASPEED Technology Inc. * @@ -108,7 +108,7 @@ static struct nvic_intc_irq_info ast2700_ssp_intcmap[] = { static qemu_irq aspeed_soc_ast27x0ssp_get_irq(AspeedCoprocessorState *s, int dev) { - Aspeed27x0CoprocessorState *a = ASPEED27X0SSP_SOC(s); + Aspeed27x0CoprocessorState *a = ASPEED27X0SSP_COPROCESSOR(s); AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); int or_idx; @@ -130,7 +130,7 @@ static qemu_irq aspeed_soc_ast27x0ssp_get_irq(AspeedCoprocessorState *s, static void aspeed_soc_ast27x0ssp_init(Object *obj) { - Aspeed27x0CoprocessorState *a = ASPEED27X0SSP_SOC(obj); + Aspeed27x0CoprocessorState *a = ASPEED27X0SSP_COPROCESSOR(obj); AspeedCoprocessorState *s = ASPEED_COPROCESSOR(obj); AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); int i; @@ -161,7 +161,7 @@ static void aspeed_soc_ast27x0ssp_init(Object *obj) static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) { - Aspeed27x0CoprocessorState *a = ASPEED27X0SSP_SOC(dev_soc); + Aspeed27x0CoprocessorState *a = ASPEED27X0SSP_COPROCESSOR(dev_soc); AspeedCoprocessorState *s = ASPEED_COPROCESSOR(dev_soc); AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); DeviceState *armv7m; @@ -286,7 +286,7 @@ static void aspeed_soc_ast27x0ssp_class_init(ObjectClass *klass, const void *dat static const TypeInfo aspeed_soc_ast27x0ssp_types[] = { { - .name = TYPE_ASPEED27X0SSP_SOC, + .name = TYPE_ASPEED27X0SSP_COPROCESSOR, .parent = TYPE_ASPEED_COPROCESSOR, .instance_size = sizeof(Aspeed27x0CoprocessorState), .instance_init = aspeed_soc_ast27x0ssp_init, diff --git a/include/hw/arm/aspeed_coprocessor.h b/include/hw/arm/aspeed_coprocessor.h index 110e776c62020..1c201a15c61c2 100644 --- a/include/hw/arm/aspeed_coprocessor.h +++ b/include/hw/arm/aspeed_coprocessor.h @@ -50,8 +50,9 @@ struct Aspeed27x0CoprocessorState { ARMv7MState armv7m; }; -#define TYPE_ASPEED27X0SSP_SOC "aspeed27x0ssp-soc" -OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0CoprocessorState, ASPEED27X0SSP_SOC) +#define TYPE_ASPEED27X0SSP_COPROCESSOR "aspeed27x0ssp-coprocessor" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0CoprocessorState, + ASPEED27X0SSP_COPROCESSOR) #define TYPE_ASPEED27X0TSP_SOC "aspeed27x0tsp-soc" DECLARE_OBJ_CHECKERS(Aspeed27x0CoprocessorState, AspeedCoprocessorClass, From a6366527e35ad3125fc9fcf7f7d88b19e221b695 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Oct 2025 13:43:27 +0800 Subject: [PATCH 1547/1794] hw/arm/aspeed_ast27x0-tsp: Rename type to TYPE_ASPEED27X0TSP_COPROCESSOR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the AST27x0 TSP type from TYPE_ASPEED27X0TSP_SOC to TYPE_ASPEED27X0TSP_COPROCESSOR to align with the naming convention used for the SSP coprocessor (TYPE_ASPEED27X0SSP_COPROCESSOR). This change clarifies that TSP is implemented as a coprocessor rather than a full SoC. This ensures consistent terminology between SSP and TSP components and improves clarity within the coprocessor subsystem code. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013054334.955331-16-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 3 ++- hw/arm/aspeed_ast27x0-tsp.c | 10 +++++----- include/hw/arm/aspeed_coprocessor.h | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index 67982d2fa0736..a61ecff3909b8 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -149,7 +149,8 @@ static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) s->tsp_sysclk = clock_new(OBJECT(s), "TSP_SYSCLK"); clock_set_hz(s->tsp_sysclk, 200000000ULL); - object_initialize_child(OBJECT(s), "tsp", &s->tsp, TYPE_ASPEED27X0TSP_SOC); + object_initialize_child(OBJECT(s), "tsp", &s->tsp, + TYPE_ASPEED27X0TSP_COPROCESSOR); memory_region_init(&s->tsp_memory, OBJECT(&s->tsp), "tsp-memory", UINT64_MAX); diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index e18c624361e27..7f109101fe44d 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -1,5 +1,5 @@ /* - * ASPEED Ast27x0 TSP SoC + * ASPEED Ast27x0 TSP Coprocessor * * Copyright (C) 2025 ASPEED Technology Inc. * @@ -108,7 +108,7 @@ static struct nvic_intc_irq_info ast2700_tsp_intcmap[] = { static qemu_irq aspeed_soc_ast27x0tsp_get_irq(AspeedCoprocessorState *s, int dev) { - Aspeed27x0CoprocessorState *a = ASPEED27X0TSP_SOC(s); + Aspeed27x0CoprocessorState *a = ASPEED27X0TSP_COPROCESSOR(s); AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); int or_idx; @@ -130,7 +130,7 @@ static qemu_irq aspeed_soc_ast27x0tsp_get_irq(AspeedCoprocessorState *s, static void aspeed_soc_ast27x0tsp_init(Object *obj) { - Aspeed27x0CoprocessorState *a = ASPEED27X0TSP_SOC(obj); + Aspeed27x0CoprocessorState *a = ASPEED27X0TSP_COPROCESSOR(obj); AspeedCoprocessorState *s = ASPEED_COPROCESSOR(obj); AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); int i; @@ -161,7 +161,7 @@ static void aspeed_soc_ast27x0tsp_init(Object *obj) static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) { - Aspeed27x0CoprocessorState *a = ASPEED27X0TSP_SOC(dev_soc); + Aspeed27x0CoprocessorState *a = ASPEED27X0TSP_COPROCESSOR(dev_soc); AspeedCoprocessorState *s = ASPEED_COPROCESSOR(dev_soc); AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); DeviceState *armv7m; @@ -286,7 +286,7 @@ static void aspeed_soc_ast27x0tsp_class_init(ObjectClass *klass, const void *dat static const TypeInfo aspeed_soc_ast27x0tsp_types[] = { { - .name = TYPE_ASPEED27X0TSP_SOC, + .name = TYPE_ASPEED27X0TSP_COPROCESSOR, .parent = TYPE_ASPEED_COPROCESSOR, .instance_size = sizeof(Aspeed27x0CoprocessorState), .instance_init = aspeed_soc_ast27x0tsp_init, diff --git a/include/hw/arm/aspeed_coprocessor.h b/include/hw/arm/aspeed_coprocessor.h index 1c201a15c61c2..d77655d65911a 100644 --- a/include/hw/arm/aspeed_coprocessor.h +++ b/include/hw/arm/aspeed_coprocessor.h @@ -54,8 +54,8 @@ struct Aspeed27x0CoprocessorState { OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0CoprocessorState, ASPEED27X0SSP_COPROCESSOR) -#define TYPE_ASPEED27X0TSP_SOC "aspeed27x0tsp-soc" +#define TYPE_ASPEED27X0TSP_COPROCESSOR "aspeed27x0tsp-coprocessor" DECLARE_OBJ_CHECKERS(Aspeed27x0CoprocessorState, AspeedCoprocessorClass, - ASPEED27X0TSP_SOC, TYPE_ASPEED27X0TSP_SOC) + ASPEED27X0TSP_COPROCESSOR, TYPE_ASPEED27X0TSP_COPROCESSOR) #endif /* ASPEED_COPROCESSOR_H */ From 0cf9761b0ae6dd04815b54eb785301f35015246d Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Oct 2025 13:43:28 +0800 Subject: [PATCH 1548/1794] hw/arm/aspeed_ast27x0-{ssp,tsp}: Fix coding style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix coding style warnings in aspeed_ast27x0-ssp.c and aspeed_ast27x0-tsp.c reported by checkpatch.pl regarding line length exceeding 80 characters. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013054334.955331-17-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-ssp.c | 3 ++- hw/arm/aspeed_ast27x0-tsp.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c index d27be8b92558e..936c7c72e8f74 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -263,7 +263,8 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) sc->memmap[ASPEED_DEV_SCUIO], 0x1000); } -static void aspeed_soc_ast27x0ssp_class_init(ObjectClass *klass, const void *data) +static void aspeed_soc_ast27x0ssp_class_init(ObjectClass *klass, + const void *data) { static const char * const valid_cpu_types[] = { ARM_CPU_TYPE_NAME("cortex-m4"), /* TODO: cortex-m4f */ diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index 7f109101fe44d..9318f8c86c519 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -263,7 +263,8 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) sc->memmap[ASPEED_DEV_SCUIO], 0x1000); } -static void aspeed_soc_ast27x0tsp_class_init(ObjectClass *klass, const void *data) +static void aspeed_soc_ast27x0tsp_class_init(ObjectClass *klass, + const void *data) { static const char * const valid_cpu_types[] = { ARM_CPU_TYPE_NAME("cortex-m4"), /* TODO cortex-m4f */ From 6a9d1ccd39bf0305c94691ce0ca228599d4719f3 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 29 Jul 2025 13:12:26 +0200 Subject: [PATCH 1549/1794] hw/display/xenfb: Replace unreachable code by g_assert_not_reached() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xenfb_mouse_event() has a switch statement whose controlling expression move->axis is an enum InputAxis. The enum values are INPUT_AXIS_X and INPUT_AXIS_Y, encoded as 0 and 1. The switch has a case for both axes. In addition, it has an unreachable default label. This convinces Coverity that move->axis can be greater than 1. It duly reports a buffer overrun when it is used to subscript an array with two elements. Replace the unreachable code by g_assert_not_reached(). Resolves: Coverity CID 1613906 Signed-off-by: Markus Armbruster Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250729111226.3627499-1-armbru@redhat.com> [PMD: s/abort/g_assert_not_reached/] Signed-off-by: Philippe Mathieu-Daudé --- hw/display/xenfb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index 22822fecea305..164fd0b248571 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -283,8 +283,7 @@ static void xenfb_mouse_event(DeviceState *dev, QemuConsole *src, scale = surface_height(surface) - 1; break; default: - scale = 0x8000; - break; + g_assert_not_reached(); } xenfb->axis[move->axis] = move->value * scale / 0x7fff; } From 9df035ecf735a41a0dd7686bf1d81e1b2f30eff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Aug 2025 17:21:13 +0200 Subject: [PATCH 1550/1794] hw/ppc: Do not open-code cpu_resume() in spin_kick() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to make the code easier to follow / review, use the cpu_resume() helper instead of open-coding it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250924173028.53658-2-philmd@linaro.org> --- hw/ppc/ppce500_spin.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c index 2310f62a91e4a..bc70e50e926eb 100644 --- a/hw/ppc/ppce500_spin.c +++ b/hw/ppc/ppce500_spin.c @@ -99,8 +99,7 @@ static void spin_kick(CPUState *cs, run_on_cpu_data data) cs->halted = 0; cs->exception_index = -1; - cs->stopped = false; - qemu_cpu_kick(cs); + cpu_resume(cs); } static void spin_write(void *opaque, hwaddr addr, uint64_t value, From 18727804c06575e6a69566c593e79bfa4a197adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Aug 2025 16:16:30 +0200 Subject: [PATCH 1551/1794] hw/xtensa/xtfpga: Have xtfpga_init() only initialize MMU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_reset() should not be used with an unrealized CPU. Here we simply want to initialize the MMU, not the CPU, so just call reset_mmu(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Max Filippov Message-Id: <20250925013513.67780-1-philmd@linaro.org> --- hw/xtensa/xtfpga.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 6efffae466b8d..55de1a7a073c1 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -268,7 +268,7 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) /* Need MMU initialized prior to ELF loading, * so that ELF gets loaded into virtual addresses */ - cpu_reset(CPU(cpu)); + reset_mmu(cenv); } if (smp_cpus > 1) { extints = xtensa_mx_pic_get_extints(mx_pic); From 525e1c9908704377007e1cef2bbc4e2bef7e5197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 2 Oct 2025 04:26:32 +0200 Subject: [PATCH 1552/1794] hw/sparc/leon3: Remove unnecessary CPU() QOM cast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit env_cpu() already returns a CPUState type, no need to cast. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Clément Chigot Message-Id: <20251002033623.26800-1-philmd@linaro.org> --- hw/sparc/leon3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 0aeaad3becc9a..09d2cec488c91 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -192,7 +192,7 @@ static void leon3_cache_control_int(CPUSPARCState *env) static void leon3_irq_ack(CPUSPARCState *env, int intno) { - CPUState *cpu = CPU(env_cpu(env)); + CPUState *cpu = env_cpu(env); grlib_irqmp_ack(env->irq_manager, cpu->cpu_index, intno); } From 1b2a50cc003962f42ce0bc91fbd4b8b380036ac2 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Thu, 2 Oct 2025 09:34:14 +0200 Subject: [PATCH 1553/1794] hw/net/can/xlnx-versal-canfd: remove unused include directives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop unecessary include directives in xlnx-versal-canfd.c. Reviewed-by: Alistair Francis Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Signed-off-by: Luc Michel Message-ID: <20251002073418.109375-6-luc.michel@amd.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/can/xlnx-versal-canfd.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/net/can/xlnx-versal-canfd.c b/hw/net/can/xlnx-versal-canfd.c index 3eb111949f89f..343348660b5a2 100644 --- a/hw/net/can/xlnx-versal-canfd.c +++ b/hw/net/can/xlnx-versal-canfd.c @@ -35,12 +35,8 @@ #include "hw/irq.h" #include "hw/register.h" #include "qapi/error.h" -#include "qemu/bitops.h" #include "qemu/log.h" -#include "qemu/cutils.h" -#include "qemu/event_notifier.h" #include "hw/qdev-properties.h" -#include "qom/object_interfaces.h" #include "migration/vmstate.h" #include "hw/net/xlnx-versal-canfd.h" #include "trace.h" From fc08d5f699c53ad46a3d8b8e5159e3ab6a1124ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 3 Oct 2025 12:30:24 +0200 Subject: [PATCH 1554/1794] hw/arm/aspeed: Don't set 'auto_create_sdcard' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Aspeed machines inherited from a 'no_sdcard' attribute when first introduced in QEMU. This attribute was later renamed to 'auto_create_sdcard' by commit cdc8d7cadaac ("hw/boards: Rename no_sdcard -> auto_create_sdcard") and set to 'true'. This has the indesirable efect to automatically create SD cards at init time. Remove 'auto_create_sdcard' to avoid creating a SD card device. Cc: Philippe Mathieu-Daudé Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251003103024.1863551-1-clg@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/aspeed.c | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 6046ec0bb2a2d..58cfbc7137942 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1418,7 +1418,6 @@ static void aspeed_machine_palmetto_class_init(ObjectClass *oc, amc->spi_model = "mx25l25635f"; amc->num_cs = 1; amc->i2c_init = palmetto_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 256 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1436,7 +1435,6 @@ static void aspeed_machine_quanta_q71l_class_init(ObjectClass *oc, amc->spi_model = "mx25l25635e"; amc->num_cs = 1; amc->i2c_init = quanta_q71l_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 128 * MiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1455,7 +1453,6 @@ static void aspeed_machine_supermicrox11_bmc_class_init(ObjectClass *oc, amc->num_cs = 1; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = palmetto_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 256 * MiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1474,7 +1471,6 @@ static void aspeed_machine_supermicro_x11spi_bmc_class_init(ObjectClass *oc, amc->num_cs = 1; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = palmetto_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1492,7 +1488,6 @@ static void aspeed_machine_ast2500_evb_class_init(ObjectClass *oc, amc->spi_model = "mx25l25635f"; amc->num_cs = 1; amc->i2c_init = ast2500_evb_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1511,7 +1506,6 @@ static void aspeed_machine_yosemitev2_class_init(ObjectClass *oc, amc->spi_model = "mx25l25635e"; amc->num_cs = 2; amc->i2c_init = yosemitev2_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1529,7 +1523,6 @@ static void aspeed_machine_romulus_class_init(ObjectClass *oc, amc->spi_model = "mx66l1g45g"; amc->num_cs = 2; amc->i2c_init = romulus_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1548,7 +1541,6 @@ static void aspeed_machine_tiogapass_class_init(ObjectClass *oc, amc->spi_model = "mx25l25635e"; amc->num_cs = 2; amc->i2c_init = tiogapass_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1566,7 +1558,6 @@ static void aspeed_machine_sonorapass_class_init(ObjectClass *oc, amc->spi_model = "mx66l1g45g"; amc->num_cs = 2; amc->i2c_init = sonorapass_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1584,7 +1575,6 @@ static void aspeed_machine_witherspoon_class_init(ObjectClass *oc, amc->spi_model = "mx66l1g45g"; amc->num_cs = 2; amc->i2c_init = witherspoon_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1606,7 +1596,6 @@ static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, ASPEED_MAC3_ON; amc->sdhci_wp_inverted = true; amc->i2c_init = ast2600_evb_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); aspeed_machine_ast2600_class_emmc_init(oc); @@ -1625,7 +1614,6 @@ static void aspeed_machine_g220a_class_init(ObjectClass *oc, const void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = g220a_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 1024 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1644,7 +1632,6 @@ static void aspeed_machine_fp5280g2_class_init(ObjectClass *oc, amc->num_cs = 2; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = fp5280g2_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1663,7 +1650,6 @@ static void aspeed_machine_rainier_class_init(ObjectClass *oc, const void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = rainier_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); aspeed_machine_ast2600_class_emmc_init(oc); @@ -1686,7 +1672,6 @@ static void aspeed_machine_fuji_class_init(ObjectClass *oc, const void *data) amc->macs_mask = ASPEED_MAC3_ON; amc->i2c_init = fuji_bmc_i2c_init; amc->uart_default = ASPEED_DEV_UART1; - mc->auto_create_sdcard = true; mc->default_ram_size = FUJI_BMC_RAM_SIZE; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1708,7 +1693,6 @@ static void aspeed_machine_bletchley_class_init(ObjectClass *oc, amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON; amc->i2c_init = bletchley_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = BLETCHLEY_BMC_RAM_SIZE; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1728,7 +1712,6 @@ static void aspeed_machine_catalina_class_init(ObjectClass *oc, amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON; amc->i2c_init = catalina_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = CATALINA_BMC_RAM_SIZE; aspeed_machine_class_init_cpus_defaults(mc); aspeed_machine_ast2600_class_emmc_init(oc); @@ -1796,7 +1779,6 @@ static void aspeed_machine_fby35_class_init(ObjectClass *oc, const void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC3_ON; amc->i2c_init = fby35_i2c_init; - mc->auto_create_sdcard = true; /* FIXME: Replace this macro with something more general */ mc->default_ram_size = FUJI_BMC_RAM_SIZE; aspeed_machine_class_init_cpus_defaults(mc); @@ -1909,7 +1891,6 @@ static void aspeed_machine_ast2700a0_evb_class_init(ObjectClass *oc, amc->uart_default = ASPEED_DEV_UART12; amc->i2c_init = ast2700_evb_i2c_init; amc->vbootrom = true; - mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1932,7 +1913,6 @@ static void aspeed_machine_ast2700a1_evb_class_init(ObjectClass *oc, amc->uart_default = ASPEED_DEV_UART12; amc->i2c_init = ast2700_evb_i2c_init; amc->vbootrom = true; - mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1953,7 +1933,6 @@ static void aspeed_machine_qcom_dc_scm_v1_class_init(ObjectClass *oc, amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = qcom_dc_scm_bmc_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1973,7 +1952,6 @@ static void aspeed_machine_qcom_firework_class_init(ObjectClass *oc, amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = qcom_dc_scm_firework_i2c_init; - mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); }; From d39ac36fe298400eac7e9272c950bedca29490c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 7 Oct 2025 03:55:08 +0200 Subject: [PATCH 1555/1794] hw/s390x/sclp: Do not ignore address_space_read/write() errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If address_space_read() fails, return PGM_ADDRESSING. In the unlikely case address_space_write() fails (we already checked the address is readable), return PGM_PROTECTION. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Jason J. Herne Message-Id: <20251007015802.24748-1-philmd@linaro.org> --- hw/s390x/sclp.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 51e88ba8f1214..8602a566a4937 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -306,6 +306,7 @@ int sclp_service_call(S390CPU *cpu, uint64_t sccb, uint32_t code) g_autofree SCCB *work_sccb = NULL; AddressSpace *as = CPU(cpu)->as; const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; + MemTxResult ret; /* first some basic checks on program checks */ if (env->psw.mask & PSW_MASK_PSTATE) { @@ -320,7 +321,10 @@ int sclp_service_call(S390CPU *cpu, uint64_t sccb, uint32_t code) } /* the header contains the actual length of the sccb */ - address_space_read(as, sccb, attrs, &header, sizeof(SCCBHeader)); + ret = address_space_read(as, sccb, attrs, &header, sizeof(SCCBHeader)); + if (ret != MEMTX_OK) { + return -PGM_ADDRESSING; + } /* Valid sccb sizes */ if (be16_to_cpu(header.length) < sizeof(SCCBHeader)) { @@ -333,7 +337,11 @@ int sclp_service_call(S390CPU *cpu, uint64_t sccb, uint32_t code) * the host has checked the values */ work_sccb = g_malloc0(be16_to_cpu(header.length)); - address_space_read(as, sccb, attrs, work_sccb, be16_to_cpu(header.length)); + ret = address_space_read(as, sccb, attrs, + work_sccb, be16_to_cpu(header.length)); + if (ret != MEMTX_OK) { + return -PGM_ADDRESSING; + } if (!sclp_command_code_valid(code)) { work_sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); @@ -347,7 +355,11 @@ int sclp_service_call(S390CPU *cpu, uint64_t sccb, uint32_t code) sclp_c->execute(sclp, work_sccb, code); out_write: - address_space_write(as, sccb, attrs, work_sccb, be16_to_cpu(header.length)); + ret = address_space_write(as, sccb, attrs, + work_sccb, be16_to_cpu(header.length)); + if (ret != MEMTX_OK) { + return -PGM_PROTECTION; + } sclp_c->service_interrupt(sclp, sccb); From fe5d03c4a3ec7edfe0a9bf4d232b04aca4d72fc9 Mon Sep 17 00:00:00 2001 From: Mohamed Mediouni Date: Tue, 7 Oct 2025 22:31:50 +0200 Subject: [PATCH 1556/1794] hw/vmapple: include missing headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disablement by default led to: ../hw/vmapple/vmapple.c:276:39: error: use of undeclared identifier 'GTIMER_VIRT' 276 | qdev_connect_gpio_out(cpudev, GTIMER_VIRT, | ^ ../hw/vmapple/vmapple.c:479:54: error: use of undeclared identifier 'QEMU_PSCI_CONDUIT_HVC' 479 | object_property_set_int(cpu, "psci-conduit", QEMU_PSCI_CONDUIT_HVC, | ^ ../hw/vmapple/vmapple.c:556:13: error: call to undeclared function 'arm_build_mp_affinity'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 556 | arm_build_mp_affinity(n, GICV3_TARGETLIST_BITS); | ^ 3 errors generated. pretty quickly. Signed-off-by: Mohamed Mediouni Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20251007203153.30136-2-mohamed@unpredictable.fr> Signed-off-by: Philippe Mathieu-Daudé --- hw/vmapple/vmapple.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/vmapple/vmapple.c b/hw/vmapple/vmapple.c index 16e6110b68f8e..1e4365f32c953 100644 --- a/hw/vmapple/vmapple.c +++ b/hw/vmapple/vmapple.c @@ -51,6 +51,8 @@ #include "system/reset.h" #include "system/runstate.h" #include "system/system.h" +#include "target/arm/gtimer.h" +#include "target/arm/cpu.h" struct VMAppleMachineState { MachineState parent; From faf1fae7645303180b1404716cb2e9d740f0eb2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 08:14:37 +0200 Subject: [PATCH 1557/1794] hw/loongarch/boot: Remove unnecessary cast to target_ulong MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce initrd_size scope. It is already of signed type (ssize_t), no need to cast to unsigned for the comparison. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251009201947.34643-2-philmd@linaro.org> --- hw/loongarch/boot.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index a516415822d9f..3dd48cb8aab01 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -306,7 +306,7 @@ static ram_addr_t alloc_initrd_memory(struct loongarch_boot_info *info, static int64_t load_kernel_info(struct loongarch_boot_info *info) { uint64_t kernel_entry, kernel_low, kernel_high, initrd_offset = 0; - ssize_t kernel_size, initrd_size; + ssize_t kernel_size; kernel_size = load_elf(info->kernel_filename, NULL, cpu_loongarch_virt_to_phys, NULL, @@ -328,7 +328,8 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info) } if (info->initrd_filename) { - initrd_size = get_image_size(info->initrd_filename); + ssize_t initrd_size = get_image_size(info->initrd_filename); + if (initrd_size > 0) { initrd_offset = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB); initrd_offset = alloc_initrd_memory(info, initrd_offset, @@ -337,7 +338,7 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info) initrd_offset, initrd_size); } - if (initrd_size == (target_ulong)-1) { + if (initrd_size == -1) { error_report("could not load initial ram disk '%s'", info->initrd_filename); exit(1); From b73cf656b7222971de75172ee6e037c98f05bec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 16:38:34 +0200 Subject: [PATCH 1558/1794] hw/hppa: Convert type_init() -> DEFINE_TYPES() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prefer DEFINE_TYPES() macro over type_init() to register multiple QOM types. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251009143106.22724-3-philmd@linaro.org> --- hw/hppa/machine.c | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index dacedc5409c6c..2ab5fcb471ae5 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -709,16 +709,6 @@ static void HP_B160L_machine_init_class_init(ObjectClass *oc, const void *data) nc->nmi_monitor_handler = hppa_nmi; } -static const TypeInfo HP_B160L_machine_init_typeinfo = { - .name = MACHINE_TYPE_NAME("B160L"), - .parent = TYPE_MACHINE, - .class_init = HP_B160L_machine_init_class_init, - .interfaces = (const InterfaceInfo[]) { - { TYPE_NMI }, - { } - }, -}; - static void HP_C3700_machine_init_class_init(ObjectClass *oc, const void *data) { static const char * const valid_cpu_types[] = { @@ -745,20 +735,24 @@ static void HP_C3700_machine_init_class_init(ObjectClass *oc, const void *data) nc->nmi_monitor_handler = hppa_nmi; } -static const TypeInfo HP_C3700_machine_init_typeinfo = { - .name = MACHINE_TYPE_NAME("C3700"), - .parent = TYPE_MACHINE, - .class_init = HP_C3700_machine_init_class_init, - .interfaces = (const InterfaceInfo[]) { - { TYPE_NMI }, - { } +static const TypeInfo hppa_machine_types[] = { + { + .name = MACHINE_TYPE_NAME("B160L"), + .parent = TYPE_MACHINE, + .class_init = HP_B160L_machine_init_class_init, + .interfaces = (const InterfaceInfo[]) { + { TYPE_NMI }, + { } + }, + }, { + .name = MACHINE_TYPE_NAME("C3700"), + .parent = TYPE_MACHINE, + .class_init = HP_C3700_machine_init_class_init, + .interfaces = (const InterfaceInfo[]) { + { TYPE_NMI }, + { } + }, }, }; -static void hppa_machine_init_register_types(void) -{ - type_register_static(&HP_B160L_machine_init_typeinfo); - type_register_static(&HP_C3700_machine_init_typeinfo); -} - -type_init(hppa_machine_init_register_types) +DEFINE_TYPES(hppa_machine_types) From 9ccf5f38114d397802452cdf2e794fa26ed457f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 16:42:02 +0200 Subject: [PATCH 1559/1794] hw/hppa: Factor QOM HPPA_COMMON_MACHINE out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit B160L and C3700 share a lot of common code. Factor it out as an abstract HPPA_COMMON_MACHINE QOM parent. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251009143106.22724-4-philmd@linaro.org> --- hw/hppa/machine.c | 61 +++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 2ab5fcb471ae5..c8da159a11431 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -36,6 +36,13 @@ #include "net/net.h" #include "qemu/log.h" +#define TYPE_HPPA_COMMON_MACHINE MACHINE_TYPE_NAME("hppa-common") +OBJECT_DECLARE_SIMPLE_TYPE(HppaMachineState, HPPA_COMMON_MACHINE) + +struct HppaMachineState { + MachineState parent_obj; +}; + #define MIN_SEABIOS_HPPA_VERSION 12 /* require at least this fw version */ #define HPA_POWER_BUTTON (FIRMWARE_END - 0x10) @@ -683,6 +690,22 @@ static void hppa_nmi(NMIState *n, int cpu_index, Error **errp) } } +static void hppa_machine_common_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + NMIClass *nc = NMI_CLASS(oc); + + mc->reset = hppa_machine_reset; + mc->block_default_type = IF_SCSI; + mc->default_cpus = 1; + mc->max_cpus = HPPA_MAX_CPUS; + mc->default_boot_order = "cd"; + mc->default_ram_id = "ram"; + mc->default_nic = "tulip"; + + nc->nmi_monitor_handler = hppa_nmi; +} + static void HP_B160L_machine_init_class_init(ObjectClass *oc, const void *data) { static const char * const valid_cpu_types[] = { @@ -690,23 +713,13 @@ static void HP_B160L_machine_init_class_init(ObjectClass *oc, const void *data) NULL }; MachineClass *mc = MACHINE_CLASS(oc); - NMIClass *nc = NMI_CLASS(oc); mc->desc = "HP B160L workstation"; mc->default_cpu_type = TYPE_HPPA_CPU; mc->valid_cpu_types = valid_cpu_types; mc->init = machine_HP_B160L_init; - mc->reset = hppa_machine_reset; - mc->block_default_type = IF_SCSI; - mc->max_cpus = HPPA_MAX_CPUS; - mc->default_cpus = 1; mc->is_default = true; mc->default_ram_size = 512 * MiB; - mc->default_boot_order = "cd"; - mc->default_ram_id = "ram"; - mc->default_nic = "tulip"; - - nc->nmi_monitor_handler = hppa_nmi; } static void HP_C3700_machine_init_class_init(ObjectClass *oc, const void *data) @@ -716,42 +729,34 @@ static void HP_C3700_machine_init_class_init(ObjectClass *oc, const void *data) NULL }; MachineClass *mc = MACHINE_CLASS(oc); - NMIClass *nc = NMI_CLASS(oc); mc->desc = "HP C3700 workstation"; mc->default_cpu_type = TYPE_HPPA64_CPU; mc->valid_cpu_types = valid_cpu_types; mc->init = machine_HP_C3700_init; - mc->reset = hppa_machine_reset; - mc->block_default_type = IF_SCSI; mc->max_cpus = HPPA_MAX_CPUS; - mc->default_cpus = 1; - mc->is_default = false; mc->default_ram_size = 1024 * MiB; - mc->default_boot_order = "cd"; - mc->default_ram_id = "ram"; - mc->default_nic = "tulip"; - - nc->nmi_monitor_handler = hppa_nmi; } static const TypeInfo hppa_machine_types[] = { { - .name = MACHINE_TYPE_NAME("B160L"), - .parent = TYPE_MACHINE, - .class_init = HP_B160L_machine_init_class_init, + .name = TYPE_HPPA_COMMON_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(HppaMachineState), + .class_init = hppa_machine_common_class_init, + .abstract = true, .interfaces = (const InterfaceInfo[]) { { TYPE_NMI }, { } }, + }, { + .name = MACHINE_TYPE_NAME("B160L"), + .parent = TYPE_HPPA_COMMON_MACHINE, + .class_init = HP_B160L_machine_init_class_init, }, { .name = MACHINE_TYPE_NAME("C3700"), - .parent = TYPE_MACHINE, + .parent = TYPE_HPPA_COMMON_MACHINE, .class_init = HP_C3700_machine_init_class_init, - .interfaces = (const InterfaceInfo[]) { - { TYPE_NMI }, - { } - }, }, }; From 9fedc11ff127636900cc7a0a3e7214e5cb60a313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Oct 2025 07:58:16 +0200 Subject: [PATCH 1560/1794] hw/hppa: Reduce variables scope in common_init() 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: <20251010061836.45739-4-philmd@linaro.org> --- hw/hppa/machine.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index c8da159a11431..cddca69b93868 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -352,16 +352,11 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus, TranslateFn *translate) { const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - const char *initrd_filename = machine->initrd_filename; - const char *firmware = machine->firmware; MachineClass *mc = MACHINE_GET_CLASS(machine); DeviceState *dev; PCIDevice *pci_dev; - char *firmware_filename; - uint64_t firmware_low, firmware_high; long size; - uint64_t kernel_entry = 0, kernel_low, kernel_high; + uint64_t kernel_entry = 0; MemoryRegion *addr_space = get_system_memory(); MemoryRegion *rom_region; SysBusDevice *s; @@ -431,6 +426,10 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus, firmware on 64-bit machines by default if not specified on command line. */ if (!qtest_enabled()) { + const char *firmware = machine->firmware; + uint64_t firmware_low, firmware_high; + char *firmware_filename; + if (!firmware) { firmware = lasi_dev ? "hppa-firmware.img" : "hppa-firmware64.img"; } @@ -467,6 +466,10 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus, /* Load kernel */ if (kernel_filename) { + const char *kernel_cmdline = machine->kernel_cmdline; + const char *initrd_filename = machine->initrd_filename; + uint64_t kernel_low, kernel_high; + size = load_elf(kernel_filename, NULL, linux_kernel_virt_to_phys, NULL, &kernel_entry, &kernel_low, &kernel_high, NULL, ELFDATA2MSB, EM_PARISC, 0, 0); From 194dfadd6625504438107e3e740cd9dde158b335 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 10 Oct 2025 16:57:56 +0200 Subject: [PATCH 1561/1794] rust: bits: disable double_parens check It is showing in the output of the bits! macro when using the nightly toolchain, though it's not clear if it is intentional or a bug. Shut it up for now. Link: https://github.com/rust-lang/rust-clippy/issues/15852 Reported-by: Richard Henderson Suggested-by: Manos Pitsidianakis Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20251010145756.787800-1-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- rust/qemu-macros/src/lib.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/rust/qemu-macros/src/lib.rs b/rust/qemu-macros/src/lib.rs index 3e21b67b47195..3bf315c4c0a6f 100644 --- a/rust/qemu-macros/src/lib.rs +++ b/rust/qemu-macros/src/lib.rs @@ -401,7 +401,14 @@ pub fn bits_const_internal(ts: TokenStream) -> TokenStream { let ts = proc_macro2::TokenStream::from(ts); let mut it = ts.into_iter(); - BitsConstInternal::parse(&mut it) - .unwrap_or_else(syn::Error::into_compile_error) - .into() + let out = BitsConstInternal::parse(&mut it).unwrap_or_else(syn::Error::into_compile_error); + + // https://github.com/rust-lang/rust-clippy/issues/15852 + quote! { + { + #[allow(clippy::double_parens)] + #out + } + } + .into() } From ceda1563d6d347647419f61508856e5236069e02 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 13 Oct 2025 16:01:55 +0200 Subject: [PATCH 1562/1794] rust: migration: hide more warnings from call_func_with_field! The call_func_with_field! macro uses dead code willingly to infer the appropriate type. This has started adding a new warning: error: unused variable: `value__` 79 | break phantom__(&{ let value__: $typ; value__.$($field).+ }) So shut it up together with the existing unreachable_code warning. Signed-off-by: Paolo Bonzini --- rust/migration/src/vmstate.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs index e04b19b3c9f31..5f5708ad39ebf 100644 --- a/rust/migration/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -72,6 +72,7 @@ macro_rules! call_func_with_field { ($func:expr, $typ:ty, $($field:tt).+) => { $func(loop { #![allow(unreachable_code)] + #![allow(unused_variables)] const fn phantom__(_: &T) -> ::core::marker::PhantomData { ::core::marker::PhantomData } // Unreachable code is exempt from checks on uninitialized values. // Use that trick to infer the type of this PhantomData. From 414ac7e057047706914232142d7a7d38440b3f9b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 13 Oct 2025 16:49:12 +0200 Subject: [PATCH 1563/1794] rust: hpet: fix fw_cfg handling HPET ids for fw_cfg are not assigned correctly, because there is a read but no write. This is caught by nightly Rust as an unused-assignments warning, so fix it. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/fw_cfg.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs index e569b57b93b53..bb4ea8909ad9e 100644 --- a/rust/hw/timer/hpet/src/fw_cfg.rs +++ b/rust/hw/timer/hpet/src/fw_cfg.rs @@ -40,7 +40,7 @@ impl HPETFwConfig { assert!(bql::is_locked()); // SAFETY: all accesses go through these methods, which guarantee // that the accesses are protected by the BQL. - let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) }; + let fw_cfg = unsafe { &mut *addr_of_mut!(hpet_fw_cfg) }; if fw_cfg.count == u8::MAX { // first instance @@ -60,7 +60,7 @@ impl HPETFwConfig { assert!(bql::is_locked()); // SAFETY: all accesses go through these methods, which guarantee // that the accesses are protected by the BQL. - let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) }; + let fw_cfg = unsafe { &mut *addr_of_mut!(hpet_fw_cfg) }; fw_cfg.hpet[hpet_id].event_timer_block_id = timer_block_id; fw_cfg.hpet[hpet_id].address = address; From c79a35acadee784610aed40134a12738381b4fba Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Thu, 10 Jul 2025 14:46:10 -0500 Subject: [PATCH 1564/1794] target/i386: Add TSA attack variants TSA-SQ and TSA-L1 Transient Scheduler Attacks (TSA) are new speculative side channel attacks related to the execution timing of instructions under specific microarchitectural conditions. In some cases, an attacker may be able to use this timing information to infer data from other contexts, resulting in information leakage. AMD has identified two sub-variants two variants of TSA. CPUID Fn8000_0021 ECX[1] (TSA_SQ_NO). If this bit is 1, the CPU is not vulnerable to TSA-SQ. CPUID Fn8000_0021 ECX[2] (TSA_L1_NO). If this bit is 1, the CPU is not vulnerable to TSA-L1. Add the new feature word FEAT_8000_0021_ECX and corresponding bits to detect TSA variants. Link: https://www.amd.com/content/dam/amd/en/documents/resources/bulletin/technical-guidance-for-mitigating-transient-scheduler-attacks.pdf Co-developed-by: Borislav Petkov (AMD) Signed-off-by: Borislav Petkov (AMD) Signed-off-by: Babu Moger Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/12881b2c03fa351316057ddc5f39c011074b4549.1752176771.git.babu.moger@amd.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 17 +++++++++++++++++ target/i386/cpu.h | 6 ++++++ 2 files changed, 23 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index ab18de894e40a..339881b40f8cd 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1415,6 +1415,22 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .tcg_features = 0, .unmigratable_flags = 0, }, + [FEAT_8000_0021_ECX] = { + .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL, "tsa-sq-no", "tsa-l1-no", NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, + .cpuid = { .eax = 0x80000021, .reg = R_ECX, }, + .tcg_features = 0, + .unmigratable_flags = 0, + }, [FEAT_8000_0022_EAX] = { .type = CPUID_FEATURE_WORD, .feat_names = { @@ -8526,6 +8542,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *eax = *ebx = *ecx = *edx = 0; *eax = env->features[FEAT_8000_0021_EAX]; *ebx = env->features[FEAT_8000_0021_EBX]; + *ecx = env->features[FEAT_8000_0021_ECX]; break; case 0x80000022: *eax = *ebx = *ecx = *edx = 0; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 8b7c173838e51..f74f13534e71a 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -643,6 +643,7 @@ typedef enum FeatureWord { FEAT_8000_0008_EBX, /* CPUID[8000_0008].EBX */ FEAT_8000_0021_EAX, /* CPUID[8000_0021].EAX */ FEAT_8000_0021_EBX, /* CPUID[8000_0021].EBX */ + FEAT_8000_0021_ECX, /* CPUID[8000_0021].ECX */ FEAT_8000_0022_EAX, /* CPUID[8000_0022].EAX */ FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */ FEAT_KVM, /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */ @@ -1126,6 +1127,11 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); */ #define CPUID_8000_0021_EBX_RAPSIZE (8U << 16) +/* CPU is not vulnerable TSA SA-SQ attack */ +#define CPUID_8000_0021_ECX_TSA_SQ_NO (1U << 1) +/* CPU is not vulnerable TSA SA-L1 attack */ +#define CPUID_8000_0021_ECX_TSA_L1_NO (1U << 2) + /* Performance Monitoring Version 2 */ #define CPUID_8000_0022_EAX_PERFMON_V2 (1U << 0) From d8ec0baf4a15082cdc4abe1de28face9a26f0dc9 Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Thu, 10 Jul 2025 14:46:11 -0500 Subject: [PATCH 1565/1794] target/i386: Add TSA feature flag verw-clear Transient Scheduler Attacks (TSA) are new speculative side channel attacks related to the execution timing of instructions under specific microarchitectural conditions. In some cases, an attacker may be able to use this timing information to infer data from other contexts, resulting in information leakage CPUID Fn8000_0021 EAX[5] (VERW_CLEAR). If this bit is 1, the memory form of the VERW instruction may be used to help mitigate TSA. Link: https://www.amd.com/content/dam/amd/en/documents/resources/bulletin/technical-guidance-for-mitigating-transient-scheduler-attacks.pdf Co-developed-by: Borislav Petkov (AMD) Signed-off-by: Borislav Petkov (AMD) Signed-off-by: Babu Moger Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/e6362672e3a67a9df661a8f46598335a1a2d2754.1752176771.git.babu.moger@amd.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 2 +- target/i386/cpu.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 339881b40f8cd..4f99cbc5c0b70 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1397,7 +1397,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .type = CPUID_FEATURE_WORD, .feat_names = { "no-nested-data-bp", "fs-gs-base-ns", "lfence-always-serializing", NULL, - NULL, NULL, "null-sel-clr-base", NULL, + NULL, "verw-clear", "null-sel-clr-base", NULL, "auto-ibrs", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, diff --git a/target/i386/cpu.h b/target/i386/cpu.h index f74f13534e71a..ce948861a7659 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1104,6 +1104,8 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define CPUID_8000_0021_EAX_FS_GS_BASE_NS (1U << 1) /* LFENCE is always serializing */ #define CPUID_8000_0021_EAX_LFENCE_ALWAYS_SERIALIZING (1U << 2) +/* Memory form of VERW mitigates TSA */ +#define CPUID_8000_0021_EAX_VERW_CLEAR (1U << 5) /* Null Selector Clears Base */ #define CPUID_8000_0021_EAX_NULL_SEL_CLR_BASE (1U << 6) /* Automatic IBRS */ From df9a3372ddebfcfc135861fa2d53cef6f98065f9 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Thu, 12 Jun 2025 16:21:55 +0200 Subject: [PATCH 1566/1794] target/i386: Fix CR2 handling for non-canonical addresses Commit 3563362ddfae ("target/i386: Introduce structures for mmu_translate") accidentally modified CR2 for non-canonical address exceptions while these should lead to a #GP / #SS instead -- without changing CR2. Fix that. A KUT test for this was submitted as [1]. [1] https://lore.kernel.org/kvm/20250612141637.131314-1-minipli@grsecurity.net/ Fixes: 3563362ddfae ("target/i386: Introduce structures for mmu_translate") Signed-off-by: Mathias Krause Link: https://lore.kernel.org/r/20250612142155.132175-1-minipli@grsecurity.net Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- target/i386/tcg/system/excp_helper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/i386/tcg/system/excp_helper.c b/target/i386/tcg/system/excp_helper.c index 50040f6fcafab..f622b5d588eb3 100644 --- a/target/i386/tcg/system/excp_helper.c +++ b/target/i386/tcg/system/excp_helper.c @@ -592,7 +592,8 @@ static bool get_physical_address(CPUX86State *env, vaddr addr, if (sext != 0 && sext != -1) { *err = (TranslateFault){ .exception_index = EXCP0D_GPF, - .cr2 = addr, + /* non-canonical #GP doesn't change CR2 */ + .cr2 = env->cr[2], }; return false; } From 00001a22d183ce96c110690987bf9dd6a8548552 Mon Sep 17 00:00:00 2001 From: Jon Kohler Date: Wed, 8 Oct 2025 13:25:57 -0700 Subject: [PATCH 1567/1794] i386/kvm: Expose ARCH_CAP_FB_CLEAR when invulnerable to MDS Newer Intel hardware (Sapphire Rapids and higher) sets multiple MDS immunity bits in MSR_IA32_ARCH_CAPABILITIES but lacks the hardware-level MSR_ARCH_CAP_FB_CLEAR (bit 17): ARCH_CAP_MDS_NO ARCH_CAP_TAA_NO ARCH_CAP_PSDP_NO ARCH_CAP_FBSDP_NO ARCH_CAP_SBDR_SSDP_NO This prevents VMs with fb-clear=on from migrating from older hardware (Cascade Lake, Ice Lake) to newer hardware, limiting live migration capabilities. Note fb-clear was first introduced in v8.1.0 [1]. Expose MSR_ARCH_CAP_FB_CLEAR for MDS-invulnerable systems to enable seamless migration between hardware generations. Note: There is no impact when a guest migrates to newer hardware as the existing bit combinations already mark the host as MMIO-immune and disable FB_CLEAR operations in the kernel (see Linux's arch_cap_mmio_immune() and vmx_update_fb_clear_dis()). See kernel side discussion for [2] for additional context. [1] 22e1094ca82 ("target/i386: add support for FB_CLEAR feature") [2] https://patchwork.kernel.org/project/kvm/patch/20250401044931.793203-1-jon@nutanix.com/ Cc: Pawan Gupta Suggested-by: Sean Christopherson Signed-off-by: Jon Kohler Link: https://lore.kernel.org/r/20251008202557.4141285-1-jon@nutanix.com Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index db40caa3412f9..e40e374b046e5 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -653,6 +653,23 @@ uint64_t kvm_arch_get_supported_msr_feature(KVMState *s, uint32_t index) must_be_one = (uint32_t)value; can_be_one = (uint32_t)(value >> 32); return can_be_one & ~must_be_one; + case MSR_IA32_ARCH_CAPABILITIES: + /* + * Special handling for fb-clear bit in ARCH_CAPABILITIES MSR. + * KVM will only report the bit if it is enabled in the host, + * but, for live migration capability purposes, we want to + * expose the bit to the guest even if it is disabled in the + * host, as long as the host itself is not vulnerable to + * the issue that the fb-clear bit is meant to mitigate. + */ + if ((value & MSR_ARCH_CAP_MDS_NO) && + (value & MSR_ARCH_CAP_TAA_NO) && + (value & MSR_ARCH_CAP_SBDR_SSDP_NO) && + (value & MSR_ARCH_CAP_FBSDP_NO) && + (value & MSR_ARCH_CAP_PSDP_NO)) { + value |= MSR_ARCH_CAP_FB_CLEAR; + } + return value; default: return value; From df32e5c568c9cf68c15a9bbd98d0c3aff19eab63 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 11 Oct 2025 09:13:29 +0200 Subject: [PATCH 1568/1794] i386/cpu: Prevent delivering SIPI during SMM in TCG mode [commit message by YiFei Zhu] A malicious kernel may control the instruction pointer in SMM in a multi-processor VM by sending a sequence of IPIs via APIC: CPU0 CPU1 IPI(CPU1, MODE_INIT) x86_cpu_exec_reset() apic_init_reset() s->wait_for_sipi = true IPI(CPU1, MODE_SMI) do_smm_enter() env->hflags |= HF_SMM_MASK; IPI(CPU1, MODE_STARTUP, vector) do_cpu_sipi() apic_sipi() /* s->wait_for_sipi check passes */ cpu_x86_load_seg_cache_sipi(vector) A different sequence, SMI INIT SIPI, is also buggy in TCG because INIT is not blocked or latched during SMM. However, it is not vulnerable to an instruction pointer control in the same way because x86_cpu_exec_reset clears env->hflags, exiting SMM. Fixes: a9bad65d2c1f ("target-i386: wake up processors that receive an SMI") Analyzed-by: YiFei Zhu Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- hw/intc/apic.c | 2 -- target/i386/helper.c | 4 ++++ target/i386/tcg/system/seg_helper.c | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/intc/apic.c b/hw/intc/apic.c index 6d7859640c275..c7680338563a6 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -646,8 +646,6 @@ void apic_sipi(DeviceState *dev) { APICCommonState *s = APIC(dev); - cpu_reset_interrupt(CPU(s->cpu), CPU_INTERRUPT_SIPI); - if (!s->wait_for_sipi) return; cpu_x86_load_seg_cache_sipi(s->cpu, s->sipi_vector); diff --git a/target/i386/helper.c b/target/i386/helper.c index 651041ccfa683..72b2e195a31ed 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -621,6 +621,10 @@ void do_cpu_init(X86CPU *cpu) void do_cpu_sipi(X86CPU *cpu) { + CPUX86State *env = &cpu->env; + if (env->hflags & HF_SMM_MASK) { + return; + } apic_sipi(cpu->apic_state); } diff --git a/target/i386/tcg/system/seg_helper.c b/target/i386/tcg/system/seg_helper.c index 38072e51d721c..8c7856be81e24 100644 --- a/target/i386/tcg/system/seg_helper.c +++ b/target/i386/tcg/system/seg_helper.c @@ -182,6 +182,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) apic_poll_irq(cpu->apic_state); break; case CPU_INTERRUPT_SIPI: + cpu_reset_interrupt(cs, CPU_INTERRUPT_SIPI); do_cpu_sipi(cpu); break; case CPU_INTERRUPT_SMI: From cdba90ac1b0ac789b10c0b5f6ef7e9558237ec66 Mon Sep 17 00:00:00 2001 From: YiFei Zhu Date: Thu, 25 Sep 2025 10:30:57 +0000 Subject: [PATCH 1569/1794] i386/tcg/smm_helper: Properly apply DR values on SMM entry / exit do_smm_enter and helper_rsm sets the env->dr, but does not sync the values with cpu_x86_update_dr7. A malicious kernel may control the instruction pointer in SMM by setting a breakpoint on the SMI entry point, and after do_smm_enter cpu->breakpoints contains the stale breakpoint; and because IDT is not reloaded upon SMI entry, the debug exception handler controlled by the malicious kernel is invoked. Fixes: 01df040b5247 ("x86: Debug register emulation (Jan Kiszka)") Reported-by: unvariant.winter@gmail.com Signed-off-by: YiFei Zhu Link: https://lore.kernel.org/r/2bacb9b24e9d337dbe48791aa25d349eb9c52c3a.1758794468.git.zhuyifei@google.com Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- target/i386/tcg/system/smm_helper.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/i386/tcg/system/smm_helper.c b/target/i386/tcg/system/smm_helper.c index 251eb7856ce76..fb028a8272f25 100644 --- a/target/i386/tcg/system/smm_helper.c +++ b/target/i386/tcg/system/smm_helper.c @@ -168,7 +168,7 @@ void do_smm_enter(X86CPU *cpu) env->cr[0] & ~(CR0_PE_MASK | CR0_EM_MASK | CR0_TS_MASK | CR0_PG_MASK)); cpu_x86_update_cr4(env, 0); - env->dr[7] = 0x00000400; + helper_set_dr(env, 7, 0x00000400); cpu_x86_load_seg_cache(env, R_CS, (env->smbase >> 4) & 0xffff, env->smbase, 0xffffffff, @@ -233,8 +233,8 @@ void helper_rsm(CPUX86State *env) env->eip = x86_ldq_phys(cs, sm_state + 0x7f78); cpu_load_eflags(env, x86_ldl_phys(cs, sm_state + 0x7f70), ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK)); - env->dr[6] = x86_ldl_phys(cs, sm_state + 0x7f68); - env->dr[7] = x86_ldl_phys(cs, sm_state + 0x7f60); + helper_set_dr(env, 6, x86_ldl_phys(cs, sm_state + 0x7f68)); + helper_set_dr(env, 7, x86_ldl_phys(cs, sm_state + 0x7f60)); cpu_x86_update_cr4(env, x86_ldl_phys(cs, sm_state + 0x7f48)); cpu_x86_update_cr3(env, x86_ldq_phys(cs, sm_state + 0x7f50)); @@ -268,8 +268,8 @@ void helper_rsm(CPUX86State *env) env->regs[R_EDX] = x86_ldl_phys(cs, sm_state + 0x7fd8); env->regs[R_ECX] = x86_ldl_phys(cs, sm_state + 0x7fd4); env->regs[R_EAX] = x86_ldl_phys(cs, sm_state + 0x7fd0); - env->dr[6] = x86_ldl_phys(cs, sm_state + 0x7fcc); - env->dr[7] = x86_ldl_phys(cs, sm_state + 0x7fc8); + helper_set_dr(env, 6, x86_ldl_phys(cs, sm_state + 0x7fcc)); + helper_set_dr(env, 7, x86_ldl_phys(cs, sm_state + 0x7fc8)); env->tr.selector = x86_ldl_phys(cs, sm_state + 0x7fc4) & 0xffff; env->tr.base = x86_ldl_phys(cs, sm_state + 0x7f64); From 5a2faa0a0a2cbdad4a108a0e122b0e51b9bc94fd Mon Sep 17 00:00:00 2001 From: Thomas Ogrisegg Date: Tue, 15 Jul 2025 23:03:07 +0200 Subject: [PATCH 1570/1794] target/i386: fix x86_64 pushw op For x86_64 a 16 bit push op (pushw) of a memory address would generate a 64 bit store on the stack instead of a 16 bit store. For example: pushw (%rax) behaves like pushq (%rax) which is incorrect. This patch fixes that. Signed-off-by: Thomas Ogrisegg Link: https://lore.kernel.org/r/20250715210307.GA1115@x1.fnord.at Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 51038657f0f99..a50f57dbaab31 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1541,7 +1541,7 @@ static void decode_group4_5(DisasContext *s, CPUX86State *env, X86OpEntry *entry [0x0b] = X86_OP_ENTRYr(CALLF_m, M,p), [0x0c] = X86_OP_ENTRYr(JMP_m, E,f64, zextT0), [0x0d] = X86_OP_ENTRYr(JMPF_m, M,p), - [0x0e] = X86_OP_ENTRYr(PUSH, E,f64), + [0x0e] = X86_OP_ENTRYr(PUSH, E,d64), }; int w = (*b & 1); From 0d22b621b7969eefde3535a0805977a334936fd7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 13 Oct 2025 18:08:12 +0200 Subject: [PATCH 1571/1794] target/i386: fix access to the T bit of the TSS The T bit is bit 0 of the 16-bit word at offset 100 of the TSS. However, accessing it with a 32-bit word is not really correct, because bytes 102-103 contain the I/O map base address (relative to the base of the TSS) and bits 1-15 are reserved. In particular, any task switch to a TSS that has a nonzero I/O map base address is broken. This fixes the eventinj and taskswitch tests in kvm-unit-tests. Cc: qemu-stable@nongnu.org Fixes: ad441b8b791 ("target/i386: implement TSS trap bit", 2025-05-12) Reported-by: Thomas Huth Closes: https://gitlab.com/qemu-project/qemu/-/issues/3101 Tested-by: Thomas Huth Signed-off-by: Paolo Bonzini --- target/i386/tcg/seg_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index 071f3fbd83d24..f49fe851cdffd 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -456,7 +456,7 @@ static void switch_tss_ra(CPUX86State *env, int tss_selector, new_segs[i] = access_ldw(&new, tss_base + (0x48 + i * 4)); } new_ldt = access_ldw(&new, tss_base + 0x60); - new_trap = access_ldl(&new, tss_base + 0x64); + new_trap = access_ldw(&new, tss_base + 0x64) & 1; } else { /* 16 bit */ new_cr3 = 0; From 5142397c79330aab9bef3230991c8ac0c251110f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 13 Oct 2025 18:24:54 +0200 Subject: [PATCH 1572/1794] async: access bottom half flags with qatomic_read Running test-aio-multithread under TSAN reveals data races on bh->flags. Because bottom halves may be scheduled or canceled asynchronously, without taking a lock, adjust aio_compute_bh_timeout() and aio_ctx_check() to use a relaxed read to access the flags. Use an acquire load to ensure that anything that was written prior to qemu_bh_schedule() is visible. Closes: https://gitlab.com/qemu-project/qemu/-/issues/2749 Closes: https://gitlab.com/qemu-project/qemu/-/issues/851 Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- util/async.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/util/async.c b/util/async.c index 2719c629ae976..a736d2cd0d089 100644 --- a/util/async.c +++ b/util/async.c @@ -256,8 +256,9 @@ static int64_t aio_compute_bh_timeout(BHList *head, int timeout) QEMUBH *bh; QSLIST_FOREACH_RCU(bh, head, next) { - if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { - if (bh->flags & BH_IDLE) { + int flags = qatomic_load_acquire(&bh->flags); + if ((flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { + if (flags & BH_IDLE) { /* idle bottom halves will be polled at least * every 10ms */ timeout = 10000000; @@ -335,14 +336,16 @@ aio_ctx_check(GSource *source) aio_notify_accept(ctx); QSLIST_FOREACH_RCU(bh, &ctx->bh_list, next) { - if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { + int flags = qatomic_load_acquire(&bh->flags); + if ((flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { return true; } } QSIMPLEQ_FOREACH(s, &ctx->bh_slice_list, next) { QSLIST_FOREACH_RCU(bh, &s->bh_list, next) { - if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { + int flags = qatomic_load_acquire(&bh->flags); + if ((flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { return true; } } From 58aa1d08bbc406ba3982f32ffb1bef0ff4f8f369 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 13 Oct 2025 18:34:28 +0200 Subject: [PATCH 1573/1794] target/i386: user: do not set up a valid LDT on reset In user-mode emulation, QEMU uses the default setting of the LDT base and limit, which places it at the bottom 64K of virtual address space. However, by default there is no LDT at all in Linux processes, and therefore the limit should be 0. This is visible as a NULL pointer dereference in LSL and LAR instructions when they try to read the LDT at an unmapped address. Resolves: #1376 Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 4f99cbc5c0b70..455caff6b2305 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8649,7 +8649,11 @@ static void x86_cpu_reset_hold(Object *obj, ResetType type) env->idt.limit = 0xffff; env->gdt.limit = 0xffff; +#if defined(CONFIG_USER_ONLY) + env->ldt.limit = 0; +#else env->ldt.limit = 0xffff; +#endif env->ldt.flags = DESC_P_MASK | (2 << DESC_TYPE_SHIFT); env->tr.limit = 0xffff; env->tr.flags = DESC_P_MASK | (11 << DESC_TYPE_SHIFT); From 2808652b4fae100dd1a46344c686db6e6136784e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 13 Oct 2025 12:59:28 +0200 Subject: [PATCH 1574/1794] monitor: clarify "info accel" help message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for adding "info accelerators", explain that this command is about runtime statistics. Reviewed-by: Daniel P. Berrangé Signed-off-by: Paolo Bonzini --- hmp-commands-info.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index eaaa880c1b303..c2aa40056bb95 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -271,12 +271,12 @@ ERST .name = "accel", .args_type = "", .params = "", - .help = "show accelerator info", + .help = "show accelerator statistics", }, SRST ``info accel`` - Show accelerator info. + Show accelerator statistics. ERST SRST From 71d5babbd6fffc7def1ecbf29f9753e3a2807761 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 13 Oct 2025 12:49:04 +0200 Subject: [PATCH 1575/1794] monitor: generalize query-mshv/"info mshv" to query-accelerators/"info accelerators" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The recently-introduced query-mshv command is a duplicate of query-kvm, and neither provides a full view of which accelerators are supported by a particular binary of QEMU and which is in use. KVM was the first accelerator added to QEMU, predating QOM and TYPE_ACCEL, so it got a pass. But now, instead of adding a badly designed copy, solve the problem completely for all accelerators with a command that provides the whole picture: >> {"execute": "query-accelerators"} << {"return": {"enabled": "tcg", "present": ["kvm", "mshv", "qtest", "tcg", "xen"]}} Cc: Praveen K Paladugu Cc: Magnus Kulke Suggested-by: Markus Armbruster Reviewed-by: Daniel P. Berrangé Signed-off-by: Paolo Bonzini --- hmp-commands-info.hx | 15 ++++++++---- hw/core/machine-hmp-cmds.c | 23 +++++++++++-------- hw/core/machine-qmp-cmds.c | 24 +++++++++++++------ include/monitor/hmp.h | 2 +- qapi/accelerator.json | 47 +++++++++++++++++++++++++++++--------- 5 files changed, 77 insertions(+), 34 deletions(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index c2aa40056bb95..25b4aed51f56d 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -308,16 +308,21 @@ SRST ERST { - .name = "mshv", + .name = "accelerators", .args_type = "", .params = "", - .help = "show MSHV information", - .cmd = hmp_info_mshv, + .help = "show present and enabled information", + .cmd = hmp_info_accelerators, }, SRST - ``info mshv`` - Show MSHV information. + ``info accelerators`` + Show which accelerators are compiled into a QEMU binary, and what accelerator + is in use. For example:: + + kvm qtest [tcg] + + indicates that TCG in use, and that KVM and qtest are also available. ERST { diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index 682ed9f49b836..74a56600be835 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -163,19 +163,22 @@ void hmp_info_kvm(Monitor *mon, const QDict *qdict) qapi_free_KvmInfo(info); } -void hmp_info_mshv(Monitor *mon, const QDict *qdict) +void hmp_info_accelerators(Monitor *mon, const QDict *qdict) { - MshvInfo *info; - - info = qmp_query_mshv(NULL); - monitor_printf(mon, "mshv support: "); - if (info->present) { - monitor_printf(mon, "%s\n", info->enabled ? "enabled" : "disabled"); - } else { - monitor_printf(mon, "not compiled\n"); + AcceleratorInfo *info; + AcceleratorList *accel; + + info = qmp_query_accelerators(NULL); + for (accel = info->present; accel; accel = accel->next) { + char trail = accel->next ? ' ' : '\n'; + if (info->enabled == accel->value) { + monitor_printf(mon, "[%s]%c", Accelerator_str(accel->value), trail); + } else { + monitor_printf(mon, "%s%c", Accelerator_str(accel->value), trail); + } } - qapi_free_MshvInfo(info); + qapi_free_AcceleratorInfo(info); } void hmp_info_uuid(Monitor *mon, const QDict *qdict) diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index e24bf0d97bfc5..51d5c230f7e07 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -31,15 +31,25 @@ #include /* - * QMP query for MSHV + * QMP query for enabled and present accelerators */ -MshvInfo *qmp_query_mshv(Error **errp) +AcceleratorInfo *qmp_query_accelerators(Error **errp) { - MshvInfo *info = g_malloc0(sizeof(*info)); - - info->enabled = mshv_enabled(); - info->present = accel_find("mshv"); - + AcceleratorInfo *info = g_malloc0(sizeof(*info)); + AccelClass *current_class = ACCEL_GET_CLASS(current_accel()); + int i; + + for (i = ACCELERATOR__MAX; i-- > 0; ) { + const char *s = Accelerator_str(i); + AccelClass *this_class = accel_find(s); + + if (this_class) { + QAPI_LIST_PREPEND(info->present, i); + if (this_class == current_class) { + info->enabled = i; + } + } + } return info; } diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index 31bd812e5f418..897dfaa2b6d93 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -24,7 +24,7 @@ strList *hmp_split_at_comma(const char *str); void hmp_info_name(Monitor *mon, const QDict *qdict); void hmp_info_version(Monitor *mon, const QDict *qdict); void hmp_info_kvm(Monitor *mon, const QDict *qdict); -void hmp_info_mshv(Monitor *mon, const QDict *qdict); +void hmp_info_accelerators(Monitor *mon, const QDict *qdict); void hmp_info_status(Monitor *mon, const QDict *qdict); void hmp_info_uuid(Monitor *mon, const QDict *qdict); void hmp_info_chardev(Monitor *mon, const QDict *qdict); diff --git a/qapi/accelerator.json b/qapi/accelerator.json index 664e02724655a..2b920608847cc 100644 --- a/qapi/accelerator.json +++ b/qapi/accelerator.json @@ -56,30 +56,55 @@ 'features': [ 'unstable' ] } ## -# @MshvInfo: +# @Accelerator: # # Information about support for MSHV acceleration # -# @enabled: true if MSHV acceleration is active +# @hvf: Apple Hypervisor.framework # -# @present: true if MSHV acceleration is built into this executable +# @kvm: KVM +# +# @mshv: Hyper-V +# +# @nvmm: NetBSD NVMM +# +# @qtest: QTest (dummy accelerator) +# +# @tcg: TCG (dynamic translation) +# +# @whpx: Windows Hypervisor Platform +# +# @xen: Xen +# +# Since: 10.2.0 +## +{ 'enum': 'Accelerator', 'data': ['hvf', 'kvm', 'mshv', 'nvmm', 'qtest', 'tcg', 'whpx', 'xen'] } + +## +# @AcceleratorInfo: +# +# Information about support for various accelerators +# +# @enabled: the accelerator that is in use +# +# @present: the list of accelerators that are built into this executable # # Since: 10.2.0 ## -{ 'struct': 'MshvInfo', 'data': {'enabled': 'bool', 'present': 'bool'} } +{ 'struct': 'AcceleratorInfo', 'data': {'enabled': 'Accelerator', 'present': ['Accelerator']} } ## -# @query-mshv: +# @query-accelerators: # -# Return information about MSHV acceleration +# Return information about accelerators # -# Returns: @MshvInfo +# Returns: @AcceleratorInfo # -# Since: 10.0.92 +# Since: 10.2.0 # # .. qmp-example:: # -# -> { "execute": "query-mshv" } -# <- { "return": { "enabled": true, "present": true } } +# -> { "execute": "query-accelerators" } +# <- { "return": { "enabled": "mshv", "present": ["kvm", "mshv", "qtest", "tcg"] } } ## -{ 'command': 'query-mshv', 'returns': 'MshvInfo' } +{ 'command': 'query-accelerators', 'returns': 'AcceleratorInfo' } From 665a8035b7e65f5dcb5cdacb3a31c9087cff8684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 06:07:13 +0200 Subject: [PATCH 1576/1794] accel/kvm: Introduce KvmPutState enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Join the 3 KVM_PUT_*_STATE definitions in a single enum. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Link: https://lore.kernel.org/r/20251008040715.81513-3-philmd@linaro.org Signed-off-by: Paolo Bonzini --- include/system/kvm.h | 18 ++++++++++-------- target/arm/kvm.c | 2 +- target/i386/kvm/kvm.c | 6 +++--- target/loongarch/kvm/kvm.c | 4 ++-- target/mips/kvm.c | 6 +++--- target/ppc/kvm.c | 2 +- target/riscv/kvm/kvm-cpu.c | 2 +- target/s390x/kvm/kvm.c | 2 +- 8 files changed, 22 insertions(+), 20 deletions(-) diff --git a/include/system/kvm.h b/include/system/kvm.h index 4fc09e3891005..8f9eecf044c2a 100644 --- a/include/system/kvm.h +++ b/include/system/kvm.h @@ -340,14 +340,16 @@ int kvm_arch_process_async_events(CPUState *cpu); int kvm_arch_get_registers(CPUState *cpu, Error **errp); -/* state subset only touched by the VCPU itself during runtime */ -#define KVM_PUT_RUNTIME_STATE 1 -/* state subset modified during VCPU reset */ -#define KVM_PUT_RESET_STATE 2 -/* full state set, modified during initialization or on vmload */ -#define KVM_PUT_FULL_STATE 3 - -int kvm_arch_put_registers(CPUState *cpu, int level, Error **errp); +typedef enum kvm_put_state { + /* state subset only touched by the VCPU itself during runtime */ + KVM_PUT_RUNTIME_STATE = 1, + /* state subset modified during VCPU reset */ + KVM_PUT_RESET_STATE = 2, + /* full state set, modified during initialization or on vmload */ + KVM_PUT_FULL_STATE = 3, +} KvmPutState; + +int kvm_arch_put_registers(CPUState *cpu, KvmPutState level, Error **errp); int kvm_arch_get_default_type(MachineState *ms); diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 4f769d69b38d5..0d57081e69fbb 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -2123,7 +2123,7 @@ static int kvm_arch_put_sve(CPUState *cs) return 0; } -int kvm_arch_put_registers(CPUState *cs, int level, Error **errp) +int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp) { uint64_t val; uint32_t fpr; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index e40e374b046e5..309f043373cd6 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -3924,7 +3924,7 @@ static void kvm_init_msrs(X86CPU *cpu) assert(kvm_buf_set_msrs(cpu) == 0); } -static int kvm_put_msrs(X86CPU *cpu, int level) +static int kvm_put_msrs(X86CPU *cpu, KvmPutState level) { CPUX86State *env = &cpu->env; int i; @@ -5044,7 +5044,7 @@ static int kvm_get_apic(X86CPU *cpu) return 0; } -static int kvm_put_vcpu_events(X86CPU *cpu, int level) +static int kvm_put_vcpu_events(X86CPU *cpu, KvmPutState level) { CPUState *cs = CPU(cpu); CPUX86State *env = &cpu->env; @@ -5287,7 +5287,7 @@ static int kvm_get_nested_state(X86CPU *cpu) return ret; } -int kvm_arch_put_registers(CPUState *cpu, int level, Error **errp) +int kvm_arch_put_registers(CPUState *cpu, KvmPutState level, Error **errp) { X86CPU *x86_cpu = X86_CPU(cpu); int ret; diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index e5ea2dba9daa0..4e4f4e79f6472 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -325,7 +325,7 @@ static int kvm_loongarch_get_csr(CPUState *cs) return ret; } -static int kvm_loongarch_put_csr(CPUState *cs, int level) +static int kvm_loongarch_put_csr(CPUState *cs, KvmPutState level) { int ret = 0; CPULoongArchState *env = cpu_env(cs); @@ -763,7 +763,7 @@ int kvm_arch_get_registers(CPUState *cs, Error **errp) return ret; } -int kvm_arch_put_registers(CPUState *cs, int level, Error **errp) +int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp) { int ret; static int once; diff --git a/target/mips/kvm.c b/target/mips/kvm.c index 450947c3fa56e..912cd5dfa0e06 100644 --- a/target/mips/kvm.c +++ b/target/mips/kvm.c @@ -590,7 +590,7 @@ static void kvm_mips_update_state(void *opaque, bool running, RunState state) } } -static int kvm_mips_put_fpu_registers(CPUState *cs, int level) +static int kvm_mips_put_fpu_registers(CPUState *cs, KvmPutState level) { CPUMIPSState *env = cpu_env(cs); int err, ret = 0; @@ -749,7 +749,7 @@ static int kvm_mips_get_fpu_registers(CPUState *cs) } -static int kvm_mips_put_cp0_registers(CPUState *cs, int level) +static int kvm_mips_put_cp0_registers(CPUState *cs, KvmPutState level) { CPUMIPSState *env = cpu_env(cs); int err, ret = 0; @@ -1177,7 +1177,7 @@ static int kvm_mips_get_cp0_registers(CPUState *cs) return ret; } -int kvm_arch_put_registers(CPUState *cs, int level, Error **errp) +int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp) { CPUMIPSState *env = cpu_env(cs); struct kvm_regs regs; diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 2521ff65c6c35..cd60893a17d86 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -907,7 +907,7 @@ int kvmppc_put_books_sregs(PowerPCCPU *cpu) return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_SREGS, &sregs); } -int kvm_arch_put_registers(CPUState *cs, int level, Error **errp) +int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 187c2c9501e35..75ca3fb9fd9a4 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1369,7 +1369,7 @@ int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state) return 0; } -int kvm_arch_put_registers(CPUState *cs, int level, Error **errp) +int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp) { int ret = 0; diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 491cc5f9756ac..916dac1f14ec9 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -468,7 +468,7 @@ static int can_sync_regs(CPUState *cs, int regs) #define KVM_SYNC_REQUIRED_REGS (KVM_SYNC_GPRS | KVM_SYNC_ACRS | \ KVM_SYNC_CRS | KVM_SYNC_PREFIX) -int kvm_arch_put_registers(CPUState *cs, int level, Error **errp) +int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp) { CPUS390XState *env = cpu_env(cs); struct kvm_fpu fpu = {}; From 0de36ecb2b0e21a4bf610c2934b250a577490c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 06:07:14 +0200 Subject: [PATCH 1577/1794] accel/kvm: Factor kvm_cpu_synchronize_put() out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The same code is duplicated 3 times: factor a common method. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Link: https://lore.kernel.org/r/20251008040715.81513-4-philmd@linaro.org Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 47 ++++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 58802f7c3cc15..56031925c4e9e 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2937,22 +2937,32 @@ void kvm_cpu_synchronize_state(CPUState *cpu) } } -static void do_kvm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) +static bool kvm_cpu_synchronize_put(CPUState *cpu, KvmPutState state, + const char *desc) { Error *err = NULL; - int ret = kvm_arch_put_registers(cpu, KVM_PUT_RESET_STATE, &err); + int ret = kvm_arch_put_registers(cpu, state, &err); if (ret) { if (err) { - error_reportf_err(err, "Restoring resisters after reset: "); + error_reportf_err(err, "Restoring resisters %s: ", desc); } else { - error_report("Failed to put registers after reset: %s", + error_report("Failed to put registers %s: %s", desc, strerror(-ret)); } - cpu_dump_state(cpu, stderr, CPU_DUMP_CODE); - vm_stop(RUN_STATE_INTERNAL_ERROR); + return false; } cpu->vcpu_dirty = false; + + return true; +} + +static void do_kvm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) +{ + if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_RESET_STATE, "after reset")) { + cpu_dump_state(cpu, stderr, CPU_DUMP_CODE); + vm_stop(RUN_STATE_INTERNAL_ERROR); + } } void kvm_cpu_synchronize_post_reset(CPUState *cpu) @@ -2966,19 +2976,9 @@ void kvm_cpu_synchronize_post_reset(CPUState *cpu) static void do_kvm_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) { - Error *err = NULL; - int ret = kvm_arch_put_registers(cpu, KVM_PUT_FULL_STATE, &err); - if (ret) { - if (err) { - error_reportf_err(err, "Putting registers after init: "); - } else { - error_report("Failed to put registers after init: %s", - strerror(-ret)); - } + if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_FULL_STATE, "after init")) { exit(1); } - - cpu->vcpu_dirty = false; } void kvm_cpu_synchronize_post_init(CPUState *cpu) @@ -3168,20 +3168,11 @@ int kvm_cpu_exec(CPUState *cpu) MemTxAttrs attrs; if (cpu->vcpu_dirty) { - Error *err = NULL; - ret = kvm_arch_put_registers(cpu, KVM_PUT_RUNTIME_STATE, &err); - if (ret) { - if (err) { - error_reportf_err(err, "Putting registers after init: "); - } else { - error_report("Failed to put registers after init: %s", - strerror(-ret)); - } + if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_RUNTIME_STATE, + "at runtime")) { ret = -1; break; } - - cpu->vcpu_dirty = false; } kvm_arch_pre_run(cpu, run); From 818231bc6d24859fde179d1ab022bd932f409d10 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 17 Sep 2025 12:10:45 +0200 Subject: [PATCH 1578/1794] rust: bql: add BqlRefCell::get_mut() This method is rarely useful in QEMU due to the pervasiveness of shared references, but add it for when a &mut BqlRefCell<> is used. Signed-off-by: Paolo Bonzini --- rust/bql/src/cell.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/rust/bql/src/cell.rs b/rust/bql/src/cell.rs index 24ab294b60d08..54cfe6145c534 100644 --- a/rust/bql/src/cell.rs +++ b/rust/bql/src/cell.rs @@ -580,6 +580,23 @@ impl BqlRefCell { } } + /// Returns a mutable reference to the underlying data in this cell, + /// while the owner already has a mutable reference to the cell. + /// + /// # Examples + /// + /// ``` + /// use bql::BqlRefCell; + /// + /// let mut c = BqlRefCell::new(5); + /// + /// *c.get_mut() = 10; + /// ``` + #[inline] + pub const fn get_mut(&mut self) -> &mut T { + self.value.get_mut() + } + /// Returns a raw pointer to the underlying data in this cell. /// /// # Examples From 5c776a767703f9f6cd6ee87752e794d2cbab1165 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Sep 2025 10:13:29 +0200 Subject: [PATCH 1579/1794] rust: migration: do not pass raw pointer to VMStateDescription::fields Pass a slice instead; a function that accepts a raw pointer should arguably be declared as unsafe. But since it is now much easier to forget vmstate_fields!, validate the value (at least to some extent) before passing it to C. (Unfortunately, doing the same for subsections would require const ptr::is_null(), which is only stable in Rust 1.84). Suggested-by: Zhao Liu Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/migration/src/vmstate.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs index 5f5708ad39ebf..2e320262f0638 100644 --- a/rust/migration/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -425,7 +425,7 @@ macro_rules! vmstate_fields { ..::common::zeroable::Zeroable::ZERO } ]; - _FIELDS.as_ptr() + _FIELDS }} } @@ -677,8 +677,11 @@ impl VMStateDescriptionBuilder { } #[must_use] - pub const fn fields(mut self, fields: *const VMStateField) -> Self { - self.0.fields = fields; + pub const fn fields(mut self, fields: &'static [VMStateField]) -> Self { + if fields[fields.len() - 1].flags.0 != VMStateFlags::VMS_END.0 { + panic!("fields are not terminated, use vmstate_fields!"); + } + self.0.fields = fields.as_ptr(); self } From 64bce66d6f142272415de5807f38c2b8a884c4c2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Sep 2025 10:32:23 +0200 Subject: [PATCH 1580/1794] rust: migration: do not store raw pointers into VMStateSubsectionsWrapper Raw pointers were used to insert a NULL one at the end of the array. However, Option<&...> has the same layout and does not remove Sync from the type of the array. As an extra benefit, this enables validation of the terminator of the subsection array, because is_null() in const context would not be stable until Rust 1.84. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/migration/src/vmstate.rs | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs index 2e320262f0638..a6ee7e938531d 100644 --- a/rust/migration/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -470,33 +470,21 @@ macro_rules! impl_vmstate_struct { }; } -/// A transparent wrapper type for the `subsections` field of -/// [`VMStateDescription`]. -/// -/// This is necessary to be able to declare subsection descriptions as statics, -/// because the only way to implement `Sync` for a foreign type (and `*const` -/// pointers are foreign types in Rust) is to create a wrapper struct and -/// `unsafe impl Sync` for it. -/// -/// This struct is used in the -/// [`vm_state_subsections`](crate::vmstate_subsections) macro implementation. -#[repr(transparent)] -pub struct VMStateSubsectionsWrapper(pub &'static [*const crate::bindings::VMStateDescription]); - -unsafe impl Sync for VMStateSubsectionsWrapper {} +/// The type returned by [`vmstate_subsections!`](crate::vmstate_subsections). +pub type VMStateSubsections = &'static [Option<&'static crate::bindings::VMStateDescription>]; /// Helper macro to declare a list of subsections ([`VMStateDescription`]) /// into a static and return a pointer to the array of pointers it created. #[macro_export] macro_rules! vmstate_subsections { ($($subsection:expr),*$(,)*) => {{ - static _SUBSECTIONS: $crate::vmstate::VMStateSubsectionsWrapper = $crate::vmstate::VMStateSubsectionsWrapper(&[ + static _SUBSECTIONS: $crate::vmstate::VMStateSubsections = &[ $({ static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection.get(); - ::core::ptr::addr_of!(_SUBSECTION) + Some(&_SUBSECTION) }),*, - ::core::ptr::null() - ]); + None, + ]; &_SUBSECTIONS }} } @@ -686,8 +674,9 @@ impl VMStateDescriptionBuilder { } #[must_use] - pub const fn subsections(mut self, subs: &'static VMStateSubsectionsWrapper) -> Self { - self.0.subsections = subs.0.as_ptr(); + pub const fn subsections(mut self, subs: &'static VMStateSubsections) -> Self { + let subs: *const Option<&bindings::VMStateDescription> = subs.as_ptr(); + self.0.subsections = subs.cast::<*const bindings::VMStateDescription>(); self } From 5b4fa9780728ba526f0529199ef90d6132a3faf2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Sep 2025 10:41:12 +0200 Subject: [PATCH 1581/1794] rust: migration: validate termination of subsection arrays For consistency with fields(), validate the value (at least to some extent) before passing it to C. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/migration/src/vmstate.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs index a6ee7e938531d..6b0f96c4da8d5 100644 --- a/rust/migration/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -675,6 +675,9 @@ impl VMStateDescriptionBuilder { #[must_use] pub const fn subsections(mut self, subs: &'static VMStateSubsections) -> Self { + if subs[subs.len() - 1].is_some() { + panic!("subsections are not terminated, use vmstate_subsections!"); + } let subs: *const Option<&bindings::VMStateDescription> = subs.as_ptr(); self.0.subsections = subs.cast::<*const bindings::VMStateDescription>(); self From 8999ca00a4b69b0b7332bf3ae2fc17d473923793 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 17 Sep 2025 12:37:59 +0200 Subject: [PATCH 1582/1794] rust: migration: extract vmstate_fields_ref This is useful when building a VMState for generic structs, because you have to avoid nested statics. Using vmstate_fields! will fail in the likely case where the _FIELDS static uses Self from an outer item, because that is forbidden. The separate macros are needed because you cannot just do .fields(vmstate_fields_ref! { vmstate_of!(PL011State, clock), }) The value returned by vmstate_fields_ref! is not promoted to static, which is unfortunate but intentional (https://github.com/rust-lang/rust/issues/60502): error[E0716]: temporary value dropped while borrowed --> rust/hw/char/pl011/libpl011.rlib.p/structured/device.rs:743:17 | 738 | / VMStateDescriptionBuilder::::new() 739 | | .name(c"pl011/clock") 740 | | .version_id(1) 741 | | .minimum_version_id(1) 742 | | .needed(&PL011State::clock_needed) 743 | | .fields(vmstate_fields_ref! { | | _________________^ 744 | || vmstate_of!(PL011State, clock), 745 | || }) | ||_________^- argument requires that borrow lasts for `'static` | |_________| | creates a temporary value which is freed while still in use 746 | .build(); | - temporary value is freed at the end of this statement Thus it is necessary to use the "static", whether explicitly or hidden by vmstate_fields. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/migration/src/vmstate.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs index 6b0f96c4da8d5..59e665f6c3ada 100644 --- a/rust/migration/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -412,19 +412,30 @@ macro_rules! vmstate_exist_fn { }}; } +/// Add a terminator to the fields in the arguments, and return +/// a reference to the resulting array of values. +#[macro_export] +macro_rules! vmstate_fields_ref { + ($($field:expr),*$(,)*) => { + &[ + $($field),*, + $crate::bindings::VMStateField { + flags: $crate::bindings::VMStateFlags::VMS_END, + ..::common::zeroable::Zeroable::ZERO + } + ] + } +} + /// Helper macro to declare a list of /// ([`VMStateField`](`crate::bindings::VMStateField`)) into a static and return /// a pointer to the array of values it created. #[macro_export] macro_rules! vmstate_fields { ($($field:expr),*$(,)*) => {{ - static _FIELDS: &[$crate::bindings::VMStateField] = &[ + static _FIELDS: &[$crate::bindings::VMStateField] = $crate::vmstate_fields_ref!( $($field),*, - $crate::bindings::VMStateField { - flags: $crate::bindings::VMStateFlags::VMS_END, - ..::common::zeroable::Zeroable::ZERO - } - ]; + ); _FIELDS }} } From 4526418affcd536748a344bdba9a6913a5f7e135 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 Oct 2025 10:24:54 +0200 Subject: [PATCH 1583/1794] rust: move VMState from bql to migration The high-level wrapper Migratable will contain a BqlCell, which would introduce a circular dependency betwen the bql and migration crates. Move the implementation of VMState for cells to "migration", together with the implementation for std types. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/Cargo.lock | 2 +- rust/bql/Cargo.toml | 1 - rust/bql/meson.build | 1 - rust/bql/src/cell.rs | 6 ------ rust/meson.build | 2 +- rust/migration/Cargo.toml | 1 + rust/migration/meson.build | 4 ++-- rust/migration/src/vmstate.rs | 2 ++ 8 files changed, 7 insertions(+), 12 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 110851334905c..5c2f8ea9240c7 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -59,7 +59,6 @@ name = "bql" version = "0.1.0" dependencies = [ "glib-sys", - "migration", ] [[package]] @@ -198,6 +197,7 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" name = "migration" version = "0.1.0" dependencies = [ + "bql", "common", "glib-sys", "util", diff --git a/rust/bql/Cargo.toml b/rust/bql/Cargo.toml index d5177e5f8e2a9..8fd8131102848 100644 --- a/rust/bql/Cargo.toml +++ b/rust/bql/Cargo.toml @@ -13,7 +13,6 @@ repository.workspace = true rust-version.workspace = true [dependencies] -migration = { path = "../migration" } glib-sys.workspace = true [features] diff --git a/rust/bql/meson.build b/rust/bql/meson.build index 22d7c9b8776f1..091372dd7b660 100644 --- a/rust/bql/meson.build +++ b/rust/bql/meson.build @@ -37,7 +37,6 @@ _bql_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _bql_cfg, - link_with: [_migration_rs], dependencies: [glib_sys_rs], ) diff --git a/rust/bql/src/cell.rs b/rust/bql/src/cell.rs index 54cfe6145c534..8ade7db629cfe 100644 --- a/rust/bql/src/cell.rs +++ b/rust/bql/src/cell.rs @@ -151,8 +151,6 @@ use std::{ ptr::NonNull, }; -use migration::impl_vmstate_transparent; - /// A mutable memory location that is protected by the Big QEMU Lock. /// /// # Memory layout @@ -364,8 +362,6 @@ impl BqlCell { } } -impl_vmstate_transparent!(crate::cell::BqlCell where T: VMState); - /// A mutable memory location with dynamically checked borrow rules, /// protected by the Big QEMU Lock. /// @@ -691,8 +687,6 @@ impl From for BqlRefCell { } } -impl_vmstate_transparent!(crate::cell::BqlRefCell where T: VMState); - struct BorrowRef<'b> { borrow: &'b Cell, } diff --git a/rust/meson.build b/rust/meson.build index 6ba075c8c7162..76e10699b371a 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -29,8 +29,8 @@ subdir('qemu-macros') subdir('common') subdir('bits') subdir('util') -subdir('migration') subdir('bql') +subdir('migration') subdir('qom') subdir('system') subdir('chardev') diff --git a/rust/migration/Cargo.toml b/rust/migration/Cargo.toml index 94504f3625c92..b995c4c8c8882 100644 --- a/rust/migration/Cargo.toml +++ b/rust/migration/Cargo.toml @@ -13,6 +13,7 @@ repository.workspace = true rust-version.workspace = true [dependencies] +bql = { path = "../bql" } common = { path = "../common" } util = { path = "../util" } glib-sys.workspace = true diff --git a/rust/migration/meson.build b/rust/migration/meson.build index 18be65c92cf00..845136239e8aa 100644 --- a/rust/migration/meson.build +++ b/rust/migration/meson.build @@ -37,12 +37,12 @@ _migration_rs = static_library( ), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', - link_with: [_util_rs], + link_with: [_util_rs, _bql_rs], dependencies: [common_rs, glib_sys_rs], ) migration_rs = declare_dependency(link_with: [_migration_rs], - dependencies: [migration, qemuutil]) + dependencies: [bql_rs, migration, qemuutil]) # Doctests are essentially integration tests, so they need the same dependencies. # Note that running them requires the object files for C code, so place them diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs index 59e665f6c3ada..445fe7fbc08a4 100644 --- a/rust/migration/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -276,6 +276,8 @@ macro_rules! impl_vmstate_transparent { }; } +impl_vmstate_transparent!(bql::BqlCell where T: VMState); +impl_vmstate_transparent!(bql::BqlRefCell where T: VMState); impl_vmstate_transparent!(std::cell::Cell where T: VMState); impl_vmstate_transparent!(std::cell::UnsafeCell where T: VMState); impl_vmstate_transparent!(std::pin::Pin where T: VMState); From 44a9d1b86c06ea955e4720ae5de8c130ff5719bc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 15 Sep 2025 11:34:36 +0200 Subject: [PATCH 1584/1794] rust: migration: add high-level migration wrappers Instead of dealing with pre/post callbacks, allow devices to implement a snapshot/restore mechanism; this has two main advantages: - it can be easily implemented via procedural macros - there can be generic implementations to deal with various kinds of interior-mutable containers, from BqlRefCell to Mutex, so that C code does not see Rust concepts such as Mutex<>. Using it is easy; you can implement the snapshot/restore trait ToMigrationState and declare your state like: regs: Migratable> Migratable<> allows dereferencing to the underlying object with no run-time cost. Note that Migratable<> actually does not accept ToMigrationState, only the similar ToMigrationStateShared trait that the user will mostly not care about. This is required by the fact that pre/post callbacks take a &self, and ensures that the argument is a Mutex or BqlRefCell (including an array or Arc<> thereof). Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 1 + rust/migration/meson.build | 1 + rust/migration/src/lib.rs | 3 + rust/migration/src/migratable.rs | 434 +++++++++++++++++++++++++++++++ 4 files changed, 439 insertions(+) create mode 100644 rust/migration/src/migratable.rs diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 2f0ab2e282105..79c26d9d165cd 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -155,6 +155,7 @@ module status ``hwcore::irq`` complete ``hwcore::qdev`` stable ``hwcore::sysbus`` stable +``migration::migratable`` proof of concept ``migration::vmstate`` stable ``qom`` stable ``system::memory`` stable diff --git a/rust/migration/meson.build b/rust/migration/meson.build index 845136239e8aa..0d25455baa972 100644 --- a/rust/migration/meson.build +++ b/rust/migration/meson.build @@ -31,6 +31,7 @@ _migration_rs = static_library( [ 'src/lib.rs', 'src/bindings.rs', + 'src/migratable.rs', 'src/vmstate.rs', ], {'.' : _migration_bindings_inc_rs}, diff --git a/rust/migration/src/lib.rs b/rust/migration/src/lib.rs index 5f51dde440617..efe9896b61995 100644 --- a/rust/migration/src/lib.rs +++ b/rust/migration/src/lib.rs @@ -2,5 +2,8 @@ pub mod bindings; +pub mod migratable; +pub use migratable::*; + pub mod vmstate; pub use vmstate::*; diff --git a/rust/migration/src/migratable.rs b/rust/migration/src/migratable.rs new file mode 100644 index 0000000000000..46e533c16d133 --- /dev/null +++ b/rust/migration/src/migratable.rs @@ -0,0 +1,434 @@ +// Copyright 2025 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::{ + fmt, + mem::size_of, + ptr::{self, addr_of, NonNull}, + sync::{Arc, Mutex}, +}; + +use bql::{BqlCell, BqlRefCell}; +use common::Zeroable; + +use crate::{ + bindings, vmstate_fields_ref, vmstate_of, InvalidError, VMState, VMStateDescriptionBuilder, +}; + +/// Enables QEMU migration support even when a type is wrapped with +/// synchronization primitives (like `Mutex`) that the C migration +/// code cannot directly handle. The trait provides methods to +/// extract essential state for migration and restore it after +/// migration completes. +/// +/// On top of extracting data from synchronization wrappers during save +/// and restoring it during load, it's also possible to use `ToMigrationState` +/// to convert runtime representations to migration-safe formats. +/// +/// # Examples +/// +/// ``` +/// use bql::BqlCell; +/// use migration::{InvalidError, ToMigrationState, VMState}; +/// # use migration::VMStateField; +/// +/// # #[derive(Debug, PartialEq, Eq)] +/// struct DeviceState { +/// counter: BqlCell, +/// enabled: bool, +/// } +/// +/// # #[derive(Debug)] +/// #[derive(Default)] +/// struct DeviceMigrationState { +/// counter: u32, +/// enabled: bool, +/// } +/// +/// # unsafe impl VMState for DeviceMigrationState { +/// # const BASE: VMStateField = ::common::Zeroable::ZERO; +/// # } +/// impl ToMigrationState for DeviceState { +/// type Migrated = DeviceMigrationState; +/// +/// fn snapshot_migration_state( +/// &self, +/// target: &mut Self::Migrated, +/// ) -> Result<(), InvalidError> { +/// target.counter = self.counter.get(); +/// target.enabled = self.enabled; +/// Ok(()) +/// } +/// +/// fn restore_migrated_state_mut( +/// &mut self, +/// source: Self::Migrated, +/// _version_id: u8, +/// ) -> Result<(), InvalidError> { +/// self.counter.set(source.counter); +/// self.enabled = source.enabled; +/// Ok(()) +/// } +/// } +/// # bql::start_test(); +/// # let dev = DeviceState { counter: 10.into(), enabled: true }; +/// # let mig = dev.to_migration_state().unwrap(); +/// # assert!(matches!(*mig, DeviceMigrationState { counter: 10, enabled: true })); +/// # let mut dev2 = DeviceState { counter: 42.into(), enabled: false }; +/// # dev2.restore_migrated_state_mut(*mig, 1).unwrap(); +/// # assert_eq!(dev2, dev); +/// ``` +pub trait ToMigrationState { + /// The type used to represent the migrated state. + type Migrated: Default + VMState; + + /// Capture the current state into a migration-safe format, failing + /// if the state cannot be migrated. + fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError>; + + /// Restores state from a migrated representation, failing if the + /// state cannot be restored. + fn restore_migrated_state_mut( + &mut self, + source: Self::Migrated, + version_id: u8, + ) -> Result<(), InvalidError>; + + /// Convenience method to combine allocation and state capture + /// into a single operation. + fn to_migration_state(&self) -> Result, InvalidError> { + let mut migrated = Box::::default(); + self.snapshot_migration_state(&mut migrated)?; + Ok(migrated) + } +} + +// Implementations for primitive types. Do not use a blanket implementation +// for all Copy types, because [T; N] is Copy if T is Copy; that would conflict +// with the below implementation for arrays. +macro_rules! impl_for_primitive { + ($($t:ty),*) => { + $( + impl ToMigrationState for $t { + type Migrated = Self; + + fn snapshot_migration_state( + &self, + target: &mut Self::Migrated, + ) -> Result<(), InvalidError> { + *target = *self; + Ok(()) + } + + fn restore_migrated_state_mut( + &mut self, + source: Self::Migrated, + _version_id: u8, + ) -> Result<(), InvalidError> { + *self = source; + Ok(()) + } + } + )* + }; +} + +impl_for_primitive!(u8, u16, u32, u64, i8, i16, i32, i64, bool); + +impl ToMigrationState for [T; N] +where + [T::Migrated; N]: Default, +{ + type Migrated = [T::Migrated; N]; + + fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError> { + for (item, target_item) in self.iter().zip(target.iter_mut()) { + item.snapshot_migration_state(target_item)?; + } + Ok(()) + } + + fn restore_migrated_state_mut( + &mut self, + source: Self::Migrated, + version_id: u8, + ) -> Result<(), InvalidError> { + for (item, source_item) in self.iter_mut().zip(source) { + item.restore_migrated_state_mut(source_item, version_id)?; + } + Ok(()) + } +} + +impl ToMigrationState for Mutex { + type Migrated = T::Migrated; + + fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError> { + self.lock().unwrap().snapshot_migration_state(target) + } + + fn restore_migrated_state_mut( + &mut self, + source: Self::Migrated, + version_id: u8, + ) -> Result<(), InvalidError> { + self.get_mut() + .unwrap() + .restore_migrated_state_mut(source, version_id) + } +} + +impl ToMigrationState for BqlRefCell { + type Migrated = T::Migrated; + + fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError> { + self.borrow().snapshot_migration_state(target) + } + + fn restore_migrated_state_mut( + &mut self, + source: Self::Migrated, + version_id: u8, + ) -> Result<(), InvalidError> { + self.get_mut() + .restore_migrated_state_mut(source, version_id) + } +} + +/// Extension trait for types that support migration state restoration +/// through interior mutability. +/// +/// This trait extends [`ToMigrationState`] for types that can restore +/// their state without requiring mutable access. While user structs +/// will generally use `ToMigrationState`, the device will have multiple +/// references and therefore the device struct has to employ an interior +/// mutability wrapper like [`Mutex`] or [`BqlRefCell`]. +/// +/// Anything that implements this trait can in turn be used within +/// [`Migratable`], which makes no assumptions on how to achieve mutable +/// access to the runtime state. +/// +/// # Examples +/// +/// ``` +/// use std::sync::Mutex; +/// +/// use migration::ToMigrationStateShared; +/// +/// let device_state = Mutex::new(42); +/// // Can restore without &mut access +/// device_state.restore_migrated_state(100, 1).unwrap(); +/// assert_eq!(*device_state.lock().unwrap(), 100); +/// ``` +pub trait ToMigrationStateShared: ToMigrationState { + /// Restores state from a migrated representation to an interior-mutable + /// object. Similar to `restore_migrated_state_mut`, but requires a + /// shared reference; therefore it can be used to restore a device's + /// state even though devices have multiple references to them. + fn restore_migrated_state( + &self, + source: Self::Migrated, + version_id: u8, + ) -> Result<(), InvalidError>; +} + +impl ToMigrationStateShared for [T; N] +where + [T::Migrated; N]: Default, +{ + fn restore_migrated_state( + &self, + source: Self::Migrated, + version_id: u8, + ) -> Result<(), InvalidError> { + for (item, source_item) in self.iter().zip(source) { + item.restore_migrated_state(source_item, version_id)?; + } + Ok(()) + } +} + +// Arc requires the contained object to be interior-mutable +impl ToMigrationState for Arc { + type Migrated = T::Migrated; + + fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError> { + (**self).snapshot_migration_state(target) + } + + fn restore_migrated_state_mut( + &mut self, + source: Self::Migrated, + version_id: u8, + ) -> Result<(), InvalidError> { + (**self).restore_migrated_state(source, version_id) + } +} + +impl ToMigrationStateShared for Arc { + fn restore_migrated_state( + &self, + source: Self::Migrated, + version_id: u8, + ) -> Result<(), InvalidError> { + (**self).restore_migrated_state(source, version_id) + } +} + +// Interior-mutable types. Note how they only require ToMigrationState for +// the inner type! + +impl ToMigrationStateShared for Mutex { + fn restore_migrated_state( + &self, + source: Self::Migrated, + version_id: u8, + ) -> Result<(), InvalidError> { + self.lock() + .unwrap() + .restore_migrated_state_mut(source, version_id) + } +} + +impl ToMigrationStateShared for BqlRefCell { + fn restore_migrated_state( + &self, + source: Self::Migrated, + version_id: u8, + ) -> Result<(), InvalidError> { + self.borrow_mut() + .restore_migrated_state_mut(source, version_id) + } +} + +/// A wrapper that enables QEMU migration for types with shared state. +/// +/// `Migratable` provides a bridge between Rust types that use interior +/// mutability (like `Mutex`) and QEMU's C-based migration infrastructure. +/// It manages the lifecycle of migration state and provides automatic +/// conversion between runtime and migration representations. +/// +/// ```ignore +/// # use std::sync::Mutex; +/// # use migration::Migratable; +/// +/// pub struct DeviceRegs { +/// status: u32, +/// } +/// +/// pub struct SomeDevice { +/// // ... +/// registers: Migratable>, +/// } +/// ``` +#[repr(C)] +pub struct Migratable { + /// Pointer to migration state, valid only during migration operations. + /// C vmstate does not support NULL pointers, so no `Option>`. + migration_state: BqlCell<*mut T::Migrated>, + + /// The runtime state that can be accessed during normal operation + runtime_state: T, +} + +impl std::ops::Deref for Migratable { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.runtime_state + } +} + +impl std::ops::DerefMut for Migratable { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.runtime_state + } +} + +impl Migratable { + /// Creates a new `Migratable` wrapper around the given runtime state. + /// + /// # Returns + /// A new `Migratable` instance ready for use and migration + pub fn new(runtime_state: T) -> Self { + Self { + migration_state: BqlCell::new(ptr::null_mut()), + runtime_state, + } + } + + fn pre_save(&self) -> Result<(), InvalidError> { + let state = self.runtime_state.to_migration_state()?; + self.migration_state.set(Box::into_raw(state)); + Ok(()) + } + + fn post_save(&self) -> Result<(), InvalidError> { + let state = unsafe { Box::from_raw(self.migration_state.replace(ptr::null_mut())) }; + drop(state); + Ok(()) + } + + fn pre_load(&self) -> Result<(), InvalidError> { + self.migration_state + .set(Box::into_raw(Box::::default())); + Ok(()) + } + + fn post_load(&self, version_id: u8) -> Result<(), InvalidError> { + let state = unsafe { Box::from_raw(self.migration_state.replace(ptr::null_mut())) }; + self.runtime_state + .restore_migrated_state(*state, version_id) + } +} + +impl fmt::Debug for Migratable +where + T::Migrated: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut struct_f = f.debug_struct("Migratable"); + struct_f.field("runtime_state", &self.runtime_state); + + let state = NonNull::new(self.migration_state.get()).map(|x| unsafe { x.as_ref() }); + struct_f.field("migration_state", &state); + struct_f.finish() + } +} + +impl Default for Migratable { + fn default() -> Self { + Self::new(T::default()) + } +} + +impl Migratable { + const FIELD: bindings::VMStateField = vmstate_of!(Self, migration_state); + + const FIELDS: &[bindings::VMStateField] = vmstate_fields_ref! { + Migratable::::FIELD + }; + + const VMSD: &'static bindings::VMStateDescription = VMStateDescriptionBuilder::::new() + .version_id(1) + .minimum_version_id(1) + .pre_save(&Self::pre_save) + .pre_load(&Self::pre_load) + .post_save(&Self::post_save) + .post_load(&Self::post_load) + .fields(Self::FIELDS) + .build() + .as_ref(); +} + +unsafe impl VMState for Migratable { + const BASE: bindings::VMStateField = { + bindings::VMStateField { + vmsd: addr_of!(*Self::VMSD), + size: size_of::(), + flags: bindings::VMStateFlags::VMS_STRUCT, + ..Zeroable::ZERO + } + }; +} From 29cf500e3b489021219dfdb90c2abc6f275c5775 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 Oct 2025 10:25:07 +0200 Subject: [PATCH 1585/1794] rust: qemu-macros: add ToMigrationState derive macro Add a macro that recursively builds the "migrated" version of a struct. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/Cargo.lock | 1 + rust/migration/Cargo.toml | 1 + rust/migration/meson.build | 2 +- rust/migration/src/lib.rs | 2 + rust/migration/src/migratable.rs | 12 +- rust/qemu-macros/src/lib.rs | 88 +++++++ rust/qemu-macros/src/migration_state.rs | 298 ++++++++++++++++++++++++ rust/qemu-macros/src/tests.rs | 113 ++++++++- 8 files changed, 512 insertions(+), 5 deletions(-) create mode 100644 rust/qemu-macros/src/migration_state.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 5c2f8ea9240c7..0c1df625df1de 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -200,6 +200,7 @@ dependencies = [ "bql", "common", "glib-sys", + "qemu_macros", "util", ] diff --git a/rust/migration/Cargo.toml b/rust/migration/Cargo.toml index b995c4c8c8882..415457496d647 100644 --- a/rust/migration/Cargo.toml +++ b/rust/migration/Cargo.toml @@ -15,6 +15,7 @@ rust-version.workspace = true [dependencies] bql = { path = "../bql" } common = { path = "../common" } +qemu_macros = { path = "../qemu-macros" } util = { path = "../util" } glib-sys.workspace = true diff --git a/rust/migration/meson.build b/rust/migration/meson.build index 0d25455baa972..444494700ad02 100644 --- a/rust/migration/meson.build +++ b/rust/migration/meson.build @@ -39,7 +39,7 @@ _migration_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', link_with: [_util_rs, _bql_rs], - dependencies: [common_rs, glib_sys_rs], + dependencies: [common_rs, glib_sys_rs, qemu_macros], ) migration_rs = declare_dependency(link_with: [_migration_rs], diff --git a/rust/migration/src/lib.rs b/rust/migration/src/lib.rs index efe9896b61995..c9bdf0d413358 100644 --- a/rust/migration/src/lib.rs +++ b/rust/migration/src/lib.rs @@ -2,6 +2,8 @@ pub mod bindings; +pub use qemu_macros::ToMigrationState; + pub mod migratable; pub use migratable::*; diff --git a/rust/migration/src/migratable.rs b/rust/migration/src/migratable.rs index 46e533c16d133..ded6fe8f4a6c5 100644 --- a/rust/migration/src/migratable.rs +++ b/rust/migration/src/migratable.rs @@ -79,6 +79,10 @@ use crate::{ /// # dev2.restore_migrated_state_mut(*mig, 1).unwrap(); /// # assert_eq!(dev2, dev); /// ``` +/// +/// More commonly, the trait is derived through the +/// [`derive(ToMigrationState)`](qemu_macros::ToMigrationState) procedural +/// macro. pub trait ToMigrationState { /// The type used to represent the migrated state. type Migrated: Default + VMState; @@ -309,13 +313,17 @@ impl ToMigrationStateShared for BqlRefCell { /// It manages the lifecycle of migration state and provides automatic /// conversion between runtime and migration representations. /// -/// ```ignore +/// ``` /// # use std::sync::Mutex; -/// # use migration::Migratable; +/// # use migration::{Migratable, ToMigrationState, VMState, VMStateField}; /// +/// #[derive(ToMigrationState)] /// pub struct DeviceRegs { /// status: u32, /// } +/// # unsafe impl VMState for DeviceRegsMigration { +/// # const BASE: VMStateField = ::common::Zeroable::ZERO; +/// # } /// /// pub struct SomeDevice { /// // ... diff --git a/rust/qemu-macros/src/lib.rs b/rust/qemu-macros/src/lib.rs index 3bf315c4c0a6f..50239f228be2d 100644 --- a/rust/qemu-macros/src/lib.rs +++ b/rust/qemu-macros/src/lib.rs @@ -13,9 +13,13 @@ use syn::{ Attribute, Data, DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, Variant, }; + mod bits; use bits::BitsConstInternal; +mod migration_state; +use migration_state::MigrationStateDerive; + #[cfg(test)] mod tests; @@ -412,3 +416,87 @@ pub fn bits_const_internal(ts: TokenStream) -> TokenStream { } .into() } + +/// Derive macro for generating migration state structures and trait +/// implementations. +/// +/// This macro generates a migration state struct and implements the +/// `ToMigrationState` trait for the annotated struct, enabling state +/// serialization and restoration. Note that defining a `VMStateDescription` +/// for the migration state struct is left to the user. +/// +/// # Container attributes +/// +/// The following attributes can be applied to the struct: +/// +/// - `#[migration_state(rename = CustomName)]` - Customizes the name of the +/// generated migration struct. By default, the generated struct is named +/// `{OriginalName}Migration`. +/// +/// # Field attributes +/// +/// The following attributes can be applied to individual fields: +/// +/// - `#[migration_state(omit)]` - Excludes the field from the migration state +/// entirely. +/// +/// - `#[migration_state(into(Type))]` - Converts the field using `.into()` +/// during both serialization and restoration. +/// +/// - `#[migration_state(try_into(Type))]` - Converts the field using +/// `.try_into()` during both serialization and restoration. Returns +/// `InvalidError` on conversion failure. +/// +/// - `#[migration_state(clone)]` - Clones the field value. +/// +/// Fields without any attributes use `ToMigrationState` recursively; note that +/// this is a simple copy for types that implement `Copy`. +/// +/// # Attribute compatibility +/// +/// - `omit` cannot be used with any other attributes +/// - only one of `into(Type)`, `try_into(Type)` can be used, but they can be +/// coupled with `clone`. +/// +/// # Examples +/// +/// Basic usage: +/// ```ignore +/// #[derive(ToMigrationState)] +/// struct MyStruct { +/// field1: u32, +/// field2: Timer, +/// } +/// ``` +/// +/// With attributes: +/// ```ignore +/// #[derive(ToMigrationState)] +/// #[migration_state(rename = CustomMigration)] +/// struct MyStruct { +/// #[migration_state(omit)] +/// runtime_field: u32, +/// +/// #[migration_state(clone)] +/// shared_data: String, +/// +/// #[migration_state(into(Cow<'static, str>), clone)] +/// converted_field: String, +/// +/// #[migration_state(try_into(i8))] +/// fallible_field: u32, +/// +/// // Default: use ToMigrationState trait recursively +/// nested_field: NestedStruct, +/// +/// // Primitive types have a default implementation of ToMigrationState +/// simple_field: u32, +/// } +/// ``` +#[proc_macro_derive(ToMigrationState, attributes(migration_state))] +pub fn derive_to_migration_state(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + MigrationStateDerive::expand(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} diff --git a/rust/qemu-macros/src/migration_state.rs b/rust/qemu-macros/src/migration_state.rs new file mode 100644 index 0000000000000..5edf0efe687f4 --- /dev/null +++ b/rust/qemu-macros/src/migration_state.rs @@ -0,0 +1,298 @@ +use std::borrow::Cow; + +use proc_macro2::TokenStream; +use quote::{format_ident, quote, ToTokens}; +use syn::{spanned::Spanned, DeriveInput, Error, Field, Ident, Result, Type}; + +use crate::get_fields; + +#[derive(Debug, Default)] +enum ConversionMode { + #[default] + None, + Omit, + Into(Type), + TryInto(Type), + ToMigrationState, +} + +impl ConversionMode { + fn target_type(&self, original_type: &Type) -> TokenStream { + match self { + ConversionMode::Into(ty) | ConversionMode::TryInto(ty) => ty.to_token_stream(), + ConversionMode::ToMigrationState => { + quote! { <#original_type as ToMigrationState>::Migrated } + } + _ => original_type.to_token_stream(), + } + } +} + +#[derive(Debug, Default)] +struct ContainerAttrs { + rename: Option, +} + +impl ContainerAttrs { + fn parse_from(&mut self, attrs: &[syn::Attribute]) -> Result<()> { + use attrs::{set, with, Attrs}; + Attrs::new() + .once("rename", with::eq(set::parse(&mut self.rename))) + .parse_attrs("migration_state", attrs)?; + Ok(()) + } + + fn parse(attrs: &[syn::Attribute]) -> Result { + let mut container_attrs = Self::default(); + container_attrs.parse_from(attrs)?; + Ok(container_attrs) + } +} + +#[derive(Debug, Default)] +struct FieldAttrs { + conversion: ConversionMode, + clone: bool, +} + +impl FieldAttrs { + fn parse_from(&mut self, attrs: &[syn::Attribute]) -> Result<()> { + let mut omit_flag = false; + let mut into_type: Option = None; + let mut try_into_type: Option = None; + + use attrs::{set, with, Attrs}; + Attrs::new() + .once("omit", set::flag(&mut omit_flag)) + .once("into", with::paren(set::parse(&mut into_type))) + .once("try_into", with::paren(set::parse(&mut try_into_type))) + .once("clone", set::flag(&mut self.clone)) + .parse_attrs("migration_state", attrs)?; + + self.conversion = match (omit_flag, into_type, try_into_type, self.clone) { + // Valid combinations of attributes first... + (true, None, None, false) => ConversionMode::Omit, + (false, Some(ty), None, _) => ConversionMode::Into(ty), + (false, None, Some(ty), _) => ConversionMode::TryInto(ty), + (false, None, None, true) => ConversionMode::None, // clone without conversion + (false, None, None, false) => ConversionMode::ToMigrationState, // default behavior + + // ... then the error cases + (true, _, _, _) => { + return Err(Error::new( + attrs[0].span(), + "ToMigrationState: omit cannot be used with other attributes", + )); + } + (_, Some(_), Some(_), _) => { + return Err(Error::new( + attrs[0].span(), + "ToMigrationState: into and try_into attributes cannot be used together", + )); + } + }; + + Ok(()) + } + + fn parse(attrs: &[syn::Attribute]) -> Result { + let mut field_attrs = Self::default(); + field_attrs.parse_from(attrs)?; + Ok(field_attrs) + } +} + +#[derive(Debug)] +struct MigrationStateField { + name: Ident, + original_type: Type, + attrs: FieldAttrs, +} + +impl MigrationStateField { + fn maybe_clone(&self, mut value: TokenStream) -> TokenStream { + if self.attrs.clone { + value = quote! { #value.clone() }; + } + value + } + + fn generate_migration_state_field(&self) -> TokenStream { + let name = &self.name; + let field_type = self.attrs.conversion.target_type(&self.original_type); + + quote! { + pub #name: #field_type, + } + } + + fn generate_snapshot_field(&self) -> TokenStream { + let name = &self.name; + let value = self.maybe_clone(quote! { self.#name }); + + match &self.attrs.conversion { + ConversionMode::Omit => { + unreachable!("Omitted fields are filtered out during processing") + } + ConversionMode::None => quote! { + target.#name = #value; + }, + ConversionMode::Into(_) => quote! { + target.#name = #value.into(); + }, + ConversionMode::TryInto(_) => quote! { + target.#name = #value.try_into().map_err(|_| migration::InvalidError)?; + }, + ConversionMode::ToMigrationState => quote! { + self.#name.snapshot_migration_state(&mut target.#name)?; + }, + } + } + + fn generate_restore_field(&self) -> TokenStream { + let name = &self.name; + + match &self.attrs.conversion { + ConversionMode::Omit => { + unreachable!("Omitted fields are filtered out during processing") + } + ConversionMode::None => quote! { + self.#name = #name; + }, + ConversionMode::Into(_) => quote! { + self.#name = #name.into(); + }, + ConversionMode::TryInto(_) => quote! { + self.#name = #name.try_into().map_err(|_| migration::InvalidError)?; + }, + ConversionMode::ToMigrationState => quote! { + self.#name.restore_migrated_state_mut(#name, _version_id)?; + }, + } + } +} + +#[derive(Debug)] +pub struct MigrationStateDerive { + input: DeriveInput, + fields: Vec, + container_attrs: ContainerAttrs, +} + +impl MigrationStateDerive { + fn parse(input: DeriveInput) -> Result { + let container_attrs = ContainerAttrs::parse(&input.attrs)?; + let fields = get_fields(&input, "ToMigrationState")?; + let fields = Self::process_fields(fields)?; + + Ok(Self { + input, + fields, + container_attrs, + }) + } + + fn process_fields( + fields: &syn::punctuated::Punctuated, + ) -> Result> { + let processed = fields + .iter() + .map(|field| { + let attrs = FieldAttrs::parse(&field.attrs)?; + Ok((field, attrs)) + }) + .collect::>>()? + .into_iter() + .filter(|(_, attrs)| !matches!(attrs.conversion, ConversionMode::Omit)) + .map(|(field, attrs)| MigrationStateField { + name: field.ident.as_ref().unwrap().clone(), + original_type: field.ty.clone(), + attrs, + }) + .collect(); + + Ok(processed) + } + + fn migration_state_name(&self) -> Cow<'_, Ident> { + match &self.container_attrs.rename { + Some(rename) => Cow::Borrowed(rename), + None => Cow::Owned(format_ident!("{}Migration", &self.input.ident)), + } + } + + fn generate_migration_state_struct(&self) -> TokenStream { + let name = self.migration_state_name(); + let fields = self + .fields + .iter() + .map(MigrationStateField::generate_migration_state_field); + + quote! { + #[derive(Default)] + pub struct #name { + #(#fields)* + } + } + } + + fn generate_snapshot_migration_state(&self) -> TokenStream { + let fields = self + .fields + .iter() + .map(MigrationStateField::generate_snapshot_field); + + quote! { + fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), migration::InvalidError> { + #(#fields)* + Ok(()) + } + } + } + + fn generate_restore_migrated_state(&self) -> TokenStream { + let names: Vec<_> = self.fields.iter().map(|f| &f.name).collect(); + let fields = self + .fields + .iter() + .map(MigrationStateField::generate_restore_field); + + // version_id could be used or not depending on conversion attributes + quote! { + #[allow(clippy::used_underscore_binding)] + fn restore_migrated_state_mut(&mut self, source: Self::Migrated, _version_id: u8) -> Result<(), migration::InvalidError> { + let Self::Migrated { #(#names),* } = source; + #(#fields)* + Ok(()) + } + } + } + + fn generate(&self) -> TokenStream { + let struct_name = &self.input.ident; + let generics = &self.input.generics; + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let name = self.migration_state_name(); + let migration_state_struct = self.generate_migration_state_struct(); + let snapshot_impl = self.generate_snapshot_migration_state(); + let restore_impl = self.generate_restore_migrated_state(); + + quote! { + #migration_state_struct + + impl #impl_generics ToMigrationState for #struct_name #ty_generics #where_clause { + type Migrated = #name; + + #snapshot_impl + + #restore_impl + } + } + } + + pub fn expand(input: DeriveInput) -> Result { + let tokens = Self::parse(input)?.generate(); + Ok(tokens) + } +} diff --git a/rust/qemu-macros/src/tests.rs b/rust/qemu-macros/src/tests.rs index ac998d20e3040..65691412ff57e 100644 --- a/rust/qemu-macros/src/tests.rs +++ b/rust/qemu-macros/src/tests.rs @@ -7,7 +7,7 @@ use quote::quote; use super::*; macro_rules! derive_compile_fail { - ($derive_fn:ident, $input:expr, $($error_msg:expr),+ $(,)?) => {{ + ($derive_fn:path, $input:expr, $($error_msg:expr),+ $(,)?) => {{ let input: proc_macro2::TokenStream = $input; let error_msg = &[$( quote! { ::core::compile_error! { $error_msg } } ),*]; let derive_fn: fn(input: syn::DeriveInput) -> Result = @@ -24,7 +24,7 @@ macro_rules! derive_compile_fail { } macro_rules! derive_compile { - ($derive_fn:ident, $input:expr, $($expected:tt)*) => {{ + ($derive_fn:path, $input:expr, $($expected:tt)*) => {{ let input: proc_macro2::TokenStream = $input; let expected: proc_macro2::TokenStream = $($expected)*; let derive_fn: fn(input: syn::DeriveInput) -> Result = @@ -345,3 +345,112 @@ fn test_derive_tryinto() { } ); } + +#[test] +fn test_derive_to_migration_state() { + derive_compile_fail!( + MigrationStateDerive::expand, + quote! { + struct MyStruct { + #[migration_state(omit, clone)] + bad: u32, + } + }, + "ToMigrationState: omit cannot be used with other attributes" + ); + derive_compile_fail!( + MigrationStateDerive::expand, + quote! { + struct MyStruct { + #[migration_state(into)] + bad: u32, + } + }, + "unexpected end of input, expected parentheses" + ); + derive_compile_fail!( + MigrationStateDerive::expand, + quote! { + struct MyStruct { + #[migration_state(into(String), try_into(String))] + bad: &'static str, + } + }, + "ToMigrationState: into and try_into attributes cannot be used together" + ); + derive_compile!( + MigrationStateDerive::expand, + quote! { + #[migration_state(rename = CustomMigration)] + struct MyStruct { + #[migration_state(omit)] + runtime_field: u32, + + #[migration_state(clone)] + shared_data: String, + + #[migration_state(into(Cow<'static, str>), clone)] + converted_field: String, + + #[migration_state(try_into(i8))] + fallible_field: u32, + + nested_field: NestedStruct, + simple_field: u32, + } + }, + quote! { + #[derive(Default)] + pub struct CustomMigration { + pub shared_data: String, + pub converted_field: Cow<'static, str>, + pub fallible_field: i8, + pub nested_field: ::Migrated, + pub simple_field: ::Migrated, + } + impl ToMigrationState for MyStruct { + type Migrated = CustomMigration; + fn snapshot_migration_state( + &self, + target: &mut Self::Migrated + ) -> Result<(), migration::InvalidError> { + target.shared_data = self.shared_data.clone(); + target.converted_field = self.converted_field.clone().into(); + target.fallible_field = self + .fallible_field + .try_into() + .map_err(|_| migration::InvalidError)?; + self.nested_field + .snapshot_migration_state(&mut target.nested_field)?; + self.simple_field + .snapshot_migration_state(&mut target.simple_field)?; + Ok(()) + } + #[allow(clippy::used_underscore_binding)] + fn restore_migrated_state_mut( + &mut self, + source: Self::Migrated, + _version_id: u8 + ) -> Result<(), migration::InvalidError> { + let Self::Migrated { + shared_data, + converted_field, + fallible_field, + nested_field, + simple_field + } = source; + self.shared_data = shared_data; + self.converted_field = converted_field.into(); + self.fallible_field = fallible_field + .try_into() + .map_err(|_| migration::InvalidError)?; + self.nested_field + .restore_migrated_state_mut(nested_field, _version_id)?; + self.simple_field + .restore_migrated_state_mut(simple_field, _version_id)?; + Ok(()) + } + } + } + ); +} From 67913e95bf7e9fb3c9e904055d7021b243ff1674 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 30 Sep 2025 10:56:26 +0200 Subject: [PATCH 1586/1794] timer: constify some functions Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- include/qemu/timer.h | 6 +++--- util/qemu-timer.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/qemu/timer.h b/include/qemu/timer.h index abd2204f3beac..aec730ac257e7 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -699,7 +699,7 @@ void timer_mod_anticipate(QEMUTimer *ts, int64_t expire_time); * * Returns: true if the timer is pending */ -bool timer_pending(QEMUTimer *ts); +bool timer_pending(const QEMUTimer *ts); /** * timer_expired: @@ -710,7 +710,7 @@ bool timer_pending(QEMUTimer *ts); * * Returns: true if the timer has expired */ -bool timer_expired(QEMUTimer *timer_head, int64_t current_time); +bool timer_expired(const QEMUTimer *timer_head, int64_t current_time); /** * timer_expire_time_ns: @@ -720,7 +720,7 @@ bool timer_expired(QEMUTimer *timer_head, int64_t current_time); * * Returns: the expiry time in nanoseconds */ -uint64_t timer_expire_time_ns(QEMUTimer *ts); +uint64_t timer_expire_time_ns(const QEMUTimer *ts); /** * timer_get: diff --git a/util/qemu-timer.c b/util/qemu-timer.c index 1fb48be281a75..56f11b6a641f6 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -89,7 +89,7 @@ static inline QEMUClock *qemu_clock_ptr(QEMUClockType type) return &qemu_clocks[type]; } -static bool timer_expired_ns(QEMUTimer *timer_head, int64_t current_time) +static bool timer_expired_ns(const QEMUTimer *timer_head, int64_t current_time) { return timer_head && (timer_head->expire_time <= current_time); } @@ -475,12 +475,12 @@ void timer_mod_anticipate(QEMUTimer *ts, int64_t expire_time) timer_mod_anticipate_ns(ts, expire_time * ts->scale); } -bool timer_pending(QEMUTimer *ts) +bool timer_pending(const QEMUTimer *ts) { return ts->expire_time >= 0; } -bool timer_expired(QEMUTimer *timer_head, int64_t current_time) +bool timer_expired(const QEMUTimer *timer_head, int64_t current_time) { return timer_expired_ns(timer_head, current_time * timer_head->scale); } @@ -649,7 +649,7 @@ void init_clocks(QEMUTimerListNotifyCB *notify_cb) #endif } -uint64_t timer_expire_time_ns(QEMUTimer *ts) +uint64_t timer_expire_time_ns(const QEMUTimer *ts) { return timer_pending(ts) ? ts->expire_time : -1; } From 7ee5875d423598ac55a0b55881d9a1ee5c3c7daf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 19 Sep 2025 11:18:44 +0200 Subject: [PATCH 1587/1794] rust: migration: implement ToMigrationState as part of impl_vmstate_bitsized This is most likely desirable, and is the easiest way to migrate a bit-sized value without peeking at the innards of the bilge crate. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/migration/src/vmstate.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs index 445fe7fbc08a4..42e5df8d818ff 100644 --- a/rust/migration/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -296,6 +296,25 @@ macro_rules! impl_vmstate_bitsized { as ::bilge::prelude::Number>::UnderlyingType as $crate::vmstate::VMState>::VARRAY_FLAG; } + + impl $crate::migratable::ToMigrationState for $type { + type Migrated = <<$type as ::bilge::prelude::Bitsized>::ArbitraryInt + as ::bilge::prelude::Number>::UnderlyingType; + + fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), $crate::InvalidError> { + *target = Self::Migrated::from(*self); + Ok(()) + } + + fn restore_migrated_state_mut( + &mut self, + source: Self::Migrated, + version_id: u8, + ) -> Result<(), $crate::InvalidError> { + *self = Self::from(source); + Ok(()) + } + } }; } From f55fc1c0929d0ecdaad089f40e087f2c81ed6614 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 23 Sep 2025 18:16:58 -0700 Subject: [PATCH 1588/1794] accel/tcg: Add clear_flags argument to page_set_flags Expand the interface of page_set_flags to separate the set of flags to be set and the set of flags to be cleared. This allows us to replace PAGE_RESET with the PAGE_VALID bit within clear_flags. Replace PAGE_TARGET_STICKY with TARGET_PAGE_NOTSTICKY; aarch64-linux-user is the only user. Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 114 +++++++++------------------------ bsd-user/bsd-mem.h | 7 +- bsd-user/mmap.c | 6 +- include/exec/page-protection.h | 13 ++-- include/user/page-protection.h | 9 ++- linux-user/arm/elfload.c | 2 +- linux-user/hppa/elfload.c | 2 +- linux-user/mmap.c | 32 +++++---- linux-user/x86_64/elfload.c | 2 +- target/arm/cpu.h | 1 - 10 files changed, 71 insertions(+), 117 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 916f18754f685..1800dffa63f14 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -269,48 +269,6 @@ static void pageflags_create(vaddr start, vaddr last, int flags) interval_tree_insert(&p->itree, &pageflags_root); } -/* A subroutine of page_set_flags: remove everything in [start,last]. */ -static bool pageflags_unset(vaddr start, vaddr last) -{ - bool inval_tb = false; - - while (true) { - PageFlagsNode *p = pageflags_find(start, last); - vaddr p_last; - - if (!p) { - break; - } - - if (p->flags & PAGE_EXEC) { - inval_tb = true; - } - - interval_tree_remove(&p->itree, &pageflags_root); - p_last = p->itree.last; - - if (p->itree.start < start) { - /* Truncate the node from the end, or split out the middle. */ - p->itree.last = start - 1; - interval_tree_insert(&p->itree, &pageflags_root); - if (last < p_last) { - pageflags_create(last + 1, p_last, p->flags); - break; - } - } else if (p_last <= last) { - /* Range completely covers node -- remove it. */ - g_free_rcu(p, rcu); - } else { - /* Truncate the node from the start. */ - p->itree.start = last + 1; - interval_tree_insert(&p->itree, &pageflags_root); - break; - } - } - - return inval_tb; -} - /* * A subroutine of page_set_flags: nothing overlaps [start,last], * but check adjacent mappings and maybe merge into a single range. @@ -356,15 +314,6 @@ static void pageflags_create_merge(vaddr start, vaddr last, int flags) } } -/* - * Allow the target to decide if PAGE_TARGET_[12] may be reset. - * By default, they are not kept. - */ -#ifndef PAGE_TARGET_STICKY -#define PAGE_TARGET_STICKY 0 -#endif -#define PAGE_STICKY (PAGE_ANON | PAGE_PASSTHROUGH | PAGE_TARGET_STICKY) - /* A subroutine of page_set_flags: add flags to [start,last]. */ static bool pageflags_set_clear(vaddr start, vaddr last, int set_flags, int clear_flags) @@ -377,7 +326,7 @@ static bool pageflags_set_clear(vaddr start, vaddr last, restart: p = pageflags_find(start, last); if (!p) { - if (set_flags) { + if (set_flags & PAGE_VALID) { pageflags_create_merge(start, last, set_flags); } goto done; @@ -391,11 +340,12 @@ static bool pageflags_set_clear(vaddr start, vaddr last, /* * Need to flush if an overlapping executable region - * removes exec, or adds write. + * removes exec, adds write, or is a new mapping. */ if ((p_flags & PAGE_EXEC) && (!(merge_flags & PAGE_EXEC) - || (merge_flags & ~p_flags & PAGE_WRITE))) { + || (merge_flags & ~p_flags & PAGE_WRITE) + || (clear_flags & PAGE_VALID))) { inval_tb = true; } @@ -404,7 +354,7 @@ static bool pageflags_set_clear(vaddr start, vaddr last, * attempting to merge with adjacent regions. */ if (start == p_start && last == p_last) { - if (merge_flags) { + if (merge_flags & PAGE_VALID) { p->flags = merge_flags; } else { interval_tree_remove(&p->itree, &pageflags_root); @@ -424,12 +374,12 @@ static bool pageflags_set_clear(vaddr start, vaddr last, interval_tree_insert(&p->itree, &pageflags_root); if (last < p_last) { - if (merge_flags) { + if (merge_flags & PAGE_VALID) { pageflags_create(start, last, merge_flags); } pageflags_create(last + 1, p_last, p_flags); } else { - if (merge_flags) { + if (merge_flags & PAGE_VALID) { pageflags_create(start, p_last, merge_flags); } if (p_last < last) { @@ -438,18 +388,18 @@ static bool pageflags_set_clear(vaddr start, vaddr last, } } } else { - if (start < p_start && set_flags) { + if (start < p_start && (set_flags & PAGE_VALID)) { pageflags_create(start, p_start - 1, set_flags); } if (last < p_last) { interval_tree_remove(&p->itree, &pageflags_root); p->itree.start = last + 1; interval_tree_insert(&p->itree, &pageflags_root); - if (merge_flags) { + if (merge_flags & PAGE_VALID) { pageflags_create(start, last, merge_flags); } } else { - if (merge_flags) { + if (merge_flags & PAGE_VALID) { p->flags = merge_flags; } else { interval_tree_remove(&p->itree, &pageflags_root); @@ -497,7 +447,7 @@ static bool pageflags_set_clear(vaddr start, vaddr last, g_free_rcu(p, rcu); goto restart; } - if (set_flags) { + if (set_flags & PAGE_VALID) { pageflags_create(start, last, set_flags); } @@ -505,42 +455,36 @@ static bool pageflags_set_clear(vaddr start, vaddr last, return inval_tb; } -void page_set_flags(vaddr start, vaddr last, int flags) +void page_set_flags(vaddr start, vaddr last, int set_flags, int clear_flags) { - bool reset = false; - bool inval_tb = false; - - /* This function should never be called with addresses outside the - guest address space. If this assert fires, it probably indicates - a missing call to h2g_valid. */ + /* + * This function should never be called with addresses outside the + * guest address space. If this assert fires, it probably indicates + * a missing call to h2g_valid. + */ assert(start <= last); assert(last <= guest_addr_max); - /* Only set PAGE_ANON with new mappings. */ - assert(!(flags & PAGE_ANON) || (flags & PAGE_RESET)); assert_memory_lock(); start &= TARGET_PAGE_MASK; last |= ~TARGET_PAGE_MASK; - if (!(flags & PAGE_VALID)) { - flags = 0; - } else { - reset = flags & PAGE_RESET; - flags &= ~PAGE_RESET; - if (flags & PAGE_WRITE) { - flags |= PAGE_WRITE_ORG; - } + if (set_flags & PAGE_WRITE) { + set_flags |= PAGE_WRITE_ORG; + } + if (clear_flags & PAGE_WRITE) { + clear_flags |= PAGE_WRITE_ORG; } - if (!flags || reset) { + if (clear_flags & PAGE_VALID) { page_reset_target_data(start, last); - inval_tb |= pageflags_unset(start, last); - } - if (flags) { - inval_tb |= pageflags_set_clear(start, last, flags, - ~(reset ? 0 : PAGE_STICKY)); + clear_flags = -1; + } else { + /* Only set PAGE_ANON with new mappings. */ + assert(!(set_flags & PAGE_ANON)); } - if (inval_tb) { + + if (pageflags_set_clear(start, last, set_flags, clear_flags)) { tb_invalidate_phys_range(NULL, start, last); } } diff --git a/bsd-user/bsd-mem.h b/bsd-user/bsd-mem.h index 1be906c5914be..416d0f8c23da1 100644 --- a/bsd-user/bsd-mem.h +++ b/bsd-user/bsd-mem.h @@ -390,8 +390,9 @@ static inline abi_long do_bsd_shmat(int shmid, abi_ulong shmaddr, int shmflg) raddr = h2g(host_raddr); page_set_flags(raddr, raddr + shm_info.shm_segsz - 1, - PAGE_VALID | PAGE_RESET | PAGE_READ | - (shmflg & SHM_RDONLY ? 0 : PAGE_WRITE)); + PAGE_VALID | PAGE_READ | + (shmflg & SHM_RDONLY ? 0 : PAGE_WRITE), + PAGE_VALID); for (int i = 0; i < N_BSD_SHM_REGIONS; i++) { if (bsd_shm_regions[i].start == 0) { @@ -428,7 +429,7 @@ static inline abi_long do_bsd_shmdt(abi_ulong shmaddr) abi_ulong size = bsd_shm_regions[i].size; bsd_shm_regions[i].start = 0; - page_set_flags(shmaddr, shmaddr + size - 1, 0); + page_set_flags(shmaddr, shmaddr + size - 1, 0, PAGE_VALID); mmap_reserve(shmaddr, size); } } diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index 47e317517cb54..24ba1728eb558 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -122,7 +122,7 @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot) if (ret != 0) goto error; } - page_set_flags(start, start + len - 1, prot | PAGE_VALID); + page_set_flags(start, start + len - 1, prot, PAGE_RWX); mmap_unlock(); return 0; error: @@ -652,7 +652,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, } } the_end1: - page_set_flags(start, start + len - 1, prot | PAGE_VALID); + page_set_flags(start, start + len - 1, prot | PAGE_VALID, PAGE_VALID); the_end: #ifdef DEBUG_MMAP printf("ret=0x" TARGET_ABI_FMT_lx "\n", start); @@ -763,7 +763,7 @@ int target_munmap(abi_ulong start, abi_ulong len) } if (ret == 0) { - page_set_flags(start, start + len - 1, 0); + page_set_flags(start, start + len - 1, 0, PAGE_VALID); } mmap_unlock(); return ret; diff --git a/include/exec/page-protection.h b/include/exec/page-protection.h index c43231af8b543..5a18f98a3aed9 100644 --- a/include/exec/page-protection.h +++ b/include/exec/page-protection.h @@ -23,8 +23,11 @@ * Low-Address-Protection. Used with PAGE_WRITE in tlb_set_page_with_attrs() */ #define PAGE_WRITE_INV 0x0020 -/* For use with page_set_flags: page is being replaced; target_data cleared. */ -#define PAGE_RESET 0x0040 +/* + * For linux-user, indicates that the page is mapped with the same semantics + * in both guest and host. + */ +#define PAGE_PASSTHROUGH 0x40 /* For linux-user, indicates that the page is MAP_ANON. */ #define PAGE_ANON 0x0080 @@ -32,10 +35,4 @@ #define PAGE_TARGET_1 0x0200 #define PAGE_TARGET_2 0x0400 -/* - * For linux-user, indicates that the page is mapped with the same semantics - * in both guest and host. - */ -#define PAGE_PASSTHROUGH 0x0800 - #endif diff --git a/include/user/page-protection.h b/include/user/page-protection.h index 4bde664e4a74f..41b23e72fcb88 100644 --- a/include/user/page-protection.h +++ b/include/user/page-protection.h @@ -23,14 +23,19 @@ int page_get_flags(vaddr address); * page_set_flags: * @start: first byte of range * @last: last byte of range - * @flags: flags to set + * @set_flags: flags to set + * @clr_flags: flags to clear * Context: holding mmap lock * * Modify the flags of a page and invalidate the code if necessary. * The flag PAGE_WRITE_ORG is positioned automatically depending * on PAGE_WRITE. The mmap_lock should already be held. + * + * For each page, flags = (flags & ~clr_flags) | set_flags. + * If clr_flags includes PAGE_VALID, this indicates a new mapping + * and page_reset_target_data will be called as well. */ -void page_set_flags(vaddr start, vaddr last, int flags); +void page_set_flags(vaddr start, vaddr last, int set_flags, int clr_flags); void page_reset_target_data(vaddr start, vaddr last); diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index b1a4db44660b3..fef61022a3df7 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -243,7 +243,7 @@ bool init_guest_commpage(void) } page_set_flags(commpage, commpage | (host_page_size - 1), - PAGE_READ | PAGE_EXEC | PAGE_VALID); + PAGE_READ | PAGE_EXEC | PAGE_VALID, PAGE_VALID); return true; } diff --git a/linux-user/hppa/elfload.c b/linux-user/hppa/elfload.c index 018034f244e58..4600708702077 100644 --- a/linux-user/hppa/elfload.c +++ b/linux-user/hppa/elfload.c @@ -42,6 +42,6 @@ bool init_guest_commpage(void) * Special case the entry points during translation (see do_page_zero). */ page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK, - PAGE_EXEC | PAGE_VALID); + PAGE_EXEC | PAGE_VALID, PAGE_VALID); return true; } diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 847092a28a6e1..527ca5f211d70 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -165,6 +165,13 @@ static int target_to_host_prot(int prot) (prot & PROT_EXEC ? PROT_READ : 0); } +/* Target bits to be cleared by mprotect if not present in target_prot. */ +#ifdef TARGET_AARCH64 +#define TARGET_PAGE_NOTSTICKY PAGE_BTI +#else +#define TARGET_PAGE_NOTSTICKY 0 +#endif + /* NOTE: all the constants are the HOST ones, but addresses are target. */ int target_mprotect(abi_ulong start, abi_ulong len, int target_prot) { @@ -262,7 +269,7 @@ int target_mprotect(abi_ulong start, abi_ulong len, int target_prot) } } - page_set_flags(start, last, page_flags); + page_set_flags(start, last, page_flags, PAGE_RWX | TARGET_PAGE_NOTSTICKY); ret = 0; error: @@ -561,17 +568,17 @@ static abi_long mmap_end(abi_ulong start, abi_ulong last, if (flags & MAP_ANONYMOUS) { page_flags |= PAGE_ANON; } - page_flags |= PAGE_RESET; if (passthrough_start > passthrough_last) { - page_set_flags(start, last, page_flags); + page_set_flags(start, last, page_flags, PAGE_VALID); } else { if (start < passthrough_start) { - page_set_flags(start, passthrough_start - 1, page_flags); + page_set_flags(start, passthrough_start - 1, + page_flags, PAGE_VALID); } page_set_flags(passthrough_start, passthrough_last, - page_flags | PAGE_PASSTHROUGH); + page_flags | PAGE_PASSTHROUGH, PAGE_VALID); if (passthrough_last < last) { - page_set_flags(passthrough_last + 1, last, page_flags); + page_set_flags(passthrough_last + 1, last, page_flags, PAGE_VALID); } } shm_region_rm_complete(start, last); @@ -1088,7 +1095,7 @@ int target_munmap(abi_ulong start, abi_ulong len) mmap_lock(); ret = mmap_reserve_or_unmap(start, len); if (likely(ret == 0)) { - page_set_flags(start, start + len - 1, 0); + page_set_flags(start, start + len - 1, 0, PAGE_VALID); shm_region_rm_complete(start, start + len - 1); } mmap_unlock(); @@ -1179,10 +1186,10 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, } else { new_addr = h2g(host_addr); prot = page_get_flags(old_addr); - page_set_flags(old_addr, old_addr + old_size - 1, 0); + page_set_flags(old_addr, old_addr + old_size - 1, 0, PAGE_VALID); shm_region_rm_complete(old_addr, old_addr + old_size - 1); page_set_flags(new_addr, new_addr + new_size - 1, - prot | PAGE_VALID | PAGE_RESET); + prot | PAGE_VALID, PAGE_VALID); shm_region_rm_complete(new_addr, new_addr + new_size - 1); } mmap_unlock(); @@ -1428,9 +1435,10 @@ abi_ulong target_shmat(CPUArchState *cpu_env, int shmid, last = shmaddr + m_len - 1; page_set_flags(shmaddr, last, - PAGE_VALID | PAGE_RESET | PAGE_READ | + PAGE_VALID | PAGE_READ | (shmflg & SHM_RDONLY ? 0 : PAGE_WRITE) | - (shmflg & SHM_EXEC ? PAGE_EXEC : 0)); + (shmflg & SHM_EXEC ? PAGE_EXEC : 0), + PAGE_VALID); shm_region_rm_complete(shmaddr, last); shm_region_add(shmaddr, last); @@ -1471,7 +1479,7 @@ abi_long target_shmdt(abi_ulong shmaddr) if (rv == 0) { abi_ulong size = last - shmaddr + 1; - page_set_flags(shmaddr, last, 0); + page_set_flags(shmaddr, last, 0, PAGE_VALID); shm_region_rm_complete(shmaddr, last); mmap_reserve_or_unmap(shmaddr, size); } diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c index 1e7000c6bcfd7..5914f76e83353 100644 --- a/linux-user/x86_64/elfload.c +++ b/linux-user/x86_64/elfload.c @@ -37,7 +37,7 @@ bool init_guest_commpage(void) } page_set_flags(TARGET_VSYSCALL_PAGE, TARGET_VSYSCALL_PAGE | ~TARGET_PAGE_MASK, - PAGE_EXEC | PAGE_VALID); + PAGE_EXEC | PAGE_VALID, PAGE_VALID); return true; } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 1d4e13320c3e9..bf221e6f97318 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2642,7 +2642,6 @@ extern const uint64_t pred_esz_masks[5]; */ #define PAGE_BTI PAGE_TARGET_1 #define PAGE_MTE PAGE_TARGET_2 -#define PAGE_TARGET_STICKY PAGE_MTE /* We associate one allocation tag per 16 bytes, the minimum. */ #define LOG2_TAG_GRANULE 4 From a79fbb6988f6aad36a64ef9fd1a5fc226a8e4130 Mon Sep 17 00:00:00 2001 From: Jon Wilson Date: Tue, 23 Sep 2025 18:52:28 -0700 Subject: [PATCH 1589/1794] linux-user: Support MADV_DONTDUMP, MADV_DODUMP Set and clear PAGE_DONTDUMP, and honor that in vma_dump_size. Signed-off-by: Jon Wilson [rth: Use new page_set_flags semantics; also handle DODUMP] Signed-off-by: Richard Henderson --- include/exec/page-protection.h | 6 +++++- linux-user/elfload.c | 4 ++-- linux-user/mmap.c | 6 ++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/include/exec/page-protection.h b/include/exec/page-protection.h index 5a18f98a3aed9..c50ce57d150f3 100644 --- a/include/exec/page-protection.h +++ b/include/exec/page-protection.h @@ -30,7 +30,11 @@ #define PAGE_PASSTHROUGH 0x40 /* For linux-user, indicates that the page is MAP_ANON. */ #define PAGE_ANON 0x0080 - +/* + * For linux-user, indicates that the page should not be + * included in a core dump. + */ +#define PAGE_DONTDUMP 0x0100 /* Target-specific bits that will be used via page_get_flags(). */ #define PAGE_TARGET_1 0x0200 #define PAGE_TARGET_2 0x0400 diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 1370ec59be4df..0002d5be2f5f9 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2127,8 +2127,8 @@ static void bswap_note(struct elf_note *en) */ static size_t vma_dump_size(vaddr start, vaddr end, int flags) { - /* The area must be readable. */ - if (!(flags & PAGE_READ)) { + /* The area must be readable and dumpable. */ + if (!(flags & PAGE_READ) || (flags & PAGE_DONTDUMP)) { return 0; } diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 527ca5f211d70..423c77856a3aa 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -1248,6 +1248,12 @@ abi_long target_madvise(abi_ulong start, abi_ulong len_in, int advice) */ mmap_lock(); switch (advice) { + case MADV_DONTDUMP: + page_set_flags(start, start + len - 1, PAGE_DONTDUMP, 0); + break; + case MADV_DODUMP: + page_set_flags(start, start + len - 1, 0, PAGE_DONTDUMP); + break; case MADV_WIPEONFORK: case MADV_KEEPONFORK: ret = -EINVAL; From ec03dd9723781c7e9d4b4f70c7f54d12da9459d5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Oct 2025 12:10:52 -0700 Subject: [PATCH 1590/1794] accel/tcg: Hoist first page lookup above pointer_wrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For strict alignment targets we registered cpu_pointer_wrap_notreached, but generic code used it before recognizing the alignment exception. Hoist the first page lookup, so that the alignment exception happens first. Cc: qemu-stable@nongnu.org Buglink: https://bugs.debian.org/1112285 Fixes: a4027ed7d4be ("target: Use cpu_pointer_wrap_notreached for strict align targets") Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé --- accel/tcg/cputlb.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 3010dd4f5df5f..631f1fe135936 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1742,6 +1742,7 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi, uintptr_t ra, MMUAccessType type, MMULookupLocals *l) { bool crosspage; + vaddr last; int flags; l->memop = get_memop(oi); @@ -1751,13 +1752,15 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi, l->page[0].addr = addr; l->page[0].size = memop_size(l->memop); - l->page[1].addr = (addr + l->page[0].size - 1) & TARGET_PAGE_MASK; + l->page[1].addr = 0; l->page[1].size = 0; - crosspage = (addr ^ l->page[1].addr) & TARGET_PAGE_MASK; - if (likely(!crosspage)) { - mmu_lookup1(cpu, &l->page[0], l->memop, l->mmu_idx, type, ra); + /* Lookup and recognize exceptions from the first page. */ + mmu_lookup1(cpu, &l->page[0], l->memop, l->mmu_idx, type, ra); + last = addr + l->page[0].size - 1; + crosspage = (addr ^ last) & TARGET_PAGE_MASK; + if (likely(!crosspage)) { flags = l->page[0].flags; if (unlikely(flags & (TLB_WATCHPOINT | TLB_NOTDIRTY))) { mmu_watch_or_dirty(cpu, &l->page[0], type, ra); @@ -1767,18 +1770,18 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi, } } else { /* Finish compute of page crossing. */ - int size0 = l->page[1].addr - addr; + vaddr addr1 = last & TARGET_PAGE_MASK; + int size0 = addr1 - addr; l->page[1].size = l->page[0].size - size0; l->page[0].size = size0; - l->page[1].addr = cpu->cc->tcg_ops->pointer_wrap(cpu, l->mmu_idx, - l->page[1].addr, addr); + addr1, addr); /* - * Lookup both pages, recognizing exceptions from either. If the - * second lookup potentially resized, refresh first CPUTLBEntryFull. + * Lookup and recognize exceptions from the second page. + * If the lookup potentially resized the table, refresh the + * first CPUTLBEntryFull pointer. */ - mmu_lookup1(cpu, &l->page[0], l->memop, l->mmu_idx, type, ra); if (mmu_lookup1(cpu, &l->page[1], 0, l->mmu_idx, type, ra)) { uintptr_t index = tlb_index(cpu, l->mmu_idx, addr); l->page[0].full = &cpu->neg.tlb.d[l->mmu_idx].fulltlb[index]; From 2e9ff01a912997c4af066aab5e43a52db6c68aaf Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 9 Oct 2025 10:59:31 +0800 Subject: [PATCH 1591/1794] target/loongarch: Add missing TLB flush with different asid If asid is changed in function helper_csrwr_asid(), qemu TLB is flushed, however loongArch TLB is still valid. So loongArch TLB need be invalidated in function invalidate_tlb() with different asid and bit effective need be cleared. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- target/loongarch/tcg/tlb_helper.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 8cfce48a297ac..f8fada5b9a26a 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -117,13 +117,7 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index) uint8_t tlb_v0 = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, V); uint8_t tlb_v1 = FIELD_EX64(tlb->tlb_entry1, TLBENTRY, V); uint64_t tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); - uint8_t tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); - if (!tlb_e) { - return; - } - - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); pagesize = MAKE_64BIT_MASK(tlb_ps, 1); mask = MAKE_64BIT_MASK(0, tlb_ps + 1); @@ -145,11 +139,19 @@ static void invalidate_tlb(CPULoongArchState *env, int index) { LoongArchTLB *tlb; uint16_t csr_asid, tlb_asid, tlb_g; + uint8_t tlb_e; csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); tlb = &env->tlb[index]; + tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); + if (!tlb_e) { + return; + } + + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); + /* QEMU TLB is flushed when asid is changed */ if (tlb_g == 0 && tlb_asid != csr_asid) { return; } From f72848e31fbe30377ce8ed3b1ce9be7346933d34 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 9 Oct 2025 10:59:32 +0800 Subject: [PATCH 1592/1794] target/loongarch: Skip global TLB when calculating replaced TLB When new TLB entry is added, TLB index is calculated from invalid entry at first and then from different ASID, and randomly at last. With different ASID, global TLB should be skipped since ASID is not useful when global TLB is added. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- target/loongarch/tcg/tlb_helper.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index f8fada5b9a26a..f1d183cb6419b 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -371,7 +371,7 @@ void helper_tlbfill(CPULoongArchState *env) uint16_t pagesize, stlb_ps; uint16_t asid, tlb_asid; LoongArchTLB *tlb; - uint8_t tlb_e; + uint8_t tlb_e, tlb_g; if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { entryhi = env->CSR_TLBREHI; @@ -400,7 +400,8 @@ void helper_tlbfill(CPULoongArchState *env) } tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - if (asid != tlb_asid) { + tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); + if (tlb_g == 0 && asid != tlb_asid) { set = i; } } @@ -423,7 +424,8 @@ void helper_tlbfill(CPULoongArchState *env) } tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - if (asid != tlb_asid) { + tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); + if (tlb_g == 0 && asid != tlb_asid) { index = i; } } From 52d90741637e498042269f17925b500f4ffe3ef8 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 13 Oct 2025 14:35:15 +0800 Subject: [PATCH 1593/1794] hw/loongarch/virt: Remove header file ls7a.h LoongArch virt machine uses GPEX PCIE host bridge rather than 7A host bridge. Remove header file ls7a.h and put hardware information to file include/hw/loongarch/virt.h Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- MAINTAINERS | 1 - hw/intc/loongarch_pic_kvm.c | 1 - hw/loongarch/virt-acpi-build.c | 1 - hw/loongarch/virt-fdt-build.c | 1 - hw/loongarch/virt.c | 1 - include/hw/intc/loongarch_pic_common.h | 2 +- include/hw/loongarch/virt.h | 39 ++++++++++++++++++++++++++ include/hw/pci-host/ls7a.h | 39 -------------------------- 8 files changed, 40 insertions(+), 45 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 84cfd85e1fa16..0c766961f39aa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1308,7 +1308,6 @@ F: include/hw/intc/loongarch_*.h F: include/hw/intc/loongson_ipi_common.h F: hw/intc/loongarch_*.c F: hw/intc/loongson_ipi_common.c -F: include/hw/pci-host/ls7a.h F: hw/rtc/ls7a_rtc.c F: gdb-xml/loongarch*.xml diff --git a/hw/intc/loongarch_pic_kvm.c b/hw/intc/loongarch_pic_kvm.c index dd504ec6a6fac..6cfddf45206a3 100644 --- a/hw/intc/loongarch_pic_kvm.c +++ b/hw/intc/loongarch_pic_kvm.c @@ -10,7 +10,6 @@ #include "hw/boards.h" #include "hw/intc/loongarch_pch_pic.h" #include "hw/loongarch/virt.h" -#include "hw/pci-host/ls7a.h" #include "system/kvm.h" static void kvm_pch_pic_access_reg(int fd, uint64_t addr, void *val, bool write) diff --git a/hw/loongarch/virt-acpi-build.c b/hw/loongarch/virt-acpi-build.c index 8c2228a77225c..3694c9827f045 100644 --- a/hw/loongarch/virt-acpi-build.c +++ b/hw/loongarch/virt-acpi-build.c @@ -21,7 +21,6 @@ #include "system/reset.h" /* Supported chipsets: */ -#include "hw/pci-host/ls7a.h" #include "hw/loongarch/virt.h" #include "hw/acpi/utils.h" diff --git a/hw/loongarch/virt-fdt-build.c b/hw/loongarch/virt-fdt-build.c index 728ce466996f8..1f0ba01f711e0 100644 --- a/hw/loongarch/virt-fdt-build.c +++ b/hw/loongarch/virt-fdt-build.c @@ -12,7 +12,6 @@ #include "hw/loader.h" #include "hw/loongarch/virt.h" #include "hw/pci-host/gpex.h" -#include "hw/pci-host/ls7a.h" #include "system/device_tree.h" #include "system/reset.h" #include "target/loongarch/cpu.h" diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index c1760423ee1f9..efd1f9ac49d67 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -29,7 +29,6 @@ #include "hw/intc/loongarch_pch_pic.h" #include "hw/intc/loongarch_pch_msi.h" #include "hw/intc/loongarch_dintc.h" -#include "hw/pci-host/ls7a.h" #include "hw/pci-host/gpex.h" #include "hw/misc/unimp.h" #include "hw/loongarch/fw_cfg.h" diff --git a/include/hw/intc/loongarch_pic_common.h b/include/hw/intc/loongarch_pic_common.h index f774c975d4055..675ba96e64eca 100644 --- a/include/hw/intc/loongarch_pic_common.h +++ b/include/hw/intc/loongarch_pic_common.h @@ -7,7 +7,7 @@ #ifndef HW_LOONGARCH_PIC_COMMON_H #define HW_LOONGARCH_PIC_COMMON_H -#include "hw/pci-host/ls7a.h" +#include "hw/loongarch/virt.h" #include "hw/sysbus.h" #define PCH_PIC_INT_ID 0x00 diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 76fa57cd070cf..0cc1b499a7bce 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -25,6 +25,7 @@ #define IOCSRF_VM 11 #define IOCSRF_DMSI 15 +/* IOCSR region */ #define VERSION_REG 0x0 #define FEATURE_REG 0x8 #define VENDOR_REG 0x10 @@ -36,6 +37,18 @@ #define LOONGARCH_MAX_CPUS 256 +/* MMIO memory region */ +#define VIRT_PCH_REG_BASE 0x10000000UL +#define VIRT_PCH_REG_SIZE 0x400 +#define VIRT_IOAPIC_REG_BASE (VIRT_PCH_REG_BASE) +#define VIRT_MISC_REG_BASE (VIRT_PCH_REG_BASE + 0x00080000) +#define VIRT_RTC_REG_BASE (VIRT_MISC_REG_BASE + 0x00050100) +#define VIRT_RTC_LEN 0x100 +#define VIRT_PLATFORM_BUS_BASEADDRESS 0x16000000UL +#define VIRT_PLATFORM_BUS_SIZE 0x02000000 +#define VIRT_PCI_IO_BASE 0x18004000UL +#define VIRT_PCI_IO_OFFSET 0x4000 +#define VIRT_PCI_IO_SIZE 0xC000 #define VIRT_FWCFG_BASE 0x1e020000UL #define VIRT_BIOS_BASE 0x1c000000UL #define VIRT_BIOS_SIZE (16 * MiB) @@ -44,6 +57,16 @@ #define VIRT_FLASH0_SIZE VIRT_BIOS_SIZE #define VIRT_FLASH1_BASE 0x1d000000UL #define VIRT_FLASH1_SIZE (16 * MiB) +#define VIRT_UART_BASE 0x1fe001e0UL +#define VIRT_UART_SIZE 0x100 +#define VIRT_PCI_CFG_BASE 0x20000000UL +#define VIRT_PCI_CFG_SIZE 0x08000000UL +#define VIRT_DINTC_BASE 0x2FE00000UL +#define VIRT_DINTC_SIZE 0x00100000UL +#define VIRT_PCH_MSI_ADDR_LOW 0x2FF00000UL +#define VIRT_PCH_MSI_SIZE 0x8 +#define VIRT_PCI_MEM_BASE 0x40000000UL +#define VIRT_PCI_MEM_SIZE 0x40000000UL #define VIRT_LOWMEM_BASE 0 #define VIRT_LOWMEM_SIZE 0x10000000 @@ -53,6 +76,22 @@ #define VIRT_GED_REG_ADDR QEMU_ALIGN_UP(VIRT_GED_MEM_ADDR + MEMORY_HOTPLUG_IO_LEN, 4) #define VIRT_GED_CPUHP_ADDR QEMU_ALIGN_UP(VIRT_GED_REG_ADDR + ACPI_GED_REG_COUNT, 4) +/* + * GSI_BASE is hard-coded with 64 in linux kernel, else kernel fails to boot + * 0 - 15 GSI for ISA devices even if there is no ISA devices + * 16 - 63 GSI for CPU devices such as timers/perf monitor etc + * 64 - GSI for external devices + */ +#define VIRT_PCH_PIC_IRQ_NUM 32 +#define VIRT_GSI_BASE 64 +#define VIRT_DEVICE_IRQS 16 +#define VIRT_UART_IRQ (VIRT_GSI_BASE + 2) +#define VIRT_UART_COUNT 4 +#define VIRT_RTC_IRQ (VIRT_GSI_BASE + 6) +#define VIRT_SCI_IRQ (VIRT_GSI_BASE + 7) +#define VIRT_PLATFORM_BUS_IRQ (VIRT_GSI_BASE + 8) +#define VIRT_PLATFORM_BUS_NUM_IRQS 2 + #define COMMAND_LINE_SIZE 512 #define FDT_BASE 0x100000 diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h index bfdbfe36146da..33e7942de9f7b 100644 --- a/include/hw/pci-host/ls7a.h +++ b/include/hw/pci-host/ls7a.h @@ -13,43 +13,4 @@ #include "qemu/range.h" #include "qom/object.h" -#define VIRT_PCI_MEM_BASE 0x40000000UL -#define VIRT_PCI_MEM_SIZE 0x40000000UL -#define VIRT_PCI_IO_OFFSET 0x4000 -#define VIRT_PCI_CFG_BASE 0x20000000 -#define VIRT_PCI_CFG_SIZE 0x08000000 -#define VIRT_PCI_IO_BASE 0x18004000UL -#define VIRT_PCI_IO_SIZE 0xC000 - -#define VIRT_PCH_REG_BASE 0x10000000UL -#define VIRT_IOAPIC_REG_BASE (VIRT_PCH_REG_BASE) -#define VIRT_PCH_MSI_ADDR_LOW 0x2FF00000UL -#define VIRT_DINTC_SIZE 0x100000UL -#define VIRT_DINTC_BASE 0x2FE00000UL -#define VIRT_PCH_REG_SIZE 0x400 -#define VIRT_PCH_MSI_SIZE 0x8 - -/* - * GSI_BASE is hard-coded with 64 in linux kernel, else kernel fails to boot - * 0 - 15 GSI for ISA devices even if there is no ISA devices - * 16 - 63 GSI for CPU devices such as timers/perf monitor etc - * 64 - GSI for external devices - */ -#define VIRT_PCH_PIC_IRQ_NUM 32 -#define VIRT_GSI_BASE 64 -#define VIRT_DEVICE_IRQS 16 -#define VIRT_UART_COUNT 4 -#define VIRT_UART_IRQ (VIRT_GSI_BASE + 2) -#define VIRT_UART_BASE 0x1fe001e0 -#define VIRT_UART_SIZE 0x100 -#define VIRT_RTC_IRQ (VIRT_GSI_BASE + 6) -#define VIRT_MISC_REG_BASE (VIRT_PCH_REG_BASE + 0x00080000) -#define VIRT_RTC_REG_BASE (VIRT_MISC_REG_BASE + 0x00050100) -#define VIRT_RTC_LEN 0x100 -#define VIRT_SCI_IRQ (VIRT_GSI_BASE + 7) - -#define VIRT_PLATFORM_BUS_BASEADDRESS 0x16000000 -#define VIRT_PLATFORM_BUS_SIZE 0x2000000 -#define VIRT_PLATFORM_BUS_NUM_IRQS 2 -#define VIRT_PLATFORM_BUS_IRQ (VIRT_GSI_BASE + 8) #endif From a9c5e0ed9b63becde7a6188bf71f95e31a132b19 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 13 Oct 2025 14:35:16 +0800 Subject: [PATCH 1594/1794] hw/loongarch/virt: Sort order by hardware device base address With header file include/hw/loongarch/virt.h, hardware device definition order is sorted by its base address. Add remove unused macro VIRT_IOAPIC_REG_BASE and VIRT_MISC_REG_BASE. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/loongarch/virt.c | 2 +- include/hw/loongarch/virt.h | 42 +++++++++++++++++-------------------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index efd1f9ac49d67..49434ad1828b0 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -520,7 +520,7 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) } /* PCH_PIC memory region */ - memory_region_add_subregion(get_system_memory(), VIRT_IOAPIC_REG_BASE, + memory_region_add_subregion(get_system_memory(), VIRT_PCH_REG_BASE, sysbus_mmio_get_region(SYS_BUS_DEVICE(pch_pic), 0)); /* Connect pch_pic irqs to extioi */ diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 0cc1b499a7bce..27b1755802092 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -13,50 +13,47 @@ #include "hw/block/flash.h" #include "hw/loongarch/boot.h" -#define IOCSRF_TEMP 0 -#define IOCSRF_NODECNT 1 -#define IOCSRF_MSI 2 -#define IOCSRF_EXTIOI 3 -#define IOCSRF_CSRIPI 4 -#define IOCSRF_FREQCSR 5 -#define IOCSRF_FREQSCALE 6 -#define IOCSRF_DVFSV1 7 -#define IOCSRF_GMOD 9 -#define IOCSRF_VM 11 -#define IOCSRF_DMSI 15 - /* IOCSR region */ #define VERSION_REG 0x0 #define FEATURE_REG 0x8 +#define IOCSRF_TEMP 0 +#define IOCSRF_NODECNT 1 +#define IOCSRF_MSI 2 +#define IOCSRF_EXTIOI 3 +#define IOCSRF_CSRIPI 4 +#define IOCSRF_FREQCSR 5 +#define IOCSRF_FREQSCALE 6 +#define IOCSRF_DVFSV1 7 +#define IOCSRF_GMOD 9 +#define IOCSRF_VM 11 +#define IOCSRF_DMSI 15 #define VENDOR_REG 0x10 #define CPUNAME_REG 0x20 #define MISC_FUNC_REG 0x420 -#define IOCSRM_EXTIOI_EN 48 -#define IOCSRM_EXTIOI_INT_ENCODE 49 -#define IOCSRM_DMSI_EN 51 +#define IOCSRM_EXTIOI_EN 48 +#define IOCSRM_EXTIOI_INT_ENCODE 49 +#define IOCSRM_DMSI_EN 51 #define LOONGARCH_MAX_CPUS 256 /* MMIO memory region */ #define VIRT_PCH_REG_BASE 0x10000000UL #define VIRT_PCH_REG_SIZE 0x400 -#define VIRT_IOAPIC_REG_BASE (VIRT_PCH_REG_BASE) -#define VIRT_MISC_REG_BASE (VIRT_PCH_REG_BASE + 0x00080000) -#define VIRT_RTC_REG_BASE (VIRT_MISC_REG_BASE + 0x00050100) +#define VIRT_RTC_REG_BASE 0x100d0100UL #define VIRT_RTC_LEN 0x100 #define VIRT_PLATFORM_BUS_BASEADDRESS 0x16000000UL #define VIRT_PLATFORM_BUS_SIZE 0x02000000 #define VIRT_PCI_IO_BASE 0x18004000UL #define VIRT_PCI_IO_OFFSET 0x4000 #define VIRT_PCI_IO_SIZE 0xC000 -#define VIRT_FWCFG_BASE 0x1e020000UL #define VIRT_BIOS_BASE 0x1c000000UL -#define VIRT_BIOS_SIZE (16 * MiB) +#define VIRT_BIOS_SIZE 0x01000000UL #define VIRT_FLASH_SECTOR_SIZE (256 * KiB) #define VIRT_FLASH0_BASE VIRT_BIOS_BASE #define VIRT_FLASH0_SIZE VIRT_BIOS_SIZE #define VIRT_FLASH1_BASE 0x1d000000UL -#define VIRT_FLASH1_SIZE (16 * MiB) +#define VIRT_FLASH1_SIZE 0x01000000UL +#define VIRT_FWCFG_BASE 0x1e020000UL #define VIRT_UART_BASE 0x1fe001e0UL #define VIRT_UART_SIZE 0x100 #define VIRT_PCI_CFG_BASE 0x20000000UL @@ -70,6 +67,7 @@ #define VIRT_LOWMEM_BASE 0 #define VIRT_LOWMEM_SIZE 0x10000000 +#define FDT_BASE 0x100000 #define VIRT_HIGHMEM_BASE 0x80000000 #define VIRT_GED_EVT_ADDR 0x100e0000 #define VIRT_GED_MEM_ADDR QEMU_ALIGN_UP(VIRT_GED_EVT_ADDR + ACPI_GED_EVT_SEL_LEN, 4) @@ -94,8 +92,6 @@ #define COMMAND_LINE_SIZE 512 -#define FDT_BASE 0x100000 - struct LoongArchVirtMachineState { /*< private >*/ MachineState parent_obj; From 482869ca84a33c4d407f580400033fa5f146c1a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Oct 2025 05:12:51 +0200 Subject: [PATCH 1595/1794] accel/tcg: Name gen_goto_tb()'s TB slot index as @tb_slot_idx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tcg_gen_goto_tb() takes an unsigned index to the TB slot (0 or 1). Declare the argument as unsigned and rename it as @tb_slot_idx (which is more descriptive than @n) on all targets. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20251010031745.37528-1-philmd@linaro.org> --- target/alpha/translate.c | 8 ++++---- target/arm/tcg/translate-a64.c | 8 ++++---- target/arm/tcg/translate.c | 8 ++++---- target/avr/translate.c | 7 ++++--- target/hexagon/translate.c | 8 ++++---- target/loongarch/tcg/translate.c | 7 ++++--- target/microblaze/translate.c | 7 ++++--- target/mips/tcg/translate.c | 7 ++++--- target/ppc/translate.c | 7 ++++--- target/riscv/translate.c | 9 +++++---- target/rx/translate.c | 7 ++++--- target/sh4/translate.c | 7 ++++--- target/sparc/translate.c | 6 +++--- target/tricore/translate.c | 7 ++++--- 14 files changed, 56 insertions(+), 47 deletions(-) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index f11b382438c2e..a492520075e35 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -436,18 +436,18 @@ static DisasJumpType gen_store_conditional(DisasContext *ctx, int ra, int rb, return DISAS_NEXT; } -static void gen_goto_tb(DisasContext *ctx, int idx, int32_t disp) +static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_idx, int32_t disp) { if (translator_use_goto_tb(&ctx->base, ctx->base.pc_next + disp)) { /* With PCREL, PC must always be up-to-date. */ if (ctx->pcrel) { gen_pc_disp(ctx, cpu_pc, disp); - tcg_gen_goto_tb(idx); + tcg_gen_goto_tb(tb_slot_idx); } else { - tcg_gen_goto_tb(idx); + tcg_gen_goto_tb(tb_slot_idx); gen_pc_disp(ctx, cpu_pc, disp); } - tcg_gen_exit_tb(ctx->base.tb, idx); + tcg_gen_exit_tb(ctx->base.tb, tb_slot_idx); } else { gen_pc_disp(ctx, cpu_pc, disp); tcg_gen_lookup_and_goto_ptr(); diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 918d5ed112007..3292d7cbfd9b8 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -528,7 +528,7 @@ static inline bool use_goto_tb(DisasContext *s, uint64_t dest) return translator_use_goto_tb(&s->base, dest); } -static void gen_goto_tb(DisasContext *s, int n, int64_t diff) +static void gen_goto_tb(DisasContext *s, unsigned tb_slot_idx, int64_t diff) { if (use_goto_tb(s, s->pc_curr + diff)) { /* @@ -541,12 +541,12 @@ static void gen_goto_tb(DisasContext *s, int n, int64_t diff) */ if (tb_cflags(s->base.tb) & CF_PCREL) { gen_a64_update_pc(s, diff); - tcg_gen_goto_tb(n); + tcg_gen_goto_tb(tb_slot_idx); } else { - tcg_gen_goto_tb(n); + tcg_gen_goto_tb(tb_slot_idx); gen_a64_update_pc(s, diff); } - tcg_gen_exit_tb(s->base.tb, n); + tcg_gen_exit_tb(s->base.tb, tb_slot_idx); s->base.is_jmp = DISAS_NORETURN; } else { gen_a64_update_pc(s, diff); diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 3df0bbcb7f800..5f64fed220275 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -1310,7 +1310,7 @@ static void gen_goto_ptr(void) * cpu_loop_exec. Any live exit_requests will be processed as we * enter the next TB. */ -static void gen_goto_tb(DisasContext *s, int n, target_long diff) +static void gen_goto_tb(DisasContext *s, unsigned tb_slot_idx, target_long diff) { if (translator_use_goto_tb(&s->base, s->pc_curr + diff)) { /* @@ -1323,12 +1323,12 @@ static void gen_goto_tb(DisasContext *s, int n, target_long diff) */ if (tb_cflags(s->base.tb) & CF_PCREL) { gen_update_pc(s, diff); - tcg_gen_goto_tb(n); + tcg_gen_goto_tb(tb_slot_idx); } else { - tcg_gen_goto_tb(n); + tcg_gen_goto_tb(tb_slot_idx); gen_update_pc(s, diff); } - tcg_gen_exit_tb(s->base.tb, n); + tcg_gen_exit_tb(s->base.tb, tb_slot_idx); } else { gen_update_pc(s, diff); gen_goto_ptr(); diff --git a/target/avr/translate.c b/target/avr/translate.c index 804b0b21dbd56..ef6f655a458b7 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -981,14 +981,15 @@ static void gen_pop_ret(DisasContext *ctx, TCGv ret) } } -static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_idx, + target_ulong dest) { const TranslationBlock *tb = ctx->base.tb; if (translator_use_goto_tb(&ctx->base, dest)) { - tcg_gen_goto_tb(n); + tcg_gen_goto_tb(tb_slot_idx); tcg_gen_movi_i32(cpu_pc, dest); - tcg_gen_exit_tb(tb, n); + tcg_gen_exit_tb(tb, tb_slot_idx); } else { tcg_gen_movi_i32(cpu_pc, dest); tcg_gen_lookup_and_goto_ptr(); diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 02fd40c160feb..50766eafe27f3 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -133,15 +133,15 @@ static bool use_goto_tb(DisasContext *ctx, target_ulong dest) return translator_use_goto_tb(&ctx->base, dest); } -static void gen_goto_tb(DisasContext *ctx, int idx, target_ulong dest, bool - move_to_pc) +static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_idx, + target_ulong dest, bool move_to_pc) { if (use_goto_tb(ctx, dest)) { - tcg_gen_goto_tb(idx); + tcg_gen_goto_tb(tb_slot_idx); if (move_to_pc) { tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], dest); } - tcg_gen_exit_tb(ctx->base.tb, idx); + tcg_gen_exit_tb(ctx->base.tb, tb_slot_idx); } else { if (move_to_pc) { tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], dest); diff --git a/target/loongarch/tcg/translate.c b/target/loongarch/tcg/translate.c index 53a0b4c3ce91e..57c3b8c4bb2dd 100644 --- a/target/loongarch/tcg/translate.c +++ b/target/loongarch/tcg/translate.c @@ -99,16 +99,17 @@ void generate_exception(DisasContext *ctx, int excp) ctx->base.is_jmp = DISAS_NORETURN; } -static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_idx, + target_ulong dest) { if (ctx->va32) { dest = (uint32_t) dest; } if (translator_use_goto_tb(&ctx->base, dest)) { - tcg_gen_goto_tb(n); + tcg_gen_goto_tb(tb_slot_idx); tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_exit_tb(ctx->base.tb, n); + tcg_gen_exit_tb(ctx->base.tb, tb_slot_idx); } else { tcg_gen_movi_tl(cpu_pc, dest); tcg_gen_lookup_and_goto_ptr(); diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 5098a1db4dc7d..3dd74b021e8fe 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -116,12 +116,13 @@ static void gen_raise_hw_excp(DisasContext *dc, uint32_t esr_ec) gen_raise_exception_sync(dc, EXCP_HW_EXCP); } -static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) +static void gen_goto_tb(DisasContext *dc, unsigned tb_slot_idx, + target_ulong dest) { if (translator_use_goto_tb(&dc->base, dest)) { - tcg_gen_goto_tb(n); + tcg_gen_goto_tb(tb_slot_idx); tcg_gen_movi_i32(cpu_pc, dest); - tcg_gen_exit_tb(dc->base.tb, n); + tcg_gen_exit_tb(dc->base.tb, tb_slot_idx); } else { tcg_gen_movi_i32(cpu_pc, dest); tcg_gen_lookup_and_goto_ptr(); diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index d91d6efe02ca3..54849e9ff1a4f 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -4362,12 +4362,13 @@ static void gen_trap(DisasContext *ctx, uint32_t opc, } } -static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_idx, + target_ulong dest) { if (translator_use_goto_tb(&ctx->base, dest)) { - tcg_gen_goto_tb(n); + tcg_gen_goto_tb(tb_slot_idx); gen_save_pc(dest); - tcg_gen_exit_tb(ctx->base.tb, n); + tcg_gen_exit_tb(ctx->base.tb, tb_slot_idx); } else { gen_save_pc(dest); tcg_gen_lookup_and_goto_ptr(); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index d422789a1d0d2..17e6d07c8c2db 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3668,16 +3668,17 @@ static void gen_lookup_and_goto_ptr(DisasContext *ctx) } /*** Branch ***/ -static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_idx, + target_ulong dest) { if (NARROW_MODE(ctx)) { dest = (uint32_t) dest; } if (use_goto_tb(ctx, dest)) { pmu_count_insns(ctx); - tcg_gen_goto_tb(n); + tcg_gen_goto_tb(tb_slot_idx); tcg_gen_movi_tl(cpu_nip, dest & ~3); - tcg_gen_exit_tb(ctx->base.tb, n); + tcg_gen_exit_tb(ctx->base.tb, tb_slot_idx); } else { tcg_gen_movi_tl(cpu_nip, dest & ~3); gen_lookup_and_goto_ptr(ctx); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 6fc06c71f5136..9a53aecbfe998 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -286,7 +286,8 @@ static void exit_tb(DisasContext *ctx) tcg_gen_exit_tb(NULL, 0); } -static void gen_goto_tb(DisasContext *ctx, int n, target_long diff) +static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_idx, + target_long diff) { target_ulong dest = ctx->base.pc_next + diff; @@ -305,12 +306,12 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_long diff) */ if (tb_cflags(ctx->base.tb) & CF_PCREL) { gen_update_pc(ctx, diff); - tcg_gen_goto_tb(n); + tcg_gen_goto_tb(tb_slot_idx); } else { - tcg_gen_goto_tb(n); + tcg_gen_goto_tb(tb_slot_idx); gen_update_pc(ctx, diff); } - tcg_gen_exit_tb(ctx->base.tb, n); + tcg_gen_exit_tb(ctx->base.tb, tb_slot_idx); } else { gen_update_pc(ctx, diff); lookup_and_goto_ptr(ctx); diff --git a/target/rx/translate.c b/target/rx/translate.c index 19a9584a82974..5fc589c706b60 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -147,12 +147,13 @@ void rx_cpu_dump_state(CPUState *cs, FILE *f, int flags) } } -static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) +static void gen_goto_tb(DisasContext *dc, unsigned tb_slot_idx, + target_ulong dest) { if (translator_use_goto_tb(&dc->base, dest)) { - tcg_gen_goto_tb(n); + tcg_gen_goto_tb(tb_slot_idx); tcg_gen_movi_i32(cpu_pc, dest); - tcg_gen_exit_tb(dc->base.tb, n); + tcg_gen_exit_tb(dc->base.tb, tb_slot_idx); } else { tcg_gen_movi_i32(cpu_pc, dest); tcg_gen_lookup_and_goto_ptr(); diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 70fd13aa3f55e..137b75a4fb2e1 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -231,12 +231,13 @@ static bool use_goto_tb(DisasContext *ctx, target_ulong dest) return translator_use_goto_tb(&ctx->base, dest); } -static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_idx, + target_ulong dest) { if (use_goto_tb(ctx, dest)) { - tcg_gen_goto_tb(n); + tcg_gen_goto_tb(tb_slot_idx); tcg_gen_movi_i32(cpu_pc, dest); - tcg_gen_exit_tb(ctx->base.tb, n); + tcg_gen_exit_tb(ctx->base.tb, tb_slot_idx); } else { tcg_gen_movi_i32(cpu_pc, dest); if (use_exit_tb(ctx)) { diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 810e2491a61d7..d6b599b71fee8 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -363,15 +363,15 @@ static bool use_goto_tb(DisasContext *s, target_ulong pc, target_ulong npc) translator_use_goto_tb(&s->base, npc); } -static void gen_goto_tb(DisasContext *s, int tb_num, +static void gen_goto_tb(DisasContext *s, unsigned tb_slot_idx, target_ulong pc, target_ulong npc) { if (use_goto_tb(s, pc, npc)) { /* jump to same page: we can use a direct jump */ - tcg_gen_goto_tb(tb_num); + tcg_gen_goto_tb(tb_slot_idx); tcg_gen_movi_tl(cpu_pc, pc); tcg_gen_movi_tl(cpu_npc, npc); - tcg_gen_exit_tb(s->base.tb, tb_num); + tcg_gen_exit_tb(s->base.tb, tb_slot_idx); } else { /* jump to another page: we can use an indirect jump */ tcg_gen_movi_tl(cpu_pc, pc); diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 3d0e7a10bd8ac..7c6e3095971d6 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -2816,12 +2816,13 @@ static inline void gen_save_pc(target_ulong pc) tcg_gen_movi_tl(cpu_PC, pc); } -static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_index, + target_ulong dest) { if (translator_use_goto_tb(&ctx->base, dest)) { - tcg_gen_goto_tb(n); + tcg_gen_goto_tb(tb_slot_index); gen_save_pc(dest); - tcg_gen_exit_tb(ctx->base.tb, n); + tcg_gen_exit_tb(ctx->base.tb, tb_slot_index); } else { gen_save_pc(dest); tcg_gen_lookup_and_goto_ptr(); From a6a5f0b4644ed41878936d264bc23dd72532771f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 27 Aug 2025 14:07:26 +0200 Subject: [PATCH 1596/1794] target/alpha: Access CPUState::cpu_index via helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPUState::cpu_index is a target agnostic field, meant for common code (i.e. accel/ and system/ folders). Target specific code should use the CPUClass::get_arch_id() helper, even if there is a 1:1 mapping. In preparation of generic changes around CPU indexing, introduce the whoami helper to access the generic CPUState::cpu_index field. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Richard Henderson Message-Id: <20250925010438.59755-1-philmd@linaro.org> --- target/alpha/helper.h | 1 + target/alpha/sys_helper.c | 5 +++++ target/alpha/translate.c | 3 +-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/target/alpha/helper.h b/target/alpha/helper.h index 788d2fbf289b9..954a5c8294cf5 100644 --- a/target/alpha/helper.h +++ b/target/alpha/helper.h @@ -92,6 +92,7 @@ DEF_HELPER_FLAGS_1(tbia, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(tbis, TCG_CALL_NO_RWG, void, env, i64) DEF_HELPER_1(halt, void, i64) +DEF_HELPER_1(whami, i64, env) DEF_HELPER_FLAGS_0(get_vmtime, TCG_CALL_NO_RWG, i64) DEF_HELPER_FLAGS_0(get_walltime, TCG_CALL_NO_RWG, i64) diff --git a/target/alpha/sys_helper.c b/target/alpha/sys_helper.c index 87e37605c133f..0e0a619975bd0 100644 --- a/target/alpha/sys_helper.c +++ b/target/alpha/sys_helper.c @@ -67,3 +67,8 @@ void helper_set_alarm(CPUAlphaState *env, uint64_t expire) timer_del(cpu->alarm_timer); } } + +uint64_t HELPER(whami)(CPUAlphaState *env) +{ + return env_cpu(env)->cpu_index; +} diff --git a/target/alpha/translate.c b/target/alpha/translate.c index a492520075e35..b1d8a4eb80ae6 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -1126,8 +1126,7 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode) break; case 0x3C: /* WHAMI */ - tcg_gen_ld32s_i64(ctx->ir[IR_V0], tcg_env, - -offsetof(AlphaCPU, env) + offsetof(CPUState, cpu_index)); + gen_helper_whami(ctx->ir[IR_V0], tcg_env); break; case 0x3E: From 5940712ed0f770cae4d5e5999dc46b6675db8120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 25 Sep 2025 02:31:34 +0200 Subject: [PATCH 1597/1794] target/alpha: Replace VMSTATE_UINTTL() -> VMSTATE_UINT64() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All these CPUAlphaState fields are of uint64_t type (except the @fir[] array which uses float64, expanded to the same type definition). Use the appropriate VMSTATE_UINT64() macro. There is no functional change (the migration stream is not modified), because the Alpha targets are only built as 64-bit: $ git grep TARGET_LONG_BITS configs/targets/alpha* configs/targets/alpha-linux-user.mak:4:TARGET_LONG_BITS=64 configs/targets/alpha-softmmu.mak:2:TARGET_LONG_BITS=64 Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20250925005137.59378-1-philmd@linaro.org> --- target/alpha/machine.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/target/alpha/machine.c b/target/alpha/machine.c index 5f302b166da61..6828b123ca104 100644 --- a/target/alpha/machine.c +++ b/target/alpha/machine.c @@ -25,8 +25,8 @@ static const VMStateInfo vmstate_fpcr = { }; static const VMStateField vmstate_env_fields[] = { - VMSTATE_UINTTL_ARRAY(ir, CPUAlphaState, 31), - VMSTATE_UINTTL_ARRAY(fir, CPUAlphaState, 31), + VMSTATE_UINT64_ARRAY(ir, CPUAlphaState, 31), + VMSTATE_UINT64_ARRAY(fir, CPUAlphaState, 31), /* Save the architecture value of the fpcr, not the internally expanded version. Since this architecture value does not exist in memory to be stored, this requires a but of hoop @@ -41,27 +41,27 @@ static const VMStateField vmstate_env_fields[] = { .flags = VMS_SINGLE, .offset = 0 }, - VMSTATE_UINTTL(pc, CPUAlphaState), - VMSTATE_UINTTL(unique, CPUAlphaState), - VMSTATE_UINTTL(lock_addr, CPUAlphaState), - VMSTATE_UINTTL(lock_value, CPUAlphaState), + VMSTATE_UINT64(pc, CPUAlphaState), + VMSTATE_UINT64(unique, CPUAlphaState), + VMSTATE_UINT64(lock_addr, CPUAlphaState), + VMSTATE_UINT64(lock_value, CPUAlphaState), VMSTATE_UINT32(flags, CPUAlphaState), VMSTATE_UINT32(pcc_ofs, CPUAlphaState), - VMSTATE_UINTTL(trap_arg0, CPUAlphaState), - VMSTATE_UINTTL(trap_arg1, CPUAlphaState), - VMSTATE_UINTTL(trap_arg2, CPUAlphaState), + VMSTATE_UINT64(trap_arg0, CPUAlphaState), + VMSTATE_UINT64(trap_arg1, CPUAlphaState), + VMSTATE_UINT64(trap_arg2, CPUAlphaState), - VMSTATE_UINTTL(exc_addr, CPUAlphaState), - VMSTATE_UINTTL(palbr, CPUAlphaState), - VMSTATE_UINTTL(ptbr, CPUAlphaState), - VMSTATE_UINTTL(vptptr, CPUAlphaState), - VMSTATE_UINTTL(sysval, CPUAlphaState), - VMSTATE_UINTTL(usp, CPUAlphaState), + VMSTATE_UINT64(exc_addr, CPUAlphaState), + VMSTATE_UINT64(palbr, CPUAlphaState), + VMSTATE_UINT64(ptbr, CPUAlphaState), + VMSTATE_UINT64(vptptr, CPUAlphaState), + VMSTATE_UINT64(sysval, CPUAlphaState), + VMSTATE_UINT64(usp, CPUAlphaState), - VMSTATE_UINTTL_ARRAY(shadow, CPUAlphaState, 8), - VMSTATE_UINTTL_ARRAY(scratch, CPUAlphaState, 24), + VMSTATE_UINT64_ARRAY(shadow, CPUAlphaState, 8), + VMSTATE_UINT64_ARRAY(scratch, CPUAlphaState, 24), VMSTATE_END_OF_LIST() }; From 152820a991e6b0b4cbbdb594f87e43252626c0da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 2 Oct 2025 16:40:37 +0200 Subject: [PATCH 1598/1794] target/i386/monitor: Propagate CPU address space to 'info mem' handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to replace the cpu_physical_memory_read() calls by address_space_read() equivalents. Since the latter requires an address space, and these commands are run in the context of a vCPU, propagate its first address space. Next commit will do the replacements. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Message-Id: <20251002145742.75624-2-philmd@linaro.org> --- target/i386/monitor.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 3c9b6ca62f2a2..7e7854e6c1b2c 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -68,7 +68,7 @@ static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr, pte & PG_RW_MASK ? 'W' : '-'); } -static void tlb_info_32(Monitor *mon, CPUArchState *env) +static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as) { unsigned int l1, l2; uint32_t pgd, pde, pte; @@ -96,7 +96,7 @@ static void tlb_info_32(Monitor *mon, CPUArchState *env) } } -static void tlb_info_pae32(Monitor *mon, CPUArchState *env) +static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as) { unsigned int l1, l2, l3; uint64_t pdpe, pde, pte; @@ -136,7 +136,7 @@ static void tlb_info_pae32(Monitor *mon, CPUArchState *env) } #ifdef TARGET_X86_64 -static void tlb_info_la48(Monitor *mon, CPUArchState *env, +static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as, uint64_t l0, uint64_t pml4_addr) { uint64_t l1, l2, l3, l4; @@ -197,7 +197,7 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, } } -static void tlb_info_la57(Monitor *mon, CPUArchState *env) +static void tlb_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as) { uint64_t l0; uint64_t pml5e; @@ -208,7 +208,7 @@ static void tlb_info_la57(Monitor *mon, CPUArchState *env) cpu_physical_memory_read(pml5_addr + l0 * 8, &pml5e, 8); pml5e = le64_to_cpu(pml5e); if (pml5e & PG_PRESENT_MASK) { - tlb_info_la48(mon, env, l0, pml5e & 0x3fffffffff000ULL); + tlb_info_la48(mon, env, as, l0, pml5e & 0x3fffffffff000ULL); } } } @@ -217,6 +217,7 @@ static void tlb_info_la57(Monitor *mon, CPUArchState *env) void hmp_info_tlb(Monitor *mon, const QDict *qdict) { CPUArchState *env; + AddressSpace *as; env = mon_get_cpu_env(mon); if (!env) { @@ -228,21 +229,22 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict) monitor_printf(mon, "PG disabled\n"); return; } + as = cpu_get_address_space(env_cpu(env), X86ASIdx_MEM); if (env->cr[4] & CR4_PAE_MASK) { #ifdef TARGET_X86_64 if (env->hflags & HF_LMA_MASK) { if (env->cr[4] & CR4_LA57_MASK) { - tlb_info_la57(mon, env); + tlb_info_la57(mon, env, as); } else { - tlb_info_la48(mon, env, 0, env->cr[3] & 0x3fffffffff000ULL); + tlb_info_la48(mon, env, as, 0, env->cr[3] & 0x3fffffffff000ULL); } } else #endif { - tlb_info_pae32(mon, env); + tlb_info_pae32(mon, env, as); } } else { - tlb_info_32(mon, env); + tlb_info_32(mon, env, as); } } @@ -271,7 +273,7 @@ static void mem_print(Monitor *mon, CPUArchState *env, } } -static void mem_info_32(Monitor *mon, CPUArchState *env) +static void mem_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as) { unsigned int l1, l2; int prot, last_prot; @@ -312,7 +314,7 @@ static void mem_info_32(Monitor *mon, CPUArchState *env) mem_print(mon, env, &start, &last_prot, (hwaddr)1 << 32, 0); } -static void mem_info_pae32(Monitor *mon, CPUArchState *env) +static void mem_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as) { unsigned int l1, l2, l3; int prot, last_prot; @@ -369,7 +371,7 @@ static void mem_info_pae32(Monitor *mon, CPUArchState *env) #ifdef TARGET_X86_64 -static void mem_info_la48(Monitor *mon, CPUArchState *env) +static void mem_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as) { int prot, last_prot; uint64_t l1, l2, l3, l4; @@ -449,7 +451,7 @@ static void mem_info_la48(Monitor *mon, CPUArchState *env) mem_print(mon, env, &start, &last_prot, (hwaddr)1 << 48, 0); } -static void mem_info_la57(Monitor *mon, CPUArchState *env) +static void mem_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as) { int prot, last_prot; uint64_t l0, l1, l2, l3, l4; @@ -545,6 +547,7 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env) void hmp_info_mem(Monitor *mon, const QDict *qdict) { CPUArchState *env; + AddressSpace *as; env = mon_get_cpu_env(mon); if (!env) { @@ -556,21 +559,22 @@ void hmp_info_mem(Monitor *mon, const QDict *qdict) monitor_printf(mon, "PG disabled\n"); return; } + as = cpu_get_address_space(env_cpu(env), X86ASIdx_MEM); if (env->cr[4] & CR4_PAE_MASK) { #ifdef TARGET_X86_64 if (env->hflags & HF_LMA_MASK) { if (env->cr[4] & CR4_LA57_MASK) { - mem_info_la57(mon, env); + mem_info_la57(mon, env, as); } else { - mem_info_la48(mon, env); + mem_info_la48(mon, env, as); } } else #endif { - mem_info_pae32(mon, env); + mem_info_pae32(mon, env, as); } } else { - mem_info_32(mon, env); + mem_info_32(mon, env, as); } } From 17db4d61d148aa87b84907e3aa6bee95d29955a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 2 Oct 2025 05:02:10 +0200 Subject: [PATCH 1599/1794] target/i386/monitor: Replace legacy cpu_physical_memory_read() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit b7ecba0f6f6 ("docs/devel/loads-stores.rst: Document our various load and store APIs") mentioned cpu_physical_memory_*() methods are legacy, the replacement being address_space_*(). Replace: - cpu_physical_memory_read(len=4) -> address_space_ldl() - cpu_physical_memory_read(len=8) -> address_space_ldq() inlining the little endianness conversion via the '_le' suffix. As with the previous implementation, ignore whether the memory read succeeded or failed. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Message-Id: <20251002145742.75624-3-philmd@linaro.org> --- target/i386/monitor.c | 96 ++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 52 deletions(-) diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 7e7854e6c1b2c..d2bb873d49473 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -30,6 +30,7 @@ #include "qobject/qdict.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc.h" +#include "system/memory.h" /* Perform linear address sign extension */ static hwaddr addr_canonical(CPUArchState *env, hwaddr addr) @@ -70,21 +71,21 @@ static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr, static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as) { + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; unsigned int l1, l2; uint32_t pgd, pde, pte; pgd = env->cr[3] & ~0xfff; for(l1 = 0; l1 < 1024; l1++) { - cpu_physical_memory_read(pgd + l1 * 4, &pde, 4); - pde = le32_to_cpu(pde); + pde = address_space_ldl_le(as, pgd + l1 * 4, attrs, NULL); if (pde & PG_PRESENT_MASK) { if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { /* 4M pages */ print_pte(mon, env, (l1 << 22), pde, ~((1 << 21) - 1)); } else { for(l2 = 0; l2 < 1024; l2++) { - cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, &pte, 4); - pte = le32_to_cpu(pte); + pte = address_space_ldl_le(as, (pde & ~0xfff) + l2 * 4, + attrs, NULL); if (pte & PG_PRESENT_MASK) { print_pte(mon, env, (l1 << 22) + (l2 << 12), pte & ~PG_PSE_MASK, @@ -98,19 +99,18 @@ static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as) static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as) { + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; unsigned int l1, l2, l3; uint64_t pdpe, pde, pte; uint64_t pdp_addr, pd_addr, pt_addr; pdp_addr = env->cr[3] & ~0x1f; for (l1 = 0; l1 < 4; l1++) { - cpu_physical_memory_read(pdp_addr + l1 * 8, &pdpe, 8); - pdpe = le64_to_cpu(pdpe); + pdpe = address_space_ldq_le(as, pdp_addr + l1 * 8, attrs, NULL); if (pdpe & PG_PRESENT_MASK) { pd_addr = pdpe & 0x3fffffffff000ULL; for (l2 = 0; l2 < 512; l2++) { - cpu_physical_memory_read(pd_addr + l2 * 8, &pde, 8); - pde = le64_to_cpu(pde); + pde = address_space_ldq_le(as, pd_addr + l2 * 8, attrs, NULL); if (pde & PG_PRESENT_MASK) { if (pde & PG_PSE_MASK) { /* 2M pages with PAE, CR4.PSE is ignored */ @@ -119,8 +119,8 @@ static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as) } else { pt_addr = pde & 0x3fffffffff000ULL; for (l3 = 0; l3 < 512; l3++) { - cpu_physical_memory_read(pt_addr + l3 * 8, &pte, 8); - pte = le64_to_cpu(pte); + pte = address_space_ldq_le(as, pt_addr + l3 * 8, + attrs, NULL); if (pte & PG_PRESENT_MASK) { print_pte(mon, env, (l1 << 30) + (l2 << 21) + (l3 << 12), @@ -139,21 +139,20 @@ static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as) static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as, uint64_t l0, uint64_t pml4_addr) { + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; uint64_t l1, l2, l3, l4; uint64_t pml4e, pdpe, pde, pte; uint64_t pdp_addr, pd_addr, pt_addr; for (l1 = 0; l1 < 512; l1++) { - cpu_physical_memory_read(pml4_addr + l1 * 8, &pml4e, 8); - pml4e = le64_to_cpu(pml4e); + pml4e = address_space_ldq_le(as, pml4_addr + l1 * 8, attrs, NULL); if (!(pml4e & PG_PRESENT_MASK)) { continue; } pdp_addr = pml4e & 0x3fffffffff000ULL; for (l2 = 0; l2 < 512; l2++) { - cpu_physical_memory_read(pdp_addr + l2 * 8, &pdpe, 8); - pdpe = le64_to_cpu(pdpe); + pdpe = address_space_ldq_le(as, pdp_addr + l2 * 8, attrs, NULL); if (!(pdpe & PG_PRESENT_MASK)) { continue; } @@ -167,8 +166,7 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as, pd_addr = pdpe & 0x3fffffffff000ULL; for (l3 = 0; l3 < 512; l3++) { - cpu_physical_memory_read(pd_addr + l3 * 8, &pde, 8); - pde = le64_to_cpu(pde); + pde = address_space_ldq_le(as, pd_addr + l3 * 8, attrs, NULL); if (!(pde & PG_PRESENT_MASK)) { continue; } @@ -182,10 +180,8 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as, pt_addr = pde & 0x3fffffffff000ULL; for (l4 = 0; l4 < 512; l4++) { - cpu_physical_memory_read(pt_addr - + l4 * 8, - &pte, 8); - pte = le64_to_cpu(pte); + pte = address_space_ldq_le(as, pt_addr + l4 * 8, + attrs, NULL); if (pte & PG_PRESENT_MASK) { print_pte(mon, env, (l0 << 48) + (l1 << 39) + (l2 << 30) + (l3 << 21) + (l4 << 12), @@ -199,14 +195,14 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as, static void tlb_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as) { + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; uint64_t l0; uint64_t pml5e; uint64_t pml5_addr; pml5_addr = env->cr[3] & 0x3fffffffff000ULL; for (l0 = 0; l0 < 512; l0++) { - cpu_physical_memory_read(pml5_addr + l0 * 8, &pml5e, 8); - pml5e = le64_to_cpu(pml5e); + pml5e = address_space_ldq_le(as, pml5_addr + l0 * 8, attrs, NULL); if (pml5e & PG_PRESENT_MASK) { tlb_info_la48(mon, env, as, l0, pml5e & 0x3fffffffff000ULL); } @@ -275,6 +271,7 @@ static void mem_print(Monitor *mon, CPUArchState *env, static void mem_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as) { + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; unsigned int l1, l2; int prot, last_prot; uint32_t pgd, pde, pte; @@ -284,8 +281,7 @@ static void mem_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as) last_prot = 0; start = -1; for(l1 = 0; l1 < 1024; l1++) { - cpu_physical_memory_read(pgd + l1 * 4, &pde, 4); - pde = le32_to_cpu(pde); + pde = address_space_ldl_le(as, pgd + l1 * 4, attrs, NULL); end = l1 << 22; if (pde & PG_PRESENT_MASK) { if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { @@ -293,8 +289,8 @@ static void mem_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as) mem_print(mon, env, &start, &last_prot, end, prot); } else { for(l2 = 0; l2 < 1024; l2++) { - cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, &pte, 4); - pte = le32_to_cpu(pte); + pte = address_space_ldl_le(as, (pde & ~0xfff) + l2 * 4, + attrs, NULL); end = (l1 << 22) + (l2 << 12); if (pte & PG_PRESENT_MASK) { prot = pte & pde & @@ -316,6 +312,7 @@ static void mem_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as) static void mem_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as) { + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; unsigned int l1, l2, l3; int prot, last_prot; uint64_t pdpe, pde, pte; @@ -326,14 +323,12 @@ static void mem_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as) last_prot = 0; start = -1; for (l1 = 0; l1 < 4; l1++) { - cpu_physical_memory_read(pdp_addr + l1 * 8, &pdpe, 8); - pdpe = le64_to_cpu(pdpe); + pdpe = address_space_ldq_le(as, pdp_addr + l1 * 8, attrs, NULL); end = l1 << 30; if (pdpe & PG_PRESENT_MASK) { pd_addr = pdpe & 0x3fffffffff000ULL; for (l2 = 0; l2 < 512; l2++) { - cpu_physical_memory_read(pd_addr + l2 * 8, &pde, 8); - pde = le64_to_cpu(pde); + pde = address_space_ldq_le(as, pd_addr + l2 * 8, attrs, NULL); end = (l1 << 30) + (l2 << 21); if (pde & PG_PRESENT_MASK) { if (pde & PG_PSE_MASK) { @@ -343,8 +338,8 @@ static void mem_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as) } else { pt_addr = pde & 0x3fffffffff000ULL; for (l3 = 0; l3 < 512; l3++) { - cpu_physical_memory_read(pt_addr + l3 * 8, &pte, 8); - pte = le64_to_cpu(pte); + pte = address_space_ldq_le(as, pt_addr + l3 * 8, + attrs, NULL); end = (l1 << 30) + (l2 << 21) + (l3 << 12); if (pte & PG_PRESENT_MASK) { prot = pte & pde & (PG_USER_MASK | PG_RW_MASK | @@ -373,6 +368,7 @@ static void mem_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as) #ifdef TARGET_X86_64 static void mem_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as) { + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; int prot, last_prot; uint64_t l1, l2, l3, l4; uint64_t pml4e, pdpe, pde, pte; @@ -382,14 +378,12 @@ static void mem_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as) last_prot = 0; start = -1; for (l1 = 0; l1 < 512; l1++) { - cpu_physical_memory_read(pml4_addr + l1 * 8, &pml4e, 8); - pml4e = le64_to_cpu(pml4e); + pml4e = address_space_ldq_le(as, pml4_addr + l1 * 8, attrs, NULL); end = l1 << 39; if (pml4e & PG_PRESENT_MASK) { pdp_addr = pml4e & 0x3fffffffff000ULL; for (l2 = 0; l2 < 512; l2++) { - cpu_physical_memory_read(pdp_addr + l2 * 8, &pdpe, 8); - pdpe = le64_to_cpu(pdpe); + pdpe = address_space_ldq_le(as, pdp_addr + l2 * 8, attrs, NULL); end = (l1 << 39) + (l2 << 30); if (pdpe & PG_PRESENT_MASK) { if (pdpe & PG_PSE_MASK) { @@ -400,8 +394,8 @@ static void mem_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as) } else { pd_addr = pdpe & 0x3fffffffff000ULL; for (l3 = 0; l3 < 512; l3++) { - cpu_physical_memory_read(pd_addr + l3 * 8, &pde, 8); - pde = le64_to_cpu(pde); + pde = address_space_ldq_le(as, pd_addr + l3 * 8, + attrs, NULL); end = (l1 << 39) + (l2 << 30) + (l3 << 21); if (pde & PG_PRESENT_MASK) { if (pde & PG_PSE_MASK) { @@ -413,10 +407,10 @@ static void mem_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as) } else { pt_addr = pde & 0x3fffffffff000ULL; for (l4 = 0; l4 < 512; l4++) { - cpu_physical_memory_read(pt_addr - + l4 * 8, - &pte, 8); - pte = le64_to_cpu(pte); + pte = address_space_ldq_le(as, + pt_addr + + l4 * 8, + attrs, NULL); end = (l1 << 39) + (l2 << 30) + (l3 << 21) + (l4 << 12); if (pte & PG_PRESENT_MASK) { @@ -453,6 +447,7 @@ static void mem_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as) static void mem_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as) { + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; int prot, last_prot; uint64_t l0, l1, l2, l3, l4; uint64_t pml5e, pml4e, pdpe, pde, pte; @@ -462,8 +457,7 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as) last_prot = 0; start = -1; for (l0 = 0; l0 < 512; l0++) { - cpu_physical_memory_read(pml5_addr + l0 * 8, &pml5e, 8); - pml5e = le64_to_cpu(pml5e); + pml5e = address_space_ldq_le(as, pml5_addr + l0 * 8, attrs, NULL); end = l0 << 48; if (!(pml5e & PG_PRESENT_MASK)) { prot = 0; @@ -473,8 +467,7 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as) pml4_addr = pml5e & 0x3fffffffff000ULL; for (l1 = 0; l1 < 512; l1++) { - cpu_physical_memory_read(pml4_addr + l1 * 8, &pml4e, 8); - pml4e = le64_to_cpu(pml4e); + pml4e = address_space_ldq_le(as, pml4_addr + l1 * 8, attrs, NULL); end = (l0 << 48) + (l1 << 39); if (!(pml4e & PG_PRESENT_MASK)) { prot = 0; @@ -484,8 +477,7 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as) pdp_addr = pml4e & 0x3fffffffff000ULL; for (l2 = 0; l2 < 512; l2++) { - cpu_physical_memory_read(pdp_addr + l2 * 8, &pdpe, 8); - pdpe = le64_to_cpu(pdpe); + pdpe = address_space_ldq_le(as, pdp_addr + l2 * 8, attrs, NULL); end = (l0 << 48) + (l1 << 39) + (l2 << 30); if (pdpe & PG_PRESENT_MASK) { prot = 0; @@ -503,8 +495,8 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as) pd_addr = pdpe & 0x3fffffffff000ULL; for (l3 = 0; l3 < 512; l3++) { - cpu_physical_memory_read(pd_addr + l3 * 8, &pde, 8); - pde = le64_to_cpu(pde); + pde = address_space_ldq_le(as, pd_addr + l3 * 8, + attrs, NULL); end = (l0 << 48) + (l1 << 39) + (l2 << 30) + (l3 << 21); if (pde & PG_PRESENT_MASK) { prot = 0; @@ -522,8 +514,8 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as) pt_addr = pde & 0x3fffffffff000ULL; for (l4 = 0; l4 < 512; l4++) { - cpu_physical_memory_read(pt_addr + l4 * 8, &pte, 8); - pte = le64_to_cpu(pte); + pte = address_space_ldq_le(as, pt_addr + l4 * 8, + attrs, NULL); end = (l0 << 48) + (l1 << 39) + (l2 << 30) + (l3 << 21) + (l4 << 12); if (pte & PG_PRESENT_MASK) { From e9f173cd51b8b0a877553ab77bed879f09afd61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 12:59:15 +0200 Subject: [PATCH 1600/1794] target/hppa: Use hwaddr type for HPPATLBEntry::pa MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HPPATLBEntry::@pa is a physical address, use the appropriate type. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251009101040.18378-2-philmd@linaro.org> --- target/hppa/cpu.h | 2 +- target/hppa/trace-events | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 672ab3750c886..869a75876e222 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -187,7 +187,7 @@ typedef struct HPPATLBEntry { struct HPPATLBEntry *unused_next; }; - target_ulong pa; + hwaddr pa; unsigned entry_valid : 1; diff --git a/target/hppa/trace-events b/target/hppa/trace-events index a10ba73d5d45c..01761a4559b2a 100644 --- a/target/hppa/trace-events +++ b/target/hppa/trace-events @@ -1,13 +1,13 @@ # See docs/devel/tracing.rst for syntax documentation. # mem_helper.c -disable hppa_tlb_flush_ent(void *env, void *ent, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p va_b=0x%lx va_e=0x%lx pa=0x%lx" -disable hppa_tlb_find_entry(void *env, void *ent, int valid, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p valid=%d va_b=0x%lx va_e=0x%lx pa=0x%lx" +disable hppa_tlb_flush_ent(void *env, void *ent, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p va_b=0x%lx va_e=0x%lx pa=0x%" PRIx64 +disable hppa_tlb_find_entry(void *env, void *ent, int valid, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p valid=%d va_b=0x%lx va_e=0x%lx pa=0x%" PRIx64 disable hppa_tlb_find_entry_not_found(void *env, uint64_t addr) "env=%p addr=%08lx" disable hppa_tlb_get_physical_address(void *env, int ret, int prot, uint64_t addr, uint64_t phys) "env=%p ret=%d prot=%d addr=0x%lx phys=0x%lx" disable hppa_tlb_fill_excp(void *env, uint64_t addr, int size, int type, int mmu_idx) "env=%p addr=0x%lx size=%d type=%d mmu_idx=%d" disable hppa_tlb_fill_success(void *env, uint64_t addr, uint64_t phys, int size, int type, int mmu_idx) "env=%p addr=0x%lx phys=0x%lx size=%d type=%d mmu_idx=%d" -disable hppa_tlb_itlba(void *env, void *ent, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p va_b=0x%lx va_e=0x%lx pa=0x%lx" +disable hppa_tlb_itlba(void *env, void *ent, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p va_b=0x%lx va_e=0x%lx pa=0x%" PRIx64 disable hppa_tlb_itlbp(void *env, void *ent, int access_id, int u, int pl2, int pl1, int type, int b, int d, int t) "env=%p ent=%p access_id=%x u=%d pl2=%d pl1=%d type=%d b=%d d=%d t=%d" disable hppa_tlb_ptlb(void *env) "env=%p" disable hppa_tlb_ptlb_local(void *env) "env=%p" From 4fe2763a904a16197b5de5375e6779b81d363d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 15:30:56 +0200 Subject: [PATCH 1601/1794] target/hppa: Have hppa_form_gva*() return vaddr type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Return a 'vaddr' type for "guest virtual address". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251009101040.18378-3-philmd@linaro.org> --- target/hppa/cpu.h | 8 ++++---- target/hppa/helper.c | 4 ++-- target/hppa/mem_helper.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 869a75876e222..c652ef945ac4f 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -320,8 +320,8 @@ void hppa_translate_code(CPUState *cs, TranslationBlock *tb, #define CPU_RESOLVING_TYPE TYPE_HPPA_CPU -static inline target_ulong hppa_form_gva_mask(uint64_t gva_offset_mask, - uint64_t spc, target_ulong off) +static inline vaddr hppa_form_gva_mask(uint64_t gva_offset_mask, + uint64_t spc, target_ulong off) { #ifdef CONFIG_USER_ONLY return off & gva_offset_mask; @@ -330,8 +330,8 @@ static inline target_ulong hppa_form_gva_mask(uint64_t gva_offset_mask, #endif } -static inline target_ulong hppa_form_gva(CPUHPPAState *env, uint64_t spc, - target_ulong off) +static inline vaddr hppa_form_gva(CPUHPPAState *env, uint64_t spc, + target_ulong off) { return hppa_form_gva_mask(env->gva_offset_mask, spc, off); } diff --git a/target/hppa/helper.c b/target/hppa/helper.c index d7f8495d9825b..edcd2bf27c83a 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -148,8 +148,8 @@ void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags) m = UINT32_MAX; } - qemu_fprintf(f, "IA_F %08" PRIx64 ":%0*" PRIx64 " (" TARGET_FMT_lx ")\n" - "IA_B %08" PRIx64 ":%0*" PRIx64 " (" TARGET_FMT_lx ")\n", + qemu_fprintf(f, "IA_F %08" PRIx64 ":%0*" PRIx64 " (0x%" VADDR_PRIx ")\n" + "IA_B %08" PRIx64 ":%0*" PRIx64 " (0x%" VADDR_PRIx ")\n", env->iasq_f >> 32, w, m & env->iaoq_f, hppa_form_gva_mask(env->gva_offset_mask, env->iasq_f, env->iaoq_f), diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 9bdd0a6f23d6b..cce82e6599989 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -803,7 +803,7 @@ void HELPER(diag_btlb)(CPUHPPAState *env) uint64_t HELPER(b_gate_priv)(CPUHPPAState *env, uint64_t iaoq_f) { - uint64_t gva = hppa_form_gva(env, env->iasq_f, iaoq_f); + vaddr gva = hppa_form_gva(env, env->iasq_f, iaoq_f); HPPATLBEntry *ent = hppa_find_tlb(env, gva); if (ent == NULL) { From 714dd08fed17b4c091bd59d46b61e923552a7358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 11:25:06 +0200 Subject: [PATCH 1602/1794] target/hppa: Explode MO_TExx -> MO_TE | MO_xx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the implicit MO_TE definition in order to replace it in a commits. Mechanical change using: $ for n in UW UL UQ UO SW SL SQ; do \ sed -i -e "s/MO_TE$n/MO_TE | MO_$n/" \ $(git grep -l MO_TE$n target/openrisc); \ done Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251009101040.18378-4-philmd@linaro.org> --- target/hppa/translate.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 7a81cfcb88794..859c6cf5f9bfb 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1691,7 +1691,7 @@ static bool do_floadw(DisasContext *ctx, unsigned rt, unsigned rb, nullify_over(ctx); tmp = tcg_temp_new_i32(); - do_load_32(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TEUL); + do_load_32(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TE | MO_UL); save_frw_i32(rt, tmp); if (rt == 0) { @@ -1716,7 +1716,7 @@ static bool do_floadd(DisasContext *ctx, unsigned rt, unsigned rb, nullify_over(ctx); tmp = tcg_temp_new_i64(); - do_load_64(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TEUQ); + do_load_64(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TE | MO_UQ); save_frd(rt, tmp); if (rt == 0) { @@ -1750,7 +1750,7 @@ static bool do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb, nullify_over(ctx); tmp = load_frw_i32(rt); - do_store_32(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TEUL); + do_store_32(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TE | MO_UL); return nullify_end(ctx); } @@ -1770,7 +1770,7 @@ static bool do_fstored(DisasContext *ctx, unsigned rt, unsigned rb, nullify_over(ctx); tmp = load_frd(rt); - do_store_64(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TEUQ); + do_store_64(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TE | MO_UQ); return nullify_end(ctx); } From 4eef4dab502f555c4609bcd3606ea076544d5c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 11:31:19 +0200 Subject: [PATCH 1603/1794] target/hppa: Conceal MO_TE within do_load() 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: <20251009101040.18378-5-philmd@linaro.org> --- target/hppa/translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 859c6cf5f9bfb..ee0c874342cb7 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1676,7 +1676,7 @@ static bool do_load(DisasContext *ctx, unsigned rt, unsigned rb, /* Make sure if RT == RB, we see the result of the load. */ dest = tcg_temp_new_i64(); } - do_load_64(ctx, dest, rb, rx, scale, disp, sp, modify, mop); + do_load_64(ctx, dest, rb, rx, scale, disp, sp, modify, MO_TE | mop); save_gpr(ctx, rt, dest); return nullify_end(ctx); @@ -3302,7 +3302,7 @@ static bool trans_ld(DisasContext *ctx, arg_ldst *a) return gen_illegal(ctx); } return do_load(ctx, a->t, a->b, a->x, a->scale ? a->size : 0, - a->disp, a->sp, a->m, a->size | MO_TE); + a->disp, a->sp, a->m, a->size); } static bool trans_st(DisasContext *ctx, arg_ldst *a) From e3da43671179afb09e55b4ba3d0352b4276dc4cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 11:32:21 +0200 Subject: [PATCH 1604/1794] target/hppa: Conceal MO_TE within do_load_32/64() 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: <20251009101040.18378-6-philmd@linaro.org> --- target/hppa/translate.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index ee0c874342cb7..4680d8263452f 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1599,6 +1599,7 @@ static void do_load_32(DisasContext *ctx, TCGv_i32 dest, unsigned rb, /* Caller uses nullify_over/nullify_end. */ assert(ctx->null_cond.c == TCG_COND_NEVER); + mop |= MO_TE; form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, MMU_DISABLED(ctx)); tcg_gen_qemu_ld_i32(dest, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); @@ -1617,6 +1618,7 @@ static void do_load_64(DisasContext *ctx, TCGv_i64 dest, unsigned rb, /* Caller uses nullify_over/nullify_end. */ assert(ctx->null_cond.c == TCG_COND_NEVER); + mop |= MO_TE; form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, MMU_DISABLED(ctx)); tcg_gen_qemu_ld_i64(dest, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); @@ -1676,7 +1678,7 @@ static bool do_load(DisasContext *ctx, unsigned rt, unsigned rb, /* Make sure if RT == RB, we see the result of the load. */ dest = tcg_temp_new_i64(); } - do_load_64(ctx, dest, rb, rx, scale, disp, sp, modify, MO_TE | mop); + do_load_64(ctx, dest, rb, rx, scale, disp, sp, modify, mop); save_gpr(ctx, rt, dest); return nullify_end(ctx); @@ -1691,7 +1693,7 @@ static bool do_floadw(DisasContext *ctx, unsigned rt, unsigned rb, nullify_over(ctx); tmp = tcg_temp_new_i32(); - do_load_32(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TE | MO_UL); + do_load_32(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_UL); save_frw_i32(rt, tmp); if (rt == 0) { @@ -1716,7 +1718,7 @@ static bool do_floadd(DisasContext *ctx, unsigned rt, unsigned rb, nullify_over(ctx); tmp = tcg_temp_new_i64(); - do_load_64(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TE | MO_UQ); + do_load_64(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_UQ); save_frd(rt, tmp); if (rt == 0) { From 0807994a0cca7ee512975523ecaef4926a5632f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 11:28:14 +0200 Subject: [PATCH 1605/1794] target/hppa: Conceal MO_TE within do_store() 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: <20251009101040.18378-7-philmd@linaro.org> --- target/hppa/translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 4680d8263452f..3f78bb2a76a8c 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1739,7 +1739,7 @@ static bool do_store(DisasContext *ctx, unsigned rt, unsigned rb, int modify, MemOp mop) { nullify_over(ctx); - do_store_64(ctx, load_gpr(ctx, rt), rb, 0, 0, disp, sp, modify, mop); + do_store_64(ctx, load_gpr(ctx, rt), rb, 0, 0, disp, sp, modify, MO_TE | mop); return nullify_end(ctx); } @@ -3313,7 +3313,7 @@ static bool trans_st(DisasContext *ctx, arg_ldst *a) if (!ctx->is_pa20 && a->size > MO_32) { return gen_illegal(ctx); } - return do_store(ctx, a->t, a->b, a->disp, a->sp, a->m, a->size | MO_TE); + return do_store(ctx, a->t, a->b, a->disp, a->sp, a->m, a->size); } static bool trans_ldc(DisasContext *ctx, arg_ldst *a) From 64d1c178513c77172d4917fa03e3a9c332050c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 11:30:04 +0200 Subject: [PATCH 1606/1794] target/hppa: Conceal MO_TE within do_store_32/64() 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: <20251009101040.18378-8-philmd@linaro.org> --- target/hppa/translate.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 3f78bb2a76a8c..92071affe47a5 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1637,6 +1637,7 @@ static void do_store_32(DisasContext *ctx, TCGv_i32 src, unsigned rb, /* Caller uses nullify_over/nullify_end. */ assert(ctx->null_cond.c == TCG_COND_NEVER); + mop |= MO_TE; form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, MMU_DISABLED(ctx)); tcg_gen_qemu_st_i32(src, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); @@ -1655,6 +1656,7 @@ static void do_store_64(DisasContext *ctx, TCGv_i64 src, unsigned rb, /* Caller uses nullify_over/nullify_end. */ assert(ctx->null_cond.c == TCG_COND_NEVER); + mop |= MO_TE; form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, MMU_DISABLED(ctx)); tcg_gen_qemu_st_i64(src, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); @@ -1739,7 +1741,7 @@ static bool do_store(DisasContext *ctx, unsigned rt, unsigned rb, int modify, MemOp mop) { nullify_over(ctx); - do_store_64(ctx, load_gpr(ctx, rt), rb, 0, 0, disp, sp, modify, MO_TE | mop); + do_store_64(ctx, load_gpr(ctx, rt), rb, 0, 0, disp, sp, modify, mop); return nullify_end(ctx); } @@ -1752,7 +1754,7 @@ static bool do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb, nullify_over(ctx); tmp = load_frw_i32(rt); - do_store_32(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TE | MO_UL); + do_store_32(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_UL); return nullify_end(ctx); } @@ -1772,7 +1774,7 @@ static bool do_fstored(DisasContext *ctx, unsigned rt, unsigned rb, nullify_over(ctx); tmp = load_frd(rt); - do_store_64(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TE | MO_UQ); + do_store_64(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_UQ); return nullify_end(ctx); } From 6c1c734f786f8279c3b0c4bf57063bc79cc5c828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 11:45:27 +0200 Subject: [PATCH 1607/1794] target/hppa: Introduce mo_endian() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mo_endian() returns the target endianness. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251009101040.18378-9-philmd@linaro.org> --- target/hppa/translate.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 92071affe47a5..6fec63cb43334 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -104,6 +104,11 @@ typedef struct DisasContext { #define MMU_DISABLED(C) MMU_IDX_MMU_DISABLED((C)->mmu_idx) #endif +static inline MemOp mo_endian(DisasContext *ctx) +{ + return MO_TE; +} + /* Note that ssm/rsm instructions number PSW_W and PSW_E differently. */ static int expand_sm_imm(DisasContext *ctx, int val) { @@ -1599,7 +1604,7 @@ static void do_load_32(DisasContext *ctx, TCGv_i32 dest, unsigned rb, /* Caller uses nullify_over/nullify_end. */ assert(ctx->null_cond.c == TCG_COND_NEVER); - mop |= MO_TE; + mop |= mo_endian(ctx); form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, MMU_DISABLED(ctx)); tcg_gen_qemu_ld_i32(dest, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); @@ -1618,7 +1623,7 @@ static void do_load_64(DisasContext *ctx, TCGv_i64 dest, unsigned rb, /* Caller uses nullify_over/nullify_end. */ assert(ctx->null_cond.c == TCG_COND_NEVER); - mop |= MO_TE; + mop |= mo_endian(ctx); form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, MMU_DISABLED(ctx)); tcg_gen_qemu_ld_i64(dest, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); @@ -1637,7 +1642,7 @@ static void do_store_32(DisasContext *ctx, TCGv_i32 src, unsigned rb, /* Caller uses nullify_over/nullify_end. */ assert(ctx->null_cond.c == TCG_COND_NEVER); - mop |= MO_TE; + mop |= mo_endian(ctx); form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, MMU_DISABLED(ctx)); tcg_gen_qemu_st_i32(src, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); @@ -1656,7 +1661,7 @@ static void do_store_64(DisasContext *ctx, TCGv_i64 src, unsigned rb, /* Caller uses nullify_over/nullify_end. */ assert(ctx->null_cond.c == TCG_COND_NEVER); - mop |= MO_TE; + mop |= mo_endian(ctx); form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, MMU_DISABLED(ctx)); tcg_gen_qemu_st_i64(src, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); @@ -3320,7 +3325,7 @@ static bool trans_st(DisasContext *ctx, arg_ldst *a) static bool trans_ldc(DisasContext *ctx, arg_ldst *a) { - MemOp mop = MO_TE | MO_ALIGN | a->size; + MemOp mop = mo_endian(ctx) | MO_ALIGN | a->size; TCGv_i64 dest, ofs; TCGv_i64 addr; From 7307ff955818d9e4eec2b155e501e360acf6eaf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Mar 2025 14:31:49 +0100 Subject: [PATCH 1608/1794] target/hppa: Replace MO_TE -> MO_BE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only build the PA-RISC targets using big endianness order: $ git grep TARGET_BIG_ENDIAN configs/targets/hppa-* configs/targets/hppa-linux-user.mak:5:TARGET_BIG_ENDIAN=y configs/targets/hppa-softmmu.mak:2:TARGET_BIG_ENDIAN=y Therefore the MO_TE definition always expands to MO_BE. Use the latter to simplify. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251009101040.18378-10-philmd@linaro.org> --- target/hppa/translate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 6fec63cb43334..853cba2ba4f15 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -106,7 +106,8 @@ typedef struct DisasContext { static inline MemOp mo_endian(DisasContext *ctx) { - return MO_TE; + /* The PSW_E bit sets the (little) endianness, but we don't implement it. */ + return MO_BE; } /* Note that ssm/rsm instructions number PSW_W and PSW_E differently. */ From cea82f8cdd07697a48ae1c4e026707463f432a45 Mon Sep 17 00:00:00 2001 From: Gabriel Brookman Date: Thu, 9 Oct 2025 16:51:11 -0400 Subject: [PATCH 1609/1794] target/hppa: correct size bit parity for fmpyadd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the fmpyadd instruction on the hppa architecture, there is a bit used to specify whether the instruction is operating on a 32 bit or 64 bit floating point register. For most instructions, such a bit is 0 when operating on the smaller register and 1 when operating on the larger register. However, according to page 6-57 of the PA-RISC 1.1 Architecture and Instruction Set Reference Manual, this convention is reversed for the fmpyadd instruction specifically, meaning the bit is 1 for operations on 32 bit registers and 0 for 64 bit registers. See also page 6-18 (fig. 6-8) and 6-19 (table 6-16), where the f field for FMPYADD and FMPYSUB is documented. Previously, QEMU decoded this operation as operating on the other size of register, leading to bugs when translating the fmpyadd instruction. This patch fixes that issue. Reported-by: Andreas Hüttel Signed-off-by: Gabriel Brookman Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3096 Reviewed-by: Richard Henderson Acked-by: Helge Deller Message-ID: <20251009-hppa-correct-fmpyadd-size-bit-decoding-v1-1-f63bb6c3290c@gmail.com> [PMD: Add documentation refs mentioned by Andreas K. Huettel] Signed-off-by: Philippe Mathieu-Daudé --- target/hppa/insns.decode | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 4eaac750ea8c0..13c6a55bf2af0 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -365,10 +365,10 @@ fstd 011100 ..... ..... .. ............1. @ldstim11 &mpyadd rm1 rm2 ta ra tm @mpyadd ...... rm1:5 rm2:5 ta:5 ra:5 . tm:5 &mpyadd -fmpyadd_f 000110 ..... ..... ..... ..... 0 ..... @mpyadd -fmpyadd_d 000110 ..... ..... ..... ..... 1 ..... @mpyadd -fmpysub_f 100110 ..... ..... ..... ..... 0 ..... @mpyadd -fmpysub_d 100110 ..... ..... ..... ..... 1 ..... @mpyadd +fmpyadd_f 000110 ..... ..... ..... ..... 1 ..... @mpyadd +fmpyadd_d 000110 ..... ..... ..... ..... 0 ..... @mpyadd +fmpysub_f 100110 ..... ..... ..... ..... 1 ..... @mpyadd +fmpysub_d 100110 ..... ..... ..... ..... 0 ..... @mpyadd #### # Conditional Branches From 108773e700075a6dab8e4ae8d1330f6dfccba1d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 25 Sep 2025 02:39:45 +0200 Subject: [PATCH 1610/1794] target/loongarch: Replace VMSTATE_UINTTL() -> VMSTATE_UINT64() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All LoongArchCPU::pc and LoongArchCPU::gpr[] fields are of uint64_t type. Use the appropriate VMSTATE_UINT64() macro. There is no functional change (the migration stream is not modified), because the LoongArch targets are only built as 64-bit: $ git grep TARGET_LONG_BITS configs/targets/loongarch64* configs/targets/loongarch64-linux-user.mak:7:TARGET_LONG_BITS=64 configs/targets/loongarch64-softmmu.mak:7:TARGET_LONG_BITS=64 Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Song Gao Message-Id: <20250925004327.58764-1-philmd@linaro.org> --- target/loongarch/machine.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c index 73190fb3672a4..0366a5076375e 100644 --- a/target/loongarch/machine.c +++ b/target/loongarch/machine.c @@ -191,8 +191,8 @@ const VMStateDescription vmstate_loongarch_cpu = { .version_id = 4, .minimum_version_id = 4, .fields = (const VMStateField[]) { - VMSTATE_UINTTL_ARRAY(env.gpr, LoongArchCPU, 32), - VMSTATE_UINTTL(env.pc, LoongArchCPU), + VMSTATE_UINT64_ARRAY(env.gpr, LoongArchCPU, 32), + VMSTATE_UINT64(env.pc, LoongArchCPU), /* Remaining CSRs */ VMSTATE_UINT64(env.CSR_CRMD, LoongArchCPU), From 656940971f253412a565da9f21cf6f05a284c3d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 08:14:50 +0200 Subject: [PATCH 1611/1794] target/loongarch: Remove target_ulong use in gen_goto_tb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit translator_use_goto_tb() expects a vaddr type since commit b1c09220b4c ("accel/tcg: Replace target_ulong with vaddr in translator*()"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Song Gao Message-Id: <20251009201947.34643-3-philmd@linaro.org> --- target/loongarch/tcg/translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/loongarch/tcg/translate.c b/target/loongarch/tcg/translate.c index 57c3b8c4bb2dd..055f6fb6046b9 100644 --- a/target/loongarch/tcg/translate.c +++ b/target/loongarch/tcg/translate.c @@ -99,8 +99,7 @@ void generate_exception(DisasContext *ctx, int excp) ctx->base.is_jmp = DISAS_NORETURN; } -static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_idx, - target_ulong dest) +static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_idx, vaddr dest) { if (ctx->va32) { dest = (uint32_t) dest; From 7f306758fb83f1b64d02647171d5f65fc157ac25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 08:15:23 +0200 Subject: [PATCH 1612/1794] target/loongarch: Remove target_ulong use in gdb_write_register handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ldq_le_p() returns a uint64_t type, big enough to also hold ldl_le_p() return value. If we were building for a 32-bit LoongArch target, ldq_le_p() would not fit in target_ulong. Better stick to plain uint64_t. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Song Gao Message-Id: <20251009201947.34643-4-philmd@linaro.org> --- target/loongarch/gdbstub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/loongarch/gdbstub.c b/target/loongarch/gdbstub.c index 471eda28c7307..23a5eecc20bd0 100644 --- a/target/loongarch/gdbstub.c +++ b/target/loongarch/gdbstub.c @@ -62,7 +62,7 @@ int loongarch_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) int loongarch_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { CPULoongArchState *env = cpu_env(cs); - target_ulong tmp; + uint64_t tmp; int length = 0; if (n < 0 || n > 34) { From f756b7745a16a20815449099004076e3587e0448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 08:15:13 +0200 Subject: [PATCH 1613/1794] target/loongarch: Do not use target_ulong type for LDDIR level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LDDIR level page table is a 5-bit immediate. Using the uint32_t type for it is sufficient. Avoid the target_ulong type. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Song Gao Message-Id: <20251009201947.34643-5-philmd@linaro.org> --- target/loongarch/cpu-mmu.h | 2 +- target/loongarch/cpu_helper.c | 2 +- target/loongarch/tcg/helper.h | 2 +- target/loongarch/tcg/insn_trans/trans_privileged.c.inc | 2 +- target/loongarch/tcg/tlb_helper.c | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h index 0068d22efcb0c..dbc69c7c0f2d8 100644 --- a/target/loongarch/cpu-mmu.h +++ b/target/loongarch/cpu-mmu.h @@ -34,7 +34,7 @@ TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context, MMUAccessType access_type, int mmu_idx, int is_debug); void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, - uint64_t *dir_width, target_ulong level); + uint64_t *dir_width, unsigned int level); hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); #endif /* LOONGARCH_CPU_MMU_H */ diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 4a9db3ea4c1f0..867e7c88670a7 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -17,7 +17,7 @@ #include "tcg/tcg_loongarch.h" void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, - uint64_t *dir_width, target_ulong level) + uint64_t *dir_width, unsigned int level) { switch (level) { case 1: diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h index db57dbfc16763..7e508c5a7b9a3 100644 --- a/target/loongarch/tcg/helper.h +++ b/target/loongarch/tcg/helper.h @@ -129,7 +129,7 @@ DEF_HELPER_2(invtlb_all_asid, void, env, tl) DEF_HELPER_3(invtlb_page_asid, void, env, tl, tl) DEF_HELPER_3(invtlb_page_asid_or_g, void, env, tl, tl) -DEF_HELPER_4(lddir, tl, env, tl, tl, i32) +DEF_HELPER_4(lddir, tl, env, tl, i32, i32) DEF_HELPER_4(ldpte, void, env, tl, tl, i32) DEF_HELPER_1(ertn, void, env) DEF_HELPER_1(idle, void, env) diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc index a407ab51b74f2..64e53a44606e1 100644 --- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc @@ -380,7 +380,7 @@ static bool trans_lddir(DisasContext *ctx, arg_lddir *a) if (check_plv(ctx)) { return false; } - gen_helper_lddir(dest, tcg_env, src, tcg_constant_tl(a->imm), mem_idx); + gen_helper_lddir(dest, tcg_env, src, tcg_constant_i32(a->imm), mem_idx); return true; } diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index f1d183cb6419b..e119f78d92585 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -599,7 +599,7 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, - target_ulong level, uint32_t mem_idx) + uint32_t level, uint32_t mem_idx) { CPUState *cs = env_cpu(env); target_ulong badvaddr, index, phys; @@ -607,7 +607,7 @@ target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, if (unlikely((level == 0) || (level > 4))) { qemu_log_mask(LOG_GUEST_ERROR, - "Attepted LDDIR with level %"PRId64"\n", level); + "Attepted LDDIR with level %u\n", level); return base; } From eefee97c4ca386ff82691417846fd252d90d978e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Aug 2025 06:43:19 +0200 Subject: [PATCH 1614/1794] target/m68k: Remove unused @cpu_exception_index TCGv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When moving the @exception_index from each target ArchCPU to the global CPUState in commit 27103424c40 ("cpu: Move exception_index field from CPU_COMMON to CPUState"), the @cpu_exception_index TCGv has been created for m68k target. For years, no code ever used this register. Simply remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250924171308.53036-1-philmd@linaro.org> --- target/m68k/translate.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 97afceb129701..0cee54f4900bf 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -45,7 +45,6 @@ #undef DEFO64 static TCGv_i32 cpu_halted; -static TCGv_i32 cpu_exception_index; static char cpu_reg_names[2 * 8 * 3 + 5 * 4]; static TCGv cpu_dregs[8]; @@ -81,11 +80,6 @@ void m68k_tcg_init(void) cpu_halted = tcg_global_mem_new_i32(tcg_env, -offsetof(M68kCPU, env) + offsetof(CPUState, halted), "HALTED"); - cpu_exception_index = tcg_global_mem_new_i32(tcg_env, - -offsetof(M68kCPU, env) + - offsetof(CPUState, exception_index), - "EXCEPTION"); - p = cpu_reg_names; for (i = 0; i < 8; i++) { sprintf(p, "D%d", i); From cb4ca3b358660646db62c256f360598e92c65b97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Aug 2025 06:42:05 +0200 Subject: [PATCH 1615/1794] target/m68k: Remove pointless @cpu_halted TCGv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid registering a TCGv to write the generic CPUState::halted field. Access it directly via @env in both STOP / HALT opcodes. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250925012454.60602-1-philmd@linaro.org> --- target/m68k/translate.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 0cee54f4900bf..eb1ba150745e9 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -44,8 +44,6 @@ #undef DEFO32 #undef DEFO64 -static TCGv_i32 cpu_halted; - static char cpu_reg_names[2 * 8 * 3 + 5 * 4]; static TCGv cpu_dregs[8]; static TCGv cpu_aregs[8]; @@ -77,9 +75,6 @@ void m68k_tcg_init(void) #undef DEFO32 #undef DEFO64 - cpu_halted = tcg_global_mem_new_i32(tcg_env, - -offsetof(M68kCPU, env) + - offsetof(CPUState, halted), "HALTED"); p = cpu_reg_names; for (i = 0; i < 8; i++) { sprintf(p, "D%d", i); @@ -4506,7 +4501,8 @@ DISAS_INSN(halt) gen_exception(s, s->pc, EXCP_SEMIHOSTING); return; } - tcg_gen_movi_i32(cpu_halted, 1); + tcg_gen_st_i32(tcg_constant_i32(1), tcg_env, + offsetof(CPUState, halted) - offsetof(M68kCPU, env)); gen_exception(s, s->pc, EXCP_HLT); } @@ -4522,7 +4518,8 @@ DISAS_INSN(stop) ext = read_im16(env, s); gen_set_sr_im(s, ext, 0); - tcg_gen_movi_i32(cpu_halted, 1); + tcg_gen_st_i32(tcg_constant_i32(1), tcg_env, + offsetof(CPUState, halted) - offsetof(M68kCPU, env)); gen_exception(s, s->pc, EXCP_HLT); } From 1f097e46a38ed3b699dcac56c8adf6113c9278b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 06:35:19 +0200 Subject: [PATCH 1616/1794] target/microblaze: Remove target_ulong use in cpu_handle_mmu_fault() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_handle_mmu_fault() -- renamed in commit f429d607c71 -- expects a vaddr type for its address argument since commit 7510454e3e7 ("cpu: Turn cpu_handle_mmu_fault() into a CPUClass hook"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20251015180115.97493-2-philmd@linaro.org> --- target/microblaze/mmu.c | 4 ++-- target/microblaze/mmu.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index 8703ff5c657ed..db24cb399cec4 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -78,7 +78,7 @@ static void mmu_change_pid(CPUMBState *env, unsigned int newpid) /* rw - 0 = read, 1 = write, 2 = fetch. */ unsigned int mmu_translate(MicroBlazeCPU *cpu, MicroBlazeMMULookup *lu, - target_ulong vaddr, MMUAccessType rw, int mmu_idx) + vaddr vaddr, MMUAccessType rw, int mmu_idx) { MicroBlazeMMU *mmu = &cpu->env.mmu; unsigned int i, hit = 0; @@ -172,7 +172,7 @@ unsigned int mmu_translate(MicroBlazeCPU *cpu, MicroBlazeMMULookup *lu, } done: qemu_log_mask(CPU_LOG_MMU, - "MMU vaddr=0x" TARGET_FMT_lx + "MMU vaddr=0x%" VADDR_PRIx " rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", vaddr, rw, tlb_wr, tlb_ex, hit); return hit; diff --git a/target/microblaze/mmu.h b/target/microblaze/mmu.h index 1068bd2d52b77..2aca39c923b81 100644 --- a/target/microblaze/mmu.h +++ b/target/microblaze/mmu.h @@ -86,7 +86,7 @@ typedef struct { } MicroBlazeMMULookup; unsigned int mmu_translate(MicroBlazeCPU *cpu, MicroBlazeMMULookup *lu, - target_ulong vaddr, MMUAccessType rw, int mmu_idx); + vaddr vaddr, MMUAccessType rw, int mmu_idx); uint32_t mmu_read(CPUMBState *env, bool ea, uint32_t rn); void mmu_write(CPUMBState *env, bool ea, uint32_t rn, uint32_t v); void mmu_init(MicroBlazeMMU *mmu); From 0651ea17e89903789d11dc601dc5ecd7c8e810c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 06:36:32 +0200 Subject: [PATCH 1617/1794] target/microblaze: Remove target_ulong uses in get_phys_page_attrs_debug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CPUClass::get_phys_page_debug() handler takes a 'vaddr' address and return a 'hwaddr' type since commit 00b941e581b ("cpu: Turn cpu_get_phys_page_debug() into a CPUClass hook"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20251015180115.97493-3-philmd@linaro.org> --- target/microblaze/helper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index ef0e2f973fa79..cf577a722689e 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -274,7 +274,8 @@ hwaddr mb_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr, MemTxAttrs *attrs) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); - target_ulong vaddr, paddr = 0; + vaddr vaddr; + hwaddr paddr = 0; MicroBlazeMMULookup lu; int mmu_idx = cpu_mmu_index(cs, false); unsigned int hit; From dc693a4a88a4c02b490378a7dc05c8e83f59cf97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 06:42:24 +0200 Subject: [PATCH 1618/1794] target/microblaze: Remove target_ulong use in gen_goto_tb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit translator_use_goto_tb() expects a vaddr type since commit b1c09220b4c ("accel/tcg: Replace target_ulong with vaddr in translator_*()"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20251015180115.97493-4-philmd@linaro.org> --- target/microblaze/translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 3dd74b021e8fe..b93a40fedbcf3 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -116,8 +116,7 @@ static void gen_raise_hw_excp(DisasContext *dc, uint32_t esr_ec) gen_raise_exception_sync(dc, EXCP_HW_EXCP); } -static void gen_goto_tb(DisasContext *dc, unsigned tb_slot_idx, - target_ulong dest) +static void gen_goto_tb(DisasContext *dc, unsigned tb_slot_idx, vaddr dest) { if (translator_use_goto_tb(&dc->base, dest)) { tcg_gen_goto_tb(tb_slot_idx); From 92035b7b0dd9fd40527a58479c9865a6dce104b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 06:56:49 +0200 Subject: [PATCH 1619/1794] target/microblaze: Remove target_ulong use in helper_stackprot() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 36a9529e60e ("target/microblaze: Simplify compute_ldst_addr_type{a,b}"), helper_stackprot() takes a TCGv_i32 argument. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20251015180115.97493-5-philmd@linaro.org> --- target/microblaze/helper.h | 2 +- target/microblaze/op_helper.c | 4 ++-- target/microblaze/translate.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/target/microblaze/helper.h b/target/microblaze/helper.h index ef4fad9b91eb6..01eba592b2604 100644 --- a/target/microblaze/helper.h +++ b/target/microblaze/helper.h @@ -20,7 +20,7 @@ DEF_HELPER_FLAGS_3(fcmp_ne, TCG_CALL_NO_WG, i32, env, i32, i32) DEF_HELPER_FLAGS_3(fcmp_ge, TCG_CALL_NO_WG, i32, env, i32, i32) DEF_HELPER_FLAGS_2(pcmpbf, TCG_CALL_NO_RWG_SE, i32, i32, i32) -DEF_HELPER_FLAGS_2(stackprot, TCG_CALL_NO_WG, void, env, tl) +DEF_HELPER_FLAGS_2(stackprot, TCG_CALL_NO_WG, void, env, i32) DEF_HELPER_FLAGS_2(get, TCG_CALL_NO_RWG, i32, i32, i32) DEF_HELPER_FLAGS_3(put, TCG_CALL_NO_RWG, void, i32, i32, i32) diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c index b8365b3b1d289..df93c4229d69b 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -365,13 +365,13 @@ uint32_t helper_pcmpbf(uint32_t a, uint32_t b) return 0; } -void helper_stackprot(CPUMBState *env, target_ulong addr) +void helper_stackprot(CPUMBState *env, uint32_t addr) { if (addr < env->slr || addr > env->shr) { CPUState *cs = env_cpu(env); qemu_log_mask(CPU_LOG_INT, "Stack protector violation at " - TARGET_FMT_lx " %x %x\n", + "0x%x 0x%x 0x%x\n", addr, env->slr, env->shr); env->ear = addr; diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index b93a40fedbcf3..994e1d5cef0c9 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -606,7 +606,7 @@ DO_TYPEBI(xori, false, tcg_gen_xori_i32) static TCGv compute_ldst_addr_typea(DisasContext *dc, int ra, int rb) { - TCGv ret; + TCGv_i32 ret; /* If any of the regs is r0, set t to the value of the other reg. */ if (ra && rb) { @@ -628,7 +628,7 @@ static TCGv compute_ldst_addr_typea(DisasContext *dc, int ra, int rb) static TCGv compute_ldst_addr_typeb(DisasContext *dc, int ra, int imm) { - TCGv ret; + TCGv_i32 ret; /* If any of the regs is r0, set t to the value of the other reg. */ if (ra && imm) { From 1bf79694bc09995e805bb017515ef3cd5720fd04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 15 Oct 2025 19:33:39 +0200 Subject: [PATCH 1620/1794] target/microblaze: Have compute_ldst_addr_type[ab] return TCGv_i32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both compute_ldst_addr_typea() and compute_ldst_addr_typeb() bodies use a TCGv_i32, so return the same type. Suggested-by: Anton Johansson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20251015180115.97493-6-philmd@linaro.org> --- target/microblaze/translate.c | 48 +++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 994e1d5cef0c9..1554b9e67b0f2 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -604,7 +604,7 @@ static bool trans_wdic(DisasContext *dc, arg_wdic *a) DO_TYPEA(xor, false, tcg_gen_xor_i32) DO_TYPEBI(xori, false, tcg_gen_xori_i32) -static TCGv compute_ldst_addr_typea(DisasContext *dc, int ra, int rb) +static TCGv_i32 compute_ldst_addr_typea(DisasContext *dc, int ra, int rb) { TCGv_i32 ret; @@ -626,7 +626,7 @@ static TCGv compute_ldst_addr_typea(DisasContext *dc, int ra, int rb) return ret; } -static TCGv compute_ldst_addr_typeb(DisasContext *dc, int ra, int imm) +static TCGv_i32 compute_ldst_addr_typeb(DisasContext *dc, int ra, int imm) { TCGv_i32 ret; @@ -750,13 +750,13 @@ static bool do_load(DisasContext *dc, int rd, TCGv addr, MemOp mop, static bool trans_lbu(DisasContext *dc, arg_typea *arg) { - TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + TCGv_i32 addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); return do_load(dc, arg->rd, addr, MO_UB, dc->mem_index, false); } static bool trans_lbur(DisasContext *dc, arg_typea *arg) { - TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + TCGv_i32 addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); return do_load(dc, arg->rd, addr, MO_UB, dc->mem_index, true); } @@ -776,19 +776,19 @@ static bool trans_lbuea(DisasContext *dc, arg_typea *arg) static bool trans_lbui(DisasContext *dc, arg_typeb *arg) { - TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + TCGv_i32 addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); return do_load(dc, arg->rd, addr, MO_UB, dc->mem_index, false); } static bool trans_lhu(DisasContext *dc, arg_typea *arg) { - TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + TCGv_i32 addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); return do_load(dc, arg->rd, addr, MO_UW, dc->mem_index, false); } static bool trans_lhur(DisasContext *dc, arg_typea *arg) { - TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + TCGv_i32 addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); return do_load(dc, arg->rd, addr, MO_UW, dc->mem_index, true); } @@ -810,19 +810,19 @@ static bool trans_lhuea(DisasContext *dc, arg_typea *arg) static bool trans_lhui(DisasContext *dc, arg_typeb *arg) { - TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + TCGv_i32 addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); return do_load(dc, arg->rd, addr, MO_UW, dc->mem_index, false); } static bool trans_lw(DisasContext *dc, arg_typea *arg) { - TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + TCGv_i32 addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); return do_load(dc, arg->rd, addr, MO_UL, dc->mem_index, false); } static bool trans_lwr(DisasContext *dc, arg_typea *arg) { - TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + TCGv_i32 addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); return do_load(dc, arg->rd, addr, MO_UL, dc->mem_index, true); } @@ -844,16 +844,16 @@ static bool trans_lwea(DisasContext *dc, arg_typea *arg) static bool trans_lwi(DisasContext *dc, arg_typeb *arg) { - TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + TCGv_i32 addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); return do_load(dc, arg->rd, addr, MO_UL, dc->mem_index, false); } static bool trans_lwx(DisasContext *dc, arg_typea *arg) { - TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + TCGv_i32 addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); /* lwx does not throw unaligned access errors, so force alignment */ - tcg_gen_andi_tl(addr, addr, ~3); + tcg_gen_andi_i32(addr, addr, ~3); tcg_gen_qemu_ld_i32(cpu_res_val, addr, dc->mem_index, mo_endian(dc) | MO_UL); @@ -910,13 +910,13 @@ static bool do_store(DisasContext *dc, int rd, TCGv addr, MemOp mop, static bool trans_sb(DisasContext *dc, arg_typea *arg) { - TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + TCGv_i32 addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); return do_store(dc, arg->rd, addr, MO_UB, dc->mem_index, false); } static bool trans_sbr(DisasContext *dc, arg_typea *arg) { - TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + TCGv_i32 addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); return do_store(dc, arg->rd, addr, MO_UB, dc->mem_index, true); } @@ -936,19 +936,19 @@ static bool trans_sbea(DisasContext *dc, arg_typea *arg) static bool trans_sbi(DisasContext *dc, arg_typeb *arg) { - TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + TCGv_i32 addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); return do_store(dc, arg->rd, addr, MO_UB, dc->mem_index, false); } static bool trans_sh(DisasContext *dc, arg_typea *arg) { - TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + TCGv_i32 addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); return do_store(dc, arg->rd, addr, MO_UW, dc->mem_index, false); } static bool trans_shr(DisasContext *dc, arg_typea *arg) { - TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + TCGv_i32 addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); return do_store(dc, arg->rd, addr, MO_UW, dc->mem_index, true); } @@ -970,19 +970,19 @@ static bool trans_shea(DisasContext *dc, arg_typea *arg) static bool trans_shi(DisasContext *dc, arg_typeb *arg) { - TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + TCGv_i32 addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); return do_store(dc, arg->rd, addr, MO_UW, dc->mem_index, false); } static bool trans_sw(DisasContext *dc, arg_typea *arg) { - TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + TCGv_i32 addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); return do_store(dc, arg->rd, addr, MO_UL, dc->mem_index, false); } static bool trans_swr(DisasContext *dc, arg_typea *arg) { - TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + TCGv_i32 addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); return do_store(dc, arg->rd, addr, MO_UL, dc->mem_index, true); } @@ -1004,19 +1004,19 @@ static bool trans_swea(DisasContext *dc, arg_typea *arg) static bool trans_swi(DisasContext *dc, arg_typeb *arg) { - TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + TCGv_i32 addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); return do_store(dc, arg->rd, addr, MO_UL, dc->mem_index, false); } static bool trans_swx(DisasContext *dc, arg_typea *arg) { - TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + TCGv_i32 addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); TCGLabel *swx_done = gen_new_label(); TCGLabel *swx_fail = gen_new_label(); TCGv_i32 tval; /* swx does not throw unaligned access errors, so force alignment */ - tcg_gen_andi_tl(addr, addr, ~3); + tcg_gen_andi_i32(addr, addr, ~3); /* * Compare the address vs the one we used during lwx. From 2c12ba4d8bdd309359d1072594f5a317e09da78b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 15 Oct 2025 19:34:49 +0200 Subject: [PATCH 1621/1794] target/microblaze: Have do_load/store() take a TCGv_i32 address argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All callers of do_load() and do_store() pass a TCGv_i32 address type, have both functions take a TCGv_i32. Suggested-by: Anton Johansson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20251015180115.97493-7-philmd@linaro.org> --- target/microblaze/translate.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 1554b9e67b0f2..ed53848bad5bc 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -708,7 +708,7 @@ static inline MemOp mo_endian(DisasContext *dc) return dc->cfg->endi ? MO_LE : MO_BE; } -static bool do_load(DisasContext *dc, int rd, TCGv addr, MemOp mop, +static bool do_load(DisasContext *dc, int rd, TCGv_i32 addr, MemOp mop, int mem_index, bool rev) { MemOp size = mop & MO_SIZE; @@ -726,7 +726,7 @@ static bool do_load(DisasContext *dc, int rd, TCGv addr, MemOp mop, mop ^= MO_BSWAP; } if (size < MO_32) { - tcg_gen_xori_tl(addr, addr, 3 - size); + tcg_gen_xori_i32(addr, addr, 3 - size); } } @@ -868,7 +868,7 @@ static bool trans_lwx(DisasContext *dc, arg_typea *arg) return true; } -static bool do_store(DisasContext *dc, int rd, TCGv addr, MemOp mop, +static bool do_store(DisasContext *dc, int rd, TCGv_i32 addr, MemOp mop, int mem_index, bool rev) { MemOp size = mop & MO_SIZE; @@ -886,7 +886,7 @@ static bool do_store(DisasContext *dc, int rd, TCGv addr, MemOp mop, mop ^= MO_BSWAP; } if (size < MO_32) { - tcg_gen_xori_tl(addr, addr, 3 - size); + tcg_gen_xori_i32(addr, addr, 3 - size); } } From 5dfe024f9e2d97bb5b2e8f7bd520e8a55e39a725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 06:58:51 +0200 Subject: [PATCH 1622/1794] target/microblaze: Convert CPUMBState::res_addr field to uint32_t type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPUMBState::@res_addr field is used as u32 since commit cfeea807e5a ("target-microblaze: Tighten up TCGv_i32 vs TCGv type usage"). Convert it as such, bumping the migration version. Use the RES_ADDR_NONE definition when appropriate. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20251015180115.97493-8-philmd@linaro.org> --- target/microblaze/cpu.h | 2 +- target/microblaze/machine.c | 6 +++--- target/microblaze/translate.c | 17 +++++++++-------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 3ce28b302fe55..14b107876a432 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -259,7 +259,7 @@ struct CPUArchState { /* lwx/swx reserved address */ #define RES_ADDR_NONE 0xffffffff /* Use 0xffffffff to indicate no reservation */ - target_ulong res_addr; + uint32_t res_addr; uint32_t res_val; /* Internal flags. */ diff --git a/target/microblaze/machine.c b/target/microblaze/machine.c index a4cf38dc89177..48efa546d3913 100644 --- a/target/microblaze/machine.c +++ b/target/microblaze/machine.c @@ -78,7 +78,7 @@ static const VMStateField vmstate_env_fields[] = { VMSTATE_UINT32(iflags, CPUMBState), VMSTATE_UINT32(res_val, CPUMBState), - VMSTATE_UINTTL(res_addr, CPUMBState), + VMSTATE_UINT32(res_addr, CPUMBState), VMSTATE_STRUCT(mmu, CPUMBState, 0, vmstate_mmu, MicroBlazeMMU), @@ -87,8 +87,8 @@ static const VMStateField vmstate_env_fields[] = { static const VMStateDescription vmstate_env = { .name = "env", - .version_id = 0, - .minimum_version_id = 0, + .version_id = 1, + .minimum_version_id = 1, .fields = vmstate_env_fields, }; diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index ed53848bad5bc..6442a250c5dd0 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -55,7 +55,7 @@ static TCGv_i32 cpu_imm; static TCGv_i32 cpu_bvalue; static TCGv_i32 cpu_btarget; static TCGv_i32 cpu_iflags; -static TCGv cpu_res_addr; +static TCGv_i32 cpu_res_addr; static TCGv_i32 cpu_res_val; /* This is the state at translation time. */ @@ -857,7 +857,7 @@ static bool trans_lwx(DisasContext *dc, arg_typea *arg) tcg_gen_qemu_ld_i32(cpu_res_val, addr, dc->mem_index, mo_endian(dc) | MO_UL); - tcg_gen_mov_tl(cpu_res_addr, addr); + tcg_gen_mov_i32(cpu_res_addr, addr); if (arg->rd) { tcg_gen_mov_i32(cpu_R[arg->rd], cpu_res_val); @@ -1024,7 +1024,7 @@ static bool trans_swx(DisasContext *dc, arg_typea *arg) * branch, but we know we can use the equal version in the global. * In either case, addr is no longer needed. */ - tcg_gen_brcond_tl(TCG_COND_NE, cpu_res_addr, addr, swx_fail); + tcg_gen_brcond_i32(TCG_COND_NE, cpu_res_addr, addr, swx_fail); /* * Compare the value loaded during lwx with current contents of @@ -1052,7 +1052,7 @@ static bool trans_swx(DisasContext *dc, arg_typea *arg) * Prevent the saved address from working again without another ldx. * Akin to the pseudocode setting reservation = 0. */ - tcg_gen_movi_tl(cpu_res_addr, -1); + tcg_gen_movi_i32(cpu_res_addr, RES_ADDR_NONE); return true; } @@ -1173,7 +1173,7 @@ static bool trans_brk(DisasContext *dc, arg_typea_br *arg) tcg_gen_movi_i32(cpu_R[arg->rd], dc->base.pc_next); } tcg_gen_ori_i32(cpu_msr, cpu_msr, MSR_BIP); - tcg_gen_movi_tl(cpu_res_addr, -1); + tcg_gen_movi_i32(cpu_res_addr, RES_ADDR_NONE); dc->base.is_jmp = DISAS_EXIT; return true; @@ -1194,7 +1194,7 @@ static bool trans_brki(DisasContext *dc, arg_typeb_br *arg) if (arg->rd) { tcg_gen_movi_i32(cpu_R[arg->rd], dc->base.pc_next); } - tcg_gen_movi_tl(cpu_res_addr, -1); + tcg_gen_movi_i32(cpu_res_addr, RES_ADDR_NONE); #ifdef CONFIG_USER_ONLY switch (imm) { @@ -1885,6 +1885,7 @@ void mb_tcg_init(void) tcg_global_mem_new_i32(tcg_env, i32s[i].ofs, i32s[i].name); } - cpu_res_addr = - tcg_global_mem_new(tcg_env, offsetof(CPUMBState, res_addr), "res_addr"); + cpu_res_addr = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUMBState, res_addr), + "res_addr"); } From 2795bc52af4949aa7c7d900f897e5fc81c6c9b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 25 Sep 2025 02:46:47 +0200 Subject: [PATCH 1623/1794] target/openrisc: Replace VMSTATE_UINTTL() -> VMSTATE_UINT32() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both OpenRISCTLBEntry fields are of uint32_t type. Use the appropriate VMSTATE_UINT32() macro. There is no functional change (the migration stream is not modified), because the OpenRISC targets are only built as 32-bit: $ git grep TARGET_LONG_BITS configs/targets/or1k-* configs/targets/or1k-linux-user.mak:5:TARGET_LONG_BITS=32 configs/targets/or1k-softmmu.mak:5:TARGET_LONG_BITS=32 Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Richard Henderson Message-Id: <20251010070702.51484-2-philmd@linaro.org> --- target/openrisc/machine.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/openrisc/machine.c b/target/openrisc/machine.c index 081c706d02c42..fa054e528bddb 100644 --- a/target/openrisc/machine.c +++ b/target/openrisc/machine.c @@ -26,8 +26,8 @@ static const VMStateDescription vmstate_tlb_entry = { .version_id = 1, .minimum_version_id = 1, .fields = (const VMStateField[]) { - VMSTATE_UINTTL(mr, OpenRISCTLBEntry), - VMSTATE_UINTTL(tr, OpenRISCTLBEntry), + VMSTATE_UINT32(mr, OpenRISCTLBEntry), + VMSTATE_UINT32(tr, OpenRISCTLBEntry), VMSTATE_END_OF_LIST() } }; From a3c4facd395b9d39996ed60d42b1f448d3f727cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 09:14:15 +0200 Subject: [PATCH 1624/1794] target/openrisc: Do not use target_ulong for @mr in MTSPR helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenRISCTLBEntry::@mr field is a uint32_t type since its introduction in commit 726fe045720 ("target-or32: Add MMU support"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Richard Henderson Message-Id: <20251010070702.51484-3-philmd@linaro.org> --- target/openrisc/sys_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index b091a9c6685a4..ad59939db3b80 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -45,7 +45,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) OpenRISCCPU *cpu = env_archcpu(env); #ifndef CONFIG_USER_ONLY CPUState *cs = env_cpu(env); - target_ulong mr; + uint32_t mr; int idx; #endif From 1843e89bec5f8551a9a554aca9f51d76e6ae9493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 09:06:05 +0200 Subject: [PATCH 1625/1794] target/openrisc: Remove unused cpu_openrisc_map_address_*() handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 23d45ebdb19 ("target/openrisc: Remove indirect function calls for mmu") removed the last uses of both cpu_openrisc_map_address_code() and cpu_openrisc_map_address_data() helpers. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Richard Henderson Message-Id: <20251010070702.51484-4-philmd@linaro.org> --- target/openrisc/cpu.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index f4bcf00b07357..87201365a91b9 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -220,15 +220,6 @@ typedef struct OpenRISCTLBEntry { typedef struct CPUOpenRISCTLBContext { OpenRISCTLBEntry itlb[TLB_SIZE]; OpenRISCTLBEntry dtlb[TLB_SIZE]; - - int (*cpu_openrisc_map_address_code)(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, - target_ulong address, int rw); - int (*cpu_openrisc_map_address_data)(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, - target_ulong address, int rw); } CPUOpenRISCTLBContext; #endif From 2367c94cbb1876b789ff91f78fd7daf2c1d6fb93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 09:06:25 +0200 Subject: [PATCH 1626/1794] target/openrisc: Remove target_ulong use in raise_mmu_exception() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TCGCPUOps::tlb_fill() handler provides a vaddr type (since commit 7510454e3e7 "cpu: Turn cpu_handle_mmu_fault() into a CPUClass hook"). Do not inline get_phys_nommu(), rely on the linker to optimize at linking time. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Richard Henderson Message-Id: <20251010070702.51484-5-philmd@linaro.org> --- target/openrisc/mmu.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index acea50c41eb76..ffb732e0d1f6d 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -28,15 +28,14 @@ #include "qemu/host-utils.h" #include "hw/loader.h" -static inline void get_phys_nommu(hwaddr *phys_addr, int *prot, - target_ulong address) +static void get_phys_nommu(hwaddr *phys_addr, int *prot, vaddr address) { *phys_addr = address; *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; } static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot, - target_ulong addr, int need, bool super) + vaddr addr, int need, bool super) { int idx = (addr >> TARGET_PAGE_BITS) & TLB_MASK; uint32_t imr = cpu->env.tlb.itlb[idx].mr; @@ -95,7 +94,7 @@ static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot, } } -static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address, +static void raise_mmu_exception(OpenRISCCPU *cpu, vaddr address, int exception) { CPUState *cs = CPU(cpu); From 9dc4862dc4ae95f4f5b94052b95699a83e1d197c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 09:06:52 +0200 Subject: [PATCH 1627/1794] target/openrisc: Use vaddr type for $pc jumps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit translator_use_goto_tb() expects a vaddr type since commit b1c09220b4c ("accel/tcg: Replace target_ulong with vaddr in translator_*()"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Richard Henderson Message-Id: <20251010070702.51484-6-philmd@linaro.org> --- target/openrisc/translate.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 5ab3bc7021dc4..9f61f917b3b95 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -51,7 +51,7 @@ typedef struct DisasContext { uint32_t avr; /* If not -1, jmp_pc contains this value and so is a direct jump. */ - target_ulong jmp_pc_imm; + vaddr jmp_pc_imm; /* The temporary corresponding to register 0 for this compilation. */ TCGv R0; @@ -580,7 +580,7 @@ static bool trans_l_muldu(DisasContext *dc, arg_ab *a) static bool trans_l_j(DisasContext *dc, arg_l_j *a) { - target_ulong tmp_pc = dc->base.pc_next + a->n * 4; + vaddr tmp_pc = dc->base.pc_next + a->n * 4; tcg_gen_movi_tl(jmp_pc, tmp_pc); dc->jmp_pc_imm = tmp_pc; @@ -590,8 +590,8 @@ static bool trans_l_j(DisasContext *dc, arg_l_j *a) static bool trans_l_jal(DisasContext *dc, arg_l_jal *a) { - target_ulong tmp_pc = dc->base.pc_next + a->n * 4; - target_ulong ret_pc = dc->base.pc_next + 8; + vaddr tmp_pc = dc->base.pc_next + a->n * 4; + vaddr ret_pc = dc->base.pc_next + 8; tcg_gen_movi_tl(cpu_regs[9], ret_pc); /* Optimize jal being used to load the PC for PIC. */ @@ -605,7 +605,7 @@ static bool trans_l_jal(DisasContext *dc, arg_l_jal *a) static void do_bf(DisasContext *dc, arg_l_bf *a, TCGCond cond) { - target_ulong tmp_pc = dc->base.pc_next + a->n * 4; + vaddr tmp_pc = dc->base.pc_next + a->n * 4; TCGv t_next = tcg_constant_tl(dc->base.pc_next + 8); TCGv t_true = tcg_constant_tl(tmp_pc); @@ -1586,7 +1586,7 @@ static void openrisc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); - target_ulong jmp_dest; + vaddr jmp_dest; /* If we have already exited the TB, nothing following has effect. */ if (dc->base.is_jmp == DISAS_NORETURN) { From 81e2fb236b702c7e172b64be4fd11aacebe50e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 10:11:59 +0200 Subject: [PATCH 1628/1794] target/openrisc: Remove 'TARGET_LONG_BITS != 32' dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OpenRISC targets are only built as 32-bit: $ git grep TARGET_LONG_BITS configs/targets/or1k-* configs/targets/or1k-linux-user.mak:5:TARGET_LONG_BITS=32 configs/targets/or1k-softmmu.mak:5:TARGET_LONG_BITS=32 Remove the dead code guarded within TARGET_LONG_BITS != 32. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251010070702.51484-7-philmd@linaro.org> --- target/openrisc/translate.c | 33 ++++----------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 9f61f917b3b95..29e6b51a93087 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -298,19 +298,8 @@ static void gen_muld(DisasContext *dc, TCGv srca, TCGv srcb) tcg_gen_ext_tl_i64(t1, srca); tcg_gen_ext_tl_i64(t2, srcb); - if (TARGET_LONG_BITS == 32) { - tcg_gen_mul_i64(cpu_mac, t1, t2); - tcg_gen_movi_tl(cpu_sr_ov, 0); - } else { - TCGv_i64 high = tcg_temp_new_i64(); - - tcg_gen_muls2_i64(cpu_mac, high, t1, t2); - tcg_gen_sari_i64(t1, cpu_mac, 63); - tcg_gen_negsetcond_i64(TCG_COND_NE, t1, t1, high); - tcg_gen_trunc_i64_tl(cpu_sr_ov, t1); - - gen_ove_ov(dc); - } + tcg_gen_mul_i64(cpu_mac, t1, t2); + tcg_gen_movi_tl(cpu_sr_ov, 0); } static void gen_muldu(DisasContext *dc, TCGv srca, TCGv srcb) @@ -320,18 +309,8 @@ static void gen_muldu(DisasContext *dc, TCGv srca, TCGv srcb) tcg_gen_extu_tl_i64(t1, srca); tcg_gen_extu_tl_i64(t2, srcb); - if (TARGET_LONG_BITS == 32) { - tcg_gen_mul_i64(cpu_mac, t1, t2); - tcg_gen_movi_tl(cpu_sr_cy, 0); - } else { - TCGv_i64 high = tcg_temp_new_i64(); - - tcg_gen_mulu2_i64(cpu_mac, high, t1, t2); - tcg_gen_setcondi_i64(TCG_COND_NE, high, high, 0); - tcg_gen_trunc_i64_tl(cpu_sr_cy, high); - - gen_ove_cy(dc); - } + tcg_gen_mul_i64(cpu_mac, t1, t2); + tcg_gen_movi_tl(cpu_sr_cy, 0); } static void gen_mac(DisasContext *dc, TCGv srca, TCGv srcb) @@ -349,11 +328,7 @@ static void gen_mac(DisasContext *dc, TCGv srca, TCGv srcb) tcg_gen_xor_i64(t1, t1, cpu_mac); tcg_gen_andc_i64(t1, t1, t2); -#if TARGET_LONG_BITS == 32 tcg_gen_extrh_i64_i32(cpu_sr_ov, t1); -#else - tcg_gen_mov_i64(cpu_sr_ov, t1); -#endif gen_ove_ov(dc); } From ef797ac00257ed31f534058fc43b8ae7c0019ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Mar 2025 10:19:50 +0100 Subject: [PATCH 1629/1794] target/openrisc: Explode MO_TExx -> MO_TE | MO_xx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the implicit MO_TE definition in order to replace it in the next commit. Mechanical change using: $ for n in UW UL UQ UO SW SL SQ; do \ sed -i -e "s/MO_TE$n/MO_TE | MO_$n/" \ $(git grep -l MO_TE$n target/openrisc); \ done Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251010070702.51484-8-philmd@linaro.org> --- target/openrisc/translate.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 29e6b51a93087..52d51313f77f0 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -622,7 +622,7 @@ static bool trans_l_lwa(DisasContext *dc, arg_load *a) check_r0_write(dc, a->d); ea = tcg_temp_new(); tcg_gen_addi_tl(ea, cpu_R(dc, a->a), a->i); - tcg_gen_qemu_ld_tl(cpu_R(dc, a->d), ea, dc->mem_idx, MO_TEUL); + tcg_gen_qemu_ld_tl(cpu_R(dc, a->d), ea, dc->mem_idx, MO_TE | MO_UL); tcg_gen_mov_tl(cpu_lock_addr, ea); tcg_gen_mov_tl(cpu_lock_value, cpu_R(dc, a->d)); return true; @@ -640,13 +640,13 @@ static void do_load(DisasContext *dc, arg_load *a, MemOp mop) static bool trans_l_lwz(DisasContext *dc, arg_load *a) { - do_load(dc, a, MO_TEUL); + do_load(dc, a, MO_TE | MO_UL); return true; } static bool trans_l_lws(DisasContext *dc, arg_load *a) { - do_load(dc, a, MO_TESL); + do_load(dc, a, MO_TE | MO_SL); return true; } @@ -664,13 +664,13 @@ static bool trans_l_lbs(DisasContext *dc, arg_load *a) static bool trans_l_lhz(DisasContext *dc, arg_load *a) { - do_load(dc, a, MO_TEUW); + do_load(dc, a, MO_TE | MO_UW); return true; } static bool trans_l_lhs(DisasContext *dc, arg_load *a) { - do_load(dc, a, MO_TESW); + do_load(dc, a, MO_TE | MO_SW); return true; } @@ -688,7 +688,7 @@ static bool trans_l_swa(DisasContext *dc, arg_store *a) val = tcg_temp_new(); tcg_gen_atomic_cmpxchg_tl(val, cpu_lock_addr, cpu_lock_value, - cpu_R(dc, a->b), dc->mem_idx, MO_TEUL); + cpu_R(dc, a->b), dc->mem_idx, MO_TE | MO_UL); tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, val, cpu_lock_value); tcg_gen_br(lab_done); @@ -710,7 +710,7 @@ static void do_store(DisasContext *dc, arg_store *a, MemOp mop) static bool trans_l_sw(DisasContext *dc, arg_store *a) { - do_store(dc, a, MO_TEUL); + do_store(dc, a, MO_TE | MO_UL); return true; } @@ -722,7 +722,7 @@ static bool trans_l_sb(DisasContext *dc, arg_store *a) static bool trans_l_sh(DisasContext *dc, arg_store *a) { - do_store(dc, a, MO_TEUW); + do_store(dc, a, MO_TE | MO_UW); return true; } From 22c36c0a20ce7bccbfe61ff7ee676c7a0e6b7c51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Mar 2025 10:23:46 +0100 Subject: [PATCH 1630/1794] target/openrisc: Conceal MO_TE within do_load() 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: <20251010070702.51484-9-philmd@linaro.org> --- target/openrisc/translate.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 52d51313f77f0..a6ba1747eca07 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -632,6 +632,8 @@ static void do_load(DisasContext *dc, arg_load *a, MemOp mop) { TCGv ea; + mop |= MO_TE; + check_r0_write(dc, a->d); ea = tcg_temp_new(); tcg_gen_addi_tl(ea, cpu_R(dc, a->a), a->i); @@ -640,13 +642,13 @@ static void do_load(DisasContext *dc, arg_load *a, MemOp mop) static bool trans_l_lwz(DisasContext *dc, arg_load *a) { - do_load(dc, a, MO_TE | MO_UL); + do_load(dc, a, MO_UL); return true; } static bool trans_l_lws(DisasContext *dc, arg_load *a) { - do_load(dc, a, MO_TE | MO_SL); + do_load(dc, a, MO_SL); return true; } @@ -664,13 +666,13 @@ static bool trans_l_lbs(DisasContext *dc, arg_load *a) static bool trans_l_lhz(DisasContext *dc, arg_load *a) { - do_load(dc, a, MO_TE | MO_UW); + do_load(dc, a, MO_UW); return true; } static bool trans_l_lhs(DisasContext *dc, arg_load *a) { - do_load(dc, a, MO_TE | MO_SW); + do_load(dc, a, MO_SW); return true; } From 2f737e19a4dbb72596c29656cec07f26198161d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Mar 2025 10:23:46 +0100 Subject: [PATCH 1631/1794] target/openrisc: Conceal MO_TE within do_store() 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: <20251010070702.51484-10-philmd@linaro.org> --- target/openrisc/translate.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index a6ba1747eca07..623513bb15c48 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -706,13 +706,16 @@ static bool trans_l_swa(DisasContext *dc, arg_store *a) static void do_store(DisasContext *dc, arg_store *a, MemOp mop) { TCGv t0 = tcg_temp_new(); + + mop |= MO_TE; + tcg_gen_addi_tl(t0, cpu_R(dc, a->a), a->i); tcg_gen_qemu_st_tl(cpu_R(dc, a->b), t0, dc->mem_idx, mop); } static bool trans_l_sw(DisasContext *dc, arg_store *a) { - do_store(dc, a, MO_TE | MO_UL); + do_store(dc, a, MO_UL); return true; } @@ -724,7 +727,7 @@ static bool trans_l_sb(DisasContext *dc, arg_store *a) static bool trans_l_sh(DisasContext *dc, arg_store *a) { - do_store(dc, a, MO_TE | MO_UW); + do_store(dc, a, MO_UW); return true; } From 73cd283e58d4a8f03ed8786e148ceedf676bee40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Mar 2025 10:25:49 +0100 Subject: [PATCH 1632/1794] target/openrisc: Introduce mo_endian() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mo_endian() returns the target endianness. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251010070702.51484-11-philmd@linaro.org> --- target/openrisc/translate.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 623513bb15c48..1a43e5ab6f734 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -59,6 +59,11 @@ typedef struct DisasContext { TCGv zero; } DisasContext; +static inline MemOp mo_endian(DisasContext *dc) +{ + return MO_TE; +} + static inline bool is_user(DisasContext *dc) { #ifdef CONFIG_USER_ONLY @@ -622,7 +627,8 @@ static bool trans_l_lwa(DisasContext *dc, arg_load *a) check_r0_write(dc, a->d); ea = tcg_temp_new(); tcg_gen_addi_tl(ea, cpu_R(dc, a->a), a->i); - tcg_gen_qemu_ld_tl(cpu_R(dc, a->d), ea, dc->mem_idx, MO_TE | MO_UL); + tcg_gen_qemu_ld_tl(cpu_R(dc, a->d), ea, dc->mem_idx, + mo_endian(dc) | MO_UL); tcg_gen_mov_tl(cpu_lock_addr, ea); tcg_gen_mov_tl(cpu_lock_value, cpu_R(dc, a->d)); return true; @@ -632,7 +638,7 @@ static void do_load(DisasContext *dc, arg_load *a, MemOp mop) { TCGv ea; - mop |= MO_TE; + mop |= mo_endian(dc); check_r0_write(dc, a->d); ea = tcg_temp_new(); @@ -690,7 +696,8 @@ static bool trans_l_swa(DisasContext *dc, arg_store *a) val = tcg_temp_new(); tcg_gen_atomic_cmpxchg_tl(val, cpu_lock_addr, cpu_lock_value, - cpu_R(dc, a->b), dc->mem_idx, MO_TE | MO_UL); + cpu_R(dc, a->b), dc->mem_idx, + mo_endian(dc) | MO_UL); tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, val, cpu_lock_value); tcg_gen_br(lab_done); @@ -707,7 +714,7 @@ static void do_store(DisasContext *dc, arg_store *a, MemOp mop) { TCGv t0 = tcg_temp_new(); - mop |= MO_TE; + mop |= mo_endian(dc); tcg_gen_addi_tl(t0, cpu_R(dc, a->a), a->i); tcg_gen_qemu_st_tl(cpu_R(dc, a->b), t0, dc->mem_idx, mop); From 4f6c60683f2033dd9177ca0bf1299ffea1c07dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Mar 2025 10:27:08 +0100 Subject: [PATCH 1633/1794] target/openrisc: Replace MO_TE -> MO_BE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only build the OpenRISC targets using big endianness order: $ git grep TARGET_BIG_ENDIAN configs/targets/or1k-* configs/targets/or1k-linux-user.mak:2:TARGET_BIG_ENDIAN=y configs/targets/or1k-softmmu.mak:2:TARGET_BIG_ENDIAN=y Therefore the MO_TE definition always expands to MO_BE. Use the latter to simplify. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251010070702.51484-12-philmd@linaro.org> --- target/openrisc/translate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 1a43e5ab6f734..3d9dfd663aad8 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -61,7 +61,8 @@ typedef struct DisasContext { static inline MemOp mo_endian(DisasContext *dc) { - return MO_TE; + /* The SR_LEE bit sets the (little) endianness, but we don't implement it. */ + return MO_BE; } static inline bool is_user(DisasContext *dc) From 500708331e3f0c856270c14d836c79fcc65bdfde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 09:32:05 +0200 Subject: [PATCH 1634/1794] target/openrisc: Inline tcg_gen_trunc_i64_tl() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OpenRISC targets are only built as 32-bit, so tcg_gen_trunc_i64_tl expands to tcg_gen_extrl_i64_i32(). Use the latter to simplify the next commit mechanical change. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Richard Henderson Message-Id: <20251010070702.51484-13-philmd@linaro.org> --- target/openrisc/translate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 3d9dfd663aad8..beb4a546fd73a 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -351,7 +351,7 @@ static void gen_macu(DisasContext *dc, TCGv srca, TCGv srcb) /* Note that overflow is only computed during addition stage. */ tcg_gen_add_i64(cpu_mac, cpu_mac, t1); tcg_gen_setcond_i64(TCG_COND_LTU, t1, cpu_mac, t1); - tcg_gen_trunc_i64_tl(cpu_sr_cy, t1); + tcg_gen_extrl_i64_i32(cpu_sr_cy, t1); gen_ove_cy(dc); } @@ -392,7 +392,7 @@ static void gen_msbu(DisasContext *dc, TCGv srca, TCGv srcb) /* Note that overflow is only computed during subtraction stage. */ tcg_gen_setcond_i64(TCG_COND_LTU, t2, cpu_mac, t1); tcg_gen_sub_i64(cpu_mac, cpu_mac, t1); - tcg_gen_trunc_i64_tl(cpu_sr_cy, t2); + tcg_gen_extrl_i64_i32(cpu_sr_cy, t2); gen_ove_cy(dc); } @@ -917,7 +917,7 @@ static bool trans_l_movhi(DisasContext *dc, arg_l_movhi *a) static bool trans_l_macrc(DisasContext *dc, arg_l_macrc *a) { check_r0_write(dc, a->d); - tcg_gen_trunc_i64_tl(cpu_R(dc, a->d), cpu_mac); + tcg_gen_extrl_i64_i32(cpu_R(dc, a->d), cpu_mac); tcg_gen_movi_i64(cpu_mac, 0); return true; } From 5b67dbf1dc3caa6992576725a0783fa68fe6990c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 09:59:05 +0200 Subject: [PATCH 1635/1794] target/openrisc: Replace target_ulong -> uint32_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OpenRISC targets are only built as 32-bit: $ git grep TARGET_LONG_BITS configs/targets/or1k-* configs/targets/or1k-linux-user.mak:5:TARGET_LONG_BITS=32 configs/targets/or1k-softmmu.mak:5:TARGET_LONG_BITS=32 Therefore target_ulong always expands to uint32_t. Replace and adapt the API uses mechanically: target_ulong -> uint32_t target_long -> int32_t tl -> i32 TCGv -> TCGv_i32 tcg_temp_new -> tcg_temp_new_i32 tcg_global_mem_new -> tcg_global_mem_new_i32 VMSTATE_UINTTL -> VMSTATE_UINT32 There is no functional change (the migration stream is not modified). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Richard Henderson Message-Id: <20251010070702.51484-14-philmd@linaro.org> --- target/openrisc/cpu.h | 22 +- target/openrisc/fpu_helper.c | 8 +- target/openrisc/helper.h | 8 +- target/openrisc/machine.c | 16 +- target/openrisc/sys_helper.c | 5 +- target/openrisc/translate.c | 391 ++++++++++++++++++----------------- 6 files changed, 228 insertions(+), 222 deletions(-) diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index 87201365a91b9..c8e2827930be8 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -224,20 +224,20 @@ typedef struct CPUOpenRISCTLBContext { #endif typedef struct CPUArchState { - target_ulong shadow_gpr[16][32]; /* Shadow registers */ + uint32_t shadow_gpr[16][32]; /* Shadow registers */ - target_ulong pc; /* Program counter */ - target_ulong ppc; /* Prev PC */ - target_ulong jmp_pc; /* Jump PC */ + uint32_t pc; /* Program counter */ + uint32_t ppc; /* Prev PC */ + uint32_t jmp_pc; /* Jump PC */ uint64_t mac; /* Multiply registers MACHI:MACLO */ - target_ulong epcr; /* Exception PC register */ - target_ulong eear; /* Exception EA register */ + uint32_t epcr; /* Exception PC register */ + uint32_t eear; /* Exception EA register */ - target_ulong sr_f; /* the SR_F bit, values 0, 1. */ - target_ulong sr_cy; /* the SR_CY bit, values 0, 1. */ - target_long sr_ov; /* the SR_OV bit (in the sign bit only) */ + uint32_t sr_f; /* the SR_F bit, values 0, 1. */ + uint32_t sr_cy; /* the SR_CY bit, values 0, 1. */ + int32_t sr_ov; /* the SR_OV bit (in the sign bit only) */ uint32_t sr; /* Supervisor register, without SR_{F,CY,OV} */ uint32_t esr; /* Exception supervisor register */ uint32_t evbar; /* Exception vector base address register */ @@ -245,8 +245,8 @@ typedef struct CPUArchState { uint32_t fpcsr; /* Float register */ float_status fp_status; - target_ulong lock_addr; - target_ulong lock_value; + uint32_t lock_addr; + uint32_t lock_value; uint32_t dflag; /* In delay slot (boolean) */ diff --git a/target/openrisc/fpu_helper.c b/target/openrisc/fpu_helper.c index dba997255c6b8..39b6195dd7d35 100644 --- a/target/openrisc/fpu_helper.c +++ b/target/openrisc/fpu_helper.c @@ -146,10 +146,10 @@ uint32_t helper_float_madd_s(CPUOpenRISCState *env, uint32_t a, #define FLOAT_CMP(name, impl) \ -target_ulong helper_float_ ## name ## _d(CPUOpenRISCState *env, \ +uint32_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ uint64_t fdt0, uint64_t fdt1) \ { return float64_ ## impl(fdt0, fdt1, &env->fp_status); } \ -target_ulong helper_float_ ## name ## _s(CPUOpenRISCState *env, \ +uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ uint32_t fdt0, uint32_t fdt1) \ { return float32_ ## impl(fdt0, fdt1, &env->fp_status); } @@ -160,13 +160,13 @@ FLOAT_CMP(un, unordered_quiet) #undef FLOAT_CMP #define FLOAT_UCMP(name, expr) \ -target_ulong helper_float_ ## name ## _d(CPUOpenRISCState *env, \ +uint32_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ uint64_t fdt0, uint64_t fdt1) \ { \ FloatRelation r = float64_compare_quiet(fdt0, fdt1, &env->fp_status); \ return expr; \ } \ -target_ulong helper_float_ ## name ## _s(CPUOpenRISCState *env, \ +uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ uint32_t fdt0, uint32_t fdt1) \ { \ FloatRelation r = float32_compare_quiet(fdt0, fdt1, &env->fp_status); \ diff --git a/target/openrisc/helper.h b/target/openrisc/helper.h index d847814a28d01..e0a8d402271bb 100644 --- a/target/openrisc/helper.h +++ b/target/openrisc/helper.h @@ -47,8 +47,8 @@ FOP_CALC(rem) #undef FOP_CALC #define FOP_CMP(op) \ -DEF_HELPER_FLAGS_3(float_ ## op ## _s, TCG_CALL_NO_RWG, tl, env, i32, i32) \ -DEF_HELPER_FLAGS_3(float_ ## op ## _d, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(float_ ## op ## _s, TCG_CALL_NO_RWG, i32, env, i32, i32) \ +DEF_HELPER_FLAGS_3(float_ ## op ## _d, TCG_CALL_NO_RWG, i32, env, i64, i64) FOP_CMP(eq) FOP_CMP(lt) FOP_CMP(le) @@ -62,5 +62,5 @@ FOP_CMP(ult) DEF_HELPER_FLAGS_1(rfe, 0, void, env) /* sys */ -DEF_HELPER_FLAGS_3(mtspr, 0, void, env, tl, tl) -DEF_HELPER_FLAGS_3(mfspr, TCG_CALL_NO_WG, tl, env, tl, tl) +DEF_HELPER_FLAGS_3(mtspr, 0, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mfspr, TCG_CALL_NO_WG, i32, env, i32, i32) diff --git a/target/openrisc/machine.c b/target/openrisc/machine.c index fa054e528bddb..f2853674f0ffa 100644 --- a/target/openrisc/machine.c +++ b/target/openrisc/machine.c @@ -72,14 +72,14 @@ static const VMStateDescription vmstate_env = { .version_id = 6, .minimum_version_id = 6, .fields = (const VMStateField[]) { - VMSTATE_UINTTL_2DARRAY(shadow_gpr, CPUOpenRISCState, 16, 32), - VMSTATE_UINTTL(pc, CPUOpenRISCState), - VMSTATE_UINTTL(ppc, CPUOpenRISCState), - VMSTATE_UINTTL(jmp_pc, CPUOpenRISCState), - VMSTATE_UINTTL(lock_addr, CPUOpenRISCState), - VMSTATE_UINTTL(lock_value, CPUOpenRISCState), - VMSTATE_UINTTL(epcr, CPUOpenRISCState), - VMSTATE_UINTTL(eear, CPUOpenRISCState), + VMSTATE_UINT32_2DARRAY(shadow_gpr, CPUOpenRISCState, 16, 32), + VMSTATE_UINT32(pc, CPUOpenRISCState), + VMSTATE_UINT32(ppc, CPUOpenRISCState), + VMSTATE_UINT32(jmp_pc, CPUOpenRISCState), + VMSTATE_UINT32(lock_addr, CPUOpenRISCState), + VMSTATE_UINT32(lock_value, CPUOpenRISCState), + VMSTATE_UINT32(epcr, CPUOpenRISCState), + VMSTATE_UINT32(eear, CPUOpenRISCState), /* Save the architecture value of the SR, not the internally expanded version. Since this architecture value does not diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index ad59939db3b80..7ad908b632221 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -40,7 +40,7 @@ static inline bool is_user(CPUOpenRISCState *env) #endif } -void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) +void HELPER(mtspr)(CPUOpenRISCState *env, uint32_t spr, uint32_t rb) { OpenRISCCPU *cpu = env_archcpu(env); #ifndef CONFIG_USER_ONLY @@ -213,8 +213,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) #endif } -target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd, - target_ulong spr) +uint32_t HELPER(mfspr)(CPUOpenRISCState *env, uint32_t rd, uint32_t spr) { OpenRISCCPU *cpu = env_archcpu(env); #ifndef CONFIG_USER_ONLY diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index beb4a546fd73a..6fa4d6cfa709c 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -54,9 +54,9 @@ typedef struct DisasContext { vaddr jmp_pc_imm; /* The temporary corresponding to register 0 for this compilation. */ - TCGv R0; + TCGv_i32 R0; /* The constant zero. */ - TCGv zero; + TCGv_i32 zero; } DisasContext; static inline MemOp mo_endian(DisasContext *dc) @@ -77,16 +77,16 @@ static inline bool is_user(DisasContext *dc) /* Include the auto-generated decoder. */ #include "decode-insns.c.inc" -static TCGv cpu_sr; -static TCGv cpu_regs[32]; -static TCGv cpu_pc; -static TCGv jmp_pc; /* l.jr/l.jalr temp pc */ -static TCGv cpu_ppc; -static TCGv cpu_sr_f; /* bf/bnf, F flag taken */ -static TCGv cpu_sr_cy; /* carry (unsigned overflow) */ -static TCGv cpu_sr_ov; /* signed overflow */ -static TCGv cpu_lock_addr; -static TCGv cpu_lock_value; +static TCGv_i32 cpu_sr; +static TCGv_i32 cpu_regs[32]; +static TCGv_i32 cpu_pc; +static TCGv_i32 jmp_pc; /* l.jr/l.jalr temp pc */ +static TCGv_i32 cpu_ppc; +static TCGv_i32 cpu_sr_f; /* bf/bnf, F flag taken */ +static TCGv_i32 cpu_sr_cy; /* carry (unsigned overflow) */ +static TCGv_i32 cpu_sr_ov; /* signed overflow */ +static TCGv_i32 cpu_lock_addr; +static TCGv_i32 cpu_lock_value; static TCGv_i32 fpcsr; static TCGv_i64 cpu_mac; /* MACHI:MACLO */ static TCGv_i32 cpu_dflag; @@ -101,27 +101,27 @@ void openrisc_translate_init(void) }; int i; - cpu_sr = tcg_global_mem_new(tcg_env, + cpu_sr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUOpenRISCState, sr), "sr"); cpu_dflag = tcg_global_mem_new_i32(tcg_env, offsetof(CPUOpenRISCState, dflag), "dflag"); - cpu_pc = tcg_global_mem_new(tcg_env, + cpu_pc = tcg_global_mem_new_i32(tcg_env, offsetof(CPUOpenRISCState, pc), "pc"); - cpu_ppc = tcg_global_mem_new(tcg_env, + cpu_ppc = tcg_global_mem_new_i32(tcg_env, offsetof(CPUOpenRISCState, ppc), "ppc"); - jmp_pc = tcg_global_mem_new(tcg_env, + jmp_pc = tcg_global_mem_new_i32(tcg_env, offsetof(CPUOpenRISCState, jmp_pc), "jmp_pc"); - cpu_sr_f = tcg_global_mem_new(tcg_env, + cpu_sr_f = tcg_global_mem_new_i32(tcg_env, offsetof(CPUOpenRISCState, sr_f), "sr_f"); - cpu_sr_cy = tcg_global_mem_new(tcg_env, + cpu_sr_cy = tcg_global_mem_new_i32(tcg_env, offsetof(CPUOpenRISCState, sr_cy), "sr_cy"); - cpu_sr_ov = tcg_global_mem_new(tcg_env, + cpu_sr_ov = tcg_global_mem_new_i32(tcg_env, offsetof(CPUOpenRISCState, sr_ov), "sr_ov"); - cpu_lock_addr = tcg_global_mem_new(tcg_env, + cpu_lock_addr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUOpenRISCState, lock_addr), "lock_addr"); - cpu_lock_value = tcg_global_mem_new(tcg_env, + cpu_lock_value = tcg_global_mem_new_i32(tcg_env, offsetof(CPUOpenRISCState, lock_value), "lock_value"); fpcsr = tcg_global_mem_new_i32(tcg_env, @@ -131,7 +131,7 @@ void openrisc_translate_init(void) offsetof(CPUOpenRISCState, mac), "mac"); for (i = 0; i < 32; i++) { - cpu_regs[i] = tcg_global_mem_new(tcg_env, + cpu_regs[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUOpenRISCState, shadow_gpr[0][i]), regnames[i]); @@ -145,7 +145,7 @@ static void gen_exception(DisasContext *dc, unsigned int excp) static void gen_illegal_exception(DisasContext *dc) { - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); + tcg_gen_movi_i32(cpu_pc, dc->base.pc_next); gen_exception(dc, EXCP_ILLEGAL); dc->base.is_jmp = DISAS_NORETURN; } @@ -165,7 +165,7 @@ static bool check_of64a32s(DisasContext *dc) return dc->cpucfgr & CPUCFGR_OF64A32S; } -static TCGv cpu_R(DisasContext *dc, int reg) +static TCGv_i32 cpu_R(DisasContext *dc, int reg) { if (reg == 0) { return dc->R0; @@ -206,126 +206,133 @@ static void gen_ove_cyov(DisasContext *dc) } } -static void gen_add(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb) +static void gen_add(DisasContext *dc, TCGv_i32 dest, + TCGv_i32 srca, TCGv_i32 srcb) { - TCGv t0 = tcg_temp_new(); - TCGv res = tcg_temp_new(); + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 res = tcg_temp_new_i32(); - tcg_gen_add2_tl(res, cpu_sr_cy, srca, dc->zero, srcb, dc->zero); - tcg_gen_xor_tl(cpu_sr_ov, srca, srcb); - tcg_gen_xor_tl(t0, res, srcb); - tcg_gen_andc_tl(cpu_sr_ov, t0, cpu_sr_ov); + tcg_gen_add2_i32(res, cpu_sr_cy, srca, dc->zero, srcb, dc->zero); + tcg_gen_xor_i32(cpu_sr_ov, srca, srcb); + tcg_gen_xor_i32(t0, res, srcb); + tcg_gen_andc_i32(cpu_sr_ov, t0, cpu_sr_ov); - tcg_gen_mov_tl(dest, res); + tcg_gen_mov_i32(dest, res); gen_ove_cyov(dc); } -static void gen_addc(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb) +static void gen_addc(DisasContext *dc, TCGv_i32 dest, + TCGv_i32 srca, TCGv_i32 srcb) { - TCGv t0 = tcg_temp_new(); - TCGv res = tcg_temp_new(); + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 res = tcg_temp_new_i32(); - tcg_gen_addcio_tl(res, cpu_sr_cy, srca, srcb, cpu_sr_cy); - tcg_gen_xor_tl(cpu_sr_ov, srca, srcb); - tcg_gen_xor_tl(t0, res, srcb); - tcg_gen_andc_tl(cpu_sr_ov, t0, cpu_sr_ov); + tcg_gen_addcio_i32(res, cpu_sr_cy, srca, srcb, cpu_sr_cy); + tcg_gen_xor_i32(cpu_sr_ov, srca, srcb); + tcg_gen_xor_i32(t0, res, srcb); + tcg_gen_andc_i32(cpu_sr_ov, t0, cpu_sr_ov); - tcg_gen_mov_tl(dest, res); + tcg_gen_mov_i32(dest, res); gen_ove_cyov(dc); } -static void gen_sub(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb) +static void gen_sub(DisasContext *dc, TCGv_i32 dest, + TCGv_i32 srca, TCGv_i32 srcb) { - TCGv res = tcg_temp_new(); + TCGv_i32 res = tcg_temp_new_i32(); - tcg_gen_sub_tl(res, srca, srcb); - tcg_gen_xor_tl(cpu_sr_cy, srca, srcb); - tcg_gen_xor_tl(cpu_sr_ov, res, srcb); - tcg_gen_and_tl(cpu_sr_ov, cpu_sr_ov, cpu_sr_cy); - tcg_gen_setcond_tl(TCG_COND_LTU, cpu_sr_cy, srca, srcb); + tcg_gen_sub_i32(res, srca, srcb); + tcg_gen_xor_i32(cpu_sr_cy, srca, srcb); + tcg_gen_xor_i32(cpu_sr_ov, res, srcb); + tcg_gen_and_i32(cpu_sr_ov, cpu_sr_ov, cpu_sr_cy); + tcg_gen_setcond_i32(TCG_COND_LTU, cpu_sr_cy, srca, srcb); - tcg_gen_mov_tl(dest, res); + tcg_gen_mov_i32(dest, res); gen_ove_cyov(dc); } -static void gen_mul(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb) +static void gen_mul(DisasContext *dc, TCGv_i32 dest, + TCGv_i32 srca, TCGv_i32 srcb) { - TCGv t0 = tcg_temp_new(); + TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_muls2_tl(dest, cpu_sr_ov, srca, srcb); - tcg_gen_sari_tl(t0, dest, TARGET_LONG_BITS - 1); - tcg_gen_negsetcond_tl(TCG_COND_NE, cpu_sr_ov, cpu_sr_ov, t0); + tcg_gen_muls2_i32(dest, cpu_sr_ov, srca, srcb); + tcg_gen_sari_i32(t0, dest, TARGET_LONG_BITS - 1); + tcg_gen_negsetcond_i32(TCG_COND_NE, cpu_sr_ov, cpu_sr_ov, t0); gen_ove_ov(dc); } -static void gen_mulu(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb) +static void gen_mulu(DisasContext *dc, TCGv_i32 dest, + TCGv_i32 srca, TCGv_i32 srcb) { - tcg_gen_muls2_tl(dest, cpu_sr_cy, srca, srcb); - tcg_gen_setcondi_tl(TCG_COND_NE, cpu_sr_cy, cpu_sr_cy, 0); + tcg_gen_muls2_i32(dest, cpu_sr_cy, srca, srcb); + tcg_gen_setcondi_i32(TCG_COND_NE, cpu_sr_cy, cpu_sr_cy, 0); gen_ove_cy(dc); } -static void gen_div(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb) +static void gen_div(DisasContext *dc, TCGv_i32 dest, + TCGv_i32 srca, TCGv_i32 srcb) { - TCGv t0 = tcg_temp_new(); + TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_sr_ov, srcb, 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_ov, srcb, 0); /* The result of divide-by-zero is undefined. Suppress the host-side exception by dividing by 1. */ - tcg_gen_or_tl(t0, srcb, cpu_sr_ov); - tcg_gen_div_tl(dest, srca, t0); + tcg_gen_or_i32(t0, srcb, cpu_sr_ov); + tcg_gen_div_i32(dest, srca, t0); - tcg_gen_neg_tl(cpu_sr_ov, cpu_sr_ov); + tcg_gen_neg_i32(cpu_sr_ov, cpu_sr_ov); gen_ove_ov(dc); } -static void gen_divu(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb) +static void gen_divu(DisasContext *dc, TCGv_i32 dest, + TCGv_i32 srca, TCGv_i32 srcb) { - TCGv t0 = tcg_temp_new(); + TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_sr_cy, srcb, 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_cy, srcb, 0); /* The result of divide-by-zero is undefined. Suppress the host-side exception by dividing by 1. */ - tcg_gen_or_tl(t0, srcb, cpu_sr_cy); - tcg_gen_divu_tl(dest, srca, t0); + tcg_gen_or_i32(t0, srcb, cpu_sr_cy); + tcg_gen_divu_i32(dest, srca, t0); gen_ove_cy(dc); } -static void gen_muld(DisasContext *dc, TCGv srca, TCGv srcb) +static void gen_muld(DisasContext *dc, TCGv_i32 srca, TCGv_i32 srcb) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); - tcg_gen_ext_tl_i64(t1, srca); - tcg_gen_ext_tl_i64(t2, srcb); + tcg_gen_ext_i32_i64(t1, srca); + tcg_gen_ext_i32_i64(t2, srcb); tcg_gen_mul_i64(cpu_mac, t1, t2); - tcg_gen_movi_tl(cpu_sr_ov, 0); + tcg_gen_movi_i32(cpu_sr_ov, 0); } -static void gen_muldu(DisasContext *dc, TCGv srca, TCGv srcb) +static void gen_muldu(DisasContext *dc, TCGv_i32 srca, TCGv_i32 srcb) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); - tcg_gen_extu_tl_i64(t1, srca); - tcg_gen_extu_tl_i64(t2, srcb); + tcg_gen_extu_i32_i64(t1, srca); + tcg_gen_extu_i32_i64(t2, srcb); tcg_gen_mul_i64(cpu_mac, t1, t2); - tcg_gen_movi_tl(cpu_sr_cy, 0); + tcg_gen_movi_i32(cpu_sr_cy, 0); } -static void gen_mac(DisasContext *dc, TCGv srca, TCGv srcb) +static void gen_mac(DisasContext *dc, TCGv_i32 srca, TCGv_i32 srcb) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); - tcg_gen_ext_tl_i64(t1, srca); - tcg_gen_ext_tl_i64(t2, srcb); + tcg_gen_ext_i32_i64(t1, srca); + tcg_gen_ext_i32_i64(t2, srcb); tcg_gen_mul_i64(t1, t1, t2); /* Note that overflow is only computed during addition stage. */ @@ -339,13 +346,13 @@ static void gen_mac(DisasContext *dc, TCGv srca, TCGv srcb) gen_ove_ov(dc); } -static void gen_macu(DisasContext *dc, TCGv srca, TCGv srcb) +static void gen_macu(DisasContext *dc, TCGv_i32 srca, TCGv_i32 srcb) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); - tcg_gen_extu_tl_i64(t1, srca); - tcg_gen_extu_tl_i64(t2, srcb); + tcg_gen_extu_i32_i64(t1, srca); + tcg_gen_extu_i32_i64(t2, srcb); tcg_gen_mul_i64(t1, t1, t2); /* Note that overflow is only computed during addition stage. */ @@ -356,13 +363,13 @@ static void gen_macu(DisasContext *dc, TCGv srca, TCGv srcb) gen_ove_cy(dc); } -static void gen_msb(DisasContext *dc, TCGv srca, TCGv srcb) +static void gen_msb(DisasContext *dc, TCGv_i32 srca, TCGv_i32 srcb) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); - tcg_gen_ext_tl_i64(t1, srca); - tcg_gen_ext_tl_i64(t2, srcb); + tcg_gen_ext_i32_i64(t1, srca); + tcg_gen_ext_i32_i64(t2, srcb); tcg_gen_mul_i64(t1, t1, t2); /* Note that overflow is only computed during subtraction stage. */ @@ -380,13 +387,13 @@ static void gen_msb(DisasContext *dc, TCGv srca, TCGv srcb) gen_ove_ov(dc); } -static void gen_msbu(DisasContext *dc, TCGv srca, TCGv srcb) +static void gen_msbu(DisasContext *dc, TCGv_i32 srca, TCGv_i32 srcb) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); - tcg_gen_extu_tl_i64(t1, srca); - tcg_gen_extu_tl_i64(t2, srcb); + tcg_gen_extu_i32_i64(t1, srca); + tcg_gen_extu_i32_i64(t2, srcb); tcg_gen_mul_i64(t1, t1, t2); /* Note that overflow is only computed during subtraction stage. */ @@ -421,84 +428,84 @@ static bool trans_l_sub(DisasContext *dc, arg_dab *a) static bool trans_l_and(DisasContext *dc, arg_dab *a) { check_r0_write(dc, a->d); - tcg_gen_and_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b)); + tcg_gen_and_i32(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_or(DisasContext *dc, arg_dab *a) { check_r0_write(dc, a->d); - tcg_gen_or_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b)); + tcg_gen_or_i32(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_xor(DisasContext *dc, arg_dab *a) { check_r0_write(dc, a->d); - tcg_gen_xor_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b)); + tcg_gen_xor_i32(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_sll(DisasContext *dc, arg_dab *a) { check_r0_write(dc, a->d); - tcg_gen_shl_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b)); + tcg_gen_shl_i32(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_srl(DisasContext *dc, arg_dab *a) { check_r0_write(dc, a->d); - tcg_gen_shr_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b)); + tcg_gen_shr_i32(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_sra(DisasContext *dc, arg_dab *a) { check_r0_write(dc, a->d); - tcg_gen_sar_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b)); + tcg_gen_sar_i32(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_ror(DisasContext *dc, arg_dab *a) { check_r0_write(dc, a->d); - tcg_gen_rotr_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b)); + tcg_gen_rotr_i32(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_exths(DisasContext *dc, arg_da *a) { check_r0_write(dc, a->d); - tcg_gen_ext16s_tl(cpu_R(dc, a->d), cpu_R(dc, a->a)); + tcg_gen_ext16s_i32(cpu_R(dc, a->d), cpu_R(dc, a->a)); return true; } static bool trans_l_extbs(DisasContext *dc, arg_da *a) { check_r0_write(dc, a->d); - tcg_gen_ext8s_tl(cpu_R(dc, a->d), cpu_R(dc, a->a)); + tcg_gen_ext8s_i32(cpu_R(dc, a->d), cpu_R(dc, a->a)); return true; } static bool trans_l_exthz(DisasContext *dc, arg_da *a) { check_r0_write(dc, a->d); - tcg_gen_ext16u_tl(cpu_R(dc, a->d), cpu_R(dc, a->a)); + tcg_gen_ext16u_i32(cpu_R(dc, a->d), cpu_R(dc, a->a)); return true; } static bool trans_l_extbz(DisasContext *dc, arg_da *a) { check_r0_write(dc, a->d); - tcg_gen_ext8u_tl(cpu_R(dc, a->d), cpu_R(dc, a->a)); + tcg_gen_ext8u_i32(cpu_R(dc, a->d), cpu_R(dc, a->a)); return true; } static bool trans_l_cmov(DisasContext *dc, arg_dab *a) { check_r0_write(dc, a->d); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_R(dc, a->d), cpu_sr_f, dc->zero, + tcg_gen_movcond_i32(TCG_COND_NE, cpu_R(dc, a->d), cpu_sr_f, dc->zero, cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } @@ -506,16 +513,16 @@ static bool trans_l_cmov(DisasContext *dc, arg_dab *a) static bool trans_l_ff1(DisasContext *dc, arg_da *a) { check_r0_write(dc, a->d); - tcg_gen_ctzi_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), -1); - tcg_gen_addi_tl(cpu_R(dc, a->d), cpu_R(dc, a->d), 1); + tcg_gen_ctzi_i32(cpu_R(dc, a->d), cpu_R(dc, a->a), -1); + tcg_gen_addi_i32(cpu_R(dc, a->d), cpu_R(dc, a->d), 1); return true; } static bool trans_l_fl1(DisasContext *dc, arg_da *a) { check_r0_write(dc, a->d); - tcg_gen_clzi_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), TARGET_LONG_BITS); - tcg_gen_subfi_tl(cpu_R(dc, a->d), TARGET_LONG_BITS, cpu_R(dc, a->d)); + tcg_gen_clzi_i32(cpu_R(dc, a->d), cpu_R(dc, a->a), TARGET_LONG_BITS); + tcg_gen_subfi_i32(cpu_R(dc, a->d), TARGET_LONG_BITS, cpu_R(dc, a->d)); return true; } @@ -563,7 +570,7 @@ static bool trans_l_j(DisasContext *dc, arg_l_j *a) { vaddr tmp_pc = dc->base.pc_next + a->n * 4; - tcg_gen_movi_tl(jmp_pc, tmp_pc); + tcg_gen_movi_i32(jmp_pc, tmp_pc); dc->jmp_pc_imm = tmp_pc; dc->delayed_branch = 2; return true; @@ -574,10 +581,10 @@ static bool trans_l_jal(DisasContext *dc, arg_l_jal *a) vaddr tmp_pc = dc->base.pc_next + a->n * 4; vaddr ret_pc = dc->base.pc_next + 8; - tcg_gen_movi_tl(cpu_regs[9], ret_pc); + tcg_gen_movi_i32(cpu_regs[9], ret_pc); /* Optimize jal being used to load the PC for PIC. */ if (tmp_pc != ret_pc) { - tcg_gen_movi_tl(jmp_pc, tmp_pc); + tcg_gen_movi_i32(jmp_pc, tmp_pc); dc->jmp_pc_imm = tmp_pc; dc->delayed_branch = 2; } @@ -587,10 +594,10 @@ static bool trans_l_jal(DisasContext *dc, arg_l_jal *a) static void do_bf(DisasContext *dc, arg_l_bf *a, TCGCond cond) { vaddr tmp_pc = dc->base.pc_next + a->n * 4; - TCGv t_next = tcg_constant_tl(dc->base.pc_next + 8); - TCGv t_true = tcg_constant_tl(tmp_pc); + TCGv_i32 t_next = tcg_constant_i32(dc->base.pc_next + 8); + TCGv_i32 t_true = tcg_constant_i32(tmp_pc); - tcg_gen_movcond_tl(cond, jmp_pc, cpu_sr_f, dc->zero, t_true, t_next); + tcg_gen_movcond_i32(cond, jmp_pc, cpu_sr_f, dc->zero, t_true, t_next); dc->delayed_branch = 2; } @@ -608,43 +615,43 @@ static bool trans_l_bnf(DisasContext *dc, arg_l_bf *a) static bool trans_l_jr(DisasContext *dc, arg_l_jr *a) { - tcg_gen_mov_tl(jmp_pc, cpu_R(dc, a->b)); + tcg_gen_mov_i32(jmp_pc, cpu_R(dc, a->b)); dc->delayed_branch = 2; return true; } static bool trans_l_jalr(DisasContext *dc, arg_l_jalr *a) { - tcg_gen_mov_tl(jmp_pc, cpu_R(dc, a->b)); - tcg_gen_movi_tl(cpu_regs[9], dc->base.pc_next + 8); + tcg_gen_mov_i32(jmp_pc, cpu_R(dc, a->b)); + tcg_gen_movi_i32(cpu_regs[9], dc->base.pc_next + 8); dc->delayed_branch = 2; return true; } static bool trans_l_lwa(DisasContext *dc, arg_load *a) { - TCGv ea; + TCGv_i32 ea; check_r0_write(dc, a->d); - ea = tcg_temp_new(); - tcg_gen_addi_tl(ea, cpu_R(dc, a->a), a->i); - tcg_gen_qemu_ld_tl(cpu_R(dc, a->d), ea, dc->mem_idx, - mo_endian(dc) | MO_UL); - tcg_gen_mov_tl(cpu_lock_addr, ea); - tcg_gen_mov_tl(cpu_lock_value, cpu_R(dc, a->d)); + ea = tcg_temp_new_i32(); + tcg_gen_addi_i32(ea, cpu_R(dc, a->a), a->i); + tcg_gen_qemu_ld_i32(cpu_R(dc, a->d), ea, dc->mem_idx, + mo_endian(dc) | MO_UL); + tcg_gen_mov_i32(cpu_lock_addr, ea); + tcg_gen_mov_i32(cpu_lock_value, cpu_R(dc, a->d)); return true; } static void do_load(DisasContext *dc, arg_load *a, MemOp mop) { - TCGv ea; + TCGv_i32 ea; mop |= mo_endian(dc); check_r0_write(dc, a->d); - ea = tcg_temp_new(); - tcg_gen_addi_tl(ea, cpu_R(dc, a->a), a->i); - tcg_gen_qemu_ld_tl(cpu_R(dc, a->d), ea, dc->mem_idx, mop); + ea = tcg_temp_new_i32(); + tcg_gen_addi_i32(ea, cpu_R(dc, a->a), a->i); + tcg_gen_qemu_ld_i32(cpu_R(dc, a->d), ea, dc->mem_idx, mop); } static bool trans_l_lwz(DisasContext *dc, arg_load *a) @@ -685,40 +692,40 @@ static bool trans_l_lhs(DisasContext *dc, arg_load *a) static bool trans_l_swa(DisasContext *dc, arg_store *a) { - TCGv ea, val; + TCGv_i32 ea, val; TCGLabel *lab_fail, *lab_done; - ea = tcg_temp_new(); - tcg_gen_addi_tl(ea, cpu_R(dc, a->a), a->i); + ea = tcg_temp_new_i32(); + tcg_gen_addi_i32(ea, cpu_R(dc, a->a), a->i); lab_fail = gen_new_label(); lab_done = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_NE, ea, cpu_lock_addr, lab_fail); + tcg_gen_brcond_i32(TCG_COND_NE, ea, cpu_lock_addr, lab_fail); - val = tcg_temp_new(); - tcg_gen_atomic_cmpxchg_tl(val, cpu_lock_addr, cpu_lock_value, + val = tcg_temp_new_i32(); + tcg_gen_atomic_cmpxchg_i32(val, cpu_lock_addr, cpu_lock_value, cpu_R(dc, a->b), dc->mem_idx, mo_endian(dc) | MO_UL); - tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, val, cpu_lock_value); + tcg_gen_setcond_i32(TCG_COND_EQ, cpu_sr_f, val, cpu_lock_value); tcg_gen_br(lab_done); gen_set_label(lab_fail); - tcg_gen_movi_tl(cpu_sr_f, 0); + tcg_gen_movi_i32(cpu_sr_f, 0); gen_set_label(lab_done); - tcg_gen_movi_tl(cpu_lock_addr, -1); + tcg_gen_movi_i32(cpu_lock_addr, -1); return true; } static void do_store(DisasContext *dc, arg_store *a, MemOp mop) { - TCGv t0 = tcg_temp_new(); + TCGv_i32 t0 = tcg_temp_new_i32(); mop |= mo_endian(dc); - tcg_gen_addi_tl(t0, cpu_R(dc, a->a), a->i); - tcg_gen_qemu_st_tl(cpu_R(dc, a->b), t0, dc->mem_idx, mop); + tcg_gen_addi_i32(t0, cpu_R(dc, a->a), a->i); + tcg_gen_qemu_st_i32(cpu_R(dc, a->b), t0, dc->mem_idx, mop); } static bool trans_l_sw(DisasContext *dc, arg_store *a) @@ -760,75 +767,75 @@ static bool trans_l_adrp(DisasContext *dc, arg_l_adrp *a) static bool trans_l_addi(DisasContext *dc, arg_rri *a) { check_r0_write(dc, a->d); - gen_add(dc, cpu_R(dc, a->d), cpu_R(dc, a->a), tcg_constant_tl(a->i)); + gen_add(dc, cpu_R(dc, a->d), cpu_R(dc, a->a), tcg_constant_i32(a->i)); return true; } static bool trans_l_addic(DisasContext *dc, arg_rri *a) { check_r0_write(dc, a->d); - gen_addc(dc, cpu_R(dc, a->d), cpu_R(dc, a->a), tcg_constant_tl(a->i)); + gen_addc(dc, cpu_R(dc, a->d), cpu_R(dc, a->a), tcg_constant_i32(a->i)); return true; } static bool trans_l_muli(DisasContext *dc, arg_rri *a) { check_r0_write(dc, a->d); - gen_mul(dc, cpu_R(dc, a->d), cpu_R(dc, a->a), tcg_constant_tl(a->i)); + gen_mul(dc, cpu_R(dc, a->d), cpu_R(dc, a->a), tcg_constant_i32(a->i)); return true; } static bool trans_l_maci(DisasContext *dc, arg_l_maci *a) { - gen_mac(dc, cpu_R(dc, a->a), tcg_constant_tl(a->i)); + gen_mac(dc, cpu_R(dc, a->a), tcg_constant_i32(a->i)); return true; } static bool trans_l_andi(DisasContext *dc, arg_rrk *a) { check_r0_write(dc, a->d); - tcg_gen_andi_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), a->k); + tcg_gen_andi_i32(cpu_R(dc, a->d), cpu_R(dc, a->a), a->k); return true; } static bool trans_l_ori(DisasContext *dc, arg_rrk *a) { check_r0_write(dc, a->d); - tcg_gen_ori_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), a->k); + tcg_gen_ori_i32(cpu_R(dc, a->d), cpu_R(dc, a->a), a->k); return true; } static bool trans_l_xori(DisasContext *dc, arg_rri *a) { check_r0_write(dc, a->d); - tcg_gen_xori_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), a->i); + tcg_gen_xori_i32(cpu_R(dc, a->d), cpu_R(dc, a->a), a->i); return true; } static bool trans_l_mfspr(DisasContext *dc, arg_l_mfspr *a) { - TCGv spr = tcg_temp_new(); + TCGv_i32 spr = tcg_temp_new_i32(); check_r0_write(dc, a->d); if (translator_io_start(&dc->base)) { if (dc->delayed_branch) { - tcg_gen_mov_tl(cpu_pc, jmp_pc); - tcg_gen_discard_tl(jmp_pc); + tcg_gen_mov_i32(cpu_pc, jmp_pc); + tcg_gen_discard_i32(jmp_pc); } else { - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next + 4); + tcg_gen_movi_i32(cpu_pc, dc->base.pc_next + 4); } dc->base.is_jmp = DISAS_EXIT; } - tcg_gen_ori_tl(spr, cpu_R(dc, a->a), a->k); + tcg_gen_ori_i32(spr, cpu_R(dc, a->a), a->k); gen_helper_mfspr(cpu_R(dc, a->d), tcg_env, cpu_R(dc, a->d), spr); return true; } static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a) { - TCGv spr = tcg_temp_new(); + TCGv_i32 spr = tcg_temp_new_i32(); translator_io_start(&dc->base); @@ -839,14 +846,14 @@ static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a) * of the cpu state first, allowing it to be overwritten. */ if (dc->delayed_branch) { - tcg_gen_mov_tl(cpu_pc, jmp_pc); - tcg_gen_discard_tl(jmp_pc); + tcg_gen_mov_i32(cpu_pc, jmp_pc); + tcg_gen_discard_i32(jmp_pc); } else { - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next + 4); + tcg_gen_movi_i32(cpu_pc, dc->base.pc_next + 4); } dc->base.is_jmp = DISAS_EXIT; - tcg_gen_ori_tl(spr, cpu_R(dc, a->a), a->k); + tcg_gen_ori_i32(spr, cpu_R(dc, a->a), a->k); gen_helper_mtspr(tcg_env, spr, cpu_R(dc, a->b)); return true; } @@ -878,7 +885,7 @@ static bool trans_l_msbu(DisasContext *dc, arg_ab *a) static bool trans_l_slli(DisasContext *dc, arg_dal *a) { check_r0_write(dc, a->d); - tcg_gen_shli_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), + tcg_gen_shli_i32(cpu_R(dc, a->d), cpu_R(dc, a->a), a->l & (TARGET_LONG_BITS - 1)); return true; } @@ -886,7 +893,7 @@ static bool trans_l_slli(DisasContext *dc, arg_dal *a) static bool trans_l_srli(DisasContext *dc, arg_dal *a) { check_r0_write(dc, a->d); - tcg_gen_shri_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), + tcg_gen_shri_i32(cpu_R(dc, a->d), cpu_R(dc, a->a), a->l & (TARGET_LONG_BITS - 1)); return true; } @@ -894,7 +901,7 @@ static bool trans_l_srli(DisasContext *dc, arg_dal *a) static bool trans_l_srai(DisasContext *dc, arg_dal *a) { check_r0_write(dc, a->d); - tcg_gen_sari_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), + tcg_gen_sari_i32(cpu_R(dc, a->d), cpu_R(dc, a->a), a->l & (TARGET_LONG_BITS - 1)); return true; } @@ -902,7 +909,7 @@ static bool trans_l_srai(DisasContext *dc, arg_dal *a) static bool trans_l_rori(DisasContext *dc, arg_dal *a) { check_r0_write(dc, a->d); - tcg_gen_rotri_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), + tcg_gen_rotri_i32(cpu_R(dc, a->d), cpu_R(dc, a->a), a->l & (TARGET_LONG_BITS - 1)); return true; } @@ -910,7 +917,7 @@ static bool trans_l_rori(DisasContext *dc, arg_dal *a) static bool trans_l_movhi(DisasContext *dc, arg_l_movhi *a) { check_r0_write(dc, a->d); - tcg_gen_movi_tl(cpu_R(dc, a->d), a->k << 16); + tcg_gen_movi_i32(cpu_R(dc, a->d), a->k << 16); return true; } @@ -924,137 +931,137 @@ static bool trans_l_macrc(DisasContext *dc, arg_l_macrc *a) static bool trans_l_sfeq(DisasContext *dc, arg_ab *a) { - tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, + tcg_gen_setcond_i32(TCG_COND_EQ, cpu_sr_f, cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_sfne(DisasContext *dc, arg_ab *a) { - tcg_gen_setcond_tl(TCG_COND_NE, cpu_sr_f, + tcg_gen_setcond_i32(TCG_COND_NE, cpu_sr_f, cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_sfgtu(DisasContext *dc, arg_ab *a) { - tcg_gen_setcond_tl(TCG_COND_GTU, cpu_sr_f, + tcg_gen_setcond_i32(TCG_COND_GTU, cpu_sr_f, cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_sfgeu(DisasContext *dc, arg_ab *a) { - tcg_gen_setcond_tl(TCG_COND_GEU, cpu_sr_f, + tcg_gen_setcond_i32(TCG_COND_GEU, cpu_sr_f, cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_sfltu(DisasContext *dc, arg_ab *a) { - tcg_gen_setcond_tl(TCG_COND_LTU, cpu_sr_f, + tcg_gen_setcond_i32(TCG_COND_LTU, cpu_sr_f, cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_sfleu(DisasContext *dc, arg_ab *a) { - tcg_gen_setcond_tl(TCG_COND_LEU, cpu_sr_f, + tcg_gen_setcond_i32(TCG_COND_LEU, cpu_sr_f, cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_sfgts(DisasContext *dc, arg_ab *a) { - tcg_gen_setcond_tl(TCG_COND_GT, cpu_sr_f, + tcg_gen_setcond_i32(TCG_COND_GT, cpu_sr_f, cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_sfges(DisasContext *dc, arg_ab *a) { - tcg_gen_setcond_tl(TCG_COND_GE, cpu_sr_f, + tcg_gen_setcond_i32(TCG_COND_GE, cpu_sr_f, cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_sflts(DisasContext *dc, arg_ab *a) { - tcg_gen_setcond_tl(TCG_COND_LT, cpu_sr_f, + tcg_gen_setcond_i32(TCG_COND_LT, cpu_sr_f, cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_sfles(DisasContext *dc, arg_ab *a) { - tcg_gen_setcond_tl(TCG_COND_LE, + tcg_gen_setcond_i32(TCG_COND_LE, cpu_sr_f, cpu_R(dc, a->a), cpu_R(dc, a->b)); return true; } static bool trans_l_sfeqi(DisasContext *dc, arg_ai *a) { - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_sr_f, cpu_R(dc, a->a), a->i); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_f, cpu_R(dc, a->a), a->i); return true; } static bool trans_l_sfnei(DisasContext *dc, arg_ai *a) { - tcg_gen_setcondi_tl(TCG_COND_NE, cpu_sr_f, cpu_R(dc, a->a), a->i); + tcg_gen_setcondi_i32(TCG_COND_NE, cpu_sr_f, cpu_R(dc, a->a), a->i); return true; } static bool trans_l_sfgtui(DisasContext *dc, arg_ai *a) { - tcg_gen_setcondi_tl(TCG_COND_GTU, cpu_sr_f, cpu_R(dc, a->a), a->i); + tcg_gen_setcondi_i32(TCG_COND_GTU, cpu_sr_f, cpu_R(dc, a->a), a->i); return true; } static bool trans_l_sfgeui(DisasContext *dc, arg_ai *a) { - tcg_gen_setcondi_tl(TCG_COND_GEU, cpu_sr_f, cpu_R(dc, a->a), a->i); + tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_sr_f, cpu_R(dc, a->a), a->i); return true; } static bool trans_l_sfltui(DisasContext *dc, arg_ai *a) { - tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_sr_f, cpu_R(dc, a->a), a->i); + tcg_gen_setcondi_i32(TCG_COND_LTU, cpu_sr_f, cpu_R(dc, a->a), a->i); return true; } static bool trans_l_sfleui(DisasContext *dc, arg_ai *a) { - tcg_gen_setcondi_tl(TCG_COND_LEU, cpu_sr_f, cpu_R(dc, a->a), a->i); + tcg_gen_setcondi_i32(TCG_COND_LEU, cpu_sr_f, cpu_R(dc, a->a), a->i); return true; } static bool trans_l_sfgtsi(DisasContext *dc, arg_ai *a) { - tcg_gen_setcondi_tl(TCG_COND_GT, cpu_sr_f, cpu_R(dc, a->a), a->i); + tcg_gen_setcondi_i32(TCG_COND_GT, cpu_sr_f, cpu_R(dc, a->a), a->i); return true; } static bool trans_l_sfgesi(DisasContext *dc, arg_ai *a) { - tcg_gen_setcondi_tl(TCG_COND_GE, cpu_sr_f, cpu_R(dc, a->a), a->i); + tcg_gen_setcondi_i32(TCG_COND_GE, cpu_sr_f, cpu_R(dc, a->a), a->i); return true; } static bool trans_l_sfltsi(DisasContext *dc, arg_ai *a) { - tcg_gen_setcondi_tl(TCG_COND_LT, cpu_sr_f, cpu_R(dc, a->a), a->i); + tcg_gen_setcondi_i32(TCG_COND_LT, cpu_sr_f, cpu_R(dc, a->a), a->i); return true; } static bool trans_l_sflesi(DisasContext *dc, arg_ai *a) { - tcg_gen_setcondi_tl(TCG_COND_LE, cpu_sr_f, cpu_R(dc, a->a), a->i); + tcg_gen_setcondi_i32(TCG_COND_LE, cpu_sr_f, cpu_R(dc, a->a), a->i); return true; } static bool trans_l_sys(DisasContext *dc, arg_l_sys *a) { - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); + tcg_gen_movi_i32(cpu_pc, dc->base.pc_next); gen_exception(dc, EXCP_SYSCALL); dc->base.is_jmp = DISAS_NORETURN; return true; @@ -1062,7 +1069,7 @@ static bool trans_l_sys(DisasContext *dc, arg_l_sys *a) static bool trans_l_trap(DisasContext *dc, arg_l_trap *a) { - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); + tcg_gen_movi_i32(cpu_pc, dc->base.pc_next); gen_exception(dc, EXCP_TRAP); dc->base.is_jmp = DISAS_NORETURN; return true; @@ -1096,7 +1103,7 @@ static bool trans_l_rfe(DisasContext *dc, arg_l_rfe *a) } static bool do_fp2(DisasContext *dc, arg_da *a, - void (*fn)(TCGv, TCGv_env, TCGv)) + void (*fn)(TCGv_i32, TCGv_env, TCGv_i32)) { if (!check_of32s(dc)) { return false; @@ -1108,7 +1115,7 @@ static bool do_fp2(DisasContext *dc, arg_da *a, } static bool do_fp3(DisasContext *dc, arg_dab *a, - void (*fn)(TCGv, TCGv_env, TCGv, TCGv)) + void (*fn)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32)) { if (!check_of32s(dc)) { return false; @@ -1120,7 +1127,7 @@ static bool do_fp3(DisasContext *dc, arg_dab *a, } static bool do_fpcmp(DisasContext *dc, arg_ab *a, - void (*fn)(TCGv, TCGv_env, TCGv, TCGv), + void (*fn)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32), bool inv, bool swap) { if (!check_of32s(dc)) { @@ -1132,7 +1139,7 @@ static bool do_fpcmp(DisasContext *dc, arg_ab *a, fn(cpu_sr_f, tcg_env, cpu_R(dc, a->a), cpu_R(dc, a->b)); } if (inv) { - tcg_gen_xori_tl(cpu_sr_f, cpu_sr_f, 1); + tcg_gen_xori_i32(cpu_sr_f, cpu_sr_f, 1); } gen_helper_update_fpcsr(tcg_env); return true; @@ -1325,7 +1332,7 @@ static bool do_dp2(DisasContext *dc, arg_da_pair *a, } static bool do_dpcmp(DisasContext *dc, arg_ab_pair *a, - void (*fn)(TCGv, TCGv_env, TCGv_i64, TCGv_i64), + void (*fn)(TCGv_i32, TCGv_env, TCGv_i64, TCGv_i64), bool inv, bool swap) { TCGv_i64 t0, t1; @@ -1347,7 +1354,7 @@ static bool do_dpcmp(DisasContext *dc, arg_ab_pair *a, } if (inv) { - tcg_gen_xori_tl(cpu_sr_f, cpu_sr_f, 1); + tcg_gen_xori_i32(cpu_sr_f, cpu_sr_f, 1); } gen_helper_update_fpcsr(tcg_env); return true; @@ -1532,7 +1539,7 @@ static void openrisc_tr_tb_start(DisasContextBase *db, CPUState *cs) /* Allow the TCG optimizer to see that R0 == 0, when it's true, which is the common case. */ - dc->zero = tcg_constant_tl(0); + dc->zero = tcg_constant_i32(0); if (dc->tb_flags & TB_FLAGS_R0_0) { dc->R0 = dc->zero; } else { @@ -1588,32 +1595,32 @@ static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) /* For DISAS_TOO_MANY, jump to the next insn. */ jmp_dest = dc->base.pc_next; - tcg_gen_movi_tl(cpu_ppc, jmp_dest - 4); + tcg_gen_movi_i32(cpu_ppc, jmp_dest - 4); switch (dc->base.is_jmp) { case DISAS_JUMP: jmp_dest = dc->jmp_pc_imm; if (jmp_dest == -1) { /* The jump destination is indirect/computed; use jmp_pc. */ - tcg_gen_mov_tl(cpu_pc, jmp_pc); - tcg_gen_discard_tl(jmp_pc); + tcg_gen_mov_i32(cpu_pc, jmp_pc); + tcg_gen_discard_i32(jmp_pc); tcg_gen_lookup_and_goto_ptr(); break; } /* The jump destination is direct; use jmp_pc_imm. However, we will have stored into jmp_pc as well; we know now that it wasn't needed. */ - tcg_gen_discard_tl(jmp_pc); + tcg_gen_discard_i32(jmp_pc); /* fallthru */ case DISAS_TOO_MANY: if (translator_use_goto_tb(&dc->base, jmp_dest)) { tcg_gen_goto_tb(0); - tcg_gen_movi_tl(cpu_pc, jmp_dest); + tcg_gen_movi_i32(cpu_pc, jmp_dest); tcg_gen_exit_tb(dc->base.tb, 0); break; } - tcg_gen_movi_tl(cpu_pc, jmp_dest); + tcg_gen_movi_i32(cpu_pc, jmp_dest); tcg_gen_lookup_and_goto_ptr(); break; From b4c93258a9d4e567d6e74d47cfcbbac0ff542bfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Oct 2025 10:53:07 +0200 Subject: [PATCH 1636/1794] target/riscv/kvm: Replace legacy cpu_physical_memory_read/write() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit b7ecba0f6f6 ("docs/devel/loads-stores.rst: Document our various load and store APIs") mentioned cpu_physical_memory_*() methods are legacy, the replacement being address_space_*(). Since the SBI DBCN is handled within a vCPU context, use its default address space. Replace using the address space API. As with the previous implementation, ignore whether the memory accesses succeeded or failed. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Message-Id: <20251002145742.75624-4-philmd@linaro.org> --- target/riscv/kvm/kvm-cpu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 75ca3fb9fd9a4..0dd0d59d41ad6 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -36,6 +36,7 @@ #include "hw/pci/pci.h" #include "exec/memattrs.h" #include "system/address-spaces.h" +#include "system/memory.h" #include "hw/boards.h" #include "hw/irq.h" #include "hw/intc/riscv_imsic.h" @@ -1564,6 +1565,7 @@ bool kvm_arch_stop_on_emulation_error(CPUState *cs) static void kvm_riscv_handle_sbi_dbcn(CPUState *cs, struct kvm_run *run) { + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; g_autofree uint8_t *buf = NULL; RISCVCPU *cpu = RISCV_CPU(cs); target_ulong num_bytes; @@ -1602,9 +1604,9 @@ static void kvm_riscv_handle_sbi_dbcn(CPUState *cs, struct kvm_run *run) exit(1); } - cpu_physical_memory_write(addr, buf, ret); + address_space_write(cs->as, addr, attrs, buf, ret); } else { - cpu_physical_memory_read(addr, buf, num_bytes); + address_space_read(cs->as, addr, attrs, buf, num_bytes); ret = qemu_chr_fe_write_all(serial_hd(0)->be, buf, num_bytes); if (ret < 0) { From d936261844f68a07a3c51db08278eda273457d94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 2 Oct 2025 05:09:23 +0200 Subject: [PATCH 1637/1794] target/riscv/monitor: Replace legacy cpu_physical_memory_read() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit b7ecba0f6f6 ("docs/devel/loads-stores.rst: Document our various load and store APIs") mentioned cpu_physical_memory_*() methods are legacy, the replacement being address_space_*(). Propagate the address space to walk_pte(), then replace the cpu_physical_memory_read() by address_space_read(). Since the monitor command are run with a vCPU context, use its default address space. As with the previous implementation, ignore whether the memory read succeeded or failed. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Message-Id: <20251002145742.75624-5-philmd@linaro.org> --- target/riscv/monitor.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/target/riscv/monitor.c b/target/riscv/monitor.c index 100005ea4e99f..8a77476db93dd 100644 --- a/target/riscv/monitor.c +++ b/target/riscv/monitor.c @@ -23,6 +23,7 @@ #include "cpu_bits.h" #include "monitor/monitor.h" #include "monitor/hmp-target.h" +#include "system/memory.h" #ifdef TARGET_RISCV64 #define PTE_HEADER_FIELDS "vaddr paddr "\ @@ -77,11 +78,13 @@ static void print_pte(Monitor *mon, int va_bits, target_ulong vaddr, attr & PTE_D ? 'd' : '-'); } -static void walk_pte(Monitor *mon, hwaddr base, target_ulong start, +static void walk_pte(Monitor *mon, AddressSpace *as, + hwaddr base, target_ulong start, int level, int ptidxbits, int ptesize, int va_bits, target_ulong *vbase, hwaddr *pbase, hwaddr *last_paddr, target_ulong *last_size, int *last_attr) { + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; hwaddr pte_addr; hwaddr paddr; target_ulong last_start = -1; @@ -100,7 +103,7 @@ static void walk_pte(Monitor *mon, hwaddr base, target_ulong start, for (idx = 0; idx < (1UL << ptidxbits); idx++) { pte_addr = base + idx * ptesize; - cpu_physical_memory_read(pte_addr, &pte, ptesize); + address_space_read(as, pte_addr, attrs, &pte, ptesize); paddr = (hwaddr)(pte >> PTE_PPN_SHIFT) << PGSHIFT; attr = pte & 0xff; @@ -132,7 +135,7 @@ static void walk_pte(Monitor *mon, hwaddr base, target_ulong start, *last_size = pgsize; } else { /* pointer to the next level of the page table */ - walk_pte(mon, paddr, start, level - 1, ptidxbits, ptesize, + walk_pte(mon, as, paddr, start, level - 1, ptidxbits, ptesize, va_bits, vbase, pbase, last_paddr, last_size, last_attr); } @@ -145,6 +148,7 @@ static void walk_pte(Monitor *mon, hwaddr base, target_ulong start, static void mem_info_svxx(Monitor *mon, CPUArchState *env) { + AddressSpace *as = env_cpu(env)->as; int levels, ptidxbits, ptesize, vm, va_bits; hwaddr base; target_ulong vbase; @@ -199,7 +203,7 @@ static void mem_info_svxx(Monitor *mon, CPUArchState *env) last_attr = 0; /* walk page tables, starting from address 0 */ - walk_pte(mon, base, 0, levels - 1, ptidxbits, ptesize, va_bits, + walk_pte(mon, as, base, 0, levels - 1, ptidxbits, ptesize, va_bits, &vbase, &pbase, &last_paddr, &last_size, &last_attr); /* don't forget the last one */ From 89e1cd7363e0a066a6b7a6059998efa3a89cc1b9 Mon Sep 17 00:00:00 2001 From: Anton Johansson Date: Wed, 1 Oct 2025 09:32:34 +0200 Subject: [PATCH 1638/1794] target/riscv: Use 32 bits for misa extensions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit uint32_t is already in use in most places storing misa extensions such as CPUArchState::misa_exts, RISCVCPUProfile::misa_exts, RISCVImpliedExtsRule::implied_misa_exts. Additionally. the field is already migrated as uint32_t. Signed-off-by: Anton Johansson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Pierrick Bouvier Message-ID: <20251001073306.28573-2-anjo@rev.ng> Signed-off-by: Philippe Mathieu-Daudé --- target/riscv/cpu.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 2c2266415ec87..4c13012442d98 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -50,7 +50,7 @@ typedef struct CPUArchState CPURISCVState; */ #define RISCV_UW2_ALWAYS_STORE_AMO 1 -#define RV(x) ((target_ulong)1 << (x - 'A')) +#define RV(x) BIT(x - 'A') /* * Update misa_bits[], misa_ext_info_arr[] and misa_ext_cfgs[] @@ -582,7 +582,7 @@ struct RISCVCPUClass { RISCVCPUDef *def; }; -static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext) +static inline int riscv_has_ext(CPURISCVState *env, uint32_t ext) { return (env->misa_ext & ext) != 0; } From 886b0cea411691d6fd75fae97ab40ff833fdb9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Oct 2025 14:46:36 +0200 Subject: [PATCH 1639/1794] target/riscv: Replace HOST_BIG_ENDIAN #ifdef with if() check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace preprocessor-time #ifdef with a compile-time check to ensure all code paths are built and tested. This reduces build-time configuration complexity and simplifies code maintainability. No functional change intended. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-Id: <20251010134226.72221-14-philmd@linaro.org> --- target/riscv/insn_trans/trans_rvv.c.inc | 16 ++++++------- target/riscv/vector_helper.c | 32 ++++++++++++------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index f4b5460340ec0..2a487179f6388 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -3351,19 +3351,19 @@ static void load_element(TCGv_i64 dest, TCGv_ptr base, /* offset of the idx element with base register r */ static uint32_t endian_ofs(DisasContext *s, int r, int idx) { -#if HOST_BIG_ENDIAN - return vreg_ofs(s, r) + ((idx ^ (7 >> s->sew)) << s->sew); -#else - return vreg_ofs(s, r) + (idx << s->sew); -#endif + if (HOST_BIG_ENDIAN) { + return vreg_ofs(s, r) + ((idx ^ (7 >> s->sew)) << s->sew); + } else { + return vreg_ofs(s, r) + (idx << s->sew); + } } /* adjust the index according to the endian */ static void endian_adjust(TCGv_i32 ofs, int sew) { -#if HOST_BIG_ENDIAN - tcg_gen_xori_i32(ofs, ofs, 7 >> sew); -#endif + if (HOST_BIG_ENDIAN) { + tcg_gen_xori_i32(ofs, ofs, 7 >> sew); + } } /* Load idx >= VLMAX ? 0 : vreg[idx] */ diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 41ea223106794..2de3358ee8640 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -235,26 +235,26 @@ vext_continuous_ldst_host(CPURISCVState *env, vext_ldst_elem_fn_host *ldst_host, void *vd, uint32_t evl, uint32_t reg_start, void *host, uint32_t esz, bool is_load) { -#if HOST_BIG_ENDIAN - for (; reg_start < evl; reg_start++, host += esz) { - ldst_host(vd, reg_start, host); - } -#else - if (esz == 1) { - uint32_t byte_offset = reg_start * esz; - uint32_t size = (evl - reg_start) * esz; - - if (is_load) { - memcpy(vd + byte_offset, host, size); - } else { - memcpy(host, vd + byte_offset, size); - } - } else { + if (HOST_BIG_ENDIAN) { for (; reg_start < evl; reg_start++, host += esz) { ldst_host(vd, reg_start, host); } + } else { + if (esz == 1) { + uint32_t byte_offset = reg_start * esz; + uint32_t size = (evl - reg_start) * esz; + + if (is_load) { + memcpy(vd + byte_offset, host, size); + } else { + memcpy(host, vd + byte_offset, size); + } + } else { + for (; reg_start < evl; reg_start++, host += esz) { + ldst_host(vd, reg_start, host); + } + } } -#endif } static void vext_set_tail_elems_1s(target_ulong vl, void *vd, From 2982b948a9faa8edd0c8a9366fab77cc1094e0a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 16:58:54 +0200 Subject: [PATCH 1640/1794] target/rx: Replace target_ulong -> vaddr for translator API uses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit b1c09220b4c ("accel/tcg: Replace target_ulong with vaddr in translator_*()") the API takes vaddr argument, not target_ulong. Update the 2 callers. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251009151607.26278-2-philmd@linaro.org> --- target/rx/translate.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/rx/translate.c b/target/rx/translate.c index 5fc589c706b60..1c911cd9a67f5 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -85,7 +85,7 @@ static uint32_t decode_load_bytes(DisasContext *ctx, uint32_t insn, static uint32_t li(DisasContext *ctx, int sz) { - target_ulong addr; + vaddr addr; uint32_t tmp; CPURXState *env = ctx->env; addr = ctx->base.pc_next; @@ -147,8 +147,7 @@ void rx_cpu_dump_state(CPUState *cs, FILE *f, int flags) } } -static void gen_goto_tb(DisasContext *dc, unsigned tb_slot_idx, - target_ulong dest) +static void gen_goto_tb(DisasContext *dc, unsigned tb_slot_idx, vaddr dest) { if (translator_use_goto_tb(&dc->base, dest)) { tcg_gen_goto_tb(tb_slot_idx); From 0f4af4e2672040e505c7b337d48ec7cf2c1687bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 16:54:35 +0200 Subject: [PATCH 1641/1794] target/rx: Use MemOp type in gen_ld[u]() and gen_st() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The @size argument is of MemOp type. All callers respect that. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251009151607.26278-3-philmd@linaro.org> --- target/rx/translate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/rx/translate.c b/target/rx/translate.c index 1c911cd9a67f5..744edd56afce0 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -161,19 +161,19 @@ static void gen_goto_tb(DisasContext *dc, unsigned tb_slot_idx, vaddr dest) } /* generic load wrapper */ -static inline void rx_gen_ld(unsigned int size, TCGv reg, TCGv mem) +static inline void rx_gen_ld(MemOp size, TCGv reg, TCGv mem) { tcg_gen_qemu_ld_i32(reg, mem, 0, size | MO_SIGN | MO_TE); } /* unsigned load wrapper */ -static inline void rx_gen_ldu(unsigned int size, TCGv reg, TCGv mem) +static inline void rx_gen_ldu(MemOp size, TCGv reg, TCGv mem) { tcg_gen_qemu_ld_i32(reg, mem, 0, size | MO_TE); } /* generic store wrapper */ -static inline void rx_gen_st(unsigned int size, TCGv reg, TCGv mem) +static inline void rx_gen_st(MemOp size, TCGv reg, TCGv mem) { tcg_gen_qemu_st_i32(reg, mem, 0, size | MO_TE); } From 2062fa663c00927b4bfe63bd1d3f4614af3f0b9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 16:52:39 +0200 Subject: [PATCH 1642/1794] target/rx: Propagate DisasContext to generated helpers 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: <20251009151607.26278-4-philmd@linaro.org> --- target/rx/translate.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/target/rx/translate.c b/target/rx/translate.c index 744edd56afce0..b314d94b0189b 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -1894,7 +1894,7 @@ static bool trans_ITOF(DisasContext *ctx, arg_ITOF * a) return true; } -static void rx_bsetm(TCGv mem, TCGv mask) +static void rx_bsetm(DisasContext *ctx, TCGv mem, TCGv mask) { TCGv val; val = tcg_temp_new(); @@ -1903,7 +1903,7 @@ static void rx_bsetm(TCGv mem, TCGv mask) rx_gen_st(MO_8, val, mem); } -static void rx_bclrm(TCGv mem, TCGv mask) +static void rx_bclrm(DisasContext *ctx, TCGv mem, TCGv mask) { TCGv val; val = tcg_temp_new(); @@ -1912,7 +1912,7 @@ static void rx_bclrm(TCGv mem, TCGv mask) rx_gen_st(MO_8, val, mem); } -static void rx_btstm(TCGv mem, TCGv mask) +static void rx_btstm(DisasContext *ctx, TCGv mem, TCGv mask) { TCGv val; val = tcg_temp_new(); @@ -1922,7 +1922,7 @@ static void rx_btstm(TCGv mem, TCGv mask) tcg_gen_mov_i32(cpu_psw_z, cpu_psw_c); } -static void rx_bnotm(TCGv mem, TCGv mask) +static void rx_bnotm(DisasContext *ctx, TCGv mem, TCGv mask) { TCGv val; val = tcg_temp_new(); @@ -1931,17 +1931,17 @@ static void rx_bnotm(TCGv mem, TCGv mask) rx_gen_st(MO_8, val, mem); } -static void rx_bsetr(TCGv reg, TCGv mask) +static void rx_bsetr(DisasContext *ctx, TCGv reg, TCGv mask) { tcg_gen_or_i32(reg, reg, mask); } -static void rx_bclrr(TCGv reg, TCGv mask) +static void rx_bclrr(DisasContext *ctx, TCGv reg, TCGv mask) { tcg_gen_andc_i32(reg, reg, mask); } -static inline void rx_btstr(TCGv reg, TCGv mask) +static inline void rx_btstr(DisasContext *ctx, TCGv reg, TCGv mask) { TCGv t0; t0 = tcg_temp_new(); @@ -1950,7 +1950,7 @@ static inline void rx_btstr(TCGv reg, TCGv mask) tcg_gen_mov_i32(cpu_psw_z, cpu_psw_c); } -static inline void rx_bnotr(TCGv reg, TCGv mask) +static inline void rx_bnotr(DisasContext *ctx, TCGv reg, TCGv mask) { tcg_gen_xor_i32(reg, reg, mask); } @@ -1963,7 +1963,7 @@ static inline void rx_bnotr(TCGv reg, TCGv mask) mem = tcg_temp_new(); \ mask = tcg_constant_i32(1 << a->imm); \ addr = rx_index_addr(ctx, mem, a->ld, MO_8, a->rs); \ - cat3(rx_, op, m)(addr, mask); \ + cat3(rx_, op, m)(ctx, addr, mask); \ return true; \ } \ static bool cat3(trans_, name, _ir)(DisasContext *ctx, \ @@ -1971,7 +1971,7 @@ static inline void rx_bnotr(TCGv reg, TCGv mask) { \ TCGv mask; \ mask = tcg_constant_i32(1 << a->imm); \ - cat3(rx_, op, r)(cpu_regs[a->rd], mask); \ + cat3(rx_, op, r)(ctx, cpu_regs[a->rd], mask); \ return true; \ } \ static bool cat3(trans_, name, _rr)(DisasContext *ctx, \ @@ -1982,7 +1982,7 @@ static inline void rx_bnotr(TCGv reg, TCGv mask) b = tcg_temp_new(); \ tcg_gen_andi_i32(b, cpu_regs[a->rs], 31); \ tcg_gen_shl_i32(mask, tcg_constant_i32(1), b); \ - cat3(rx_, op, r)(cpu_regs[a->rd], mask); \ + cat3(rx_, op, r)(ctx, cpu_regs[a->rd], mask); \ return true; \ } \ static bool cat3(trans_, name, _rm)(DisasContext *ctx, \ @@ -1995,7 +1995,7 @@ static inline void rx_bnotr(TCGv reg, TCGv mask) tcg_gen_shl_i32(mask, tcg_constant_i32(1), b); \ mem = tcg_temp_new(); \ addr = rx_index_addr(ctx, mem, a->ld, MO_8, a->rs); \ - cat3(rx_, op, m)(addr, mask); \ + cat3(rx_, op, m)(ctx, addr, mask); \ return true; \ } From 8b71fd6ffec8787f0685ccce5f4dd9db50fea792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 16:53:50 +0200 Subject: [PATCH 1643/1794] target/rx: Propagate DisasContext to push() / pop() 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: <20251009151607.26278-5-philmd@linaro.org> --- target/rx/translate.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/target/rx/translate.c b/target/rx/translate.c index b314d94b0189b..912daaf9fb6ad 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -414,13 +414,13 @@ static void move_to_cr(DisasContext *ctx, TCGv val, int cr) } } -static void push(TCGv val) +static void push(DisasContext *ctx, TCGv val) { tcg_gen_subi_i32(cpu_sp, cpu_sp, 4); rx_gen_st(MO_32, val, cpu_sp); } -static void pop(TCGv ret) +static void pop(DisasContext *ctx, TCGv ret) { rx_gen_ld(MO_32, ret, cpu_sp); tcg_gen_addi_i32(cpu_sp, cpu_sp, 4); @@ -619,7 +619,7 @@ static bool trans_POPC(DisasContext *ctx, arg_POPC *a) { TCGv val; val = tcg_temp_new(); - pop(val); + pop(ctx, val); move_to_cr(ctx, val, a->cr); return true; } @@ -634,7 +634,7 @@ static bool trans_POPM(DisasContext *ctx, arg_POPM *a) } r = a->rd; while (r <= a->rd2 && r < 16) { - pop(cpu_regs[r++]); + pop(ctx, cpu_regs[r++]); } return true; } @@ -670,7 +670,7 @@ static bool trans_PUSHC(DisasContext *ctx, arg_PUSHC *a) TCGv val; val = tcg_temp_new(); move_from_cr(ctx, val, a->cr, ctx->pc); - push(val); + push(ctx, val); return true; } @@ -685,7 +685,7 @@ static bool trans_PUSHM(DisasContext *ctx, arg_PUSHM *a) } r = a->rs2; while (r >= a->rs && r >= 0) { - push(cpu_regs[r--]); + push(ctx, cpu_regs[r--]); } return true; } @@ -772,7 +772,7 @@ static bool trans_SCCnd(DisasContext *ctx, arg_SCCnd *a) static bool trans_RTSD_i(DisasContext *ctx, arg_RTSD_i *a) { tcg_gen_addi_i32(cpu_sp, cpu_sp, a->imm << 2); - pop(cpu_pc); + pop(ctx, cpu_pc); ctx->base.is_jmp = DISAS_JUMP; return true; } @@ -792,9 +792,9 @@ static bool trans_RTSD_irr(DisasContext *ctx, arg_RTSD_irr *a) tcg_gen_addi_i32(cpu_sp, cpu_sp, adj << 2); dst = a->rd; while (dst <= a->rd2 && dst < 16) { - pop(cpu_regs[dst++]); + pop(ctx, cpu_regs[dst++]); } - pop(cpu_pc); + pop(ctx, cpu_pc); ctx->base.is_jmp = DISAS_JUMP; return true; } @@ -1585,7 +1585,7 @@ static bool trans_BRA_l(DisasContext *ctx, arg_BRA_l *a) static inline void rx_save_pc(DisasContext *ctx) { TCGv pc = tcg_constant_i32(ctx->base.pc_next); - push(pc); + push(ctx, pc); } /* jmp rs */ @@ -1626,7 +1626,7 @@ static bool trans_BSR_l(DisasContext *ctx, arg_BSR_l *a) /* rts */ static bool trans_RTS(DisasContext *ctx, arg_RTS *a) { - pop(cpu_pc); + pop(ctx, cpu_pc); ctx->base.is_jmp = DISAS_JUMP; return true; } @@ -2154,8 +2154,8 @@ static bool trans_RTE(DisasContext *ctx, arg_RTE *a) TCGv psw; if (is_privileged(ctx, 1)) { psw = tcg_temp_new(); - pop(cpu_pc); - pop(psw); + pop(ctx, cpu_pc); + pop(ctx, psw); gen_helper_set_psw_rte(tcg_env, psw); ctx->base.is_jmp = DISAS_EXIT; } From 363fff6d1b1f71b4f41c490076127ce3021bffc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 16:55:19 +0200 Subject: [PATCH 1644/1794] target/rx: Propagate DisasContext to gen_ld[u]() and gen_st() 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: <20251009151607.26278-6-philmd@linaro.org> --- target/rx/translate.c | 64 +++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/target/rx/translate.c b/target/rx/translate.c index 912daaf9fb6ad..3c7bcc9933d3e 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -161,19 +161,19 @@ static void gen_goto_tb(DisasContext *dc, unsigned tb_slot_idx, vaddr dest) } /* generic load wrapper */ -static inline void rx_gen_ld(MemOp size, TCGv reg, TCGv mem) +static inline void rx_gen_ld(DisasContext *ctx, MemOp size, TCGv reg, TCGv mem) { tcg_gen_qemu_ld_i32(reg, mem, 0, size | MO_SIGN | MO_TE); } /* unsigned load wrapper */ -static inline void rx_gen_ldu(MemOp size, TCGv reg, TCGv mem) +static inline void rx_gen_ldu(DisasContext *ctx, MemOp size, TCGv reg, TCGv mem) { tcg_gen_qemu_ld_i32(reg, mem, 0, size | MO_TE); } /* generic store wrapper */ -static inline void rx_gen_st(MemOp size, TCGv reg, TCGv mem) +static inline void rx_gen_st(DisasContext *ctx, MemOp size, TCGv reg, TCGv mem) { tcg_gen_qemu_st_i32(reg, mem, 0, size | MO_TE); } @@ -417,12 +417,12 @@ static void move_to_cr(DisasContext *ctx, TCGv val, int cr) static void push(DisasContext *ctx, TCGv val) { tcg_gen_subi_i32(cpu_sp, cpu_sp, 4); - rx_gen_st(MO_32, val, cpu_sp); + rx_gen_st(ctx, MO_32, val, cpu_sp); } static void pop(DisasContext *ctx, TCGv ret) { - rx_gen_ld(MO_32, ret, cpu_sp); + rx_gen_ld(ctx, MO_32, ret, cpu_sp); tcg_gen_addi_i32(cpu_sp, cpu_sp, 4); } @@ -432,7 +432,7 @@ static bool trans_MOV_rm(DisasContext *ctx, arg_MOV_rm *a) TCGv mem; mem = tcg_temp_new(); tcg_gen_addi_i32(mem, cpu_regs[a->rd], a->dsp << a->sz); - rx_gen_st(a->sz, cpu_regs[a->rs], mem); + rx_gen_st(ctx, a->sz, cpu_regs[a->rs], mem); return true; } @@ -442,7 +442,7 @@ static bool trans_MOV_mr(DisasContext *ctx, arg_MOV_mr *a) TCGv mem; mem = tcg_temp_new(); tcg_gen_addi_i32(mem, cpu_regs[a->rs], a->dsp << a->sz); - rx_gen_ld(a->sz, cpu_regs[a->rd], mem); + rx_gen_ld(ctx, a->sz, cpu_regs[a->rd], mem); return true; } @@ -463,7 +463,7 @@ static bool trans_MOV_im(DisasContext *ctx, arg_MOV_im *a) imm = tcg_constant_i32(a->imm); mem = tcg_temp_new(); tcg_gen_addi_i32(mem, cpu_regs[a->rd], a->dsp << a->sz); - rx_gen_st(a->sz, imm, mem); + rx_gen_st(ctx, a->sz, imm, mem); return true; } @@ -473,7 +473,7 @@ static bool trans_MOV_ar(DisasContext *ctx, arg_MOV_ar *a) TCGv mem; mem = tcg_temp_new(); rx_gen_regindex(ctx, mem, a->sz, a->ri, a->rb); - rx_gen_ld(a->sz, cpu_regs[a->rd], mem); + rx_gen_ld(ctx, a->sz, cpu_regs[a->rd], mem); return true; } @@ -483,7 +483,7 @@ static bool trans_MOV_ra(DisasContext *ctx, arg_MOV_ra *a) TCGv mem; mem = tcg_temp_new(); rx_gen_regindex(ctx, mem, a->sz, a->ri, a->rb); - rx_gen_st(a->sz, cpu_regs[a->rs], mem); + rx_gen_st(ctx, a->sz, cpu_regs[a->rs], mem); return true; } @@ -505,18 +505,18 @@ static bool trans_MOV_mm(DisasContext *ctx, arg_MOV_mm *a) if (a->lds == 3) { /* mov. rs,dsp[rd] */ addr = rx_index_addr(ctx, mem, a->ldd, a->sz, a->rs); - rx_gen_st(a->sz, cpu_regs[a->rd], addr); + rx_gen_st(ctx, a->sz, cpu_regs[a->rd], addr); } else if (a->ldd == 3) { /* mov. dsp[rs],rd */ addr = rx_index_addr(ctx, mem, a->lds, a->sz, a->rs); - rx_gen_ld(a->sz, cpu_regs[a->rd], addr); + rx_gen_ld(ctx, a->sz, cpu_regs[a->rd], addr); } else { /* mov. dsp[rs],dsp[rd] */ tmp = tcg_temp_new(); addr = rx_index_addr(ctx, mem, a->lds, a->sz, a->rs); - rx_gen_ld(a->sz, tmp, addr); + rx_gen_ld(ctx, a->sz, tmp, addr); addr = rx_index_addr(ctx, mem, a->ldd, a->sz, a->rd); - rx_gen_st(a->sz, tmp, addr); + rx_gen_st(ctx, a->sz, tmp, addr); } return true; } @@ -531,7 +531,7 @@ static bool trans_MOV_rp(DisasContext *ctx, arg_MOV_rp *a) if (a->ad == 1) { tcg_gen_subi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); } - rx_gen_st(a->sz, val, cpu_regs[a->rd]); + rx_gen_st(ctx, a->sz, val, cpu_regs[a->rd]); if (a->ad == 0) { tcg_gen_addi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); } @@ -547,7 +547,7 @@ static bool trans_MOV_pr(DisasContext *ctx, arg_MOV_pr *a) if (a->ad == 1) { tcg_gen_subi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); } - rx_gen_ld(a->sz, val, cpu_regs[a->rd]); + rx_gen_ld(ctx, a->sz, val, cpu_regs[a->rd]); if (a->ad == 0) { tcg_gen_addi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); } @@ -562,7 +562,7 @@ static bool trans_MOVU_mr(DisasContext *ctx, arg_MOVU_mr *a) TCGv mem; mem = tcg_temp_new(); tcg_gen_addi_i32(mem, cpu_regs[a->rs], a->dsp << a->sz); - rx_gen_ldu(a->sz, cpu_regs[a->rd], mem); + rx_gen_ldu(ctx, a->sz, cpu_regs[a->rd], mem); return true; } @@ -579,7 +579,7 @@ static bool trans_MOVU_ar(DisasContext *ctx, arg_MOVU_ar *a) TCGv mem; mem = tcg_temp_new(); rx_gen_regindex(ctx, mem, a->sz, a->ri, a->rb); - rx_gen_ldu(a->sz, cpu_regs[a->rd], mem); + rx_gen_ldu(ctx, a->sz, cpu_regs[a->rd], mem); return true; } @@ -592,7 +592,7 @@ static bool trans_MOVU_pr(DisasContext *ctx, arg_MOVU_pr *a) if (a->ad == 1) { tcg_gen_subi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); } - rx_gen_ldu(a->sz, val, cpu_regs[a->rd]); + rx_gen_ldu(ctx, a->sz, val, cpu_regs[a->rd]); if (a->ad == 0) { tcg_gen_addi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); } @@ -647,7 +647,7 @@ static bool trans_PUSH_r(DisasContext *ctx, arg_PUSH_r *a) val = tcg_temp_new(); tcg_gen_mov_i32(val, cpu_regs[a->rs]); tcg_gen_subi_i32(cpu_sp, cpu_sp, 4); - rx_gen_st(a->sz, val, cpu_sp); + rx_gen_st(ctx, a->sz, val, cpu_sp); return true; } @@ -658,9 +658,9 @@ static bool trans_PUSH_m(DisasContext *ctx, arg_PUSH_m *a) mem = tcg_temp_new(); val = tcg_temp_new(); addr = rx_index_addr(ctx, mem, a->ld, a->sz, a->rs); - rx_gen_ld(a->sz, val, addr); + rx_gen_ld(ctx, a->sz, val, addr); tcg_gen_subi_i32(cpu_sp, cpu_sp, 4); - rx_gen_st(a->sz, val, cpu_sp); + rx_gen_st(ctx, a->sz, val, cpu_sp); return true; } @@ -761,7 +761,7 @@ static bool trans_SCCnd(DisasContext *ctx, arg_SCCnd *a) mem = tcg_temp_new(); tcg_gen_setcondi_i32(dc.cond, val, dc.value, 0); addr = rx_index_addr(ctx, mem, a->sz, a->ld, a->rd); - rx_gen_st(a->sz, val, addr); + rx_gen_st(ctx, a->sz, val, addr); } else { tcg_gen_setcondi_i32(dc.cond, cpu_regs[a->rd], dc.value, 0); } @@ -1898,25 +1898,25 @@ static void rx_bsetm(DisasContext *ctx, TCGv mem, TCGv mask) { TCGv val; val = tcg_temp_new(); - rx_gen_ld(MO_8, val, mem); + rx_gen_ld(ctx, MO_8, val, mem); tcg_gen_or_i32(val, val, mask); - rx_gen_st(MO_8, val, mem); + rx_gen_st(ctx, MO_8, val, mem); } static void rx_bclrm(DisasContext *ctx, TCGv mem, TCGv mask) { TCGv val; val = tcg_temp_new(); - rx_gen_ld(MO_8, val, mem); + rx_gen_ld(ctx, MO_8, val, mem); tcg_gen_andc_i32(val, val, mask); - rx_gen_st(MO_8, val, mem); + rx_gen_st(ctx, MO_8, val, mem); } static void rx_btstm(DisasContext *ctx, TCGv mem, TCGv mask) { TCGv val; val = tcg_temp_new(); - rx_gen_ld(MO_8, val, mem); + rx_gen_ld(ctx, MO_8, val, mem); tcg_gen_and_i32(val, val, mask); tcg_gen_setcondi_i32(TCG_COND_NE, cpu_psw_c, val, 0); tcg_gen_mov_i32(cpu_psw_z, cpu_psw_c); @@ -1926,9 +1926,9 @@ static void rx_bnotm(DisasContext *ctx, TCGv mem, TCGv mask) { TCGv val; val = tcg_temp_new(); - rx_gen_ld(MO_8, val, mem); + rx_gen_ld(ctx, MO_8, val, mem); tcg_gen_xor_i32(val, val, mask); - rx_gen_st(MO_8, val, mem); + rx_gen_st(ctx, MO_8, val, mem); } static void rx_bsetr(DisasContext *ctx, TCGv reg, TCGv mask) @@ -2023,9 +2023,9 @@ static bool trans_BMCnd_im(DisasContext *ctx, arg_BMCnd_im *a) val = tcg_temp_new(); mem = tcg_temp_new(); addr = rx_index_addr(ctx, mem, a->ld, MO_8, a->rd); - rx_gen_ld(MO_8, val, addr); + rx_gen_ld(ctx, MO_8, val, addr); bmcnd_op(val, a->cd, a->imm); - rx_gen_st(MO_8, val, addr); + rx_gen_st(ctx, MO_8, val, addr); return true; } From cb8e4556d584c89e58fcff1757df7e2bf2553a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 16:55:33 +0200 Subject: [PATCH 1645/1794] target/rx: Factor mo_endian() helper out 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: <20251009151607.26278-7-philmd@linaro.org> --- target/rx/translate.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/target/rx/translate.c b/target/rx/translate.c index 3c7bcc9933d3e..6ed7ef629ffcf 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -72,6 +72,11 @@ static TCGv_i64 cpu_acc; #define cpu_sp cpu_regs[0] +static inline MemOp mo_endian(DisasContext *dc) +{ + return MO_TE; +} + /* decoder helper */ static uint32_t decode_load_bytes(DisasContext *ctx, uint32_t insn, int i, int n) @@ -163,19 +168,19 @@ static void gen_goto_tb(DisasContext *dc, unsigned tb_slot_idx, vaddr dest) /* generic load wrapper */ static inline void rx_gen_ld(DisasContext *ctx, MemOp size, TCGv reg, TCGv mem) { - tcg_gen_qemu_ld_i32(reg, mem, 0, size | MO_SIGN | MO_TE); + tcg_gen_qemu_ld_i32(reg, mem, 0, size | MO_SIGN | mo_endian(ctx)); } /* unsigned load wrapper */ static inline void rx_gen_ldu(DisasContext *ctx, MemOp size, TCGv reg, TCGv mem) { - tcg_gen_qemu_ld_i32(reg, mem, 0, size | MO_TE); + tcg_gen_qemu_ld_i32(reg, mem, 0, size | mo_endian(ctx)); } /* generic store wrapper */ static inline void rx_gen_st(DisasContext *ctx, MemOp size, TCGv reg, TCGv mem) { - tcg_gen_qemu_st_i32(reg, mem, 0, size | MO_TE); + tcg_gen_qemu_st_i32(reg, mem, 0, size | mo_endian(ctx)); } /* [ri, rb] */ @@ -226,7 +231,7 @@ static inline TCGv rx_load_source(DisasContext *ctx, TCGv mem, if (ld < 3) { mop = mi_to_mop(mi); addr = rx_index_addr(ctx, mem, ld, mop & MO_SIZE, rs); - tcg_gen_qemu_ld_i32(mem, addr, 0, mop | MO_TE); + tcg_gen_qemu_ld_i32(mem, addr, 0, mop | mo_endian(ctx)); return mem; } else { return cpu_regs[rs]; From f9903a8a23ec14a8bef1d9af908e505c44b977af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 16:55:50 +0200 Subject: [PATCH 1646/1794] target/rx: Replace MO_TE -> MO_LE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only build the RX targets using little endianness order: $ git grep TARGET_BIG_ENDIAN configs/targets/rx-* $ Therefore the MO_TE definition always expands to MO_LE. Use the latter to simplify. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251009151607.26278-8-philmd@linaro.org> --- target/rx/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/rx/translate.c b/target/rx/translate.c index 6ed7ef629ffcf..f2168243c88f4 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -74,7 +74,7 @@ static TCGv_i64 cpu_acc; static inline MemOp mo_endian(DisasContext *dc) { - return MO_TE; + return MO_LE; } /* decoder helper */ From 86114e43ffda717c0ed4225e5d5ddf315e930835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 16:57:11 +0200 Subject: [PATCH 1647/1794] target/rx: Expand TCG register definitions for 32-bit target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RX target is only built as 32-bit: $ git grep TARGET_LONG_BITS configs/targets/rx-* configs/targets/rx-softmmu.mak:5:TARGET_LONG_BITS=32 Therefore target_ulong always expands to uint32_t. Replace and adapt the API uses mechanically: TCGv -> TCGv_i32 tcg_temp_new -> tcg_temp_new_i32 There is no functional change. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251009151607.26278-9-philmd@linaro.org> --- target/rx/translate.c | 326 +++++++++++++++++++++--------------------- 1 file changed, 165 insertions(+), 161 deletions(-) diff --git a/target/rx/translate.c b/target/rx/translate.c index f2168243c88f4..55285d8166f3f 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -40,8 +40,8 @@ typedef struct DisasContext { } DisasContext; typedef struct DisasCompare { - TCGv value; - TCGv temp; + TCGv_i32 value; + TCGv_i32 temp; TCGCond cond; } DisasCompare; @@ -63,11 +63,11 @@ const char *rx_crname(uint8_t cr) #define DISAS_EXIT DISAS_TARGET_2 /* global register indexes */ -static TCGv cpu_regs[16]; -static TCGv cpu_psw_o, cpu_psw_s, cpu_psw_z, cpu_psw_c; -static TCGv cpu_psw_i, cpu_psw_pm, cpu_psw_u, cpu_psw_ipl; -static TCGv cpu_usp, cpu_fpsw, cpu_bpsw, cpu_bpc, cpu_isp; -static TCGv cpu_fintv, cpu_intb, cpu_pc; +static TCGv_i32 cpu_regs[16]; +static TCGv_i32 cpu_psw_o, cpu_psw_s, cpu_psw_z, cpu_psw_c; +static TCGv_i32 cpu_psw_i, cpu_psw_pm, cpu_psw_u, cpu_psw_ipl; +static TCGv_i32 cpu_usp, cpu_fpsw, cpu_bpsw, cpu_bpc, cpu_isp; +static TCGv_i32 cpu_fintv, cpu_intb, cpu_pc; static TCGv_i64 cpu_acc; #define cpu_sp cpu_regs[0] @@ -166,25 +166,28 @@ static void gen_goto_tb(DisasContext *dc, unsigned tb_slot_idx, vaddr dest) } /* generic load wrapper */ -static inline void rx_gen_ld(DisasContext *ctx, MemOp size, TCGv reg, TCGv mem) +static inline void rx_gen_ld(DisasContext *ctx, MemOp size, + TCGv_i32 reg, TCGv_i32 mem) { tcg_gen_qemu_ld_i32(reg, mem, 0, size | MO_SIGN | mo_endian(ctx)); } /* unsigned load wrapper */ -static inline void rx_gen_ldu(DisasContext *ctx, MemOp size, TCGv reg, TCGv mem) +static inline void rx_gen_ldu(DisasContext *ctx, MemOp size, + TCGv_i32 reg, TCGv_i32 mem) { tcg_gen_qemu_ld_i32(reg, mem, 0, size | mo_endian(ctx)); } /* generic store wrapper */ -static inline void rx_gen_st(DisasContext *ctx, MemOp size, TCGv reg, TCGv mem) +static inline void rx_gen_st(DisasContext *ctx, MemOp size, + TCGv_i32 reg, TCGv_i32 mem) { tcg_gen_qemu_st_i32(reg, mem, 0, size | mo_endian(ctx)); } /* [ri, rb] */ -static inline void rx_gen_regindex(DisasContext *ctx, TCGv mem, +static inline void rx_gen_regindex(DisasContext *ctx, TCGv_i32 mem, int size, int ri, int rb) { tcg_gen_shli_i32(mem, cpu_regs[ri], size); @@ -192,7 +195,7 @@ static inline void rx_gen_regindex(DisasContext *ctx, TCGv mem, } /* dsp[reg] */ -static inline TCGv rx_index_addr(DisasContext *ctx, TCGv mem, +static inline TCGv_i32 rx_index_addr(DisasContext *ctx, TCGv_i32 mem, int ld, int size, int reg) { uint32_t dsp; @@ -223,10 +226,10 @@ static inline MemOp mi_to_mop(unsigned mi) } /* load source operand */ -static inline TCGv rx_load_source(DisasContext *ctx, TCGv mem, +static inline TCGv_i32 rx_load_source(DisasContext *ctx, TCGv_i32 mem, int ld, int mi, int rs) { - TCGv addr; + TCGv_i32 addr; MemOp mop; if (ld < 3) { mop = mi_to_mop(mi); @@ -320,7 +323,7 @@ static void psw_cond(DisasCompare *dc, uint32_t cond) } } -static void move_from_cr(DisasContext *ctx, TCGv ret, int cr, uint32_t pc) +static void move_from_cr(DisasContext *ctx, TCGv_i32 ret, int cr, uint32_t pc) { switch (cr) { case 0: /* PSW */ @@ -366,7 +369,7 @@ static void move_from_cr(DisasContext *ctx, TCGv ret, int cr, uint32_t pc) } } -static void move_to_cr(DisasContext *ctx, TCGv val, int cr) +static void move_to_cr(DisasContext *ctx, TCGv_i32 val, int cr) { if (cr >= 8 && !is_privileged(ctx, 0)) { /* Some control registers can only be written in privileged mode. */ @@ -419,13 +422,13 @@ static void move_to_cr(DisasContext *ctx, TCGv val, int cr) } } -static void push(DisasContext *ctx, TCGv val) +static void push(DisasContext *ctx, TCGv_i32 val) { tcg_gen_subi_i32(cpu_sp, cpu_sp, 4); rx_gen_st(ctx, MO_32, val, cpu_sp); } -static void pop(DisasContext *ctx, TCGv ret) +static void pop(DisasContext *ctx, TCGv_i32 ret) { rx_gen_ld(ctx, MO_32, ret, cpu_sp); tcg_gen_addi_i32(cpu_sp, cpu_sp, 4); @@ -434,8 +437,8 @@ static void pop(DisasContext *ctx, TCGv ret) /* mov. rs,dsp5[rd] */ static bool trans_MOV_rm(DisasContext *ctx, arg_MOV_rm *a) { - TCGv mem; - mem = tcg_temp_new(); + TCGv_i32 mem; + mem = tcg_temp_new_i32(); tcg_gen_addi_i32(mem, cpu_regs[a->rd], a->dsp << a->sz); rx_gen_st(ctx, a->sz, cpu_regs[a->rs], mem); return true; @@ -444,8 +447,8 @@ static bool trans_MOV_rm(DisasContext *ctx, arg_MOV_rm *a) /* mov. dsp5[rs],rd */ static bool trans_MOV_mr(DisasContext *ctx, arg_MOV_mr *a) { - TCGv mem; - mem = tcg_temp_new(); + TCGv_i32 mem; + mem = tcg_temp_new_i32(); tcg_gen_addi_i32(mem, cpu_regs[a->rs], a->dsp << a->sz); rx_gen_ld(ctx, a->sz, cpu_regs[a->rd], mem); return true; @@ -464,9 +467,9 @@ static bool trans_MOV_ir(DisasContext *ctx, arg_MOV_ir *a) /* mov. #imm, dsp[rd] */ static bool trans_MOV_im(DisasContext *ctx, arg_MOV_im *a) { - TCGv imm, mem; + TCGv_i32 imm, mem; imm = tcg_constant_i32(a->imm); - mem = tcg_temp_new(); + mem = tcg_temp_new_i32(); tcg_gen_addi_i32(mem, cpu_regs[a->rd], a->dsp << a->sz); rx_gen_st(ctx, a->sz, imm, mem); return true; @@ -475,8 +478,8 @@ static bool trans_MOV_im(DisasContext *ctx, arg_MOV_im *a) /* mov. [ri,rb],rd */ static bool trans_MOV_ar(DisasContext *ctx, arg_MOV_ar *a) { - TCGv mem; - mem = tcg_temp_new(); + TCGv_i32 mem; + mem = tcg_temp_new_i32(); rx_gen_regindex(ctx, mem, a->sz, a->ri, a->rb); rx_gen_ld(ctx, a->sz, cpu_regs[a->rd], mem); return true; @@ -485,8 +488,8 @@ static bool trans_MOV_ar(DisasContext *ctx, arg_MOV_ar *a) /* mov. rd,[ri,rb] */ static bool trans_MOV_ra(DisasContext *ctx, arg_MOV_ra *a) { - TCGv mem; - mem = tcg_temp_new(); + TCGv_i32 mem; + mem = tcg_temp_new_i32(); rx_gen_regindex(ctx, mem, a->sz, a->ri, a->rb); rx_gen_st(ctx, a->sz, cpu_regs[a->rs], mem); return true; @@ -498,7 +501,7 @@ static bool trans_MOV_ra(DisasContext *ctx, arg_MOV_ra *a) /* mov. rs,rd */ static bool trans_MOV_mm(DisasContext *ctx, arg_MOV_mm *a) { - TCGv tmp, mem, addr; + TCGv_i32 tmp, mem, addr; if (a->lds == 3 && a->ldd == 3) { /* mov. rs,rd */ @@ -506,7 +509,7 @@ static bool trans_MOV_mm(DisasContext *ctx, arg_MOV_mm *a) return true; } - mem = tcg_temp_new(); + mem = tcg_temp_new_i32(); if (a->lds == 3) { /* mov. rs,dsp[rd] */ addr = rx_index_addr(ctx, mem, a->ldd, a->sz, a->rs); @@ -517,7 +520,7 @@ static bool trans_MOV_mm(DisasContext *ctx, arg_MOV_mm *a) rx_gen_ld(ctx, a->sz, cpu_regs[a->rd], addr); } else { /* mov. dsp[rs],dsp[rd] */ - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i32(); addr = rx_index_addr(ctx, mem, a->lds, a->sz, a->rs); rx_gen_ld(ctx, a->sz, tmp, addr); addr = rx_index_addr(ctx, mem, a->ldd, a->sz, a->rd); @@ -530,8 +533,8 @@ static bool trans_MOV_mm(DisasContext *ctx, arg_MOV_mm *a) /* mov. rs,[-rd] */ static bool trans_MOV_rp(DisasContext *ctx, arg_MOV_rp *a) { - TCGv val; - val = tcg_temp_new(); + TCGv_i32 val; + val = tcg_temp_new_i32(); tcg_gen_mov_i32(val, cpu_regs[a->rs]); if (a->ad == 1) { tcg_gen_subi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); @@ -547,8 +550,8 @@ static bool trans_MOV_rp(DisasContext *ctx, arg_MOV_rp *a) /* mov. [-rd],rs */ static bool trans_MOV_pr(DisasContext *ctx, arg_MOV_pr *a) { - TCGv val; - val = tcg_temp_new(); + TCGv_i32 val; + val = tcg_temp_new_i32(); if (a->ad == 1) { tcg_gen_subi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); } @@ -564,8 +567,8 @@ static bool trans_MOV_pr(DisasContext *ctx, arg_MOV_pr *a) /* movu. dsp[rs],rd */ static bool trans_MOVU_mr(DisasContext *ctx, arg_MOVU_mr *a) { - TCGv mem; - mem = tcg_temp_new(); + TCGv_i32 mem; + mem = tcg_temp_new_i32(); tcg_gen_addi_i32(mem, cpu_regs[a->rs], a->dsp << a->sz); rx_gen_ldu(ctx, a->sz, cpu_regs[a->rd], mem); return true; @@ -581,8 +584,8 @@ static bool trans_MOVU_rr(DisasContext *ctx, arg_MOVU_rr *a) /* movu. [ri,rb],rd */ static bool trans_MOVU_ar(DisasContext *ctx, arg_MOVU_ar *a) { - TCGv mem; - mem = tcg_temp_new(); + TCGv_i32 mem; + mem = tcg_temp_new_i32(); rx_gen_regindex(ctx, mem, a->sz, a->ri, a->rb); rx_gen_ldu(ctx, a->sz, cpu_regs[a->rd], mem); return true; @@ -592,8 +595,8 @@ static bool trans_MOVU_ar(DisasContext *ctx, arg_MOVU_ar *a) /* mov. [-rd],rs */ static bool trans_MOVU_pr(DisasContext *ctx, arg_MOVU_pr *a) { - TCGv val; - val = tcg_temp_new(); + TCGv_i32 val; + val = tcg_temp_new_i32(); if (a->ad == 1) { tcg_gen_subi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); } @@ -622,8 +625,8 @@ static bool trans_POP(DisasContext *ctx, arg_POP *a) /* popc cr */ static bool trans_POPC(DisasContext *ctx, arg_POPC *a) { - TCGv val; - val = tcg_temp_new(); + TCGv_i32 val; + val = tcg_temp_new_i32(); pop(ctx, val); move_to_cr(ctx, val, a->cr); return true; @@ -648,8 +651,8 @@ static bool trans_POPM(DisasContext *ctx, arg_POPM *a) /* push. rs */ static bool trans_PUSH_r(DisasContext *ctx, arg_PUSH_r *a) { - TCGv val; - val = tcg_temp_new(); + TCGv_i32 val; + val = tcg_temp_new_i32(); tcg_gen_mov_i32(val, cpu_regs[a->rs]); tcg_gen_subi_i32(cpu_sp, cpu_sp, 4); rx_gen_st(ctx, a->sz, val, cpu_sp); @@ -659,9 +662,9 @@ static bool trans_PUSH_r(DisasContext *ctx, arg_PUSH_r *a) /* push. dsp[rs] */ static bool trans_PUSH_m(DisasContext *ctx, arg_PUSH_m *a) { - TCGv mem, val, addr; - mem = tcg_temp_new(); - val = tcg_temp_new(); + TCGv_i32 mem, val, addr; + mem = tcg_temp_new_i32(); + val = tcg_temp_new_i32(); addr = rx_index_addr(ctx, mem, a->ld, a->sz, a->rs); rx_gen_ld(ctx, a->sz, val, addr); tcg_gen_subi_i32(cpu_sp, cpu_sp, 4); @@ -672,8 +675,8 @@ static bool trans_PUSH_m(DisasContext *ctx, arg_PUSH_m *a) /* pushc rx */ static bool trans_PUSHC(DisasContext *ctx, arg_PUSHC *a) { - TCGv val; - val = tcg_temp_new(); + TCGv_i32 val; + val = tcg_temp_new_i32(); move_from_cr(ctx, val, a->cr, ctx->pc); push(ctx, val); return true; @@ -698,8 +701,8 @@ static bool trans_PUSHM(DisasContext *ctx, arg_PUSHM *a) /* xchg rs,rd */ static bool trans_XCHG_rr(DisasContext *ctx, arg_XCHG_rr *a) { - TCGv tmp; - tmp = tcg_temp_new(); + TCGv_i32 tmp; + tmp = tcg_temp_new_i32(); tcg_gen_mov_i32(tmp, cpu_regs[a->rs]); tcg_gen_mov_i32(cpu_regs[a->rs], cpu_regs[a->rd]); tcg_gen_mov_i32(cpu_regs[a->rd], tmp); @@ -709,8 +712,8 @@ static bool trans_XCHG_rr(DisasContext *ctx, arg_XCHG_rr *a) /* xchg dsp[rs].,rd */ static bool trans_XCHG_mr(DisasContext *ctx, arg_XCHG_mr *a) { - TCGv mem, addr; - mem = tcg_temp_new(); + TCGv_i32 mem, addr; + mem = tcg_temp_new_i32(); switch (a->mi) { case 0: /* dsp[rs].b */ case 1: /* dsp[rs].w */ @@ -731,8 +734,8 @@ static bool trans_XCHG_mr(DisasContext *ctx, arg_XCHG_mr *a) static inline void stcond(TCGCond cond, int rd, int imm) { - TCGv z; - TCGv _imm; + TCGv_i32 z; + TCGv_i32 _imm; z = tcg_constant_i32(0); _imm = tcg_constant_i32(imm); tcg_gen_movcond_i32(cond, cpu_regs[rd], cpu_psw_z, z, @@ -758,12 +761,12 @@ static bool trans_STNZ(DisasContext *ctx, arg_STNZ *a) static bool trans_SCCnd(DisasContext *ctx, arg_SCCnd *a) { DisasCompare dc; - TCGv val, mem, addr; - dc.temp = tcg_temp_new(); + TCGv_i32 val, mem, addr; + dc.temp = tcg_temp_new_i32(); psw_cond(&dc, a->cd); if (a->ld < 3) { - val = tcg_temp_new(); - mem = tcg_temp_new(); + val = tcg_temp_new_i32(); + mem = tcg_temp_new_i32(); tcg_gen_setcondi_i32(dc.cond, val, dc.value, 0); addr = rx_index_addr(ctx, mem, a->sz, a->ld, a->rd); rx_gen_st(ctx, a->sz, val, addr); @@ -804,8 +807,8 @@ static bool trans_RTSD_irr(DisasContext *ctx, arg_RTSD_irr *a) return true; } -typedef void (*op2fn)(TCGv ret, TCGv arg1); -typedef void (*op3fn)(TCGv ret, TCGv arg1, TCGv arg2); +typedef void (*op2fn)(TCGv_i32 ret, TCGv_i32 arg1); +typedef void (*op3fn)(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); static inline void rx_gen_op_rr(op2fn opr, int dst, int src) { @@ -819,20 +822,20 @@ static inline void rx_gen_op_rrr(op3fn opr, int dst, int src, int src2) static inline void rx_gen_op_irr(op3fn opr, int dst, int src, uint32_t src2) { - TCGv imm = tcg_constant_i32(src2); + TCGv_i32 imm = tcg_constant_i32(src2); opr(cpu_regs[dst], cpu_regs[src], imm); } static inline void rx_gen_op_mr(op3fn opr, DisasContext *ctx, int dst, int src, int ld, int mi) { - TCGv val, mem; - mem = tcg_temp_new(); + TCGv_i32 val, mem; + mem = tcg_temp_new_i32(); val = rx_load_source(ctx, mem, ld, mi, src); opr(cpu_regs[dst], cpu_regs[dst], val); } -static void rx_and(TCGv ret, TCGv arg1, TCGv arg2) +static void rx_and(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { tcg_gen_and_i32(cpu_psw_s, arg1, arg2); tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); @@ -862,7 +865,7 @@ static bool trans_AND_rrr(DisasContext *ctx, arg_AND_rrr *a) return true; } -static void rx_or(TCGv ret, TCGv arg1, TCGv arg2) +static void rx_or(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { tcg_gen_or_i32(cpu_psw_s, arg1, arg2); tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); @@ -892,7 +895,7 @@ static bool trans_OR_rrr(DisasContext *ctx, arg_OR_rrr *a) return true; } -static void rx_xor(TCGv ret, TCGv arg1, TCGv arg2) +static void rx_xor(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { tcg_gen_xor_i32(cpu_psw_s, arg1, arg2); tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); @@ -914,7 +917,7 @@ static bool trans_XOR_mr(DisasContext *ctx, arg_XOR_mr *a) return true; } -static void rx_tst(TCGv ret, TCGv arg1, TCGv arg2) +static void rx_tst(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { tcg_gen_and_i32(cpu_psw_s, arg1, arg2); tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); @@ -935,7 +938,7 @@ static bool trans_TST_mr(DisasContext *ctx, arg_TST_mr *a) return true; } -static void rx_not(TCGv ret, TCGv arg1) +static void rx_not(TCGv_i32 ret, TCGv_i32 arg1) { tcg_gen_not_i32(ret, arg1); tcg_gen_mov_i32(cpu_psw_z, ret); @@ -950,7 +953,7 @@ static bool trans_NOT_rr(DisasContext *ctx, arg_NOT_rr *a) return true; } -static void rx_neg(TCGv ret, TCGv arg1) +static void rx_neg(TCGv_i32 ret, TCGv_i32 arg1) { tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_o, arg1, 0x80000000); tcg_gen_neg_i32(ret, arg1); @@ -969,9 +972,9 @@ static bool trans_NEG_rr(DisasContext *ctx, arg_NEG_rr *a) } /* ret = arg1 + arg2 + psw_c */ -static void rx_adc(TCGv ret, TCGv arg1, TCGv arg2) +static void rx_adc(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - TCGv z = tcg_constant_i32(0); + TCGv_i32 z = tcg_constant_i32(0); tcg_gen_add2_i32(cpu_psw_s, cpu_psw_c, arg1, z, cpu_psw_c, z); tcg_gen_add2_i32(cpu_psw_s, cpu_psw_c, cpu_psw_s, cpu_psw_c, arg2, z); tcg_gen_xor_i32(cpu_psw_o, cpu_psw_s, arg1); @@ -1007,9 +1010,9 @@ static bool trans_ADC_mr(DisasContext *ctx, arg_ADC_mr *a) } /* ret = arg1 + arg2 */ -static void rx_add(TCGv ret, TCGv arg1, TCGv arg2) +static void rx_add(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - TCGv z = tcg_constant_i32(0); + TCGv_i32 z = tcg_constant_i32(0); tcg_gen_add2_i32(cpu_psw_s, cpu_psw_c, arg1, z, arg2, z); tcg_gen_xor_i32(cpu_psw_o, cpu_psw_s, arg1); tcg_gen_xor_i32(cpu_psw_z, arg1, arg2); @@ -1042,7 +1045,7 @@ static bool trans_ADD_rrr(DisasContext *ctx, arg_ADD_rrr *a) } /* ret = arg1 - arg2 */ -static void rx_sub(TCGv ret, TCGv arg1, TCGv arg2) +static void rx_sub(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { tcg_gen_sub_i32(cpu_psw_s, arg1, arg2); tcg_gen_setcond_i32(TCG_COND_GEU, cpu_psw_c, arg1, arg2); @@ -1056,17 +1059,17 @@ static void rx_sub(TCGv ret, TCGv arg1, TCGv arg2) } } -static void rx_cmp(TCGv dummy, TCGv arg1, TCGv arg2) +static void rx_cmp(TCGv_i32 dummy, TCGv_i32 arg1, TCGv_i32 arg2) { rx_sub(NULL, arg1, arg2); } /* ret = arg1 - arg2 - !psw_c */ /* -> ret = arg1 + ~arg2 + psw_c */ -static void rx_sbb(TCGv ret, TCGv arg1, TCGv arg2) +static void rx_sbb(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - TCGv temp; - temp = tcg_temp_new(); + TCGv_i32 temp; + temp = tcg_temp_new_i32(); tcg_gen_not_i32(temp, arg2); rx_adc(ret, arg1, temp); } @@ -1192,7 +1195,7 @@ static bool trans_MUL_rrr(DisasContext *ctx, arg_MUL_rrr *a) /* emul #imm, rd */ static bool trans_EMUL_ir(DisasContext *ctx, arg_EMUL_ir *a) { - TCGv imm = tcg_constant_i32(a->imm); + TCGv_i32 imm = tcg_constant_i32(a->imm); if (a->rd > 14) { qemu_log_mask(LOG_GUEST_ERROR, "rd too large %d", a->rd); } @@ -1205,11 +1208,11 @@ static bool trans_EMUL_ir(DisasContext *ctx, arg_EMUL_ir *a) /* emul dsp[rs], rd */ static bool trans_EMUL_mr(DisasContext *ctx, arg_EMUL_mr *a) { - TCGv val, mem; + TCGv_i32 val, mem; if (a->rd > 14) { qemu_log_mask(LOG_GUEST_ERROR, "rd too large %d", a->rd); } - mem = tcg_temp_new(); + mem = tcg_temp_new_i32(); val = rx_load_source(ctx, mem, a->ld, a->mi, a->rs); tcg_gen_muls2_i32(cpu_regs[a->rd], cpu_regs[(a->rd + 1) & 15], cpu_regs[a->rd], val); @@ -1219,7 +1222,7 @@ static bool trans_EMUL_mr(DisasContext *ctx, arg_EMUL_mr *a) /* emulu #imm, rd */ static bool trans_EMULU_ir(DisasContext *ctx, arg_EMULU_ir *a) { - TCGv imm = tcg_constant_i32(a->imm); + TCGv_i32 imm = tcg_constant_i32(a->imm); if (a->rd > 14) { qemu_log_mask(LOG_GUEST_ERROR, "rd too large %d", a->rd); } @@ -1232,23 +1235,23 @@ static bool trans_EMULU_ir(DisasContext *ctx, arg_EMULU_ir *a) /* emulu dsp[rs], rd */ static bool trans_EMULU_mr(DisasContext *ctx, arg_EMULU_mr *a) { - TCGv val, mem; + TCGv_i32 val, mem; if (a->rd > 14) { qemu_log_mask(LOG_GUEST_ERROR, "rd too large %d", a->rd); } - mem = tcg_temp_new(); + mem = tcg_temp_new_i32(); val = rx_load_source(ctx, mem, a->ld, a->mi, a->rs); tcg_gen_mulu2_i32(cpu_regs[a->rd], cpu_regs[(a->rd + 1) & 15], cpu_regs[a->rd], val); return true; } -static void rx_div(TCGv ret, TCGv arg1, TCGv arg2) +static void rx_div(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { gen_helper_div(ret, tcg_env, arg1, arg2); } -static void rx_divu(TCGv ret, TCGv arg1, TCGv arg2) +static void rx_divu(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { gen_helper_divu(ret, tcg_env, arg1, arg2); } @@ -1288,8 +1291,8 @@ static bool trans_DIVU_mr(DisasContext *ctx, arg_DIVU_mr *a) /* shll #imm:5, rs2, rd */ static bool trans_SHLL_irr(DisasContext *ctx, arg_SHLL_irr *a) { - TCGv tmp; - tmp = tcg_temp_new(); + TCGv_i32 tmp; + tmp = tcg_temp_new_i32(); if (a->imm) { tcg_gen_sari_i32(cpu_psw_c, cpu_regs[a->rs2], 32 - a->imm); tcg_gen_shli_i32(cpu_regs[a->rd], cpu_regs[a->rs2], a->imm); @@ -1311,14 +1314,14 @@ static bool trans_SHLL_irr(DisasContext *ctx, arg_SHLL_irr *a) static bool trans_SHLL_rr(DisasContext *ctx, arg_SHLL_rr *a) { TCGLabel *noshift, *done; - TCGv count, tmp; + TCGv_i32 count, tmp; noshift = gen_new_label(); done = gen_new_label(); /* if (cpu_regs[a->rs]) { */ tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_regs[a->rs], 0, noshift); - count = tcg_temp_new(); - tmp = tcg_temp_new(); + count = tcg_temp_new_i32(); + tmp = tcg_temp_new_i32(); tcg_gen_andi_i32(tmp, cpu_regs[a->rs], 31); tcg_gen_sub_i32(count, tcg_constant_i32(32), tmp); tcg_gen_sar_i32(cpu_psw_c, cpu_regs[a->rd], count); @@ -1342,7 +1345,7 @@ static bool trans_SHLL_rr(DisasContext *ctx, arg_SHLL_rr *a) static inline void shiftr_imm(uint32_t rd, uint32_t rs, uint32_t imm, unsigned int alith) { - static void (* const gen_sXri[])(TCGv ret, TCGv arg1, int arg2) = { + static void (* const gen_sXri[])(TCGv_i32 ret, TCGv_i32 arg1, int arg2) = { tcg_gen_shri_i32, tcg_gen_sari_i32, }; tcg_debug_assert(alith < 2); @@ -1362,17 +1365,18 @@ static inline void shiftr_imm(uint32_t rd, uint32_t rs, uint32_t imm, static inline void shiftr_reg(uint32_t rd, uint32_t rs, unsigned int alith) { TCGLabel *noshift, *done; - TCGv count; - static void (* const gen_sXri[])(TCGv ret, TCGv arg1, int arg2) = { + TCGv_i32 count; + static void (* const gen_sXri[])(TCGv_i32 ret, TCGv_i32 arg1, int arg2) = { tcg_gen_shri_i32, tcg_gen_sari_i32, }; - static void (* const gen_sXr[])(TCGv ret, TCGv arg1, TCGv arg2) = { + static void (* const gen_sXr[])(TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2) = { tcg_gen_shr_i32, tcg_gen_sar_i32, }; tcg_debug_assert(alith < 2); noshift = gen_new_label(); done = gen_new_label(); - count = tcg_temp_new(); + count = tcg_temp_new_i32(); /* if (cpu_regs[rs]) { */ tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_regs[rs], 0, noshift); tcg_gen_andi_i32(count, cpu_regs[rs], 31); @@ -1424,8 +1428,8 @@ static bool trans_SHLR_rr(DisasContext *ctx, arg_SHLR_rr *a) /* rolc rd */ static bool trans_ROLC(DisasContext *ctx, arg_ROLC *a) { - TCGv tmp; - tmp = tcg_temp_new(); + TCGv_i32 tmp; + tmp = tcg_temp_new_i32(); tcg_gen_shri_i32(tmp, cpu_regs[a->rd], 31); tcg_gen_shli_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1); tcg_gen_or_i32(cpu_regs[a->rd], cpu_regs[a->rd], cpu_psw_c); @@ -1438,8 +1442,8 @@ static bool trans_ROLC(DisasContext *ctx, arg_ROLC *a) /* rorc rd */ static bool trans_RORC(DisasContext *ctx, arg_RORC *a) { - TCGv tmp; - tmp = tcg_temp_new(); + TCGv_i32 tmp; + tmp = tcg_temp_new_i32(); tcg_gen_andi_i32(tmp, cpu_regs[a->rd], 0x00000001); tcg_gen_shri_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1); tcg_gen_shli_i32(cpu_psw_c, cpu_psw_c, 31); @@ -1514,8 +1518,8 @@ static bool trans_REVL(DisasContext *ctx, arg_REVL *a) /* revw rs, rd */ static bool trans_REVW(DisasContext *ctx, arg_REVW *a) { - TCGv tmp; - tmp = tcg_temp_new(); + TCGv_i32 tmp; + tmp = tcg_temp_new_i32(); tcg_gen_andi_i32(tmp, cpu_regs[a->rs], 0x00ff00ff); tcg_gen_shli_i32(tmp, tmp, 8); tcg_gen_shri_i32(cpu_regs[a->rd], cpu_regs[a->rs], 8); @@ -1532,7 +1536,7 @@ static void rx_bcnd_main(DisasContext *ctx, int cd, int dst) switch (cd) { case 0 ... 13: - dc.temp = tcg_temp_new(); + dc.temp = tcg_temp_new_i32(); psw_cond(&dc, cd); t = gen_new_label(); done = gen_new_label(); @@ -1589,7 +1593,7 @@ static bool trans_BRA_l(DisasContext *ctx, arg_BRA_l *a) static inline void rx_save_pc(DisasContext *ctx) { - TCGv pc = tcg_constant_i32(ctx->base.pc_next); + TCGv_i32 pc = tcg_constant_i32(ctx->base.pc_next); push(ctx, pc); } @@ -1672,7 +1676,7 @@ static bool trans_SMOVB(DisasContext *ctx, arg_SMOVB *a) #define STRING(op) \ do { \ - TCGv size = tcg_constant_i32(a->sz); \ + TCGv_i32 size = tcg_constant_i32(a->sz); \ gen_helper_##op(tcg_env, size); \ } while (0) @@ -1803,7 +1807,7 @@ static bool trans_MVTACLO(DisasContext *ctx, arg_MVTACLO *a) /* racw #imm */ static bool trans_RACW(DisasContext *ctx, arg_RACW *a) { - TCGv imm = tcg_constant_i32(a->imm + 1); + TCGv_i32 imm = tcg_constant_i32(a->imm + 1); gen_helper_racw(tcg_env, imm); return true; } @@ -1811,8 +1815,8 @@ static bool trans_RACW(DisasContext *ctx, arg_RACW *a) /* sat rd */ static bool trans_SAT(DisasContext *ctx, arg_SAT *a) { - TCGv tmp, z; - tmp = tcg_temp_new(); + TCGv_i32 tmp, z; + tmp = tcg_temp_new_i32(); z = tcg_constant_i32(0); /* S == 1 -> 0xffffffff / S == 0 -> 0x00000000 */ tcg_gen_sari_i32(tmp, cpu_psw_s, 31); @@ -1835,7 +1839,7 @@ static bool trans_SATR(DisasContext *ctx, arg_SATR *a) static bool cat3(trans_, name, _ir)(DisasContext *ctx, \ cat3(arg_, name, _ir) * a) \ { \ - TCGv imm = tcg_constant_i32(li(ctx, 0)); \ + TCGv_i32 imm = tcg_constant_i32(li(ctx, 0)); \ gen_helper_##op(cpu_regs[a->rd], tcg_env, \ cpu_regs[a->rd], imm); \ return true; \ @@ -1843,8 +1847,8 @@ static bool trans_SATR(DisasContext *ctx, arg_SATR *a) static bool cat3(trans_, name, _mr)(DisasContext *ctx, \ cat3(arg_, name, _mr) * a) \ { \ - TCGv val, mem; \ - mem = tcg_temp_new(); \ + TCGv_i32 val, mem; \ + mem = tcg_temp_new_i32(); \ val = rx_load_source(ctx, mem, a->ld, MO_32, a->rs); \ gen_helper_##op(cpu_regs[a->rd], tcg_env, \ cpu_regs[a->rd], val); \ @@ -1854,8 +1858,8 @@ static bool trans_SATR(DisasContext *ctx, arg_SATR *a) #define FCONVOP(name, op) \ static bool trans_##name(DisasContext *ctx, arg_##name * a) \ { \ - TCGv val, mem; \ - mem = tcg_temp_new(); \ + TCGv_i32 val, mem; \ + mem = tcg_temp_new_i32(); \ val = rx_load_source(ctx, mem, a->ld, MO_32, a->rs); \ gen_helper_##op(cpu_regs[a->rd], tcg_env, val); \ return true; \ @@ -1869,7 +1873,7 @@ FOP(FDIV, fdiv) /* fcmp #imm, rd */ static bool trans_FCMP_ir(DisasContext *ctx, arg_FCMP_ir * a) { - TCGv imm = tcg_constant_i32(li(ctx, 0)); + TCGv_i32 imm = tcg_constant_i32(li(ctx, 0)); gen_helper_fcmp(tcg_env, cpu_regs[a->rd], imm); return true; } @@ -1878,8 +1882,8 @@ static bool trans_FCMP_ir(DisasContext *ctx, arg_FCMP_ir * a) /* fcmp rs, rd */ static bool trans_FCMP_mr(DisasContext *ctx, arg_FCMP_mr *a) { - TCGv val, mem; - mem = tcg_temp_new(); + TCGv_i32 val, mem; + mem = tcg_temp_new_i32(); val = rx_load_source(ctx, mem, a->ld, MO_32, a->rs); gen_helper_fcmp(tcg_env, cpu_regs[a->rd], val); return true; @@ -1892,70 +1896,70 @@ FCONVOP(ROUND, round) /* itof dsp[rs], rd */ static bool trans_ITOF(DisasContext *ctx, arg_ITOF * a) { - TCGv val, mem; - mem = tcg_temp_new(); + TCGv_i32 val, mem; + mem = tcg_temp_new_i32(); val = rx_load_source(ctx, mem, a->ld, a->mi, a->rs); gen_helper_itof(cpu_regs[a->rd], tcg_env, val); return true; } -static void rx_bsetm(DisasContext *ctx, TCGv mem, TCGv mask) +static void rx_bsetm(DisasContext *ctx, TCGv_i32 mem, TCGv_i32 mask) { - TCGv val; - val = tcg_temp_new(); + TCGv_i32 val; + val = tcg_temp_new_i32(); rx_gen_ld(ctx, MO_8, val, mem); tcg_gen_or_i32(val, val, mask); rx_gen_st(ctx, MO_8, val, mem); } -static void rx_bclrm(DisasContext *ctx, TCGv mem, TCGv mask) +static void rx_bclrm(DisasContext *ctx, TCGv_i32 mem, TCGv_i32 mask) { - TCGv val; - val = tcg_temp_new(); + TCGv_i32 val; + val = tcg_temp_new_i32(); rx_gen_ld(ctx, MO_8, val, mem); tcg_gen_andc_i32(val, val, mask); rx_gen_st(ctx, MO_8, val, mem); } -static void rx_btstm(DisasContext *ctx, TCGv mem, TCGv mask) +static void rx_btstm(DisasContext *ctx, TCGv_i32 mem, TCGv_i32 mask) { - TCGv val; - val = tcg_temp_new(); + TCGv_i32 val; + val = tcg_temp_new_i32(); rx_gen_ld(ctx, MO_8, val, mem); tcg_gen_and_i32(val, val, mask); tcg_gen_setcondi_i32(TCG_COND_NE, cpu_psw_c, val, 0); tcg_gen_mov_i32(cpu_psw_z, cpu_psw_c); } -static void rx_bnotm(DisasContext *ctx, TCGv mem, TCGv mask) +static void rx_bnotm(DisasContext *ctx, TCGv_i32 mem, TCGv_i32 mask) { - TCGv val; - val = tcg_temp_new(); + TCGv_i32 val; + val = tcg_temp_new_i32(); rx_gen_ld(ctx, MO_8, val, mem); tcg_gen_xor_i32(val, val, mask); rx_gen_st(ctx, MO_8, val, mem); } -static void rx_bsetr(DisasContext *ctx, TCGv reg, TCGv mask) +static void rx_bsetr(DisasContext *ctx, TCGv_i32 reg, TCGv_i32 mask) { tcg_gen_or_i32(reg, reg, mask); } -static void rx_bclrr(DisasContext *ctx, TCGv reg, TCGv mask) +static void rx_bclrr(DisasContext *ctx, TCGv_i32 reg, TCGv_i32 mask) { tcg_gen_andc_i32(reg, reg, mask); } -static inline void rx_btstr(DisasContext *ctx, TCGv reg, TCGv mask) +static inline void rx_btstr(DisasContext *ctx, TCGv_i32 reg, TCGv_i32 mask) { - TCGv t0; - t0 = tcg_temp_new(); + TCGv_i32 t0; + t0 = tcg_temp_new_i32(); tcg_gen_and_i32(t0, reg, mask); tcg_gen_setcondi_i32(TCG_COND_NE, cpu_psw_c, t0, 0); tcg_gen_mov_i32(cpu_psw_z, cpu_psw_c); } -static inline void rx_bnotr(DisasContext *ctx, TCGv reg, TCGv mask) +static inline void rx_bnotr(DisasContext *ctx, TCGv_i32 reg, TCGv_i32 mask) { tcg_gen_xor_i32(reg, reg, mask); } @@ -1964,8 +1968,8 @@ static inline void rx_bnotr(DisasContext *ctx, TCGv reg, TCGv mask) static bool cat3(trans_, name, _im)(DisasContext *ctx, \ cat3(arg_, name, _im) * a) \ { \ - TCGv mask, mem, addr; \ - mem = tcg_temp_new(); \ + TCGv_i32 mask, mem, addr; \ + mem = tcg_temp_new_i32(); \ mask = tcg_constant_i32(1 << a->imm); \ addr = rx_index_addr(ctx, mem, a->ld, MO_8, a->rs); \ cat3(rx_, op, m)(ctx, addr, mask); \ @@ -1974,7 +1978,7 @@ static inline void rx_bnotr(DisasContext *ctx, TCGv reg, TCGv mask) static bool cat3(trans_, name, _ir)(DisasContext *ctx, \ cat3(arg_, name, _ir) * a) \ { \ - TCGv mask; \ + TCGv_i32 mask; \ mask = tcg_constant_i32(1 << a->imm); \ cat3(rx_, op, r)(ctx, cpu_regs[a->rd], mask); \ return true; \ @@ -1982,9 +1986,9 @@ static inline void rx_bnotr(DisasContext *ctx, TCGv reg, TCGv mask) static bool cat3(trans_, name, _rr)(DisasContext *ctx, \ cat3(arg_, name, _rr) * a) \ { \ - TCGv mask, b; \ - mask = tcg_temp_new(); \ - b = tcg_temp_new(); \ + TCGv_i32 mask, b; \ + mask = tcg_temp_new_i32(); \ + b = tcg_temp_new_i32(); \ tcg_gen_andi_i32(b, cpu_regs[a->rs], 31); \ tcg_gen_shl_i32(mask, tcg_constant_i32(1), b); \ cat3(rx_, op, r)(ctx, cpu_regs[a->rd], mask); \ @@ -1993,12 +1997,12 @@ static inline void rx_bnotr(DisasContext *ctx, TCGv reg, TCGv mask) static bool cat3(trans_, name, _rm)(DisasContext *ctx, \ cat3(arg_, name, _rm) * a) \ { \ - TCGv mask, mem, addr, b; \ - mask = tcg_temp_new(); \ - b = tcg_temp_new(); \ + TCGv_i32 mask, mem, addr, b; \ + mask = tcg_temp_new_i32(); \ + b = tcg_temp_new_i32(); \ tcg_gen_andi_i32(b, cpu_regs[a->rd], 7); \ tcg_gen_shl_i32(mask, tcg_constant_i32(1), b); \ - mem = tcg_temp_new(); \ + mem = tcg_temp_new_i32(); \ addr = rx_index_addr(ctx, mem, a->ld, MO_8, a->rs); \ cat3(rx_, op, m)(ctx, addr, mask); \ return true; \ @@ -2009,12 +2013,12 @@ BITOP(BCLR, bclr) BITOP(BTST, btst) BITOP(BNOT, bnot) -static inline void bmcnd_op(TCGv val, TCGCond cond, int pos) +static inline void bmcnd_op(TCGv_i32 val, TCGCond cond, int pos) { - TCGv bit; + TCGv_i32 bit; DisasCompare dc; - dc.temp = tcg_temp_new(); - bit = tcg_temp_new(); + dc.temp = tcg_temp_new_i32(); + bit = tcg_temp_new_i32(); psw_cond(&dc, cond); tcg_gen_andi_i32(val, val, ~(1 << pos)); tcg_gen_setcondi_i32(dc.cond, bit, dc.value, 0); @@ -2024,9 +2028,9 @@ static inline void bmcnd_op(TCGv val, TCGCond cond, int pos) /* bmcnd #imm, dsp[rd] */ static bool trans_BMCnd_im(DisasContext *ctx, arg_BMCnd_im *a) { - TCGv val, mem, addr; - val = tcg_temp_new(); - mem = tcg_temp_new(); + TCGv_i32 val, mem, addr; + val = tcg_temp_new_i32(); + mem = tcg_temp_new_i32(); addr = rx_index_addr(ctx, mem, a->ld, MO_8, a->rd); rx_gen_ld(ctx, MO_8, val, addr); bmcnd_op(val, a->cd, a->imm); @@ -2118,7 +2122,7 @@ static bool trans_MVTIPL(DisasContext *ctx, arg_MVTIPL *a) /* mvtc #imm, rd */ static bool trans_MVTC_i(DisasContext *ctx, arg_MVTC_i *a) { - TCGv imm; + TCGv_i32 imm; imm = tcg_constant_i32(a->imm); move_to_cr(ctx, imm, a->cr); @@ -2142,9 +2146,9 @@ static bool trans_MVFC(DisasContext *ctx, arg_MVFC *a) /* rtfi */ static bool trans_RTFI(DisasContext *ctx, arg_RTFI *a) { - TCGv psw; + TCGv_i32 psw; if (is_privileged(ctx, 1)) { - psw = tcg_temp_new(); + psw = tcg_temp_new_i32(); tcg_gen_mov_i32(cpu_pc, cpu_bpc); tcg_gen_mov_i32(psw, cpu_bpsw); gen_helper_set_psw_rte(tcg_env, psw); @@ -2156,9 +2160,9 @@ static bool trans_RTFI(DisasContext *ctx, arg_RTFI *a) /* rte */ static bool trans_RTE(DisasContext *ctx, arg_RTE *a) { - TCGv psw; + TCGv_i32 psw; if (is_privileged(ctx, 1)) { - psw = tcg_temp_new(); + psw = tcg_temp_new_i32(); pop(ctx, cpu_pc); pop(ctx, psw); gen_helper_set_psw_rte(tcg_env, psw); @@ -2179,7 +2183,7 @@ static bool trans_BRK(DisasContext *ctx, arg_BRK *a) /* int #imm */ static bool trans_INT(DisasContext *ctx, arg_INT *a) { - TCGv vec; + TCGv_i32 vec; tcg_debug_assert(a->imm < 0x100); vec = tcg_constant_i32(a->imm); From bec018f122677a2e53b7f914c20663986faf2cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 21:56:54 +0200 Subject: [PATCH 1648/1794] target/rx: Un-inline various helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rely on the linker to optimize at linking time. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251009200012.33650-1-philmd@linaro.org> --- target/rx/translate.c | 51 ++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/target/rx/translate.c b/target/rx/translate.c index 55285d8166f3f..ef865f14bf5ee 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -166,37 +166,34 @@ static void gen_goto_tb(DisasContext *dc, unsigned tb_slot_idx, vaddr dest) } /* generic load wrapper */ -static inline void rx_gen_ld(DisasContext *ctx, MemOp size, - TCGv_i32 reg, TCGv_i32 mem) +static void rx_gen_ld(DisasContext *ctx, MemOp size, TCGv_i32 reg, TCGv_i32 mem) { tcg_gen_qemu_ld_i32(reg, mem, 0, size | MO_SIGN | mo_endian(ctx)); } /* unsigned load wrapper */ -static inline void rx_gen_ldu(DisasContext *ctx, MemOp size, - TCGv_i32 reg, TCGv_i32 mem) +static void rx_gen_ldu(DisasContext *ctx, MemOp size, TCGv_i32 reg, TCGv_i32 mem) { tcg_gen_qemu_ld_i32(reg, mem, 0, size | mo_endian(ctx)); } /* generic store wrapper */ -static inline void rx_gen_st(DisasContext *ctx, MemOp size, - TCGv_i32 reg, TCGv_i32 mem) +static void rx_gen_st(DisasContext *ctx, MemOp size, TCGv_i32 reg, TCGv_i32 mem) { tcg_gen_qemu_st_i32(reg, mem, 0, size | mo_endian(ctx)); } /* [ri, rb] */ -static inline void rx_gen_regindex(DisasContext *ctx, TCGv_i32 mem, - int size, int ri, int rb) +static void rx_gen_regindex(DisasContext *ctx, TCGv_i32 mem, + int size, int ri, int rb) { tcg_gen_shli_i32(mem, cpu_regs[ri], size); tcg_gen_add_i32(mem, mem, cpu_regs[rb]); } /* dsp[reg] */ -static inline TCGv_i32 rx_index_addr(DisasContext *ctx, TCGv_i32 mem, - int ld, int size, int reg) +static TCGv_i32 rx_index_addr(DisasContext *ctx, TCGv_i32 mem, + int ld, int size, int reg) { uint32_t dsp; @@ -226,8 +223,8 @@ static inline MemOp mi_to_mop(unsigned mi) } /* load source operand */ -static inline TCGv_i32 rx_load_source(DisasContext *ctx, TCGv_i32 mem, - int ld, int mi, int rs) +static TCGv_i32 rx_load_source(DisasContext *ctx, TCGv_i32 mem, + int ld, int mi, int rs) { TCGv_i32 addr; MemOp mop; @@ -732,7 +729,7 @@ static bool trans_XCHG_mr(DisasContext *ctx, arg_XCHG_mr *a) return true; } -static inline void stcond(TCGCond cond, int rd, int imm) +static void stcond(TCGCond cond, int rd, int imm) { TCGv_i32 z; TCGv_i32 _imm; @@ -810,24 +807,24 @@ static bool trans_RTSD_irr(DisasContext *ctx, arg_RTSD_irr *a) typedef void (*op2fn)(TCGv_i32 ret, TCGv_i32 arg1); typedef void (*op3fn)(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -static inline void rx_gen_op_rr(op2fn opr, int dst, int src) +static void rx_gen_op_rr(op2fn opr, int dst, int src) { opr(cpu_regs[dst], cpu_regs[src]); } -static inline void rx_gen_op_rrr(op3fn opr, int dst, int src, int src2) +static void rx_gen_op_rrr(op3fn opr, int dst, int src, int src2) { opr(cpu_regs[dst], cpu_regs[src], cpu_regs[src2]); } -static inline void rx_gen_op_irr(op3fn opr, int dst, int src, uint32_t src2) +static void rx_gen_op_irr(op3fn opr, int dst, int src, uint32_t src2) { TCGv_i32 imm = tcg_constant_i32(src2); opr(cpu_regs[dst], cpu_regs[src], imm); } -static inline void rx_gen_op_mr(op3fn opr, DisasContext *ctx, - int dst, int src, int ld, int mi) +static void rx_gen_op_mr(op3fn opr, DisasContext *ctx, + int dst, int src, int ld, int mi) { TCGv_i32 val, mem; mem = tcg_temp_new_i32(); @@ -1342,8 +1339,8 @@ static bool trans_SHLL_rr(DisasContext *ctx, arg_SHLL_rr *a) return true; } -static inline void shiftr_imm(uint32_t rd, uint32_t rs, uint32_t imm, - unsigned int alith) +static void shiftr_imm(uint32_t rd, uint32_t rs, uint32_t imm, + unsigned int alith) { static void (* const gen_sXri[])(TCGv_i32 ret, TCGv_i32 arg1, int arg2) = { tcg_gen_shri_i32, tcg_gen_sari_i32, @@ -1362,7 +1359,7 @@ static inline void shiftr_imm(uint32_t rd, uint32_t rs, uint32_t imm, tcg_gen_mov_i32(cpu_psw_s, cpu_regs[rd]); } -static inline void shiftr_reg(uint32_t rd, uint32_t rs, unsigned int alith) +static void shiftr_reg(uint32_t rd, uint32_t rs, unsigned int alith) { TCGLabel *noshift, *done; TCGv_i32 count; @@ -1456,7 +1453,7 @@ static bool trans_RORC(DisasContext *ctx, arg_RORC *a) enum {ROTR = 0, ROTL = 1}; enum {ROT_IMM = 0, ROT_REG = 1}; -static inline void rx_rot(int ir, int dir, int rd, int src) +static void rx_rot(int ir, int dir, int rd, int src) { switch (dir) { case ROTL: @@ -1591,7 +1588,7 @@ static bool trans_BRA_l(DisasContext *ctx, arg_BRA_l *a) return true; } -static inline void rx_save_pc(DisasContext *ctx) +static void rx_save_pc(DisasContext *ctx) { TCGv_i32 pc = tcg_constant_i32(ctx->base.pc_next); push(ctx, pc); @@ -1950,7 +1947,7 @@ static void rx_bclrr(DisasContext *ctx, TCGv_i32 reg, TCGv_i32 mask) tcg_gen_andc_i32(reg, reg, mask); } -static inline void rx_btstr(DisasContext *ctx, TCGv_i32 reg, TCGv_i32 mask) +static void rx_btstr(DisasContext *ctx, TCGv_i32 reg, TCGv_i32 mask) { TCGv_i32 t0; t0 = tcg_temp_new_i32(); @@ -1959,7 +1956,7 @@ static inline void rx_btstr(DisasContext *ctx, TCGv_i32 reg, TCGv_i32 mask) tcg_gen_mov_i32(cpu_psw_z, cpu_psw_c); } -static inline void rx_bnotr(DisasContext *ctx, TCGv_i32 reg, TCGv_i32 mask) +static void rx_bnotr(DisasContext *ctx, TCGv_i32 reg, TCGv_i32 mask) { tcg_gen_xor_i32(reg, reg, mask); } @@ -2013,7 +2010,7 @@ BITOP(BCLR, bclr) BITOP(BTST, btst) BITOP(BNOT, bnot) -static inline void bmcnd_op(TCGv_i32 val, TCGCond cond, int pos) +static void bmcnd_op(TCGv_i32 val, TCGCond cond, int pos) { TCGv_i32 bit; DisasCompare dc; @@ -2054,7 +2051,7 @@ enum { PSW_U = 9, }; -static inline void clrsetpsw(DisasContext *ctx, int cb, int val) +static void clrsetpsw(DisasContext *ctx, int cb, int val) { if (cb < 8) { switch (cb) { From 6d9dad71265cda5888d4779681977c5b389730a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Oct 2025 14:46:49 +0200 Subject: [PATCH 1649/1794] target/s390x: Replace HOST_BIG_ENDIAN #ifdef with if() check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace preprocessor-time #ifdef with a compile-time check to ensure all code paths are built and tested. This reduces build-time configuration complexity and simplifies code maintainability. No functional change intended. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: David Hildenbrand Message-Id: <20251010134226.72221-15-philmd@linaro.org> --- target/s390x/tcg/translate.c | 6 +++--- target/s390x/tcg/translate_vx.c.inc | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index c7e8574438c25..ec9e5a07516af 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -258,9 +258,9 @@ static inline int vec_reg_offset(uint8_t reg, uint8_t enr, MemOp es) * 16 byte operations to handle it in a special way. */ g_assert(es <= MO_64); -#if !HOST_BIG_ENDIAN - offs ^= (8 - bytes); -#endif + if (!HOST_BIG_ENDIAN) { + offs ^= (8 - bytes); + } return offs + vec_full_reg_offset(reg); } diff --git a/target/s390x/tcg/translate_vx.c.inc b/target/s390x/tcg/translate_vx.c.inc index e073e5ad3aa58..f3b4b48ab7b76 100644 --- a/target/s390x/tcg/translate_vx.c.inc +++ b/target/s390x/tcg/translate_vx.c.inc @@ -175,9 +175,9 @@ static void get_vec_element_ptr_i64(TCGv_ptr ptr, uint8_t reg, TCGv_i64 enr, /* convert it to an element offset relative to tcg_env (vec_reg_offset() */ tcg_gen_shli_i64(tmp, tmp, es); -#if !HOST_BIG_ENDIAN - tcg_gen_xori_i64(tmp, tmp, 8 - NUM_VEC_ELEMENT_BYTES(es)); -#endif + if (!HOST_BIG_ENDIAN) { + tcg_gen_xori_i64(tmp, tmp, 8 - NUM_VEC_ELEMENT_BYTES(es)); + } tcg_gen_addi_i64(tmp, tmp, vec_full_reg_offset(reg)); /* generate the final ptr by adding tcg_env */ From 82a1e739010b3134862b27123684c26cd3b471e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 08:34:14 +0200 Subject: [PATCH 1650/1794] target/sh4: Convert CPUSH4State::sr register to uint32_t type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since its introduction in commit fdf9b3e831e the %SR register is a uint32_t type. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20251008064814.90520-2-philmd@linaro.org> --- target/sh4/cpu.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index c41ab70dd7c02..db27a693f1274 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -365,14 +365,14 @@ static inline int cpu_ptel_pr (uint32_t ptel) #define PTEA_TC (1 << 3) #define cpu_ptea_tc(ptea) (((ptea) & PTEA_TC) >> 3) -static inline target_ulong cpu_read_sr(CPUSH4State *env) +static inline uint32_t cpu_read_sr(CPUSH4State *env) { return env->sr | (env->sr_m << SR_M) | (env->sr_q << SR_Q) | (env->sr_t << SR_T); } -static inline void cpu_write_sr(CPUSH4State *env, target_ulong sr) +static inline void cpu_write_sr(CPUSH4State *env, uint32_t sr) { env->sr_m = (sr >> SR_M) & 1; env->sr_q = (sr >> SR_Q) & 1; From c954994968f8bb38087dcc6401560100ee3fc78c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 08:24:13 +0200 Subject: [PATCH 1651/1794] target/sh4: Remove target_ulong use in cpu_sh4_is_cached() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 852d481faf7 ("SH: Improve movca.l/ocbi emulation") helper_movcal() pass a uint32_t type to cpu_sh4_is_cached(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20251008064814.90520-3-philmd@linaro.org> --- target/sh4/cpu.h | 2 +- target/sh4/helper.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index db27a693f1274..b0759010c47d8 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -277,7 +277,7 @@ void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, hwaddr addr, uint32_t mem_value); #endif -int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr); +int cpu_sh4_is_cached(CPUSH4State *env, uint32_t addr); void cpu_load_tlb(CPUSH4State * env); diff --git a/target/sh4/helper.c b/target/sh4/helper.c index 1744ef0e6d876..4f1e2072296db 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -47,7 +47,7 @@ #if defined(CONFIG_USER_ONLY) -int cpu_sh4_is_cached(CPUSH4State *env, target_ulong addr) +int cpu_sh4_is_cached(CPUSH4State *env, uint32_t addr) { /* For user mode, only U0 area is cacheable. */ return !(addr & 0x80000000); @@ -735,7 +735,7 @@ void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, hwaddr addr, } } -int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr) +int cpu_sh4_is_cached(CPUSH4State *env, uint32_t addr) { int n; int use_asid = !(env->mmucr & MMUCR_SV) || !(env->sr & (1u << SR_MD)); From b0469ec667cf8d507dd4b23b6ed9e7e7082abbeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 08:28:13 +0200 Subject: [PATCH 1652/1794] target/sh4: Use hwaddr type for hardware addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CPUClass::get_phys_page_debug() handler returns a 'hwaddr' type. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20251008064814.90520-4-philmd@linaro.org> --- target/sh4/helper.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/sh4/helper.c b/target/sh4/helper.c index 4f1e2072296db..55ab1dc9947b6 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -325,7 +325,7 @@ static int find_utlb_entry(CPUSH4State * env, target_ulong address, int use_asid MMU_ITLB_MULTIPLE, MMU_ITLB_VIOLATION, MMU_IADDR_ERROR, MMU_DADDR_ERROR_READ, MMU_DADDR_ERROR_WRITE. */ -static int get_mmu_address(CPUSH4State * env, target_ulong * physical, +static int get_mmu_address(CPUSH4State *env, hwaddr *physical, int *prot, target_ulong address, MMUAccessType access_type) { @@ -392,7 +392,7 @@ static int get_mmu_address(CPUSH4State * env, target_ulong * physical, return n; } -static int get_physical_address(CPUSH4State * env, target_ulong * physical, +static int get_physical_address(CPUSH4State *env, hwaddr* physical, int *prot, target_ulong address, MMUAccessType access_type) { @@ -433,7 +433,7 @@ static int get_physical_address(CPUSH4State * env, target_ulong * physical, hwaddr superh_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { - target_ulong physical; + hwaddr physical; int prot; if (get_physical_address(cpu_env(cs), &physical, &prot, addr, MMU_DATA_LOAD) @@ -800,7 +800,7 @@ bool superh_cpu_tlb_fill(CPUState *cs, vaddr address, int size, CPUSH4State *env = cpu_env(cs); int ret; - target_ulong physical; + hwaddr physical; int prot; ret = get_physical_address(env, &physical, &prot, address, access_type); From 0edd1789c738af6ce825106d7f331fedf546ee77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 08:32:47 +0200 Subject: [PATCH 1653/1794] target/sh4: Remove target_ulong uses in superh_cpu_get_phys_page_debug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CPUClass::get_phys_page_debug() handler takes a 'vaddr' address type since commit 00b941e581b ("cpu: Turn cpu_get_phys_page_debug() into a CPUClass hook"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20251008064814.90520-5-philmd@linaro.org> --- target/sh4/helper.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/sh4/helper.c b/target/sh4/helper.c index 55ab1dc9947b6..f5c37c2d80d93 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -231,11 +231,11 @@ static int itlb_replacement(CPUSH4State * env) /* Find the corresponding entry in the right TLB Return entry, MMU_DTLB_MISS or MMU_DTLB_MULTIPLE */ -static int find_tlb_entry(CPUSH4State * env, target_ulong address, +static int find_tlb_entry(CPUSH4State *env, vaddr address, tlb_t * entries, uint8_t nbtlb, int use_asid) { int match = MMU_DTLB_MISS; - uint32_t start, end; + vaddr start, end; uint8_t asid; int i; @@ -291,7 +291,7 @@ static int copy_utlb_entry_itlb(CPUSH4State *env, int utlb) /* Find itlb entry Return entry, MMU_ITLB_MISS, MMU_ITLB_MULTIPLE or MMU_DTLB_MULTIPLE */ -static int find_itlb_entry(CPUSH4State * env, target_ulong address, +static int find_itlb_entry(CPUSH4State *env, vaddr address, int use_asid) { int e; @@ -309,7 +309,7 @@ static int find_itlb_entry(CPUSH4State * env, target_ulong address, /* Find utlb entry Return entry, MMU_DTLB_MISS, MMU_DTLB_MULTIPLE */ -static int find_utlb_entry(CPUSH4State * env, target_ulong address, int use_asid) +static int find_utlb_entry(CPUSH4State *env, vaddr address, int use_asid) { /* per utlb access */ increment_urc(env); @@ -326,7 +326,7 @@ static int find_utlb_entry(CPUSH4State * env, target_ulong address, int use_asid MMU_IADDR_ERROR, MMU_DADDR_ERROR_READ, MMU_DADDR_ERROR_WRITE. */ static int get_mmu_address(CPUSH4State *env, hwaddr *physical, - int *prot, target_ulong address, + int *prot, vaddr address, MMUAccessType access_type) { int use_asid, n; @@ -393,7 +393,7 @@ static int get_mmu_address(CPUSH4State *env, hwaddr *physical, } static int get_physical_address(CPUSH4State *env, hwaddr* physical, - int *prot, target_ulong address, + int *prot, vaddr address, MMUAccessType access_type) { /* P1, P2 and P4 areas do not use translation */ From 42c90609b8fd405b34e1d05fb8495068521759b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 08:30:09 +0200 Subject: [PATCH 1654/1794] target/sh4: Use vaddr type for TLB virtual addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tlb_flush_page() expects a vaddr type since commit 732d548732e ("accel: Replace target_ulong in tlb_*()"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20251008064814.90520-6-philmd@linaro.org> --- target/sh4/helper.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/sh4/helper.c b/target/sh4/helper.c index f5c37c2d80d93..3b18a320b86db 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -452,7 +452,7 @@ void cpu_load_tlb(CPUSH4State * env) if (entry->v) { /* Overwriting valid entry in utlb. */ - target_ulong address = entry->vpn << 10; + vaddr address = entry->vpn << 10; tlb_flush_page(cs, address); } @@ -528,7 +528,7 @@ void cpu_sh4_write_mmaped_itlb_addr(CPUSH4State *s, hwaddr addr, tlb_t * entry = &s->itlb[index]; if (entry->v) { /* Overwriting valid entry in itlb. */ - target_ulong address = entry->vpn << 10; + vaddr address = entry->vpn << 10; tlb_flush_page(env_cpu(s), address); } entry->asid = asid; @@ -570,7 +570,7 @@ void cpu_sh4_write_mmaped_itlb_data(CPUSH4State *s, hwaddr addr, /* ITLB Data Array 1 */ if (entry->v) { /* Overwriting valid entry in utlb. */ - target_ulong address = entry->vpn << 10; + vaddr address = entry->vpn << 10; tlb_flush_page(env_cpu(s), address); } entry->ppn = (mem_value & 0x1ffffc00) >> 10; @@ -665,7 +665,7 @@ void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, hwaddr addr, CPUState *cs = env_cpu(s); /* Overwriting valid entry in utlb. */ - target_ulong address = entry->vpn << 10; + vaddr address = entry->vpn << 10; tlb_flush_page(cs, address); } entry->asid = asid; @@ -716,7 +716,7 @@ void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, hwaddr addr, /* UTLB Data Array 1 */ if (entry->v) { /* Overwriting valid entry in utlb. */ - target_ulong address = entry->vpn << 10; + vaddr address = entry->vpn << 10; tlb_flush_page(env_cpu(s), address); } entry->ppn = (mem_value & 0x1ffffc00) >> 10; From 90470a5fcc57b29703fbab5e3ff9c05d420e39f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 08:22:58 +0200 Subject: [PATCH 1655/1794] target/sh4: Remove target_ulong use in gen_goto_tb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit translator_use_goto_tb() expects a vaddr type since commit b1c09220b4c ("accel/tcg: Replace target_ulong with vaddr in translator_*()"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20251008064814.90520-7-philmd@linaro.org> --- target/sh4/translate.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 137b75a4fb2e1..b3ae0a3814c7e 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -223,7 +223,7 @@ static inline bool use_exit_tb(DisasContext *ctx) return (ctx->tbflags & TB_FLAG_GUSA_EXCLUSIVE) != 0; } -static bool use_goto_tb(DisasContext *ctx, target_ulong dest) +static bool use_goto_tb(DisasContext *ctx, vaddr dest) { if (use_exit_tb(ctx)) { return false; @@ -231,8 +231,7 @@ static bool use_goto_tb(DisasContext *ctx, target_ulong dest) return translator_use_goto_tb(&ctx->base, dest); } -static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_idx, - target_ulong dest) +static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_idx, vaddr dest) { if (use_goto_tb(ctx, dest)) { tcg_gen_goto_tb(tb_slot_idx); @@ -268,7 +267,7 @@ static void gen_jump(DisasContext * ctx) } /* Immediate conditional jump (bt or bf) */ -static void gen_conditional_jump(DisasContext *ctx, target_ulong dest, +static void gen_conditional_jump(DisasContext *ctx, vaddr dest, bool jump_if_true) { TCGLabel *l1 = gen_new_label(); From 7b2325470ebf9f1414ef4782f7560156e8241f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Oct 2025 16:13:32 +0200 Subject: [PATCH 1656/1794] target/sparc: Reduce inclusions of 'exec/cpu-common.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only 2 files require declarations from "exec/cpu-common.h". Include it there once, instead than polluting all files including "cpu.h". Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20251002145742.75624-7-philmd@linaro.org> Reviewed-by: Manos Pitsidianakis --- target/sparc/cpu.h | 1 - target/sparc/helper.c | 1 + target/sparc/int64_helper.c | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 31cb3d97eb1b9..7169a502432da 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -3,7 +3,6 @@ #include "qemu/bswap.h" #include "cpu-qom.h" -#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" diff --git a/target/sparc/helper.c b/target/sparc/helper.c index 9163b9d46ad01..c5d88de37c994 100644 --- a/target/sparc/helper.c +++ b/target/sparc/helper.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "qemu/timer.h" #include "qemu/host-utils.h" +#include "exec/cpu-common.h" #include "exec/helper-proto.h" void cpu_raise_exception_ra(CPUSPARCState *env, int tt, uintptr_t ra) diff --git a/target/sparc/int64_helper.c b/target/sparc/int64_helper.c index 23adda4cad7bc..96ef81c26cd12 100644 --- a/target/sparc/int64_helper.c +++ b/target/sparc/int64_helper.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "cpu.h" +#include "exec/cpu-common.h" #include "exec/helper-proto.h" #include "exec/log.h" #include "trace.h" From 809b460f30501fac008c13f23e6c0c127b8260bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Oct 2025 05:19:33 +0200 Subject: [PATCH 1657/1794] target/tricore: Remove target_ulong use in gen_goto_tb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit translator_use_goto_tb() expects a vaddr type since commit b1c09220b4c ("accel/tcg: Replace target_ulong with vaddr in translator_*()"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251010052141.42460-2-philmd@linaro.org> --- target/tricore/translate.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 7c6e3095971d6..dd09f0651f5c7 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -72,7 +72,8 @@ static const char *regnames_d[] = { typedef struct DisasContext { DisasContextBase base; - target_ulong pc_succ_insn; + + vaddr pc_succ_insn; uint32_t opcode; /* Routine used to access memory */ int mem_idx; @@ -2811,13 +2812,12 @@ static void gen_calc_usb_mulr_h(TCGv arg) /* helpers for generating program flow micro-ops */ -static inline void gen_save_pc(target_ulong pc) +static inline void gen_save_pc(vaddr pc) { tcg_gen_movi_tl(cpu_PC, pc); } -static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_index, - target_ulong dest) +static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_index, vaddr dest) { if (translator_use_goto_tb(&ctx->base, dest)) { tcg_gen_goto_tb(tb_slot_index); From 0d5f95425614ba5281177f76ab438756d8213317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Oct 2025 05:23:57 +0200 Subject: [PATCH 1658/1794] target/tricore: Replace target_ulong -> vaddr with tlb_fill() callees MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tlb_fill() provides a vaddr type since commit 68d6eee73c ("target/tricore: Convert to CPUClass::tlb_fill"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251010052141.42460-3-philmd@linaro.org> --- target/tricore/helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/tricore/helper.c b/target/tricore/helper.c index e4c53d453dddd..7574111c87df8 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -35,7 +35,7 @@ enum { }; static int get_physical_address(CPUTriCoreState *env, hwaddr *physical, - int *prot, target_ulong address, + int *prot, vaddr address, MMUAccessType access_type, int mmu_idx) { int ret = TLBRET_MATCH; @@ -61,7 +61,7 @@ hwaddr tricore_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) } /* TODO: Add exception support */ -static void raise_mmu_exception(CPUTriCoreState *env, target_ulong address, +static void raise_mmu_exception(CPUTriCoreState *env, vaddr address, int rw, int tlb_error) { } From a15e899626893b5201b7b307090148ca7ff25e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Oct 2025 05:44:01 +0200 Subject: [PATCH 1659/1794] target/tricore: Remove target_ulong use in translate_insn() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 85c19af63e7 ("include/exec: Use vaddr in DisasContextBase for virtual addresses") the DisasContextBase::pc_first field is a vaddr type. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251010052141.42460-4-philmd@linaro.org> --- target/tricore/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/tricore/translate.c b/target/tricore/translate.c index dd09f0651f5c7..4b769b204ab2b 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -8414,7 +8414,7 @@ static void tricore_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) ctx->base.pc_next = ctx->pc_succ_insn; if (ctx->base.is_jmp == DISAS_NEXT) { - target_ulong page_start; + vaddr page_start; page_start = ctx->base.pc_first & TARGET_PAGE_MASK; if (ctx->base.pc_next - page_start >= TARGET_PAGE_SIZE From 44e2b68d275340c23f21f37f7c44e25c1b7543c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Oct 2025 05:47:05 +0200 Subject: [PATCH 1660/1794] target/tricore: Remove target_ulong use in gen_addi_d() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Callers pass either int32_t or int16_t. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251010052141.42460-5-philmd@linaro.org> --- target/tricore/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 4b769b204ab2b..116f45135bbed 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -1308,7 +1308,7 @@ gen_msubui64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, gen_msubu64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); } -static inline void gen_addi_d(TCGv ret, TCGv r1, target_ulong r2) +static inline void gen_addi_d(TCGv ret, TCGv r1, int32_t r2) { TCGv temp = tcg_constant_i32(r2); gen_add_d(ret, r1, temp); From 30257dcd2b0b8aeefc0c4b8ee10524037111a0f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Oct 2025 05:48:50 +0200 Subject: [PATCH 1661/1794] target/tricore: Remove unnecessary cast to target_ulong 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: <20251010052141.42460-6-philmd@linaro.org> --- target/tricore/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 4f035b6f76849..04319e107ba2c 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -37,7 +37,7 @@ static const gchar *tricore_gdb_arch_name(CPUState *cs) static void tricore_cpu_set_pc(CPUState *cs, vaddr value) { - cpu_env(cs)->PC = value & ~(target_ulong)1; + cpu_env(cs)->PC = value & ~1; } static vaddr tricore_cpu_get_pc(CPUState *cs) From c558aa942119afb0c1a852a85be81b8f6e2c8b5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Oct 2025 05:55:41 +0200 Subject: [PATCH 1662/1794] target/tricore: Replace target_ulong -> uint32_t in op_helper.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TriCore target is only built as 32-bit: $ git grep TARGET_LONG_BITS configs/targets/tricore-* configs/targets/tricore-softmmu.mak:2:TARGET_LONG_BITS=32 Therefore target_ulong type always expands to uint32_t. This is a mechanical replacement. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251010052141.42460-7-philmd@linaro.org> --- target/tricore/op_helper.c | 219 +++++++++++++++++-------------------- 1 file changed, 103 insertions(+), 116 deletions(-) diff --git a/target/tricore/op_helper.c b/target/tricore/op_helper.c index 9910c13f4b50b..610f148a237d3 100644 --- a/target/tricore/op_helper.c +++ b/target/tricore/op_helper.c @@ -149,15 +149,15 @@ static uint32_t ssov32(CPUTriCoreState *env, int64_t arg) if (arg > max_pos) { env->PSW_USB_V = (1 << 31); env->PSW_USB_SV = (1 << 31); - ret = (target_ulong)max_pos; + ret = (uint32_t)max_pos; } else { if (arg < max_neg) { env->PSW_USB_V = (1 << 31); env->PSW_USB_SV = (1 << 31); - ret = (target_ulong)max_neg; + ret = (uint32_t)max_neg; } else { env->PSW_USB_V = 0; - ret = (target_ulong)arg; + ret = (uint32_t)arg; } } env->PSW_USB_AV = arg ^ arg * 2u; @@ -172,10 +172,10 @@ static uint32_t suov32_pos(CPUTriCoreState *env, uint64_t arg) if (arg > max_pos) { env->PSW_USB_V = (1 << 31); env->PSW_USB_SV = (1 << 31); - ret = (target_ulong)max_pos; + ret = (uint32_t)max_pos; } else { env->PSW_USB_V = 0; - ret = (target_ulong)arg; + ret = (uint32_t)arg; } env->PSW_USB_AV = arg ^ arg * 2u; env->PSW_USB_SAV |= env->PSW_USB_AV; @@ -192,7 +192,7 @@ static uint32_t suov32_neg(CPUTriCoreState *env, int64_t arg) ret = 0; } else { env->PSW_USB_V = 0; - ret = (target_ulong)arg; + ret = (uint32_t)arg; } env->PSW_USB_AV = arg ^ arg * 2u; env->PSW_USB_SAV |= env->PSW_USB_AV; @@ -260,8 +260,7 @@ static uint32_t suov16(CPUTriCoreState *env, int32_t hw0, int32_t hw1) return (hw0 & 0xffff) | (hw1 << 16); } -target_ulong helper_add_ssov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2) +uint32_t helper_add_ssov(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int64_t t1 = sextract64(r1, 0, 32); int64_t t2 = sextract64(r2, 0, 32); @@ -294,8 +293,7 @@ uint64_t helper_add64_ssov(CPUTriCoreState *env, uint64_t r1, uint64_t r2) return result; } -target_ulong helper_add_h_ssov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2) +uint32_t helper_add_h_ssov(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int32_t ret_hw0, ret_hw1; @@ -397,8 +395,7 @@ uint32_t helper_addsur_h_ssov(CPUTriCoreState *env, uint64_t r1, uint32_t r2_l, } -target_ulong helper_add_suov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2) +uint32_t helper_add_suov(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int64_t t1 = extract64(r1, 0, 32); int64_t t2 = extract64(r2, 0, 32); @@ -406,8 +403,7 @@ target_ulong helper_add_suov(CPUTriCoreState *env, target_ulong r1, return suov32_pos(env, result); } -target_ulong helper_add_h_suov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2) +uint32_t helper_add_h_suov(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int32_t ret_hw0, ret_hw1; @@ -416,8 +412,7 @@ target_ulong helper_add_h_suov(CPUTriCoreState *env, target_ulong r1, return suov16(env, ret_hw0, ret_hw1); } -target_ulong helper_sub_ssov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2) +uint32_t helper_sub_ssov(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int64_t t1 = sextract64(r1, 0, 32); int64_t t2 = sextract64(r2, 0, 32); @@ -450,8 +445,7 @@ uint64_t helper_sub64_ssov(CPUTriCoreState *env, uint64_t r1, uint64_t r2) return result; } -target_ulong helper_sub_h_ssov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2) +uint32_t helper_sub_h_ssov(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int32_t ret_hw0, ret_hw1; @@ -552,8 +546,7 @@ uint32_t helper_subadr_h_ssov(CPUTriCoreState *env, uint64_t r1, uint32_t r2_l, return (result1 & 0xffff0000ULL) | ((result0 >> 16) & 0xffffULL); } -target_ulong helper_sub_suov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2) +uint32_t helper_sub_suov(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int64_t t1 = extract64(r1, 0, 32); int64_t t2 = extract64(r2, 0, 32); @@ -561,8 +554,7 @@ target_ulong helper_sub_suov(CPUTriCoreState *env, target_ulong r1, return suov32_neg(env, result); } -target_ulong helper_sub_h_suov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2) +uint32_t helper_sub_h_suov(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int32_t ret_hw0, ret_hw1; @@ -571,8 +563,7 @@ target_ulong helper_sub_h_suov(CPUTriCoreState *env, target_ulong r1, return suov16(env, ret_hw0, ret_hw1); } -target_ulong helper_mul_ssov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2) +uint32_t helper_mul_ssov(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int64_t t1 = sextract64(r1, 0, 32); int64_t t2 = sextract64(r2, 0, 32); @@ -580,8 +571,7 @@ target_ulong helper_mul_ssov(CPUTriCoreState *env, target_ulong r1, return ssov32(env, result); } -target_ulong helper_mul_suov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2) +uint32_t helper_mul_suov(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int64_t t1 = extract64(r1, 0, 32); int64_t t2 = extract64(r2, 0, 32); @@ -590,8 +580,7 @@ target_ulong helper_mul_suov(CPUTriCoreState *env, target_ulong r1, return suov32_pos(env, result); } -target_ulong helper_sha_ssov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2) +uint32_t helper_sha_ssov(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int64_t t1 = sextract64(r1, 0, 32); int32_t t2 = sextract64(r2, 0, 6); @@ -606,14 +595,14 @@ target_ulong helper_sha_ssov(CPUTriCoreState *env, target_ulong r1, return ssov32(env, result); } -uint32_t helper_abs_ssov(CPUTriCoreState *env, target_ulong r1) +uint32_t helper_abs_ssov(CPUTriCoreState *env, uint32_t r1) { - target_ulong result; + uint32_t result; result = ((int32_t)r1 >= 0) ? r1 : (0 - r1); return ssov32(env, result); } -uint32_t helper_abs_h_ssov(CPUTriCoreState *env, target_ulong r1) +uint32_t helper_abs_h_ssov(CPUTriCoreState *env, uint32_t r1) { int32_t ret_h0, ret_h1; @@ -626,8 +615,7 @@ uint32_t helper_abs_h_ssov(CPUTriCoreState *env, target_ulong r1) return ssov16(env, ret_h0, ret_h1); } -target_ulong helper_absdif_ssov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2) +uint32_t helper_absdif_ssov(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int64_t t1 = sextract64(r1, 0, 32); int64_t t2 = sextract64(r2, 0, 32); @@ -641,8 +629,7 @@ target_ulong helper_absdif_ssov(CPUTriCoreState *env, target_ulong r1, return ssov32(env, result); } -uint32_t helper_absdif_h_ssov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2) +uint32_t helper_absdif_h_ssov(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int32_t t1, t2; int32_t ret_h0, ret_h1; @@ -666,8 +653,8 @@ uint32_t helper_absdif_h_ssov(CPUTriCoreState *env, target_ulong r1, return ssov16(env, ret_h0, ret_h1); } -target_ulong helper_madd32_ssov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2, target_ulong r3) +uint32_t helper_madd32_ssov(CPUTriCoreState *env, uint32_t r1, + uint32_t r2, uint32_t r3) { int64_t t1 = sextract64(r1, 0, 32); int64_t t2 = sextract64(r2, 0, 32); @@ -678,8 +665,8 @@ target_ulong helper_madd32_ssov(CPUTriCoreState *env, target_ulong r1, return ssov32(env, result); } -target_ulong helper_madd32_suov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2, target_ulong r3) +uint32_t helper_madd32_suov(CPUTriCoreState *env, uint32_t r1, + uint32_t r2, uint32_t r3) { uint64_t t1 = extract64(r1, 0, 32); uint64_t t2 = extract64(r2, 0, 32); @@ -690,8 +677,8 @@ target_ulong helper_madd32_suov(CPUTriCoreState *env, target_ulong r1, return suov32_pos(env, result); } -uint64_t helper_madd64_ssov(CPUTriCoreState *env, target_ulong r1, - uint64_t r2, target_ulong r3) +uint64_t helper_madd64_ssov(CPUTriCoreState *env, uint32_t r1, + uint64_t r2, uint32_t r3) { uint64_t ret, ovf; int64_t t1 = sextract64(r1, 0, 32); @@ -848,8 +835,8 @@ uint32_t helper_maddr_q_ssov(CPUTriCoreState *env, uint32_t r1, uint32_t r2, return ret & 0xffff0000ll; } -uint64_t helper_madd64_suov(CPUTriCoreState *env, target_ulong r1, - uint64_t r2, target_ulong r3) +uint64_t helper_madd64_suov(CPUTriCoreState *env, uint32_t r1, + uint64_t r2, uint32_t r3) { uint64_t ret, mul; uint64_t t1 = extract64(r1, 0, 32); @@ -873,8 +860,8 @@ uint64_t helper_madd64_suov(CPUTriCoreState *env, target_ulong r1, return ret; } -target_ulong helper_msub32_ssov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2, target_ulong r3) +uint32_t helper_msub32_ssov(CPUTriCoreState *env, uint32_t r1, + uint32_t r2, uint32_t r3) { int64_t t1 = sextract64(r1, 0, 32); int64_t t2 = sextract64(r2, 0, 32); @@ -885,8 +872,8 @@ target_ulong helper_msub32_ssov(CPUTriCoreState *env, target_ulong r1, return ssov32(env, result); } -target_ulong helper_msub32_suov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2, target_ulong r3) +uint32_t helper_msub32_suov(CPUTriCoreState *env, uint32_t r1, + uint32_t r2, uint32_t r3) { uint64_t t1 = extract64(r1, 0, 32); uint64_t t2 = extract64(r2, 0, 32); @@ -912,8 +899,8 @@ target_ulong helper_msub32_suov(CPUTriCoreState *env, target_ulong r1, return result; } -uint64_t helper_msub64_ssov(CPUTriCoreState *env, target_ulong r1, - uint64_t r2, target_ulong r3) +uint64_t helper_msub64_ssov(CPUTriCoreState *env, uint32_t r1, + uint64_t r2, uint32_t r3) { uint64_t ret, ovf; int64_t t1 = sextract64(r1, 0, 32); @@ -944,8 +931,8 @@ uint64_t helper_msub64_ssov(CPUTriCoreState *env, target_ulong r1, return ret; } -uint64_t helper_msub64_suov(CPUTriCoreState *env, target_ulong r1, - uint64_t r2, target_ulong r3) +uint64_t helper_msub64_suov(CPUTriCoreState *env, uint32_t r1, + uint64_t r2, uint32_t r3) { uint64_t ret, mul; uint64_t t1 = extract64(r1, 0, 32); @@ -1097,7 +1084,7 @@ uint32_t helper_msubr_q_ssov(CPUTriCoreState *env, uint32_t r1, uint32_t r2, return ret & 0xffff0000ll; } -uint32_t helper_abs_b(CPUTriCoreState *env, target_ulong arg) +uint32_t helper_abs_b(CPUTriCoreState *env, uint32_t arg) { int32_t b, i; int32_t ovf = 0; @@ -1120,7 +1107,7 @@ uint32_t helper_abs_b(CPUTriCoreState *env, target_ulong arg) return ret; } -uint32_t helper_abs_h(CPUTriCoreState *env, target_ulong arg) +uint32_t helper_abs_h(CPUTriCoreState *env, uint32_t arg) { int32_t h, i; int32_t ovf = 0; @@ -1143,7 +1130,7 @@ uint32_t helper_abs_h(CPUTriCoreState *env, target_ulong arg) return ret; } -uint32_t helper_absdif_b(CPUTriCoreState *env, target_ulong r1, target_ulong r2) +uint32_t helper_absdif_b(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int32_t b, i; int32_t extr_r2; @@ -1167,7 +1154,7 @@ uint32_t helper_absdif_b(CPUTriCoreState *env, target_ulong r1, target_ulong r2) return ret; } -uint32_t helper_absdif_h(CPUTriCoreState *env, target_ulong r1, target_ulong r2) +uint32_t helper_absdif_h(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int32_t h, i; int32_t extr_r2; @@ -1296,7 +1283,7 @@ uint32_t helper_maddr_q(CPUTriCoreState *env, uint32_t r1, uint32_t r2, return ret & 0xffff0000ll; } -uint32_t helper_add_b(CPUTriCoreState *env, target_ulong r1, target_ulong r2) +uint32_t helper_add_b(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int32_t b, i; int32_t extr_r1, extr_r2; @@ -1322,7 +1309,7 @@ uint32_t helper_add_b(CPUTriCoreState *env, target_ulong r1, target_ulong r2) return ret; } -uint32_t helper_add_h(CPUTriCoreState *env, target_ulong r1, target_ulong r2) +uint32_t helper_add_h(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int32_t h, i; int32_t extr_r1, extr_r2; @@ -1451,7 +1438,7 @@ uint32_t helper_msubr_q(CPUTriCoreState *env, uint32_t r1, uint32_t r2, return ret & 0xffff0000ll; } -uint32_t helper_sub_b(CPUTriCoreState *env, target_ulong r1, target_ulong r2) +uint32_t helper_sub_b(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int32_t b, i; int32_t extr_r1, extr_r2; @@ -1477,7 +1464,7 @@ uint32_t helper_sub_b(CPUTriCoreState *env, target_ulong r1, target_ulong r2) return ret; } -uint32_t helper_sub_h(CPUTriCoreState *env, target_ulong r1, target_ulong r2) +uint32_t helper_sub_h(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int32_t h, i; int32_t extr_r1, extr_r2; @@ -1502,7 +1489,7 @@ uint32_t helper_sub_h(CPUTriCoreState *env, target_ulong r1, target_ulong r2) return ret; } -uint32_t helper_eq_b(target_ulong r1, target_ulong r2) +uint32_t helper_eq_b(uint32_t r1, uint32_t r2) { uint32_t ret, msk; int32_t i; @@ -1519,7 +1506,7 @@ uint32_t helper_eq_b(target_ulong r1, target_ulong r2) return ret; } -uint32_t helper_eq_h(target_ulong r1, target_ulong r2) +uint32_t helper_eq_h(uint32_t r1, uint32_t r2) { int32_t ret = 0; @@ -1534,7 +1521,7 @@ uint32_t helper_eq_h(target_ulong r1, target_ulong r2) return ret; } -uint32_t helper_eqany_b(target_ulong r1, target_ulong r2) +uint32_t helper_eqany_b(uint32_t r1, uint32_t r2) { int32_t i; uint32_t ret = 0; @@ -1546,7 +1533,7 @@ uint32_t helper_eqany_b(target_ulong r1, target_ulong r2) return ret; } -uint32_t helper_eqany_h(target_ulong r1, target_ulong r2) +uint32_t helper_eqany_h(uint32_t r1, uint32_t r2) { uint32_t ret; @@ -1556,7 +1543,7 @@ uint32_t helper_eqany_h(target_ulong r1, target_ulong r2) return ret; } -uint32_t helper_lt_b(target_ulong r1, target_ulong r2) +uint32_t helper_lt_b(uint32_t r1, uint32_t r2) { int32_t i; uint32_t ret = 0; @@ -1570,7 +1557,7 @@ uint32_t helper_lt_b(target_ulong r1, target_ulong r2) return ret; } -uint32_t helper_lt_bu(target_ulong r1, target_ulong r2) +uint32_t helper_lt_bu(uint32_t r1, uint32_t r2) { int32_t i; uint32_t ret = 0; @@ -1584,7 +1571,7 @@ uint32_t helper_lt_bu(target_ulong r1, target_ulong r2) return ret; } -uint32_t helper_lt_h(target_ulong r1, target_ulong r2) +uint32_t helper_lt_h(uint32_t r1, uint32_t r2) { uint32_t ret = 0; @@ -1599,7 +1586,7 @@ uint32_t helper_lt_h(target_ulong r1, target_ulong r2) return ret; } -uint32_t helper_lt_hu(target_ulong r1, target_ulong r2) +uint32_t helper_lt_hu(uint32_t r1, uint32_t r2) { uint32_t ret = 0; @@ -1615,7 +1602,7 @@ uint32_t helper_lt_hu(target_ulong r1, target_ulong r2) } #define EXTREMA_H_B(name, op) \ -uint32_t helper_##name ##_b(target_ulong r1, target_ulong r2) \ +uint32_t helper_##name ##_b(uint32_t r1, uint32_t r2) \ { \ int32_t i, extr_r1, extr_r2; \ uint32_t ret = 0; \ @@ -1629,7 +1616,7 @@ uint32_t helper_##name ##_b(target_ulong r1, target_ulong r2) \ return ret; \ } \ \ -uint32_t helper_##name ##_bu(target_ulong r1, target_ulong r2)\ +uint32_t helper_##name ##_bu(uint32_t r1, uint32_t r2) \ { \ int32_t i; \ uint32_t extr_r1, extr_r2; \ @@ -1644,7 +1631,7 @@ uint32_t helper_##name ##_bu(target_ulong r1, target_ulong r2)\ return ret; \ } \ \ -uint32_t helper_##name ##_h(target_ulong r1, target_ulong r2) \ +uint32_t helper_##name ##_h(uint32_t r1, uint32_t r2) \ { \ int32_t extr_r1, extr_r2; \ uint32_t ret = 0; \ @@ -1662,7 +1649,7 @@ uint32_t helper_##name ##_h(target_ulong r1, target_ulong r2) \ return ret; \ } \ \ -uint32_t helper_##name ##_hu(target_ulong r1, target_ulong r2)\ +uint32_t helper_##name ##_hu(uint32_t r1, uint32_t r2) \ { \ uint32_t extr_r1, extr_r2; \ uint32_t ret = 0; \ @@ -1729,7 +1716,7 @@ EXTREMA_H_B(min, <) #undef EXTREMA_H_B -uint32_t helper_clo_h(target_ulong r1) +uint32_t helper_clo_h(uint32_t r1) { uint32_t ret_hw0 = extract32(r1, 0, 16); uint32_t ret_hw1 = extract32(r1, 16, 16); @@ -1747,7 +1734,7 @@ uint32_t helper_clo_h(target_ulong r1) return ret_hw0 | (ret_hw1 << 16); } -uint32_t helper_clz_h(target_ulong r1) +uint32_t helper_clz_h(uint32_t r1) { uint32_t ret_hw0 = extract32(r1, 0, 16); uint32_t ret_hw1 = extract32(r1, 16, 16); @@ -1765,7 +1752,7 @@ uint32_t helper_clz_h(target_ulong r1) return ret_hw0 | (ret_hw1 << 16); } -uint32_t helper_cls_h(target_ulong r1) +uint32_t helper_cls_h(uint32_t r1) { uint32_t ret_hw0 = extract32(r1, 0, 16); uint32_t ret_hw1 = extract32(r1, 16, 16); @@ -1783,7 +1770,7 @@ uint32_t helper_cls_h(target_ulong r1) return ret_hw0 | (ret_hw1 << 16); } -uint32_t helper_sh(target_ulong r1, target_ulong r2) +uint32_t helper_sh(uint32_t r1, uint32_t r2) { int32_t shift_count = sextract32(r2, 0, 6); @@ -1796,7 +1783,7 @@ uint32_t helper_sh(target_ulong r1, target_ulong r2) } } -uint32_t helper_sh_h(target_ulong r1, target_ulong r2) +uint32_t helper_sh_h(uint32_t r1, uint32_t r2) { int32_t ret_hw0, ret_hw1; int32_t shift_count; @@ -1816,7 +1803,7 @@ uint32_t helper_sh_h(target_ulong r1, target_ulong r2) } } -uint32_t helper_sha(CPUTriCoreState *env, target_ulong r1, target_ulong r2) +uint32_t helper_sha(CPUTriCoreState *env, uint32_t r1, uint32_t r2) { int32_t shift_count; int64_t result, t1; @@ -1854,7 +1841,7 @@ uint32_t helper_sha(CPUTriCoreState *env, target_ulong r1, target_ulong r2) return ret; } -uint32_t helper_sha_h(target_ulong r1, target_ulong r2) +uint32_t helper_sha_h(uint32_t r1, uint32_t r2) { int32_t shift_count; int32_t ret_hw0, ret_hw1; @@ -1874,7 +1861,7 @@ uint32_t helper_sha_h(target_ulong r1, target_ulong r2) } } -uint32_t helper_bmerge(target_ulong r1, target_ulong r2) +uint32_t helper_bmerge(uint32_t r1, uint32_t r2) { uint32_t i, ret; @@ -1905,7 +1892,7 @@ uint64_t helper_bsplit(uint32_t r1) return ret; } -uint32_t helper_parity(target_ulong r1) +uint32_t helper_parity(uint32_t r1) { uint32_t ret; uint32_t nOnes, i; @@ -1942,7 +1929,7 @@ uint32_t helper_parity(target_ulong r1) } uint32_t helper_pack(uint32_t carry, uint32_t r1_low, uint32_t r1_high, - target_ulong r2) + uint32_t r2) { uint32_t ret; int32_t fp_exp, fp_frac, temp_exp, fp_exp_frac; @@ -1983,7 +1970,7 @@ uint32_t helper_pack(uint32_t carry, uint32_t r1_low, uint32_t r1_high, return ret; } -uint64_t helper_unpack(target_ulong arg1) +uint64_t helper_unpack(uint32_t arg1) { int32_t fp_exp = extract32(arg1, 23, 8); int32_t fp_frac = extract32(arg1, 0, 23); @@ -2408,7 +2395,7 @@ uint32_t helper_shuffle(uint32_t arg0, uint32_t arg1) /* context save area (CSA) related helpers */ -static int cdc_increment(target_ulong *psw) +static int cdc_increment(uint32_t *psw) { if ((*psw & MASK_PSW_CDC) == 0x7f) { return 0; @@ -2426,7 +2413,7 @@ static int cdc_increment(target_ulong *psw) return 0; } -static int cdc_decrement(target_ulong *psw) +static int cdc_decrement(uint32_t *psw) { if ((*psw & MASK_PSW_CDC) == 0x7f) { return 0; @@ -2442,7 +2429,7 @@ static int cdc_decrement(target_ulong *psw) return 0; } -static bool cdc_zero(target_ulong *psw) +static bool cdc_zero(uint32_t *psw) { int cdc = *psw & MASK_PSW_CDC; /* Returns TRUE if PSW.CDC.COUNT == 0 or if PSW.CDC == @@ -2457,7 +2444,7 @@ static bool cdc_zero(target_ulong *psw) return count == 0; } -static void save_context_upper(CPUTriCoreState *env, target_ulong ea) +static void save_context_upper(CPUTriCoreState *env, uint32_t ea) { cpu_stl_data(env, ea, env->PCXI); cpu_stl_data(env, ea+4, psw_read(env)); @@ -2477,7 +2464,7 @@ static void save_context_upper(CPUTriCoreState *env, target_ulong ea) cpu_stl_data(env, ea+60, env->gpr_d[15]); } -static void save_context_lower(CPUTriCoreState *env, target_ulong ea) +static void save_context_lower(CPUTriCoreState *env, uint32_t ea) { cpu_stl_data(env, ea, env->PCXI); cpu_stl_data(env, ea+4, env->gpr_a[11]); @@ -2497,8 +2484,8 @@ static void save_context_lower(CPUTriCoreState *env, target_ulong ea) cpu_stl_data(env, ea+60, env->gpr_d[7]); } -static void restore_context_upper(CPUTriCoreState *env, target_ulong ea, - target_ulong *new_PCXI, target_ulong *new_PSW) +static void restore_context_upper(CPUTriCoreState *env, uint32_t ea, + uint32_t *new_PCXI, uint32_t *new_PSW) { *new_PCXI = cpu_ldl_data(env, ea); *new_PSW = cpu_ldl_data(env, ea+4); @@ -2518,8 +2505,8 @@ static void restore_context_upper(CPUTriCoreState *env, target_ulong ea, env->gpr_d[15] = cpu_ldl_data(env, ea+60); } -static void restore_context_lower(CPUTriCoreState *env, target_ulong ea, - target_ulong *ra, target_ulong *pcxi) +static void restore_context_lower(CPUTriCoreState *env, uint32_t ea, + uint32_t *ra, uint32_t *pcxi) { *pcxi = cpu_ldl_data(env, ea); *ra = cpu_ldl_data(env, ea+4); @@ -2541,10 +2528,10 @@ static void restore_context_lower(CPUTriCoreState *env, target_ulong ea, void helper_call(CPUTriCoreState *env, uint32_t next_pc) { - target_ulong tmp_FCX; - target_ulong ea; - target_ulong new_FCX; - target_ulong psw; + uint32_t tmp_FCX; + uint32_t ea; + uint32_t new_FCX; + uint32_t psw; psw = psw_read(env); /* if (FCX == 0) trap(FCU); */ @@ -2604,9 +2591,9 @@ void helper_call(CPUTriCoreState *env, uint32_t next_pc) void helper_ret(CPUTriCoreState *env) { - target_ulong ea; - target_ulong new_PCXI; - target_ulong new_PSW, psw; + uint32_t ea; + uint32_t new_PCXI; + uint32_t new_PSW, psw; psw = psw_read(env); /* if (PSW.CDE) then if (cdc_decrement()) then trap(CDU);*/ @@ -2657,9 +2644,9 @@ void helper_ret(CPUTriCoreState *env) void helper_bisr(CPUTriCoreState *env, uint32_t const9) { - target_ulong tmp_FCX; - target_ulong ea; - target_ulong new_FCX; + uint32_t tmp_FCX; + uint32_t ea; + uint32_t new_FCX; if (env->FCX == 0) { /* FCU trap */ @@ -2701,9 +2688,9 @@ void helper_bisr(CPUTriCoreState *env, uint32_t const9) void helper_rfe(CPUTriCoreState *env) { - target_ulong ea; - target_ulong new_PCXI; - target_ulong new_PSW; + uint32_t ea; + uint32_t new_PCXI; + uint32_t new_PSW; /* if (PCXI[19: 0] == 0) then trap(CSU); */ if ((env->PCXI & 0xfffff) == 0) { /* raise csu trap */ @@ -2762,35 +2749,35 @@ void helper_rfm(CPUTriCoreState *env) } } -void helper_ldlcx(CPUTriCoreState *env, target_ulong ea) +void helper_ldlcx(CPUTriCoreState *env, uint32_t ea) { uint32_t dummy; /* insn doesn't load PCXI and RA */ restore_context_lower(env, ea, &dummy, &dummy); } -void helper_lducx(CPUTriCoreState *env, target_ulong ea) +void helper_lducx(CPUTriCoreState *env, uint32_t ea) { uint32_t dummy; /* insn doesn't load PCXI and PSW */ restore_context_upper(env, ea, &dummy, &dummy); } -void helper_stlcx(CPUTriCoreState *env, target_ulong ea) +void helper_stlcx(CPUTriCoreState *env, uint32_t ea) { save_context_lower(env, ea); } -void helper_stucx(CPUTriCoreState *env, target_ulong ea) +void helper_stucx(CPUTriCoreState *env, uint32_t ea) { save_context_upper(env, ea); } void helper_svlcx(CPUTriCoreState *env) { - target_ulong tmp_FCX; - target_ulong ea; - target_ulong new_FCX; + uint32_t tmp_FCX; + uint32_t ea; + uint32_t new_FCX; if (env->FCX == 0) { /* FCU trap */ @@ -2831,9 +2818,9 @@ void helper_svlcx(CPUTriCoreState *env) void helper_svucx(CPUTriCoreState *env) { - target_ulong tmp_FCX; - target_ulong ea; - target_ulong new_FCX; + uint32_t tmp_FCX; + uint32_t ea; + uint32_t new_FCX; if (env->FCX == 0) { /* FCU trap */ @@ -2874,8 +2861,8 @@ void helper_svucx(CPUTriCoreState *env) void helper_rslcx(CPUTriCoreState *env) { - target_ulong ea; - target_ulong new_PCXI; + uint32_t ea; + uint32_t new_PCXI; /* if (PCXI[19: 0] == 0) then trap(CSU); */ if ((env->PCXI & 0xfffff) == 0) { /* CSU trap */ From 6b2e4fcb836cdd4b770086db03e07d0ff93f9612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Oct 2025 05:26:56 +0200 Subject: [PATCH 1663/1794] target/tricore: Declare registers as TCGv_i32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPUTriCoreState register are declared as uint32_t since the target introduction in commit 48e06fe0ed8 ("target-tricore: Add target stubs and qom-cpu"). Mechanical replacement of: TCGv -> TCGv_i32 tcg_temp_new -> tcg_temp_new_i32 Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251010052141.42460-8-philmd@linaro.org> --- target/tricore/translate.c | 82 +++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 116f45135bbed..112a1b9a12e83 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -44,19 +44,19 @@ /* * TCG registers */ -static TCGv cpu_PC; -static TCGv cpu_PCXI; -static TCGv cpu_PSW; -static TCGv cpu_ICR; +static TCGv_i32 cpu_PC; +static TCGv_i32 cpu_PCXI; +static TCGv_i32 cpu_PSW; +static TCGv_i32 cpu_ICR; /* GPR registers */ -static TCGv cpu_gpr_a[16]; -static TCGv cpu_gpr_d[16]; +static TCGv_i32 cpu_gpr_a[16]; +static TCGv_i32 cpu_gpr_d[16]; /* PSW Flag cache */ -static TCGv cpu_PSW_C; -static TCGv cpu_PSW_V; -static TCGv cpu_PSW_SV; -static TCGv cpu_PSW_AV; -static TCGv cpu_PSW_SAV; +static TCGv_i32 cpu_PSW_C; +static TCGv_i32 cpu_PSW_V; +static TCGv_i32 cpu_PSW_SV; +static TCGv_i32 cpu_PSW_AV; +static TCGv_i32 cpu_PSW_SAV; static const char *regnames_a[] = { "a0" , "a1" , "a2" , "a3" , "a4" , "a5" , @@ -8480,14 +8480,14 @@ void cpu_state_reset(CPUTriCoreState *env) static void tricore_tcg_init_csfr(void) { - cpu_PCXI = tcg_global_mem_new(tcg_env, - offsetof(CPUTriCoreState, PCXI), "PCXI"); - cpu_PSW = tcg_global_mem_new(tcg_env, - offsetof(CPUTriCoreState, PSW), "PSW"); - cpu_PC = tcg_global_mem_new(tcg_env, - offsetof(CPUTriCoreState, PC), "PC"); - cpu_ICR = tcg_global_mem_new(tcg_env, - offsetof(CPUTriCoreState, ICR), "ICR"); + cpu_PCXI = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUTriCoreState, PCXI), "PCXI"); + cpu_PSW = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUTriCoreState, PSW), "PSW"); + cpu_PC = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUTriCoreState, PC), "PC"); + cpu_ICR = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUTriCoreState, ICR), "ICR"); } void tricore_tcg_init(void) @@ -8496,30 +8496,32 @@ void tricore_tcg_init(void) /* reg init */ for (i = 0 ; i < 16 ; i++) { - cpu_gpr_a[i] = tcg_global_mem_new(tcg_env, - offsetof(CPUTriCoreState, gpr_a[i]), - regnames_a[i]); + cpu_gpr_a[i] = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUTriCoreState, + gpr_a[i]), + regnames_a[i]); } for (i = 0 ; i < 16 ; i++) { - cpu_gpr_d[i] = tcg_global_mem_new(tcg_env, - offsetof(CPUTriCoreState, gpr_d[i]), - regnames_d[i]); + cpu_gpr_d[i] = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUTriCoreState, + gpr_d[i]), + regnames_d[i]); } tricore_tcg_init_csfr(); /* init PSW flag cache */ - cpu_PSW_C = tcg_global_mem_new(tcg_env, - offsetof(CPUTriCoreState, PSW_USB_C), - "PSW_C"); - cpu_PSW_V = tcg_global_mem_new(tcg_env, - offsetof(CPUTriCoreState, PSW_USB_V), - "PSW_V"); - cpu_PSW_SV = tcg_global_mem_new(tcg_env, - offsetof(CPUTriCoreState, PSW_USB_SV), - "PSW_SV"); - cpu_PSW_AV = tcg_global_mem_new(tcg_env, - offsetof(CPUTriCoreState, PSW_USB_AV), - "PSW_AV"); - cpu_PSW_SAV = tcg_global_mem_new(tcg_env, - offsetof(CPUTriCoreState, PSW_USB_SAV), - "PSW_SAV"); + cpu_PSW_C = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUTriCoreState, PSW_USB_C), + "PSW_C"); + cpu_PSW_V = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUTriCoreState, PSW_USB_V), + "PSW_V"); + cpu_PSW_SV = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUTriCoreState, PSW_USB_SV), + "PSW_SV"); + cpu_PSW_AV = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUTriCoreState, PSW_USB_AV), + "PSW_AV"); + cpu_PSW_SAV = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUTriCoreState, PSW_USB_SAV), + "PSW_SAV"); } From f30c8aa229da11b40eb3a2e696e7e79d639c21fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Oct 2025 05:27:59 +0200 Subject: [PATCH 1664/1794] target/tricore: Inline tcg_gen_ld32u_tl() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TriCore target is only built as 32-bit, so tcg_gen_ld32u_tl() expands to tcg_gen_ld_i32(). Use the latter to simplify the next commit mechanical change. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251010052141.42460-9-philmd@linaro.org> --- target/tricore/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 112a1b9a12e83..2cfd2e5b8dfc0 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -7959,7 +7959,7 @@ static void decode_sys_interrupts(DisasContext *ctx) tmp = tcg_temp_new(); l1 = gen_new_label(); - tcg_gen_ld32u_tl(tmp, tcg_env, offsetof(CPUTriCoreState, DBGSR)); + tcg_gen_ld_i32(tmp, tcg_env, offsetof(CPUTriCoreState, DBGSR)); tcg_gen_andi_tl(tmp, tmp, MASK_DBGSR_DE); tcg_gen_brcondi_tl(TCG_COND_NE, tmp, 1, l1); gen_helper_rfm(tcg_env); From 4f08815467e5cfbd3f117f34b11da1beb613b775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Oct 2025 05:29:17 +0200 Subject: [PATCH 1665/1794] target/tricore: Expand TCG helpers for 32-bit target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TriCore target is only built as 32-bit: $ git grep TARGET_LONG_BITS configs/targets/tricore-* configs/targets/tricore-softmmu.mak:2:TARGET_LONG_BITS=32 Therefore tcg_FOO_tl() always expands to tcg_FOO_i32(). This is a mechanical replacement. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251010052141.42460-10-philmd@linaro.org> --- target/tricore/translate.c | 2595 ++++++++++++++++++------------------ 1 file changed, 1298 insertions(+), 1297 deletions(-) diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 2cfd2e5b8dfc0..ef3653b8820fa 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -139,9 +139,9 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) TCGv arg00 = tcg_temp_new(); \ TCGv arg01 = tcg_temp_new(); \ TCGv arg11 = tcg_temp_new(); \ - tcg_gen_sari_tl(arg00, arg0, 16); \ - tcg_gen_ext16s_tl(arg01, arg0); \ - tcg_gen_ext16s_tl(arg11, arg1); \ + tcg_gen_sari_i32(arg00, arg0, 16); \ + tcg_gen_ext16s_i32(arg01, arg0); \ + tcg_gen_ext16s_i32(arg11, arg1); \ gen_helper_##name(ret, arg00, arg01, arg11, arg11, n); \ } while (0) @@ -150,10 +150,10 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) TCGv arg01 = tcg_temp_new(); \ TCGv arg10 = tcg_temp_new(); \ TCGv arg11 = tcg_temp_new(); \ - tcg_gen_sari_tl(arg00, arg0, 16); \ - tcg_gen_ext16s_tl(arg01, arg0); \ - tcg_gen_sari_tl(arg11, arg1, 16); \ - tcg_gen_ext16s_tl(arg10, arg1); \ + tcg_gen_sari_i32(arg00, arg0, 16); \ + tcg_gen_ext16s_i32(arg01, arg0); \ + tcg_gen_sari_i32(arg11, arg1, 16); \ + tcg_gen_ext16s_i32(arg10, arg1); \ gen_helper_##name(ret, arg00, arg01, arg10, arg11, n); \ } while (0) @@ -162,10 +162,10 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) TCGv arg01 = tcg_temp_new(); \ TCGv arg10 = tcg_temp_new(); \ TCGv arg11 = tcg_temp_new(); \ - tcg_gen_sari_tl(arg00, arg0, 16); \ - tcg_gen_ext16s_tl(arg01, arg0); \ - tcg_gen_sari_tl(arg10, arg1, 16); \ - tcg_gen_ext16s_tl(arg11, arg1); \ + tcg_gen_sari_i32(arg00, arg0, 16); \ + tcg_gen_ext16s_i32(arg01, arg0); \ + tcg_gen_sari_i32(arg10, arg1, 16); \ + tcg_gen_ext16s_i32(arg11, arg1); \ gen_helper_##name(ret, arg00, arg01, arg10, arg11, n); \ } while (0) @@ -173,9 +173,9 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) TCGv arg00 = tcg_temp_new(); \ TCGv arg01 = tcg_temp_new(); \ TCGv arg11 = tcg_temp_new(); \ - tcg_gen_sari_tl(arg01, arg0, 16); \ - tcg_gen_ext16s_tl(arg00, arg0); \ - tcg_gen_sari_tl(arg11, arg1, 16); \ + tcg_gen_sari_i32(arg01, arg0, 16); \ + tcg_gen_ext16s_i32(arg00, arg0); \ + tcg_gen_sari_i32(arg11, arg1, 16); \ gen_helper_##name(ret, arg00, arg01, arg11, arg11, n); \ } while (0) @@ -214,16 +214,16 @@ static inline void gen_offset_ld(DisasContext *ctx, TCGv r1, TCGv r2, int16_t con, MemOp mop) { TCGv temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, r2, con); - tcg_gen_qemu_ld_tl(r1, temp, ctx->mem_idx, mop); + tcg_gen_addi_i32(temp, r2, con); + tcg_gen_qemu_ld_i32(r1, temp, ctx->mem_idx, mop); } static inline void gen_offset_st(DisasContext *ctx, TCGv r1, TCGv r2, int16_t con, MemOp mop) { TCGv temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, r2, con); - tcg_gen_qemu_st_tl(r1, temp, ctx->mem_idx, mop); + tcg_gen_addi_i32(temp, r2, con); + tcg_gen_qemu_st_i32(r1, temp, ctx->mem_idx, mop); } static void gen_st_2regs_64(TCGv rh, TCGv rl, TCGv address, DisasContext *ctx) @@ -238,7 +238,7 @@ static void gen_offset_st_2regs(TCGv rh, TCGv rl, TCGv base, int16_t con, DisasContext *ctx) { TCGv temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, base, con); + tcg_gen_addi_i32(temp, base, con); gen_st_2regs_64(rh, rl, temp, ctx); } @@ -255,7 +255,7 @@ static void gen_offset_ld_2regs(TCGv rh, TCGv rl, TCGv base, int16_t con, DisasContext *ctx) { TCGv temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, base, con); + tcg_gen_addi_i32(temp, base, con); gen_ld_2regs_64(rh, rl, temp, ctx); } @@ -263,18 +263,18 @@ static void gen_st_preincr(DisasContext *ctx, TCGv r1, TCGv r2, int16_t off, MemOp mop) { TCGv temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, r2, off); - tcg_gen_qemu_st_tl(r1, temp, ctx->mem_idx, mop); - tcg_gen_mov_tl(r2, temp); + tcg_gen_addi_i32(temp, r2, off); + tcg_gen_qemu_st_i32(r1, temp, ctx->mem_idx, mop); + tcg_gen_mov_i32(r2, temp); } static void gen_ld_preincr(DisasContext *ctx, TCGv r1, TCGv r2, int16_t off, MemOp mop) { TCGv temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, r2, off); - tcg_gen_qemu_ld_tl(r1, temp, ctx->mem_idx, mop); - tcg_gen_mov_tl(r2, temp); + tcg_gen_addi_i32(temp, r2, off); + tcg_gen_qemu_ld_i32(r1, temp, ctx->mem_idx, mop); + tcg_gen_mov_i32(r2, temp); } /* M(EA, word) = (M(EA, word) & ~E[a][63:32]) | (E[a][31:0] & E[a][63:32]); */ @@ -285,15 +285,15 @@ static void gen_ldmst(DisasContext *ctx, int ereg, TCGv ea) CHECK_REG_PAIR(ereg); /* temp = (M(EA, word) */ - tcg_gen_qemu_ld_tl(temp, ea, ctx->mem_idx, MO_LEUL); + tcg_gen_qemu_ld_i32(temp, ea, ctx->mem_idx, MO_LEUL); /* temp = temp & ~E[a][63:32]) */ - tcg_gen_andc_tl(temp, temp, cpu_gpr_d[ereg+1]); + tcg_gen_andc_i32(temp, temp, cpu_gpr_d[ereg + 1]); /* temp2 = (E[a][31:0] & E[a][63:32]); */ - tcg_gen_and_tl(temp2, cpu_gpr_d[ereg], cpu_gpr_d[ereg+1]); + tcg_gen_and_i32(temp2, cpu_gpr_d[ereg], cpu_gpr_d[ereg + 1]); /* temp = temp | temp2; */ - tcg_gen_or_tl(temp, temp, temp2); + tcg_gen_or_i32(temp, temp, temp2); /* M(EA, word) = temp; */ - tcg_gen_qemu_st_tl(temp, ea, ctx->mem_idx, MO_LEUL); + tcg_gen_qemu_st_i32(temp, ea, ctx->mem_idx, MO_LEUL); } /* tmp = M(EA, word); @@ -303,9 +303,9 @@ static void gen_swap(DisasContext *ctx, int reg, TCGv ea) { TCGv temp = tcg_temp_new(); - tcg_gen_qemu_ld_tl(temp, ea, ctx->mem_idx, MO_LEUL); - tcg_gen_qemu_st_tl(cpu_gpr_d[reg], ea, ctx->mem_idx, MO_LEUL); - tcg_gen_mov_tl(cpu_gpr_d[reg], temp); + tcg_gen_qemu_ld_i32(temp, ea, ctx->mem_idx, MO_LEUL); + tcg_gen_qemu_st_i32(cpu_gpr_d[reg], ea, ctx->mem_idx, MO_LEUL); + tcg_gen_mov_i32(cpu_gpr_d[reg], temp); } static void gen_cmpswap(DisasContext *ctx, int reg, TCGv ea) @@ -313,11 +313,11 @@ static void gen_cmpswap(DisasContext *ctx, int reg, TCGv ea) TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); CHECK_REG_PAIR(reg); - tcg_gen_qemu_ld_tl(temp, ea, ctx->mem_idx, MO_LEUL); - tcg_gen_movcond_tl(TCG_COND_EQ, temp2, cpu_gpr_d[reg+1], temp, + tcg_gen_qemu_ld_i32(temp, ea, ctx->mem_idx, MO_LEUL); + tcg_gen_movcond_i32(TCG_COND_EQ, temp2, cpu_gpr_d[reg + 1], temp, cpu_gpr_d[reg], temp); - tcg_gen_qemu_st_tl(temp2, ea, ctx->mem_idx, MO_LEUL); - tcg_gen_mov_tl(cpu_gpr_d[reg], temp); + tcg_gen_qemu_st_i32(temp2, ea, ctx->mem_idx, MO_LEUL); + tcg_gen_mov_i32(cpu_gpr_d[reg], temp); } static void gen_swapmsk(DisasContext *ctx, int reg, TCGv ea) @@ -326,12 +326,12 @@ static void gen_swapmsk(DisasContext *ctx, int reg, TCGv ea) TCGv temp2 = tcg_temp_new(); TCGv temp3 = tcg_temp_new(); CHECK_REG_PAIR(reg); - tcg_gen_qemu_ld_tl(temp, ea, ctx->mem_idx, MO_LEUL); - tcg_gen_and_tl(temp2, cpu_gpr_d[reg], cpu_gpr_d[reg+1]); - tcg_gen_andc_tl(temp3, temp, cpu_gpr_d[reg+1]); - tcg_gen_or_tl(temp2, temp2, temp3); - tcg_gen_qemu_st_tl(temp2, ea, ctx->mem_idx, MO_LEUL); - tcg_gen_mov_tl(cpu_gpr_d[reg], temp); + tcg_gen_qemu_ld_i32(temp, ea, ctx->mem_idx, MO_LEUL); + tcg_gen_and_i32(temp2, cpu_gpr_d[reg], cpu_gpr_d[reg + 1]); + tcg_gen_andc_i32(temp3, temp, cpu_gpr_d[reg + 1]); + tcg_gen_or_i32(temp2, temp2, temp3); + tcg_gen_qemu_st_i32(temp2, ea, ctx->mem_idx, MO_LEUL); + tcg_gen_mov_i32(cpu_gpr_d[reg], temp); } /* We generate loads and store to core special function register (csfr) through @@ -341,7 +341,7 @@ static void gen_swapmsk(DisasContext *ctx, int reg, TCGv ea) #define R(ADDRESS, REG, FEATURE) \ case ADDRESS: \ if (has_feature(ctx, FEATURE)) { \ - tcg_gen_ld_tl(ret, tcg_env, offsetof(CPUTriCoreState, REG)); \ + tcg_gen_ld_i32(ret, tcg_env, offsetof(CPUTriCoreState, REG)); \ } \ break; #define A(ADDRESS, REG, FEATURE) R(ADDRESS, REG, FEATURE) @@ -366,7 +366,7 @@ static inline void gen_mfcr(DisasContext *ctx, TCGv ret, int32_t offset) #define A(ADDRESS, REG, FEATURE) R(ADDRESS, REG, FEATURE) \ case ADDRESS: \ if (has_feature(ctx, FEATURE)) { \ - tcg_gen_st_tl(r1, tcg_env, offsetof(CPUTriCoreState, REG)); \ + tcg_gen_st_i32(r1, tcg_env, offsetof(CPUTriCoreState, REG)); \ } \ break; /* Endinit protected registers @@ -399,20 +399,20 @@ static inline void gen_add_d(TCGv ret, TCGv r1, TCGv r2) TCGv t0 = tcg_temp_new_i32(); TCGv result = tcg_temp_new_i32(); /* Addition and set V/SV bits */ - tcg_gen_add_tl(result, r1, r2); + tcg_gen_add_i32(result, r1, r2); /* calc V bit */ - tcg_gen_xor_tl(cpu_PSW_V, result, r1); - tcg_gen_xor_tl(t0, r1, r2); - tcg_gen_andc_tl(cpu_PSW_V, cpu_PSW_V, t0); + tcg_gen_xor_i32(cpu_PSW_V, result, r1); + tcg_gen_xor_i32(t0, r1, r2); + tcg_gen_andc_i32(cpu_PSW_V, cpu_PSW_V, t0); /* Calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV/SAV bits */ - tcg_gen_add_tl(cpu_PSW_AV, result, result); - tcg_gen_xor_tl(cpu_PSW_AV, result, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, result, result); + tcg_gen_xor_i32(cpu_PSW_AV, result, cpu_PSW_AV); /* calc SAV */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ - tcg_gen_mov_tl(ret, result); + tcg_gen_mov_i32(ret, result); } static inline void @@ -430,13 +430,13 @@ gen_add64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) tcg_gen_andc_i64(t1, t1, t0); tcg_gen_extrh_i64_i32(cpu_PSW_V, t1); /* calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* calc AV/SAV bits */ tcg_gen_extrh_i64_i32(temp, result); - tcg_gen_add_tl(cpu_PSW_AV, temp, temp); - tcg_gen_xor_tl(cpu_PSW_AV, temp, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, temp, temp); + tcg_gen_xor_i32(cpu_PSW_AV, temp, cpu_PSW_AV); /* calc SAV */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ tcg_gen_mov_i64(ret, result); } @@ -453,38 +453,38 @@ gen_addsub64_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, (*op1)(temp, r1_low, r2); /* calc V0 bit */ - tcg_gen_xor_tl(temp2, temp, r1_low); - tcg_gen_xor_tl(temp3, r1_low, r2); + tcg_gen_xor_i32(temp2, temp, r1_low); + tcg_gen_xor_i32(temp3, r1_low, r2); if (op1 == tcg_gen_add_tl) { - tcg_gen_andc_tl(temp2, temp2, temp3); + tcg_gen_andc_i32(temp2, temp2, temp3); } else { - tcg_gen_and_tl(temp2, temp2, temp3); + tcg_gen_and_i32(temp2, temp2, temp3); } (*op2)(temp3, r1_high, r3); /* calc V1 bit */ - tcg_gen_xor_tl(cpu_PSW_V, temp3, r1_high); - tcg_gen_xor_tl(temp4, r1_high, r3); + tcg_gen_xor_i32(cpu_PSW_V, temp3, r1_high); + tcg_gen_xor_i32(temp4, r1_high, r3); if (op2 == tcg_gen_add_tl) { - tcg_gen_andc_tl(cpu_PSW_V, cpu_PSW_V, temp4); + tcg_gen_andc_i32(cpu_PSW_V, cpu_PSW_V, temp4); } else { - tcg_gen_and_tl(cpu_PSW_V, cpu_PSW_V, temp4); + tcg_gen_and_i32(cpu_PSW_V, cpu_PSW_V, temp4); } /* combine V0/V1 bits */ - tcg_gen_or_tl(cpu_PSW_V, cpu_PSW_V, temp2); + tcg_gen_or_i32(cpu_PSW_V, cpu_PSW_V, temp2); /* calc sv bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* write result */ - tcg_gen_mov_tl(ret_low, temp); - tcg_gen_mov_tl(ret_high, temp3); + tcg_gen_mov_i32(ret_low, temp); + tcg_gen_mov_i32(ret_high, temp3); /* calc AV bit */ - tcg_gen_add_tl(temp, ret_low, ret_low); - tcg_gen_xor_tl(temp, temp, ret_low); - tcg_gen_add_tl(cpu_PSW_AV, ret_high, ret_high); - tcg_gen_xor_tl(cpu_PSW_AV, cpu_PSW_AV, ret_high); - tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp); + tcg_gen_add_i32(temp, ret_low, ret_low); + tcg_gen_xor_i32(temp, temp, ret_low); + tcg_gen_add_i32(cpu_PSW_AV, ret_high, ret_high); + tcg_gen_xor_i32(cpu_PSW_AV, cpu_PSW_AV, ret_high); + tcg_gen_or_i32(cpu_PSW_AV, cpu_PSW_AV, temp); /* calc SAV bit */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } /* ret = r2 + (r1 * r3); */ @@ -509,14 +509,14 @@ static inline void gen_madd32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) tcg_gen_setcondi_i64(TCG_COND_LT, t2, t1, -0x80000000LL); tcg_gen_or_i64(t2, t2, t3); tcg_gen_extrl_i64_i32(cpu_PSW_V, t2); - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + tcg_gen_shli_i32(cpu_PSW_V, cpu_PSW_V, 31); /* Calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV/SAV bits */ - tcg_gen_add_tl(cpu_PSW_AV, ret, ret); - tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, ret, ret); + tcg_gen_xor_i32(cpu_PSW_AV, ret, cpu_PSW_AV); /* calc SAV */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } static inline void gen_maddi32_d(TCGv ret, TCGv r1, TCGv r2, int32_t con) @@ -534,23 +534,23 @@ gen_madd64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, TCGv t3 = tcg_temp_new(); TCGv t4 = tcg_temp_new(); - tcg_gen_muls2_tl(t1, t2, r1, r3); + tcg_gen_muls2_i32(t1, t2, r1, r3); /* only the add can overflow */ - tcg_gen_add2_tl(t3, t4, r2_low, r2_high, t1, t2); + tcg_gen_add2_i32(t3, t4, r2_low, r2_high, t1, t2); /* calc V bit */ - tcg_gen_xor_tl(cpu_PSW_V, t4, r2_high); - tcg_gen_xor_tl(t1, r2_high, t2); - tcg_gen_andc_tl(cpu_PSW_V, cpu_PSW_V, t1); + tcg_gen_xor_i32(cpu_PSW_V, t4, r2_high); + tcg_gen_xor_i32(t1, r2_high, t2); + tcg_gen_andc_i32(cpu_PSW_V, cpu_PSW_V, t1); /* Calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV/SAV bits */ - tcg_gen_add_tl(cpu_PSW_AV, t4, t4); - tcg_gen_xor_tl(cpu_PSW_AV, t4, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, t4, t4); + tcg_gen_xor_i32(cpu_PSW_AV, t4, cpu_PSW_AV); /* calc SAV */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back the result */ - tcg_gen_mov_tl(ret_low, t3); - tcg_gen_mov_tl(ret_high, t4); + tcg_gen_mov_i32(ret_low, t3); + tcg_gen_mov_i32(ret_high, t4); } static inline void @@ -573,14 +573,14 @@ gen_maddu64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, calc V bit */ tcg_gen_setcond_i64(TCG_COND_LTU, t2, t2, t1); tcg_gen_extrl_i64_i32(cpu_PSW_V, t2); - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + tcg_gen_shli_i32(cpu_PSW_V, cpu_PSW_V, 31); /* Calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV/SAV bits */ - tcg_gen_add_tl(cpu_PSW_AV, ret_high, ret_high); - tcg_gen_xor_tl(cpu_PSW_AV, ret_high, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, ret_high, ret_high); + tcg_gen_xor_i32(cpu_PSW_AV, ret_high, cpu_PSW_AV); /* calc SAV */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } static inline void @@ -714,13 +714,13 @@ gen_madds_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, } tcg_gen_extr_i64_i32(temp, temp2, temp64); gen_adds(ret_low, r1_low, temp); - tcg_gen_mov_tl(temp, cpu_PSW_V); - tcg_gen_mov_tl(temp3, cpu_PSW_AV); + tcg_gen_mov_i32(temp, cpu_PSW_V); + tcg_gen_mov_i32(temp3, cpu_PSW_AV); gen_adds(ret_high, r1_high, temp2); /* combine v bits */ - tcg_gen_or_tl(cpu_PSW_V, cpu_PSW_V, temp); + tcg_gen_or_i32(cpu_PSW_V, cpu_PSW_V, temp); /* combine av bits */ - tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp3); + tcg_gen_or_i32(cpu_PSW_AV, cpu_PSW_AV, temp3); } static inline void gen_subs(TCGv ret, TCGv r1, TCGv r2); @@ -751,13 +751,13 @@ gen_maddsus_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, } tcg_gen_extr_i64_i32(temp, temp2, temp64); gen_subs(ret_low, r1_low, temp); - tcg_gen_mov_tl(temp, cpu_PSW_V); - tcg_gen_mov_tl(temp3, cpu_PSW_AV); + tcg_gen_mov_i32(temp, cpu_PSW_V); + tcg_gen_mov_i32(temp3, cpu_PSW_AV); gen_adds(ret_high, r1_high, temp2); /* combine v bits */ - tcg_gen_or_tl(cpu_PSW_V, cpu_PSW_V, temp); + tcg_gen_or_i32(cpu_PSW_V, cpu_PSW_V, temp); /* combine av bits */ - tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp3); + tcg_gen_or_i32(cpu_PSW_AV, cpu_PSW_AV, temp3); } static inline void @@ -876,8 +876,8 @@ gen_maddr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); - tcg_gen_andi_tl(temp2, r1, 0xffff0000); - tcg_gen_shli_tl(temp, r1, 16); + tcg_gen_andi_i32(temp2, r1, 0xffff0000); + tcg_gen_shli_i32(temp, r1, 16); gen_maddr64_h(ret, temp, temp2, r2, r3, n, mode); } @@ -902,8 +902,8 @@ gen_maddsur32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } - tcg_gen_andi_tl(temp2, r1, 0xffff0000); - tcg_gen_shli_tl(temp, r1, 16); + tcg_gen_andi_i32(temp2, r1, 0xffff0000); + tcg_gen_shli_i32(temp, r1, 16); gen_helper_addsur_h(ret, tcg_env, temp64, temp, temp2); } @@ -937,8 +937,8 @@ gen_maddr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); - tcg_gen_andi_tl(temp2, r1, 0xffff0000); - tcg_gen_shli_tl(temp, r1, 16); + tcg_gen_andi_i32(temp2, r1, 0xffff0000); + tcg_gen_shli_i32(temp, r1, 16); gen_maddr64s_h(ret, temp, temp2, r2, r3, n, mode); } @@ -963,8 +963,8 @@ gen_maddsur32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } - tcg_gen_andi_tl(temp2, r1, 0xffff0000); - tcg_gen_shli_tl(temp, r1, 16); + tcg_gen_andi_i32(temp2, r1, 0xffff0000); + tcg_gen_shli_i32(temp, r1, 16); gen_helper_addsur_h_ssov(ret, tcg_env, temp64, temp, temp2); } @@ -1009,27 +1009,27 @@ gen_madd32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, tcg_gen_setcondi_i64(TCG_COND_LT, t2, t3, -0x80000000LL); tcg_gen_or_i64(t1, t1, t2); tcg_gen_extrl_i64_i32(cpu_PSW_V, t1); - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + tcg_gen_shli_i32(cpu_PSW_V, cpu_PSW_V, 31); /* We produce an overflow on the host if the mul before was (0x80000000 * 0x80000000) << 1). If this is the case, we negate the ovf. */ if (n == 1) { - tcg_gen_setcondi_tl(TCG_COND_EQ, temp, arg2, 0x80000000); - tcg_gen_setcond_tl(TCG_COND_EQ, temp2, arg2, arg3); - tcg_gen_and_tl(temp, temp, temp2); - tcg_gen_shli_tl(temp, temp, 31); + tcg_gen_setcondi_i32(TCG_COND_EQ, temp, arg2, 0x80000000); + tcg_gen_setcond_i32(TCG_COND_EQ, temp2, arg2, arg3); + tcg_gen_and_i32(temp, temp, temp2); + tcg_gen_shli_i32(temp, temp, 31); /* negate v bit, if special condition */ - tcg_gen_xor_tl(cpu_PSW_V, cpu_PSW_V, temp); + tcg_gen_xor_i32(cpu_PSW_V, cpu_PSW_V, temp); } /* Calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV/SAV bits */ - tcg_gen_add_tl(cpu_PSW_AV, temp3, temp3); - tcg_gen_xor_tl(cpu_PSW_AV, temp3, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, temp3, temp3); + tcg_gen_xor_i32(cpu_PSW_AV, temp3, cpu_PSW_AV); /* calc SAV */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ - tcg_gen_mov_tl(ret, temp3); + tcg_gen_mov_i32(ret, temp3); } static inline void @@ -1038,13 +1038,13 @@ gen_m16add32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); if (n == 0) { - tcg_gen_mul_tl(temp, arg2, arg3); + tcg_gen_mul_i32(temp, arg2, arg3); } else { /* n is expected to be 1 */ - tcg_gen_mul_tl(temp, arg2, arg3); - tcg_gen_shli_tl(temp, temp, 1); + tcg_gen_mul_i32(temp, arg2, arg3); + tcg_gen_shli_i32(temp, temp, 1); /* catch special case r1 = r2 = 0x8000 */ - tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, temp, 0x80000000); - tcg_gen_sub_tl(temp, temp, temp2); + tcg_gen_setcondi_i32(TCG_COND_EQ, temp2, temp, 0x80000000); + tcg_gen_sub_i32(temp, temp, temp2); } gen_add_d(ret, arg1, temp); } @@ -1055,13 +1055,13 @@ gen_m16adds32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); if (n == 0) { - tcg_gen_mul_tl(temp, arg2, arg3); + tcg_gen_mul_i32(temp, arg2, arg3); } else { /* n is expected to be 1 */ - tcg_gen_mul_tl(temp, arg2, arg3); - tcg_gen_shli_tl(temp, temp, 1); + tcg_gen_mul_i32(temp, arg2, arg3); + tcg_gen_shli_i32(temp, temp, 1); /* catch special case r1 = r2 = 0x8000 */ - tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, temp, 0x80000000); - tcg_gen_sub_tl(temp, temp, temp2); + tcg_gen_setcondi_i32(TCG_COND_EQ, temp2, temp, 0x80000000); + tcg_gen_sub_i32(temp, temp, temp2); } gen_adds(ret, arg1, temp); } @@ -1077,13 +1077,13 @@ gen_m16add64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, TCGv_i64 t3 = tcg_temp_new_i64(); if (n == 0) { - tcg_gen_mul_tl(temp, arg2, arg3); + tcg_gen_mul_i32(temp, arg2, arg3); } else { /* n is expected to be 1 */ - tcg_gen_mul_tl(temp, arg2, arg3); - tcg_gen_shli_tl(temp, temp, 1); + tcg_gen_mul_i32(temp, arg2, arg3); + tcg_gen_shli_i32(temp, temp, 1); /* catch special case r1 = r2 = 0x8000 */ - tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, temp, 0x80000000); - tcg_gen_sub_tl(temp, temp, temp2); + tcg_gen_setcondi_i32(TCG_COND_EQ, temp2, temp, 0x80000000); + tcg_gen_sub_i32(temp, temp, temp2); } tcg_gen_ext_i32_i64(t2, temp); tcg_gen_shli_i64(t2, t2, 16); @@ -1103,13 +1103,13 @@ gen_m16adds64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, TCGv_i64 t2 = tcg_temp_new_i64(); if (n == 0) { - tcg_gen_mul_tl(temp, arg2, arg3); + tcg_gen_mul_i32(temp, arg2, arg3); } else { /* n is expected to be 1 */ - tcg_gen_mul_tl(temp, arg2, arg3); - tcg_gen_shli_tl(temp, temp, 1); + tcg_gen_mul_i32(temp, arg2, arg3); + tcg_gen_shli_i32(temp, temp, 1); /* catch special case r1 = r2 = 0x8000 */ - tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, temp, 0x80000000); - tcg_gen_sub_tl(temp, temp, temp2); + tcg_gen_setcondi_i32(TCG_COND_EQ, temp2, temp, 0x80000000); + tcg_gen_sub_i32(temp, temp, temp2); } tcg_gen_ext_i32_i64(t2, temp); tcg_gen_shli_i64(t2, t2, 16); @@ -1149,22 +1149,22 @@ gen_madd64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, if (n == 1) { temp = tcg_temp_new(); temp2 = tcg_temp_new(); - tcg_gen_setcondi_tl(TCG_COND_EQ, temp, arg2, 0x80000000); - tcg_gen_setcond_tl(TCG_COND_EQ, temp2, arg2, arg3); - tcg_gen_and_tl(temp, temp, temp2); - tcg_gen_shli_tl(temp, temp, 31); + tcg_gen_setcondi_i32(TCG_COND_EQ, temp, arg2, 0x80000000); + tcg_gen_setcond_i32(TCG_COND_EQ, temp2, arg2, arg3); + tcg_gen_and_i32(temp, temp, temp2); + tcg_gen_shli_i32(temp, temp, 31); /* negate v bit, if special condition */ - tcg_gen_xor_tl(cpu_PSW_V, cpu_PSW_V, temp); + tcg_gen_xor_i32(cpu_PSW_V, cpu_PSW_V, temp); } /* write back result */ tcg_gen_extr_i64_i32(rl, rh, t4); /* Calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV/SAV bits */ - tcg_gen_add_tl(cpu_PSW_AV, rh, rh); - tcg_gen_xor_tl(cpu_PSW_AV, rh, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, rh, rh); + tcg_gen_xor_i32(cpu_PSW_AV, rh, cpu_PSW_AV); /* calc SAV */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } static inline void @@ -1219,15 +1219,15 @@ static inline void gen_msub32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) tcg_gen_setcondi_i64(TCG_COND_LT, t2, t1, -0x80000000LL); tcg_gen_or_i64(t2, t2, t3); tcg_gen_extrl_i64_i32(cpu_PSW_V, t2); - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + tcg_gen_shli_i32(cpu_PSW_V, cpu_PSW_V, 31); /* Calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV/SAV bits */ - tcg_gen_add_tl(cpu_PSW_AV, ret, ret); - tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, ret, ret); + tcg_gen_xor_i32(cpu_PSW_AV, ret, cpu_PSW_AV); /* calc SAV */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } static inline void gen_msubi32_d(TCGv ret, TCGv r1, TCGv r2, int32_t con) @@ -1245,23 +1245,23 @@ gen_msub64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, TCGv t3 = tcg_temp_new(); TCGv t4 = tcg_temp_new(); - tcg_gen_muls2_tl(t1, t2, r1, r3); + tcg_gen_muls2_i32(t1, t2, r1, r3); /* only the sub can overflow */ - tcg_gen_sub2_tl(t3, t4, r2_low, r2_high, t1, t2); + tcg_gen_sub2_i32(t3, t4, r2_low, r2_high, t1, t2); /* calc V bit */ - tcg_gen_xor_tl(cpu_PSW_V, t4, r2_high); - tcg_gen_xor_tl(t1, r2_high, t2); - tcg_gen_and_tl(cpu_PSW_V, cpu_PSW_V, t1); + tcg_gen_xor_i32(cpu_PSW_V, t4, r2_high); + tcg_gen_xor_i32(t1, r2_high, t2); + tcg_gen_and_i32(cpu_PSW_V, cpu_PSW_V, t1); /* Calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV/SAV bits */ - tcg_gen_add_tl(cpu_PSW_AV, t4, t4); - tcg_gen_xor_tl(cpu_PSW_AV, t4, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, t4, t4); + tcg_gen_xor_i32(cpu_PSW_AV, t4, cpu_PSW_AV); /* calc SAV */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back the result */ - tcg_gen_mov_tl(ret_low, t3); - tcg_gen_mov_tl(ret_high, t4); + tcg_gen_mov_i32(ret_low, t3); + tcg_gen_mov_i32(ret_high, t4); } static inline void @@ -1290,14 +1290,14 @@ gen_msubu64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, /* calc V bit, only the sub can overflow, if t1 > t2 */ tcg_gen_setcond_i64(TCG_COND_GTU, t1, t1, t2); tcg_gen_extrl_i64_i32(cpu_PSW_V, t1); - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + tcg_gen_shli_i32(cpu_PSW_V, cpu_PSW_V, 31); /* Calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV/SAV bits */ - tcg_gen_add_tl(cpu_PSW_AV, ret_high, ret_high); - tcg_gen_xor_tl(cpu_PSW_AV, ret_high, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, ret_high, ret_high); + tcg_gen_xor_i32(cpu_PSW_AV, ret_high, cpu_PSW_AV); /* calc SAV */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } static inline void @@ -1320,22 +1320,22 @@ static inline void gen_add_CC(TCGv ret, TCGv r1, TCGv r2) TCGv t0 = tcg_temp_new_i32(); TCGv result = tcg_temp_new_i32(); - tcg_gen_movi_tl(t0, 0); + tcg_gen_movi_i32(t0, 0); /* Addition and set C/V/SV bits */ tcg_gen_add2_i32(result, cpu_PSW_C, r1, t0, r2, t0); /* calc V bit */ - tcg_gen_xor_tl(cpu_PSW_V, result, r1); - tcg_gen_xor_tl(t0, r1, r2); - tcg_gen_andc_tl(cpu_PSW_V, cpu_PSW_V, t0); + tcg_gen_xor_i32(cpu_PSW_V, result, r1); + tcg_gen_xor_i32(t0, r1, r2); + tcg_gen_andc_i32(cpu_PSW_V, cpu_PSW_V, t0); /* Calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV/SAV bits */ - tcg_gen_add_tl(cpu_PSW_AV, result, result); - tcg_gen_xor_tl(cpu_PSW_AV, result, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, result, result); + tcg_gen_xor_i32(cpu_PSW_AV, result, cpu_PSW_AV); /* calc SAV */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ - tcg_gen_mov_tl(ret, result); + tcg_gen_mov_i32(ret, result); } static inline void gen_addi_CC(TCGv ret, TCGv r1, int32_t con) @@ -1352,18 +1352,18 @@ static inline void gen_addc_CC(TCGv ret, TCGv r1, TCGv r2) /* Addition, carry and set C/V/SV bits */ tcg_gen_addcio_i32(result, cpu_PSW_C, r1, r2, cpu_PSW_C); /* calc V bit */ - tcg_gen_xor_tl(cpu_PSW_V, result, r1); - tcg_gen_xor_tl(t0, r1, r2); - tcg_gen_andc_tl(cpu_PSW_V, cpu_PSW_V, t0); + tcg_gen_xor_i32(cpu_PSW_V, result, r1); + tcg_gen_xor_i32(t0, r1, r2); + tcg_gen_andc_i32(cpu_PSW_V, cpu_PSW_V, t0); /* Calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV/SAV bits */ - tcg_gen_add_tl(cpu_PSW_AV, result, result); - tcg_gen_xor_tl(cpu_PSW_AV, result, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, result, result); + tcg_gen_xor_i32(cpu_PSW_AV, result, cpu_PSW_AV); /* calc SAV */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ - tcg_gen_mov_tl(ret, result); + tcg_gen_mov_i32(ret, result); } static inline void gen_addci_CC(TCGv ret, TCGv r1, int32_t con) @@ -1382,27 +1382,27 @@ static inline void gen_cond_add(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, TCGv t0 = tcg_constant_i32(0); /* create mask for sticky bits */ - tcg_gen_setcond_tl(cond, mask, r4, t0); - tcg_gen_shli_tl(mask, mask, 31); + tcg_gen_setcond_i32(cond, mask, r4, t0); + tcg_gen_shli_i32(mask, mask, 31); - tcg_gen_add_tl(result, r1, r2); + tcg_gen_add_i32(result, r1, r2); /* Calc PSW_V */ - tcg_gen_xor_tl(temp, result, r1); - tcg_gen_xor_tl(temp2, r1, r2); - tcg_gen_andc_tl(temp, temp, temp2); - tcg_gen_movcond_tl(cond, cpu_PSW_V, r4, t0, temp, cpu_PSW_V); + tcg_gen_xor_i32(temp, result, r1); + tcg_gen_xor_i32(temp2, r1, r2); + tcg_gen_andc_i32(temp, temp, temp2); + tcg_gen_movcond_i32(cond, cpu_PSW_V, r4, t0, temp, cpu_PSW_V); /* Set PSW_SV */ - tcg_gen_and_tl(temp, temp, mask); - tcg_gen_or_tl(cpu_PSW_SV, temp, cpu_PSW_SV); + tcg_gen_and_i32(temp, temp, mask); + tcg_gen_or_i32(cpu_PSW_SV, temp, cpu_PSW_SV); /* calc AV bit */ - tcg_gen_add_tl(temp, result, result); - tcg_gen_xor_tl(temp, temp, result); - tcg_gen_movcond_tl(cond, cpu_PSW_AV, r4, t0, temp, cpu_PSW_AV); + tcg_gen_add_i32(temp, result, result); + tcg_gen_xor_i32(temp, temp, result); + tcg_gen_movcond_i32(cond, cpu_PSW_AV, r4, t0, temp, cpu_PSW_AV); /* calc SAV bit */ - tcg_gen_and_tl(temp, temp, mask); - tcg_gen_or_tl(cpu_PSW_SAV, temp, cpu_PSW_SAV); + tcg_gen_and_i32(temp, temp, mask); + tcg_gen_or_i32(cpu_PSW_SAV, temp, cpu_PSW_SAV); /* write back result */ - tcg_gen_movcond_tl(cond, r3, r4, t0, result, r1); + tcg_gen_movcond_i32(cond, r3, r4, t0, result, r1); } static inline void gen_condi_add(TCGCond cond, TCGv r1, int32_t r2, @@ -1417,20 +1417,20 @@ static inline void gen_sub_d(TCGv ret, TCGv r1, TCGv r2) TCGv temp = tcg_temp_new_i32(); TCGv result = tcg_temp_new_i32(); - tcg_gen_sub_tl(result, r1, r2); + tcg_gen_sub_i32(result, r1, r2); /* calc V bit */ - tcg_gen_xor_tl(cpu_PSW_V, result, r1); - tcg_gen_xor_tl(temp, r1, r2); - tcg_gen_and_tl(cpu_PSW_V, cpu_PSW_V, temp); + tcg_gen_xor_i32(cpu_PSW_V, result, r1); + tcg_gen_xor_i32(temp, r1, r2); + tcg_gen_and_i32(cpu_PSW_V, cpu_PSW_V, temp); /* calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV bit */ - tcg_gen_add_tl(cpu_PSW_AV, result, result); - tcg_gen_xor_tl(cpu_PSW_AV, result, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, result, result); + tcg_gen_xor_i32(cpu_PSW_AV, result, cpu_PSW_AV); /* calc SAV bit */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ - tcg_gen_mov_tl(ret, result); + tcg_gen_mov_i32(ret, result); } static inline void @@ -1448,13 +1448,13 @@ gen_sub64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) tcg_gen_and_i64(t1, t1, t0); tcg_gen_extrh_i64_i32(cpu_PSW_V, t1); /* calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* calc AV/SAV bits */ tcg_gen_extrh_i64_i32(temp, result); - tcg_gen_add_tl(cpu_PSW_AV, temp, temp); - tcg_gen_xor_tl(cpu_PSW_AV, temp, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, temp, temp); + tcg_gen_xor_i32(cpu_PSW_AV, temp, cpu_PSW_AV); /* calc SAV */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ tcg_gen_mov_i64(ret, result); } @@ -1464,28 +1464,28 @@ static inline void gen_sub_CC(TCGv ret, TCGv r1, TCGv r2) TCGv result = tcg_temp_new(); TCGv temp = tcg_temp_new(); - tcg_gen_sub_tl(result, r1, r2); + tcg_gen_sub_i32(result, r1, r2); /* calc C bit */ - tcg_gen_setcond_tl(TCG_COND_GEU, cpu_PSW_C, r1, r2); + tcg_gen_setcond_i32(TCG_COND_GEU, cpu_PSW_C, r1, r2); /* calc V bit */ - tcg_gen_xor_tl(cpu_PSW_V, result, r1); - tcg_gen_xor_tl(temp, r1, r2); - tcg_gen_and_tl(cpu_PSW_V, cpu_PSW_V, temp); + tcg_gen_xor_i32(cpu_PSW_V, result, r1); + tcg_gen_xor_i32(temp, r1, r2); + tcg_gen_and_i32(cpu_PSW_V, cpu_PSW_V, temp); /* calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV bit */ - tcg_gen_add_tl(cpu_PSW_AV, result, result); - tcg_gen_xor_tl(cpu_PSW_AV, result, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, result, result); + tcg_gen_xor_i32(cpu_PSW_AV, result, cpu_PSW_AV); /* calc SAV bit */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ - tcg_gen_mov_tl(ret, result); + tcg_gen_mov_i32(ret, result); } static inline void gen_subc_CC(TCGv ret, TCGv r1, TCGv r2) { TCGv temp = tcg_temp_new(); - tcg_gen_not_tl(temp, r2); + tcg_gen_not_i32(temp, r2); gen_addc_CC(ret, r1, temp); } @@ -1499,27 +1499,27 @@ static inline void gen_cond_sub(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, TCGv t0 = tcg_constant_i32(0); /* create mask for sticky bits */ - tcg_gen_setcond_tl(cond, mask, r4, t0); - tcg_gen_shli_tl(mask, mask, 31); + tcg_gen_setcond_i32(cond, mask, r4, t0); + tcg_gen_shli_i32(mask, mask, 31); - tcg_gen_sub_tl(result, r1, r2); + tcg_gen_sub_i32(result, r1, r2); /* Calc PSW_V */ - tcg_gen_xor_tl(temp, result, r1); - tcg_gen_xor_tl(temp2, r1, r2); - tcg_gen_and_tl(temp, temp, temp2); - tcg_gen_movcond_tl(cond, cpu_PSW_V, r4, t0, temp, cpu_PSW_V); + tcg_gen_xor_i32(temp, result, r1); + tcg_gen_xor_i32(temp2, r1, r2); + tcg_gen_and_i32(temp, temp, temp2); + tcg_gen_movcond_i32(cond, cpu_PSW_V, r4, t0, temp, cpu_PSW_V); /* Set PSW_SV */ - tcg_gen_and_tl(temp, temp, mask); - tcg_gen_or_tl(cpu_PSW_SV, temp, cpu_PSW_SV); + tcg_gen_and_i32(temp, temp, mask); + tcg_gen_or_i32(cpu_PSW_SV, temp, cpu_PSW_SV); /* calc AV bit */ - tcg_gen_add_tl(temp, result, result); - tcg_gen_xor_tl(temp, temp, result); - tcg_gen_movcond_tl(cond, cpu_PSW_AV, r4, t0, temp, cpu_PSW_AV); + tcg_gen_add_i32(temp, result, result); + tcg_gen_xor_i32(temp, temp, result); + tcg_gen_movcond_i32(cond, cpu_PSW_AV, r4, t0, temp, cpu_PSW_AV); /* calc SAV bit */ - tcg_gen_and_tl(temp, temp, mask); - tcg_gen_or_tl(cpu_PSW_SAV, temp, cpu_PSW_SAV); + tcg_gen_and_i32(temp, temp, mask); + tcg_gen_or_i32(cpu_PSW_SAV, temp, cpu_PSW_SAV); /* write back result */ - tcg_gen_movcond_tl(cond, r3, r4, t0, result, r1); + tcg_gen_movcond_i32(cond, r3, r4, t0, result, r1); } static inline void @@ -1575,13 +1575,13 @@ gen_msubs_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, } tcg_gen_extr_i64_i32(temp, temp2, temp64); gen_subs(ret_low, r1_low, temp); - tcg_gen_mov_tl(temp, cpu_PSW_V); - tcg_gen_mov_tl(temp3, cpu_PSW_AV); + tcg_gen_mov_i32(temp, cpu_PSW_V); + tcg_gen_mov_i32(temp3, cpu_PSW_AV); gen_subs(ret_high, r1_high, temp2); /* combine v bits */ - tcg_gen_or_tl(cpu_PSW_V, cpu_PSW_V, temp); + tcg_gen_or_i32(cpu_PSW_V, cpu_PSW_V, temp); /* combine av bits */ - tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp3); + tcg_gen_or_i32(cpu_PSW_AV, cpu_PSW_AV, temp3); } static inline void @@ -1667,8 +1667,8 @@ gen_msubr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); - tcg_gen_andi_tl(temp2, r1, 0xffff0000); - tcg_gen_shli_tl(temp, r1, 16); + tcg_gen_andi_i32(temp2, r1, 0xffff0000); + tcg_gen_shli_i32(temp, r1, 16); gen_msubr64_h(ret, temp, temp2, r2, r3, n, mode); } @@ -1701,8 +1701,8 @@ gen_msubr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); - tcg_gen_andi_tl(temp2, r1, 0xffff0000); - tcg_gen_shli_tl(temp, r1, 16); + tcg_gen_andi_i32(temp2, r1, 0xffff0000); + tcg_gen_shli_i32(temp, r1, 16); gen_msubr64s_h(ret, temp, temp2, r2, r3, n, mode); } @@ -1749,16 +1749,16 @@ gen_msub32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, tcg_gen_setcondi_i64(TCG_COND_LT, t2, t3, -0x80000000LL); tcg_gen_or_i64(t1, t1, t2); tcg_gen_extrl_i64_i32(cpu_PSW_V, t1); - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + tcg_gen_shli_i32(cpu_PSW_V, cpu_PSW_V, 31); /* Calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV/SAV bits */ - tcg_gen_add_tl(cpu_PSW_AV, temp3, temp3); - tcg_gen_xor_tl(cpu_PSW_AV, temp3, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, temp3, temp3); + tcg_gen_xor_i32(cpu_PSW_AV, temp3, cpu_PSW_AV); /* calc SAV */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ - tcg_gen_mov_tl(ret, temp3); + tcg_gen_mov_i32(ret, temp3); } static inline void @@ -1767,13 +1767,13 @@ gen_m16sub32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); if (n == 0) { - tcg_gen_mul_tl(temp, arg2, arg3); + tcg_gen_mul_i32(temp, arg2, arg3); } else { /* n is expected to be 1 */ - tcg_gen_mul_tl(temp, arg2, arg3); - tcg_gen_shli_tl(temp, temp, 1); + tcg_gen_mul_i32(temp, arg2, arg3); + tcg_gen_shli_i32(temp, temp, 1); /* catch special case r1 = r2 = 0x8000 */ - tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, temp, 0x80000000); - tcg_gen_sub_tl(temp, temp, temp2); + tcg_gen_setcondi_i32(TCG_COND_EQ, temp2, temp, 0x80000000); + tcg_gen_sub_i32(temp, temp, temp2); } gen_sub_d(ret, arg1, temp); } @@ -1784,13 +1784,13 @@ gen_m16subs32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); if (n == 0) { - tcg_gen_mul_tl(temp, arg2, arg3); + tcg_gen_mul_i32(temp, arg2, arg3); } else { /* n is expected to be 1 */ - tcg_gen_mul_tl(temp, arg2, arg3); - tcg_gen_shli_tl(temp, temp, 1); + tcg_gen_mul_i32(temp, arg2, arg3); + tcg_gen_shli_i32(temp, temp, 1); /* catch special case r1 = r2 = 0x8000 */ - tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, temp, 0x80000000); - tcg_gen_sub_tl(temp, temp, temp2); + tcg_gen_setcondi_i32(TCG_COND_EQ, temp2, temp, 0x80000000); + tcg_gen_sub_i32(temp, temp, temp2); } gen_subs(ret, arg1, temp); } @@ -1806,13 +1806,13 @@ gen_m16sub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, TCGv_i64 t3 = tcg_temp_new_i64(); if (n == 0) { - tcg_gen_mul_tl(temp, arg2, arg3); + tcg_gen_mul_i32(temp, arg2, arg3); } else { /* n is expected to be 1 */ - tcg_gen_mul_tl(temp, arg2, arg3); - tcg_gen_shli_tl(temp, temp, 1); + tcg_gen_mul_i32(temp, arg2, arg3); + tcg_gen_shli_i32(temp, temp, 1); /* catch special case r1 = r2 = 0x8000 */ - tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, temp, 0x80000000); - tcg_gen_sub_tl(temp, temp, temp2); + tcg_gen_setcondi_i32(TCG_COND_EQ, temp2, temp, 0x80000000); + tcg_gen_sub_i32(temp, temp, temp2); } tcg_gen_ext_i32_i64(t2, temp); tcg_gen_shli_i64(t2, t2, 16); @@ -1832,13 +1832,13 @@ gen_m16subs64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, TCGv_i64 t2 = tcg_temp_new_i64(); if (n == 0) { - tcg_gen_mul_tl(temp, arg2, arg3); + tcg_gen_mul_i32(temp, arg2, arg3); } else { /* n is expected to be 1 */ - tcg_gen_mul_tl(temp, arg2, arg3); - tcg_gen_shli_tl(temp, temp, 1); + tcg_gen_mul_i32(temp, arg2, arg3); + tcg_gen_shli_i32(temp, temp, 1); /* catch special case r1 = r2 = 0x8000 */ - tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, temp, 0x80000000); - tcg_gen_sub_tl(temp, temp, temp2); + tcg_gen_setcondi_i32(TCG_COND_EQ, temp2, temp, 0x80000000); + tcg_gen_sub_i32(temp, temp, temp2); } tcg_gen_ext_i32_i64(t2, temp); tcg_gen_shli_i64(t2, t2, 16); @@ -1878,22 +1878,22 @@ gen_msub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, if (n == 1) { temp = tcg_temp_new(); temp2 = tcg_temp_new(); - tcg_gen_setcondi_tl(TCG_COND_EQ, temp, arg2, 0x80000000); - tcg_gen_setcond_tl(TCG_COND_EQ, temp2, arg2, arg3); - tcg_gen_and_tl(temp, temp, temp2); - tcg_gen_shli_tl(temp, temp, 31); + tcg_gen_setcondi_i32(TCG_COND_EQ, temp, arg2, 0x80000000); + tcg_gen_setcond_i32(TCG_COND_EQ, temp2, arg2, arg3); + tcg_gen_and_i32(temp, temp, temp2); + tcg_gen_shli_i32(temp, temp, 31); /* negate v bit, if special condition */ - tcg_gen_xor_tl(cpu_PSW_V, cpu_PSW_V, temp); + tcg_gen_xor_i32(cpu_PSW_V, cpu_PSW_V, temp); } /* write back result */ tcg_gen_extr_i64_i32(rl, rh, t4); /* Calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV/SAV bits */ - tcg_gen_add_tl(cpu_PSW_AV, rh, rh); - tcg_gen_xor_tl(cpu_PSW_AV, rh, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, rh, rh); + tcg_gen_xor_i32(cpu_PSW_AV, rh, cpu_PSW_AV); /* calc SAV */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } static inline void @@ -2012,8 +2012,8 @@ gen_msubadr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } - tcg_gen_andi_tl(temp2, r1, 0xffff0000); - tcg_gen_shli_tl(temp, r1, 16); + tcg_gen_andi_i32(temp2, r1, 0xffff0000); + tcg_gen_shli_i32(temp, r1, 16); gen_helper_subadr_h(ret, tcg_env, temp64, temp, temp2); } @@ -2043,13 +2043,13 @@ gen_msubads_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, } tcg_gen_extr_i64_i32(temp, temp2, temp64); gen_adds(ret_low, r1_low, temp); - tcg_gen_mov_tl(temp, cpu_PSW_V); - tcg_gen_mov_tl(temp3, cpu_PSW_AV); + tcg_gen_mov_i32(temp, cpu_PSW_V); + tcg_gen_mov_i32(temp3, cpu_PSW_AV); gen_subs(ret_high, r1_high, temp2); /* combine v bits */ - tcg_gen_or_tl(cpu_PSW_V, cpu_PSW_V, temp); + tcg_gen_or_i32(cpu_PSW_V, cpu_PSW_V, temp); /* combine av bits */ - tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp3); + tcg_gen_or_i32(cpu_PSW_AV, cpu_PSW_AV, temp3); } static inline void @@ -2105,24 +2105,24 @@ gen_msubadr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } - tcg_gen_andi_tl(temp2, r1, 0xffff0000); - tcg_gen_shli_tl(temp, r1, 16); + tcg_gen_andi_i32(temp2, r1, 0xffff0000); + tcg_gen_shli_i32(temp, r1, 16); gen_helper_subadr_h_ssov(ret, tcg_env, temp64, temp, temp2); } static inline void gen_abs(TCGv ret, TCGv r1) { - tcg_gen_abs_tl(ret, r1); + tcg_gen_abs_i32(ret, r1); /* overflow can only happen, if r1 = 0x80000000 */ - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_PSW_V, r1, 0x80000000); - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_PSW_V, r1, 0x80000000); + tcg_gen_shli_i32(cpu_PSW_V, cpu_PSW_V, 31); /* calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV bit */ - tcg_gen_add_tl(cpu_PSW_AV, ret, ret); - tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, ret, ret); + tcg_gen_xor_i32(cpu_PSW_AV, ret, cpu_PSW_AV); /* calc SAV bit */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } static inline void gen_absdif(TCGv ret, TCGv r1, TCGv r2) @@ -2130,25 +2130,25 @@ static inline void gen_absdif(TCGv ret, TCGv r1, TCGv r2) TCGv temp = tcg_temp_new_i32(); TCGv result = tcg_temp_new_i32(); - tcg_gen_sub_tl(result, r1, r2); - tcg_gen_sub_tl(temp, r2, r1); - tcg_gen_movcond_tl(TCG_COND_GT, result, r1, r2, result, temp); + tcg_gen_sub_i32(result, r1, r2); + tcg_gen_sub_i32(temp, r2, r1); + tcg_gen_movcond_i32(TCG_COND_GT, result, r1, r2, result, temp); /* calc V bit */ - tcg_gen_xor_tl(cpu_PSW_V, result, r1); - tcg_gen_xor_tl(temp, result, r2); - tcg_gen_movcond_tl(TCG_COND_GT, cpu_PSW_V, r1, r2, cpu_PSW_V, temp); - tcg_gen_xor_tl(temp, r1, r2); - tcg_gen_and_tl(cpu_PSW_V, cpu_PSW_V, temp); + tcg_gen_xor_i32(cpu_PSW_V, result, r1); + tcg_gen_xor_i32(temp, result, r2); + tcg_gen_movcond_i32(TCG_COND_GT, cpu_PSW_V, r1, r2, cpu_PSW_V, temp); + tcg_gen_xor_i32(temp, r1, r2); + tcg_gen_and_i32(cpu_PSW_V, cpu_PSW_V, temp); /* calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV bit */ - tcg_gen_add_tl(cpu_PSW_AV, result, result); - tcg_gen_xor_tl(cpu_PSW_AV, result, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, result, result); + tcg_gen_xor_i32(cpu_PSW_AV, result, cpu_PSW_AV); /* calc SAV bit */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ - tcg_gen_mov_tl(ret, result); + tcg_gen_mov_i32(ret, result); } static inline void gen_absdifi(TCGv ret, TCGv r1, int32_t con) @@ -2168,19 +2168,19 @@ static inline void gen_mul_i32s(TCGv ret, TCGv r1, TCGv r2) TCGv high = tcg_temp_new(); TCGv low = tcg_temp_new(); - tcg_gen_muls2_tl(low, high, r1, r2); - tcg_gen_mov_tl(ret, low); + tcg_gen_muls2_i32(low, high, r1, r2); + tcg_gen_mov_i32(ret, low); /* calc V bit */ - tcg_gen_sari_tl(low, low, 31); - tcg_gen_setcond_tl(TCG_COND_NE, cpu_PSW_V, high, low); - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + tcg_gen_sari_i32(low, low, 31); + tcg_gen_setcond_i32(TCG_COND_NE, cpu_PSW_V, high, low); + tcg_gen_shli_i32(cpu_PSW_V, cpu_PSW_V, 31); /* calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV bit */ - tcg_gen_add_tl(cpu_PSW_AV, ret, ret); - tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, ret, ret); + tcg_gen_xor_i32(cpu_PSW_AV, ret, cpu_PSW_AV); /* calc SAV bit */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } static inline void gen_muli_i32s(TCGv ret, TCGv r1, int32_t con) @@ -2191,16 +2191,16 @@ static inline void gen_muli_i32s(TCGv ret, TCGv r1, int32_t con) static inline void gen_mul_i64s(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) { - tcg_gen_muls2_tl(ret_low, ret_high, r1, r2); + tcg_gen_muls2_i32(ret_low, ret_high, r1, r2); /* clear V bit */ - tcg_gen_movi_tl(cpu_PSW_V, 0); + tcg_gen_movi_i32(cpu_PSW_V, 0); /* calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV bit */ - tcg_gen_add_tl(cpu_PSW_AV, ret_high, ret_high); - tcg_gen_xor_tl(cpu_PSW_AV, ret_high, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, ret_high, ret_high); + tcg_gen_xor_i32(cpu_PSW_AV, ret_high, cpu_PSW_AV); /* calc SAV bit */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } static inline void gen_muli_i64s(TCGv ret_low, TCGv ret_high, TCGv r1, @@ -2212,16 +2212,16 @@ static inline void gen_muli_i64s(TCGv ret_low, TCGv ret_high, TCGv r1, static inline void gen_mul_i64u(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) { - tcg_gen_mulu2_tl(ret_low, ret_high, r1, r2); + tcg_gen_mulu2_i32(ret_low, ret_high, r1, r2); /* clear V bit */ - tcg_gen_movi_tl(cpu_PSW_V, 0); + tcg_gen_movi_i32(cpu_PSW_V, 0); /* calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* Calc AV bit */ - tcg_gen_add_tl(cpu_PSW_AV, ret_high, ret_high); - tcg_gen_xor_tl(cpu_PSW_AV, ret_high, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, ret_high, ret_high); + tcg_gen_xor_i32(cpu_PSW_AV, ret_high, cpu_PSW_AV); /* calc SAV bit */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } static inline void gen_muli_i64u(TCGv ret_low, TCGv ret_high, TCGv r1, @@ -2264,7 +2264,7 @@ gen_mul_q(TCGv rl, TCGv rh, TCGv arg1, TCGv arg2, uint32_t n, uint32_t up_shift) if (n == 0) { if (up_shift == 32) { - tcg_gen_muls2_tl(rh, rl, arg1, arg2); + tcg_gen_muls2_i32(rh, rl, arg1, arg2); } else if (up_shift == 16) { tcg_gen_ext_i32_i64(temp_64, arg1); tcg_gen_ext_i32_i64(temp2_64, arg2); @@ -2273,10 +2273,10 @@ gen_mul_q(TCGv rl, TCGv rh, TCGv arg1, TCGv arg2, uint32_t n, uint32_t up_shift) tcg_gen_shri_i64(temp_64, temp_64, up_shift); tcg_gen_extr_i64_i32(rl, rh, temp_64); } else { - tcg_gen_muls2_tl(rl, rh, arg1, arg2); + tcg_gen_muls2_i32(rl, rh, arg1, arg2); } /* reset v bit */ - tcg_gen_movi_tl(cpu_PSW_V, 0); + tcg_gen_movi_i32(cpu_PSW_V, 0); } else { /* n is expected to be 1 */ tcg_gen_ext_i32_i64(temp_64, arg1); tcg_gen_ext_i32_i64(temp2_64, arg2); @@ -2291,26 +2291,26 @@ gen_mul_q(TCGv rl, TCGv rh, TCGv arg1, TCGv arg2, uint32_t n, uint32_t up_shift) tcg_gen_extr_i64_i32(rl, rh, temp_64); /* overflow only occurs if r1 = r2 = 0x8000 */ if (up_shift == 0) {/* result is 64 bit */ - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_PSW_V, rh, + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_PSW_V, rh, 0x80000000); } else { /* result is 32 bit */ - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_PSW_V, rl, + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_PSW_V, rl, 0x80000000); } - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + tcg_gen_shli_i32(cpu_PSW_V, cpu_PSW_V, 31); /* calc sv overflow bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); } /* calc av overflow bit */ if (up_shift == 0) { - tcg_gen_add_tl(cpu_PSW_AV, rh, rh); - tcg_gen_xor_tl(cpu_PSW_AV, rh, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, rh, rh); + tcg_gen_xor_i32(cpu_PSW_AV, rh, cpu_PSW_AV); } else { - tcg_gen_add_tl(cpu_PSW_AV, rl, rl); - tcg_gen_xor_tl(cpu_PSW_AV, rl, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, rl, rl); + tcg_gen_xor_i32(cpu_PSW_AV, rl, cpu_PSW_AV); } /* calc sav overflow bit */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } static void @@ -2318,47 +2318,47 @@ gen_mul_q_16(TCGv ret, TCGv arg1, TCGv arg2, uint32_t n) { TCGv temp = tcg_temp_new(); if (n == 0) { - tcg_gen_mul_tl(ret, arg1, arg2); + tcg_gen_mul_i32(ret, arg1, arg2); } else { /* n is expected to be 1 */ - tcg_gen_mul_tl(ret, arg1, arg2); - tcg_gen_shli_tl(ret, ret, 1); + tcg_gen_mul_i32(ret, arg1, arg2); + tcg_gen_shli_i32(ret, ret, 1); /* catch special case r1 = r2 = 0x8000 */ - tcg_gen_setcondi_tl(TCG_COND_EQ, temp, ret, 0x80000000); - tcg_gen_sub_tl(ret, ret, temp); + tcg_gen_setcondi_i32(TCG_COND_EQ, temp, ret, 0x80000000); + tcg_gen_sub_i32(ret, ret, temp); } /* reset v bit */ - tcg_gen_movi_tl(cpu_PSW_V, 0); + tcg_gen_movi_i32(cpu_PSW_V, 0); /* calc av overflow bit */ - tcg_gen_add_tl(cpu_PSW_AV, ret, ret); - tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, ret, ret); + tcg_gen_xor_i32(cpu_PSW_AV, ret, cpu_PSW_AV); /* calc sav overflow bit */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } static void gen_mulr_q(TCGv ret, TCGv arg1, TCGv arg2, uint32_t n) { TCGv temp = tcg_temp_new(); if (n == 0) { - tcg_gen_mul_tl(ret, arg1, arg2); - tcg_gen_addi_tl(ret, ret, 0x8000); + tcg_gen_mul_i32(ret, arg1, arg2); + tcg_gen_addi_i32(ret, ret, 0x8000); } else { - tcg_gen_mul_tl(ret, arg1, arg2); - tcg_gen_shli_tl(ret, ret, 1); - tcg_gen_addi_tl(ret, ret, 0x8000); + tcg_gen_mul_i32(ret, arg1, arg2); + tcg_gen_shli_i32(ret, ret, 1); + tcg_gen_addi_i32(ret, ret, 0x8000); /* catch special case r1 = r2 = 0x8000 */ - tcg_gen_setcondi_tl(TCG_COND_EQ, temp, ret, 0x80008000); - tcg_gen_muli_tl(temp, temp, 0x8001); - tcg_gen_sub_tl(ret, ret, temp); + tcg_gen_setcondi_i32(TCG_COND_EQ, temp, ret, 0x80008000); + tcg_gen_muli_i32(temp, temp, 0x8001); + tcg_gen_sub_i32(ret, ret, temp); } /* reset v bit */ - tcg_gen_movi_tl(cpu_PSW_V, 0); + tcg_gen_movi_i32(cpu_PSW_V, 0); /* calc av overflow bit */ - tcg_gen_add_tl(cpu_PSW_AV, ret, ret); - tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, ret, ret); + tcg_gen_xor_i32(cpu_PSW_AV, ret, cpu_PSW_AV); /* calc sav overflow bit */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* cut halfword off */ - tcg_gen_andi_tl(ret, ret, 0xffff0000); + tcg_gen_andi_i32(ret, ret, 0xffff0000); } static inline void @@ -2447,23 +2447,23 @@ gen_msubsui_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, static void gen_saturate(TCGv ret, TCGv arg, int32_t up, int32_t low) { - tcg_gen_smax_tl(ret, arg, tcg_constant_i32(low)); - tcg_gen_smin_tl(ret, ret, tcg_constant_i32(up)); + tcg_gen_smax_i32(ret, arg, tcg_constant_i32(low)); + tcg_gen_smin_i32(ret, ret, tcg_constant_i32(up)); } static void gen_saturate_u(TCGv ret, TCGv arg, int32_t up) { - tcg_gen_umin_tl(ret, arg, tcg_constant_i32(up)); + tcg_gen_umin_i32(ret, arg, tcg_constant_i32(up)); } static void gen_shi(TCGv ret, TCGv r1, int32_t shift_count) { if (shift_count == -32) { - tcg_gen_movi_tl(ret, 0); + tcg_gen_movi_i32(ret, 0); } else if (shift_count >= 0) { - tcg_gen_shli_tl(ret, r1, shift_count); + tcg_gen_shli_i32(ret, r1, shift_count); } else { - tcg_gen_shri_tl(ret, r1, -shift_count); + tcg_gen_shri_i32(ret, r1, -shift_count); } } @@ -2472,16 +2472,16 @@ static void gen_sh_hi(TCGv ret, TCGv r1, int32_t shiftcount) TCGv temp_low, temp_high; if (shiftcount == -16) { - tcg_gen_movi_tl(ret, 0); + tcg_gen_movi_i32(ret, 0); } else { temp_high = tcg_temp_new(); temp_low = tcg_temp_new(); - tcg_gen_andi_tl(temp_low, r1, 0xffff); - tcg_gen_andi_tl(temp_high, r1, 0xffff0000); + tcg_gen_andi_i32(temp_low, r1, 0xffff); + tcg_gen_andi_i32(temp_high, r1, 0xffff0000); gen_shi(temp_low, temp_low, shiftcount); gen_shi(ret, temp_high, shiftcount); - tcg_gen_deposit_tl(ret, ret, temp_low, 0, 16); + tcg_gen_deposit_i32(ret, ret, temp_low, 0, 16); } } @@ -2493,16 +2493,16 @@ static void gen_shaci(TCGv ret, TCGv r1, int32_t shift_count) if (shift_count == 0) { /* Clear PSW.C and PSW.V */ - tcg_gen_movi_tl(cpu_PSW_C, 0); - tcg_gen_mov_tl(cpu_PSW_V, cpu_PSW_C); - tcg_gen_mov_tl(ret, r1); + tcg_gen_movi_i32(cpu_PSW_C, 0); + tcg_gen_mov_i32(cpu_PSW_V, cpu_PSW_C); + tcg_gen_mov_i32(ret, r1); } else if (shift_count == -32) { /* set PSW.C */ - tcg_gen_mov_tl(cpu_PSW_C, r1); + tcg_gen_mov_i32(cpu_PSW_C, r1); /* fill ret completely with sign bit */ - tcg_gen_sari_tl(ret, r1, 31); + tcg_gen_sari_i32(ret, r1, 31); /* clear PSW.V */ - tcg_gen_movi_tl(cpu_PSW_V, 0); + tcg_gen_movi_i32(cpu_PSW_V, 0); } else if (shift_count > 0) { TCGv t_max = tcg_constant_i32(0x7FFFFFFF >> shift_count); TCGv t_min = tcg_constant_i32(((int32_t) -0x80000000) >> shift_count); @@ -2510,30 +2510,30 @@ static void gen_shaci(TCGv ret, TCGv r1, int32_t shift_count) /* calc carry */ msk_start = 32 - shift_count; msk = ((1 << shift_count) - 1) << msk_start; - tcg_gen_andi_tl(cpu_PSW_C, r1, msk); + tcg_gen_andi_i32(cpu_PSW_C, r1, msk); /* calc v/sv bits */ - tcg_gen_setcond_tl(TCG_COND_GT, temp, r1, t_max); - tcg_gen_setcond_tl(TCG_COND_LT, temp2, r1, t_min); - tcg_gen_or_tl(cpu_PSW_V, temp, temp2); - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + tcg_gen_setcond_i32(TCG_COND_GT, temp, r1, t_max); + tcg_gen_setcond_i32(TCG_COND_LT, temp2, r1, t_min); + tcg_gen_or_i32(cpu_PSW_V, temp, temp2); + tcg_gen_shli_i32(cpu_PSW_V, cpu_PSW_V, 31); /* calc sv */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_V, cpu_PSW_SV); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_V, cpu_PSW_SV); /* do shift */ - tcg_gen_shli_tl(ret, r1, shift_count); + tcg_gen_shli_i32(ret, r1, shift_count); } else { /* clear PSW.V */ - tcg_gen_movi_tl(cpu_PSW_V, 0); + tcg_gen_movi_i32(cpu_PSW_V, 0); /* calc carry */ msk = (1 << -shift_count) - 1; - tcg_gen_andi_tl(cpu_PSW_C, r1, msk); + tcg_gen_andi_i32(cpu_PSW_C, r1, msk); /* do shift */ - tcg_gen_sari_tl(ret, r1, -shift_count); + tcg_gen_sari_i32(ret, r1, -shift_count); } /* calc av overflow bit */ - tcg_gen_add_tl(cpu_PSW_AV, ret, ret); - tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, ret, ret); + tcg_gen_xor_i32(cpu_PSW_AV, ret, cpu_PSW_AV); /* calc sav overflow bit */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } static void gen_shas(TCGv ret, TCGv r1, TCGv r2) @@ -2552,23 +2552,23 @@ static void gen_sha_hi(TCGv ret, TCGv r1, int32_t shift_count) TCGv low, high; if (shift_count == 0) { - tcg_gen_mov_tl(ret, r1); + tcg_gen_mov_i32(ret, r1); } else if (shift_count > 0) { low = tcg_temp_new(); high = tcg_temp_new(); - tcg_gen_andi_tl(high, r1, 0xffff0000); - tcg_gen_shli_tl(low, r1, shift_count); - tcg_gen_shli_tl(ret, high, shift_count); - tcg_gen_deposit_tl(ret, ret, low, 0, 16); + tcg_gen_andi_i32(high, r1, 0xffff0000); + tcg_gen_shli_i32(low, r1, shift_count); + tcg_gen_shli_i32(ret, high, shift_count); + tcg_gen_deposit_i32(ret, ret, low, 0, 16); } else { low = tcg_temp_new(); high = tcg_temp_new(); - tcg_gen_ext16s_tl(low, r1); - tcg_gen_sari_tl(low, low, -shift_count); - tcg_gen_sari_tl(ret, r1, -shift_count); - tcg_gen_deposit_tl(ret, ret, low, 0, 16); + tcg_gen_ext16s_i32(low, r1); + tcg_gen_sari_i32(low, low, -shift_count); + tcg_gen_sari_i32(ret, r1, -shift_count); + tcg_gen_deposit_i32(ret, ret, low, 0, 16); } } @@ -2578,9 +2578,9 @@ static void gen_sh_cond(int cond, TCGv ret, TCGv r1, TCGv r2) TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); - tcg_gen_shli_tl(temp, ret, 1); - tcg_gen_setcond_tl(cond, temp2, r1, r2); - tcg_gen_or_tl(ret, temp, temp2); + tcg_gen_shli_i32(temp, ret, 1); + tcg_gen_setcond_i32(cond, temp2, r1, r2); + tcg_gen_or_i32(ret, temp, temp2); } static void gen_sh_condi(int cond, TCGv ret, TCGv r1, int32_t con) @@ -2626,13 +2626,13 @@ static inline void gen_bit_2op(TCGv ret, TCGv r1, TCGv r2, temp1 = tcg_temp_new(); temp2 = tcg_temp_new(); - tcg_gen_shri_tl(temp2, r2, pos2); - tcg_gen_shri_tl(temp1, r1, pos1); + tcg_gen_shri_i32(temp2, r2, pos2); + tcg_gen_shri_i32(temp1, r1, pos1); (*op1)(temp1, temp1, temp2); (*op2)(temp1 , ret, temp1); - tcg_gen_deposit_tl(ret, ret, temp1, 0, 1); + tcg_gen_deposit_i32(ret, ret, temp1, 0, 1); } /* ret = r1[pos1] op1 r2[pos2]; */ @@ -2645,12 +2645,12 @@ static inline void gen_bit_1op(TCGv ret, TCGv r1, TCGv r2, temp1 = tcg_temp_new(); temp2 = tcg_temp_new(); - tcg_gen_shri_tl(temp2, r2, pos2); - tcg_gen_shri_tl(temp1, r1, pos1); + tcg_gen_shri_i32(temp2, r2, pos2); + tcg_gen_shri_i32(temp1, r1, pos1); (*op1)(ret, temp1, temp2); - tcg_gen_andi_tl(ret, ret, 0x1); + tcg_gen_andi_i32(ret, ret, 0x1); } static inline void gen_accumulating_cond(int cond, TCGv ret, TCGv r1, TCGv r2, @@ -2659,13 +2659,13 @@ static inline void gen_accumulating_cond(int cond, TCGv ret, TCGv r1, TCGv r2, TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); /* temp = (arg1 cond arg2 )*/ - tcg_gen_setcond_tl(cond, temp, r1, r2); + tcg_gen_setcond_i32(cond, temp, r1, r2); /* temp2 = ret[0]*/ - tcg_gen_andi_tl(temp2, ret, 0x1); + tcg_gen_andi_i32(temp2, ret, 0x1); /* temp = temp insn temp2 */ (*op)(temp, temp, temp2); /* ret = {ret[31:1], temp} */ - tcg_gen_deposit_tl(ret, ret, temp, 0, 1); + tcg_gen_deposit_i32(ret, ret, temp, 0, 1); } static inline void @@ -2684,25 +2684,25 @@ static inline void gen_eqany_bi(TCGv ret, TCGv r1, int32_t con) TCGv b3 = tcg_temp_new(); /* byte 0 */ - tcg_gen_andi_tl(b0, r1, 0xff); - tcg_gen_setcondi_tl(TCG_COND_EQ, b0, b0, con & 0xff); + tcg_gen_andi_i32(b0, r1, 0xff); + tcg_gen_setcondi_i32(TCG_COND_EQ, b0, b0, con & 0xff); /* byte 1 */ - tcg_gen_andi_tl(b1, r1, 0xff00); - tcg_gen_setcondi_tl(TCG_COND_EQ, b1, b1, con & 0xff00); + tcg_gen_andi_i32(b1, r1, 0xff00); + tcg_gen_setcondi_i32(TCG_COND_EQ, b1, b1, con & 0xff00); /* byte 2 */ - tcg_gen_andi_tl(b2, r1, 0xff0000); - tcg_gen_setcondi_tl(TCG_COND_EQ, b2, b2, con & 0xff0000); + tcg_gen_andi_i32(b2, r1, 0xff0000); + tcg_gen_setcondi_i32(TCG_COND_EQ, b2, b2, con & 0xff0000); /* byte 3 */ - tcg_gen_andi_tl(b3, r1, 0xff000000); - tcg_gen_setcondi_tl(TCG_COND_EQ, b3, b3, con & 0xff000000); + tcg_gen_andi_i32(b3, r1, 0xff000000); + tcg_gen_setcondi_i32(TCG_COND_EQ, b3, b3, con & 0xff000000); /* combine them */ - tcg_gen_or_tl(ret, b0, b1); - tcg_gen_or_tl(ret, ret, b2); - tcg_gen_or_tl(ret, ret, b3); + tcg_gen_or_i32(ret, b0, b1); + tcg_gen_or_i32(ret, ret, b2); + tcg_gen_or_i32(ret, ret, b3); } static inline void gen_eqany_hi(TCGv ret, TCGv r1, int32_t con) @@ -2711,15 +2711,15 @@ static inline void gen_eqany_hi(TCGv ret, TCGv r1, int32_t con) TCGv h1 = tcg_temp_new(); /* halfword 0 */ - tcg_gen_andi_tl(h0, r1, 0xffff); - tcg_gen_setcondi_tl(TCG_COND_EQ, h0, h0, con & 0xffff); + tcg_gen_andi_i32(h0, r1, 0xffff); + tcg_gen_setcondi_i32(TCG_COND_EQ, h0, h0, con & 0xffff); /* halfword 1 */ - tcg_gen_andi_tl(h1, r1, 0xffff0000); - tcg_gen_setcondi_tl(TCG_COND_EQ, h1, h1, con & 0xffff0000); + tcg_gen_andi_i32(h1, r1, 0xffff0000); + tcg_gen_setcondi_i32(TCG_COND_EQ, h1, h1, con & 0xffff0000); /* combine them */ - tcg_gen_or_tl(ret, h0, h1); + tcg_gen_or_i32(ret, h0, h1); } /* mask = ((1 << width) -1) << pos; @@ -2730,14 +2730,14 @@ static inline void gen_insert(TCGv ret, TCGv r1, TCGv r2, TCGv width, TCGv pos) TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); - tcg_gen_shl_tl(mask, tcg_constant_tl(1), width); - tcg_gen_subi_tl(mask, mask, 1); - tcg_gen_shl_tl(mask, mask, pos); + tcg_gen_shl_i32(mask, tcg_constant_i32(1), width); + tcg_gen_subi_i32(mask, mask, 1); + tcg_gen_shl_i32(mask, mask, pos); - tcg_gen_shl_tl(temp, r2, pos); - tcg_gen_and_tl(temp, temp, mask); - tcg_gen_andc_tl(temp2, r1, mask); - tcg_gen_or_tl(ret, temp, temp2); + tcg_gen_shl_i32(temp, r2, pos); + tcg_gen_and_i32(temp, temp, mask); + tcg_gen_andc_i32(temp2, r1, mask); + tcg_gen_or_i32(ret, temp, temp2); } static inline void gen_bsplit(TCGv rl, TCGv rh, TCGv r1) @@ -2786,35 +2786,35 @@ static void gen_calc_usb_mul_h(TCGv arg_low, TCGv arg_high) { TCGv temp = tcg_temp_new(); /* calc AV bit */ - tcg_gen_add_tl(temp, arg_low, arg_low); - tcg_gen_xor_tl(temp, temp, arg_low); - tcg_gen_add_tl(cpu_PSW_AV, arg_high, arg_high); - tcg_gen_xor_tl(cpu_PSW_AV, cpu_PSW_AV, arg_high); - tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp); + tcg_gen_add_i32(temp, arg_low, arg_low); + tcg_gen_xor_i32(temp, temp, arg_low); + tcg_gen_add_i32(cpu_PSW_AV, arg_high, arg_high); + tcg_gen_xor_i32(cpu_PSW_AV, cpu_PSW_AV, arg_high); + tcg_gen_or_i32(cpu_PSW_AV, cpu_PSW_AV, temp); /* calc SAV bit */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - tcg_gen_movi_tl(cpu_PSW_V, 0); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_movi_i32(cpu_PSW_V, 0); } static void gen_calc_usb_mulr_h(TCGv arg) { TCGv temp = tcg_temp_new(); /* calc AV bit */ - tcg_gen_add_tl(temp, arg, arg); - tcg_gen_xor_tl(temp, temp, arg); - tcg_gen_shli_tl(cpu_PSW_AV, temp, 16); - tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp); + tcg_gen_add_i32(temp, arg, arg); + tcg_gen_xor_i32(temp, temp, arg); + tcg_gen_shli_i32(cpu_PSW_AV, temp, 16); + tcg_gen_or_i32(cpu_PSW_AV, cpu_PSW_AV, temp); /* calc SAV bit */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* clear V bit */ - tcg_gen_movi_tl(cpu_PSW_V, 0); + tcg_gen_movi_i32(cpu_PSW_V, 0); } /* helpers for generating program flow micro-ops */ static inline void gen_save_pc(vaddr pc) { - tcg_gen_movi_tl(cpu_PC, pc); + tcg_gen_movi_i32(cpu_PC, pc); } static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_index, vaddr dest) @@ -2844,7 +2844,7 @@ static inline void gen_branch_cond(DisasContext *ctx, TCGCond cond, TCGv r1, TCGv r2, int16_t address) { TCGLabel *jumpLabel = gen_new_label(); - tcg_gen_brcond_tl(cond, r1, r2, jumpLabel); + tcg_gen_brcond_i32(cond, r1, r2, jumpLabel); gen_goto_tb(ctx, 1, ctx->pc_succ_insn); @@ -2863,8 +2863,8 @@ static void gen_loop(DisasContext *ctx, int r1, int32_t offset) { TCGLabel *l1 = gen_new_label(); - tcg_gen_subi_tl(cpu_gpr_a[r1], cpu_gpr_a[r1], 1); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr_a[r1], -1, l1); + tcg_gen_subi_i32(cpu_gpr_a[r1], cpu_gpr_a[r1], 1); + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_gpr_a[r1], -1, l1); gen_goto_tb(ctx, 1, ctx->base.pc_next + offset); gen_set_label(l1); gen_goto_tb(ctx, 0, ctx->pc_succ_insn); @@ -2874,20 +2874,20 @@ static void gen_fcall_save_ctx(DisasContext *ctx) { TCGv temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, cpu_gpr_a[10], -4); - tcg_gen_qemu_st_tl(cpu_gpr_a[11], temp, ctx->mem_idx, MO_LESL); - tcg_gen_movi_tl(cpu_gpr_a[11], ctx->pc_succ_insn); - tcg_gen_mov_tl(cpu_gpr_a[10], temp); + tcg_gen_addi_i32(temp, cpu_gpr_a[10], -4); + tcg_gen_qemu_st_i32(cpu_gpr_a[11], temp, ctx->mem_idx, MO_LESL); + tcg_gen_movi_i32(cpu_gpr_a[11], ctx->pc_succ_insn); + tcg_gen_mov_i32(cpu_gpr_a[10], temp); } static void gen_fret(DisasContext *ctx) { TCGv temp = tcg_temp_new(); - tcg_gen_andi_tl(temp, cpu_gpr_a[11], ~0x1); - tcg_gen_qemu_ld_tl(cpu_gpr_a[11], cpu_gpr_a[10], ctx->mem_idx, MO_LESL); - tcg_gen_addi_tl(cpu_gpr_a[10], cpu_gpr_a[10], 4); - tcg_gen_mov_tl(cpu_PC, temp); + tcg_gen_andi_i32(temp, cpu_gpr_a[11], ~0x1); + tcg_gen_qemu_ld_i32(cpu_gpr_a[11], cpu_gpr_a[10], ctx->mem_idx, MO_LESL); + tcg_gen_addi_i32(cpu_gpr_a[10], cpu_gpr_a[10], 4); + tcg_gen_mov_i32(cpu_PC, temp); ctx->base.is_jmp = DISAS_EXIT; } @@ -2932,12 +2932,12 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, /* SBRN-format jumps */ case OPC1_16_SBRN_JZ_T: temp = tcg_temp_new(); - tcg_gen_andi_tl(temp, cpu_gpr_d[15], 0x1u << constant); + tcg_gen_andi_i32(temp, cpu_gpr_d[15], 0x1u << constant); gen_branch_condi(ctx, TCG_COND_EQ, temp, 0, offset); break; case OPC1_16_SBRN_JNZ_T: temp = tcg_temp_new(); - tcg_gen_andi_tl(temp, cpu_gpr_d[15], 0x1u << constant); + tcg_gen_andi_i32(temp, cpu_gpr_d[15], 0x1u << constant); gen_branch_condi(ctx, TCG_COND_NE, temp, 0, offset); break; /* SBR-format jumps */ @@ -2986,7 +2986,7 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, break; /* SR-format jumps */ case OPC1_16_SR_JI: - tcg_gen_andi_tl(cpu_PC, cpu_gpr_a[r1], 0xfffffffe); + tcg_gen_andi_i32(cpu_PC, cpu_gpr_a[r1], 0xfffffffe); ctx->base.is_jmp = DISAS_EXIT; break; case OPC2_32_SYS_RET: @@ -3008,13 +3008,13 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, gen_goto_tb(ctx, 0, EA_B_ABSOLUT(offset)); break; case OPC1_32_B_JLA: - tcg_gen_movi_tl(cpu_gpr_a[11], ctx->pc_succ_insn); + tcg_gen_movi_i32(cpu_gpr_a[11], ctx->pc_succ_insn); /* fall through */ case OPC1_32_B_JA: gen_goto_tb(ctx, 0, EA_B_ABSOLUT(offset)); break; case OPC1_32_B_JL: - tcg_gen_movi_tl(cpu_gpr_a[11], ctx->pc_succ_insn); + tcg_gen_movi_i32(cpu_gpr_a[11], ctx->pc_succ_insn); gen_goto_tb(ctx, 0, ctx->base.pc_next + offset * 2); break; /* BOL format */ @@ -3046,14 +3046,14 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, case OPCM_32_BRC_JNE: temp = tcg_temp_new(); if (MASK_OP_BRC_OP2(ctx->opcode) == OPC2_32_BRC_JNED) { - tcg_gen_mov_tl(temp, cpu_gpr_d[r1]); + tcg_gen_mov_i32(temp, cpu_gpr_d[r1]); /* subi is unconditional */ - tcg_gen_subi_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 1); + tcg_gen_subi_i32(cpu_gpr_d[r1], cpu_gpr_d[r1], 1); gen_branch_condi(ctx, TCG_COND_NE, temp, constant, offset); } else { - tcg_gen_mov_tl(temp, cpu_gpr_d[r1]); + tcg_gen_mov_i32(temp, cpu_gpr_d[r1]); /* addi is unconditional */ - tcg_gen_addi_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 1); + tcg_gen_addi_i32(cpu_gpr_d[r1], cpu_gpr_d[r1], 1); gen_branch_condi(ctx, TCG_COND_NE, temp, constant, offset); } break; @@ -3062,7 +3062,7 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, n = MASK_OP_BRN_N(ctx->opcode); temp = tcg_temp_new(); - tcg_gen_andi_tl(temp, cpu_gpr_d[r1], (1 << n)); + tcg_gen_andi_i32(temp, cpu_gpr_d[r1], (1 << n)); if (MASK_OP_BRN_OP2(ctx->opcode) == OPC2_32_BRN_JNZ_T) { gen_branch_condi(ctx, TCG_COND_NE, temp, 0, offset); @@ -3119,18 +3119,18 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, temp = tcg_temp_new(); temp2 = tcg_temp_new(); if (MASK_OP_BRC_OP2(ctx->opcode) == OPC2_32_BRR_JNED) { - tcg_gen_mov_tl(temp, cpu_gpr_d[r1]); + tcg_gen_mov_i32(temp, cpu_gpr_d[r1]); /* also save r2, in case of r1 == r2, so r2 is not decremented */ - tcg_gen_mov_tl(temp2, cpu_gpr_d[r2]); + tcg_gen_mov_i32(temp2, cpu_gpr_d[r2]); /* subi is unconditional */ - tcg_gen_subi_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 1); + tcg_gen_subi_i32(cpu_gpr_d[r1], cpu_gpr_d[r1], 1); gen_branch_cond(ctx, TCG_COND_NE, temp, temp2, offset); } else { - tcg_gen_mov_tl(temp, cpu_gpr_d[r1]); + tcg_gen_mov_i32(temp, cpu_gpr_d[r1]); /* also save r2, in case of r1 == r2, so r2 is not decremented */ - tcg_gen_mov_tl(temp2, cpu_gpr_d[r2]); + tcg_gen_mov_i32(temp2, cpu_gpr_d[r2]); /* addi is unconditional */ - tcg_gen_addi_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 1); + tcg_gen_addi_i32(cpu_gpr_d[r1], cpu_gpr_d[r1], 1); gen_branch_cond(ctx, TCG_COND_NE, temp, temp2, offset); } break; @@ -3171,7 +3171,7 @@ static void decode_src_opc(DisasContext *ctx, int op1) gen_addi_d(cpu_gpr_d[15], cpu_gpr_d[r1], const4); break; case OPC1_16_SRC_ADD_A: - tcg_gen_addi_tl(cpu_gpr_a[r1], cpu_gpr_a[r1], const4); + tcg_gen_addi_i32(cpu_gpr_a[r1], cpu_gpr_a[r1], const4); break; case OPC1_16_SRC_CADD: gen_condi_add(TCG_COND_NE, cpu_gpr_d[r1], const4, cpu_gpr_d[r1], @@ -3182,37 +3182,37 @@ static void decode_src_opc(DisasContext *ctx, int op1) cpu_gpr_d[15]); break; case OPC1_16_SRC_CMOV: - temp = tcg_constant_tl(0); - temp2 = tcg_constant_tl(const4); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr_d[r1], cpu_gpr_d[15], temp, + temp = tcg_constant_i32(0); + temp2 = tcg_constant_i32(const4); + tcg_gen_movcond_i32(TCG_COND_NE, cpu_gpr_d[r1], cpu_gpr_d[15], temp, temp2, cpu_gpr_d[r1]); break; case OPC1_16_SRC_CMOVN: - temp = tcg_constant_tl(0); - temp2 = tcg_constant_tl(const4); - tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr_d[r1], cpu_gpr_d[15], temp, + temp = tcg_constant_i32(0); + temp2 = tcg_constant_i32(const4); + tcg_gen_movcond_i32(TCG_COND_EQ, cpu_gpr_d[r1], cpu_gpr_d[15], temp, temp2, cpu_gpr_d[r1]); break; case OPC1_16_SRC_EQ: - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_gpr_d[15], cpu_gpr_d[r1], + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_gpr_d[15], cpu_gpr_d[r1], const4); break; case OPC1_16_SRC_LT: - tcg_gen_setcondi_tl(TCG_COND_LT, cpu_gpr_d[15], cpu_gpr_d[r1], + tcg_gen_setcondi_i32(TCG_COND_LT, cpu_gpr_d[15], cpu_gpr_d[r1], const4); break; case OPC1_16_SRC_MOV: - tcg_gen_movi_tl(cpu_gpr_d[r1], const4); + tcg_gen_movi_i32(cpu_gpr_d[r1], const4); break; case OPC1_16_SRC_MOV_A: const4 = MASK_OP_SRC_CONST4(ctx->opcode); - tcg_gen_movi_tl(cpu_gpr_a[r1], const4); + tcg_gen_movi_i32(cpu_gpr_a[r1], const4); break; case OPC1_16_SRC_MOV_E: if (has_feature(ctx, TRICORE_FEATURE_16)) { CHECK_REG_PAIR(r1); - tcg_gen_movi_tl(cpu_gpr_d[r1], const4); - tcg_gen_sari_tl(cpu_gpr_d[r1+1], cpu_gpr_d[r1], 31); + tcg_gen_movi_i32(cpu_gpr_d[r1], const4); + tcg_gen_sari_i32(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], 31); } else { generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } @@ -3247,49 +3247,49 @@ static void decode_srr_opc(DisasContext *ctx, int op1) gen_add_d(cpu_gpr_d[15], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC1_16_SRR_ADD_A: - tcg_gen_add_tl(cpu_gpr_a[r1], cpu_gpr_a[r1], cpu_gpr_a[r2]); + tcg_gen_add_i32(cpu_gpr_a[r1], cpu_gpr_a[r1], cpu_gpr_a[r2]); break; case OPC1_16_SRR_ADDS: gen_adds(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC1_16_SRR_AND: - tcg_gen_and_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_gen_and_i32(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC1_16_SRR_CMOV: - temp = tcg_constant_tl(0); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr_d[r1], cpu_gpr_d[15], temp, + temp = tcg_constant_i32(0); + tcg_gen_movcond_i32(TCG_COND_NE, cpu_gpr_d[r1], cpu_gpr_d[15], temp, cpu_gpr_d[r2], cpu_gpr_d[r1]); break; case OPC1_16_SRR_CMOVN: - temp = tcg_constant_tl(0); - tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr_d[r1], cpu_gpr_d[15], temp, + temp = tcg_constant_i32(0); + tcg_gen_movcond_i32(TCG_COND_EQ, cpu_gpr_d[r1], cpu_gpr_d[15], temp, cpu_gpr_d[r2], cpu_gpr_d[r1]); break; case OPC1_16_SRR_EQ: - tcg_gen_setcond_tl(TCG_COND_EQ, cpu_gpr_d[15], cpu_gpr_d[r1], + tcg_gen_setcond_i32(TCG_COND_EQ, cpu_gpr_d[15], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC1_16_SRR_LT: - tcg_gen_setcond_tl(TCG_COND_LT, cpu_gpr_d[15], cpu_gpr_d[r1], + tcg_gen_setcond_i32(TCG_COND_LT, cpu_gpr_d[15], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC1_16_SRR_MOV: - tcg_gen_mov_tl(cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_gen_mov_i32(cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC1_16_SRR_MOV_A: - tcg_gen_mov_tl(cpu_gpr_a[r1], cpu_gpr_d[r2]); + tcg_gen_mov_i32(cpu_gpr_a[r1], cpu_gpr_d[r2]); break; case OPC1_16_SRR_MOV_AA: - tcg_gen_mov_tl(cpu_gpr_a[r1], cpu_gpr_a[r2]); + tcg_gen_mov_i32(cpu_gpr_a[r1], cpu_gpr_a[r2]); break; case OPC1_16_SRR_MOV_D: - tcg_gen_mov_tl(cpu_gpr_d[r1], cpu_gpr_a[r2]); + tcg_gen_mov_i32(cpu_gpr_d[r1], cpu_gpr_a[r2]); break; case OPC1_16_SRR_MUL: gen_mul_i32s(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC1_16_SRR_OR: - tcg_gen_or_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_gen_or_i32(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC1_16_SRR_SUB: gen_sub_d(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); @@ -3304,7 +3304,7 @@ static void decode_srr_opc(DisasContext *ctx, int op1) gen_subs(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC1_16_SRR_XOR: - tcg_gen_xor_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_gen_xor_i32(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -3320,32 +3320,32 @@ static void decode_ssr_opc(DisasContext *ctx, int op1) switch (op1) { case OPC1_16_SSR_ST_A: - tcg_gen_qemu_st_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); + tcg_gen_qemu_st_i32(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); break; case OPC1_16_SSR_ST_A_POSTINC: - tcg_gen_qemu_st_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); + tcg_gen_qemu_st_i32(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); break; case OPC1_16_SSR_ST_B: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); break; case OPC1_16_SSR_ST_B_POSTINC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 1); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], 1); break; case OPC1_16_SSR_ST_H: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUW); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUW); break; case OPC1_16_SSR_ST_H_POSTINC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUW); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 2); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUW); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], 2); break; case OPC1_16_SSR_ST_W: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); break; case OPC1_16_SSR_ST_W_POSTINC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -3360,7 +3360,7 @@ static void decode_sc_opc(DisasContext *ctx, int op1) switch (op1) { case OPC1_16_SC_AND: - tcg_gen_andi_tl(cpu_gpr_d[15], cpu_gpr_d[15], const16); + tcg_gen_andi_i32(cpu_gpr_d[15], cpu_gpr_d[15], const16); break; case OPC1_16_SC_BISR: if (ctx->priv == TRICORE_PRIV_SM) { @@ -3376,10 +3376,10 @@ static void decode_sc_opc(DisasContext *ctx, int op1) gen_offset_ld(ctx, cpu_gpr_d[15], cpu_gpr_a[10], const16 * 4, MO_LESL); break; case OPC1_16_SC_MOV: - tcg_gen_movi_tl(cpu_gpr_d[15], const16); + tcg_gen_movi_i32(cpu_gpr_d[15], const16); break; case OPC1_16_SC_OR: - tcg_gen_ori_tl(cpu_gpr_d[15], cpu_gpr_d[15], const16); + tcg_gen_ori_i32(cpu_gpr_d[15], cpu_gpr_d[15], const16); break; case OPC1_16_SC_ST_A: gen_offset_st(ctx, cpu_gpr_a[15], cpu_gpr_a[10], const16 * 4, MO_LESL); @@ -3388,7 +3388,7 @@ static void decode_sc_opc(DisasContext *ctx, int op1) gen_offset_st(ctx, cpu_gpr_d[15], cpu_gpr_a[10], const16 * 4, MO_LESL); break; case OPC1_16_SC_SUB_A: - tcg_gen_subi_tl(cpu_gpr_a[10], cpu_gpr_a[10], const16); + tcg_gen_subi_i32(cpu_gpr_a[10], cpu_gpr_a[10], const16); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -3405,32 +3405,32 @@ static void decode_slr_opc(DisasContext *ctx, int op1) switch (op1) { /* SLR-format */ case OPC1_16_SLR_LD_A: - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESL); + tcg_gen_qemu_ld_i32(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESL); break; case OPC1_16_SLR_LD_A_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESL); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); + tcg_gen_qemu_ld_i32(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESL); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); break; case OPC1_16_SLR_LD_BU: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); break; case OPC1_16_SLR_LD_BU_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 1); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], 1); break; case OPC1_16_SLR_LD_H: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESW); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESW); break; case OPC1_16_SLR_LD_H_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESW); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 2); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESW); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], 2); break; case OPC1_16_SLR_LD_W: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESL); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESL); break; case OPC1_16_SLR_LD_W_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESL); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESL); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -3513,17 +3513,18 @@ static void decode_sr_accu(DisasContext *ctx) switch (op2) { case OPC2_16_SR_RSUB: /* calc V bit -- overflow only if r1 = -0x80000000 */ - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_PSW_V, cpu_gpr_d[r1], -0x80000000); - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_PSW_V, + cpu_gpr_d[r1], -0x80000000); + tcg_gen_shli_i32(cpu_PSW_V, cpu_PSW_V, 31); /* calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* sub */ - tcg_gen_neg_tl(cpu_gpr_d[r1], cpu_gpr_d[r1]); + tcg_gen_neg_i32(cpu_gpr_d[r1], cpu_gpr_d[r1]); /* calc av */ - tcg_gen_add_tl(cpu_PSW_AV, cpu_gpr_d[r1], cpu_gpr_d[r1]); - tcg_gen_xor_tl(cpu_PSW_AV, cpu_gpr_d[r1], cpu_PSW_AV); + tcg_gen_add_i32(cpu_PSW_AV, cpu_gpr_d[r1], cpu_gpr_d[r1]); + tcg_gen_xor_i32(cpu_PSW_AV, cpu_gpr_d[r1], cpu_PSW_AV); /* calc sav */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); break; case OPC2_16_SR_SAT_B: gen_saturate(cpu_gpr_d[r1], cpu_gpr_d[r1], 0x7f, -0x80); @@ -3616,8 +3617,8 @@ static void decode_16Bit_opc(DisasContext *ctx) r1 = MASK_OP_SRRS_S1D(ctx->opcode); const16 = MASK_OP_SRRS_N(ctx->opcode); temp = tcg_temp_new(); - tcg_gen_shli_tl(temp, cpu_gpr_d[15], const16); - tcg_gen_add_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], temp); + tcg_gen_shli_i32(temp, cpu_gpr_d[15], const16); + tcg_gen_add_i32(cpu_gpr_a[r1], cpu_gpr_a[r2], temp); break; /* SLRO-format */ case OPC1_16_SLRO_LD_A: @@ -3766,7 +3767,7 @@ static void decode_16Bit_opc(DisasContext *ctx) break; case OPC1_16_SR_NOT: r1 = MASK_OP_SR_S1D(ctx->opcode); - tcg_gen_not_tl(cpu_gpr_d[r1], cpu_gpr_d[r1]); + tcg_gen_not_i32(cpu_gpr_d[r1], cpu_gpr_d[r1]); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -3793,18 +3794,18 @@ static void decode_abs_ldw(DisasContext *ctx) switch (op2) { case OPC2_32_ABS_LD_A: - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LESL); + tcg_gen_qemu_ld_i32(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LESL); break; case OPC2_32_ABS_LD_D: CHECK_REG_PAIR(r1); - gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); + gen_ld_2regs_64(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp, ctx); break; case OPC2_32_ABS_LD_DA: CHECK_REG_PAIR(r1); - gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); + gen_ld_2regs_64(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp, ctx); break; case OPC2_32_ABS_LD_W: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESL); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESL); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -3826,16 +3827,16 @@ static void decode_abs_ldb(DisasContext *ctx) switch (op2) { case OPC2_32_ABS_LD_B: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_SB); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_SB); break; case OPC2_32_ABS_LD_BU: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_UB); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_UB); break; case OPC2_32_ABS_LD_H: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESW); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESW); break; case OPC2_32_ABS_LD_HU: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUW); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUW); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -3908,18 +3909,18 @@ static void decode_abs_store(DisasContext *ctx) switch (op2) { case OPC2_32_ABS_ST_A: - tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LESL); + tcg_gen_qemu_st_i32(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LESL); break; case OPC2_32_ABS_ST_D: CHECK_REG_PAIR(r1); - gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); + gen_st_2regs_64(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp, ctx); break; case OPC2_32_ABS_ST_DA: CHECK_REG_PAIR(r1); - gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); + gen_st_2regs_64(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp, ctx); break; case OPC2_32_ABS_ST_W: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESL); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESL); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -3941,10 +3942,10 @@ static void decode_abs_storeb_h(DisasContext *ctx) switch (op2) { case OPC2_32_ABS_ST_B: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_UB); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_UB); break; case OPC2_32_ABS_ST_H: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUW); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUW); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -4043,11 +4044,11 @@ static void decode_bit_insert(DisasContext *ctx) temp = tcg_temp_new(); - tcg_gen_shri_tl(temp, cpu_gpr_d[r2], pos2); + tcg_gen_shri_i32(temp, cpu_gpr_d[r2], pos2); if (op2 == OPC2_32_BIT_INSN_T) { - tcg_gen_not_tl(temp, temp); + tcg_gen_not_i32(temp, temp); } - tcg_gen_deposit_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], temp, pos1, 1); + tcg_gen_deposit_i32(cpu_gpr_d[r3], cpu_gpr_d[r1], temp, pos1, 1); } static void decode_bit_logical_t2(DisasContext *ctx) @@ -4163,8 +4164,8 @@ static void decode_bit_sh_logic1(DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], 1); - tcg_gen_add_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], temp); + tcg_gen_shli_i32(cpu_gpr_d[r3], cpu_gpr_d[r3], 1); + tcg_gen_add_i32(cpu_gpr_d[r3], cpu_gpr_d[r3], temp); } static void decode_bit_sh_logic2(DisasContext *ctx) @@ -4203,8 +4204,8 @@ static void decode_bit_sh_logic2(DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], 1); - tcg_gen_add_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], temp); + tcg_gen_shli_i32(cpu_gpr_d[r3], cpu_gpr_d[r3], 1); + tcg_gen_add_i32(cpu_gpr_d[r3], cpu_gpr_d[r3], temp); } /* BO-format */ @@ -4233,14 +4234,14 @@ static void decode_bo_addrmode_post_pre_base(DisasContext *ctx) case OPC2_32_BO_CACHEA_I_POSTINC: /* instruction to access the cache, but we still need to handle the addressing mode */ - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_CACHEA_WI_PREINC: case OPC2_32_BO_CACHEA_W_PREINC: case OPC2_32_BO_CACHEA_I_PREINC: /* instruction to access the cache, but we still need to handle the addressing mode */ - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_CACHEI_WI_SHORTOFF: case OPC2_32_BO_CACHEI_W_SHORTOFF: @@ -4251,7 +4252,7 @@ static void decode_bo_addrmode_post_pre_base(DisasContext *ctx) case OPC2_32_BO_CACHEI_W_POSTINC: case OPC2_32_BO_CACHEI_WI_POSTINC: if (has_feature(ctx, TRICORE_FEATURE_131)) { - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); } else { generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } @@ -4259,7 +4260,7 @@ static void decode_bo_addrmode_post_pre_base(DisasContext *ctx) case OPC2_32_BO_CACHEI_W_PREINC: case OPC2_32_BO_CACHEI_WI_PREINC: if (has_feature(ctx, TRICORE_FEATURE_131)) { - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); } else { generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } @@ -4268,9 +4269,9 @@ static void decode_bo_addrmode_post_pre_base(DisasContext *ctx) gen_offset_st(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LESL); break; case OPC2_32_BO_ST_A_POSTINC: - tcg_gen_qemu_st_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, + tcg_gen_qemu_st_i32(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESL); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_ST_A_PREINC: gen_st_preincr(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LESL); @@ -4279,82 +4280,82 @@ static void decode_bo_addrmode_post_pre_base(DisasContext *ctx) gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_UB); break; case OPC2_32_BO_ST_B_POSTINC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_ST_B_PREINC: gen_st_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_UB); break; case OPC2_32_BO_ST_D_SHORTOFF: CHECK_REG_PAIR(r1); - gen_offset_st_2regs(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], + gen_offset_st_2regs(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], cpu_gpr_a[r2], off10, ctx); break; case OPC2_32_BO_ST_D_POSTINC: CHECK_REG_PAIR(r1); - gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], ctx); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + gen_st_2regs_64(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], cpu_gpr_a[r2], ctx); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_ST_D_PREINC: CHECK_REG_PAIR(r1); temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); - tcg_gen_mov_tl(cpu_gpr_a[r2], temp); + tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); + gen_st_2regs_64(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp, ctx); + tcg_gen_mov_i32(cpu_gpr_a[r2], temp); break; case OPC2_32_BO_ST_DA_SHORTOFF: CHECK_REG_PAIR(r1); - gen_offset_st_2regs(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], + gen_offset_st_2regs(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], cpu_gpr_a[r2], off10, ctx); break; case OPC2_32_BO_ST_DA_POSTINC: CHECK_REG_PAIR(r1); - gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], ctx); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + gen_st_2regs_64(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], cpu_gpr_a[r2], ctx); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_ST_DA_PREINC: CHECK_REG_PAIR(r1); temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); - tcg_gen_mov_tl(cpu_gpr_a[r2], temp); + tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); + gen_st_2regs_64(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp, ctx); + tcg_gen_mov_i32(cpu_gpr_a[r2], temp); break; case OPC2_32_BO_ST_H_SHORTOFF: gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); break; case OPC2_32_BO_ST_H_POSTINC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUW); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_ST_H_PREINC: gen_st_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); break; case OPC2_32_BO_ST_Q_SHORTOFF: temp = tcg_temp_new(); - tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_shri_i32(temp, cpu_gpr_d[r1], 16); gen_offset_st(ctx, temp, cpu_gpr_a[r2], off10, MO_LEUW); break; case OPC2_32_BO_ST_Q_POSTINC: temp = tcg_temp_new(); - tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_qemu_st_tl(temp, cpu_gpr_a[r2], ctx->mem_idx, + tcg_gen_shri_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_qemu_st_i32(temp, cpu_gpr_a[r2], ctx->mem_idx, MO_LEUW); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_ST_Q_PREINC: temp = tcg_temp_new(); - tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_shri_i32(temp, cpu_gpr_d[r1], 16); gen_st_preincr(ctx, temp, cpu_gpr_a[r2], off10, MO_LEUW); break; case OPC2_32_BO_ST_W_SHORTOFF: gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL); break; case OPC2_32_BO_ST_W_POSTINC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_ST_W_PREINC: gen_st_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL); @@ -4380,91 +4381,91 @@ static void decode_bo_addrmode_bitreverse_circular(DisasContext *ctx) temp2 = tcg_temp_new(); t_off10 = tcg_constant_i32(off10); CHECK_REG_PAIR(r2); - tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]); - tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); + tcg_gen_ext16u_i32(temp, cpu_gpr_a[r2 + 1]); + tcg_gen_add_i32(temp2, cpu_gpr_a[r2], temp); switch (op2) { case OPC2_32_BO_CACHEA_WI_BR: case OPC2_32_BO_CACHEA_W_BR: case OPC2_32_BO_CACHEA_I_BR: - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_CACHEA_WI_CIRC: case OPC2_32_BO_CACHEA_W_CIRC: case OPC2_32_BO_CACHEA_I_CIRC: - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_ST_A_BR: - tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + tcg_gen_qemu_st_i32(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_ST_A_CIRC: - tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + tcg_gen_qemu_st_i32(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_ST_B_BR: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_ST_B_CIRC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_ST_D_BR: CHECK_REG_PAIR(r1); - gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp2, ctx); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + gen_st_2regs_64(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp2, ctx); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_ST_D_CIRC: CHECK_REG_PAIR(r1); - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); - tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16); - tcg_gen_addi_tl(temp, temp, 4); - tcg_gen_rem_tl(temp, temp, temp2); - tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); - tcg_gen_qemu_st_tl(cpu_gpr_d[r1+1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); + tcg_gen_shri_i32(temp2, cpu_gpr_a[r2 + 1], 16); + tcg_gen_addi_i32(temp, temp, 4); + tcg_gen_rem_i32(temp, temp, temp2); + tcg_gen_add_i32(temp2, cpu_gpr_a[r2], temp); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1 + 1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_ST_DA_BR: CHECK_REG_PAIR(r1); - gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp2, ctx); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + gen_st_2regs_64(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp2, ctx); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_ST_DA_CIRC: CHECK_REG_PAIR(r1); - tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); - tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16); - tcg_gen_addi_tl(temp, temp, 4); - tcg_gen_rem_tl(temp, temp, temp2); - tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); - tcg_gen_qemu_st_tl(cpu_gpr_a[r1+1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + tcg_gen_qemu_st_i32(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); + tcg_gen_shri_i32(temp2, cpu_gpr_a[r2 + 1], 16); + tcg_gen_addi_i32(temp, temp, 4); + tcg_gen_rem_i32(temp, temp, temp2); + tcg_gen_add_i32(temp2, cpu_gpr_a[r2], temp); + tcg_gen_qemu_st_i32(cpu_gpr_a[r1 + 1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_ST_H_BR: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_ST_H_CIRC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_ST_Q_BR: - tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_qemu_st_tl(temp, temp2, ctx->mem_idx, MO_LEUW); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + tcg_gen_shri_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_qemu_st_i32(temp, temp2, ctx->mem_idx, MO_LEUW); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_ST_Q_CIRC: - tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_qemu_st_tl(temp, temp2, ctx->mem_idx, MO_LEUW); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + tcg_gen_shri_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_qemu_st_i32(temp, temp2, ctx->mem_idx, MO_LEUW); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_ST_W_BR: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_ST_W_CIRC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + tcg_gen_qemu_st_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -4488,9 +4489,9 @@ static void decode_bo_addrmode_ld_post_pre_base(DisasContext *ctx) gen_offset_ld(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LEUL); break; case OPC2_32_BO_LD_A_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, + tcg_gen_qemu_ld_i32(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_LD_A_PREINC: gen_ld_preincr(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LEUL); @@ -4499,9 +4500,9 @@ static void decode_bo_addrmode_ld_post_pre_base(DisasContext *ctx) gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_SB); break; case OPC2_32_BO_LD_B_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_SB); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_LD_B_PREINC: gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_SB); @@ -4510,54 +4511,54 @@ static void decode_bo_addrmode_ld_post_pre_base(DisasContext *ctx) gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_UB); break; case OPC2_32_BO_LD_BU_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_LD_BU_PREINC: gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_UB); break; case OPC2_32_BO_LD_D_SHORTOFF: CHECK_REG_PAIR(r1); - gen_offset_ld_2regs(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], + gen_offset_ld_2regs(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], cpu_gpr_a[r2], off10, ctx); break; case OPC2_32_BO_LD_D_POSTINC: CHECK_REG_PAIR(r1); - gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], ctx); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + gen_ld_2regs_64(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], cpu_gpr_a[r2], ctx); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_LD_D_PREINC: CHECK_REG_PAIR(r1); temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); - tcg_gen_mov_tl(cpu_gpr_a[r2], temp); + tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); + gen_ld_2regs_64(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp, ctx); + tcg_gen_mov_i32(cpu_gpr_a[r2], temp); break; case OPC2_32_BO_LD_DA_SHORTOFF: CHECK_REG_PAIR(r1); - gen_offset_ld_2regs(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], + gen_offset_ld_2regs(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], cpu_gpr_a[r2], off10, ctx); break; case OPC2_32_BO_LD_DA_POSTINC: CHECK_REG_PAIR(r1); - gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], ctx); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + gen_ld_2regs_64(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], cpu_gpr_a[r2], ctx); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_LD_DA_PREINC: CHECK_REG_PAIR(r1); temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); - tcg_gen_mov_tl(cpu_gpr_a[r2], temp); + tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); + gen_ld_2regs_64(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp, ctx); + tcg_gen_mov_i32(cpu_gpr_a[r2], temp); break; case OPC2_32_BO_LD_H_SHORTOFF: gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LESW); break; case OPC2_32_BO_LD_H_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESW); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_LD_H_PREINC: gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LESW); @@ -4566,34 +4567,34 @@ static void decode_bo_addrmode_ld_post_pre_base(DisasContext *ctx) gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); break; case OPC2_32_BO_LD_HU_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUW); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_LD_HU_PREINC: gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); break; case OPC2_32_BO_LD_Q_SHORTOFF: gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); - tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); + tcg_gen_shli_i32(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); break; case OPC2_32_BO_LD_Q_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUW); - tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_shli_i32(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_LD_Q_PREINC: gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); - tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); + tcg_gen_shli_i32(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); break; case OPC2_32_BO_LD_W_SHORTOFF: gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL); break; case OPC2_32_BO_LD_W_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_LD_W_PREINC: gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL); @@ -4619,98 +4620,98 @@ static void decode_bo_addrmode_ld_bitreverse_circular(DisasContext *ctx) temp2 = tcg_temp_new(); t_off10 = tcg_constant_i32(off10); CHECK_REG_PAIR(r2); - tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]); - tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); + tcg_gen_ext16u_i32(temp, cpu_gpr_a[r2 + 1]); + tcg_gen_add_i32(temp2, cpu_gpr_a[r2], temp); switch (op2) { case OPC2_32_BO_LD_A_BR: - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + tcg_gen_qemu_ld_i32(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_LD_A_CIRC: - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + tcg_gen_qemu_ld_i32(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_LD_B_BR: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_SB); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_SB); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_LD_B_CIRC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_SB); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_SB); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_LD_BU_BR: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_LD_BU_CIRC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_LD_D_BR: CHECK_REG_PAIR(r1); - gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp2, ctx); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + gen_ld_2regs_64(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp2, ctx); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_LD_D_CIRC: CHECK_REG_PAIR(r1); - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); - tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16); - tcg_gen_addi_tl(temp, temp, 4); - tcg_gen_rem_tl(temp, temp, temp2); - tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1+1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); + tcg_gen_shri_i32(temp2, cpu_gpr_a[r2 + 1], 16); + tcg_gen_addi_i32(temp, temp, 4); + tcg_gen_rem_i32(temp, temp, temp2); + tcg_gen_add_i32(temp2, cpu_gpr_a[r2], temp); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1 + 1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_LD_DA_BR: CHECK_REG_PAIR(r1); - gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp2, ctx); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + gen_ld_2regs_64(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp2, ctx); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_LD_DA_CIRC: CHECK_REG_PAIR(r1); - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); - tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16); - tcg_gen_addi_tl(temp, temp, 4); - tcg_gen_rem_tl(temp, temp, temp2); - tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1+1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + tcg_gen_qemu_ld_i32(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); + tcg_gen_shri_i32(temp2, cpu_gpr_a[r2 + 1], 16); + tcg_gen_addi_i32(temp, temp, 4); + tcg_gen_rem_i32(temp, temp, temp2); + tcg_gen_add_i32(temp2, cpu_gpr_a[r2], temp); + tcg_gen_qemu_ld_i32(cpu_gpr_a[r1 + 1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_LD_H_BR: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LESW); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LESW); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_LD_H_CIRC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LESW); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LESW); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_LD_HU_BR: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_LD_HU_CIRC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_LD_Q_BR: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); - tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); + tcg_gen_shli_i32(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_LD_Q_CIRC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); - tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); + tcg_gen_shli_i32(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_LD_W_BR: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_LD_W_CIRC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -4735,70 +4736,70 @@ static void decode_bo_addrmode_stctx_post_pre_base(DisasContext *ctx) switch (op2) { case OPC2_32_BO_LDLCX_SHORTOFF: - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); gen_helper_ldlcx(tcg_env, temp); break; case OPC2_32_BO_LDMST_SHORTOFF: - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); gen_ldmst(ctx, r1, temp); break; case OPC2_32_BO_LDMST_POSTINC: gen_ldmst(ctx, r1, cpu_gpr_a[r2]); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_LDMST_PREINC: - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); gen_ldmst(ctx, r1, cpu_gpr_a[r2]); break; case OPC2_32_BO_LDUCX_SHORTOFF: - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); gen_helper_lducx(tcg_env, temp); break; case OPC2_32_BO_LEA_SHORTOFF: - tcg_gen_addi_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r1], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_STLCX_SHORTOFF: - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); gen_helper_stlcx(tcg_env, temp); break; case OPC2_32_BO_STUCX_SHORTOFF: - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); gen_helper_stucx(tcg_env, temp); break; case OPC2_32_BO_SWAP_W_SHORTOFF: - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); gen_swap(ctx, r1, temp); break; case OPC2_32_BO_SWAP_W_POSTINC: gen_swap(ctx, r1, cpu_gpr_a[r2]); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_SWAP_W_PREINC: - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); gen_swap(ctx, r1, cpu_gpr_a[r2]); break; case OPC2_32_BO_CMPSWAP_W_SHORTOFF: - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); gen_cmpswap(ctx, r1, temp); break; case OPC2_32_BO_CMPSWAP_W_POSTINC: gen_cmpswap(ctx, r1, cpu_gpr_a[r2]); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_CMPSWAP_W_PREINC: - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); gen_cmpswap(ctx, r1, cpu_gpr_a[r2]); break; case OPC2_32_BO_SWAPMSK_W_SHORTOFF: - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); gen_swapmsk(ctx, r1, temp); break; case OPC2_32_BO_SWAPMSK_W_POSTINC: gen_swapmsk(ctx, r1, cpu_gpr_a[r2]); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_SWAPMSK_W_PREINC: - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); gen_swapmsk(ctx, r1, cpu_gpr_a[r2]); break; default: @@ -4822,41 +4823,41 @@ static void decode_bo_addrmode_ldmst_bitreverse_circular(DisasContext *ctx) temp2 = tcg_temp_new(); t_off10 = tcg_constant_i32(off10); CHECK_REG_PAIR(r2); - tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]); - tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); + tcg_gen_ext16u_i32(temp, cpu_gpr_a[r2 + 1]); + tcg_gen_add_i32(temp2, cpu_gpr_a[r2], temp); switch (op2) { case OPC2_32_BO_LDMST_BR: gen_ldmst(ctx, r1, temp2); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_LDMST_CIRC: gen_ldmst(ctx, r1, temp2); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_SWAP_W_BR: gen_swap(ctx, r1, temp2); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_SWAP_W_CIRC: gen_swap(ctx, r1, temp2); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_CMPSWAP_W_BR: gen_cmpswap(ctx, r1, temp2); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_CMPSWAP_W_CIRC: gen_cmpswap(ctx, r1, temp2); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; case OPC2_32_BO_SWAPMSK_W_BR: gen_swapmsk(ctx, r1, temp2); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_SWAPMSK_W_CIRC: gen_swapmsk(ctx, r1, temp2); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); + gen_helper_circ_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1], t_off10); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -4876,16 +4877,16 @@ static void decode_bol_opc(DisasContext *ctx, int32_t op1) switch (op1) { case OPC1_32_BOL_LD_A_LONGOFF: temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], address); - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LEUL); + tcg_gen_addi_i32(temp, cpu_gpr_a[r2], address); + tcg_gen_qemu_ld_i32(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LEUL); break; case OPC1_32_BOL_LD_W_LONGOFF: temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], address); - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUL); + tcg_gen_addi_i32(temp, cpu_gpr_a[r2], address); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUL); break; case OPC1_32_BOL_LEA_LONGOFF: - tcg_gen_addi_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], address); + tcg_gen_addi_i32(cpu_gpr_a[r1], cpu_gpr_a[r2], address); break; case OPC1_32_BOL_ST_A_LONGOFF: if (has_feature(ctx, TRICORE_FEATURE_16)) { @@ -4959,26 +4960,26 @@ static void decode_rc_logical_shift(DisasContext *ctx) switch (op2) { case OPC2_32_RC_AND: - tcg_gen_andi_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + tcg_gen_andi_i32(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; case OPC2_32_RC_ANDN: - tcg_gen_andi_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], ~const9); + tcg_gen_andi_i32(cpu_gpr_d[r2], cpu_gpr_d[r1], ~const9); break; case OPC2_32_RC_NAND: temp = tcg_temp_new(); - tcg_gen_movi_tl(temp, const9); - tcg_gen_nand_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], temp); + tcg_gen_movi_i32(temp, const9); + tcg_gen_nand_i32(cpu_gpr_d[r2], cpu_gpr_d[r1], temp); break; case OPC2_32_RC_NOR: temp = tcg_temp_new(); - tcg_gen_movi_tl(temp, const9); - tcg_gen_nor_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], temp); + tcg_gen_movi_i32(temp, const9); + tcg_gen_nor_i32(cpu_gpr_d[r2], cpu_gpr_d[r1], temp); break; case OPC2_32_RC_OR: - tcg_gen_ori_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + tcg_gen_ori_i32(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; case OPC2_32_RC_ORN: - tcg_gen_ori_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], ~const9); + tcg_gen_ori_i32(cpu_gpr_d[r2], cpu_gpr_d[r1], ~const9); break; case OPC2_32_RC_SH: const9 = sextract32(const9, 0, 6); @@ -5000,11 +5001,11 @@ static void decode_rc_logical_shift(DisasContext *ctx) gen_shasi(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; case OPC2_32_RC_XNOR: - tcg_gen_xori_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); - tcg_gen_not_tl(cpu_gpr_d[r2], cpu_gpr_d[r2]); + tcg_gen_xori_i32(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + tcg_gen_not_i32(cpu_gpr_d[r2], cpu_gpr_d[r2]); break; case OPC2_32_RC_XOR: - tcg_gen_xori_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + tcg_gen_xori_i32(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; case OPC2_32_RC_SHUFFLE: if (has_feature(ctx, TRICORE_FEATURE_162)) { @@ -5084,7 +5085,7 @@ static void decode_rc_accumulator(DisasContext *ctx) const9, &tcg_gen_and_tl); break; case OPC2_32_RC_EQ: - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; case OPC2_32_RC_EQANY_B: gen_eqany_bi(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); @@ -5093,41 +5094,41 @@ static void decode_rc_accumulator(DisasContext *ctx) gen_eqany_hi(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; case OPC2_32_RC_GE: - tcg_gen_setcondi_tl(TCG_COND_GE, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + tcg_gen_setcondi_i32(TCG_COND_GE, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; case OPC2_32_RC_GE_U: const9 = MASK_OP_RC_CONST9(ctx->opcode); - tcg_gen_setcondi_tl(TCG_COND_GEU, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; case OPC2_32_RC_LT: - tcg_gen_setcondi_tl(TCG_COND_LT, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + tcg_gen_setcondi_i32(TCG_COND_LT, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; case OPC2_32_RC_LT_U: const9 = MASK_OP_RC_CONST9(ctx->opcode); - tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + tcg_gen_setcondi_i32(TCG_COND_LTU, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; case OPC2_32_RC_MAX: - tcg_gen_movi_tl(temp, const9); - tcg_gen_movcond_tl(TCG_COND_GT, cpu_gpr_d[r2], cpu_gpr_d[r1], temp, + tcg_gen_movi_i32(temp, const9); + tcg_gen_movcond_i32(TCG_COND_GT, cpu_gpr_d[r2], cpu_gpr_d[r1], temp, cpu_gpr_d[r1], temp); break; case OPC2_32_RC_MAX_U: - tcg_gen_movi_tl(temp, MASK_OP_RC_CONST9(ctx->opcode)); - tcg_gen_movcond_tl(TCG_COND_GTU, cpu_gpr_d[r2], cpu_gpr_d[r1], temp, + tcg_gen_movi_i32(temp, MASK_OP_RC_CONST9(ctx->opcode)); + tcg_gen_movcond_i32(TCG_COND_GTU, cpu_gpr_d[r2], cpu_gpr_d[r1], temp, cpu_gpr_d[r1], temp); break; case OPC2_32_RC_MIN: - tcg_gen_movi_tl(temp, const9); - tcg_gen_movcond_tl(TCG_COND_LT, cpu_gpr_d[r2], cpu_gpr_d[r1], temp, + tcg_gen_movi_i32(temp, const9); + tcg_gen_movcond_i32(TCG_COND_LT, cpu_gpr_d[r2], cpu_gpr_d[r1], temp, cpu_gpr_d[r1], temp); break; case OPC2_32_RC_MIN_U: - tcg_gen_movi_tl(temp, MASK_OP_RC_CONST9(ctx->opcode)); - tcg_gen_movcond_tl(TCG_COND_LTU, cpu_gpr_d[r2], cpu_gpr_d[r1], temp, + tcg_gen_movi_i32(temp, MASK_OP_RC_CONST9(ctx->opcode)); + tcg_gen_movcond_i32(TCG_COND_LTU, cpu_gpr_d[r2], cpu_gpr_d[r1], temp, cpu_gpr_d[r1], temp); break; case OPC2_32_RC_NE: - tcg_gen_setcondi_tl(TCG_COND_NE, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + tcg_gen_setcondi_i32(TCG_COND_NE, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; case OPC2_32_RC_OR_EQ: gen_accumulating_condi(TCG_COND_EQ, cpu_gpr_d[r2], cpu_gpr_d[r1], @@ -5156,15 +5157,15 @@ static void decode_rc_accumulator(DisasContext *ctx) const9, &tcg_gen_or_tl); break; case OPC2_32_RC_RSUB: - tcg_gen_movi_tl(temp, const9); + tcg_gen_movi_i32(temp, const9); gen_sub_d(cpu_gpr_d[r2], temp, cpu_gpr_d[r1]); break; case OPC2_32_RC_RSUBS: - tcg_gen_movi_tl(temp, const9); + tcg_gen_movi_i32(temp, const9); gen_subs(cpu_gpr_d[r2], temp, cpu_gpr_d[r1]); break; case OPC2_32_RC_RSUBS_U: - tcg_gen_movi_tl(temp, const9); + tcg_gen_movi_i32(temp, const9); gen_subsu(cpu_gpr_d[r2], temp, cpu_gpr_d[r1]); break; case OPC2_32_RC_SH_EQ: @@ -5260,7 +5261,7 @@ static void decode_rc_mul(DisasContext *ctx) break; case OPC2_32_RC_MUL_64: CHECK_REG_PAIR(r2); - gen_muli_i64s(cpu_gpr_d[r2], cpu_gpr_d[r2+1], cpu_gpr_d[r1], const9); + gen_muli_i64s(cpu_gpr_d[r2], cpu_gpr_d[r2 + 1], cpu_gpr_d[r1], const9); break; case OPC2_32_RC_MULS_32: gen_mulsi_i32(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); @@ -5268,7 +5269,7 @@ static void decode_rc_mul(DisasContext *ctx) case OPC2_32_RC_MUL_U_64: const9 = MASK_OP_RC_CONST9(ctx->opcode); CHECK_REG_PAIR(r2); - gen_muli_i64u(cpu_gpr_d[r2], cpu_gpr_d[r2+1], cpu_gpr_d[r1], const9); + gen_muli_i64u(cpu_gpr_d[r2], cpu_gpr_d[r2 + 1], cpu_gpr_d[r1], const9); break; case OPC2_32_RC_MULS_U_32: const9 = MASK_OP_RC_CONST9(ctx->opcode); @@ -5300,18 +5301,18 @@ static void decode_rcpw_insert(DisasContext *ctx) CHECK_REG_PAIR(r2); /* if pos + width > 32 undefined result */ if (pos + width <= 32) { - tcg_gen_movi_tl(cpu_gpr_d[r2+1], ((1u << width) - 1) << pos); - tcg_gen_movi_tl(cpu_gpr_d[r2], (const4 << pos)); + tcg_gen_movi_i32(cpu_gpr_d[r2 + 1], ((1u << width) - 1) << pos); + tcg_gen_movi_i32(cpu_gpr_d[r2], (const4 << pos)); } break; case OPC2_32_RCPW_INSERT: - /* tcg_gen_deposit_tl() does not handle the case of width = 0 */ + /* tcg_gen_deposit_i32() does not handle the case of width = 0 */ if (width == 0) { - tcg_gen_mov_tl(cpu_gpr_d[r2], cpu_gpr_d[r1]); + tcg_gen_mov_i32(cpu_gpr_d[r2], cpu_gpr_d[r1]); /* if pos + width > 32 undefined result */ } else if (pos + width <= 32) { temp = tcg_constant_i32(const4); - tcg_gen_deposit_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], temp, pos, width); + tcg_gen_deposit_i32(cpu_gpr_d[r2], cpu_gpr_d[r1], temp, pos, width); } break; default: @@ -5342,18 +5343,18 @@ static void decode_rcrw_insert(DisasContext *ctx) switch (op2) { case OPC2_32_RCRW_IMASK: CHECK_REG_PAIR(r4); - tcg_gen_andi_tl(temp, cpu_gpr_d[r3], 0x1f); - tcg_gen_movi_tl(temp2, (1 << width) - 1); - tcg_gen_shl_tl(cpu_gpr_d[r4 + 1], temp2, temp); - tcg_gen_movi_tl(temp2, const4); - tcg_gen_shl_tl(cpu_gpr_d[r4], temp2, temp); + tcg_gen_andi_i32(temp, cpu_gpr_d[r3], 0x1f); + tcg_gen_movi_i32(temp2, (1 << width) - 1); + tcg_gen_shl_i32(cpu_gpr_d[r4 + 1], temp2, temp); + tcg_gen_movi_i32(temp2, const4); + tcg_gen_shl_i32(cpu_gpr_d[r4], temp2, temp); break; case OPC2_32_RCRW_INSERT: temp3 = tcg_temp_new(); - tcg_gen_movi_tl(temp, width); - tcg_gen_movi_tl(temp2, const4); - tcg_gen_andi_tl(temp3, cpu_gpr_d[r3], 0x1f); + tcg_gen_movi_i32(temp, width); + tcg_gen_movi_i32(temp2, const4); + tcg_gen_andi_i32(temp3, cpu_gpr_d[r3], 0x1f); gen_insert(cpu_gpr_d[r4], cpu_gpr_d[r1], temp2, temp, temp3); break; default: @@ -5389,13 +5390,13 @@ static void decode_rcr_cond_select(DisasContext *ctx) case OPC2_32_RCR_SEL: temp = tcg_constant_i32(0); temp2 = tcg_constant_i32(const9); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr_d[r4], cpu_gpr_d[r3], temp, + tcg_gen_movcond_i32(TCG_COND_NE, cpu_gpr_d[r4], cpu_gpr_d[r3], temp, cpu_gpr_d[r1], temp2); break; case OPC2_32_RCR_SELN: temp = tcg_constant_i32(0); temp2 = tcg_constant_i32(const9); - tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr_d[r4], cpu_gpr_d[r3], temp, + tcg_gen_movcond_i32(TCG_COND_EQ, cpu_gpr_d[r4], cpu_gpr_d[r3], temp, cpu_gpr_d[r1], temp2); break; default: @@ -5423,8 +5424,8 @@ static void decode_rcr_madd(DisasContext *ctx) case OPC2_32_RCR_MADD_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddi64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], - cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); + gen_maddi64_d(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], const9); break; case OPC2_32_RCR_MADDS_32: gen_maddsi_32(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); @@ -5432,15 +5433,15 @@ static void decode_rcr_madd(DisasContext *ctx) case OPC2_32_RCR_MADDS_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsi_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], - cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); + gen_maddsi_64(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], const9); break; case OPC2_32_RCR_MADD_U_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); const9 = MASK_OP_RCR_CONST9(ctx->opcode); - gen_maddui64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], - cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); + gen_maddui64_d(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], const9); break; case OPC2_32_RCR_MADDS_U_32: const9 = MASK_OP_RCR_CONST9(ctx->opcode); @@ -5450,8 +5451,8 @@ static void decode_rcr_madd(DisasContext *ctx) CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); const9 = MASK_OP_RCR_CONST9(ctx->opcode); - gen_maddsui_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], - cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); + gen_maddsui_64(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], const9); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -5478,8 +5479,8 @@ static void decode_rcr_msub(DisasContext *ctx) case OPC2_32_RCR_MSUB_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubi64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], - cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); + gen_msubi64_d(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], const9); break; case OPC2_32_RCR_MSUBS_32: gen_msubsi_32(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); @@ -5487,15 +5488,15 @@ static void decode_rcr_msub(DisasContext *ctx) case OPC2_32_RCR_MSUBS_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubsi_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], - cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); + gen_msubsi_64(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], const9); break; case OPC2_32_RCR_MSUB_U_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); const9 = MASK_OP_RCR_CONST9(ctx->opcode); - gen_msubui64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], - cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); + gen_msubui64_d(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], const9); break; case OPC2_32_RCR_MSUBS_U_32: const9 = MASK_OP_RCR_CONST9(ctx->opcode); @@ -5505,8 +5506,8 @@ static void decode_rcr_msub(DisasContext *ctx) CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); const9 = MASK_OP_RCR_CONST9(ctx->opcode); - gen_msubsui_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], - cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); + gen_msubsui_64(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], const9); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -5533,33 +5534,33 @@ static void decode_rlc_opc(DisasContext *ctx, gen_addi_d(cpu_gpr_d[r2], cpu_gpr_d[r1], const16 << 16); break; case OPC1_32_RLC_ADDIH_A: - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r1], const16 << 16); + tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r1], const16 << 16); break; case OPC1_32_RLC_MFCR: const16 = MASK_OP_RLC_CONST16(ctx->opcode); gen_mfcr(ctx, cpu_gpr_d[r2], const16); break; case OPC1_32_RLC_MOV: - tcg_gen_movi_tl(cpu_gpr_d[r2], const16); + tcg_gen_movi_i32(cpu_gpr_d[r2], const16); break; case OPC1_32_RLC_MOV_64: if (has_feature(ctx, TRICORE_FEATURE_16)) { CHECK_REG_PAIR(r2); - tcg_gen_movi_tl(cpu_gpr_d[r2], const16); - tcg_gen_movi_tl(cpu_gpr_d[r2+1], const16 >> 15); + tcg_gen_movi_i32(cpu_gpr_d[r2], const16); + tcg_gen_movi_i32(cpu_gpr_d[r2 + 1], const16 >> 15); } else { generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } break; case OPC1_32_RLC_MOV_U: const16 = MASK_OP_RLC_CONST16(ctx->opcode); - tcg_gen_movi_tl(cpu_gpr_d[r2], const16); + tcg_gen_movi_i32(cpu_gpr_d[r2], const16); break; case OPC1_32_RLC_MOV_H: - tcg_gen_movi_tl(cpu_gpr_d[r2], const16 << 16); + tcg_gen_movi_i32(cpu_gpr_d[r2], const16 << 16); break; case OPC1_32_RLC_MOVH_A: - tcg_gen_movi_tl(cpu_gpr_a[r2], const16 << 16); + tcg_gen_movi_i32(cpu_gpr_a[r2], const16 << 16); break; case OPC1_32_RLC_MTCR: const16 = MASK_OP_RLC_CONST16(ctx->opcode); @@ -5673,7 +5674,7 @@ static void decode_rr_accumulator(DisasContext *ctx) cpu_gpr_d[r2], &tcg_gen_and_tl); break; case OPC2_32_RR_EQ: - tcg_gen_setcond_tl(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_d[r1], + tcg_gen_setcond_i32(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_EQ_B: @@ -5683,7 +5684,7 @@ static void decode_rr_accumulator(DisasContext *ctx) gen_helper_eq_h(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_EQ_W: - tcg_gen_negsetcond_tl(TCG_COND_EQ, cpu_gpr_d[r3], + tcg_gen_negsetcond_i32(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_EQANY_B: @@ -5693,19 +5694,19 @@ static void decode_rr_accumulator(DisasContext *ctx) gen_helper_eqany_h(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_GE: - tcg_gen_setcond_tl(TCG_COND_GE, cpu_gpr_d[r3], cpu_gpr_d[r1], + tcg_gen_setcond_i32(TCG_COND_GE, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_GE_U: - tcg_gen_setcond_tl(TCG_COND_GEU, cpu_gpr_d[r3], cpu_gpr_d[r1], + tcg_gen_setcond_i32(TCG_COND_GEU, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_LT: - tcg_gen_setcond_tl(TCG_COND_LT, cpu_gpr_d[r3], cpu_gpr_d[r1], + tcg_gen_setcond_i32(TCG_COND_LT, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_LT_U: - tcg_gen_setcond_tl(TCG_COND_LTU, cpu_gpr_d[r3], cpu_gpr_d[r1], + tcg_gen_setcond_i32(TCG_COND_LTU, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_LT_B: @@ -5721,19 +5722,19 @@ static void decode_rr_accumulator(DisasContext *ctx) gen_helper_lt_hu(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_LT_W: - tcg_gen_negsetcond_tl(TCG_COND_LT, cpu_gpr_d[r3], + tcg_gen_negsetcond_i32(TCG_COND_LT, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_LT_WU: - tcg_gen_negsetcond_tl(TCG_COND_LTU, cpu_gpr_d[r3], + tcg_gen_negsetcond_i32(TCG_COND_LTU, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_MAX: - tcg_gen_movcond_tl(TCG_COND_GT, cpu_gpr_d[r3], cpu_gpr_d[r1], + tcg_gen_movcond_i32(TCG_COND_GT, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_MAX_U: - tcg_gen_movcond_tl(TCG_COND_GTU, cpu_gpr_d[r3], cpu_gpr_d[r1], + tcg_gen_movcond_i32(TCG_COND_GTU, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_MAX_B: @@ -5749,11 +5750,11 @@ static void decode_rr_accumulator(DisasContext *ctx) gen_helper_max_hu(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_MIN: - tcg_gen_movcond_tl(TCG_COND_LT, cpu_gpr_d[r3], cpu_gpr_d[r1], + tcg_gen_movcond_i32(TCG_COND_LT, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_MIN_U: - tcg_gen_movcond_tl(TCG_COND_LTU, cpu_gpr_d[r3], cpu_gpr_d[r1], + tcg_gen_movcond_i32(TCG_COND_LTU, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_MIN_B: @@ -5769,16 +5770,16 @@ static void decode_rr_accumulator(DisasContext *ctx) gen_helper_min_hu(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_MOV: - tcg_gen_mov_tl(cpu_gpr_d[r3], cpu_gpr_d[r2]); + tcg_gen_mov_i32(cpu_gpr_d[r3], cpu_gpr_d[r2]); break; case OPC2_32_RR_MOV_64: if (has_feature(ctx, TRICORE_FEATURE_16)) { temp = tcg_temp_new(); CHECK_REG_PAIR(r3); - tcg_gen_mov_tl(temp, cpu_gpr_d[r1]); - tcg_gen_mov_tl(cpu_gpr_d[r3], cpu_gpr_d[r2]); - tcg_gen_mov_tl(cpu_gpr_d[r3 + 1], temp); + tcg_gen_mov_i32(temp, cpu_gpr_d[r1]); + tcg_gen_mov_i32(cpu_gpr_d[r3], cpu_gpr_d[r2]); + tcg_gen_mov_i32(cpu_gpr_d[r3 + 1], temp); } else { generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } @@ -5786,14 +5787,14 @@ static void decode_rr_accumulator(DisasContext *ctx) case OPC2_32_RR_MOVS_64: if (has_feature(ctx, TRICORE_FEATURE_16)) { CHECK_REG_PAIR(r3); - tcg_gen_mov_tl(cpu_gpr_d[r3], cpu_gpr_d[r2]); - tcg_gen_sari_tl(cpu_gpr_d[r3 + 1], cpu_gpr_d[r2], 31); + tcg_gen_mov_i32(cpu_gpr_d[r3], cpu_gpr_d[r2]); + tcg_gen_sari_i32(cpu_gpr_d[r3 + 1], cpu_gpr_d[r2], 31); } else { generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } break; case OPC2_32_RR_NE: - tcg_gen_setcond_tl(TCG_COND_NE, cpu_gpr_d[r3], cpu_gpr_d[r1], + tcg_gen_setcond_i32(TCG_COND_NE, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_OR_EQ: @@ -5926,41 +5927,41 @@ static void decode_rr_logical_shift(DisasContext *ctx) switch (op2) { case OPC2_32_RR_AND: - tcg_gen_and_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_gen_and_i32(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_ANDN: - tcg_gen_andc_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_gen_andc_i32(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_CLO: - tcg_gen_not_tl(cpu_gpr_d[r3], cpu_gpr_d[r1]); - tcg_gen_clzi_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], TARGET_LONG_BITS); + tcg_gen_not_i32(cpu_gpr_d[r3], cpu_gpr_d[r1]); + tcg_gen_clzi_i32(cpu_gpr_d[r3], cpu_gpr_d[r3], TARGET_LONG_BITS); break; case OPC2_32_RR_CLO_H: gen_helper_clo_h(cpu_gpr_d[r3], cpu_gpr_d[r1]); break; case OPC2_32_RR_CLS: - tcg_gen_clrsb_tl(cpu_gpr_d[r3], cpu_gpr_d[r1]); + tcg_gen_clrsb_i32(cpu_gpr_d[r3], cpu_gpr_d[r1]); break; case OPC2_32_RR_CLS_H: gen_helper_cls_h(cpu_gpr_d[r3], cpu_gpr_d[r1]); break; case OPC2_32_RR_CLZ: - tcg_gen_clzi_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], TARGET_LONG_BITS); + tcg_gen_clzi_i32(cpu_gpr_d[r3], cpu_gpr_d[r1], TARGET_LONG_BITS); break; case OPC2_32_RR_CLZ_H: gen_helper_clz_h(cpu_gpr_d[r3], cpu_gpr_d[r1]); break; case OPC2_32_RR_NAND: - tcg_gen_nand_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_gen_nand_i32(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_NOR: - tcg_gen_nor_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_gen_nor_i32(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_OR: - tcg_gen_or_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_gen_or_i32(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_ORN: - tcg_gen_orc_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_gen_orc_i32(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_SH: gen_helper_sh(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); @@ -5978,10 +5979,10 @@ static void decode_rr_logical_shift(DisasContext *ctx) gen_shas(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_XNOR: - tcg_gen_eqv_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_gen_eqv_i32(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_XOR: - tcg_gen_xor_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_gen_xor_i32(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -6002,52 +6003,52 @@ static void decode_rr_address(DisasContext *ctx) switch (op2) { case OPC2_32_RR_ADD_A: - tcg_gen_add_tl(cpu_gpr_a[r3], cpu_gpr_a[r1], cpu_gpr_a[r2]); + tcg_gen_add_i32(cpu_gpr_a[r3], cpu_gpr_a[r1], cpu_gpr_a[r2]); break; case OPC2_32_RR_ADDSC_A: temp = tcg_temp_new(); - tcg_gen_shli_tl(temp, cpu_gpr_d[r1], n); - tcg_gen_add_tl(cpu_gpr_a[r3], cpu_gpr_a[r2], temp); + tcg_gen_shli_i32(temp, cpu_gpr_d[r1], n); + tcg_gen_add_i32(cpu_gpr_a[r3], cpu_gpr_a[r2], temp); break; case OPC2_32_RR_ADDSC_AT: temp = tcg_temp_new(); - tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 3); - tcg_gen_add_tl(temp, cpu_gpr_a[r2], temp); - tcg_gen_andi_tl(cpu_gpr_a[r3], temp, 0xFFFFFFFC); + tcg_gen_sari_i32(temp, cpu_gpr_d[r1], 3); + tcg_gen_add_i32(temp, cpu_gpr_a[r2], temp); + tcg_gen_andi_i32(cpu_gpr_a[r3], temp, 0xFFFFFFFC); break; case OPC2_32_RR_EQ_A: - tcg_gen_setcond_tl(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_a[r1], + tcg_gen_setcond_i32(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_a[r1], cpu_gpr_a[r2]); break; case OPC2_32_RR_EQZ: - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_a[r1], 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_a[r1], 0); break; case OPC2_32_RR_GE_A: - tcg_gen_setcond_tl(TCG_COND_GEU, cpu_gpr_d[r3], cpu_gpr_a[r1], + tcg_gen_setcond_i32(TCG_COND_GEU, cpu_gpr_d[r3], cpu_gpr_a[r1], cpu_gpr_a[r2]); break; case OPC2_32_RR_LT_A: - tcg_gen_setcond_tl(TCG_COND_LTU, cpu_gpr_d[r3], cpu_gpr_a[r1], + tcg_gen_setcond_i32(TCG_COND_LTU, cpu_gpr_d[r3], cpu_gpr_a[r1], cpu_gpr_a[r2]); break; case OPC2_32_RR_MOV_A: - tcg_gen_mov_tl(cpu_gpr_a[r3], cpu_gpr_d[r2]); + tcg_gen_mov_i32(cpu_gpr_a[r3], cpu_gpr_d[r2]); break; case OPC2_32_RR_MOV_AA: - tcg_gen_mov_tl(cpu_gpr_a[r3], cpu_gpr_a[r2]); + tcg_gen_mov_i32(cpu_gpr_a[r3], cpu_gpr_a[r2]); break; case OPC2_32_RR_MOV_D: - tcg_gen_mov_tl(cpu_gpr_d[r3], cpu_gpr_a[r2]); + tcg_gen_mov_i32(cpu_gpr_d[r3], cpu_gpr_a[r2]); break; case OPC2_32_RR_NE_A: - tcg_gen_setcond_tl(TCG_COND_NE, cpu_gpr_d[r3], cpu_gpr_a[r1], + tcg_gen_setcond_i32(TCG_COND_NE, cpu_gpr_d[r3], cpu_gpr_a[r1], cpu_gpr_a[r2]); break; case OPC2_32_RR_NEZ_A: - tcg_gen_setcondi_tl(TCG_COND_NE, cpu_gpr_d[r3], cpu_gpr_a[r1], 0); + tcg_gen_setcondi_i32(TCG_COND_NE, cpu_gpr_d[r3], cpu_gpr_a[r1], 0); break; case OPC2_32_RR_SUB_A: - tcg_gen_sub_tl(cpu_gpr_a[r3], cpu_gpr_a[r1], cpu_gpr_a[r2]); + tcg_gen_sub_i32(cpu_gpr_a[r3], cpu_gpr_a[r1], cpu_gpr_a[r2]); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -6064,19 +6065,19 @@ static void decode_rr_idirect(DisasContext *ctx) switch (op2) { case OPC2_32_RR_JI: - tcg_gen_andi_tl(cpu_PC, cpu_gpr_a[r1], ~0x1); + tcg_gen_andi_i32(cpu_PC, cpu_gpr_a[r1], ~0x1); break; case OPC2_32_RR_JLI: - tcg_gen_andi_tl(cpu_PC, cpu_gpr_a[r1], ~0x1); - tcg_gen_movi_tl(cpu_gpr_a[11], ctx->pc_succ_insn); + tcg_gen_andi_i32(cpu_PC, cpu_gpr_a[r1], ~0x1); + tcg_gen_movi_i32(cpu_gpr_a[11], ctx->pc_succ_insn); break; case OPC2_32_RR_CALLI: gen_helper_1arg(call, ctx->pc_succ_insn); - tcg_gen_andi_tl(cpu_PC, cpu_gpr_a[r1], ~0x1); + tcg_gen_andi_i32(cpu_PC, cpu_gpr_a[r1], ~0x1); break; case OPC2_32_RR_FCALLI: gen_fcall_save_ctx(ctx); - tcg_gen_andi_tl(cpu_PC, cpu_gpr_a[r1], ~0x1); + tcg_gen_andi_i32(cpu_PC, cpu_gpr_a[r1], ~0x1); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -6103,11 +6104,11 @@ static void decode_rr_divide(DisasContext *ctx) break; case OPC2_32_RR_BSPLIT: CHECK_REG_PAIR(r3); - gen_bsplit(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1]); + gen_bsplit(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r1]); break; case OPC2_32_RR_DVINIT_B: CHECK_REG_PAIR(r3); - gen_dvinit_b(ctx, cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], + gen_dvinit_b(ctx, cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_DVINIT_BU: @@ -6115,28 +6116,28 @@ static void decode_rr_divide(DisasContext *ctx) temp2 = tcg_temp_new(); temp3 = tcg_temp_new(); CHECK_REG_PAIR(r3); - tcg_gen_shri_tl(temp3, cpu_gpr_d[r1], 8); + tcg_gen_shri_i32(temp3, cpu_gpr_d[r1], 8); /* reset av */ - tcg_gen_movi_tl(cpu_PSW_AV, 0); + tcg_gen_movi_i32(cpu_PSW_AV, 0); if (!has_feature(ctx, TRICORE_FEATURE_131)) { - /* overflow = (abs(D[r3+1]) >= abs(D[r2])) */ - tcg_gen_abs_tl(temp, temp3); - tcg_gen_abs_tl(temp2, cpu_gpr_d[r2]); - tcg_gen_setcond_tl(TCG_COND_GE, cpu_PSW_V, temp, temp2); + /* overflow = (abs(D[r3 + 1]) >= abs(D[r2])) */ + tcg_gen_abs_i32(temp, temp3); + tcg_gen_abs_i32(temp2, cpu_gpr_d[r2]); + tcg_gen_setcond_i32(TCG_COND_GE, cpu_PSW_V, temp, temp2); } else { /* overflow = (D[b] == 0) */ - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_PSW_V, cpu_gpr_d[r2], 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_PSW_V, cpu_gpr_d[r2], 0); } - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + tcg_gen_shli_i32(cpu_PSW_V, cpu_PSW_V, 31); /* sv */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* write result */ - tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], 24); - tcg_gen_mov_tl(cpu_gpr_d[r3+1], temp3); + tcg_gen_shli_i32(cpu_gpr_d[r3], cpu_gpr_d[r1], 24); + tcg_gen_mov_i32(cpu_gpr_d[r3 + 1], temp3); break; case OPC2_32_RR_DVINIT_H: CHECK_REG_PAIR(r3); - gen_dvinit_h(ctx, cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], + gen_dvinit_h(ctx, cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_DVINIT_HU: @@ -6144,24 +6145,24 @@ static void decode_rr_divide(DisasContext *ctx) temp2 = tcg_temp_new(); temp3 = tcg_temp_new(); CHECK_REG_PAIR(r3); - tcg_gen_shri_tl(temp3, cpu_gpr_d[r1], 16); + tcg_gen_shri_i32(temp3, cpu_gpr_d[r1], 16); /* reset av */ - tcg_gen_movi_tl(cpu_PSW_AV, 0); + tcg_gen_movi_i32(cpu_PSW_AV, 0); if (!has_feature(ctx, TRICORE_FEATURE_131)) { - /* overflow = (abs(D[r3+1]) >= abs(D[r2])) */ - tcg_gen_abs_tl(temp, temp3); - tcg_gen_abs_tl(temp2, cpu_gpr_d[r2]); - tcg_gen_setcond_tl(TCG_COND_GE, cpu_PSW_V, temp, temp2); + /* overflow = (abs(D[r3 + 1]) >= abs(D[r2])) */ + tcg_gen_abs_i32(temp, temp3); + tcg_gen_abs_i32(temp2, cpu_gpr_d[r2]); + tcg_gen_setcond_i32(TCG_COND_GE, cpu_PSW_V, temp, temp2); } else { /* overflow = (D[b] == 0) */ - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_PSW_V, cpu_gpr_d[r2], 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_PSW_V, cpu_gpr_d[r2], 0); } - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + tcg_gen_shli_i32(cpu_PSW_V, cpu_PSW_V, 31); /* sv */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* write result */ - tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], 16); - tcg_gen_mov_tl(cpu_gpr_d[r3+1], temp3); + tcg_gen_shli_i32(cpu_gpr_d[r3], cpu_gpr_d[r1], 16); + tcg_gen_mov_i32(cpu_gpr_d[r3 + 1], temp3); break; case OPC2_32_RR_DVINIT: temp = tcg_temp_new(); @@ -6169,41 +6170,41 @@ static void decode_rr_divide(DisasContext *ctx) CHECK_REG_PAIR(r3); /* overflow = ((D[b] == 0) || ((D[b] == 0xFFFFFFFF) && (D[a] == 0x80000000))) */ - tcg_gen_setcondi_tl(TCG_COND_EQ, temp, cpu_gpr_d[r2], 0xffffffff); - tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, cpu_gpr_d[r1], 0x80000000); - tcg_gen_and_tl(temp, temp, temp2); - tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, cpu_gpr_d[r2], 0); - tcg_gen_or_tl(cpu_PSW_V, temp, temp2); - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + tcg_gen_setcondi_i32(TCG_COND_EQ, temp, cpu_gpr_d[r2], 0xffffffff); + tcg_gen_setcondi_i32(TCG_COND_EQ, temp2, cpu_gpr_d[r1], 0x80000000); + tcg_gen_and_i32(temp, temp, temp2); + tcg_gen_setcondi_i32(TCG_COND_EQ, temp2, cpu_gpr_d[r2], 0); + tcg_gen_or_i32(cpu_PSW_V, temp, temp2); + tcg_gen_shli_i32(cpu_PSW_V, cpu_PSW_V, 31); /* sv */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* reset av */ - tcg_gen_movi_tl(cpu_PSW_AV, 0); + tcg_gen_movi_i32(cpu_PSW_AV, 0); /* write result */ - tcg_gen_mov_tl(cpu_gpr_d[r3], cpu_gpr_d[r1]); + tcg_gen_mov_i32(cpu_gpr_d[r3], cpu_gpr_d[r1]); /* sign extend to high reg */ - tcg_gen_sari_tl(cpu_gpr_d[r3+1], cpu_gpr_d[r1], 31); + tcg_gen_sari_i32(cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], 31); break; case OPC2_32_RR_DVINIT_U: CHECK_REG_PAIR(r3); /* overflow = (D[b] == 0) */ - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_PSW_V, cpu_gpr_d[r2], 0); - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_PSW_V, cpu_gpr_d[r2], 0); + tcg_gen_shli_i32(cpu_PSW_V, cpu_PSW_V, 31); /* sv */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + tcg_gen_or_i32(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* reset av */ - tcg_gen_movi_tl(cpu_PSW_AV, 0); + tcg_gen_movi_i32(cpu_PSW_AV, 0); /* write result */ - tcg_gen_mov_tl(cpu_gpr_d[r3], cpu_gpr_d[r1]); + tcg_gen_mov_i32(cpu_gpr_d[r3], cpu_gpr_d[r1]); /* zero extend to high reg*/ - tcg_gen_movi_tl(cpu_gpr_d[r3+1], 0); + tcg_gen_movi_i32(cpu_gpr_d[r3 + 1], 0); break; case OPC2_32_RR_PARITY: gen_helper_parity(cpu_gpr_d[r3], cpu_gpr_d[r1]); break; case OPC2_32_RR_UNPACK: CHECK_REG_PAIR(r3); - gen_unpack(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1]); + gen_unpack(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r1]); break; case OPC2_32_RR_CRC32_B: if (has_feature(ctx, TRICORE_FEATURE_162)) { @@ -6229,7 +6230,7 @@ static void decode_rr_divide(DisasContext *ctx) case OPC2_32_RR_POPCNT_W: if (has_feature(ctx, TRICORE_FEATURE_162)) { - tcg_gen_ctpop_tl(cpu_gpr_d[r3], cpu_gpr_d[r1]); + tcg_gen_ctpop_i32(cpu_gpr_d[r3], cpu_gpr_d[r1]); } else { generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } @@ -6237,7 +6238,7 @@ static void decode_rr_divide(DisasContext *ctx) case OPC2_32_RR_DIV: if (has_feature(ctx, TRICORE_FEATURE_16)) { CHECK_REG_PAIR(r3); - GEN_HELPER_RR(divide, cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], + GEN_HELPER_RR(divide, cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2]); } else { generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -6246,7 +6247,7 @@ static void decode_rr_divide(DisasContext *ctx) case OPC2_32_RR_DIV_U: if (has_feature(ctx, TRICORE_FEATURE_16)) { CHECK_REG_PAIR(r3); - GEN_HELPER_RR(divide_u, cpu_gpr_d[r3], cpu_gpr_d[r3+1], + GEN_HELPER_RR(divide_u, cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2]); } else { generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -6328,69 +6329,69 @@ static void decode_rr1_mul(DisasContext *ctx) temp64 = tcg_temp_new_i64(); CHECK_REG_PAIR(r3); GEN_HELPER_LL(mul_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); - tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); - gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3+1]); + tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], temp64); + gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1]); break; case OPC2_32_RR1_MUL_H_32_LU: temp64 = tcg_temp_new_i64(); CHECK_REG_PAIR(r3); GEN_HELPER_LU(mul_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); - tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); - gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3+1]); + tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], temp64); + gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1]); break; case OPC2_32_RR1_MUL_H_32_UL: temp64 = tcg_temp_new_i64(); CHECK_REG_PAIR(r3); GEN_HELPER_UL(mul_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); - tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); - gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3+1]); + tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], temp64); + gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1]); break; case OPC2_32_RR1_MUL_H_32_UU: temp64 = tcg_temp_new_i64(); CHECK_REG_PAIR(r3); GEN_HELPER_UU(mul_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); - tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); - gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3+1]); + tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], temp64); + gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1]); break; case OPC2_32_RR1_MULM_H_64_LL: temp64 = tcg_temp_new_i64(); CHECK_REG_PAIR(r3); GEN_HELPER_LL(mulm_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); - tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); + tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], temp64); /* reset V bit */ - tcg_gen_movi_tl(cpu_PSW_V, 0); + tcg_gen_movi_i32(cpu_PSW_V, 0); /* reset AV bit */ - tcg_gen_mov_tl(cpu_PSW_AV, cpu_PSW_V); + tcg_gen_mov_i32(cpu_PSW_AV, cpu_PSW_V); break; case OPC2_32_RR1_MULM_H_64_LU: temp64 = tcg_temp_new_i64(); CHECK_REG_PAIR(r3); GEN_HELPER_LU(mulm_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); - tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); + tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], temp64); /* reset V bit */ - tcg_gen_movi_tl(cpu_PSW_V, 0); + tcg_gen_movi_i32(cpu_PSW_V, 0); /* reset AV bit */ - tcg_gen_mov_tl(cpu_PSW_AV, cpu_PSW_V); + tcg_gen_mov_i32(cpu_PSW_AV, cpu_PSW_V); break; case OPC2_32_RR1_MULM_H_64_UL: temp64 = tcg_temp_new_i64(); CHECK_REG_PAIR(r3); GEN_HELPER_UL(mulm_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); - tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); + tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], temp64); /* reset V bit */ - tcg_gen_movi_tl(cpu_PSW_V, 0); + tcg_gen_movi_i32(cpu_PSW_V, 0); /* reset AV bit */ - tcg_gen_mov_tl(cpu_PSW_AV, cpu_PSW_V); + tcg_gen_mov_i32(cpu_PSW_AV, cpu_PSW_V); break; case OPC2_32_RR1_MULM_H_64_UU: temp64 = tcg_temp_new_i64(); CHECK_REG_PAIR(r3); GEN_HELPER_UU(mulm_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); - tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); + tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], temp64); /* reset V bit */ - tcg_gen_movi_tl(cpu_PSW_V, 0); + tcg_gen_movi_i32(cpu_PSW_V, 0); /* reset AV bit */ - tcg_gen_mov_tl(cpu_PSW_AV, cpu_PSW_V); + tcg_gen_mov_i32(cpu_PSW_AV, cpu_PSW_V); break; case OPC2_32_RR1_MULR_H_16_LL: GEN_HELPER_LL(mulr_h, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], n); @@ -6436,45 +6437,45 @@ static void decode_rr1_mulq(DisasContext *ctx) break; case OPC2_32_RR1_MUL_Q_64: CHECK_REG_PAIR(r3); - gen_mul_q(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_mul_q(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, 0); break; case OPC2_32_RR1_MUL_Q_32_L: - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r2]); gen_mul_q(cpu_gpr_d[r3], temp, cpu_gpr_d[r1], temp, n, 16); break; case OPC2_32_RR1_MUL_Q_64_L: CHECK_REG_PAIR(r3); - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); - gen_mul_q(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, n, 0); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r2]); + gen_mul_q(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], temp, n, 0); break; case OPC2_32_RR1_MUL_Q_32_U: - tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); + tcg_gen_sari_i32(temp, cpu_gpr_d[r2], 16); gen_mul_q(cpu_gpr_d[r3], temp, cpu_gpr_d[r1], temp, n, 16); break; case OPC2_32_RR1_MUL_Q_64_U: CHECK_REG_PAIR(r3); - tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); - gen_mul_q(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, n, 0); + tcg_gen_sari_i32(temp, cpu_gpr_d[r2], 16); + gen_mul_q(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], temp, n, 0); break; case OPC2_32_RR1_MUL_Q_32_LL: - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); - tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_i32(temp2, cpu_gpr_d[r2]); gen_mul_q_16(cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RR1_MUL_Q_32_UU: - tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + tcg_gen_sari_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_i32(temp2, cpu_gpr_d[r2], 16); gen_mul_q_16(cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RR1_MULR_Q_32_L: - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); - tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_i32(temp2, cpu_gpr_d[r2]); gen_mulr_q(cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RR1_MULR_Q_32_U: - tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + tcg_gen_sari_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_i32(temp2, cpu_gpr_d[r2], 16); gen_mulr_q(cpu_gpr_d[r3], temp, temp2, n); break; default: @@ -6498,7 +6499,7 @@ static void decode_rr2_mul(DisasContext *ctx) break; case OPC2_32_RR2_MUL_64: CHECK_REG_PAIR(r3); - gen_mul_i64s(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], + gen_mul_i64s(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR2_MULS_32: @@ -6507,7 +6508,7 @@ static void decode_rr2_mul(DisasContext *ctx) break; case OPC2_32_RR2_MUL_U_64: CHECK_REG_PAIR(r3); - gen_mul_i64u(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], + gen_mul_i64u(cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR2_MULS_U_32: @@ -6537,16 +6538,16 @@ static void decode_rrpw_extract_insert(DisasContext *ctx) switch (op2) { case OPC2_32_RRPW_EXTR: if (width == 0) { - tcg_gen_movi_tl(cpu_gpr_d[r3], 0); + tcg_gen_movi_i32(cpu_gpr_d[r3], 0); } else if (pos + width <= 32) { - tcg_gen_sextract_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], pos, width); + tcg_gen_sextract_i32(cpu_gpr_d[r3], cpu_gpr_d[r1], pos, width); } break; case OPC2_32_RRPW_EXTR_U: if (width == 0) { - tcg_gen_movi_tl(cpu_gpr_d[r3], 0); + tcg_gen_movi_i32(cpu_gpr_d[r3], 0); } else { - tcg_gen_extract_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], pos, width); + tcg_gen_extract_i32(cpu_gpr_d[r3], cpu_gpr_d[r1], pos, width); } break; case OPC2_32_RRPW_IMASK: @@ -6554,18 +6555,18 @@ static void decode_rrpw_extract_insert(DisasContext *ctx) if (pos + width <= 32) { temp = tcg_temp_new(); - tcg_gen_movi_tl(temp, ((1u << width) - 1) << pos); - tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r2], pos); - tcg_gen_mov_tl(cpu_gpr_d[r3 + 1], temp); + tcg_gen_movi_i32(temp, ((1u << width) - 1) << pos); + tcg_gen_shli_i32(cpu_gpr_d[r3], cpu_gpr_d[r2], pos); + tcg_gen_mov_i32(cpu_gpr_d[r3 + 1], temp); } break; case OPC2_32_RRPW_INSERT: - /* tcg_gen_deposit_tl() does not handle the case of width = 0 */ + /* tcg_gen_deposit_i32() does not handle the case of width = 0 */ if (width == 0) { - tcg_gen_mov_tl(cpu_gpr_d[r3], cpu_gpr_d[r1]); + tcg_gen_mov_i32(cpu_gpr_d[r3], cpu_gpr_d[r1]); } else if (pos + width <= 32) { - tcg_gen_deposit_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + tcg_gen_deposit_i32(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], pos, width); } break; @@ -6606,12 +6607,12 @@ static void decode_rrr_cond_select(DisasContext *ctx) break; case OPC2_32_RRR_SEL: temp = tcg_constant_i32(0); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr_d[r4], cpu_gpr_d[r3], temp, + tcg_gen_movcond_i32(TCG_COND_NE, cpu_gpr_d[r4], cpu_gpr_d[r3], temp, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_SELN: temp = tcg_constant_i32(0); - tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr_d[r4], cpu_gpr_d[r3], temp, + tcg_gen_movcond_i32(TCG_COND_EQ, cpu_gpr_d[r4], cpu_gpr_d[r3], temp, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; default: @@ -6635,49 +6636,49 @@ static void decode_rrr_divide(DisasContext *ctx) case OPC2_32_RRR_DVADJ: CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); - GEN_HELPER_RRR(dvadj, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r2]); + GEN_HELPER_RRR(dvadj, cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_DVSTEP: CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); - GEN_HELPER_RRR(dvstep, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r2]); + GEN_HELPER_RRR(dvstep, cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_DVSTEP_U: CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); - GEN_HELPER_RRR(dvstep_u, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r2]); + GEN_HELPER_RRR(dvstep_u, cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_IXMAX: CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); - GEN_HELPER_RRR(ixmax, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r2]); + GEN_HELPER_RRR(ixmax, cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_IXMAX_U: CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); - GEN_HELPER_RRR(ixmax_u, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r2]); + GEN_HELPER_RRR(ixmax_u, cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_IXMIN: CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); - GEN_HELPER_RRR(ixmin, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r2]); + GEN_HELPER_RRR(ixmin, cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_IXMIN_U: CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); - GEN_HELPER_RRR(ixmin_u, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r2]); + GEN_HELPER_RRR(ixmin_u, cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_PACK: CHECK_REG_PAIR(r3); gen_helper_pack(cpu_gpr_d[r4], cpu_PSW_C, cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1]); + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1]); break; case OPC2_32_RRR_CRCN: if (has_feature(ctx, TRICORE_FEATURE_162)) { @@ -6725,8 +6726,8 @@ static void decode_rrr2_madd(DisasContext *ctx) case OPC2_32_RRR2_MADD_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_madd64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], - cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); + gen_madd64_d(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MADDS_32: gen_helper_madd32_ssov(cpu_gpr_d[r4], tcg_env, cpu_gpr_d[r1], @@ -6735,14 +6736,14 @@ static void decode_rrr2_madd(DisasContext *ctx) case OPC2_32_RRR2_MADDS_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_madds_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], - cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); + gen_madds_64(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MADD_U_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddu64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], - cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); + gen_maddu64_d(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MADDS_U_32: gen_helper_madd32_suov(cpu_gpr_d[r4], tcg_env, cpu_gpr_d[r1], @@ -6751,8 +6752,8 @@ static void decode_rrr2_madd(DisasContext *ctx) case OPC2_32_RRR2_MADDS_U_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsu_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], - cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); + gen_maddsu_64(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r2]); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -6778,8 +6779,8 @@ static void decode_rrr2_msub(DisasContext *ctx) case OPC2_32_RRR2_MSUB_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msub64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], - cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); + gen_msub64_d(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MSUBS_32: gen_helper_msub32_ssov(cpu_gpr_d[r4], tcg_env, cpu_gpr_d[r1], @@ -6788,14 +6789,14 @@ static void decode_rrr2_msub(DisasContext *ctx) case OPC2_32_RRR2_MSUBS_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubs_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], - cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); + gen_msubs_64(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MSUB_U_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubu64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], - cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); + gen_msubu64_d(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MSUBS_U_32: gen_helper_msub32_suov(cpu_gpr_d[r4], tcg_env, cpu_gpr_d[r1], @@ -6804,8 +6805,8 @@ static void decode_rrr2_msub(DisasContext *ctx) case OPC2_32_RRR2_MSUBS_U_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubsu_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], - cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); + gen_msubsu_64(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r2]); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -6829,98 +6830,98 @@ static void decode_rrr1_madd(DisasContext *ctx) case OPC2_32_RRR1_MADD_H_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_madd_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); + gen_madd_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MADD_H_LU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_madd_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); + gen_madd_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MADD_H_UL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_madd_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); + gen_madd_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MADD_H_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_madd_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); + gen_madd_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MADDS_H_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_madds_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); + gen_madds_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MADDS_H_LU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_madds_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); + gen_madds_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MADDS_H_UL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_madds_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); + gen_madds_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MADDS_H_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_madds_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); + gen_madds_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MADDM_H_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); + gen_maddm_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MADDM_H_LU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); + gen_maddm_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MADDM_H_UL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); + gen_maddm_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MADDM_H_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); + gen_maddm_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MADDMS_H_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); + gen_maddms_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MADDMS_H_LU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); + gen_maddms_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MADDMS_H_UL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); + gen_maddms_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MADDMS_H_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); + gen_maddms_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MADDR_H_LL: gen_maddr32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], @@ -6983,61 +6984,61 @@ static void decode_rrr1_maddq_h(DisasContext *ctx) case OPC2_32_RRR1_MADD_Q_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_madd64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_madd64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n); break; case OPC2_32_RRR1_MADD_Q_32_L: - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r2]); gen_madd32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], temp, n, 16); break; case OPC2_32_RRR1_MADD_Q_64_L: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); - gen_madd64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r2]); + gen_madd64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], temp, n); break; case OPC2_32_RRR1_MADD_Q_32_U: - tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); + tcg_gen_sari_i32(temp, cpu_gpr_d[r2], 16); gen_madd32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], temp, n, 16); break; case OPC2_32_RRR1_MADD_Q_64_U: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); - gen_madd64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, + tcg_gen_sari_i32(temp, cpu_gpr_d[r2], 16); + gen_madd64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], temp, n); break; case OPC2_32_RRR1_MADD_Q_32_LL: - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); - tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_i32(temp2, cpu_gpr_d[r2]); gen_m16add32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MADD_Q_64_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); - tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); - gen_m16add64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], temp, temp2, n); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_i32(temp2, cpu_gpr_d[r2]); + gen_m16add64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], temp, temp2, n); break; case OPC2_32_RRR1_MADD_Q_32_UU: - tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + tcg_gen_sari_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_i32(temp2, cpu_gpr_d[r2], 16); gen_m16add32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MADD_Q_64_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); - gen_m16add64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], temp, temp2, n); + tcg_gen_sari_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_i32(temp2, cpu_gpr_d[r2], 16); + gen_m16add64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], temp, temp2, n); break; case OPC2_32_RRR1_MADDS_Q_32: gen_madds32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], @@ -7046,90 +7047,90 @@ static void decode_rrr1_maddq_h(DisasContext *ctx) case OPC2_32_RRR1_MADDS_Q_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_madds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_madds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n); break; case OPC2_32_RRR1_MADDS_Q_32_L: - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r2]); gen_madds32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], temp, n, 16); break; case OPC2_32_RRR1_MADDS_Q_64_L: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); - gen_madds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r2]); + gen_madds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], temp, n); break; case OPC2_32_RRR1_MADDS_Q_32_U: - tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); + tcg_gen_sari_i32(temp, cpu_gpr_d[r2], 16); gen_madds32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], temp, n, 16); break; case OPC2_32_RRR1_MADDS_Q_64_U: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); - gen_madds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, + tcg_gen_sari_i32(temp, cpu_gpr_d[r2], 16); + gen_madds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], temp, n); break; case OPC2_32_RRR1_MADDS_Q_32_LL: - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); - tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_i32(temp2, cpu_gpr_d[r2]); gen_m16adds32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MADDS_Q_64_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); - tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); - gen_m16adds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], temp, temp2, n); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_i32(temp2, cpu_gpr_d[r2]); + gen_m16adds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], temp, temp2, n); break; case OPC2_32_RRR1_MADDS_Q_32_UU: - tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + tcg_gen_sari_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_i32(temp2, cpu_gpr_d[r2], 16); gen_m16adds32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MADDS_Q_64_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); - gen_m16adds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], temp, temp2, n); + tcg_gen_sari_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_i32(temp2, cpu_gpr_d[r2], 16); + gen_m16adds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], temp, temp2, n); break; case OPC2_32_RRR1_MADDR_H_64_UL: CHECK_REG_PAIR(r3); - gen_maddr64_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r3+1], + gen_maddr64_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, 2); break; case OPC2_32_RRR1_MADDRS_H_64_UL: CHECK_REG_PAIR(r3); - gen_maddr64s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r3+1], + gen_maddr64s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, 2); break; case OPC2_32_RRR1_MADDR_Q_32_LL: - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); - tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_i32(temp2, cpu_gpr_d[r2]); gen_maddr_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MADDR_Q_32_UU: - tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + tcg_gen_sari_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_i32(temp2, cpu_gpr_d[r2], 16); gen_maddr_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MADDRS_Q_32_LL: - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); - tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_i32(temp2, cpu_gpr_d[r2]); gen_maddrs_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MADDRS_Q_32_UU: - tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + tcg_gen_sari_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_i32(temp2, cpu_gpr_d[r2], 16); gen_maddrs_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; default: @@ -7153,109 +7154,109 @@ static void decode_rrr1_maddsu_h(DisasContext *ctx) case OPC2_32_RRR1_MADDSU_H_32_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsu_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); + gen_maddsu_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MADDSU_H_32_LU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsu_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); + gen_maddsu_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MADDSU_H_32_UL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsu_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); + gen_maddsu_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MADDSU_H_32_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsu_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); + gen_maddsu_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MADDSUS_H_32_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsus_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_maddsus_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MADDSUS_H_32_LU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsus_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_maddsus_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MADDSUS_H_32_UL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsus_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_maddsus_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MADDSUS_H_32_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsus_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_maddsus_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MADDSUM_H_64_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsum_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_maddsum_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MADDSUM_H_64_LU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsum_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_maddsum_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MADDSUM_H_64_UL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsum_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_maddsum_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MADDSUM_H_64_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsum_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_maddsum_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MADDSUMS_H_64_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsums_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_maddsums_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MADDSUMS_H_64_LU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsums_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_maddsums_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MADDSUMS_H_64_UL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsums_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_maddsums_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MADDSUMS_H_64_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_maddsums_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_maddsums_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MADDSUR_H_16_LL: @@ -7311,98 +7312,98 @@ static void decode_rrr1_msub(DisasContext *ctx) case OPC2_32_RRR1_MSUB_H_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msub_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); + gen_msub_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MSUB_H_LU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msub_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); + gen_msub_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MSUB_H_UL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msub_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); + gen_msub_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MSUB_H_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msub_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); + gen_msub_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MSUBS_H_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubs_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); + gen_msubs_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MSUBS_H_LU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubs_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); + gen_msubs_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MSUBS_H_UL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubs_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); + gen_msubs_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MSUBS_H_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubs_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); + gen_msubs_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MSUBM_H_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); + gen_msubm_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MSUBM_H_LU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); + gen_msubm_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MSUBM_H_UL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); + gen_msubm_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MSUBM_H_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); + gen_msubm_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MSUBMS_H_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); + gen_msubms_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MSUBMS_H_LU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); + gen_msubms_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MSUBMS_H_UL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); + gen_msubms_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MSUBMS_H_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); + gen_msubms_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MSUBR_H_LL: gen_msubr32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], @@ -7465,61 +7466,61 @@ static void decode_rrr1_msubq_h(DisasContext *ctx) case OPC2_32_RRR1_MSUB_Q_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_msub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n); break; case OPC2_32_RRR1_MSUB_Q_32_L: - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r2]); gen_msub32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], temp, n, 16); break; case OPC2_32_RRR1_MSUB_Q_64_L: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); - gen_msub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r2]); + gen_msub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], temp, n); break; case OPC2_32_RRR1_MSUB_Q_32_U: - tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); + tcg_gen_sari_i32(temp, cpu_gpr_d[r2], 16); gen_msub32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], temp, n, 16); break; case OPC2_32_RRR1_MSUB_Q_64_U: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); - gen_msub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, + tcg_gen_sari_i32(temp, cpu_gpr_d[r2], 16); + gen_msub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], temp, n); break; case OPC2_32_RRR1_MSUB_Q_32_LL: - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); - tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_i32(temp2, cpu_gpr_d[r2]); gen_m16sub32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MSUB_Q_64_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); - tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); - gen_m16sub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], temp, temp2, n); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_i32(temp2, cpu_gpr_d[r2]); + gen_m16sub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], temp, temp2, n); break; case OPC2_32_RRR1_MSUB_Q_32_UU: - tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + tcg_gen_sari_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_i32(temp2, cpu_gpr_d[r2], 16); gen_m16sub32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MSUB_Q_64_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); - gen_m16sub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], temp, temp2, n); + tcg_gen_sari_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_i32(temp2, cpu_gpr_d[r2], 16); + gen_m16sub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], temp, temp2, n); break; case OPC2_32_RRR1_MSUBS_Q_32: gen_msubs32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], @@ -7528,90 +7529,90 @@ static void decode_rrr1_msubq_h(DisasContext *ctx) case OPC2_32_RRR1_MSUBS_Q_64: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_msubs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n); break; case OPC2_32_RRR1_MSUBS_Q_32_L: - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r2]); gen_msubs32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], temp, n, 16); break; case OPC2_32_RRR1_MSUBS_Q_64_L: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); - gen_msubs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r2]); + gen_msubs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], temp, n); break; case OPC2_32_RRR1_MSUBS_Q_32_U: - tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); + tcg_gen_sari_i32(temp, cpu_gpr_d[r2], 16); gen_msubs32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], temp, n, 16); break; case OPC2_32_RRR1_MSUBS_Q_64_U: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); - gen_msubs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, + tcg_gen_sari_i32(temp, cpu_gpr_d[r2], 16); + gen_msubs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], temp, n); break; case OPC2_32_RRR1_MSUBS_Q_32_LL: - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); - tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_i32(temp2, cpu_gpr_d[r2]); gen_m16subs32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MSUBS_Q_64_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); - tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); - gen_m16subs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], temp, temp2, n); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_i32(temp2, cpu_gpr_d[r2]); + gen_m16subs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], temp, temp2, n); break; case OPC2_32_RRR1_MSUBS_Q_32_UU: - tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + tcg_gen_sari_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_i32(temp2, cpu_gpr_d[r2], 16); gen_m16subs32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MSUBS_Q_64_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); - gen_m16subs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], temp, temp2, n); + tcg_gen_sari_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_i32(temp2, cpu_gpr_d[r2], 16); + gen_m16subs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], temp, temp2, n); break; case OPC2_32_RRR1_MSUBR_H_64_UL: CHECK_REG_PAIR(r3); - gen_msubr64_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r3+1], + gen_msubr64_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, 2); break; case OPC2_32_RRR1_MSUBRS_H_64_UL: CHECK_REG_PAIR(r3); - gen_msubr64s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r3+1], + gen_msubr64s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, 2); break; case OPC2_32_RRR1_MSUBR_Q_32_LL: - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); - tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_i32(temp2, cpu_gpr_d[r2]); gen_msubr_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MSUBR_Q_32_UU: - tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + tcg_gen_sari_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_i32(temp2, cpu_gpr_d[r2], 16); gen_msubr_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MSUBRS_Q_32_LL: - tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); - tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + tcg_gen_ext16s_i32(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_i32(temp2, cpu_gpr_d[r2]); gen_msubrs_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MSUBRS_Q_32_UU: - tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + tcg_gen_sari_i32(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_i32(temp2, cpu_gpr_d[r2], 16); gen_msubrs_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; default: @@ -7635,109 +7636,109 @@ static void decode_rrr1_msubad_h(DisasContext *ctx) case OPC2_32_RRR1_MSUBAD_H_32_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubad_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); + gen_msubad_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MSUBAD_H_32_LU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubad_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); + gen_msubad_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MSUBAD_H_32_UL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubad_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); + gen_msubad_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MSUBAD_H_32_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubad_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); + gen_msubad_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MSUBADS_H_32_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubads_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_msubads_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MSUBADS_H_32_LU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubads_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_msubads_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MSUBADS_H_32_UL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubads_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_msubads_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MSUBADS_H_32_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubads_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_msubads_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MSUBADM_H_64_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubadm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_msubadm_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MSUBADM_H_64_LU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubadm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_msubadm_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MSUBADM_H_64_UL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubadm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_msubadm_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MSUBADM_H_64_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubadm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_msubadm_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MSUBADMS_H_64_LL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubadms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_msubadms_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MSUBADMS_H_64_LU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubadms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_msubadms_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MSUBADMS_H_64_UL: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubadms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_msubadms_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MSUBADMS_H_64_UU: CHECK_REG_PAIR(r4); CHECK_REG_PAIR(r3); - gen_msubadms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], - cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + gen_msubadms_h(cpu_gpr_d[r4], cpu_gpr_d[r4 + 1], cpu_gpr_d[r3], + cpu_gpr_d[r3 + 1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MSUBADR_H_16_LL: @@ -7795,43 +7796,43 @@ static void decode_rrrr_extract_insert(DisasContext *ctx) switch (op2) { case OPC2_32_RRRR_DEXTR: - tcg_gen_andi_tl(tmp_pos, cpu_gpr_d[r3], 0x1f); + tcg_gen_andi_i32(tmp_pos, cpu_gpr_d[r3], 0x1f); if (r1 == r2) { - tcg_gen_rotl_tl(cpu_gpr_d[r4], cpu_gpr_d[r1], tmp_pos); + tcg_gen_rotl_i32(cpu_gpr_d[r4], cpu_gpr_d[r1], tmp_pos); } else { TCGv msw = tcg_temp_new(); - TCGv zero = tcg_constant_tl(0); - tcg_gen_shl_tl(tmp_width, cpu_gpr_d[r1], tmp_pos); - tcg_gen_subfi_tl(msw, 32, tmp_pos); - tcg_gen_shr_tl(msw, cpu_gpr_d[r2], msw); + TCGv zero = tcg_constant_i32(0); + tcg_gen_shl_i32(tmp_width, cpu_gpr_d[r1], tmp_pos); + tcg_gen_subfi_i32(msw, 32, tmp_pos); + tcg_gen_shr_i32(msw, cpu_gpr_d[r2], msw); /* * if pos == 0, then we do cpu_gpr_d[r2] << 32, which is undefined * behaviour. So check that case here and set the low bits to zero * which effectivly returns cpu_gpr_d[r1] */ - tcg_gen_movcond_tl(TCG_COND_EQ, msw, tmp_pos, zero, zero, msw); - tcg_gen_or_tl(cpu_gpr_d[r4], tmp_width, msw); + tcg_gen_movcond_i32(TCG_COND_EQ, msw, tmp_pos, zero, zero, msw); + tcg_gen_or_i32(cpu_gpr_d[r4], tmp_width, msw); } break; case OPC2_32_RRRR_EXTR: case OPC2_32_RRRR_EXTR_U: CHECK_REG_PAIR(r3); - tcg_gen_andi_tl(tmp_width, cpu_gpr_d[r3+1], 0x1f); - tcg_gen_andi_tl(tmp_pos, cpu_gpr_d[r3], 0x1f); - tcg_gen_add_tl(tmp_pos, tmp_pos, tmp_width); - tcg_gen_subfi_tl(tmp_pos, 32, tmp_pos); - tcg_gen_shl_tl(cpu_gpr_d[r4], cpu_gpr_d[r1], tmp_pos); - tcg_gen_subfi_tl(tmp_width, 32, tmp_width); + tcg_gen_andi_i32(tmp_width, cpu_gpr_d[r3 + 1], 0x1f); + tcg_gen_andi_i32(tmp_pos, cpu_gpr_d[r3], 0x1f); + tcg_gen_add_i32(tmp_pos, tmp_pos, tmp_width); + tcg_gen_subfi_i32(tmp_pos, 32, tmp_pos); + tcg_gen_shl_i32(cpu_gpr_d[r4], cpu_gpr_d[r1], tmp_pos); + tcg_gen_subfi_i32(tmp_width, 32, tmp_width); if (op2 == OPC2_32_RRRR_EXTR) { - tcg_gen_sar_tl(cpu_gpr_d[r4], cpu_gpr_d[r4], tmp_width); + tcg_gen_sar_i32(cpu_gpr_d[r4], cpu_gpr_d[r4], tmp_width); } else { - tcg_gen_shr_tl(cpu_gpr_d[r4], cpu_gpr_d[r4], tmp_width); + tcg_gen_shr_i32(cpu_gpr_d[r4], cpu_gpr_d[r4], tmp_width); } break; case OPC2_32_RRRR_INSERT: CHECK_REG_PAIR(r3); - tcg_gen_andi_tl(tmp_width, cpu_gpr_d[r3+1], 0x1f); - tcg_gen_andi_tl(tmp_pos, cpu_gpr_d[r3], 0x1f); + tcg_gen_andi_i32(tmp_width, cpu_gpr_d[r3 + 1], 0x1f); + tcg_gen_andi_i32(tmp_pos, cpu_gpr_d[r3], 0x1f); gen_insert(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r2], tmp_width, tmp_pos); break; @@ -7860,35 +7861,35 @@ static void decode_rrrw_extract_insert(DisasContext *ctx) switch (op2) { case OPC2_32_RRRW_EXTR: - tcg_gen_andi_tl(temp, cpu_gpr_d[r3], 0x1f); - tcg_gen_addi_tl(temp, temp, width); - tcg_gen_subfi_tl(temp, 32, temp); - tcg_gen_shl_tl(cpu_gpr_d[r4], cpu_gpr_d[r1], temp); - tcg_gen_sari_tl(cpu_gpr_d[r4], cpu_gpr_d[r4], 32 - width); + tcg_gen_andi_i32(temp, cpu_gpr_d[r3], 0x1f); + tcg_gen_addi_i32(temp, temp, width); + tcg_gen_subfi_i32(temp, 32, temp); + tcg_gen_shl_i32(cpu_gpr_d[r4], cpu_gpr_d[r1], temp); + tcg_gen_sari_i32(cpu_gpr_d[r4], cpu_gpr_d[r4], 32 - width); break; case OPC2_32_RRRW_EXTR_U: if (width == 0) { - tcg_gen_movi_tl(cpu_gpr_d[r4], 0); + tcg_gen_movi_i32(cpu_gpr_d[r4], 0); } else { - tcg_gen_andi_tl(temp, cpu_gpr_d[r3], 0x1f); - tcg_gen_shr_tl(cpu_gpr_d[r4], cpu_gpr_d[r1], temp); - tcg_gen_andi_tl(cpu_gpr_d[r4], cpu_gpr_d[r4], ~0u >> (32-width)); + tcg_gen_andi_i32(temp, cpu_gpr_d[r3], 0x1f); + tcg_gen_shr_i32(cpu_gpr_d[r4], cpu_gpr_d[r1], temp); + tcg_gen_andi_i32(cpu_gpr_d[r4], cpu_gpr_d[r4], ~0u >> (32 - width)); } break; case OPC2_32_RRRW_IMASK: temp2 = tcg_temp_new(); CHECK_REG_PAIR(r4); - tcg_gen_andi_tl(temp, cpu_gpr_d[r3], 0x1f); - tcg_gen_movi_tl(temp2, (1 << width) - 1); - tcg_gen_shl_tl(temp2, temp2, temp); - tcg_gen_shl_tl(cpu_gpr_d[r4], cpu_gpr_d[r2], temp); - tcg_gen_mov_tl(cpu_gpr_d[r4+1], temp2); + tcg_gen_andi_i32(temp, cpu_gpr_d[r3], 0x1f); + tcg_gen_movi_i32(temp2, (1 << width) - 1); + tcg_gen_shl_i32(temp2, temp2, temp); + tcg_gen_shl_i32(cpu_gpr_d[r4], cpu_gpr_d[r2], temp); + tcg_gen_mov_i32(cpu_gpr_d[r4 + 1], temp2); break; case OPC2_32_RRRW_INSERT: temp2 = tcg_temp_new(); - tcg_gen_movi_tl(temp, width); - tcg_gen_andi_tl(temp2, cpu_gpr_d[r3], 0x1f); + tcg_gen_movi_i32(temp, width); + tcg_gen_andi_i32(temp2, cpu_gpr_d[r3], 0x1f); gen_insert(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r2], temp, temp2); break; default: @@ -7913,7 +7914,7 @@ static void decode_sys_interrupts(DisasContext *ctx) break; case OPC2_32_SYS_DISABLE: if (ctx->priv == TRICORE_PRIV_SM || ctx->priv == TRICORE_PRIV_UM1) { - tcg_gen_andi_tl(cpu_ICR, cpu_ICR, ~ctx->icr_ie_mask); + tcg_gen_andi_i32(cpu_ICR, cpu_ICR, ~ctx->icr_ie_mask); } else { generate_trap(ctx, TRAPC_PROT, TIN1_PRIV); } @@ -7921,9 +7922,9 @@ static void decode_sys_interrupts(DisasContext *ctx) case OPC2_32_SYS_DISABLE_D: if (has_feature(ctx, TRICORE_FEATURE_16)) { if (ctx->priv == TRICORE_PRIV_SM || ctx->priv == TRICORE_PRIV_UM1) { - tcg_gen_extract_tl(cpu_gpr_d[r1], cpu_ICR, + tcg_gen_extract_i32(cpu_gpr_d[r1], cpu_ICR, ctx->icr_ie_offset, 1); - tcg_gen_andi_tl(cpu_ICR, cpu_ICR, ~ctx->icr_ie_mask); + tcg_gen_andi_i32(cpu_ICR, cpu_ICR, ~ctx->icr_ie_mask); } else { generate_trap(ctx, TRAPC_PROT, TIN1_PRIV); } @@ -7934,7 +7935,7 @@ static void decode_sys_interrupts(DisasContext *ctx) break; case OPC2_32_SYS_ENABLE: if (ctx->priv == TRICORE_PRIV_SM || ctx->priv == TRICORE_PRIV_UM1) { - tcg_gen_ori_tl(cpu_ICR, cpu_ICR, ctx->icr_ie_mask); + tcg_gen_ori_i32(cpu_ICR, cpu_ICR, ctx->icr_ie_mask); ctx->base.is_jmp = DISAS_EXIT_UPDATE; } else { generate_trap(ctx, TRAPC_PROT, TIN1_PRIV); @@ -7960,8 +7961,8 @@ static void decode_sys_interrupts(DisasContext *ctx) l1 = gen_new_label(); tcg_gen_ld_i32(tmp, tcg_env, offsetof(CPUTriCoreState, DBGSR)); - tcg_gen_andi_tl(tmp, tmp, MASK_DBGSR_DE); - tcg_gen_brcondi_tl(TCG_COND_NE, tmp, 1, l1); + tcg_gen_andi_i32(tmp, tmp, MASK_DBGSR_DE); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 1, l1); gen_helper_rfm(tcg_env); gen_set_label(l1); ctx->base.is_jmp = DISAS_EXIT; @@ -7978,7 +7979,7 @@ static void decode_sys_interrupts(DisasContext *ctx) case OPC2_32_SYS_RESTORE: if (has_feature(ctx, TRICORE_FEATURE_16)) { if (ctx->priv == TRICORE_PRIV_SM || ctx->priv == TRICORE_PRIV_UM1) { - tcg_gen_deposit_tl(cpu_ICR, cpu_ICR, cpu_gpr_d[r1], + tcg_gen_deposit_i32(cpu_ICR, cpu_ICR, cpu_gpr_d[r1], ctx->icr_ie_offset, 1); ctx->base.is_jmp = DISAS_EXIT_UPDATE; } else { @@ -7990,13 +7991,13 @@ static void decode_sys_interrupts(DisasContext *ctx) break; case OPC2_32_SYS_TRAPSV: l1 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_GE, cpu_PSW_SV, 0, l1); + tcg_gen_brcondi_i32(TCG_COND_GE, cpu_PSW_SV, 0, l1); generate_trap(ctx, TRAPC_ASSERT, TIN5_SOVF); gen_set_label(l1); break; case OPC2_32_SYS_TRAPV: l1 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_GE, cpu_PSW_V, 0, l1); + tcg_gen_brcondi_i32(TCG_COND_GE, cpu_PSW_V, 0, l1); generate_trap(ctx, TRAPC_ASSERT, TIN5_OVF); gen_set_label(l1); break; @@ -8047,16 +8048,16 @@ static void decode_32Bit_opc(DisasContext *ctx) temp = tcg_constant_i32(EA_ABS_FORMAT(address)); temp2 = tcg_temp_new(); - tcg_gen_shri_tl(temp2, cpu_gpr_d[r1], 16); - tcg_gen_qemu_st_tl(temp2, temp, ctx->mem_idx, MO_LEUW); + tcg_gen_shri_i32(temp2, cpu_gpr_d[r1], 16); + tcg_gen_qemu_st_i32(temp2, temp, ctx->mem_idx, MO_LEUW); break; case OPC1_32_ABS_LD_Q: address = MASK_OP_ABS_OFF18(ctx->opcode); r1 = MASK_OP_ABS_S1D(ctx->opcode); temp = tcg_constant_i32(EA_ABS_FORMAT(address)); - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUW); - tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); + tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUW); + tcg_gen_shli_i32(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); break; case OPCM_32_ABS_LEA_LHA: address = MASK_OP_ABS_OFF18(ctx->opcode); @@ -8065,13 +8066,13 @@ static void decode_32Bit_opc(DisasContext *ctx) if (has_feature(ctx, TRICORE_FEATURE_162)) { op2 = MASK_OP_ABS_OP2(ctx->opcode); if (op2 == OPC2_32_ABS_LHA) { - tcg_gen_movi_tl(cpu_gpr_a[r1], address << 14); + tcg_gen_movi_i32(cpu_gpr_a[r1], address << 14); break; } /* otherwise translate regular LEA */ } - tcg_gen_movi_tl(cpu_gpr_a[r1], EA_ABS_FORMAT(address)); + tcg_gen_movi_i32(cpu_gpr_a[r1], EA_ABS_FORMAT(address)); break; /* ABSB-format */ case OPC1_32_ABSB_ST_T: @@ -8082,10 +8083,10 @@ static void decode_32Bit_opc(DisasContext *ctx) temp = tcg_constant_i32(EA_ABS_FORMAT(address)); temp2 = tcg_temp_new(); - tcg_gen_qemu_ld_tl(temp2, temp, ctx->mem_idx, MO_UB); - tcg_gen_andi_tl(temp2, temp2, ~(0x1u << bpos)); - tcg_gen_ori_tl(temp2, temp2, (b << bpos)); - tcg_gen_qemu_st_tl(temp2, temp, ctx->mem_idx, MO_UB); + tcg_gen_qemu_ld_i32(temp2, temp, ctx->mem_idx, MO_UB); + tcg_gen_andi_i32(temp2, temp2, ~(0x1u << bpos)); + tcg_gen_ori_i32(temp2, temp2, (b << bpos)); + tcg_gen_qemu_st_i32(temp2, temp, ctx->mem_idx, MO_UB); break; /* B-format */ case OPC1_32_B_CALL: @@ -8212,8 +8213,8 @@ static void decode_32Bit_opc(DisasContext *ctx) CHECK_REG_PAIR(r2); - tcg_gen_andi_tl(temp2, cpu_gpr_d[r2 + 1], 0x1f); - tcg_gen_andi_tl(temp3, cpu_gpr_d[r2], 0x1f); + tcg_gen_andi_i32(temp2, cpu_gpr_d[r2 + 1], 0x1f); + tcg_gen_andi_i32(temp3, cpu_gpr_d[r2], 0x1f); gen_insert(cpu_gpr_d[r3], cpu_gpr_d[r1], temp, temp2, temp3); break; @@ -8281,7 +8282,7 @@ static void decode_32Bit_opc(DisasContext *ctx) r3 = MASK_OP_RRPW_D(ctx->opcode); const16 = MASK_OP_RRPW_POS(ctx->opcode); - tcg_gen_extract2_tl(cpu_gpr_d[r3], cpu_gpr_d[r2], cpu_gpr_d[r1], + tcg_gen_extract2_i32(cpu_gpr_d[r3], cpu_gpr_d[r2], cpu_gpr_d[r1], 32 - const16); break; /* RRR Format */ @@ -8330,10 +8331,10 @@ static void decode_32Bit_opc(DisasContext *ctx) decode_sys_interrupts(ctx); break; case OPC1_32_SYS_RSTV: - tcg_gen_movi_tl(cpu_PSW_V, 0); - tcg_gen_mov_tl(cpu_PSW_SV, cpu_PSW_V); - tcg_gen_mov_tl(cpu_PSW_AV, cpu_PSW_V); - tcg_gen_mov_tl(cpu_PSW_SAV, cpu_PSW_V); + tcg_gen_movi_i32(cpu_PSW_V, 0); + tcg_gen_mov_i32(cpu_PSW_SV, cpu_PSW_V); + tcg_gen_mov_i32(cpu_PSW_AV, cpu_PSW_V); + tcg_gen_mov_i32(cpu_PSW_SAV, cpu_PSW_V); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); From e843ef2bbace2cac9e9b92869c4b69c175d6ca72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Oct 2025 06:01:38 +0200 Subject: [PATCH 1666/1794] target/tricore: Pass DisasContext as first argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unify style, always pass DisasContext as the first argument. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251010052141.42460-11-philmd@linaro.org> --- target/tricore/translate.c | 72 ++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/target/tricore/translate.c b/target/tricore/translate.c index ef3653b8820fa..62a2d807266fc 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -226,7 +226,7 @@ static inline void gen_offset_st(DisasContext *ctx, TCGv r1, TCGv r2, tcg_gen_qemu_st_i32(r1, temp, ctx->mem_idx, mop); } -static void gen_st_2regs_64(TCGv rh, TCGv rl, TCGv address, DisasContext *ctx) +static void gen_st_2regs_64(DisasContext *ctx, TCGv rh, TCGv rl, TCGv address) { TCGv_i64 temp = tcg_temp_new_i64(); @@ -234,15 +234,15 @@ static void gen_st_2regs_64(TCGv rh, TCGv rl, TCGv address, DisasContext *ctx) tcg_gen_qemu_st_i64(temp, address, ctx->mem_idx, MO_LEUQ); } -static void gen_offset_st_2regs(TCGv rh, TCGv rl, TCGv base, int16_t con, - DisasContext *ctx) +static void gen_offset_st_2regs(DisasContext *ctx, + TCGv rh, TCGv rl, TCGv base, int16_t con) { TCGv temp = tcg_temp_new(); tcg_gen_addi_i32(temp, base, con); - gen_st_2regs_64(rh, rl, temp, ctx); + gen_st_2regs_64(ctx, rh, rl, temp); } -static void gen_ld_2regs_64(TCGv rh, TCGv rl, TCGv address, DisasContext *ctx) +static void gen_ld_2regs_64(DisasContext *ctx, TCGv rh, TCGv rl, TCGv address) { TCGv_i64 temp = tcg_temp_new_i64(); @@ -251,12 +251,12 @@ static void gen_ld_2regs_64(TCGv rh, TCGv rl, TCGv address, DisasContext *ctx) tcg_gen_extr_i64_i32(rl, rh, temp); } -static void gen_offset_ld_2regs(TCGv rh, TCGv rl, TCGv base, int16_t con, - DisasContext *ctx) +static void gen_offset_ld_2regs(DisasContext *ctx, + TCGv rh, TCGv rl, TCGv base, int16_t con) { TCGv temp = tcg_temp_new(); tcg_gen_addi_i32(temp, base, con); - gen_ld_2regs_64(rh, rl, temp, ctx); + gen_ld_2regs_64(ctx, rh, rl, temp); } static void gen_st_preincr(DisasContext *ctx, TCGv r1, TCGv r2, int16_t off, @@ -3798,11 +3798,11 @@ static void decode_abs_ldw(DisasContext *ctx) break; case OPC2_32_ABS_LD_D: CHECK_REG_PAIR(r1); - gen_ld_2regs_64(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp, ctx); + gen_ld_2regs_64(ctx, cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp); break; case OPC2_32_ABS_LD_DA: CHECK_REG_PAIR(r1); - gen_ld_2regs_64(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp, ctx); + gen_ld_2regs_64(ctx, cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp); break; case OPC2_32_ABS_LD_W: tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESL); @@ -3913,11 +3913,11 @@ static void decode_abs_store(DisasContext *ctx) break; case OPC2_32_ABS_ST_D: CHECK_REG_PAIR(r1); - gen_st_2regs_64(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp, ctx); + gen_st_2regs_64(ctx, cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp); break; case OPC2_32_ABS_ST_DA: CHECK_REG_PAIR(r1); - gen_st_2regs_64(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp, ctx); + gen_st_2regs_64(ctx, cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp); break; case OPC2_32_ABS_ST_W: tcg_gen_qemu_st_i32(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESL); @@ -4289,36 +4289,38 @@ static void decode_bo_addrmode_post_pre_base(DisasContext *ctx) break; case OPC2_32_BO_ST_D_SHORTOFF: CHECK_REG_PAIR(r1); - gen_offset_st_2regs(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], cpu_gpr_a[r2], - off10, ctx); + gen_offset_st_2regs(ctx, + cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], cpu_gpr_a[r2], + off10); break; case OPC2_32_BO_ST_D_POSTINC: CHECK_REG_PAIR(r1); - gen_st_2regs_64(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], cpu_gpr_a[r2], ctx); + gen_st_2regs_64(ctx, cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], cpu_gpr_a[r2]); tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_ST_D_PREINC: CHECK_REG_PAIR(r1); temp = tcg_temp_new(); tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); - gen_st_2regs_64(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp, ctx); + gen_st_2regs_64(ctx, cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp); tcg_gen_mov_i32(cpu_gpr_a[r2], temp); break; case OPC2_32_BO_ST_DA_SHORTOFF: CHECK_REG_PAIR(r1); - gen_offset_st_2regs(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], cpu_gpr_a[r2], - off10, ctx); + gen_offset_st_2regs(ctx, + cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], cpu_gpr_a[r2], + off10); break; case OPC2_32_BO_ST_DA_POSTINC: CHECK_REG_PAIR(r1); - gen_st_2regs_64(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], cpu_gpr_a[r2], ctx); + gen_st_2regs_64(ctx, cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], cpu_gpr_a[r2]); tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_ST_DA_PREINC: CHECK_REG_PAIR(r1); temp = tcg_temp_new(); tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); - gen_st_2regs_64(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp, ctx); + gen_st_2regs_64(ctx, cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp); tcg_gen_mov_i32(cpu_gpr_a[r2], temp); break; case OPC2_32_BO_ST_H_SHORTOFF: @@ -4413,7 +4415,7 @@ static void decode_bo_addrmode_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_ST_D_BR: CHECK_REG_PAIR(r1); - gen_st_2regs_64(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp2, ctx); + gen_st_2regs_64(ctx, cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp2); gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_ST_D_CIRC: @@ -4428,7 +4430,7 @@ static void decode_bo_addrmode_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_ST_DA_BR: CHECK_REG_PAIR(r1); - gen_st_2regs_64(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp2, ctx); + gen_st_2regs_64(ctx, cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp2); gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_ST_DA_CIRC: @@ -4520,36 +4522,38 @@ static void decode_bo_addrmode_ld_post_pre_base(DisasContext *ctx) break; case OPC2_32_BO_LD_D_SHORTOFF: CHECK_REG_PAIR(r1); - gen_offset_ld_2regs(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], cpu_gpr_a[r2], - off10, ctx); + gen_offset_ld_2regs(ctx, + cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], cpu_gpr_a[r2], + off10); break; case OPC2_32_BO_LD_D_POSTINC: CHECK_REG_PAIR(r1); - gen_ld_2regs_64(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], cpu_gpr_a[r2], ctx); + gen_ld_2regs_64(ctx, cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], cpu_gpr_a[r2]); tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_LD_D_PREINC: CHECK_REG_PAIR(r1); temp = tcg_temp_new(); tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); - gen_ld_2regs_64(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp, ctx); + gen_ld_2regs_64(ctx, cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp); tcg_gen_mov_i32(cpu_gpr_a[r2], temp); break; case OPC2_32_BO_LD_DA_SHORTOFF: CHECK_REG_PAIR(r1); - gen_offset_ld_2regs(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], cpu_gpr_a[r2], - off10, ctx); + gen_offset_ld_2regs(ctx, + cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], cpu_gpr_a[r2], + off10); break; case OPC2_32_BO_LD_DA_POSTINC: CHECK_REG_PAIR(r1); - gen_ld_2regs_64(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], cpu_gpr_a[r2], ctx); + gen_ld_2regs_64(ctx, cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], cpu_gpr_a[r2]); tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_LD_DA_PREINC: CHECK_REG_PAIR(r1); temp = tcg_temp_new(); tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); - gen_ld_2regs_64(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp, ctx); + gen_ld_2regs_64(ctx, cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp); tcg_gen_mov_i32(cpu_gpr_a[r2], temp); break; case OPC2_32_BO_LD_H_SHORTOFF: @@ -4651,7 +4655,7 @@ static void decode_bo_addrmode_ld_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_LD_D_BR: CHECK_REG_PAIR(r1); - gen_ld_2regs_64(cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp2, ctx); + gen_ld_2regs_64(ctx, cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp2); gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_LD_D_CIRC: @@ -4666,7 +4670,7 @@ static void decode_bo_addrmode_ld_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_LD_DA_BR: CHECK_REG_PAIR(r1); - gen_ld_2regs_64(cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp2, ctx); + gen_ld_2regs_64(ctx, cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp2); gen_helper_br_update(cpu_gpr_a[r2 + 1], cpu_gpr_a[r2 + 1]); break; case OPC2_32_BO_LD_DA_CIRC: @@ -8377,7 +8381,7 @@ static void tricore_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) tcg_gen_insn_start(ctx->base.pc_next); } -static bool insn_crosses_page(CPUTriCoreState *env, DisasContext *ctx) +static bool insn_crosses_page(DisasContext *ctx, CPUTriCoreState *env) { /* * Return true if the insn at ctx->base.pc_next might cross a page boundary. @@ -8420,7 +8424,7 @@ static void tricore_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) page_start = ctx->base.pc_first & TARGET_PAGE_MASK; if (ctx->base.pc_next - page_start >= TARGET_PAGE_SIZE || (ctx->base.pc_next - page_start >= TARGET_PAGE_SIZE - 3 - && insn_crosses_page(env, ctx))) { + && insn_crosses_page(ctx, env))) { ctx->base.is_jmp = DISAS_TOO_MANY; } } From 8a2235dd077b4715b635628a0cdde835a732ea51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Oct 2025 06:15:47 +0200 Subject: [PATCH 1667/1794] target/tricore: Un-inline various helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rely on the linker to optimize at linking time. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251010052141.42460-12-philmd@linaro.org> --- target/tricore/translate.c | 274 ++++++++++++++++++------------------- 1 file changed, 135 insertions(+), 139 deletions(-) diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 62a2d807266fc..787c77292fd19 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -201,7 +201,7 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) /* For two 32-bit registers used a 64-bit register, the first registernumber needs to be even. Otherwise we trap. */ -static inline void generate_trap(DisasContext *ctx, int class, int tin); +static void generate_trap(DisasContext *ctx, int class, int tin); #define CHECK_REG_PAIR(reg) do { \ if (reg & 0x1) { \ generate_trap(ctx, TRAPC_INSN_ERR, TIN2_OPD); \ @@ -210,16 +210,16 @@ static inline void generate_trap(DisasContext *ctx, int class, int tin); /* Functions for load/save to/from memory */ -static inline void gen_offset_ld(DisasContext *ctx, TCGv r1, TCGv r2, - int16_t con, MemOp mop) +static void gen_offset_ld(DisasContext *ctx, TCGv r1, TCGv r2, + int16_t con, MemOp mop) { TCGv temp = tcg_temp_new(); tcg_gen_addi_i32(temp, r2, con); tcg_gen_qemu_ld_i32(r1, temp, ctx->mem_idx, mop); } -static inline void gen_offset_st(DisasContext *ctx, TCGv r1, TCGv r2, - int16_t con, MemOp mop) +static void gen_offset_st(DisasContext *ctx, TCGv r1, TCGv r2, + int16_t con, MemOp mop) { TCGv temp = tcg_temp_new(); tcg_gen_addi_i32(temp, r2, con); @@ -346,7 +346,7 @@ static void gen_swapmsk(DisasContext *ctx, int reg, TCGv ea) break; #define A(ADDRESS, REG, FEATURE) R(ADDRESS, REG, FEATURE) #define E(ADDRESS, REG, FEATURE) R(ADDRESS, REG, FEATURE) -static inline void gen_mfcr(DisasContext *ctx, TCGv ret, int32_t offset) +static void gen_mfcr(DisasContext *ctx, TCGv ret, int32_t offset) { /* since we're caching PSW make this a special case */ if (offset == 0xfe04) { @@ -374,8 +374,7 @@ static inline void gen_mfcr(DisasContext *ctx, TCGv ret, int32_t offset) watchdog device, we handle endinit protected registers like all-access registers for now. */ #define E(ADDRESS, REG, FEATURE) A(ADDRESS, REG, FEATURE) -static inline void gen_mtcr(DisasContext *ctx, TCGv r1, - int32_t offset) +static void gen_mtcr(DisasContext *ctx, TCGv r1, int32_t offset) { if (ctx->priv == TRICORE_PRIV_SM) { /* since we're caching PSW make this a special case */ @@ -394,7 +393,7 @@ static inline void gen_mtcr(DisasContext *ctx, TCGv r1, /* Functions for arithmetic instructions */ -static inline void gen_add_d(TCGv ret, TCGv r1, TCGv r2) +static void gen_add_d(TCGv ret, TCGv r1, TCGv r2) { TCGv t0 = tcg_temp_new_i32(); TCGv result = tcg_temp_new_i32(); @@ -415,7 +414,7 @@ static inline void gen_add_d(TCGv ret, TCGv r1, TCGv r2) tcg_gen_mov_i32(ret, result); } -static inline void +static void gen_add64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) { TCGv temp = tcg_temp_new(); @@ -441,7 +440,7 @@ gen_add64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) tcg_gen_mov_i64(ret, result); } -static inline void +static void gen_addsub64_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, void(*op1)(TCGv, TCGv, TCGv), void(*op2)(TCGv, TCGv, TCGv)) @@ -488,7 +487,7 @@ gen_addsub64_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, } /* ret = r2 + (r1 * r3); */ -static inline void gen_madd32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) +static void gen_madd32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); @@ -519,13 +518,13 @@ static inline void gen_madd32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static inline void gen_maddi32_d(TCGv ret, TCGv r1, TCGv r2, int32_t con) +static void gen_maddi32_d(TCGv ret, TCGv r1, TCGv r2, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_madd32_d(ret, r1, r2, temp); } -static inline void +static void gen_madd64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, TCGv r3) { @@ -553,7 +552,7 @@ gen_madd64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_mov_i32(ret_high, t4); } -static inline void +static void gen_maddu64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, TCGv r3) { @@ -583,7 +582,7 @@ gen_maddu64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static inline void +static void gen_maddi64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, int32_t con) { @@ -591,7 +590,7 @@ gen_maddi64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, gen_madd64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); } -static inline void +static void gen_maddui64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, int32_t con) { @@ -599,7 +598,7 @@ gen_maddui64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, gen_maddu64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); } -static inline void +static void gen_madd_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -626,7 +625,7 @@ gen_madd_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_add_tl, tcg_gen_add_tl); } -static inline void +static void gen_maddsu_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -653,7 +652,7 @@ gen_maddsu_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_sub_tl, tcg_gen_add_tl); } -static inline void +static void gen_maddsum_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -686,9 +685,9 @@ gen_maddsum_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64_2); } -static inline void gen_adds(TCGv ret, TCGv r1, TCGv r2); +static void gen_adds(TCGv ret, TCGv r1, TCGv r2); -static inline void +static void gen_madds_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -723,9 +722,9 @@ gen_madds_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_or_i32(cpu_PSW_AV, cpu_PSW_AV, temp3); } -static inline void gen_subs(TCGv ret, TCGv r1, TCGv r2); +static void gen_subs(TCGv ret, TCGv r1, TCGv r2); -static inline void +static void gen_maddsus_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -760,7 +759,7 @@ gen_maddsus_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_or_i32(cpu_PSW_AV, cpu_PSW_AV, temp3); } -static inline void +static void gen_maddsums_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -793,7 +792,7 @@ gen_maddsums_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, } -static inline void +static void gen_maddm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -821,7 +820,7 @@ gen_maddm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64_3); } -static inline void +static void gen_maddms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -847,7 +846,7 @@ gen_maddms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); } -static inline void +static void gen_maddr64_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -870,7 +869,7 @@ gen_maddr64_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, gen_helper_addr_h(ret, tcg_env, temp64, r1_low, r1_high); } -static inline void +static void gen_maddr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { TCGv temp = tcg_temp_new(); @@ -881,7 +880,7 @@ gen_maddr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) gen_maddr64_h(ret, temp, temp2, r2, r3, n, mode); } -static inline void +static void gen_maddsur32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { TCGv t_n = tcg_constant_i32(n); @@ -908,7 +907,7 @@ gen_maddsur32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) } -static inline void +static void gen_maddr64s_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -931,7 +930,7 @@ gen_maddr64s_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, gen_helper_addr_h_ssov(ret, tcg_env, temp64, r1_low, r1_high); } -static inline void +static void gen_maddr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { TCGv temp = tcg_temp_new(); @@ -942,7 +941,7 @@ gen_maddr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) gen_maddr64s_h(ret, temp, temp2, r2, r3, n, mode); } -static inline void +static void gen_maddsur32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { TCGv t_n = tcg_constant_i32(n); @@ -968,21 +967,21 @@ gen_maddsur32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) gen_helper_addsur_h_ssov(ret, tcg_env, temp64, temp, temp2); } -static inline void +static void gen_maddr_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) { TCGv t_n = tcg_constant_i32(n); gen_helper_maddr_q(ret, tcg_env, r1, r2, r3, t_n); } -static inline void +static void gen_maddrs_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) { TCGv t_n = tcg_constant_i32(n); gen_helper_maddr_q_ssov(ret, tcg_env, r1, r2, r3, t_n); } -static inline void +static void gen_madd32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, uint32_t up_shift) { @@ -1032,7 +1031,7 @@ gen_madd32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, tcg_gen_mov_i32(ret, temp3); } -static inline void +static void gen_m16add32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) { TCGv temp = tcg_temp_new(); @@ -1049,7 +1048,7 @@ gen_m16add32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) gen_add_d(ret, arg1, temp); } -static inline void +static void gen_m16adds32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) { TCGv temp = tcg_temp_new(); @@ -1066,7 +1065,7 @@ gen_m16adds32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) gen_adds(ret, arg1, temp); } -static inline void +static void gen_m16add64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, TCGv arg3, uint32_t n) { @@ -1093,7 +1092,7 @@ gen_m16add64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_extr_i64_i32(rl, rh, t3); } -static inline void +static void gen_m16adds64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, TCGv arg3, uint32_t n) { @@ -1119,7 +1118,7 @@ gen_m16adds64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_extr_i64_i32(rl, rh, t1); } -static inline void +static void gen_madd64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, TCGv arg3, uint32_t n) { @@ -1167,7 +1166,7 @@ gen_madd64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static inline void +static void gen_madds32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, uint32_t up_shift) { @@ -1185,7 +1184,7 @@ gen_madds32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, gen_helper_madd32_q_add_ssov(ret, tcg_env, t1, t2); } -static inline void +static void gen_madds64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, TCGv arg3, uint32_t n) { @@ -1198,7 +1197,7 @@ gen_madds64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, } /* ret = r2 - (r1 * r3); */ -static inline void gen_msub32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) +static void gen_msub32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); @@ -1230,13 +1229,13 @@ static inline void gen_msub32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static inline void gen_msubi32_d(TCGv ret, TCGv r1, TCGv r2, int32_t con) +static void gen_msubi32_d(TCGv ret, TCGv r1, TCGv r2, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_msub32_d(ret, r1, r2, temp); } -static inline void +static void gen_msub64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, TCGv r3) { @@ -1264,7 +1263,7 @@ gen_msub64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_mov_i32(ret_high, t4); } -static inline void +static void gen_msubi64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, int32_t con) { @@ -1272,7 +1271,7 @@ gen_msubi64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, gen_msub64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); } -static inline void +static void gen_msubu64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, TCGv r3) { @@ -1300,7 +1299,7 @@ gen_msubu64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static inline void +static void gen_msubui64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, int32_t con) { @@ -1308,14 +1307,14 @@ gen_msubui64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, gen_msubu64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); } -static inline void gen_addi_d(TCGv ret, TCGv r1, int32_t r2) +static void gen_addi_d(TCGv ret, TCGv r1, int32_t r2) { TCGv temp = tcg_constant_i32(r2); gen_add_d(ret, r1, temp); } /* calculate the carry bit too */ -static inline void gen_add_CC(TCGv ret, TCGv r1, TCGv r2) +static void gen_add_CC(TCGv ret, TCGv r1, TCGv r2) { TCGv t0 = tcg_temp_new_i32(); TCGv result = tcg_temp_new_i32(); @@ -1338,13 +1337,13 @@ static inline void gen_add_CC(TCGv ret, TCGv r1, TCGv r2) tcg_gen_mov_i32(ret, result); } -static inline void gen_addi_CC(TCGv ret, TCGv r1, int32_t con) +static void gen_addi_CC(TCGv ret, TCGv r1, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_add_CC(ret, r1, temp); } -static inline void gen_addc_CC(TCGv ret, TCGv r1, TCGv r2) +static void gen_addc_CC(TCGv ret, TCGv r1, TCGv r2) { TCGv t0 = tcg_temp_new_i32(); TCGv result = tcg_temp_new_i32(); @@ -1366,14 +1365,13 @@ static inline void gen_addc_CC(TCGv ret, TCGv r1, TCGv r2) tcg_gen_mov_i32(ret, result); } -static inline void gen_addci_CC(TCGv ret, TCGv r1, int32_t con) +static void gen_addci_CC(TCGv ret, TCGv r1, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_addc_CC(ret, r1, temp); } -static inline void gen_cond_add(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, - TCGv r4) +static void gen_cond_add(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, TCGv r4) { TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); @@ -1405,14 +1403,13 @@ static inline void gen_cond_add(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, tcg_gen_movcond_i32(cond, r3, r4, t0, result, r1); } -static inline void gen_condi_add(TCGCond cond, TCGv r1, int32_t r2, - TCGv r3, TCGv r4) +static void gen_condi_add(TCGCond cond, TCGv r1, int32_t r2, TCGv r3, TCGv r4) { TCGv temp = tcg_constant_i32(r2); gen_cond_add(cond, r1, temp, r3, r4); } -static inline void gen_sub_d(TCGv ret, TCGv r1, TCGv r2) +static void gen_sub_d(TCGv ret, TCGv r1, TCGv r2) { TCGv temp = tcg_temp_new_i32(); TCGv result = tcg_temp_new_i32(); @@ -1433,7 +1430,7 @@ static inline void gen_sub_d(TCGv ret, TCGv r1, TCGv r2) tcg_gen_mov_i32(ret, result); } -static inline void +static void gen_sub64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) { TCGv temp = tcg_temp_new(); @@ -1459,7 +1456,7 @@ gen_sub64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) tcg_gen_mov_i64(ret, result); } -static inline void gen_sub_CC(TCGv ret, TCGv r1, TCGv r2) +static void gen_sub_CC(TCGv ret, TCGv r1, TCGv r2) { TCGv result = tcg_temp_new(); TCGv temp = tcg_temp_new(); @@ -1482,15 +1479,14 @@ static inline void gen_sub_CC(TCGv ret, TCGv r1, TCGv r2) tcg_gen_mov_i32(ret, result); } -static inline void gen_subc_CC(TCGv ret, TCGv r1, TCGv r2) +static void gen_subc_CC(TCGv ret, TCGv r1, TCGv r2) { TCGv temp = tcg_temp_new(); tcg_gen_not_i32(temp, r2); gen_addc_CC(ret, r1, temp); } -static inline void gen_cond_sub(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, - TCGv r4) +static void gen_cond_sub(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, TCGv r4) { TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); @@ -1522,7 +1518,7 @@ static inline void gen_cond_sub(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, tcg_gen_movcond_i32(cond, r3, r4, t0, result, r1); } -static inline void +static void gen_msub_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -1549,7 +1545,7 @@ gen_msub_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_sub_tl, tcg_gen_sub_tl); } -static inline void +static void gen_msubs_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -1584,7 +1580,7 @@ gen_msubs_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_or_i32(cpu_PSW_AV, cpu_PSW_AV, temp3); } -static inline void +static void gen_msubm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -1612,7 +1608,7 @@ gen_msubm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64_3); } -static inline void +static void gen_msubms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -1638,7 +1634,7 @@ gen_msubms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); } -static inline void +static void gen_msubr64_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -1661,7 +1657,7 @@ gen_msubr64_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, gen_helper_subr_h(ret, tcg_env, temp64, r1_low, r1_high); } -static inline void +static void gen_msubr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { TCGv temp = tcg_temp_new(); @@ -1672,7 +1668,7 @@ gen_msubr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) gen_msubr64_h(ret, temp, temp2, r2, r3, n, mode); } -static inline void +static void gen_msubr64s_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -1695,7 +1691,7 @@ gen_msubr64s_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, gen_helper_subr_h_ssov(ret, tcg_env, temp64, r1_low, r1_high); } -static inline void +static void gen_msubr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { TCGv temp = tcg_temp_new(); @@ -1706,21 +1702,21 @@ gen_msubr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) gen_msubr64s_h(ret, temp, temp2, r2, r3, n, mode); } -static inline void +static void gen_msubr_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) { TCGv temp = tcg_constant_i32(n); gen_helper_msubr_q(ret, tcg_env, r1, r2, r3, temp); } -static inline void +static void gen_msubrs_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) { TCGv temp = tcg_constant_i32(n); gen_helper_msubr_q_ssov(ret, tcg_env, r1, r2, r3, temp); } -static inline void +static void gen_msub32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, uint32_t up_shift) { @@ -1761,7 +1757,7 @@ gen_msub32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, tcg_gen_mov_i32(ret, temp3); } -static inline void +static void gen_m16sub32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) { TCGv temp = tcg_temp_new(); @@ -1778,7 +1774,7 @@ gen_m16sub32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) gen_sub_d(ret, arg1, temp); } -static inline void +static void gen_m16subs32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) { TCGv temp = tcg_temp_new(); @@ -1795,7 +1791,7 @@ gen_m16subs32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) gen_subs(ret, arg1, temp); } -static inline void +static void gen_m16sub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, TCGv arg3, uint32_t n) { @@ -1822,7 +1818,7 @@ gen_m16sub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_extr_i64_i32(rl, rh, t3); } -static inline void +static void gen_m16subs64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, TCGv arg3, uint32_t n) { @@ -1848,7 +1844,7 @@ gen_m16subs64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_extr_i64_i32(rl, rh, t1); } -static inline void +static void gen_msub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, TCGv arg3, uint32_t n) { @@ -1896,7 +1892,7 @@ gen_msub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static inline void +static void gen_msubs32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, uint32_t up_shift) { @@ -1919,7 +1915,7 @@ gen_msubs32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, gen_helper_msub32_q_sub_ssov(ret, tcg_env, t1, t3); } -static inline void +static void gen_msubs64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, TCGv arg3, uint32_t n) { @@ -1931,7 +1927,7 @@ gen_msubs64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_extr_i64_i32(rl, rh, r1); } -static inline void +static void gen_msubad_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -1958,7 +1954,7 @@ gen_msubad_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_add_tl, tcg_gen_sub_tl); } -static inline void +static void gen_msubadm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -1991,7 +1987,7 @@ gen_msubadm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64_2); } -static inline void +static void gen_msubadr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { TCGv t_n = tcg_constant_i32(n); @@ -2017,7 +2013,7 @@ gen_msubadr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) gen_helper_subadr_h(ret, tcg_env, temp64, temp, temp2); } -static inline void +static void gen_msubads_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -2052,7 +2048,7 @@ gen_msubads_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_or_i32(cpu_PSW_AV, cpu_PSW_AV, temp3); } -static inline void +static void gen_msubadms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { @@ -2084,7 +2080,7 @@ gen_msubadms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); } -static inline void +static void gen_msubadr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { TCGv t_n = tcg_constant_i32(n); @@ -2110,7 +2106,7 @@ gen_msubadr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) gen_helper_subadr_h_ssov(ret, tcg_env, temp64, temp, temp2); } -static inline void gen_abs(TCGv ret, TCGv r1) +static void gen_abs(TCGv ret, TCGv r1) { tcg_gen_abs_i32(ret, r1); /* overflow can only happen, if r1 = 0x80000000 */ @@ -2125,7 +2121,7 @@ static inline void gen_abs(TCGv ret, TCGv r1) tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static inline void gen_absdif(TCGv ret, TCGv r1, TCGv r2) +static void gen_absdif(TCGv ret, TCGv r1, TCGv r2) { TCGv temp = tcg_temp_new_i32(); TCGv result = tcg_temp_new_i32(); @@ -2151,19 +2147,19 @@ static inline void gen_absdif(TCGv ret, TCGv r1, TCGv r2) tcg_gen_mov_i32(ret, result); } -static inline void gen_absdifi(TCGv ret, TCGv r1, int32_t con) +static void gen_absdifi(TCGv ret, TCGv r1, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_absdif(ret, r1, temp); } -static inline void gen_absdifsi(TCGv ret, TCGv r1, int32_t con) +static void gen_absdifsi(TCGv ret, TCGv r1, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_helper_absdif_ssov(ret, tcg_env, r1, temp); } -static inline void gen_mul_i32s(TCGv ret, TCGv r1, TCGv r2) +static void gen_mul_i32s(TCGv ret, TCGv r1, TCGv r2) { TCGv high = tcg_temp_new(); TCGv low = tcg_temp_new(); @@ -2183,13 +2179,13 @@ static inline void gen_mul_i32s(TCGv ret, TCGv r1, TCGv r2) tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static inline void gen_muli_i32s(TCGv ret, TCGv r1, int32_t con) +static void gen_muli_i32s(TCGv ret, TCGv r1, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_mul_i32s(ret, r1, temp); } -static inline void gen_mul_i64s(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) +static void gen_mul_i64s(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) { tcg_gen_muls2_i32(ret_low, ret_high, r1, r2); /* clear V bit */ @@ -2203,14 +2199,14 @@ static inline void gen_mul_i64s(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static inline void gen_muli_i64s(TCGv ret_low, TCGv ret_high, TCGv r1, +static void gen_muli_i64s(TCGv ret_low, TCGv ret_high, TCGv r1, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_mul_i64s(ret_low, ret_high, r1, temp); } -static inline void gen_mul_i64u(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) +static void gen_mul_i64u(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) { tcg_gen_mulu2_i32(ret_low, ret_high, r1, r2); /* clear V bit */ @@ -2224,33 +2220,33 @@ static inline void gen_mul_i64u(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static inline void gen_muli_i64u(TCGv ret_low, TCGv ret_high, TCGv r1, +static void gen_muli_i64u(TCGv ret_low, TCGv ret_high, TCGv r1, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_mul_i64u(ret_low, ret_high, r1, temp); } -static inline void gen_mulsi_i32(TCGv ret, TCGv r1, int32_t con) +static void gen_mulsi_i32(TCGv ret, TCGv r1, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_helper_mul_ssov(ret, tcg_env, r1, temp); } -static inline void gen_mulsui_i32(TCGv ret, TCGv r1, int32_t con) +static void gen_mulsui_i32(TCGv ret, TCGv r1, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_helper_mul_suov(ret, tcg_env, r1, temp); } /* gen_maddsi_32(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); */ -static inline void gen_maddsi_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) +static void gen_maddsi_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_helper_madd32_ssov(ret, tcg_env, r1, r2, temp); } -static inline void gen_maddsui_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) +static void gen_maddsui_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_helper_madd32_suov(ret, tcg_env, r1, r2, temp); @@ -2361,7 +2357,7 @@ static void gen_mulr_q(TCGv ret, TCGv arg1, TCGv arg2, uint32_t n) tcg_gen_andi_i32(ret, ret, 0xffff0000); } -static inline void +static void gen_madds_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, TCGv r3) { @@ -2371,7 +2367,7 @@ gen_madds_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); } -static inline void +static void gen_maddsi_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, int32_t con) { @@ -2379,7 +2375,7 @@ gen_maddsi_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, gen_madds_64(ret_low, ret_high, r1, r2_low, r2_high, temp); } -static inline void +static void gen_maddsu_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, TCGv r3) { @@ -2389,7 +2385,7 @@ gen_maddsu_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); } -static inline void +static void gen_maddsui_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, int32_t con) { @@ -2397,19 +2393,19 @@ gen_maddsui_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, gen_maddsu_64(ret_low, ret_high, r1, r2_low, r2_high, temp); } -static inline void gen_msubsi_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) +static void gen_msubsi_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_helper_msub32_ssov(ret, tcg_env, r1, r2, temp); } -static inline void gen_msubsui_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) +static void gen_msubsui_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_helper_msub32_suov(ret, tcg_env, r1, r2, temp); } -static inline void +static void gen_msubs_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, TCGv r3) { @@ -2419,7 +2415,7 @@ gen_msubs_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); } -static inline void +static void gen_msubsi_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, int32_t con) { @@ -2427,7 +2423,7 @@ gen_msubsi_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, gen_msubs_64(ret_low, ret_high, r1, r2_low, r2_high, temp); } -static inline void +static void gen_msubsu_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, TCGv r3) { @@ -2437,7 +2433,7 @@ gen_msubsu_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); } -static inline void +static void gen_msubsui_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, int32_t con) { @@ -2589,37 +2585,37 @@ static void gen_sh_condi(int cond, TCGv ret, TCGv r1, int32_t con) gen_sh_cond(cond, ret, r1, temp); } -static inline void gen_adds(TCGv ret, TCGv r1, TCGv r2) +static void gen_adds(TCGv ret, TCGv r1, TCGv r2) { gen_helper_add_ssov(ret, tcg_env, r1, r2); } -static inline void gen_addsi(TCGv ret, TCGv r1, int32_t con) +static void gen_addsi(TCGv ret, TCGv r1, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_helper_add_ssov(ret, tcg_env, r1, temp); } -static inline void gen_addsui(TCGv ret, TCGv r1, int32_t con) +static void gen_addsui(TCGv ret, TCGv r1, int32_t con) { TCGv temp = tcg_constant_i32(con); gen_helper_add_suov(ret, tcg_env, r1, temp); } -static inline void gen_subs(TCGv ret, TCGv r1, TCGv r2) +static void gen_subs(TCGv ret, TCGv r1, TCGv r2) { gen_helper_sub_ssov(ret, tcg_env, r1, r2); } -static inline void gen_subsu(TCGv ret, TCGv r1, TCGv r2) +static void gen_subsu(TCGv ret, TCGv r1, TCGv r2) { gen_helper_sub_suov(ret, tcg_env, r1, r2); } -static inline void gen_bit_2op(TCGv ret, TCGv r1, TCGv r2, - int pos1, int pos2, - void(*op1)(TCGv, TCGv, TCGv), - void(*op2)(TCGv, TCGv, TCGv)) +static void gen_bit_2op(TCGv ret, TCGv r1, TCGv r2, + int pos1, int pos2, + void(*op1)(TCGv, TCGv, TCGv), + void(*op2)(TCGv, TCGv, TCGv)) { TCGv temp1, temp2; @@ -2636,9 +2632,9 @@ static inline void gen_bit_2op(TCGv ret, TCGv r1, TCGv r2, } /* ret = r1[pos1] op1 r2[pos2]; */ -static inline void gen_bit_1op(TCGv ret, TCGv r1, TCGv r2, - int pos1, int pos2, - void(*op1)(TCGv, TCGv, TCGv)) +static void gen_bit_1op(TCGv ret, TCGv r1, TCGv r2, + int pos1, int pos2, + void(*op1)(TCGv, TCGv, TCGv)) { TCGv temp1, temp2; @@ -2653,8 +2649,8 @@ static inline void gen_bit_1op(TCGv ret, TCGv r1, TCGv r2, tcg_gen_andi_i32(ret, ret, 0x1); } -static inline void gen_accumulating_cond(int cond, TCGv ret, TCGv r1, TCGv r2, - void(*op)(TCGv, TCGv, TCGv)) +static void gen_accumulating_cond(int cond, TCGv ret, TCGv r1, TCGv r2, + void(*op)(TCGv, TCGv, TCGv)) { TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); @@ -2668,7 +2664,7 @@ static inline void gen_accumulating_cond(int cond, TCGv ret, TCGv r1, TCGv r2, tcg_gen_deposit_i32(ret, ret, temp, 0, 1); } -static inline void +static void gen_accumulating_condi(int cond, TCGv ret, TCGv r1, int32_t con, void(*op)(TCGv, TCGv, TCGv)) { @@ -2676,7 +2672,7 @@ gen_accumulating_condi(int cond, TCGv ret, TCGv r1, int32_t con, gen_accumulating_cond(cond, ret, r1, temp, op); } -static inline void gen_eqany_bi(TCGv ret, TCGv r1, int32_t con) +static void gen_eqany_bi(TCGv ret, TCGv r1, int32_t con) { TCGv b0 = tcg_temp_new(); TCGv b1 = tcg_temp_new(); @@ -2705,7 +2701,7 @@ static inline void gen_eqany_bi(TCGv ret, TCGv r1, int32_t con) tcg_gen_or_i32(ret, ret, b3); } -static inline void gen_eqany_hi(TCGv ret, TCGv r1, int32_t con) +static void gen_eqany_hi(TCGv ret, TCGv r1, int32_t con) { TCGv h0 = tcg_temp_new(); TCGv h1 = tcg_temp_new(); @@ -2724,7 +2720,7 @@ static inline void gen_eqany_hi(TCGv ret, TCGv r1, int32_t con) /* mask = ((1 << width) -1) << pos; ret = (r1 & ~mask) | (r2 << pos) & mask); */ -static inline void gen_insert(TCGv ret, TCGv r1, TCGv r2, TCGv width, TCGv pos) +static void gen_insert(TCGv ret, TCGv r1, TCGv r2, TCGv width, TCGv pos) { TCGv mask = tcg_temp_new(); TCGv temp = tcg_temp_new(); @@ -2740,7 +2736,7 @@ static inline void gen_insert(TCGv ret, TCGv r1, TCGv r2, TCGv width, TCGv pos) tcg_gen_or_i32(ret, temp, temp2); } -static inline void gen_bsplit(TCGv rl, TCGv rh, TCGv r1) +static void gen_bsplit(TCGv rl, TCGv rh, TCGv r1) { TCGv_i64 temp = tcg_temp_new_i64(); @@ -2748,7 +2744,7 @@ static inline void gen_bsplit(TCGv rl, TCGv rh, TCGv r1) tcg_gen_extr_i64_i32(rl, rh, temp); } -static inline void gen_unpack(TCGv rl, TCGv rh, TCGv r1) +static void gen_unpack(TCGv rl, TCGv rh, TCGv r1) { TCGv_i64 temp = tcg_temp_new_i64(); @@ -2756,7 +2752,7 @@ static inline void gen_unpack(TCGv rl, TCGv rh, TCGv r1) tcg_gen_extr_i64_i32(rl, rh, temp); } -static inline void +static void gen_dvinit_b(DisasContext *ctx, TCGv rl, TCGv rh, TCGv r1, TCGv r2) { TCGv_i64 ret = tcg_temp_new_i64(); @@ -2769,7 +2765,7 @@ gen_dvinit_b(DisasContext *ctx, TCGv rl, TCGv rh, TCGv r1, TCGv r2) tcg_gen_extr_i64_i32(rl, rh, ret); } -static inline void +static void gen_dvinit_h(DisasContext *ctx, TCGv rl, TCGv rh, TCGv r1, TCGv r2) { TCGv_i64 ret = tcg_temp_new_i64(); @@ -2812,7 +2808,7 @@ static void gen_calc_usb_mulr_h(TCGv arg) /* helpers for generating program flow micro-ops */ -static inline void gen_save_pc(vaddr pc) +static void gen_save_pc(vaddr pc) { tcg_gen_movi_i32(cpu_PC, pc); } @@ -2840,8 +2836,8 @@ static void generate_trap(DisasContext *ctx, int class, int tin) ctx->base.is_jmp = DISAS_NORETURN; } -static inline void gen_branch_cond(DisasContext *ctx, TCGCond cond, TCGv r1, - TCGv r2, int16_t address) +static void gen_branch_cond(DisasContext *ctx, TCGCond cond, TCGv r1, + TCGv r2, int16_t address) { TCGLabel *jumpLabel = gen_new_label(); tcg_gen_brcond_i32(cond, r1, r2, jumpLabel); @@ -2852,8 +2848,8 @@ static inline void gen_branch_cond(DisasContext *ctx, TCGCond cond, TCGv r1, gen_goto_tb(ctx, 0, ctx->base.pc_next + address * 2); } -static inline void gen_branch_condi(DisasContext *ctx, TCGCond cond, TCGv r1, - int r2, int16_t address) +static void gen_branch_condi(DisasContext *ctx, TCGCond cond, TCGv r1, + int r2, int16_t address) { TCGv temp = tcg_constant_i32(r2); gen_branch_cond(ctx, cond, r1, temp, address); From cd08bcaa36b4c0437b2d61f9906bf78b99aa3684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Oct 2025 05:59:37 +0200 Subject: [PATCH 1668/1794] target/tricore: Expand TCGv type for 32-bit target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TriCore target is only built as 32-bit: $ git grep TARGET_LONG_BITS configs/targets/tricore-* configs/targets/tricore-softmmu.mak:2:TARGET_LONG_BITS=32 Replace: TCGv -> TCGv_i32 tcg_temp_new -> tcg_temp_new_i32 This is a mechanical replacement, adapting style to pass the checkpatch.pl script. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20251010052141.42460-13-philmd@linaro.org> --- target/tricore/translate.c | 1248 +++++++++++++++++++----------------- 1 file changed, 642 insertions(+), 606 deletions(-) diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 787c77292fd19..fbe05a93a8a26 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -136,9 +136,9 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) } while (0) #define GEN_HELPER_LL(name, ret, arg0, arg1, n) do { \ - TCGv arg00 = tcg_temp_new(); \ - TCGv arg01 = tcg_temp_new(); \ - TCGv arg11 = tcg_temp_new(); \ + TCGv_i32 arg00 = tcg_temp_new_i32(); \ + TCGv_i32 arg01 = tcg_temp_new_i32(); \ + TCGv_i32 arg11 = tcg_temp_new_i32(); \ tcg_gen_sari_i32(arg00, arg0, 16); \ tcg_gen_ext16s_i32(arg01, arg0); \ tcg_gen_ext16s_i32(arg11, arg1); \ @@ -146,10 +146,10 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) } while (0) #define GEN_HELPER_LU(name, ret, arg0, arg1, n) do { \ - TCGv arg00 = tcg_temp_new(); \ - TCGv arg01 = tcg_temp_new(); \ - TCGv arg10 = tcg_temp_new(); \ - TCGv arg11 = tcg_temp_new(); \ + TCGv_i32 arg00 = tcg_temp_new_i32(); \ + TCGv_i32 arg01 = tcg_temp_new_i32(); \ + TCGv_i32 arg10 = tcg_temp_new_i32(); \ + TCGv_i32 arg11 = tcg_temp_new_i32(); \ tcg_gen_sari_i32(arg00, arg0, 16); \ tcg_gen_ext16s_i32(arg01, arg0); \ tcg_gen_sari_i32(arg11, arg1, 16); \ @@ -158,10 +158,10 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) } while (0) #define GEN_HELPER_UL(name, ret, arg0, arg1, n) do { \ - TCGv arg00 = tcg_temp_new(); \ - TCGv arg01 = tcg_temp_new(); \ - TCGv arg10 = tcg_temp_new(); \ - TCGv arg11 = tcg_temp_new(); \ + TCGv_i32 arg00 = tcg_temp_new_i32(); \ + TCGv_i32 arg01 = tcg_temp_new_i32(); \ + TCGv_i32 arg10 = tcg_temp_new_i32(); \ + TCGv_i32 arg11 = tcg_temp_new_i32(); \ tcg_gen_sari_i32(arg00, arg0, 16); \ tcg_gen_ext16s_i32(arg01, arg0); \ tcg_gen_sari_i32(arg10, arg1, 16); \ @@ -170,9 +170,9 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) } while (0) #define GEN_HELPER_UU(name, ret, arg0, arg1, n) do { \ - TCGv arg00 = tcg_temp_new(); \ - TCGv arg01 = tcg_temp_new(); \ - TCGv arg11 = tcg_temp_new(); \ + TCGv_i32 arg00 = tcg_temp_new_i32(); \ + TCGv_i32 arg01 = tcg_temp_new_i32(); \ + TCGv_i32 arg11 = tcg_temp_new_i32(); \ tcg_gen_sari_i32(arg01, arg0, 16); \ tcg_gen_ext16s_i32(arg00, arg0); \ tcg_gen_sari_i32(arg11, arg1, 16); \ @@ -210,23 +210,24 @@ static void generate_trap(DisasContext *ctx, int class, int tin); /* Functions for load/save to/from memory */ -static void gen_offset_ld(DisasContext *ctx, TCGv r1, TCGv r2, +static void gen_offset_ld(DisasContext *ctx, TCGv_i32 r1, TCGv_i32 r2, int16_t con, MemOp mop) { - TCGv temp = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); tcg_gen_addi_i32(temp, r2, con); tcg_gen_qemu_ld_i32(r1, temp, ctx->mem_idx, mop); } -static void gen_offset_st(DisasContext *ctx, TCGv r1, TCGv r2, +static void gen_offset_st(DisasContext *ctx, TCGv_i32 r1, TCGv_i32 r2, int16_t con, MemOp mop) { - TCGv temp = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); tcg_gen_addi_i32(temp, r2, con); tcg_gen_qemu_st_i32(r1, temp, ctx->mem_idx, mop); } -static void gen_st_2regs_64(DisasContext *ctx, TCGv rh, TCGv rl, TCGv address) +static void gen_st_2regs_64(DisasContext *ctx, TCGv_i32 rh, TCGv_i32 rl, + TCGv_i32 address) { TCGv_i64 temp = tcg_temp_new_i64(); @@ -235,14 +236,16 @@ static void gen_st_2regs_64(DisasContext *ctx, TCGv rh, TCGv rl, TCGv address) } static void gen_offset_st_2regs(DisasContext *ctx, - TCGv rh, TCGv rl, TCGv base, int16_t con) + TCGv_i32 rh, TCGv_i32 rl, + TCGv_i32 base, int16_t con) { - TCGv temp = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); tcg_gen_addi_i32(temp, base, con); gen_st_2regs_64(ctx, rh, rl, temp); } -static void gen_ld_2regs_64(DisasContext *ctx, TCGv rh, TCGv rl, TCGv address) +static void gen_ld_2regs_64(DisasContext *ctx, TCGv_i32 rh, TCGv_i32 rl, + TCGv_i32 address) { TCGv_i64 temp = tcg_temp_new_i64(); @@ -252,36 +255,37 @@ static void gen_ld_2regs_64(DisasContext *ctx, TCGv rh, TCGv rl, TCGv address) } static void gen_offset_ld_2regs(DisasContext *ctx, - TCGv rh, TCGv rl, TCGv base, int16_t con) + TCGv_i32 rh, TCGv_i32 rl, + TCGv_i32 base, int16_t con) { - TCGv temp = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); tcg_gen_addi_i32(temp, base, con); gen_ld_2regs_64(ctx, rh, rl, temp); } -static void gen_st_preincr(DisasContext *ctx, TCGv r1, TCGv r2, int16_t off, - MemOp mop) +static void gen_st_preincr(DisasContext *ctx, TCGv_i32 r1, TCGv_i32 r2, + int16_t off, MemOp mop) { - TCGv temp = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); tcg_gen_addi_i32(temp, r2, off); tcg_gen_qemu_st_i32(r1, temp, ctx->mem_idx, mop); tcg_gen_mov_i32(r2, temp); } -static void gen_ld_preincr(DisasContext *ctx, TCGv r1, TCGv r2, int16_t off, - MemOp mop) +static void gen_ld_preincr(DisasContext *ctx, TCGv_i32 r1, TCGv_i32 r2, + int16_t off, MemOp mop) { - TCGv temp = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); tcg_gen_addi_i32(temp, r2, off); tcg_gen_qemu_ld_i32(r1, temp, ctx->mem_idx, mop); tcg_gen_mov_i32(r2, temp); } /* M(EA, word) = (M(EA, word) & ~E[a][63:32]) | (E[a][31:0] & E[a][63:32]); */ -static void gen_ldmst(DisasContext *ctx, int ereg, TCGv ea) +static void gen_ldmst(DisasContext *ctx, int ereg, TCGv_i32 ea) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); CHECK_REG_PAIR(ereg); /* temp = (M(EA, word) */ @@ -299,19 +303,19 @@ static void gen_ldmst(DisasContext *ctx, int ereg, TCGv ea) /* tmp = M(EA, word); M(EA, word) = D[a]; D[a] = tmp[31:0];*/ -static void gen_swap(DisasContext *ctx, int reg, TCGv ea) +static void gen_swap(DisasContext *ctx, int reg, TCGv_i32 ea) { - TCGv temp = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); tcg_gen_qemu_ld_i32(temp, ea, ctx->mem_idx, MO_LEUL); tcg_gen_qemu_st_i32(cpu_gpr_d[reg], ea, ctx->mem_idx, MO_LEUL); tcg_gen_mov_i32(cpu_gpr_d[reg], temp); } -static void gen_cmpswap(DisasContext *ctx, int reg, TCGv ea) +static void gen_cmpswap(DisasContext *ctx, int reg, TCGv_i32 ea) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); CHECK_REG_PAIR(reg); tcg_gen_qemu_ld_i32(temp, ea, ctx->mem_idx, MO_LEUL); tcg_gen_movcond_i32(TCG_COND_EQ, temp2, cpu_gpr_d[reg + 1], temp, @@ -320,11 +324,11 @@ static void gen_cmpswap(DisasContext *ctx, int reg, TCGv ea) tcg_gen_mov_i32(cpu_gpr_d[reg], temp); } -static void gen_swapmsk(DisasContext *ctx, int reg, TCGv ea) +static void gen_swapmsk(DisasContext *ctx, int reg, TCGv_i32 ea) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); - TCGv temp3 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); + TCGv_i32 temp3 = tcg_temp_new_i32(); CHECK_REG_PAIR(reg); tcg_gen_qemu_ld_i32(temp, ea, ctx->mem_idx, MO_LEUL); tcg_gen_and_i32(temp2, cpu_gpr_d[reg], cpu_gpr_d[reg + 1]); @@ -346,7 +350,7 @@ static void gen_swapmsk(DisasContext *ctx, int reg, TCGv ea) break; #define A(ADDRESS, REG, FEATURE) R(ADDRESS, REG, FEATURE) #define E(ADDRESS, REG, FEATURE) R(ADDRESS, REG, FEATURE) -static void gen_mfcr(DisasContext *ctx, TCGv ret, int32_t offset) +static void gen_mfcr(DisasContext *ctx, TCGv_i32 ret, int32_t offset) { /* since we're caching PSW make this a special case */ if (offset == 0xfe04) { @@ -374,7 +378,7 @@ static void gen_mfcr(DisasContext *ctx, TCGv ret, int32_t offset) watchdog device, we handle endinit protected registers like all-access registers for now. */ #define E(ADDRESS, REG, FEATURE) A(ADDRESS, REG, FEATURE) -static void gen_mtcr(DisasContext *ctx, TCGv r1, int32_t offset) +static void gen_mtcr(DisasContext *ctx, TCGv_i32 r1, int32_t offset) { if (ctx->priv == TRICORE_PRIV_SM) { /* since we're caching PSW make this a special case */ @@ -393,10 +397,10 @@ static void gen_mtcr(DisasContext *ctx, TCGv r1, int32_t offset) /* Functions for arithmetic instructions */ -static void gen_add_d(TCGv ret, TCGv r1, TCGv r2) +static void gen_add_d(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2) { - TCGv t0 = tcg_temp_new_i32(); - TCGv result = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 result = tcg_temp_new_i32(); /* Addition and set V/SV bits */ tcg_gen_add_i32(result, r1, r2); /* calc V bit */ @@ -414,10 +418,9 @@ static void gen_add_d(TCGv ret, TCGv r1, TCGv r2) tcg_gen_mov_i32(ret, result); } -static void -gen_add64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) +static void gen_add64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) { - TCGv temp = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 result = tcg_temp_new_i64(); @@ -440,15 +443,16 @@ gen_add64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) tcg_gen_mov_i64(ret, result); } -static void -gen_addsub64_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, void(*op1)(TCGv, TCGv, TCGv), - void(*op2)(TCGv, TCGv, TCGv)) +static void gen_addsub64_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + void(*op1)(TCGv_i32, TCGv_i32, TCGv_i32), + void(*op2)(TCGv_i32, TCGv_i32, TCGv_i32)) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); - TCGv temp3 = tcg_temp_new(); - TCGv temp4 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); + TCGv_i32 temp3 = tcg_temp_new_i32(); + TCGv_i32 temp4 = tcg_temp_new_i32(); (*op1)(temp, r1_low, r2); /* calc V0 bit */ @@ -487,7 +491,7 @@ gen_addsub64_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, } /* ret = r2 + (r1 * r3); */ -static void gen_madd32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) +static void gen_madd32_d(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 r3) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); @@ -518,20 +522,19 @@ static void gen_madd32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static void gen_maddi32_d(TCGv ret, TCGv r1, TCGv r2, int32_t con) +static void gen_maddi32_d(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_madd32_d(ret, r1, r2, temp); } -static void -gen_madd64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, - TCGv r3) +static void gen_madd64_d(TCGv_i32 ret_low, TCGv_i32 ret_high, TCGv_i32 r1, + TCGv_i32 r2_low, TCGv_i32 r2_high, TCGv_i32 r3) { - TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_temp_new(); - TCGv t3 = tcg_temp_new(); - TCGv t4 = tcg_temp_new(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + TCGv_i32 t4 = tcg_temp_new_i32(); tcg_gen_muls2_i32(t1, t2, r1, r3); /* only the add can overflow */ @@ -552,9 +555,8 @@ gen_madd64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_mov_i32(ret_high, t4); } -static void -gen_maddu64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, - TCGv r3) +static void gen_maddu64_d(TCGv_i32 ret_low, TCGv_i32 ret_high, TCGv_i32 r1, + TCGv_i32 r2_low, TCGv_i32 r2_high, TCGv_i32 r3) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); @@ -582,29 +584,28 @@ gen_maddu64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static void -gen_maddi64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, - int32_t con) +static void gen_maddi64_d(TCGv_i32 ret_low, TCGv_i32 ret_high, TCGv_i32 r1, + TCGv_i32 r2_low, TCGv_i32 r2_high, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_madd64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); } -static void -gen_maddui64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, - int32_t con) +static void gen_maddui64_d(TCGv_i32 ret_low, TCGv_i32 ret_high, TCGv_i32 r1, + TCGv_i32 r2_low, TCGv_i32 r2_high, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_maddu64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); } -static void -gen_madd_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, uint32_t n, uint32_t mode) +static void gen_madd_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 t_n = tcg_constant_i32(n); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: @@ -625,13 +626,14 @@ gen_madd_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_add_tl, tcg_gen_add_tl); } -static void -gen_maddsu_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, uint32_t n, uint32_t mode) +static void gen_maddsu_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 t_n = tcg_constant_i32(n); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: @@ -652,11 +654,12 @@ gen_maddsu_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_sub_tl, tcg_gen_add_tl); } -static void -gen_maddsum_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, uint32_t n, uint32_t mode) +static void gen_maddsum_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); + TCGv_i32 t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); TCGv_i64 temp64_2 = tcg_temp_new_i64(); TCGv_i64 temp64_3 = tcg_temp_new_i64(); @@ -685,16 +688,17 @@ gen_maddsum_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64_2); } -static void gen_adds(TCGv ret, TCGv r1, TCGv r2); +static void gen_adds(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2); -static void -gen_madds_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, uint32_t n, uint32_t mode) +static void gen_madds_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); - TCGv temp3 = tcg_temp_new(); + TCGv_i32 t_n = tcg_constant_i32(n); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); + TCGv_i32 temp3 = tcg_temp_new_i32(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { @@ -722,16 +726,17 @@ gen_madds_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_or_i32(cpu_PSW_AV, cpu_PSW_AV, temp3); } -static void gen_subs(TCGv ret, TCGv r1, TCGv r2); +static void gen_subs(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2); -static void -gen_maddsus_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, uint32_t n, uint32_t mode) +static void gen_maddsus_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); - TCGv temp3 = tcg_temp_new(); + TCGv_i32 t_n = tcg_constant_i32(n); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); + TCGv_i32 temp3 = tcg_temp_new_i32(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { @@ -759,11 +764,12 @@ gen_maddsus_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_or_i32(cpu_PSW_AV, cpu_PSW_AV, temp3); } -static void -gen_maddsums_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, uint32_t n, uint32_t mode) +static void gen_maddsums_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); + TCGv_i32 t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); TCGv_i64 temp64_2 = tcg_temp_new_i64(); @@ -792,11 +798,12 @@ gen_maddsums_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, } -static void -gen_maddm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, uint32_t n, uint32_t mode) +static void gen_maddm_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); + TCGv_i32 t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); TCGv_i64 temp64_2 = tcg_temp_new_i64(); TCGv_i64 temp64_3 = tcg_temp_new_i64(); @@ -820,11 +827,12 @@ gen_maddm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64_3); } -static void -gen_maddms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, uint32_t n, uint32_t mode) +static void gen_maddms_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); + TCGv_i32 t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); TCGv_i64 temp64_2 = tcg_temp_new_i64(); switch (mode) { @@ -846,11 +854,11 @@ gen_maddms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); } -static void -gen_maddr64_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, - uint32_t mode) +static void gen_maddr64_h(TCGv_i32 ret, TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); + TCGv_i32 t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: @@ -869,23 +877,25 @@ gen_maddr64_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, gen_helper_addr_h(ret, tcg_env, temp64, r1_low, r1_high); } -static void -gen_maddr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) +static void gen_maddr32_h(TCGv_i32 ret, + TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); tcg_gen_andi_i32(temp2, r1, 0xffff0000); tcg_gen_shli_i32(temp, r1, 16); gen_maddr64_h(ret, temp, temp2, r2, r3, n, mode); } -static void -gen_maddsur32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) +static void gen_maddsur32_h(TCGv_i32 ret, + TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 t_n = tcg_constant_i32(n); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: @@ -907,11 +917,11 @@ gen_maddsur32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) } -static void -gen_maddr64s_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, - uint32_t n, uint32_t mode) +static void gen_maddr64s_h(TCGv_i32 ret, TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); + TCGv_i32 t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: @@ -930,23 +940,25 @@ gen_maddr64s_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, gen_helper_addr_h_ssov(ret, tcg_env, temp64, r1_low, r1_high); } -static void -gen_maddr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) +static void gen_maddr32s_h(TCGv_i32 ret, + TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); tcg_gen_andi_i32(temp2, r1, 0xffff0000); tcg_gen_shli_i32(temp, r1, 16); gen_maddr64s_h(ret, temp, temp2, r2, r3, n, mode); } -static void -gen_maddsur32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) +static void gen_maddsur32s_h(TCGv_i32 ret, + TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 t_n = tcg_constant_i32(n); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: @@ -967,27 +979,27 @@ gen_maddsur32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) gen_helper_addsur_h_ssov(ret, tcg_env, temp64, temp, temp2); } -static void -gen_maddr_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) +static void gen_maddr_q(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 r3, + uint32_t n) { - TCGv t_n = tcg_constant_i32(n); + TCGv_i32 t_n = tcg_constant_i32(n); gen_helper_maddr_q(ret, tcg_env, r1, r2, r3, t_n); } -static void -gen_maddrs_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) +static void gen_maddrs_q(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 r3, + uint32_t n) { - TCGv t_n = tcg_constant_i32(n); + TCGv_i32 t_n = tcg_constant_i32(n); gen_helper_maddr_q_ssov(ret, tcg_env, r1, r2, r3, t_n); } -static void -gen_madd32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, - uint32_t up_shift) +static void gen_madd32_q(TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, + uint32_t n, uint32_t up_shift) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); - TCGv temp3 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); + TCGv_i32 temp3 = tcg_temp_new_i32(); TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); TCGv_i64 t3 = tcg_temp_new_i64(); @@ -1031,11 +1043,12 @@ gen_madd32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, tcg_gen_mov_i32(ret, temp3); } -static void -gen_m16add32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) +static void gen_m16add32_q(TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, + uint32_t n) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); if (n == 0) { tcg_gen_mul_i32(temp, arg2, arg3); } else { /* n is expected to be 1 */ @@ -1048,11 +1061,12 @@ gen_m16add32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) gen_add_d(ret, arg1, temp); } -static void -gen_m16adds32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) +static void gen_m16adds32_q(TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, + uint32_t n) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); if (n == 0) { tcg_gen_mul_i32(temp, arg2, arg3); } else { /* n is expected to be 1 */ @@ -1065,12 +1079,12 @@ gen_m16adds32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) gen_adds(ret, arg1, temp); } -static void -gen_m16add64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, - TCGv arg3, uint32_t n) +static void gen_m16add64_q(TCGv_i32 rl, TCGv_i32 rh, + TCGv_i32 arg1_low, TCGv_i32 arg1_high, + TCGv_i32 arg2, TCGv_i32 arg3, uint32_t n) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); TCGv_i64 t3 = tcg_temp_new_i64(); @@ -1092,12 +1106,12 @@ gen_m16add64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_extr_i64_i32(rl, rh, t3); } -static void -gen_m16adds64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, - TCGv arg3, uint32_t n) +static void gen_m16adds64_q(TCGv_i32 rl, TCGv_i32 rh, + TCGv_i32 arg1_low, TCGv_i32 arg1_high, + TCGv_i32 arg2, TCGv_i32 arg3, uint32_t n) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); @@ -1118,15 +1132,15 @@ gen_m16adds64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_extr_i64_i32(rl, rh, t1); } -static void -gen_madd64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, - TCGv arg3, uint32_t n) +static void gen_madd64_q(TCGv_i32 rl, TCGv_i32 rh, + TCGv_i32 arg1_low, TCGv_i32 arg1_high, + TCGv_i32 arg2, TCGv_i32 arg3, uint32_t n) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); TCGv_i64 t3 = tcg_temp_new_i64(); TCGv_i64 t4 = tcg_temp_new_i64(); - TCGv temp, temp2; + TCGv_i32 temp, temp2; tcg_gen_concat_i32_i64(t1, arg1_low, arg1_high); tcg_gen_ext_i32_i64(t2, arg2); @@ -1146,8 +1160,8 @@ gen_madd64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, (0x80000000 * 0x80000000) << 1). If this is the case, we negate the ovf. */ if (n == 1) { - temp = tcg_temp_new(); - temp2 = tcg_temp_new(); + temp = tcg_temp_new_i32(); + temp2 = tcg_temp_new_i32(); tcg_gen_setcondi_i32(TCG_COND_EQ, temp, arg2, 0x80000000); tcg_gen_setcond_i32(TCG_COND_EQ, temp2, arg2, arg3); tcg_gen_and_i32(temp, temp, temp2); @@ -1166,9 +1180,9 @@ gen_madd64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static void -gen_madds32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, - uint32_t up_shift) +static void gen_madds32_q(TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, + uint32_t n, uint32_t up_shift) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); @@ -1184,12 +1198,12 @@ gen_madds32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, gen_helper_madd32_q_add_ssov(ret, tcg_env, t1, t2); } -static void -gen_madds64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, - TCGv arg3, uint32_t n) +static void gen_madds64_q(TCGv_i32 rl, TCGv_i32 rh, + TCGv_i32 arg1_low, TCGv_i32 arg1_high, + TCGv_i32 arg2, TCGv_i32 arg3, uint32_t n) { TCGv_i64 r1 = tcg_temp_new_i64(); - TCGv t_n = tcg_constant_i32(n); + TCGv_i32 t_n = tcg_constant_i32(n); tcg_gen_concat_i32_i64(r1, arg1_low, arg1_high); gen_helper_madd64_q_ssov(r1, tcg_env, r1, arg2, arg3, t_n); @@ -1197,7 +1211,7 @@ gen_madds64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, } /* ret = r2 - (r1 * r3); */ -static void gen_msub32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) +static void gen_msub32_d(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 r3) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); @@ -1229,20 +1243,20 @@ static void gen_msub32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static void gen_msubi32_d(TCGv ret, TCGv r1, TCGv r2, int32_t con) +static void gen_msubi32_d(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_msub32_d(ret, r1, r2, temp); } -static void -gen_msub64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, - TCGv r3) +static void gen_msub64_d(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1, TCGv_i32 r2_low, TCGv_i32 r2_high, + TCGv_i32 r3) { - TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_temp_new(); - TCGv t3 = tcg_temp_new(); - TCGv t4 = tcg_temp_new(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + TCGv_i32 t4 = tcg_temp_new_i32(); tcg_gen_muls2_i32(t1, t2, r1, r3); /* only the sub can overflow */ @@ -1263,17 +1277,16 @@ gen_msub64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_mov_i32(ret_high, t4); } -static void -gen_msubi64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, - int32_t con) +static void gen_msubi64_d(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1, TCGv_i32 r2_low, TCGv_i32 r2_high, + int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_msub64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); } -static void -gen_msubu64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, - TCGv r3) +static void gen_msubu64_d(TCGv_i32 ret_low, TCGv_i32 ret_high, TCGv_i32 r1, + TCGv_i32 r2_low, TCGv_i32 r2_high, TCGv_i32 r3) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); @@ -1299,25 +1312,25 @@ gen_msubu64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static void -gen_msubui64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, - int32_t con) +static void gen_msubui64_d(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1, TCGv_i32 r2_low, TCGv_i32 r2_high, + int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_msubu64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); } -static void gen_addi_d(TCGv ret, TCGv r1, int32_t r2) +static void gen_addi_d(TCGv_i32 ret, TCGv_i32 r1, int32_t r2) { - TCGv temp = tcg_constant_i32(r2); + TCGv_i32 temp = tcg_constant_i32(r2); gen_add_d(ret, r1, temp); } /* calculate the carry bit too */ -static void gen_add_CC(TCGv ret, TCGv r1, TCGv r2) +static void gen_add_CC(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2) { - TCGv t0 = tcg_temp_new_i32(); - TCGv result = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 result = tcg_temp_new_i32(); tcg_gen_movi_i32(t0, 0); /* Addition and set C/V/SV bits */ @@ -1337,16 +1350,16 @@ static void gen_add_CC(TCGv ret, TCGv r1, TCGv r2) tcg_gen_mov_i32(ret, result); } -static void gen_addi_CC(TCGv ret, TCGv r1, int32_t con) +static void gen_addi_CC(TCGv_i32 ret, TCGv_i32 r1, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_add_CC(ret, r1, temp); } -static void gen_addc_CC(TCGv ret, TCGv r1, TCGv r2) +static void gen_addc_CC(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2) { - TCGv t0 = tcg_temp_new_i32(); - TCGv result = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 result = tcg_temp_new_i32(); /* Addition, carry and set C/V/SV bits */ tcg_gen_addcio_i32(result, cpu_PSW_C, r1, r2, cpu_PSW_C); @@ -1365,19 +1378,20 @@ static void gen_addc_CC(TCGv ret, TCGv r1, TCGv r2) tcg_gen_mov_i32(ret, result); } -static void gen_addci_CC(TCGv ret, TCGv r1, int32_t con) +static void gen_addci_CC(TCGv_i32 ret, TCGv_i32 r1, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_addc_CC(ret, r1, temp); } -static void gen_cond_add(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, TCGv r4) +static void gen_cond_add(TCGCond cond, + TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 r3, TCGv_i32 r4) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); - TCGv result = tcg_temp_new(); - TCGv mask = tcg_temp_new(); - TCGv t0 = tcg_constant_i32(0); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); + TCGv_i32 result = tcg_temp_new_i32(); + TCGv_i32 mask = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_constant_i32(0); /* create mask for sticky bits */ tcg_gen_setcond_i32(cond, mask, r4, t0); @@ -1403,16 +1417,17 @@ static void gen_cond_add(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, TCGv r4) tcg_gen_movcond_i32(cond, r3, r4, t0, result, r1); } -static void gen_condi_add(TCGCond cond, TCGv r1, int32_t r2, TCGv r3, TCGv r4) +static void gen_condi_add(TCGCond cond, + TCGv_i32 r1, int32_t r2, TCGv_i32 r3, TCGv_i32 r4) { - TCGv temp = tcg_constant_i32(r2); + TCGv_i32 temp = tcg_constant_i32(r2); gen_cond_add(cond, r1, temp, r3, r4); } -static void gen_sub_d(TCGv ret, TCGv r1, TCGv r2) +static void gen_sub_d(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2) { - TCGv temp = tcg_temp_new_i32(); - TCGv result = tcg_temp_new_i32(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 result = tcg_temp_new_i32(); tcg_gen_sub_i32(result, r1, r2); /* calc V bit */ @@ -1430,10 +1445,9 @@ static void gen_sub_d(TCGv ret, TCGv r1, TCGv r2) tcg_gen_mov_i32(ret, result); } -static void -gen_sub64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) +static void gen_sub64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) { - TCGv temp = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 result = tcg_temp_new_i64(); @@ -1456,10 +1470,10 @@ gen_sub64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) tcg_gen_mov_i64(ret, result); } -static void gen_sub_CC(TCGv ret, TCGv r1, TCGv r2) +static void gen_sub_CC(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2) { - TCGv result = tcg_temp_new(); - TCGv temp = tcg_temp_new(); + TCGv_i32 result = tcg_temp_new_i32(); + TCGv_i32 temp = tcg_temp_new_i32(); tcg_gen_sub_i32(result, r1, r2); /* calc C bit */ @@ -1479,20 +1493,21 @@ static void gen_sub_CC(TCGv ret, TCGv r1, TCGv r2) tcg_gen_mov_i32(ret, result); } -static void gen_subc_CC(TCGv ret, TCGv r1, TCGv r2) +static void gen_subc_CC(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2) { - TCGv temp = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); tcg_gen_not_i32(temp, r2); gen_addc_CC(ret, r1, temp); } -static void gen_cond_sub(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, TCGv r4) +static void gen_cond_sub(TCGCond cond, + TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 r3, TCGv_i32 r4) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); - TCGv result = tcg_temp_new(); - TCGv mask = tcg_temp_new(); - TCGv t0 = tcg_constant_i32(0); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); + TCGv_i32 result = tcg_temp_new_i32(); + TCGv_i32 mask = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_constant_i32(0); /* create mask for sticky bits */ tcg_gen_setcond_i32(cond, mask, r4, t0); @@ -1518,13 +1533,14 @@ static void gen_cond_sub(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, TCGv r4) tcg_gen_movcond_i32(cond, r3, r4, t0, result, r1); } -static void -gen_msub_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, uint32_t n, uint32_t mode) +static void gen_msub_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 t_n = tcg_constant_i32(n); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: @@ -1545,14 +1561,15 @@ gen_msub_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_sub_tl, tcg_gen_sub_tl); } -static void -gen_msubs_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, uint32_t n, uint32_t mode) +static void gen_msubs_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); - TCGv temp3 = tcg_temp_new(); + TCGv_i32 t_n = tcg_constant_i32(n); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); + TCGv_i32 temp3 = tcg_temp_new_i32(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { @@ -1580,11 +1597,12 @@ gen_msubs_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_or_i32(cpu_PSW_AV, cpu_PSW_AV, temp3); } -static void -gen_msubm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, uint32_t n, uint32_t mode) +static void gen_msubm_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); + TCGv_i32 t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); TCGv_i64 temp64_2 = tcg_temp_new_i64(); TCGv_i64 temp64_3 = tcg_temp_new_i64(); @@ -1608,11 +1626,12 @@ gen_msubm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64_3); } -static void -gen_msubms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, uint32_t n, uint32_t mode) +static void gen_msubms_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); + TCGv_i32 t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); TCGv_i64 temp64_2 = tcg_temp_new_i64(); switch (mode) { @@ -1634,11 +1653,12 @@ gen_msubms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); } -static void -gen_msubr64_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, - uint32_t mode) +static void gen_msubr64_h(TCGv_i32 ret, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); + TCGv_i32 t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: @@ -1657,22 +1677,24 @@ gen_msubr64_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, gen_helper_subr_h(ret, tcg_env, temp64, r1_low, r1_high); } -static void -gen_msubr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) +static void gen_msubr32_h(TCGv_i32 ret, + TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); tcg_gen_andi_i32(temp2, r1, 0xffff0000); tcg_gen_shli_i32(temp, r1, 16); gen_msubr64_h(ret, temp, temp2, r2, r3, n, mode); } -static void -gen_msubr64s_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, - uint32_t n, uint32_t mode) +static void gen_msubr64s_h(TCGv_i32 ret, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); + TCGv_i32 t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: @@ -1691,36 +1713,37 @@ gen_msubr64s_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, gen_helper_subr_h_ssov(ret, tcg_env, temp64, r1_low, r1_high); } -static void -gen_msubr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) +static void gen_msubr32s_h(TCGv_i32 ret, + TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); tcg_gen_andi_i32(temp2, r1, 0xffff0000); tcg_gen_shli_i32(temp, r1, 16); gen_msubr64s_h(ret, temp, temp2, r2, r3, n, mode); } -static void -gen_msubr_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) +static void gen_msubr_q(TCGv_i32 ret, + TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 r3, uint32_t n) { - TCGv temp = tcg_constant_i32(n); + TCGv_i32 temp = tcg_constant_i32(n); gen_helper_msubr_q(ret, tcg_env, r1, r2, r3, temp); } -static void -gen_msubrs_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) +static void gen_msubrs_q(TCGv_i32 ret, + TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 r3, uint32_t n) { - TCGv temp = tcg_constant_i32(n); + TCGv_i32 temp = tcg_constant_i32(n); gen_helper_msubr_q_ssov(ret, tcg_env, r1, r2, r3, temp); } -static void -gen_msub32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, - uint32_t up_shift) +static void gen_msub32_q(TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, + uint32_t n, uint32_t up_shift) { - TCGv temp3 = tcg_temp_new(); + TCGv_i32 temp3 = tcg_temp_new_i32(); TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); TCGv_i64 t3 = tcg_temp_new_i64(); @@ -1757,11 +1780,12 @@ gen_msub32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, tcg_gen_mov_i32(ret, temp3); } -static void -gen_m16sub32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) +static void gen_m16sub32_q(TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, + uint32_t n) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); if (n == 0) { tcg_gen_mul_i32(temp, arg2, arg3); } else { /* n is expected to be 1 */ @@ -1774,11 +1798,12 @@ gen_m16sub32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) gen_sub_d(ret, arg1, temp); } -static void -gen_m16subs32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) +static void gen_m16subs32_q(TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, + uint32_t n) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); if (n == 0) { tcg_gen_mul_i32(temp, arg2, arg3); } else { /* n is expected to be 1 */ @@ -1791,12 +1816,12 @@ gen_m16subs32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) gen_subs(ret, arg1, temp); } -static void -gen_m16sub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, - TCGv arg3, uint32_t n) +static void gen_m16sub64_q(TCGv_i32 rl, TCGv_i32 rh, + TCGv_i32 arg1_low, TCGv_i32 arg1_high, + TCGv_i32 arg2, TCGv_i32 arg3, uint32_t n) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); TCGv_i64 t3 = tcg_temp_new_i64(); @@ -1818,12 +1843,12 @@ gen_m16sub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_extr_i64_i32(rl, rh, t3); } -static void -gen_m16subs64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, - TCGv arg3, uint32_t n) +static void gen_m16subs64_q(TCGv_i32 rl, TCGv_i32 rh, + TCGv_i32 arg1_low, TCGv_i32 arg1_high, + TCGv_i32 arg2, TCGv_i32 arg3, uint32_t n) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); @@ -1844,15 +1869,15 @@ gen_m16subs64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_extr_i64_i32(rl, rh, t1); } -static void -gen_msub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, - TCGv arg3, uint32_t n) +static void gen_msub64_q(TCGv_i32 rl, TCGv_i32 rh, + TCGv_i32 arg1_low, TCGv_i32 arg1_high, + TCGv_i32 arg2, TCGv_i32 arg3, uint32_t n) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); TCGv_i64 t3 = tcg_temp_new_i64(); TCGv_i64 t4 = tcg_temp_new_i64(); - TCGv temp, temp2; + TCGv_i32 temp, temp2; tcg_gen_concat_i32_i64(t1, arg1_low, arg1_high); tcg_gen_ext_i32_i64(t2, arg2); @@ -1872,8 +1897,8 @@ gen_msub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, (0x80000000 * 0x80000000) << 1). If this is the case, we negate the ovf. */ if (n == 1) { - temp = tcg_temp_new(); - temp2 = tcg_temp_new(); + temp = tcg_temp_new_i32(); + temp2 = tcg_temp_new_i32(); tcg_gen_setcondi_i32(TCG_COND_EQ, temp, arg2, 0x80000000); tcg_gen_setcond_i32(TCG_COND_EQ, temp2, arg2, arg3); tcg_gen_and_i32(temp, temp, temp2); @@ -1892,9 +1917,9 @@ gen_msub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static void -gen_msubs32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, - uint32_t up_shift) +static void gen_msubs32_q(TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, + uint32_t n, uint32_t up_shift) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); @@ -1915,25 +1940,26 @@ gen_msubs32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, gen_helper_msub32_q_sub_ssov(ret, tcg_env, t1, t3); } -static void -gen_msubs64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, - TCGv arg3, uint32_t n) +static void gen_msubs64_q(TCGv_i32 rl, TCGv_i32 rh, + TCGv_i32 arg1_low, TCGv_i32 arg1_high, + TCGv_i32 arg2, TCGv_i32 arg3, uint32_t n) { TCGv_i64 r1 = tcg_temp_new_i64(); - TCGv t_n = tcg_constant_i32(n); + TCGv_i32 t_n = tcg_constant_i32(n); tcg_gen_concat_i32_i64(r1, arg1_low, arg1_high); gen_helper_msub64_q_ssov(r1, tcg_env, r1, arg2, arg3, t_n); tcg_gen_extr_i64_i32(rl, rh, r1); } -static void -gen_msubad_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, uint32_t n, uint32_t mode) +static void gen_msubad_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 t_n = tcg_constant_i32(n); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: @@ -1954,11 +1980,12 @@ gen_msubad_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_add_tl, tcg_gen_sub_tl); } -static void -gen_msubadm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, uint32_t n, uint32_t mode) +static void gen_msubadm_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); + TCGv_i32 t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); TCGv_i64 temp64_2 = tcg_temp_new_i64(); TCGv_i64 temp64_3 = tcg_temp_new_i64(); @@ -1987,12 +2014,13 @@ gen_msubadm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64_2); } -static void -gen_msubadr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) +static void gen_msubadr32_h(TCGv_i32 ret, + TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 t_n = tcg_constant_i32(n); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: @@ -2013,14 +2041,15 @@ gen_msubadr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) gen_helper_subadr_h(ret, tcg_env, temp64, temp, temp2); } -static void -gen_msubads_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, uint32_t n, uint32_t mode) +static void gen_msubads_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); - TCGv temp3 = tcg_temp_new(); + TCGv_i32 t_n = tcg_constant_i32(n); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); + TCGv_i32 temp3 = tcg_temp_new_i32(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { @@ -2048,11 +2077,12 @@ gen_msubads_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_or_i32(cpu_PSW_AV, cpu_PSW_AV, temp3); } -static void -gen_msubadms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, - TCGv r3, uint32_t n, uint32_t mode) +static void gen_msubadms_h(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1_low, TCGv_i32 r1_high, + TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); + TCGv_i32 t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); TCGv_i64 temp64_2 = tcg_temp_new_i64(); @@ -2080,12 +2110,13 @@ gen_msubadms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); } -static void -gen_msubadr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) +static void gen_msubadr32s_h(TCGv_i32 ret, + TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 r3, + uint32_t n, uint32_t mode) { - TCGv t_n = tcg_constant_i32(n); - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 t_n = tcg_constant_i32(n); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: @@ -2106,7 +2137,7 @@ gen_msubadr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) gen_helper_subadr_h_ssov(ret, tcg_env, temp64, temp, temp2); } -static void gen_abs(TCGv ret, TCGv r1) +static void gen_abs(TCGv_i32 ret, TCGv_i32 r1) { tcg_gen_abs_i32(ret, r1); /* overflow can only happen, if r1 = 0x80000000 */ @@ -2121,10 +2152,10 @@ static void gen_abs(TCGv ret, TCGv r1) tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static void gen_absdif(TCGv ret, TCGv r1, TCGv r2) +static void gen_absdif(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2) { - TCGv temp = tcg_temp_new_i32(); - TCGv result = tcg_temp_new_i32(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 result = tcg_temp_new_i32(); tcg_gen_sub_i32(result, r1, r2); tcg_gen_sub_i32(temp, r2, r1); @@ -2147,22 +2178,22 @@ static void gen_absdif(TCGv ret, TCGv r1, TCGv r2) tcg_gen_mov_i32(ret, result); } -static void gen_absdifi(TCGv ret, TCGv r1, int32_t con) +static void gen_absdifi(TCGv_i32 ret, TCGv_i32 r1, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_absdif(ret, r1, temp); } -static void gen_absdifsi(TCGv ret, TCGv r1, int32_t con) +static void gen_absdifsi(TCGv_i32 ret, TCGv_i32 r1, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_helper_absdif_ssov(ret, tcg_env, r1, temp); } -static void gen_mul_i32s(TCGv ret, TCGv r1, TCGv r2) +static void gen_mul_i32s(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2) { - TCGv high = tcg_temp_new(); - TCGv low = tcg_temp_new(); + TCGv_i32 high = tcg_temp_new_i32(); + TCGv_i32 low = tcg_temp_new_i32(); tcg_gen_muls2_i32(low, high, r1, r2); tcg_gen_mov_i32(ret, low); @@ -2179,13 +2210,14 @@ static void gen_mul_i32s(TCGv ret, TCGv r1, TCGv r2) tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static void gen_muli_i32s(TCGv ret, TCGv r1, int32_t con) +static void gen_muli_i32s(TCGv_i32 ret, TCGv_i32 r1, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_mul_i32s(ret, r1, temp); } -static void gen_mul_i64s(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) +static void gen_mul_i64s(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1, TCGv_i32 r2) { tcg_gen_muls2_i32(ret_low, ret_high, r1, r2); /* clear V bit */ @@ -2199,14 +2231,15 @@ static void gen_mul_i64s(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static void gen_muli_i64s(TCGv ret_low, TCGv ret_high, TCGv r1, - int32_t con) +static void gen_muli_i64s(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_mul_i64s(ret_low, ret_high, r1, temp); } -static void gen_mul_i64u(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) +static void gen_mul_i64u(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1, TCGv_i32 r2) { tcg_gen_mulu2_i32(ret_low, ret_high, r1, r2); /* clear V bit */ @@ -2220,40 +2253,40 @@ static void gen_mul_i64u(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static void gen_muli_i64u(TCGv ret_low, TCGv ret_high, TCGv r1, - int32_t con) +static void gen_muli_i64u(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_mul_i64u(ret_low, ret_high, r1, temp); } -static void gen_mulsi_i32(TCGv ret, TCGv r1, int32_t con) +static void gen_mulsi_i32(TCGv_i32 ret, TCGv_i32 r1, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_helper_mul_ssov(ret, tcg_env, r1, temp); } -static void gen_mulsui_i32(TCGv ret, TCGv r1, int32_t con) +static void gen_mulsui_i32(TCGv_i32 ret, TCGv_i32 r1, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_helper_mul_suov(ret, tcg_env, r1, temp); } /* gen_maddsi_32(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); */ -static void gen_maddsi_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) +static void gen_maddsi_32(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_helper_madd32_ssov(ret, tcg_env, r1, r2, temp); } -static void gen_maddsui_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) +static void gen_maddsui_32(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_helper_madd32_suov(ret, tcg_env, r1, r2, temp); } -static void -gen_mul_q(TCGv rl, TCGv rh, TCGv arg1, TCGv arg2, uint32_t n, uint32_t up_shift) +static void gen_mul_q(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2, + uint32_t n, uint32_t up_shift) { TCGv_i64 temp_64 = tcg_temp_new_i64(); TCGv_i64 temp2_64 = tcg_temp_new_i64(); @@ -2309,10 +2342,9 @@ gen_mul_q(TCGv rl, TCGv rh, TCGv arg1, TCGv arg2, uint32_t n, uint32_t up_shift) tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static void -gen_mul_q_16(TCGv ret, TCGv arg1, TCGv arg2, uint32_t n) +static void gen_mul_q_16(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2, uint32_t n) { - TCGv temp = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); if (n == 0) { tcg_gen_mul_i32(ret, arg1, arg2); } else { /* n is expected to be 1 */ @@ -2331,9 +2363,9 @@ gen_mul_q_16(TCGv ret, TCGv arg1, TCGv arg2, uint32_t n) tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static void gen_mulr_q(TCGv ret, TCGv arg1, TCGv arg2, uint32_t n) +static void gen_mulr_q(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2, uint32_t n) { - TCGv temp = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); if (n == 0) { tcg_gen_mul_i32(ret, arg1, arg2); tcg_gen_addi_i32(ret, ret, 0x8000); @@ -2357,9 +2389,9 @@ static void gen_mulr_q(TCGv ret, TCGv arg1, TCGv arg2, uint32_t n) tcg_gen_andi_i32(ret, ret, 0xffff0000); } -static void -gen_madds_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, - TCGv r3) +static void gen_madds_64(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1, TCGv_i32 r2_low, TCGv_i32 r2_high, + TCGv_i32 r3) { TCGv_i64 temp64 = tcg_temp_new_i64(); tcg_gen_concat_i32_i64(temp64, r2_low, r2_high); @@ -2367,17 +2399,17 @@ gen_madds_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); } -static void -gen_maddsi_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, - int32_t con) +static void gen_maddsi_64(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1, TCGv_i32 r2_low, TCGv_i32 r2_high, + int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_madds_64(ret_low, ret_high, r1, r2_low, r2_high, temp); } -static void -gen_maddsu_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, - TCGv r3) +static void gen_maddsu_64(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1, TCGv_i32 r2_low, TCGv_i32 r2_high, + TCGv_i32 r3) { TCGv_i64 temp64 = tcg_temp_new_i64(); tcg_gen_concat_i32_i64(temp64, r2_low, r2_high); @@ -2385,29 +2417,29 @@ gen_maddsu_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); } -static void -gen_maddsui_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, - int32_t con) +static void gen_maddsui_64(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1, TCGv_i32 r2_low, + TCGv_i32 r2_high, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_maddsu_64(ret_low, ret_high, r1, r2_low, r2_high, temp); } -static void gen_msubsi_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) +static void gen_msubsi_32(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_helper_msub32_ssov(ret, tcg_env, r1, r2, temp); } -static void gen_msubsui_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) +static void gen_msubsui_32(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_helper_msub32_suov(ret, tcg_env, r1, r2, temp); } -static void -gen_msubs_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, - TCGv r3) +static void gen_msubs_64(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1, TCGv_i32 r2_low, TCGv_i32 r2_high, + TCGv_i32 r3) { TCGv_i64 temp64 = tcg_temp_new_i64(); tcg_gen_concat_i32_i64(temp64, r2_low, r2_high); @@ -2415,17 +2447,17 @@ gen_msubs_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); } -static void -gen_msubsi_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, - int32_t con) +static void gen_msubsi_64(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1, TCGv_i32 r2_low, TCGv_i32 r2_high, + int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_msubs_64(ret_low, ret_high, r1, r2_low, r2_high, temp); } -static void -gen_msubsu_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, - TCGv r3) +static void gen_msubsu_64(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1, TCGv_i32 r2_low, TCGv_i32 r2_high, + TCGv_i32 r3) { TCGv_i64 temp64 = tcg_temp_new_i64(); tcg_gen_concat_i32_i64(temp64, r2_low, r2_high); @@ -2433,26 +2465,26 @@ gen_msubsu_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); } -static void -gen_msubsui_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, - int32_t con) +static void gen_msubsui_64(TCGv_i32 ret_low, TCGv_i32 ret_high, + TCGv_i32 r1, TCGv_i32 r2_low, TCGv_i32 r2_high, + int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_msubsu_64(ret_low, ret_high, r1, r2_low, r2_high, temp); } -static void gen_saturate(TCGv ret, TCGv arg, int32_t up, int32_t low) +static void gen_saturate(TCGv_i32 ret, TCGv_i32 arg, int32_t up, int32_t low) { tcg_gen_smax_i32(ret, arg, tcg_constant_i32(low)); tcg_gen_smin_i32(ret, ret, tcg_constant_i32(up)); } -static void gen_saturate_u(TCGv ret, TCGv arg, int32_t up) +static void gen_saturate_u(TCGv_i32 ret, TCGv_i32 arg, int32_t up) { tcg_gen_umin_i32(ret, arg, tcg_constant_i32(up)); } -static void gen_shi(TCGv ret, TCGv r1, int32_t shift_count) +static void gen_shi(TCGv_i32 ret, TCGv_i32 r1, int32_t shift_count) { if (shift_count == -32) { tcg_gen_movi_i32(ret, 0); @@ -2463,15 +2495,15 @@ static void gen_shi(TCGv ret, TCGv r1, int32_t shift_count) } } -static void gen_sh_hi(TCGv ret, TCGv r1, int32_t shiftcount) +static void gen_sh_hi(TCGv_i32 ret, TCGv_i32 r1, int32_t shiftcount) { - TCGv temp_low, temp_high; + TCGv_i32 temp_low, temp_high; if (shiftcount == -16) { tcg_gen_movi_i32(ret, 0); } else { - temp_high = tcg_temp_new(); - temp_low = tcg_temp_new(); + temp_high = tcg_temp_new_i32(); + temp_low = tcg_temp_new_i32(); tcg_gen_andi_i32(temp_low, r1, 0xffff); tcg_gen_andi_i32(temp_high, r1, 0xffff0000); @@ -2481,11 +2513,11 @@ static void gen_sh_hi(TCGv ret, TCGv r1, int32_t shiftcount) } } -static void gen_shaci(TCGv ret, TCGv r1, int32_t shift_count) +static void gen_shaci(TCGv_i32 ret, TCGv_i32 r1, int32_t shift_count) { uint32_t msk, msk_start; - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); if (shift_count == 0) { /* Clear PSW.C and PSW.V */ @@ -2500,8 +2532,8 @@ static void gen_shaci(TCGv ret, TCGv r1, int32_t shift_count) /* clear PSW.V */ tcg_gen_movi_i32(cpu_PSW_V, 0); } else if (shift_count > 0) { - TCGv t_max = tcg_constant_i32(0x7FFFFFFF >> shift_count); - TCGv t_min = tcg_constant_i32(((int32_t) -0x80000000) >> shift_count); + TCGv_i32 t_max = tcg_constant_i32(0x7FFFFFFF >> shift_count); + TCGv_i32 t_min = tcg_constant_i32(((int32_t) -0x80000000) >> shift_count); /* calc carry */ msk_start = 32 - shift_count; @@ -2532,34 +2564,34 @@ static void gen_shaci(TCGv ret, TCGv r1, int32_t shift_count) tcg_gen_or_i32(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); } -static void gen_shas(TCGv ret, TCGv r1, TCGv r2) +static void gen_shas(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2) { gen_helper_sha_ssov(ret, tcg_env, r1, r2); } -static void gen_shasi(TCGv ret, TCGv r1, int32_t con) +static void gen_shasi(TCGv_i32 ret, TCGv_i32 r1, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_shas(ret, r1, temp); } -static void gen_sha_hi(TCGv ret, TCGv r1, int32_t shift_count) +static void gen_sha_hi(TCGv_i32 ret, TCGv_i32 r1, int32_t shift_count) { - TCGv low, high; + TCGv_i32 low, high; if (shift_count == 0) { tcg_gen_mov_i32(ret, r1); } else if (shift_count > 0) { - low = tcg_temp_new(); - high = tcg_temp_new(); + low = tcg_temp_new_i32(); + high = tcg_temp_new_i32(); tcg_gen_andi_i32(high, r1, 0xffff0000); tcg_gen_shli_i32(low, r1, shift_count); tcg_gen_shli_i32(ret, high, shift_count); tcg_gen_deposit_i32(ret, ret, low, 0, 16); } else { - low = tcg_temp_new(); - high = tcg_temp_new(); + low = tcg_temp_new_i32(); + high = tcg_temp_new_i32(); tcg_gen_ext16s_i32(low, r1); tcg_gen_sari_i32(low, low, -shift_count); @@ -2569,58 +2601,58 @@ static void gen_sha_hi(TCGv ret, TCGv r1, int32_t shift_count) } /* ret = {ret[30:0], (r1 cond r2)}; */ -static void gen_sh_cond(int cond, TCGv ret, TCGv r1, TCGv r2) +static void gen_sh_cond(int cond, TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); tcg_gen_shli_i32(temp, ret, 1); tcg_gen_setcond_i32(cond, temp2, r1, r2); tcg_gen_or_i32(ret, temp, temp2); } -static void gen_sh_condi(int cond, TCGv ret, TCGv r1, int32_t con) +static void gen_sh_condi(int cond, TCGv_i32 ret, TCGv_i32 r1, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_sh_cond(cond, ret, r1, temp); } -static void gen_adds(TCGv ret, TCGv r1, TCGv r2) +static void gen_adds(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2) { gen_helper_add_ssov(ret, tcg_env, r1, r2); } -static void gen_addsi(TCGv ret, TCGv r1, int32_t con) +static void gen_addsi(TCGv_i32 ret, TCGv_i32 r1, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_helper_add_ssov(ret, tcg_env, r1, temp); } -static void gen_addsui(TCGv ret, TCGv r1, int32_t con) +static void gen_addsui(TCGv_i32 ret, TCGv_i32 r1, int32_t con) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_helper_add_suov(ret, tcg_env, r1, temp); } -static void gen_subs(TCGv ret, TCGv r1, TCGv r2) +static void gen_subs(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2) { gen_helper_sub_ssov(ret, tcg_env, r1, r2); } -static void gen_subsu(TCGv ret, TCGv r1, TCGv r2) +static void gen_subsu(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2) { gen_helper_sub_suov(ret, tcg_env, r1, r2); } -static void gen_bit_2op(TCGv ret, TCGv r1, TCGv r2, +static void gen_bit_2op(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2, int pos1, int pos2, - void(*op1)(TCGv, TCGv, TCGv), - void(*op2)(TCGv, TCGv, TCGv)) + void(*op1)(TCGv_i32, TCGv_i32, TCGv_i32), + void(*op2)(TCGv_i32, TCGv_i32, TCGv_i32)) { - TCGv temp1, temp2; + TCGv_i32 temp1, temp2; - temp1 = tcg_temp_new(); - temp2 = tcg_temp_new(); + temp1 = tcg_temp_new_i32(); + temp2 = tcg_temp_new_i32(); tcg_gen_shri_i32(temp2, r2, pos2); tcg_gen_shri_i32(temp1, r1, pos1); @@ -2632,14 +2664,14 @@ static void gen_bit_2op(TCGv ret, TCGv r1, TCGv r2, } /* ret = r1[pos1] op1 r2[pos2]; */ -static void gen_bit_1op(TCGv ret, TCGv r1, TCGv r2, +static void gen_bit_1op(TCGv_i32 ret, TCGv_i32 r1, TCGv_i32 r2, int pos1, int pos2, - void(*op1)(TCGv, TCGv, TCGv)) + void(*op1)(TCGv_i32, TCGv_i32, TCGv_i32)) { - TCGv temp1, temp2; + TCGv_i32 temp1, temp2; - temp1 = tcg_temp_new(); - temp2 = tcg_temp_new(); + temp1 = tcg_temp_new_i32(); + temp2 = tcg_temp_new_i32(); tcg_gen_shri_i32(temp2, r2, pos2); tcg_gen_shri_i32(temp1, r1, pos1); @@ -2649,11 +2681,12 @@ static void gen_bit_1op(TCGv ret, TCGv r1, TCGv r2, tcg_gen_andi_i32(ret, ret, 0x1); } -static void gen_accumulating_cond(int cond, TCGv ret, TCGv r1, TCGv r2, - void(*op)(TCGv, TCGv, TCGv)) +static void gen_accumulating_cond(int cond, TCGv_i32 ret, + TCGv_i32 r1, TCGv_i32 r2, + void(*op)(TCGv_i32, TCGv_i32, TCGv_i32)) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); /* temp = (arg1 cond arg2 )*/ tcg_gen_setcond_i32(cond, temp, r1, r2); /* temp2 = ret[0]*/ @@ -2664,20 +2697,20 @@ static void gen_accumulating_cond(int cond, TCGv ret, TCGv r1, TCGv r2, tcg_gen_deposit_i32(ret, ret, temp, 0, 1); } -static void -gen_accumulating_condi(int cond, TCGv ret, TCGv r1, int32_t con, - void(*op)(TCGv, TCGv, TCGv)) +static void gen_accumulating_condi(int cond, TCGv_i32 ret, TCGv_i32 r1, + int32_t con, + void(*op)(TCGv_i32, TCGv_i32, TCGv_i32)) { - TCGv temp = tcg_constant_i32(con); + TCGv_i32 temp = tcg_constant_i32(con); gen_accumulating_cond(cond, ret, r1, temp, op); } -static void gen_eqany_bi(TCGv ret, TCGv r1, int32_t con) +static void gen_eqany_bi(TCGv_i32 ret, TCGv_i32 r1, int32_t con) { - TCGv b0 = tcg_temp_new(); - TCGv b1 = tcg_temp_new(); - TCGv b2 = tcg_temp_new(); - TCGv b3 = tcg_temp_new(); + TCGv_i32 b0 = tcg_temp_new_i32(); + TCGv_i32 b1 = tcg_temp_new_i32(); + TCGv_i32 b2 = tcg_temp_new_i32(); + TCGv_i32 b3 = tcg_temp_new_i32(); /* byte 0 */ tcg_gen_andi_i32(b0, r1, 0xff); @@ -2701,10 +2734,10 @@ static void gen_eqany_bi(TCGv ret, TCGv r1, int32_t con) tcg_gen_or_i32(ret, ret, b3); } -static void gen_eqany_hi(TCGv ret, TCGv r1, int32_t con) +static void gen_eqany_hi(TCGv_i32 ret, TCGv_i32 r1, int32_t con) { - TCGv h0 = tcg_temp_new(); - TCGv h1 = tcg_temp_new(); + TCGv_i32 h0 = tcg_temp_new_i32(); + TCGv_i32 h1 = tcg_temp_new_i32(); /* halfword 0 */ tcg_gen_andi_i32(h0, r1, 0xffff); @@ -2720,11 +2753,12 @@ static void gen_eqany_hi(TCGv ret, TCGv r1, int32_t con) /* mask = ((1 << width) -1) << pos; ret = (r1 & ~mask) | (r2 << pos) & mask); */ -static void gen_insert(TCGv ret, TCGv r1, TCGv r2, TCGv width, TCGv pos) +static void gen_insert(TCGv_i32 ret, + TCGv_i32 r1, TCGv_i32 r2, TCGv_i32 width, TCGv_i32 pos) { - TCGv mask = tcg_temp_new(); - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); + TCGv_i32 mask = tcg_temp_new_i32(); + TCGv_i32 temp = tcg_temp_new_i32(); + TCGv_i32 temp2 = tcg_temp_new_i32(); tcg_gen_shl_i32(mask, tcg_constant_i32(1), width); tcg_gen_subi_i32(mask, mask, 1); @@ -2736,7 +2770,7 @@ static void gen_insert(TCGv ret, TCGv r1, TCGv r2, TCGv width, TCGv pos) tcg_gen_or_i32(ret, temp, temp2); } -static void gen_bsplit(TCGv rl, TCGv rh, TCGv r1) +static void gen_bsplit(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 r1) { TCGv_i64 temp = tcg_temp_new_i64(); @@ -2744,7 +2778,7 @@ static void gen_bsplit(TCGv rl, TCGv rh, TCGv r1) tcg_gen_extr_i64_i32(rl, rh, temp); } -static void gen_unpack(TCGv rl, TCGv rh, TCGv r1) +static void gen_unpack(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 r1) { TCGv_i64 temp = tcg_temp_new_i64(); @@ -2752,8 +2786,9 @@ static void gen_unpack(TCGv rl, TCGv rh, TCGv r1) tcg_gen_extr_i64_i32(rl, rh, temp); } -static void -gen_dvinit_b(DisasContext *ctx, TCGv rl, TCGv rh, TCGv r1, TCGv r2) +static void gen_dvinit_b(DisasContext *ctx, + TCGv_i32 rl, TCGv_i32 rh, + TCGv_i32 r1, TCGv_i32 r2) { TCGv_i64 ret = tcg_temp_new_i64(); @@ -2765,8 +2800,9 @@ gen_dvinit_b(DisasContext *ctx, TCGv rl, TCGv rh, TCGv r1, TCGv r2) tcg_gen_extr_i64_i32(rl, rh, ret); } -static void -gen_dvinit_h(DisasContext *ctx, TCGv rl, TCGv rh, TCGv r1, TCGv r2) +static void gen_dvinit_h(DisasContext *ctx, + TCGv_i32 rl, TCGv_i32 rh, + TCGv_i32 r1, TCGv_i32 r2) { TCGv_i64 ret = tcg_temp_new_i64(); @@ -2778,9 +2814,9 @@ gen_dvinit_h(DisasContext *ctx, TCGv rl, TCGv rh, TCGv r1, TCGv r2) tcg_gen_extr_i64_i32(rl, rh, ret); } -static void gen_calc_usb_mul_h(TCGv arg_low, TCGv arg_high) +static void gen_calc_usb_mul_h(TCGv_i32 arg_low, TCGv_i32 arg_high) { - TCGv temp = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); /* calc AV bit */ tcg_gen_add_i32(temp, arg_low, arg_low); tcg_gen_xor_i32(temp, temp, arg_low); @@ -2792,9 +2828,9 @@ static void gen_calc_usb_mul_h(TCGv arg_low, TCGv arg_high) tcg_gen_movi_i32(cpu_PSW_V, 0); } -static void gen_calc_usb_mulr_h(TCGv arg) +static void gen_calc_usb_mulr_h(TCGv_i32 arg) { - TCGv temp = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); /* calc AV bit */ tcg_gen_add_i32(temp, arg, arg); tcg_gen_xor_i32(temp, temp, arg); @@ -2836,8 +2872,8 @@ static void generate_trap(DisasContext *ctx, int class, int tin) ctx->base.is_jmp = DISAS_NORETURN; } -static void gen_branch_cond(DisasContext *ctx, TCGCond cond, TCGv r1, - TCGv r2, int16_t address) +static void gen_branch_cond(DisasContext *ctx, TCGCond cond, TCGv_i32 r1, + TCGv_i32 r2, int16_t address) { TCGLabel *jumpLabel = gen_new_label(); tcg_gen_brcond_i32(cond, r1, r2, jumpLabel); @@ -2848,10 +2884,10 @@ static void gen_branch_cond(DisasContext *ctx, TCGCond cond, TCGv r1, gen_goto_tb(ctx, 0, ctx->base.pc_next + address * 2); } -static void gen_branch_condi(DisasContext *ctx, TCGCond cond, TCGv r1, +static void gen_branch_condi(DisasContext *ctx, TCGCond cond, TCGv_i32 r1, int r2, int16_t address) { - TCGv temp = tcg_constant_i32(r2); + TCGv_i32 temp = tcg_constant_i32(r2); gen_branch_cond(ctx, cond, r1, temp, address); } @@ -2868,7 +2904,7 @@ static void gen_loop(DisasContext *ctx, int r1, int32_t offset) static void gen_fcall_save_ctx(DisasContext *ctx) { - TCGv temp = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); tcg_gen_addi_i32(temp, cpu_gpr_a[10], -4); tcg_gen_qemu_st_i32(cpu_gpr_a[11], temp, ctx->mem_idx, MO_LESL); @@ -2878,7 +2914,7 @@ static void gen_fcall_save_ctx(DisasContext *ctx) static void gen_fret(DisasContext *ctx) { - TCGv temp = tcg_temp_new(); + TCGv_i32 temp = tcg_temp_new_i32(); tcg_gen_andi_i32(temp, cpu_gpr_a[11], ~0x1); tcg_gen_qemu_ld_i32(cpu_gpr_a[11], cpu_gpr_a[10], ctx->mem_idx, MO_LESL); @@ -2890,7 +2926,7 @@ static void gen_fret(DisasContext *ctx) static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, int r2 , int32_t constant , int32_t offset) { - TCGv temp, temp2; + TCGv_i32 temp, temp2; int n; switch (opc) { @@ -2927,12 +2963,12 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, break; /* SBRN-format jumps */ case OPC1_16_SBRN_JZ_T: - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_andi_i32(temp, cpu_gpr_d[15], 0x1u << constant); gen_branch_condi(ctx, TCG_COND_EQ, temp, 0, offset); break; case OPC1_16_SBRN_JNZ_T: - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_andi_i32(temp, cpu_gpr_d[15], 0x1u << constant); gen_branch_condi(ctx, TCG_COND_NE, temp, 0, offset); break; @@ -3040,7 +3076,7 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, } break; case OPCM_32_BRC_JNE: - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); if (MASK_OP_BRC_OP2(ctx->opcode) == OPC2_32_BRC_JNED) { tcg_gen_mov_i32(temp, cpu_gpr_d[r1]); /* subi is unconditional */ @@ -3057,7 +3093,7 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, case OPCM_32_BRN_JTT: n = MASK_OP_BRN_N(ctx->opcode); - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_andi_i32(temp, cpu_gpr_d[r1], (1 << n)); if (MASK_OP_BRN_OP2(ctx->opcode) == OPC2_32_BRN_JNZ_T) { @@ -3112,8 +3148,8 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, } break; case OPCM_32_BRR_JNE: - temp = tcg_temp_new(); - temp2 = tcg_temp_new(); + temp = tcg_temp_new_i32(); + temp2 = tcg_temp_new_i32(); if (MASK_OP_BRC_OP2(ctx->opcode) == OPC2_32_BRR_JNED) { tcg_gen_mov_i32(temp, cpu_gpr_d[r1]); /* also save r2, in case of r1 == r2, so r2 is not decremented */ @@ -3151,7 +3187,7 @@ static void decode_src_opc(DisasContext *ctx, int op1) { int r1; int32_t const4; - TCGv temp, temp2; + TCGv_i32 temp, temp2; r1 = MASK_OP_SRC_S1D(ctx->opcode); const4 = MASK_OP_SRC_CONST4_SEXT(ctx->opcode); @@ -3227,7 +3263,7 @@ static void decode_src_opc(DisasContext *ctx, int op1) static void decode_srr_opc(DisasContext *ctx, int op1) { int r1, r2; - TCGv temp; + TCGv_i32 temp; r1 = MASK_OP_SRR_S1D(ctx->opcode); r2 = MASK_OP_SRR_S2(ctx->opcode); @@ -3545,7 +3581,7 @@ static void decode_16Bit_opc(DisasContext *ctx) int r1, r2; int32_t const16; int32_t address; - TCGv temp; + TCGv_i32 temp; op1 = MASK_OP_MAJOR(ctx->opcode); @@ -3612,7 +3648,7 @@ static void decode_16Bit_opc(DisasContext *ctx) r2 = MASK_OP_SRRS_S2(ctx->opcode); r1 = MASK_OP_SRRS_S1D(ctx->opcode); const16 = MASK_OP_SRRS_N(ctx->opcode); - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_shli_i32(temp, cpu_gpr_d[15], const16); tcg_gen_add_i32(cpu_gpr_a[r1], cpu_gpr_a[r2], temp); break; @@ -3780,7 +3816,7 @@ static void decode_abs_ldw(DisasContext *ctx) int32_t op2; int32_t r1; uint32_t address; - TCGv temp; + TCGv_i32 temp; r1 = MASK_OP_ABS_S1D(ctx->opcode); address = MASK_OP_ABS_OFF18(ctx->opcode); @@ -3813,7 +3849,7 @@ static void decode_abs_ldb(DisasContext *ctx) int32_t op2; int32_t r1; uint32_t address; - TCGv temp; + TCGv_i32 temp; r1 = MASK_OP_ABS_S1D(ctx->opcode); address = MASK_OP_ABS_OFF18(ctx->opcode); @@ -3844,7 +3880,7 @@ static void decode_abs_ldst_swap(DisasContext *ctx) int32_t op2; int32_t r1; uint32_t address; - TCGv temp; + TCGv_i32 temp; r1 = MASK_OP_ABS_S1D(ctx->opcode); address = MASK_OP_ABS_OFF18(ctx->opcode); @@ -3895,7 +3931,7 @@ static void decode_abs_store(DisasContext *ctx) int32_t op2; int32_t r1; uint32_t address; - TCGv temp; + TCGv_i32 temp; r1 = MASK_OP_ABS_S1D(ctx->opcode); address = MASK_OP_ABS_OFF18(ctx->opcode); @@ -3928,7 +3964,7 @@ static void decode_abs_storeb_h(DisasContext *ctx) int32_t op2; int32_t r1; uint32_t address; - TCGv temp; + TCGv_i32 temp; r1 = MASK_OP_ABS_S1D(ctx->opcode); address = MASK_OP_ABS_OFF18(ctx->opcode); @@ -4030,7 +4066,7 @@ static void decode_bit_insert(DisasContext *ctx) uint32_t op2; int r1, r2, r3; int pos1, pos2; - TCGv temp; + TCGv_i32 temp; op2 = MASK_OP_BIT_OP2(ctx->opcode); r1 = MASK_OP_BIT_S1(ctx->opcode); r2 = MASK_OP_BIT_S2(ctx->opcode); @@ -4038,7 +4074,7 @@ static void decode_bit_insert(DisasContext *ctx) pos1 = MASK_OP_BIT_POS1(ctx->opcode); pos2 = MASK_OP_BIT_POS2(ctx->opcode); - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_shri_i32(temp, cpu_gpr_d[r2], pos2); if (op2 == OPC2_32_BIT_INSN_T) { @@ -4129,7 +4165,7 @@ static void decode_bit_sh_logic1(DisasContext *ctx) uint32_t op2; int r1, r2, r3; int pos1, pos2; - TCGv temp; + TCGv_i32 temp; op2 = MASK_OP_BIT_OP2(ctx->opcode); r1 = MASK_OP_BIT_S1(ctx->opcode); @@ -4138,7 +4174,7 @@ static void decode_bit_sh_logic1(DisasContext *ctx) pos1 = MASK_OP_BIT_POS1(ctx->opcode); pos2 = MASK_OP_BIT_POS2(ctx->opcode); - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); switch (op2) { case OPC2_32_BIT_SH_AND_T: @@ -4169,7 +4205,7 @@ static void decode_bit_sh_logic2(DisasContext *ctx) uint32_t op2; int r1, r2, r3; int pos1, pos2; - TCGv temp; + TCGv_i32 temp; op2 = MASK_OP_BIT_OP2(ctx->opcode); r1 = MASK_OP_BIT_S1(ctx->opcode); @@ -4178,7 +4214,7 @@ static void decode_bit_sh_logic2(DisasContext *ctx) pos1 = MASK_OP_BIT_POS1(ctx->opcode); pos2 = MASK_OP_BIT_POS2(ctx->opcode); - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); switch (op2) { case OPC2_32_BIT_SH_NAND_T: @@ -4212,7 +4248,7 @@ static void decode_bo_addrmode_post_pre_base(DisasContext *ctx) uint32_t op2; uint32_t off10; int32_t r1, r2; - TCGv temp; + TCGv_i32 temp; r1 = MASK_OP_BO_S1D(ctx->opcode); r2 = MASK_OP_BO_S2(ctx->opcode); @@ -4296,7 +4332,7 @@ static void decode_bo_addrmode_post_pre_base(DisasContext *ctx) break; case OPC2_32_BO_ST_D_PREINC: CHECK_REG_PAIR(r1); - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); gen_st_2regs_64(ctx, cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp); tcg_gen_mov_i32(cpu_gpr_a[r2], temp); @@ -4314,7 +4350,7 @@ static void decode_bo_addrmode_post_pre_base(DisasContext *ctx) break; case OPC2_32_BO_ST_DA_PREINC: CHECK_REG_PAIR(r1); - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); gen_st_2regs_64(ctx, cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp); tcg_gen_mov_i32(cpu_gpr_a[r2], temp); @@ -4331,19 +4367,19 @@ static void decode_bo_addrmode_post_pre_base(DisasContext *ctx) gen_st_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); break; case OPC2_32_BO_ST_Q_SHORTOFF: - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_shri_i32(temp, cpu_gpr_d[r1], 16); gen_offset_st(ctx, temp, cpu_gpr_a[r2], off10, MO_LEUW); break; case OPC2_32_BO_ST_Q_POSTINC: - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_shri_i32(temp, cpu_gpr_d[r1], 16); tcg_gen_qemu_st_i32(temp, cpu_gpr_a[r2], ctx->mem_idx, MO_LEUW); tcg_gen_addi_i32(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_ST_Q_PREINC: - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_shri_i32(temp, cpu_gpr_d[r1], 16); gen_st_preincr(ctx, temp, cpu_gpr_a[r2], off10, MO_LEUW); break; @@ -4368,15 +4404,15 @@ static void decode_bo_addrmode_bitreverse_circular(DisasContext *ctx) uint32_t op2; uint32_t off10; int32_t r1, r2; - TCGv temp, temp2, t_off10; + TCGv_i32 temp, temp2, t_off10; r1 = MASK_OP_BO_S1D(ctx->opcode); r2 = MASK_OP_BO_S2(ctx->opcode); off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode); op2 = MASK_OP_BO_OP2(ctx->opcode); - temp = tcg_temp_new(); - temp2 = tcg_temp_new(); + temp = tcg_temp_new_i32(); + temp2 = tcg_temp_new_i32(); t_off10 = tcg_constant_i32(off10); CHECK_REG_PAIR(r2); tcg_gen_ext16u_i32(temp, cpu_gpr_a[r2 + 1]); @@ -4475,7 +4511,7 @@ static void decode_bo_addrmode_ld_post_pre_base(DisasContext *ctx) uint32_t op2; uint32_t off10; int32_t r1, r2; - TCGv temp; + TCGv_i32 temp; r1 = MASK_OP_BO_S1D(ctx->opcode); r2 = MASK_OP_BO_S2(ctx->opcode); @@ -4529,7 +4565,7 @@ static void decode_bo_addrmode_ld_post_pre_base(DisasContext *ctx) break; case OPC2_32_BO_LD_D_PREINC: CHECK_REG_PAIR(r1); - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); gen_ld_2regs_64(ctx, cpu_gpr_d[r1 + 1], cpu_gpr_d[r1], temp); tcg_gen_mov_i32(cpu_gpr_a[r2], temp); @@ -4547,7 +4583,7 @@ static void decode_bo_addrmode_ld_post_pre_base(DisasContext *ctx) break; case OPC2_32_BO_LD_DA_PREINC: CHECK_REG_PAIR(r1); - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_addi_i32(temp, cpu_gpr_a[r2], off10); gen_ld_2regs_64(ctx, cpu_gpr_a[r1 + 1], cpu_gpr_a[r1], temp); tcg_gen_mov_i32(cpu_gpr_a[r2], temp); @@ -4609,15 +4645,15 @@ static void decode_bo_addrmode_ld_bitreverse_circular(DisasContext *ctx) uint32_t op2; uint32_t off10; int r1, r2; - TCGv temp, temp2, t_off10; + TCGv_i32 temp, temp2, t_off10; r1 = MASK_OP_BO_S1D(ctx->opcode); r2 = MASK_OP_BO_S2(ctx->opcode); off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode); op2 = MASK_OP_BO_OP2(ctx->opcode); - temp = tcg_temp_new(); - temp2 = tcg_temp_new(); + temp = tcg_temp_new_i32(); + temp2 = tcg_temp_new_i32(); t_off10 = tcg_constant_i32(off10); CHECK_REG_PAIR(r2); tcg_gen_ext16u_i32(temp, cpu_gpr_a[r2 + 1]); @@ -4724,7 +4760,7 @@ static void decode_bo_addrmode_stctx_post_pre_base(DisasContext *ctx) uint32_t off10; int r1, r2; - TCGv temp; + TCGv_i32 temp; r1 = MASK_OP_BO_S1D(ctx->opcode); r2 = MASK_OP_BO_S2(ctx->opcode); @@ -4732,7 +4768,7 @@ static void decode_bo_addrmode_stctx_post_pre_base(DisasContext *ctx) op2 = MASK_OP_BO_OP2(ctx->opcode); - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); switch (op2) { case OPC2_32_BO_LDLCX_SHORTOFF: @@ -4812,15 +4848,15 @@ static void decode_bo_addrmode_ldmst_bitreverse_circular(DisasContext *ctx) uint32_t op2; uint32_t off10; int r1, r2; - TCGv temp, temp2, t_off10; + TCGv_i32 temp, temp2, t_off10; r1 = MASK_OP_BO_S1D(ctx->opcode); r2 = MASK_OP_BO_S2(ctx->opcode); off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode); op2 = MASK_OP_BO_OP2(ctx->opcode); - temp = tcg_temp_new(); - temp2 = tcg_temp_new(); + temp = tcg_temp_new_i32(); + temp2 = tcg_temp_new_i32(); t_off10 = tcg_constant_i32(off10); CHECK_REG_PAIR(r2); tcg_gen_ext16u_i32(temp, cpu_gpr_a[r2 + 1]); @@ -4868,7 +4904,7 @@ static void decode_bol_opc(DisasContext *ctx, int32_t op1) { int r1, r2; int32_t address; - TCGv temp; + TCGv_i32 temp; r1 = MASK_OP_BOL_S1D(ctx->opcode); r2 = MASK_OP_BOL_S2(ctx->opcode); @@ -4876,12 +4912,12 @@ static void decode_bol_opc(DisasContext *ctx, int32_t op1) switch (op1) { case OPC1_32_BOL_LD_A_LONGOFF: - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_addi_i32(temp, cpu_gpr_a[r2], address); tcg_gen_qemu_ld_i32(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LEUL); break; case OPC1_32_BOL_LD_W_LONGOFF: - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_addi_i32(temp, cpu_gpr_a[r2], address); tcg_gen_qemu_ld_i32(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUL); break; @@ -4951,7 +4987,7 @@ static void decode_rc_logical_shift(DisasContext *ctx) uint32_t op2; int r1, r2; int32_t const9; - TCGv temp; + TCGv_i32 temp; r2 = MASK_OP_RC_D(ctx->opcode); r1 = MASK_OP_RC_S1(ctx->opcode); @@ -4966,12 +5002,12 @@ static void decode_rc_logical_shift(DisasContext *ctx) tcg_gen_andi_i32(cpu_gpr_d[r2], cpu_gpr_d[r1], ~const9); break; case OPC2_32_RC_NAND: - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_movi_i32(temp, const9); tcg_gen_nand_i32(cpu_gpr_d[r2], cpu_gpr_d[r1], temp); break; case OPC2_32_RC_NOR: - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_movi_i32(temp, const9); tcg_gen_nor_i32(cpu_gpr_d[r2], cpu_gpr_d[r1], temp); break; @@ -5026,7 +5062,7 @@ static void decode_rc_accumulator(DisasContext *ctx) int r1, r2; int16_t const9; - TCGv temp; + TCGv_i32 temp; r2 = MASK_OP_RC_D(ctx->opcode); r1 = MASK_OP_RC_S1(ctx->opcode); @@ -5034,7 +5070,7 @@ static void decode_rc_accumulator(DisasContext *ctx) op2 = MASK_OP_RC_OP2(ctx->opcode); - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); switch (op2) { case OPC2_32_RC_ABSDIF: @@ -5287,7 +5323,7 @@ static void decode_rcpw_insert(DisasContext *ctx) int r1, r2; int32_t pos, width, const4; - TCGv temp; + TCGv_i32 temp; op2 = MASK_OP_RCPW_OP2(ctx->opcode); r1 = MASK_OP_RCPW_S1(ctx->opcode); @@ -5328,7 +5364,7 @@ static void decode_rcrw_insert(DisasContext *ctx) int r1, r3, r4; int32_t width, const4; - TCGv temp, temp2, temp3; + TCGv_i32 temp, temp2, temp3; op2 = MASK_OP_RCRW_OP2(ctx->opcode); r1 = MASK_OP_RCRW_S1(ctx->opcode); @@ -5337,8 +5373,8 @@ static void decode_rcrw_insert(DisasContext *ctx) width = MASK_OP_RCRW_WIDTH(ctx->opcode); const4 = MASK_OP_RCRW_CONST4(ctx->opcode); - temp = tcg_temp_new(); - temp2 = tcg_temp_new(); + temp = tcg_temp_new_i32(); + temp2 = tcg_temp_new_i32(); switch (op2) { case OPC2_32_RCRW_IMASK: @@ -5350,7 +5386,7 @@ static void decode_rcrw_insert(DisasContext *ctx) tcg_gen_shl_i32(cpu_gpr_d[r4], temp2, temp); break; case OPC2_32_RCRW_INSERT: - temp3 = tcg_temp_new(); + temp3 = tcg_temp_new_i32(); tcg_gen_movi_i32(temp, width); tcg_gen_movi_i32(temp2, const4); @@ -5370,7 +5406,7 @@ static void decode_rcr_cond_select(DisasContext *ctx) int r1, r3, r4; int32_t const9; - TCGv temp, temp2; + TCGv_i32 temp, temp2; op2 = MASK_OP_RCR_OP2(ctx->opcode); r1 = MASK_OP_RCR_S1(ctx->opcode); @@ -5577,7 +5613,7 @@ static void decode_rr_accumulator(DisasContext *ctx) uint32_t op2; int r3, r2, r1; - TCGv temp; + TCGv_i32 temp; r3 = MASK_OP_RR_D(ctx->opcode); r2 = MASK_OP_RR_S2(ctx->opcode); @@ -5774,7 +5810,7 @@ static void decode_rr_accumulator(DisasContext *ctx) break; case OPC2_32_RR_MOV_64: if (has_feature(ctx, TRICORE_FEATURE_16)) { - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); CHECK_REG_PAIR(r3); tcg_gen_mov_i32(temp, cpu_gpr_d[r1]); @@ -5993,7 +6029,7 @@ static void decode_rr_address(DisasContext *ctx) { uint32_t op2, n; int r1, r2, r3; - TCGv temp; + TCGv_i32 temp; op2 = MASK_OP_RR_OP2(ctx->opcode); r3 = MASK_OP_RR_D(ctx->opcode); @@ -6006,12 +6042,12 @@ static void decode_rr_address(DisasContext *ctx) tcg_gen_add_i32(cpu_gpr_a[r3], cpu_gpr_a[r1], cpu_gpr_a[r2]); break; case OPC2_32_RR_ADDSC_A: - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_shli_i32(temp, cpu_gpr_d[r1], n); tcg_gen_add_i32(cpu_gpr_a[r3], cpu_gpr_a[r2], temp); break; case OPC2_32_RR_ADDSC_AT: - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_sari_i32(temp, cpu_gpr_d[r1], 3); tcg_gen_add_i32(temp, cpu_gpr_a[r2], temp); tcg_gen_andi_i32(cpu_gpr_a[r3], temp, 0xFFFFFFFC); @@ -6091,7 +6127,7 @@ static void decode_rr_divide(DisasContext *ctx) uint32_t op2; int r1, r2, r3; - TCGv temp, temp2, temp3; + TCGv_i32 temp, temp2, temp3; op2 = MASK_OP_RR_OP2(ctx->opcode); r3 = MASK_OP_RR_D(ctx->opcode); @@ -6112,9 +6148,9 @@ static void decode_rr_divide(DisasContext *ctx) cpu_gpr_d[r2]); break; case OPC2_32_RR_DVINIT_BU: - temp = tcg_temp_new(); - temp2 = tcg_temp_new(); - temp3 = tcg_temp_new(); + temp = tcg_temp_new_i32(); + temp2 = tcg_temp_new_i32(); + temp3 = tcg_temp_new_i32(); CHECK_REG_PAIR(r3); tcg_gen_shri_i32(temp3, cpu_gpr_d[r1], 8); /* reset av */ @@ -6141,9 +6177,9 @@ static void decode_rr_divide(DisasContext *ctx) cpu_gpr_d[r2]); break; case OPC2_32_RR_DVINIT_HU: - temp = tcg_temp_new(); - temp2 = tcg_temp_new(); - temp3 = tcg_temp_new(); + temp = tcg_temp_new_i32(); + temp2 = tcg_temp_new_i32(); + temp3 = tcg_temp_new_i32(); CHECK_REG_PAIR(r3); tcg_gen_shri_i32(temp3, cpu_gpr_d[r1], 16); /* reset av */ @@ -6165,8 +6201,8 @@ static void decode_rr_divide(DisasContext *ctx) tcg_gen_mov_i32(cpu_gpr_d[r3 + 1], temp3); break; case OPC2_32_RR_DVINIT: - temp = tcg_temp_new(); - temp2 = tcg_temp_new(); + temp = tcg_temp_new_i32(); + temp2 = tcg_temp_new_i32(); CHECK_REG_PAIR(r3); /* overflow = ((D[b] == 0) || ((D[b] == 0xFFFFFFFF) && (D[a] == 0x80000000))) */ @@ -6315,7 +6351,7 @@ static void decode_rr1_mul(DisasContext *ctx) uint32_t op2; int r1, r2, r3; - TCGv n; + TCGv_i32 n; TCGv_i64 temp64; r1 = MASK_OP_RR1_S1(ctx->opcode); @@ -6420,7 +6456,7 @@ static void decode_rr1_mulq(DisasContext *ctx) int r1, r2, r3; uint32_t n; - TCGv temp, temp2; + TCGv_i32 temp, temp2; r1 = MASK_OP_RR1_S1(ctx->opcode); r2 = MASK_OP_RR1_S2(ctx->opcode); @@ -6428,8 +6464,8 @@ static void decode_rr1_mulq(DisasContext *ctx) n = MASK_OP_RR1_N(ctx->opcode); op2 = MASK_OP_RR1_OP2(ctx->opcode); - temp = tcg_temp_new(); - temp2 = tcg_temp_new(); + temp = tcg_temp_new_i32(); + temp2 = tcg_temp_new_i32(); switch (op2) { case OPC2_32_RR1_MUL_Q_32: @@ -6526,7 +6562,7 @@ static void decode_rrpw_extract_insert(DisasContext *ctx) uint32_t op2; int r1, r2, r3; int32_t pos, width; - TCGv temp; + TCGv_i32 temp; op2 = MASK_OP_RRPW_OP2(ctx->opcode); r1 = MASK_OP_RRPW_S1(ctx->opcode); @@ -6554,7 +6590,7 @@ static void decode_rrpw_extract_insert(DisasContext *ctx) CHECK_REG_PAIR(r3); if (pos + width <= 32) { - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); tcg_gen_movi_i32(temp, ((1u << width) - 1) << pos); tcg_gen_shli_i32(cpu_gpr_d[r3], cpu_gpr_d[r2], pos); tcg_gen_mov_i32(cpu_gpr_d[r3 + 1], temp); @@ -6580,7 +6616,7 @@ static void decode_rrr_cond_select(DisasContext *ctx) { uint32_t op2; int r1, r2, r3, r4; - TCGv temp; + TCGv_i32 temp; op2 = MASK_OP_RRR_OP2(ctx->opcode); r1 = MASK_OP_RRR_S1(ctx->opcode); @@ -6964,7 +7000,7 @@ static void decode_rrr1_maddq_h(DisasContext *ctx) { uint32_t op2; uint32_t r1, r2, r3, r4, n; - TCGv temp, temp2; + TCGv_i32 temp, temp2; op2 = MASK_OP_RRR1_OP2(ctx->opcode); r1 = MASK_OP_RRR1_S1(ctx->opcode); @@ -6973,8 +7009,8 @@ static void decode_rrr1_maddq_h(DisasContext *ctx) r4 = MASK_OP_RRR1_D(ctx->opcode); n = MASK_OP_RRR1_N(ctx->opcode); - temp = tcg_temp_new(); - temp2 = tcg_temp_new(); + temp = tcg_temp_new_i32(); + temp2 = tcg_temp_new_i32(); switch (op2) { case OPC2_32_RRR1_MADD_Q_32: @@ -7446,7 +7482,7 @@ static void decode_rrr1_msubq_h(DisasContext *ctx) { uint32_t op2; uint32_t r1, r2, r3, r4, n; - TCGv temp, temp2; + TCGv_i32 temp, temp2; op2 = MASK_OP_RRR1_OP2(ctx->opcode); r1 = MASK_OP_RRR1_S1(ctx->opcode); @@ -7455,8 +7491,8 @@ static void decode_rrr1_msubq_h(DisasContext *ctx) r4 = MASK_OP_RRR1_D(ctx->opcode); n = MASK_OP_RRR1_N(ctx->opcode); - temp = tcg_temp_new(); - temp2 = tcg_temp_new(); + temp = tcg_temp_new_i32(); + temp2 = tcg_temp_new_i32(); switch (op2) { case OPC2_32_RRR1_MSUB_Q_32: @@ -7783,7 +7819,7 @@ static void decode_rrrr_extract_insert(DisasContext *ctx) { uint32_t op2; int r1, r2, r3, r4; - TCGv tmp_width, tmp_pos; + TCGv_i32 tmp_width, tmp_pos; r1 = MASK_OP_RRRR_S1(ctx->opcode); r2 = MASK_OP_RRRR_S2(ctx->opcode); @@ -7791,8 +7827,8 @@ static void decode_rrrr_extract_insert(DisasContext *ctx) r4 = MASK_OP_RRRR_D(ctx->opcode); op2 = MASK_OP_RRRR_OP2(ctx->opcode); - tmp_pos = tcg_temp_new(); - tmp_width = tcg_temp_new(); + tmp_pos = tcg_temp_new_i32(); + tmp_width = tcg_temp_new_i32(); switch (op2) { case OPC2_32_RRRR_DEXTR: @@ -7800,8 +7836,8 @@ static void decode_rrrr_extract_insert(DisasContext *ctx) if (r1 == r2) { tcg_gen_rotl_i32(cpu_gpr_d[r4], cpu_gpr_d[r1], tmp_pos); } else { - TCGv msw = tcg_temp_new(); - TCGv zero = tcg_constant_i32(0); + TCGv_i32 msw = tcg_temp_new_i32(); + TCGv_i32 zero = tcg_constant_i32(0); tcg_gen_shl_i32(tmp_width, cpu_gpr_d[r1], tmp_pos); tcg_gen_subfi_i32(msw, 32, tmp_pos); tcg_gen_shr_i32(msw, cpu_gpr_d[r2], msw); @@ -7848,7 +7884,7 @@ static void decode_rrrw_extract_insert(DisasContext *ctx) int r1, r2, r3, r4; int32_t width; - TCGv temp, temp2; + TCGv_i32 temp, temp2; op2 = MASK_OP_RRRW_OP2(ctx->opcode); r1 = MASK_OP_RRRW_S1(ctx->opcode); @@ -7857,7 +7893,7 @@ static void decode_rrrw_extract_insert(DisasContext *ctx) r4 = MASK_OP_RRRW_D(ctx->opcode); width = MASK_OP_RRRW_WIDTH(ctx->opcode); - temp = tcg_temp_new(); + temp = tcg_temp_new_i32(); switch (op2) { case OPC2_32_RRRW_EXTR: @@ -7877,7 +7913,7 @@ static void decode_rrrw_extract_insert(DisasContext *ctx) } break; case OPC2_32_RRRW_IMASK: - temp2 = tcg_temp_new(); + temp2 = tcg_temp_new_i32(); CHECK_REG_PAIR(r4); tcg_gen_andi_i32(temp, cpu_gpr_d[r3], 0x1f); tcg_gen_movi_i32(temp2, (1 << width) - 1); @@ -7886,7 +7922,7 @@ static void decode_rrrw_extract_insert(DisasContext *ctx) tcg_gen_mov_i32(cpu_gpr_d[r4 + 1], temp2); break; case OPC2_32_RRRW_INSERT: - temp2 = tcg_temp_new(); + temp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(temp, width); tcg_gen_andi_i32(temp2, cpu_gpr_d[r3], 0x1f); @@ -7903,7 +7939,7 @@ static void decode_sys_interrupts(DisasContext *ctx) uint32_t op2; uint32_t r1; TCGLabel *l1; - TCGv tmp; + TCGv_i32 tmp; op2 = MASK_OP_SYS_OP2(ctx->opcode); r1 = MASK_OP_SYS_S1D(ctx->opcode); @@ -7957,7 +7993,7 @@ static void decode_sys_interrupts(DisasContext *ctx) break; case OPC2_32_SYS_RFM: if (ctx->priv == TRICORE_PRIV_SM) { - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i32(); l1 = gen_new_label(); tcg_gen_ld_i32(tmp, tcg_env, offsetof(CPUTriCoreState, DBGSR)); @@ -8013,7 +8049,7 @@ static void decode_32Bit_opc(DisasContext *ctx) int32_t address, const16; int8_t b, const4; int32_t bpos; - TCGv temp, temp2, temp3; + TCGv_i32 temp, temp2, temp3; op1 = MASK_OP_MAJOR(ctx->opcode); @@ -8046,7 +8082,7 @@ static void decode_32Bit_opc(DisasContext *ctx) address = MASK_OP_ABS_OFF18(ctx->opcode); r1 = MASK_OP_ABS_S1D(ctx->opcode); temp = tcg_constant_i32(EA_ABS_FORMAT(address)); - temp2 = tcg_temp_new(); + temp2 = tcg_temp_new_i32(); tcg_gen_shri_i32(temp2, cpu_gpr_d[r1], 16); tcg_gen_qemu_st_i32(temp2, temp, ctx->mem_idx, MO_LEUW); @@ -8081,7 +8117,7 @@ static void decode_32Bit_opc(DisasContext *ctx) bpos = MASK_OP_ABSB_BPOS(ctx->opcode); temp = tcg_constant_i32(EA_ABS_FORMAT(address)); - temp2 = tcg_temp_new(); + temp2 = tcg_temp_new_i32(); tcg_gen_qemu_ld_i32(temp2, temp, ctx->mem_idx, MO_UB); tcg_gen_andi_i32(temp2, temp2, ~(0x1u << bpos)); @@ -8208,8 +8244,8 @@ static void decode_32Bit_opc(DisasContext *ctx) r3 = MASK_OP_RCRR_D(ctx->opcode); const16 = MASK_OP_RCRR_CONST4(ctx->opcode); temp = tcg_constant_i32(const16); - temp2 = tcg_temp_new(); /* width*/ - temp3 = tcg_temp_new(); /* pos */ + temp2 = tcg_temp_new_i32(); /* width*/ + temp3 = tcg_temp_new_i32(); /* pos */ CHECK_REG_PAIR(r2); From 2b6cea17be35d46021ecd9b9dcceaed7b6200dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Oct 2025 12:08:31 +0200 Subject: [PATCH 1669/1794] target/xtensa: Replace legacy cpu_physical_memory_[un]map() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit b7ecba0f6f6 ("docs/devel/loads-stores.rst: Document our various load and store APIs") mentioned cpu_physical_memory_*() methods are legacy, the replacement being address_space_*(). Replace the *_map() / *_unmap() methods in the SIMCALL helper, using the vCPU default address space. No behavioral change expected. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20251002145742.75624-6-philmd@linaro.org> Reviewed-by: Manos Pitsidianakis --- target/xtensa/xtensa-semi.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/target/xtensa/xtensa-semi.c b/target/xtensa/xtensa-semi.c index 636f421da2b45..431c263dc57db 100644 --- a/target/xtensa/xtensa-semi.c +++ b/target/xtensa/xtensa-semi.c @@ -32,6 +32,7 @@ #include "exec/target_page.h" #include "semihosting/semihost.h" #include "semihosting/uaccess.h" +#include "system/memory.h" #include "qapi/error.h" #include "qemu/log.h" @@ -192,7 +193,9 @@ void xtensa_sim_open_console(Chardev *chr) void HELPER(simcall)(CPUXtensaState *env) { + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; CPUState *cs = env_cpu(env); + AddressSpace *as = cs->as; uint32_t *regs = env->regs; switch (regs[2]) { @@ -215,7 +218,7 @@ void HELPER(simcall)(CPUXtensaState *env) TARGET_PAGE_SIZE - (vaddr & (TARGET_PAGE_SIZE - 1)); uint32_t io_sz = page_left < len ? page_left : len; hwaddr sz = io_sz; - void *buf = cpu_physical_memory_map(paddr, &sz, !is_write); + void *buf = address_space_map(as, paddr, &sz, !is_write, attrs); uint32_t io_done; bool error = false; @@ -261,7 +264,7 @@ void HELPER(simcall)(CPUXtensaState *env) error = true; io_done = 0; } - cpu_physical_memory_unmap(buf, sz, !is_write, io_done); + address_space_unmap(as, buf, sz, !is_write, io_done); } else { error = true; regs[3] = TARGET_EINVAL; @@ -408,11 +411,11 @@ void HELPER(simcall)(CPUXtensaState *env) while (sz) { hwaddr len = sz; - void *buf = cpu_physical_memory_map(base, &len, 1); + void *buf = address_space_map(as, base, &len, true, attrs); if (buf && len) { memset(buf, regs[4], len); - cpu_physical_memory_unmap(buf, len, 1, len); + address_space_unmap(as, buf, len, true, len); } else { len = 1; } From fead65d20513f005fcc589eab744f6aa88c60f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 06:28:10 +0200 Subject: [PATCH 1670/1794] target/xtensa: Remove target_ulong use in xtensa_tr_translate_insn() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 85c19af63e7 ("include/exec: Use vaddr in DisasContextBase for virtual addresses") the DisasContextBase::pc_first field is a vaddr type. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20251008051529.86378-2-philmd@linaro.org> --- target/xtensa/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 34ae2f4e16241..bb8d2ed86cf5e 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -1166,7 +1166,7 @@ static void xtensa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); CPUXtensaState *env = cpu_env(cpu); - target_ulong page_start; + vaddr page_start; /* These two conditions only apply to the first insn in the TB, but this is the first TranslateOps hook that allows exiting. */ From 91edb16601da768dc443a46fb8bcb77900f18864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 06:26:50 +0200 Subject: [PATCH 1671/1794] target/xtensa: Remove target_ulong use in xtensa_get_tb_cpu_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit bb5de52524c ("target: Widen pc/cs_base in cpu_get_tb_cpu_state"), cpu_get_tb_cpu_state() expects a uint64_t type for cs_base. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20251008051529.86378-3-philmd@linaro.org> --- target/xtensa/cpu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index ea9b6df3aa241..1eeed44e33663 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -59,13 +59,13 @@ static TCGTBCPUState xtensa_get_tb_cpu_state(CPUState *cs) { CPUXtensaState *env = cpu_env(cs); uint32_t flags = 0; - target_ulong cs_base = 0; + uint64_t cs_base = 0; flags |= xtensa_get_ring(env); if (env->sregs[PS] & PS_EXCM) { flags |= XTENSA_TBFLAG_EXCM; } else if (xtensa_option_enabled(env->config, XTENSA_OPTION_LOOP)) { - target_ulong lend_dist = + uint64_t lend_dist = env->sregs[LEND] - (env->pc & -(1u << TARGET_PAGE_BITS)); /* @@ -83,7 +83,7 @@ static TCGTBCPUState xtensa_get_tb_cpu_state(CPUState *cs) * for the TB that contains this instruction. */ if (lend_dist < (1u << TARGET_PAGE_BITS) + env->config->max_insn_size) { - target_ulong lbeg_off = env->sregs[LEND] - env->sregs[LBEG]; + uint64_t lbeg_off = env->sregs[LEND] - env->sregs[LBEG]; cs_base = lend_dist; if (lbeg_off < 256) { From 91fc6d8101de97c588e0a4263cf4f6148b3e702a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 6 Oct 2025 17:36:31 +0200 Subject: [PATCH 1672/1794] linux-user/microblaze: Fix little-endianness binary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MicroBlaze CPU model has a "little-endian" property, pointing to the @endi internal field. Commit c36ec3a9655 ("hw/microblaze: Explicit CPU endianness") took care of having all MicroBlaze boards with an explicit default endianness, so later commit 415aae543ed ("target/microblaze: Consider endianness while translating code") could infer the endianness at runtime from the @endi field, and not a compile time via the TARGET_BIG_ENDIAN definition. Doing so, we forgot to make the endianness explicit on user emulation, so there all CPUs are started with the default "little-endian=off" value, leading to breaking support for little endian binaries: $ readelf -h ./hello-world-mbel ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian $ qemu-microblazeel ./hello-world-mbel qemu: uncaught target signal 11 (Segmentation fault) - core dumped Segmentation fault (core dumped) Fix by restoring the previous behavior of starting with the builtin endianness of the binary: $ qemu-microblazeel ./hello-world-mbel Hello World Cc: qemu-stable@nongnu.org Fixes: 415aae543ed ("target/microblaze: Consider endianness while translating code") Reported-by: Edgar E. Iglesias Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20251006173350.17455-1-philmd@linaro.org> --- linux-user/microblaze/elfload.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/linux-user/microblaze/elfload.c b/linux-user/microblaze/elfload.c index 7eb1b26d17099..bdc0a953d593d 100644 --- a/linux-user/microblaze/elfload.c +++ b/linux-user/microblaze/elfload.c @@ -8,7 +8,8 @@ const char *get_elf_cpu_model(uint32_t eflags) { - return "any"; + return TARGET_BIG_ENDIAN ? "any,little-endian=off" + : "any,little-endian=on"; } void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) From 8dd133fa85bb49e2e61c489a566ab8460478dc24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 12:38:19 +0200 Subject: [PATCH 1673/1794] =?UTF-8?q?mailmap:=20Unify=20Cl=C3=A9ment=20Mat?= =?UTF-8?q?hieu--Drif=20emails?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not let git-shortlog make distinction between: . Clément Mathieu--Drif . Clement Mathieu--Drif . CLEMENT MATHIEU--DRIF as this is the same person. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Clément Mathieu--Drif Message-Id: <20251009070512.8736-3-philmd@linaro.org> --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index e7271852dc6af..15bec72470ead 100644 --- a/.mailmap +++ b/.mailmap @@ -136,6 +136,7 @@ Chen Gang Chen Gang Chen Wei-Ren Christophe Lyon +Clément Mathieu--Drif Collin L. Walling Daniel P. Berrangé Eduardo Otubo From 9b03e9cf06b33192d550564169f36eee07cf9fd0 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 8 Oct 2025 15:19:36 +0200 Subject: [PATCH 1674/1794] python/qemu: Replace some remaining "avocados" with "functional tests" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The avocado tests have been replaced by the new functional tests, so also update this in the README.rst files in the python directory accordingly. Reviewed-by: Daniel P. Berrangé Reviewed-by: Michael Tokarev Reviewed-by: Alex Bennée Signed-off-by: Thomas Huth Message-ID: <20251008131936.71160-1-thuth@redhat.com> --- python/qemu/machine/README.rst | 2 +- python/qemu/utils/README.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/qemu/machine/README.rst b/python/qemu/machine/README.rst index 8de2c3d77222e..6554c69320137 100644 --- a/python/qemu/machine/README.rst +++ b/python/qemu/machine/README.rst @@ -2,7 +2,7 @@ qemu.machine package ==================== This package provides core utilities used for testing and debugging -QEMU. It is used by the iotests, vm tests, avocado tests, and several +QEMU. It is used by the iotests, vm tests, functional tests, and several other utilities in the ./scripts directory. It is not a fully-fledged SDK and it is subject to change at any time. diff --git a/python/qemu/utils/README.rst b/python/qemu/utils/README.rst index d5f2da14540bc..5027f0b5f1188 100644 --- a/python/qemu/utils/README.rst +++ b/python/qemu/utils/README.rst @@ -2,6 +2,6 @@ qemu.utils package ================== This package provides miscellaneous utilities used for testing and -debugging QEMU. It is used primarily by the vm and avocado tests. +debugging QEMU. It is used primarily by the vm and functional tests. See the documentation in ``__init__.py`` for more information. From dadd7419d361f8f9f012cbc1413993132cd55b7a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 6 Oct 2025 18:18:50 +0200 Subject: [PATCH 1675/1794] tests/functional/aarch64: Drop some sbsaref_alpine tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test_sbsaref_alpine is one of the longest running test in our testsuite, because it does a full Linux boot a couple of times, for various different CPU configurations. That's quite a lot of testing each time, for a rather small additional test coverage. Thus let's drop some of the tests that don't provide much in addition to the other ones. Suggested-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20251006161850.181998-1-thuth@redhat.com> --- tests/functional/aarch64/test_sbsaref_alpine.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/functional/aarch64/test_sbsaref_alpine.py b/tests/functional/aarch64/test_sbsaref_alpine.py index abb8f5114bd59..be84b7adb0c75 100755 --- a/tests/functional/aarch64/test_sbsaref_alpine.py +++ b/tests/functional/aarch64/test_sbsaref_alpine.py @@ -41,15 +41,9 @@ def boot_alpine_linux(self, cpu=None): self.vm.launch() wait_for_console_pattern(self, "Welcome to Alpine Linux 3.17") - def test_sbsaref_alpine_linux_cortex_a57(self): - self.boot_alpine_linux("cortex-a57") - def test_sbsaref_alpine_linux_default_cpu(self): self.boot_alpine_linux() - def test_sbsaref_alpine_linux_max_pauth_off(self): - self.boot_alpine_linux("max,pauth=off") - def test_sbsaref_alpine_linux_max_pauth_impdef(self): self.boot_alpine_linux("max,pauth-impdef=on") From 1d7fdee97b8959871d4093d89a79192155ca674f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 10 Oct 2025 17:05:45 +0100 Subject: [PATCH 1676/1794] gitlab: purge msys pacman cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the Windows msys2 CI job we install many packages using pacman and use the GitLab cache to preserve the pacman cache across CI runs. While metadata still needs downloading, this avoids pacman re-downloading packages from msys2 if they have not changed. The problem is that pacman never automatically purges anything from its package cache. Thus the GitLab cache is growing without bound and packing/unpacking the cache is consuming an increasing amount of time in the CI job. If we run 'pacman -Sc' /after/ installing our desired package set, it will purge any cached downloaded packages that are not matching any installed package. This will (currently) cap the pacman download cache at approx 256 MB. Signed-off-by: Daniel P. Berrangé Reviewed-by: Yonggang Luo Reviewed-by: Thomas Huth Tested-by: Thomas Huth Reviewed-by: Peter Maydell Message-ID: <20251010160545.144760-1-berrange@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/windows.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index 1e6a01bd9ac87..6e1135d8b8675 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -87,6 +87,7 @@ msys2-64bit: mingw-w64-x86_64-pkgconf mingw-w64-x86_64-python mingw-w64-x86_64-zstd" + - .\msys64\usr\bin\bash -lc "pacman -Sc --noconfirm" - Write-Output "Running build at $(Get-Date -Format u)" - $env:JOBS = $(.\msys64\usr\bin\bash -lc nproc) - $env:CHERE_INVOKING = 'yes' # Preserve the current working directory From cb1379ce646e9f41b096c4d7590e558393e6e960 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 14 Oct 2025 10:34:23 +0200 Subject: [PATCH 1677/1794] tests/functional: Set current time stamp of assets when using them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are going to remove obsolete assets from the cache, so keep the time stamps of the assets that we use up-to-date to have a way to detect stale assets later. Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth Message-ID: <20251014083424.103202-2-thuth@redhat.com> --- tests/functional/qemu_test/asset.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index f666125bfaf9e..ab3a7bb591d0a 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -10,6 +10,7 @@ import os import stat import sys +import time import unittest import urllib.request from time import sleep @@ -113,6 +114,16 @@ def _wait_for_other_download(self, tmp_cache_file): self.log.debug("Time out while waiting for %s!", tmp_cache_file) raise + def _save_time_stamp(self): + ''' + Update the time stamp of the asset in the cache. Unfortunately, we + cannot use the modification or access time of the asset file itself, + since e.g. the functional jobs in the gitlab CI reload the files + from the gitlab cache and thus always have recent file time stamps, + so we have to save our asset time stamp to a separate file instead. + ''' + self.cache_file.with_suffix(".stamp").write_text(f"{int(time.time())}") + def fetch(self): if not self.cache_dir.exists(): self.cache_dir.mkdir(parents=True, exist_ok=True) @@ -120,6 +131,7 @@ def fetch(self): if self.valid(): self.log.debug("Using cached asset %s for %s", self.cache_file, self.url) + self._save_time_stamp() return str(self.cache_file) if not self.fetchable(): @@ -208,6 +220,7 @@ def fetch(self): tmp_cache_file.unlink() raise AssetError(self, "Hash does not match %s" % self.hash) tmp_cache_file.replace(self.cache_file) + self._save_time_stamp() # Remove write perms to stop tests accidentally modifying them os.chmod(self.cache_file, stat.S_IRUSR | stat.S_IRGRP) From dd10dbe61751dc85a4d84ffde73f341ab027eb73 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 14 Oct 2025 10:34:24 +0200 Subject: [PATCH 1678/1794] tests: Evict stale files in the functional download cache after a while MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The download cache of the functional tests is currently only growing. But sometimes tests get removed or changed to use different assets, thus we should clean up the stale old assets after a while when they are not in use anymore. So add a script that looks at the time stamps of the assets and removes them if they haven't been touched for more than half of a year. Since there might also be some assets around that have been added to the cache before we added the time stamp files, assume a default time stamp that is close to the creation date of this patch, so that we don't delete these files too early (so we still have all assets around in case we have to bisect an issue in the recent past of QEMU). Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth Message-ID: <20251014083424.103202-3-thuth@redhat.com> --- MAINTAINERS | 1 + scripts/clean_functional_cache.py | 45 +++++++++++++++++++++++++++++++ tests/Makefile.include | 1 + 3 files changed, 47 insertions(+) create mode 100755 scripts/clean_functional_cache.py diff --git a/MAINTAINERS b/MAINTAINERS index 0c766961f39aa..667acd933c7fe 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4397,6 +4397,7 @@ M: Thomas Huth R: Philippe Mathieu-Daudé R: Daniel P. Berrange F: docs/devel/testing/functional.rst +F: scripts/clean_functional_cache.py F: tests/functional/qemu_test/ Windows Hosted Continuous Integration diff --git a/scripts/clean_functional_cache.py b/scripts/clean_functional_cache.py new file mode 100755 index 0000000000000..c3370ffbb8704 --- /dev/null +++ b/scripts/clean_functional_cache.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +"""Delete stale assets from the download cache of the functional tests""" + +import os +import stat +import sys +import time +from pathlib import Path + + +cache_dir_env = os.getenv('QEMU_TEST_CACHE_DIR') +if cache_dir_env: + cache_dir = Path(cache_dir_env, "download") +else: + cache_dir = Path(Path("~").expanduser(), ".cache", "qemu", "download") + +if not cache_dir.exists(): + print(f"Cache dir {cache_dir} does not exist!", file=sys.stderr) + sys.exit(1) + +os.chdir(cache_dir) + +for file in cache_dir.iterdir(): + # Only consider the files that use a sha256 as filename: + if len(file.name) != 64: + continue + + try: + timestamp = int(file.with_suffix(".stamp").read_text()) + except FileNotFoundError: + # Assume it's an old file that was already in the cache before we + # added the code for evicting stale assets. Use the release date + # of QEMU v10.1 as a default timestamp. + timestamp = time.mktime((2025, 8, 26, 0, 0, 0, 0, 0, 0)) + + age = time.time() - timestamp + + # Delete files older than half of a year (183 days * 24h * 60m * 60s) + if age > 15811200: + print(f"Removing {cache_dir}/{file.name}.") + file.chmod(stat.S_IWRITE) + file.unlink() diff --git a/tests/Makefile.include b/tests/Makefile.include index e47ef4d45c993..d4dfbf3716d84 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -111,6 +111,7 @@ $(FUNCTIONAL_TARGETS): check-venv .PHONY: check-functional check-functional: check-venv @$(NINJA) precache-functional + @$(PYTHON) $(SRC_PATH)/scripts/clean_functional_cache.py @QEMU_TEST_NO_DOWNLOAD=1 $(MAKE) SPEED=thorough check-func check-func-quick .PHONY: check-func check-func-quick From 420f6c041705ebc9a7faf3cc0fd6deba865694ae Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 10 Oct 2025 16:45:25 +0200 Subject: [PATCH 1679/1794] tests/functional/alpha: Remove superfluous fetch() line from the clipper test The kernel asset is retrieved automatically via the uncompress() line below the fetch(), so the fetch() is simply not necessary here. Reviewed-by: Richard Henderson Signed-off-by: Thomas Huth Message-ID: <20251010144525.842462-1-thuth@redhat.com> --- tests/functional/alpha/test_clipper.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/alpha/test_clipper.py b/tests/functional/alpha/test_clipper.py index c5d7181953152..d2a4c2a4ed94e 100755 --- a/tests/functional/alpha/test_clipper.py +++ b/tests/functional/alpha/test_clipper.py @@ -17,7 +17,6 @@ class AlphaClipperTest(LinuxKernelTest): def test_alpha_clipper(self): self.set_machine('clipper') - kernel_path = self.ASSET_KERNEL.fetch() uncompressed_kernel = self.uncompress(self.ASSET_KERNEL, format="gz") From 5c2a4b59fad21a3e71b7a8c155d374af952b585f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 14 Oct 2025 15:00:46 +0100 Subject: [PATCH 1680/1794] tests/functional: remove use of getLogger in reverse debuging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the gap left by commit 8a44d8c2ac0921c8064fbfd00ef28e3a2588918e Author: Daniel P. Berrangé Date: Fri Sep 12 19:22:00 2025 +0100 tests/functional: use self.log for all logging ensuring that log message from the reverse debugging test actually make it into the logfile on disk. Signed-off-by: Daniel P. Berrangé Message-ID: <20251014140047.385347-2-berrange@redhat.com> Reviewed-by: Thomas Huth Tested-by: Thomas Huth Signed-off-by: Thomas Huth --- tests/functional/reverse_debugging.py | 49 ++++++++++++--------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/tests/functional/reverse_debugging.py b/tests/functional/reverse_debugging.py index 68cfcb398564a..2c37a62cd06ce 100644 --- a/tests/functional/reverse_debugging.py +++ b/tests/functional/reverse_debugging.py @@ -36,14 +36,13 @@ class ReverseDebugging(LinuxKernelTest): STEPS = 10 def run_vm(self, record, shift, args, replay_path, image_path, port): - logger = logging.getLogger('replay') vm = self.get_vm(name='record' if record else 'replay') vm.set_console() if record: - logger.info('recording the execution...') + self.log.info('recording the execution...') mode = 'record' else: - logger.info('replaying the execution...') + self.log.info('replaying the execution...') mode = 'replay' vm.add_args('-gdb', 'tcp::%d' % port, '-S') vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s,rrsnapshot=init' % @@ -68,10 +67,8 @@ def vm_get_icount(vm): def reverse_debugging(self, gdb_arch, shift=7, args=None): from qemu_test import GDB - logger = logging.getLogger('replay') - # create qcow2 for snapshots - logger.info('creating qcow2 image for VM snapshots') + self.log.info('creating qcow2 image for VM snapshots') image_path = os.path.join(self.workdir, 'disk.qcow2') qemu_img = get_qemu_img(self) if qemu_img is None: @@ -79,7 +76,7 @@ def reverse_debugging(self, gdb_arch, shift=7, args=None): 'create the temporary qcow2 image') out = check_output([qemu_img, 'create', '-f', 'qcow2', image_path, '128M'], encoding='utf8') - logger.info("qemu-img: %s" % out) + self.log.info("qemu-img: %s" % out) replay_path = os.path.join(self.workdir, 'replay.bin') @@ -90,7 +87,7 @@ def reverse_debugging(self, gdb_arch, shift=7, args=None): last_icount = self.vm_get_icount(vm) vm.shutdown() - logger.info("recorded log with %s+ steps" % last_icount) + self.log.info("recorded log with %s+ steps" % last_icount) # replay and run debug commands with Ports() as ports: @@ -98,9 +95,9 @@ def reverse_debugging(self, gdb_arch, shift=7, args=None): vm = self.run_vm(False, shift, args, replay_path, image_path, port) try: - logger.info('Connecting to gdbstub...') + self.log.info('Connecting to gdbstub...') self.reverse_debugging_run(vm, port, gdb_arch, last_icount) - logger.info('Test passed.') + self.log.info('Test passed.') except GDB.TimeoutError: # Convert a GDB timeout exception into a unittest failure exception. raise self.failureException("Timeout while connecting to or " @@ -111,8 +108,6 @@ def reverse_debugging(self, gdb_arch, shift=7, args=None): raise def reverse_debugging_run(self, vm, port, gdb_arch, last_icount): - logger = logging.getLogger('replay') - gdb_cmd = os.getenv('QEMU_TEST_GDB') gdb = GDB(gdb_cmd) @@ -135,43 +130,43 @@ def reverse_debugging_run(self, vm, port, gdb_arch, last_icount): gdb.cli("set debug remote 0") - logger.info('stepping forward') + self.log.info('stepping forward') steps = [] # record first instruction addresses for _ in range(self.STEPS): pc = self.get_pc(gdb) - logger.info('saving position %x' % pc) + self.log.info('saving position %x' % pc) steps.append(pc) gdb.cli("stepi") # visit the recorded instruction in reverse order - logger.info('stepping backward') + self.log.info('stepping backward') for addr in steps[::-1]: - logger.info('found position %x' % addr) + self.log.info('found position %x' % addr) gdb.cli("reverse-stepi") pc = self.get_pc(gdb) if pc != addr: - logger.info('Invalid PC (read %x instead of %x)' % (pc, addr)) + self.log.info('Invalid PC (read %x instead of %x)' % (pc, addr)) self.fail('Reverse stepping failed!') # visit the recorded instruction in forward order - logger.info('stepping forward') + self.log.info('stepping forward') for addr in steps: - logger.info('found position %x' % addr) + self.log.info('found position %x' % addr) pc = self.get_pc(gdb) if pc != addr: - logger.info('Invalid PC (read %x instead of %x)' % (pc, addr)) + self.log.info('Invalid PC (read %x instead of %x)' % (pc, addr)) self.fail('Forward stepping failed!') gdb.cli("stepi") # set breakpoints for the instructions just stepped over - logger.info('setting breakpoints') + self.log.info('setting breakpoints') for addr in steps: gdb.cli(f"break *{hex(addr)}") # this may hit a breakpoint if first instructions are executed # again - logger.info('continuing execution') + self.log.info('continuing execution') vm.qmp('replay-break', icount=last_icount - 1) # continue - will return after pausing # This can stop at the end of the replay-break and gdb gets a SIGINT, @@ -180,12 +175,12 @@ def reverse_debugging_run(self, vm, port, gdb_arch, last_icount): gdb.cli("continue") if self.vm_get_icount(vm) == last_icount - 1: - logger.info('reached the end (icount %s)' % (last_icount - 1)) + self.log.info('reached the end (icount %s)' % (last_icount - 1)) else: - logger.info('hit a breakpoint again at %x (icount %s)' % + self.log.info('hit a breakpoint again at %x (icount %s)' % (self.get_pc(gdb), self.vm_get_icount(vm))) - logger.info('running reverse continue to reach %x' % steps[-1]) + self.log.info('running reverse continue to reach %x' % steps[-1]) # reverse continue - will return after stopping at the breakpoint gdb.cli("reverse-continue") @@ -195,8 +190,8 @@ def reverse_debugging_run(self, vm, port, gdb_arch, last_icount): if pc != steps[-1]: self.fail("'reverse-continue' did not hit the first PC in reverse order!") - logger.info('successfully reached %x' % steps[-1]) + self.log.info('successfully reached %x' % steps[-1]) - logger.info('exiting gdb and qemu') + self.log.info('exiting gdb and qemu') gdb.exit() vm.shutdown() From 03ec40942d678ef1e98439508d1cdb2c05ff3e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 14 Oct 2025 15:00:47 +0100 Subject: [PATCH 1681/1794] tests/functional: ensure GDB client is stopped on error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the reverse_debugging_run method fails, the GDB client will not be closed resulting in python complaining about resource leaks. Hoisting the GDB client creation into the caller allows this to be cleaned up easily. While doing this, also move the VM shutdown call to match. Signed-off-by: Daniel P. Berrangé Message-ID: <20251014140047.385347-3-berrange@redhat.com> Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- tests/functional/reverse_debugging.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/functional/reverse_debugging.py b/tests/functional/reverse_debugging.py index 2c37a62cd06ce..86fca8d81f1c1 100644 --- a/tests/functional/reverse_debugging.py +++ b/tests/functional/reverse_debugging.py @@ -96,7 +96,14 @@ def reverse_debugging(self, gdb_arch, shift=7, args=None): try: self.log.info('Connecting to gdbstub...') - self.reverse_debugging_run(vm, port, gdb_arch, last_icount) + gdb_cmd = os.getenv('QEMU_TEST_GDB') + gdb = GDB(gdb_cmd) + try: + self.reverse_debugging_run(gdb, vm, port, gdb_arch, last_icount) + finally: + self.log.info('exiting gdb and qemu') + gdb.exit() + vm.shutdown() self.log.info('Test passed.') except GDB.TimeoutError: # Convert a GDB timeout exception into a unittest failure exception. @@ -107,10 +114,7 @@ def reverse_debugging(self, gdb_arch, shift=7, args=None): # skipTest(), etc. raise - def reverse_debugging_run(self, vm, port, gdb_arch, last_icount): - gdb_cmd = os.getenv('QEMU_TEST_GDB') - gdb = GDB(gdb_cmd) - + def reverse_debugging_run(self, gdb, vm, port, gdb_arch, last_icount): r = gdb.cli("set architecture").get_log() if gdb_arch not in r: self.skipTest(f"GDB does not support arch '{gdb_arch}'") @@ -191,7 +195,3 @@ def reverse_debugging_run(self, vm, port, gdb_arch, last_icount): self.fail("'reverse-continue' did not hit the first PC in reverse order!") self.log.info('successfully reached %x' % steps[-1]) - - self.log.info('exiting gdb and qemu') - gdb.exit() - vm.shutdown() From 02cf15e9bbe902523f5a1deb1dd48aef50e26e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 16:14:08 +0200 Subject: [PATCH 1682/1794] target/s390x/mmu_helper: Simplify s390_cpu_virt_mem_rw() logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to simplify the next commit, move the trigger_access_exception() call after the address_space_rw() calls. No logical change intended. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20251008141410.99865-2-philmd@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/mmu_helper.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index 487c41bf933f5..22d3d4a97dfd3 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -541,9 +541,7 @@ int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf, pages = g_malloc(nr_pages * sizeof(*pages)); ret = translate_pages(cpu, laddr, nr_pages, pages, is_write, &tec); - if (ret) { - trigger_access_exception(&cpu->env, ret, tec); - } else if (hostbuf != NULL) { + if (ret == 0 && hostbuf != NULL) { AddressSpace *as = CPU(cpu)->as; /* Copy data by stepping through the area page by page */ @@ -556,6 +554,9 @@ int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf, len -= currlen; } } + if (ret) { + trigger_access_exception(&cpu->env, ret, tec); + } g_free(pages); return ret; From d6f7f9254e333c56226bb7051e74ea57daea2fff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 16:14:09 +0200 Subject: [PATCH 1683/1794] target/s390x/mmu_helper: Do not ignore address_space_rw() errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a address_space_rw() call ever fails, break the loop and return the PGM_ADDRESSING error (after triggering an access exception). Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20251008141410.99865-3-philmd@linaro.org> Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- target/s390x/mmu_helper.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index 22d3d4a97dfd3..3b1e75f78336c 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -546,9 +546,15 @@ int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf, /* Copy data by stepping through the area page by page */ for (i = 0; i < nr_pages; i++) { + MemTxResult res; + currlen = MIN(len, TARGET_PAGE_SIZE - (laddr % TARGET_PAGE_SIZE)); - address_space_rw(as, pages[i] | (laddr & ~TARGET_PAGE_MASK), - attrs, hostbuf, currlen, is_write); + res = address_space_rw(as, pages[i] | (laddr & ~TARGET_PAGE_MASK), + attrs, hostbuf, currlen, is_write); + if (res != MEMTX_OK) { + ret = PGM_ADDRESSING; + break; + } laddr += currlen; hostbuf += currlen; len -= currlen; From 3bf9701ccd4c8a078c7ef016e87240f11a4754d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 21:52:05 +0200 Subject: [PATCH 1684/1794] gitlab: Stop cross-testing for 32-bit MIPS hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 32-bit host support is deprecated since commit 6d701c9bac1 ("meson: Deprecate 32-bit host support"). Next commits will remove support for 32-bit MIPS hosts. Stop cross-building QEMU on our CI. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Signed-off-by: Richard Henderson Message-ID: <20251009195210.33161-3-philmd@linaro.org> --- .gitlab-ci.d/container-cross.yml | 6 ------ .gitlab-ci.d/crossbuilds.yml | 14 -------------- 2 files changed, 20 deletions(-) diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index 8d3be53b75b23..0fd7341afac01 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -52,12 +52,6 @@ mips64el-debian-cross-container: variables: NAME: debian-mips64el-cross -mipsel-debian-cross-container: - extends: .container_job_template - stage: containers - variables: - NAME: debian-mipsel-cross - ppc64el-debian-cross-container: extends: .container_job_template stage: containers diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml index 8ff0c27f74d65..99dfa7eea6fc4 100644 --- a/.gitlab-ci.d/crossbuilds.yml +++ b/.gitlab-ci.d/crossbuilds.yml @@ -68,20 +68,6 @@ cross-i686-tci: # would otherwise be using a parallelism of 9. MAKE_CHECK_ARGS: check check-tcg -j2 -cross-mipsel-system: - extends: .cross_system_build_job - needs: - - job: mipsel-debian-cross-container - variables: - IMAGE: debian-mipsel-cross - -cross-mipsel-user: - extends: .cross_user_build_job - needs: - - job: mipsel-debian-cross-container - variables: - IMAGE: debian-mipsel-cross - cross-mips64el-system: extends: .cross_system_build_job needs: From 269ffaabc84e5c03226d7845318ca28eb3f180be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 21:52:10 +0200 Subject: [PATCH 1685/1794] buildsys: Remove support for 32-bit MIPS hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop detecting 32-bit MIPS host as supported, update the deprecation document. See previous commit for rationale. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Signed-off-by: Richard Henderson Message-ID: <20251009195210.33161-8-philmd@linaro.org> --- configure | 7 ------- docs/about/deprecated.rst | 13 +++++-------- docs/about/removed-features.rst | 6 ++++++ 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/configure b/configure index 461b53dd60520..8263f8137030d 100755 --- a/configure +++ b/configure @@ -404,8 +404,6 @@ elif check_define _ARCH_PPC ; then elif check_define __mips__ ; then if check_define __mips64 ; then cpu="mips64" - else - cpu="mips" fi elif check_define __s390__ ; then if check_define __s390x__ ; then @@ -473,11 +471,6 @@ case "$cpu" in host_arch=mips linux_arch=mips ;; - mips*) - cpu=mips - host_arch=mips - linux_arch=mips - ;; ppc) host_arch=ppc diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 98361f5832dc5..dacf2882e445e 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -172,17 +172,14 @@ This argument has always been ignored. Host Architectures ------------------ -Big endian MIPS since 7.2; 32-bit little endian MIPS since 9.2, MIPS since 11.0 -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +MIPS (since 11.0) +''''''''''''''''' -As Debian 10 ("Buster") moved into LTS the big endian 32 bit version of -MIPS moved out of support making it hard to maintain our -cross-compilation CI tests of the architecture. As we no longer have -CI coverage support may bitrot away before the deprecation process +MIPS is not supported by Debian 13 ("Trixie") and newer, making it hard to +maintain our cross-compilation CI tests of the architecture. As we no longer +have CI coverage support may bitrot away before the deprecation process completes. -Likewise, MIPS is not supported by Debian 13 ("Trixie") and newer. - System emulation on 32-bit x86 hosts (since 8.0) '''''''''''''''''''''''''''''''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index a5338e44c244e..53829f59e65fb 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -896,6 +896,12 @@ work around the atomicity issues in system mode by running all vCPUs in a single thread context; in user mode atomicity was simply broken. From 10.0, QEMU has disabled configuration of 64-bit guests on 32-bit hosts. +32-bit MIPS (since 11.0) +'''''''''''''''''''''''' + +Debian 12 "Bookworm" removed support for 32-bit MIPS, making it hard to +maintain our cross-compilation CI tests of the architecture. + Guest Emulator ISAs ------------------- From 4384542a575a87d31405ec2f4ee1bbab2b77a0bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 21:52:09 +0200 Subject: [PATCH 1686/1794] kvm/mips: Remove support for 32-bit hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See previous commit for rationale. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Signed-off-by: Richard Henderson Message-ID: <20251009195210.33161-7-philmd@linaro.org> --- meson.build | 2 -- 1 file changed, 2 deletions(-) diff --git a/meson.build b/meson.build index afaefa0172224..c5710a6a47c2b 100644 --- a/meson.build +++ b/meson.build @@ -295,8 +295,6 @@ elif cpu == 'ppc' kvm_targets = ['ppc-softmmu'] elif cpu == 'ppc64' kvm_targets = ['ppc-softmmu', 'ppc64-softmmu'] -elif cpu == 'mips' - kvm_targets = ['mips-softmmu', 'mipsel-softmmu'] elif cpu == 'mips64' kvm_targets = ['mips-softmmu', 'mipsel-softmmu', 'mips64-softmmu', 'mips64el-softmmu'] elif cpu == 'riscv32' From affedc6787a99f9d4f62da2a2cb80658615755b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 21:52:07 +0200 Subject: [PATCH 1687/1794] tcg/mips: Remove support for O32 and N32 ABIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See previous commit for rationale. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Signed-off-by: Richard Henderson Message-ID: <20251009195210.33161-5-philmd@linaro.org> --- common-user/host/mips/safe-syscall.inc.S | 35 ------------------------ tcg/mips/tcg-target-reg-bits.h | 8 ++---- tcg/mips/tcg-target.c.inc | 26 ++++-------------- 3 files changed, 9 insertions(+), 60 deletions(-) diff --git a/common-user/host/mips/safe-syscall.inc.S b/common-user/host/mips/safe-syscall.inc.S index 6a446149704e4..3b196cc634c4e 100644 --- a/common-user/host/mips/safe-syscall.inc.S +++ b/common-user/host/mips/safe-syscall.inc.S @@ -30,15 +30,9 @@ * arguments being syscall arguments (also 'long'). */ -#if _MIPS_SIM == _ABIO32 -/* 8 * 4 = 32 for outgoing parameters; 1 * 4 for s0 save; 1 * 4 for align. */ -#define FRAME 40 -#define OFS_S0 32 -#else /* 1 * 8 for s0 save; 1 * 8 for align. */ #define FRAME 16 #define OFS_S0 0 -#endif NESTED(safe_syscall_base, FRAME, ra) @@ -47,34 +41,6 @@ NESTED(safe_syscall_base, FRAME, ra) .cfi_adjust_cfa_offset FRAME REG_S s0, OFS_S0(sp) .cfi_rel_offset s0, OFS_S0 -#if _MIPS_SIM == _ABIO32 - /* - * The syscall calling convention is nearly the same as C: - * we enter with a0 == &signal_pending - * a1 == syscall number - * a2, a3, stack == syscall arguments - * and return the result in a0 - * and the syscall instruction needs - * v0 == syscall number - * a0 ... a3, stack == syscall arguments - * and returns the result in v0 - * Shuffle everything around appropriately. - */ - move s0, a0 /* signal_pending pointer */ - move v0, a1 /* syscall number */ - move a0, a2 /* syscall arguments */ - move a1, a3 - lw a2, FRAME+16(sp) - lw a3, FRAME+20(sp) - lw t4, FRAME+24(sp) - lw t5, FRAME+28(sp) - lw t6, FRAME+32(sp) - lw t7, FRAME+40(sp) - sw t4, 16(sp) - sw t5, 20(sp) - sw t6, 24(sp) - sw t7, 28(sp) -#else /* * The syscall calling convention is nearly the same as C: * we enter with a0 == &signal_pending @@ -95,7 +61,6 @@ NESTED(safe_syscall_base, FRAME, ra) move a3, a5 move a4, a6 move a5, a7 -#endif /* * This next sequence of code works in conjunction with the diff --git a/tcg/mips/tcg-target-reg-bits.h b/tcg/mips/tcg-target-reg-bits.h index 56fe0a725e9dd..ee346a3f25608 100644 --- a/tcg/mips/tcg-target-reg-bits.h +++ b/tcg/mips/tcg-target-reg-bits.h @@ -7,12 +7,10 @@ #ifndef TCG_TARGET_REG_BITS_H #define TCG_TARGET_REG_BITS_H -#if _MIPS_SIM == _ABIO32 -# define TCG_TARGET_REG_BITS 32 -#elif _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 -# define TCG_TARGET_REG_BITS 64 -#else +#if !defined(_MIPS_SIM) || _MIPS_SIM != _ABI64 # error "Unknown ABI" #endif +#define TCG_TARGET_REG_BITS 64 + #endif diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 400eafbab4b6f..4d9d029844f9a 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -26,16 +26,10 @@ /* used for function call generation */ #define TCG_TARGET_STACK_ALIGN 16 -#if _MIPS_SIM == _ABIO32 -# define TCG_TARGET_CALL_STACK_OFFSET 16 -# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_EVEN -# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_REF -#else -# define TCG_TARGET_CALL_STACK_OFFSET 0 -# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL -# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL -#endif +#define TCG_TARGET_CALL_STACK_OFFSET 0 #define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL #define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN #if TCG_TARGET_REG_BITS == 32 @@ -135,12 +129,10 @@ static const TCGReg tcg_target_call_iarg_regs[] = { TCG_REG_A1, TCG_REG_A2, TCG_REG_A3, -#if _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 TCG_REG_T0, TCG_REG_T1, TCG_REG_T2, TCG_REG_T3, -#endif }; static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) @@ -1053,17 +1045,11 @@ static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *arg, bool tail) * Note that __mips_abicalls requires the called function's address * to be loaded into $25 (t9), even if a direct branch is in range. * - * For n64, always drop the pointer into the constant pool. - * We can re-use helper addresses often and do not want any - * of the longer sequences tcg_out_movi may try. + * We can re-use helper addresses often; always drop the pointer + * into the constant pool. */ - if (sizeof(uintptr_t) == 8) { - tcg_out_movi_pool(s, TCG_REG_T9, (uintptr_t)arg, TCG_REG_TB); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_T9, (uintptr_t)arg); - } + tcg_out_movi_pool(s, TCG_REG_T9, (uintptr_t)arg, TCG_REG_TB); - /* But do try a direct branch, allowing the cpu better insn prefetch. */ if (tail) { if (!tcg_out_opc_jmp(s, OPC_J, arg)) { tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_T9, 0); From f032bff1aec890e93264ec7c90773fa790540bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Oct 2025 21:52:08 +0200 Subject: [PATCH 1688/1794] tcg/mips: Remove support for 32-bit hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 32-bit host support is deprecated since commit 6d701c9bac1 ("meson: Deprecate 32-bit host support"), released as v10.0. The next release being v10.2, we can remove the TCG backend for 32-bit MIPS hosts. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Signed-off-by: Richard Henderson Message-ID: <20251009195210.33161-6-philmd@linaro.org> --- tcg/mips/tcg-target-has.h | 2 - tcg/mips/tcg-target.c.inc | 285 +++++--------------------------------- 2 files changed, 38 insertions(+), 249 deletions(-) diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index b9eb338528869..88f0145efba88 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -39,11 +39,9 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions */ -#if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_ext32s_i64 1 #define TCG_TARGET_HAS_ext32u_i64 1 -#endif /* optional instructions detected at runtime */ #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 4d9d029844f9a..51a15705cbb54 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -32,15 +32,6 @@ #define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL #define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN -#if TCG_TARGET_REG_BITS == 32 -# define LO_OFF (HOST_BIG_ENDIAN * 4) -# define HI_OFF (4 - LO_OFF) -#else -/* Assert at compile-time that these values are never used for 64-bit. */ -# define LO_OFF ({ qemu_build_not_reached(); 0; }) -# define HI_OFF ({ qemu_build_not_reached(); 0; }) -#endif - #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "zero", @@ -84,11 +75,7 @@ static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { #define TCG_TMP3 TCG_REG_T7 #define TCG_GUEST_BASE_REG TCG_REG_S7 -#if TCG_TARGET_REG_BITS == 64 #define TCG_REG_TB TCG_REG_S6 -#else -#define TCG_REG_TB ({ qemu_build_not_reached(); TCG_REG_ZERO; }) -#endif /* check if we really need so many registers :P */ static const int tcg_target_reg_alloc_order[] = { @@ -559,7 +546,7 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, tcg_target_long tmp; int sh, lo; - if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { + if (type == TCG_TYPE_I32) { arg = (int32_t)arg; } @@ -567,7 +554,6 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, if (tcg_out_movi_two(s, ret, arg)) { return; } - assert(TCG_TARGET_REG_BITS == 64); /* Load addresses within 2GB of TB with 1 or 3 insns. */ tmp = tcg_tbrel_diff(s, (void *)arg); @@ -630,8 +616,7 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg ret, tcg_target_long arg) { - TCGReg tbreg = TCG_TARGET_REG_BITS == 64 ? TCG_REG_TB : 0; - tcg_out_movi_int(s, type, ret, arg, tbreg); + tcg_out_movi_int(s, type, ret, arg, TCG_REG_TB); } static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) @@ -658,7 +643,6 @@ static void tcg_out_ext16u(TCGContext *s, TCGReg rd, TCGReg rs) static void tcg_out_ext32s(TCGContext *s, TCGReg rd, TCGReg rs) { - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); tcg_out_opc_sa(s, OPC_SLL, rd, rs, 0); } @@ -701,7 +685,6 @@ static void tcg_out_bswap_subr(TCGContext *s, const tcg_insn_unit *sub) static void tcg_out_ext32u(TCGContext *s, TCGReg ret, TCGReg arg) { - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); if (use_mips32r2_instructions) { tcg_out_opc_bf(s, OPC_DEXT, ret, arg, 31, 0); } else { @@ -727,20 +710,14 @@ static void tcg_out_ldst(TCGContext *s, MIPSInsn opc, TCGReg data, static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, intptr_t arg2) { - MIPSInsn opc = OPC_LD; - if (TCG_TARGET_REG_BITS == 32 || type == TCG_TYPE_I32) { - opc = OPC_LW; - } + MIPSInsn opc = type == TCG_TYPE_I32 ? OPC_LW : OPC_LD; tcg_out_ldst(s, opc, arg, arg1, arg2); } static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, intptr_t arg2) { - MIPSInsn opc = OPC_SD; - if (TCG_TARGET_REG_BITS == 32 || type == TCG_TYPE_I32) { - opc = OPC_SW; - } + MIPSInsn opc = type == TCG_TYPE_I32 ? OPC_SW : OPC_SD; tcg_out_ldst(s, opc, arg, arg1, arg2); } @@ -918,72 +895,6 @@ void tcg_out_br(TCGContext *s, TCGLabel *l) tgen_brcond(s, TCG_TYPE_I32, TCG_COND_EQ, TCG_REG_ZERO, TCG_REG_ZERO, l); } -static int tcg_out_setcond2_int(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) -{ - int flags = 0; - - switch (cond) { - case TCG_COND_EQ: - flags |= SETCOND_INV; - /* fall through */ - case TCG_COND_NE: - flags |= SETCOND_NEZ; - tcg_out_opc_reg(s, OPC_XOR, TCG_TMP0, al, bl); - tcg_out_opc_reg(s, OPC_XOR, TCG_TMP1, ah, bh); - tcg_out_opc_reg(s, OPC_OR, ret, TCG_TMP0, TCG_TMP1); - break; - - default: - tgen_setcond(s, TCG_TYPE_I32, TCG_COND_EQ, TCG_TMP0, ah, bh); - tgen_setcond(s, TCG_TYPE_I32, tcg_unsigned_cond(cond), - TCG_TMP1, al, bl); - tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, TCG_TMP0); - tgen_setcond(s, TCG_TYPE_I32, tcg_high_cond(cond), TCG_TMP0, ah, bh); - tcg_out_opc_reg(s, OPC_OR, ret, TCG_TMP0, TCG_TMP1); - break; - } - return ret | flags; -} - -static void tgen_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg al, TCGReg ah, - TCGArg bl, bool const_bl, - TCGArg bh, bool const_bh) -{ - int tmpflags = tcg_out_setcond2_int(s, cond, ret, al, ah, bl, bh); - tcg_out_setcond_end(s, ret, tmpflags); -} - -#if TCG_TARGET_REG_BITS != 32 -__attribute__((unused)) -#endif -static const TCGOutOpSetcond2 outop_setcond2 = { - .base.static_constraint = C_O1_I4(r, r, r, rz, rz), - .out = tgen_setcond2, -}; - -static void tgen_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, - TCGArg bl, bool const_bl, - TCGArg bh, bool const_bh, TCGLabel *l) -{ - int tmpflags = tcg_out_setcond2_int(s, cond, TCG_TMP0, al, ah, bl, bh); - TCGReg tmp = tmpflags & ~SETCOND_FLAGS; - MIPSInsn b_opc = tmpflags & SETCOND_INV ? OPC_BEQ : OPC_BNE; - - tcg_out_reloc(s, s->code_ptr, R_MIPS_PC16, l, 0); - tcg_out_opc_br(s, b_opc, tmp, TCG_REG_ZERO); - tcg_out_nop(s); -} - -#if TCG_TARGET_REG_BITS != 32 -__attribute__((unused)) -#endif -static const TCGOutOpBrcond2 outop_brcond2 = { - .base.static_constraint = C_O0_I4(r, r, rz, rz), - .out = tgen_brcond2, -}; - static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, TCGReg ret, TCGReg c1, TCGArg c2, bool const_c2, TCGArg v1, bool const_v1, TCGArg v2, bool const_v2) @@ -1183,7 +1094,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP1, TCG_AREG0, table_off); /* Extract the TLB index from the address into TMP3. */ - if (TCG_TARGET_REG_BITS == 32 || addr_type == TCG_TYPE_I32) { + if (addr_type == TCG_TYPE_I32) { tcg_out_opc_sa(s, OPC_SRL, TCG_TMP3, addr, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); } else { @@ -1195,7 +1106,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_opc_reg(s, ALIAS_PADD, TCG_TMP3, TCG_TMP3, TCG_TMP1); /* Load the tlb comparator. */ - if (TCG_TARGET_REG_BITS == 64 && addr_type == TCG_TYPE_I32) { + if (addr_type == TCG_TYPE_I32) { tcg_out_ld(s, TCG_TYPE_I32, TCG_TMP0, TCG_TMP3, cmp_off + HOST_BIG_ENDIAN * 4); } else { @@ -1212,8 +1123,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, */ tcg_out_movi(s, addr_type, TCG_TMP1, TARGET_PAGE_MASK | a_mask); if (a_mask < s_mask) { - tcg_out_opc_imm(s, (TCG_TARGET_REG_BITS == 32 - || addr_type == TCG_TYPE_I32 + tcg_out_opc_imm(s, (addr_type == TCG_TYPE_I32 ? OPC_ADDIU : OPC_DADDIU), TCG_TMP2, addr, s_mask - a_mask); tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, TCG_TMP2); @@ -1222,7 +1132,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, } /* Zero extend a 32-bit guest address for a 64-bit host. */ - if (TCG_TARGET_REG_BITS == 64 && addr_type == TCG_TYPE_I32) { + if (addr_type == TCG_TYPE_I32) { tcg_out_ext32u(s, TCG_TMP2, addr); addr = TCG_TMP2; } @@ -1255,7 +1165,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, } base = addr; - if (TCG_TARGET_REG_BITS == 64 && addr_type == TCG_TYPE_I32) { + if (addr_type == TCG_TYPE_I32) { tcg_out_ext32u(s, TCG_REG_A0, base); base = TCG_REG_A0; } @@ -1291,7 +1201,7 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg lo, TCGReg hi, tcg_out_opc_imm(s, OPC_LH, lo, base, 0); break; case MO_UL: - if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I64) { + if (type == TCG_TYPE_I64) { tcg_out_opc_imm(s, OPC_LWU, lo, base, 0); break; } @@ -1300,16 +1210,7 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg lo, TCGReg hi, tcg_out_opc_imm(s, OPC_LW, lo, base, 0); break; case MO_UQ: - /* Prefer to load from offset 0 first, but allow for overlap. */ - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_opc_imm(s, OPC_LD, lo, base, 0); - } else if (HOST_BIG_ENDIAN ? hi != base : lo == base) { - tcg_out_opc_imm(s, OPC_LW, hi, base, HI_OFF); - tcg_out_opc_imm(s, OPC_LW, lo, base, LO_OFF); - } else { - tcg_out_opc_imm(s, OPC_LW, lo, base, LO_OFF); - tcg_out_opc_imm(s, OPC_LW, hi, base, HI_OFF); - } + tcg_out_opc_imm(s, OPC_LD, lo, base, 0); break; default: g_assert_not_reached(); @@ -1351,21 +1252,14 @@ static void tcg_out_qemu_ld_unalign(TCGContext *s, TCGReg lo, TCGReg hi, case MO_32: tcg_out_opc_imm(s, lw1, lo, base, 0); tcg_out_opc_imm(s, lw2, lo, base, 3); - if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I64 && !sgn) { + if (type == TCG_TYPE_I64 && !sgn) { tcg_out_ext32u(s, lo, lo); } break; case MO_64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_opc_imm(s, ld1, lo, base, 0); - tcg_out_opc_imm(s, ld2, lo, base, 7); - } else { - tcg_out_opc_imm(s, lw1, HOST_BIG_ENDIAN ? hi : lo, base, 0 + 0); - tcg_out_opc_imm(s, lw2, HOST_BIG_ENDIAN ? hi : lo, base, 0 + 3); - tcg_out_opc_imm(s, lw1, HOST_BIG_ENDIAN ? lo : hi, base, 4 + 0); - tcg_out_opc_imm(s, lw2, HOST_BIG_ENDIAN ? lo : hi, base, 4 + 3); - } + tcg_out_opc_imm(s, ld1, lo, base, 0); + tcg_out_opc_imm(s, ld2, lo, base, 7); break; default: @@ -1401,36 +1295,8 @@ static const TCGOutOpQemuLdSt outop_qemu_ld = { .out = tgen_qemu_ld, }; -static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, - TCGReg datahi, TCGReg addr, MemOpIdx oi) -{ - MemOp opc = get_memop(oi); - TCGLabelQemuLdst *ldst; - HostAddress h; - - tcg_debug_assert(TCG_TARGET_REG_BITS == 32); - ldst = prepare_host_addr(s, &h, addr, oi, true); - - if (use_mips32r6_instructions || h.aa.align >= (opc & MO_SIZE)) { - tcg_out_qemu_ld_direct(s, datalo, datahi, h.base, opc, type); - } else { - tcg_out_qemu_ld_unalign(s, datalo, datahi, h.base, opc, type); - } - - if (ldst) { - ldst->type = type; - ldst->datalo_reg = datalo; - ldst->datahi_reg = datahi; - ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); - } -} - static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { - /* Ensure that the mips32 code is compiled but discarded for mips64. */ - .base.static_constraint = - TCG_TARGET_REG_BITS == 32 ? C_O2_I1(r, r, r) : C_NotImplemented, - .out = - TCG_TARGET_REG_BITS == 32 ? tgen_qemu_ld2 : NULL, + .base.static_constraint = C_NotImplemented, }; static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg lo, TCGReg hi, @@ -1447,12 +1313,7 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg lo, TCGReg hi, tcg_out_opc_imm(s, OPC_SW, lo, base, 0); break; case MO_64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_opc_imm(s, OPC_SD, lo, base, 0); - } else { - tcg_out_opc_imm(s, OPC_SW, HOST_BIG_ENDIAN ? hi : lo, base, 0); - tcg_out_opc_imm(s, OPC_SW, HOST_BIG_ENDIAN ? lo : hi, base, 4); - } + tcg_out_opc_imm(s, OPC_SD, lo, base, 0); break; default: g_assert_not_reached(); @@ -1480,15 +1341,8 @@ static void tcg_out_qemu_st_unalign(TCGContext *s, TCGReg lo, TCGReg hi, break; case MO_64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_opc_imm(s, sd1, lo, base, 0); - tcg_out_opc_imm(s, sd2, lo, base, 7); - } else { - tcg_out_opc_imm(s, sw1, HOST_BIG_ENDIAN ? hi : lo, base, 0 + 0); - tcg_out_opc_imm(s, sw2, HOST_BIG_ENDIAN ? hi : lo, base, 0 + 3); - tcg_out_opc_imm(s, sw1, HOST_BIG_ENDIAN ? lo : hi, base, 4 + 0); - tcg_out_opc_imm(s, sw2, HOST_BIG_ENDIAN ? lo : hi, base, 4 + 3); - } + tcg_out_opc_imm(s, sd1, lo, base, 0); + tcg_out_opc_imm(s, sd2, lo, base, 7); break; default: @@ -1524,36 +1378,8 @@ static const TCGOutOpQemuLdSt outop_qemu_st = { .out = tgen_qemu_st, }; -static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, - TCGReg datahi, TCGReg addr, MemOpIdx oi) -{ - MemOp opc = get_memop(oi); - TCGLabelQemuLdst *ldst; - HostAddress h; - - tcg_debug_assert(TCG_TARGET_REG_BITS == 32); - ldst = prepare_host_addr(s, &h, addr, oi, false); - - if (use_mips32r6_instructions || h.aa.align >= (opc & MO_SIZE)) { - tcg_out_qemu_st_direct(s, datalo, datahi, h.base, opc); - } else { - tcg_out_qemu_st_unalign(s, datalo, datahi, h.base, opc); - } - - if (ldst) { - ldst->type = type; - ldst->datalo_reg = datalo; - ldst->datahi_reg = datahi; - ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); - } -} - static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { - /* Ensure that the mips32 code is compiled but discarded for mips64. */ - .base.static_constraint = - TCG_TARGET_REG_BITS == 32 ? C_O0_I3(rz, rz, r) : C_NotImplemented, - .out = - TCG_TARGET_REG_BITS == 32 ? tgen_qemu_st2 : NULL, + .base.static_constraint = C_NotImplemented, }; static void tcg_out_mb(TCGContext *s, unsigned a0) @@ -1578,22 +1404,14 @@ static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) int16_t lo = 0; if (a0) { - intptr_t ofs; - if (TCG_TARGET_REG_BITS == 64) { - ofs = tcg_tbrel_diff(s, (void *)a0); - lo = ofs; - if (ofs == lo) { - base = TCG_REG_TB; - } else { - base = TCG_REG_V0; - tcg_out_movi(s, TCG_TYPE_PTR, base, ofs - lo); - tcg_out_opc_reg(s, ALIAS_PADD, base, base, TCG_REG_TB); - } + intptr_t ofs = tcg_tbrel_diff(s, (void *)a0); + lo = ofs; + if (ofs == lo) { + base = TCG_REG_TB; } else { - ofs = a0; - lo = ofs; base = TCG_REG_V0; tcg_out_movi(s, TCG_TYPE_PTR, base, ofs - lo); + tcg_out_opc_reg(s, ALIAS_PADD, base, base, TCG_REG_TB); } } if (!tcg_out_opc_jmp(s, OPC_J, tb_ret_addr)) { @@ -1610,35 +1428,24 @@ static void tcg_out_goto_tb(TCGContext *s, int which) TCGReg base, dest; /* indirect jump method */ - if (TCG_TARGET_REG_BITS == 64) { - dest = TCG_REG_TB; - base = TCG_REG_TB; - ofs = tcg_tbrel_diff(s, (void *)ofs); - } else { - dest = TCG_TMP0; - base = TCG_REG_ZERO; - } + dest = TCG_REG_TB; + base = TCG_REG_TB; + ofs = tcg_tbrel_diff(s, (void *)ofs); tcg_out_ld(s, TCG_TYPE_PTR, dest, base, ofs); tcg_out_opc_reg(s, OPC_JR, 0, dest, 0); /* delay slot */ tcg_out_nop(s); set_jmp_reset_offset(s, which); - if (TCG_TARGET_REG_BITS == 64) { - /* For the unlinked case, need to reset TCG_REG_TB. */ - tcg_out_ldst(s, ALIAS_PADDI, TCG_REG_TB, TCG_REG_TB, - -tcg_current_code_size(s)); - } + /* For the unlinked case, need to reset TCG_REG_TB. */ + tcg_out_ldst(s, ALIAS_PADDI, TCG_REG_TB, TCG_REG_TB, + -tcg_current_code_size(s)); } static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) { tcg_out_opc_reg(s, OPC_JR, 0, a0, 0); - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_TB, a0); - } else { - tcg_out_nop(s); - } + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_TB, a0); } void tb_target_set_jmp_target(const TranslationBlock *tb, int n, @@ -1833,7 +1640,6 @@ static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; -#if TCG_TARGET_REG_BITS == 64 static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) { tcg_out_dsra(s, a0, a1, 32); @@ -1843,7 +1649,6 @@ static const TCGOutOpUnary outop_extrh_i64_i32 = { .base.static_constraint = C_O1_I1(r, r), .out_rr = tgen_extrh_i64_i32, }; -#endif static void tgen_mul(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) @@ -2232,7 +2037,6 @@ static const TCGOutOpBswap outop_bswap32 = { .out_rr = tgen_bswap32, }; -#if TCG_TARGET_REG_BITS == 64 static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) { if (use_mips32r2_instructions) { @@ -2250,7 +2054,6 @@ static const TCGOutOpUnary outop_bswap64 = { .base.static_constraint = C_O1_I1(r, r), .out_rr = tgen_bswap64, }; -#endif /* TCG_TARGET_REG_BITS == 64 */ static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { @@ -2378,7 +2181,6 @@ static const TCGOutOpLoad outop_ld16s = { .out = tgen_ld16s, }; -#if TCG_TARGET_REG_BITS == 64 static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, TCGReg base, ptrdiff_t offset) { @@ -2400,7 +2202,6 @@ static const TCGOutOpLoad outop_ld32s = { .base.static_constraint = C_O1_I1(r, r), .out = tgen_ld32s, }; -#endif static void tgen_st8_r(TCGContext *s, TCGType type, TCGReg data, TCGReg base, ptrdiff_t offset) @@ -2539,7 +2340,7 @@ static tcg_insn_unit *align_code_ptr(TCGContext *s) } /* Stack frame parameters. */ -#define REG_SIZE (TCG_TARGET_REG_BITS / 8) +#define REG_SIZE 8 #define SAVE_SIZE ((int)ARRAY_SIZE(tcg_target_callee_save_regs) * REG_SIZE) #define TEMP_SIZE (CPU_TEMP_BUF_NLONGS * (int)sizeof(long)) @@ -2571,17 +2372,15 @@ static void tcg_target_qemu_prologue(TCGContext *s) * with the address of the prologue, so we can use that instead * of TCG_REG_TB. */ -#if TCG_TARGET_REG_BITS == 64 && !defined(__mips_abicalls) +#if !defined(__mips_abicalls) # error "Unknown mips abi" #endif tcg_out_movi_int(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, guest_base, - TCG_TARGET_REG_BITS == 64 ? TCG_REG_T9 : 0); + TCG_REG_T9); tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); } - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_TB, tcg_target_call_iarg_regs[1]); - } + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_TB, tcg_target_call_iarg_regs[1]); /* Call generated code */ tcg_out_opc_reg(s, OPC_JR, 0, tcg_target_call_iarg_regs[1], 0); @@ -2637,10 +2436,6 @@ static void tcg_target_qemu_prologue(TCGContext *s) /* t3 = dcba -- delay slot */ tcg_out_opc_reg(s, OPC_OR, TCG_TMP3, TCG_TMP3, TCG_TMP1); - if (TCG_TARGET_REG_BITS == 32) { - return; - } - /* * bswap32u -- unsigned 32-bit swap. a0 = ....abcd. */ @@ -2735,9 +2530,7 @@ static void tcg_target_init(TCGContext *s) { tcg_target_detect_isa(); tcg_target_available_regs[TCG_TYPE_I32] = 0xffffffff; - if (TCG_TARGET_REG_BITS == 64) { - tcg_target_available_regs[TCG_TYPE_I64] = 0xffffffff; - } + tcg_target_available_regs[TCG_TYPE_I64] = 0xffffffff; tcg_target_call_clobber_regs = 0; tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V0); @@ -2768,9 +2561,7 @@ static void tcg_target_init(TCGContext *s) tcg_regset_set_reg(s->reserved_regs, TCG_REG_RA); /* return address */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); /* stack pointer */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_GP); /* global pointer */ - if (TCG_TARGET_REG_BITS == 64) { - tcg_regset_set_reg(s->reserved_regs, TCG_REG_TB); /* tc->tc_ptr */ - } + tcg_regset_set_reg(s->reserved_regs, TCG_REG_TB); /* tc->tc_ptr */ } typedef struct { @@ -2788,7 +2579,7 @@ static const DebugFrame debug_frame = { .h.cie.id = -1, .h.cie.version = 1, .h.cie.code_align = 1, - .h.cie.data_align = -(TCG_TARGET_REG_BITS / 8) & 0x7f, /* sleb128 */ + .h.cie.data_align = -REG_SIZE & 0x7f, /* sleb128 */ .h.cie.return_column = TCG_REG_RA, /* Total FDE size does not include the "len" member. */ From 72258b05fd425936ceec80284eeb14ca29419beb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 15 Oct 2025 11:43:55 -0700 Subject: [PATCH 1689/1794] tcg/mips: Remove ALIAS_PADD, ALIAS_PADDI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These aliases existed to simplify code for O32 and N32. Now that the 64-bit abi is the only one supported, we can use the DADD* instructions directly. Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/mips/tcg-target.c.inc | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 51a15705cbb54..60c703a093990 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -340,10 +340,6 @@ typedef enum { OPC_SYNC_ACQUIRE = OPC_SYNC | 0x11 << 6, OPC_SYNC_RELEASE = OPC_SYNC | 0x12 << 6, OPC_SYNC_RMB = OPC_SYNC | 0x13 << 6, - - /* Aliases for convenience. */ - ALIAS_PADD = sizeof(void *) == 4 ? OPC_ADDU : OPC_DADDU, - ALIAS_PADDI = sizeof(void *) == 4 ? OPC_ADDIU : OPC_DADDIU, } MIPSInsn; /* @@ -700,7 +696,7 @@ static void tcg_out_ldst(TCGContext *s, MIPSInsn opc, TCGReg data, if (ofs != lo) { tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, ofs - lo); if (addr != TCG_REG_ZERO) { - tcg_out_opc_reg(s, ALIAS_PADD, TCG_TMP0, TCG_TMP0, addr); + tcg_out_opc_reg(s, OPC_DADDU, TCG_TMP0, TCG_TMP0, addr); } addr = TCG_TMP0; } @@ -1103,7 +1099,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_opc_reg(s, OPC_AND, TCG_TMP3, TCG_TMP3, TCG_TMP0); /* Add the tlb_table pointer, creating the CPUTLBEntry address. */ - tcg_out_opc_reg(s, ALIAS_PADD, TCG_TMP3, TCG_TMP3, TCG_TMP1); + tcg_out_opc_reg(s, OPC_DADDU, TCG_TMP3, TCG_TMP3, TCG_TMP1); /* Load the tlb comparator. */ if (addr_type == TCG_TYPE_I32) { @@ -1142,7 +1138,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, /* delay slot */ base = TCG_TMP3; - tcg_out_opc_reg(s, ALIAS_PADD, base, TCG_TMP3, addr); + tcg_out_opc_reg(s, OPC_DADDU, base, TCG_TMP3, addr); } else { if (a_mask && (use_mips32r6_instructions || a_bits != s_bits)) { ldst = new_ldst_label(s); @@ -1171,9 +1167,9 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, } if (guest_base) { if (guest_base == (int16_t)guest_base) { - tcg_out_opc_imm(s, ALIAS_PADDI, TCG_REG_A0, base, guest_base); + tcg_out_opc_imm(s, OPC_DADDIU, TCG_REG_A0, base, guest_base); } else { - tcg_out_opc_reg(s, ALIAS_PADD, TCG_REG_A0, base, + tcg_out_opc_reg(s, OPC_DADDU, TCG_REG_A0, base, TCG_GUEST_BASE_REG); } base = TCG_REG_A0; @@ -1411,7 +1407,7 @@ static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) } else { base = TCG_REG_V0; tcg_out_movi(s, TCG_TYPE_PTR, base, ofs - lo); - tcg_out_opc_reg(s, ALIAS_PADD, base, base, TCG_REG_TB); + tcg_out_opc_reg(s, OPC_DADDU, base, base, TCG_REG_TB); } } if (!tcg_out_opc_jmp(s, OPC_J, tb_ret_addr)) { @@ -1419,7 +1415,7 @@ static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) tcg_out_opc_reg(s, OPC_JR, 0, TCG_TMP0, 0); } /* delay slot */ - tcg_out_opc_imm(s, ALIAS_PADDI, TCG_REG_V0, base, lo); + tcg_out_opc_imm(s, OPC_DADDIU, TCG_REG_V0, base, lo); } static void tcg_out_goto_tb(TCGContext *s, int which) @@ -1438,7 +1434,7 @@ static void tcg_out_goto_tb(TCGContext *s, int which) set_jmp_reset_offset(s, which); /* For the unlinked case, need to reset TCG_REG_TB. */ - tcg_out_ldst(s, ALIAS_PADDI, TCG_REG_TB, TCG_REG_TB, + tcg_out_ldst(s, OPC_DADDIU, TCG_REG_TB, TCG_REG_TB, -tcg_current_code_size(s)); } @@ -2360,7 +2356,7 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_set_frame(s, TCG_REG_SP, TCG_STATIC_CALL_ARGS_SIZE, TEMP_SIZE); /* TB prologue */ - tcg_out_opc_imm(s, ALIAS_PADDI, TCG_REG_SP, TCG_REG_SP, -FRAME_SIZE); + tcg_out_opc_imm(s, OPC_DADDIU, TCG_REG_SP, TCG_REG_SP, -FRAME_SIZE); for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { tcg_out_st(s, TCG_TYPE_REG, tcg_target_callee_save_regs[i], TCG_REG_SP, SAVE_OFS + i * REG_SIZE); @@ -2403,7 +2399,7 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_RA, 0); /* delay slot */ - tcg_out_opc_imm(s, ALIAS_PADDI, TCG_REG_SP, TCG_REG_SP, FRAME_SIZE); + tcg_out_opc_imm(s, OPC_DADDIU, TCG_REG_SP, TCG_REG_SP, FRAME_SIZE); if (use_mips32r2_instructions) { return; From 2ff8c9a2984bb20e350ae52152812c5370b02e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 14 Oct 2025 19:38:59 +0200 Subject: [PATCH 1690/1794] buildsys: Remove support for 32-bit PPC hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop detecting 32-bit PPC host as supported. See previous commit for rationale. Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé [rth: Retain _ARCH_PPC64 check in udiv_qrnnd] Signed-off-by: Richard Henderson Message-ID: <20251014173900.87497-4-philmd@linaro.org> --- configure | 19 +++++-------------- disas/disas-host.c | 4 +--- include/qemu/timer.h | 13 +------------ util/cacheflush.c | 4 ++-- 4 files changed, 9 insertions(+), 31 deletions(-) diff --git a/configure b/configure index 8263f8137030d..a2f66f7ff9c70 100755 --- a/configure +++ b/configure @@ -391,15 +391,11 @@ elif check_define __sparc__ ; then else cpu="sparc" fi -elif check_define _ARCH_PPC ; then - if check_define _ARCH_PPC64 ; then - if check_define _LITTLE_ENDIAN ; then - cpu="ppc64le" - else - cpu="ppc64" - fi +elif check_define _ARCH_PPC64 ; then + if check_define _LITTLE_ENDIAN ; then + cpu="ppc64le" else - cpu="ppc" + cpu="ppc64" fi elif check_define __mips__ ; then if check_define __mips64 ; then @@ -472,11 +468,6 @@ case "$cpu" in linux_arch=mips ;; - ppc) - host_arch=ppc - linux_arch=powerpc - CPU_CFLAGS="-m32" - ;; ppc64) host_arch=ppc64 linux_arch=powerpc @@ -1471,7 +1462,7 @@ probe_target_compiler() { container_image=debian-all-test-cross container_cross_prefix=mips64-linux-gnuabi64- ;; - ppc|ppc64|ppc64le) + ppc64|ppc64le) container_image=debian-all-test-cross container_cross_prefix=powerpc${target_arch#ppc}-linux-gnu- ;; diff --git a/disas/disas-host.c b/disas/disas-host.c index 8146fafe804cd..4b06f41fa6c79 100644 --- a/disas/disas-host.c +++ b/disas/disas-host.c @@ -56,11 +56,9 @@ static void initialize_debug_host(CPUDebug *s) s->info.cap_mode = CS_MODE_64; s->info.cap_insn_unit = 1; s->info.cap_insn_split = 8; -#elif defined(_ARCH_PPC) +#elif defined(_ARCH_PPC64) s->info.cap_arch = CS_ARCH_PPC; -# ifdef _ARCH_PPC64 s->info.cap_mode = CS_MODE_64; -# endif #elif defined(__riscv) #if defined(_ILP32) || (__riscv_xlen == 32) s->info.print_insn = print_insn_riscv32; diff --git a/include/qemu/timer.h b/include/qemu/timer.h index aec730ac257e7..406d741120302 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -850,12 +850,11 @@ static inline int64_t get_clock(void) /*******************************************/ /* host CPU ticks (if available) */ -#if defined(_ARCH_PPC) +#if defined(_ARCH_PPC64) static inline int64_t cpu_get_host_ticks(void) { int64_t retval; -#ifdef _ARCH_PPC64 /* This reads timebase in one 64bit go and includes Cell workaround from: http://ozlabs.org/pipermail/linuxppc-dev/2006-October/027052.html */ @@ -863,16 +862,6 @@ static inline int64_t cpu_get_host_ticks(void) "cmpwi %0,0\n\t" "beq- $-8" : "=r" (retval)); -#else - /* http://ozlabs.org/pipermail/linuxppc-dev/1999-October/003889.html */ - unsigned long junk; - __asm__ __volatile__ ("mfspr %1,269\n\t" /* mftbu */ - "mfspr %L0,268\n\t" /* mftb */ - "mfspr %0,269\n\t" /* mftbu */ - "cmpw %0,%1\n\t" - "bne $-16" - : "=r" (retval), "=r" (junk)); -#endif return retval; } diff --git a/util/cacheflush.c b/util/cacheflush.c index 17c58918de60d..69c9614e2c9bb 100644 --- a/util/cacheflush.c +++ b/util/cacheflush.c @@ -153,7 +153,7 @@ static void arch_cache_info(int *isize, int *dsize) } } -#elif defined(_ARCH_PPC) && defined(__linux__) +#elif defined(_ARCH_PPC64) && defined(__linux__) # include "elf.h" static void arch_cache_info(int *isize, int *dsize) @@ -187,7 +187,7 @@ static void fallback_cache_info(int *isize, int *dsize) } else if (*dsize) { *isize = *dsize; } else { -#if defined(_ARCH_PPC) +#if defined(_ARCH_PPC64) /* * For PPC, we're going to use the cache sizes computed for * flush_idcache_range. Which means that we must use the From 4a11070978ac3a17e6325fc1cce66c43179ae6ad Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Tue, 7 Oct 2025 19:22:32 -0500 Subject: [PATCH 1691/1794] linux-user/hexagon: Fix sigcontext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to correspond with the kernel, we've now (1) moved the preds[] to the right offset and combined the representation as a single ulong "p3_0", (2), added the cs{0,1} registers, (3) added a pad for 48 words, (4) added the user regs structure to an 8-byte aligned target_sigcontext structure. Co-authored-by: Alex Rønne Petersen Reviewed-by: Taylor Simpson Reviewed-by: Richard Henderson Signed-off-by: Brian Cain --- linux-user/hexagon/signal.c | 213 ++++++++++++++++++++---------------- 1 file changed, 119 insertions(+), 94 deletions(-) diff --git a/linux-user/hexagon/signal.c b/linux-user/hexagon/signal.c index 492b51f155005..2847952216878 100644 --- a/linux-user/hexagon/signal.c +++ b/linux-user/hexagon/signal.c @@ -23,7 +23,7 @@ #include "signal-common.h" #include "linux-user/trace.h" -struct target_sigcontext { +struct target_user_regs_struct { target_ulong r0, r1, r2, r3; target_ulong r4, r5, r6, r7; target_ulong r8, r9, r10, r11; @@ -39,14 +39,23 @@ struct target_sigcontext { target_ulong m0; target_ulong m1; target_ulong usr; + target_ulong p3_0; target_ulong gp; target_ulong ugp; target_ulong pc; target_ulong cause; target_ulong badva; - target_ulong pred[NUM_PREGS]; + target_ulong cs0; + target_ulong cs1; + target_ulong pad1; /* pad to 48 words */ }; +QEMU_BUILD_BUG_ON(sizeof(struct target_user_regs_struct) != 48 * 4); + +struct target_sigcontext { + struct target_user_regs_struct sc_regs; +} QEMU_ALIGNED(8); + struct target_ucontext { unsigned long uc_flags; target_ulong uc_link; /* target pointer */ @@ -76,53 +85,63 @@ static abi_ulong get_sigframe(struct target_sigaction *ka, static void setup_sigcontext(struct target_sigcontext *sc, CPUHexagonState *env) { - __put_user(env->gpr[HEX_REG_R00], &sc->r0); - __put_user(env->gpr[HEX_REG_R01], &sc->r1); - __put_user(env->gpr[HEX_REG_R02], &sc->r2); - __put_user(env->gpr[HEX_REG_R03], &sc->r3); - __put_user(env->gpr[HEX_REG_R04], &sc->r4); - __put_user(env->gpr[HEX_REG_R05], &sc->r5); - __put_user(env->gpr[HEX_REG_R06], &sc->r6); - __put_user(env->gpr[HEX_REG_R07], &sc->r7); - __put_user(env->gpr[HEX_REG_R08], &sc->r8); - __put_user(env->gpr[HEX_REG_R09], &sc->r9); - __put_user(env->gpr[HEX_REG_R10], &sc->r10); - __put_user(env->gpr[HEX_REG_R11], &sc->r11); - __put_user(env->gpr[HEX_REG_R12], &sc->r12); - __put_user(env->gpr[HEX_REG_R13], &sc->r13); - __put_user(env->gpr[HEX_REG_R14], &sc->r14); - __put_user(env->gpr[HEX_REG_R15], &sc->r15); - __put_user(env->gpr[HEX_REG_R16], &sc->r16); - __put_user(env->gpr[HEX_REG_R17], &sc->r17); - __put_user(env->gpr[HEX_REG_R18], &sc->r18); - __put_user(env->gpr[HEX_REG_R19], &sc->r19); - __put_user(env->gpr[HEX_REG_R20], &sc->r20); - __put_user(env->gpr[HEX_REG_R21], &sc->r21); - __put_user(env->gpr[HEX_REG_R22], &sc->r22); - __put_user(env->gpr[HEX_REG_R23], &sc->r23); - __put_user(env->gpr[HEX_REG_R24], &sc->r24); - __put_user(env->gpr[HEX_REG_R25], &sc->r25); - __put_user(env->gpr[HEX_REG_R26], &sc->r26); - __put_user(env->gpr[HEX_REG_R27], &sc->r27); - __put_user(env->gpr[HEX_REG_R28], &sc->r28); - __put_user(env->gpr[HEX_REG_R29], &sc->r29); - __put_user(env->gpr[HEX_REG_R30], &sc->r30); - __put_user(env->gpr[HEX_REG_R31], &sc->r31); - __put_user(env->gpr[HEX_REG_SA0], &sc->sa0); - __put_user(env->gpr[HEX_REG_LC0], &sc->lc0); - __put_user(env->gpr[HEX_REG_SA1], &sc->sa1); - __put_user(env->gpr[HEX_REG_LC1], &sc->lc1); - __put_user(env->gpr[HEX_REG_M0], &sc->m0); - __put_user(env->gpr[HEX_REG_M1], &sc->m1); - __put_user(env->gpr[HEX_REG_USR], &sc->usr); - __put_user(env->gpr[HEX_REG_GP], &sc->gp); - __put_user(env->gpr[HEX_REG_UGP], &sc->ugp); - __put_user(env->gpr[HEX_REG_PC], &sc->pc); - - int i; - for (i = 0; i < NUM_PREGS; i++) { - __put_user(env->pred[i], &(sc->pred[i])); + target_ulong preds = 0; + + __put_user(env->gpr[HEX_REG_R00], &sc->sc_regs.r0); + __put_user(env->gpr[HEX_REG_R01], &sc->sc_regs.r1); + __put_user(env->gpr[HEX_REG_R02], &sc->sc_regs.r2); + __put_user(env->gpr[HEX_REG_R03], &sc->sc_regs.r3); + __put_user(env->gpr[HEX_REG_R04], &sc->sc_regs.r4); + __put_user(env->gpr[HEX_REG_R05], &sc->sc_regs.r5); + __put_user(env->gpr[HEX_REG_R06], &sc->sc_regs.r6); + __put_user(env->gpr[HEX_REG_R07], &sc->sc_regs.r7); + __put_user(env->gpr[HEX_REG_R08], &sc->sc_regs.r8); + __put_user(env->gpr[HEX_REG_R09], &sc->sc_regs.r9); + __put_user(env->gpr[HEX_REG_R10], &sc->sc_regs.r10); + __put_user(env->gpr[HEX_REG_R11], &sc->sc_regs.r11); + __put_user(env->gpr[HEX_REG_R12], &sc->sc_regs.r12); + __put_user(env->gpr[HEX_REG_R13], &sc->sc_regs.r13); + __put_user(env->gpr[HEX_REG_R14], &sc->sc_regs.r14); + __put_user(env->gpr[HEX_REG_R15], &sc->sc_regs.r15); + __put_user(env->gpr[HEX_REG_R16], &sc->sc_regs.r16); + __put_user(env->gpr[HEX_REG_R17], &sc->sc_regs.r17); + __put_user(env->gpr[HEX_REG_R18], &sc->sc_regs.r18); + __put_user(env->gpr[HEX_REG_R19], &sc->sc_regs.r19); + __put_user(env->gpr[HEX_REG_R20], &sc->sc_regs.r20); + __put_user(env->gpr[HEX_REG_R21], &sc->sc_regs.r21); + __put_user(env->gpr[HEX_REG_R22], &sc->sc_regs.r22); + __put_user(env->gpr[HEX_REG_R23], &sc->sc_regs.r23); + __put_user(env->gpr[HEX_REG_R24], &sc->sc_regs.r24); + __put_user(env->gpr[HEX_REG_R25], &sc->sc_regs.r25); + __put_user(env->gpr[HEX_REG_R26], &sc->sc_regs.r26); + __put_user(env->gpr[HEX_REG_R27], &sc->sc_regs.r27); + __put_user(env->gpr[HEX_REG_R28], &sc->sc_regs.r28); + __put_user(env->gpr[HEX_REG_R29], &sc->sc_regs.r29); + __put_user(env->gpr[HEX_REG_R30], &sc->sc_regs.r30); + __put_user(env->gpr[HEX_REG_R31], &sc->sc_regs.r31); + __put_user(env->gpr[HEX_REG_SA0], &sc->sc_regs.sa0); + __put_user(env->gpr[HEX_REG_LC0], &sc->sc_regs.lc0); + __put_user(env->gpr[HEX_REG_SA1], &sc->sc_regs.sa1); + __put_user(env->gpr[HEX_REG_LC1], &sc->sc_regs.lc1); + __put_user(env->gpr[HEX_REG_M0], &sc->sc_regs.m0); + __put_user(env->gpr[HEX_REG_M1], &sc->sc_regs.m1); + __put_user(env->gpr[HEX_REG_USR], &sc->sc_regs.usr); + __put_user(env->gpr[HEX_REG_GP], &sc->sc_regs.gp); + __put_user(env->gpr[HEX_REG_UGP], &sc->sc_regs.ugp); + __put_user(env->gpr[HEX_REG_PC], &sc->sc_regs.pc); + + /* Consolidate predicates into p3_0 */ + for (int i = 0; i < NUM_PREGS; i++) { + preds |= (env->pred[i] & 0xff) << (i * 8); } + __put_user(preds, &sc->sc_regs.p3_0); + + /* Set cause and badva to 0 - these are set by kernel on exceptions */ + __put_user(0, &sc->sc_regs.cause); + __put_user(0, &sc->sc_regs.badva); + + __put_user(env->gpr[HEX_REG_CS0], &sc->sc_regs.cs0); + __put_user(env->gpr[HEX_REG_CS1], &sc->sc_regs.cs1); } static void setup_ucontext(struct target_ucontext *uc, @@ -192,53 +211,59 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, static void restore_sigcontext(CPUHexagonState *env, struct target_sigcontext *sc) { - __get_user(env->gpr[HEX_REG_R00], &sc->r0); - __get_user(env->gpr[HEX_REG_R01], &sc->r1); - __get_user(env->gpr[HEX_REG_R02], &sc->r2); - __get_user(env->gpr[HEX_REG_R03], &sc->r3); - __get_user(env->gpr[HEX_REG_R04], &sc->r4); - __get_user(env->gpr[HEX_REG_R05], &sc->r5); - __get_user(env->gpr[HEX_REG_R06], &sc->r6); - __get_user(env->gpr[HEX_REG_R07], &sc->r7); - __get_user(env->gpr[HEX_REG_R08], &sc->r8); - __get_user(env->gpr[HEX_REG_R09], &sc->r9); - __get_user(env->gpr[HEX_REG_R10], &sc->r10); - __get_user(env->gpr[HEX_REG_R11], &sc->r11); - __get_user(env->gpr[HEX_REG_R12], &sc->r12); - __get_user(env->gpr[HEX_REG_R13], &sc->r13); - __get_user(env->gpr[HEX_REG_R14], &sc->r14); - __get_user(env->gpr[HEX_REG_R15], &sc->r15); - __get_user(env->gpr[HEX_REG_R16], &sc->r16); - __get_user(env->gpr[HEX_REG_R17], &sc->r17); - __get_user(env->gpr[HEX_REG_R18], &sc->r18); - __get_user(env->gpr[HEX_REG_R19], &sc->r19); - __get_user(env->gpr[HEX_REG_R20], &sc->r20); - __get_user(env->gpr[HEX_REG_R21], &sc->r21); - __get_user(env->gpr[HEX_REG_R22], &sc->r22); - __get_user(env->gpr[HEX_REG_R23], &sc->r23); - __get_user(env->gpr[HEX_REG_R24], &sc->r24); - __get_user(env->gpr[HEX_REG_R25], &sc->r25); - __get_user(env->gpr[HEX_REG_R26], &sc->r26); - __get_user(env->gpr[HEX_REG_R27], &sc->r27); - __get_user(env->gpr[HEX_REG_R28], &sc->r28); - __get_user(env->gpr[HEX_REG_R29], &sc->r29); - __get_user(env->gpr[HEX_REG_R30], &sc->r30); - __get_user(env->gpr[HEX_REG_R31], &sc->r31); - __get_user(env->gpr[HEX_REG_SA0], &sc->sa0); - __get_user(env->gpr[HEX_REG_LC0], &sc->lc0); - __get_user(env->gpr[HEX_REG_SA1], &sc->sa1); - __get_user(env->gpr[HEX_REG_LC1], &sc->lc1); - __get_user(env->gpr[HEX_REG_M0], &sc->m0); - __get_user(env->gpr[HEX_REG_M1], &sc->m1); - __get_user(env->gpr[HEX_REG_USR], &sc->usr); - __get_user(env->gpr[HEX_REG_GP], &sc->gp); - __get_user(env->gpr[HEX_REG_UGP], &sc->ugp); - __get_user(env->gpr[HEX_REG_PC], &sc->pc); - - int i; - for (i = 0; i < NUM_PREGS; i++) { - __get_user(env->pred[i], &(sc->pred[i])); + target_ulong preds; + + __get_user(env->gpr[HEX_REG_R00], &sc->sc_regs.r0); + __get_user(env->gpr[HEX_REG_R01], &sc->sc_regs.r1); + __get_user(env->gpr[HEX_REG_R02], &sc->sc_regs.r2); + __get_user(env->gpr[HEX_REG_R03], &sc->sc_regs.r3); + __get_user(env->gpr[HEX_REG_R04], &sc->sc_regs.r4); + __get_user(env->gpr[HEX_REG_R05], &sc->sc_regs.r5); + __get_user(env->gpr[HEX_REG_R06], &sc->sc_regs.r6); + __get_user(env->gpr[HEX_REG_R07], &sc->sc_regs.r7); + __get_user(env->gpr[HEX_REG_R08], &sc->sc_regs.r8); + __get_user(env->gpr[HEX_REG_R09], &sc->sc_regs.r9); + __get_user(env->gpr[HEX_REG_R10], &sc->sc_regs.r10); + __get_user(env->gpr[HEX_REG_R11], &sc->sc_regs.r11); + __get_user(env->gpr[HEX_REG_R12], &sc->sc_regs.r12); + __get_user(env->gpr[HEX_REG_R13], &sc->sc_regs.r13); + __get_user(env->gpr[HEX_REG_R14], &sc->sc_regs.r14); + __get_user(env->gpr[HEX_REG_R15], &sc->sc_regs.r15); + __get_user(env->gpr[HEX_REG_R16], &sc->sc_regs.r16); + __get_user(env->gpr[HEX_REG_R17], &sc->sc_regs.r17); + __get_user(env->gpr[HEX_REG_R18], &sc->sc_regs.r18); + __get_user(env->gpr[HEX_REG_R19], &sc->sc_regs.r19); + __get_user(env->gpr[HEX_REG_R20], &sc->sc_regs.r20); + __get_user(env->gpr[HEX_REG_R21], &sc->sc_regs.r21); + __get_user(env->gpr[HEX_REG_R22], &sc->sc_regs.r22); + __get_user(env->gpr[HEX_REG_R23], &sc->sc_regs.r23); + __get_user(env->gpr[HEX_REG_R24], &sc->sc_regs.r24); + __get_user(env->gpr[HEX_REG_R25], &sc->sc_regs.r25); + __get_user(env->gpr[HEX_REG_R26], &sc->sc_regs.r26); + __get_user(env->gpr[HEX_REG_R27], &sc->sc_regs.r27); + __get_user(env->gpr[HEX_REG_R28], &sc->sc_regs.r28); + __get_user(env->gpr[HEX_REG_R29], &sc->sc_regs.r29); + __get_user(env->gpr[HEX_REG_R30], &sc->sc_regs.r30); + __get_user(env->gpr[HEX_REG_R31], &sc->sc_regs.r31); + __get_user(env->gpr[HEX_REG_SA0], &sc->sc_regs.sa0); + __get_user(env->gpr[HEX_REG_LC0], &sc->sc_regs.lc0); + __get_user(env->gpr[HEX_REG_SA1], &sc->sc_regs.sa1); + __get_user(env->gpr[HEX_REG_LC1], &sc->sc_regs.lc1); + __get_user(env->gpr[HEX_REG_M0], &sc->sc_regs.m0); + __get_user(env->gpr[HEX_REG_M1], &sc->sc_regs.m1); + __get_user(env->gpr[HEX_REG_USR], &sc->sc_regs.usr); + __get_user(env->gpr[HEX_REG_GP], &sc->sc_regs.gp); + __get_user(env->gpr[HEX_REG_UGP], &sc->sc_regs.ugp); + __get_user(env->gpr[HEX_REG_PC], &sc->sc_regs.pc); + + /* Restore predicates from p3_0 */ + __get_user(preds, &sc->sc_regs.p3_0); + for (int i = 0; i < NUM_PREGS; i++) { + env->pred[i] = (preds >> (i * 8)) & 0xff; } + + __get_user(env->gpr[HEX_REG_CS0], &sc->sc_regs.cs0); + __get_user(env->gpr[HEX_REG_CS1], &sc->sc_regs.cs1); } static void restore_ucontext(CPUHexagonState *env, struct target_ucontext *uc) From ef9bb7c6e44683a17765ccf000048436c9915137 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 8 Oct 2025 17:22:34 -0500 Subject: [PATCH 1692/1794] linux-user/hexagon: use abi_ulong MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the user_regs_struct to use abi_ulong instead of target_ulong. Link: https://lore.kernel.org/qemu-devel/7bf3d8c5-df07-4cbd-ba62-4c7246a5f96b@linaro.org/ Suggested-by: Richard Henderson Reviewed-by: Taylor Simpson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Brian Cain --- linux-user/hexagon/signal.c | 52 ++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/linux-user/hexagon/signal.c b/linux-user/hexagon/signal.c index 2847952216878..e5514b2bec17a 100644 --- a/linux-user/hexagon/signal.c +++ b/linux-user/hexagon/signal.c @@ -24,30 +24,30 @@ #include "linux-user/trace.h" struct target_user_regs_struct { - target_ulong r0, r1, r2, r3; - target_ulong r4, r5, r6, r7; - target_ulong r8, r9, r10, r11; - target_ulong r12, r13, r14, r15; - target_ulong r16, r17, r18, r19; - target_ulong r20, r21, r22, r23; - target_ulong r24, r25, r26, r27; - target_ulong r28, r29, r30, r31; - target_ulong sa0; - target_ulong lc0; - target_ulong sa1; - target_ulong lc1; - target_ulong m0; - target_ulong m1; - target_ulong usr; - target_ulong p3_0; - target_ulong gp; - target_ulong ugp; - target_ulong pc; - target_ulong cause; - target_ulong badva; - target_ulong cs0; - target_ulong cs1; - target_ulong pad1; /* pad to 48 words */ + abi_ulong r0, r1, r2, r3; + abi_ulong r4, r5, r6, r7; + abi_ulong r8, r9, r10, r11; + abi_ulong r12, r13, r14, r15; + abi_ulong r16, r17, r18, r19; + abi_ulong r20, r21, r22, r23; + abi_ulong r24, r25, r26, r27; + abi_ulong r28, r29, r30, r31; + abi_ulong sa0; + abi_ulong lc0; + abi_ulong sa1; + abi_ulong lc1; + abi_ulong m0; + abi_ulong m1; + abi_ulong usr; + abi_ulong p3_0; + abi_ulong gp; + abi_ulong ugp; + abi_ulong pc; + abi_ulong cause; + abi_ulong badva; + abi_ulong cs0; + abi_ulong cs1; + abi_ulong pad1; /* pad to 48 words */ }; QEMU_BUILD_BUG_ON(sizeof(struct target_user_regs_struct) != 48 * 4); @@ -85,7 +85,7 @@ static abi_ulong get_sigframe(struct target_sigaction *ka, static void setup_sigcontext(struct target_sigcontext *sc, CPUHexagonState *env) { - target_ulong preds = 0; + abi_ulong preds = 0; __put_user(env->gpr[HEX_REG_R00], &sc->sc_regs.r0); __put_user(env->gpr[HEX_REG_R01], &sc->sc_regs.r1); @@ -211,7 +211,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, static void restore_sigcontext(CPUHexagonState *env, struct target_sigcontext *sc) { - target_ulong preds; + abi_ulong preds; __get_user(env->gpr[HEX_REG_R00], &sc->sc_regs.r0); __get_user(env->gpr[HEX_REG_R01], &sc->sc_regs.r1); From 81c9311296ebf67f3821f347141a11838aa7d9d3 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 8 Oct 2025 18:06:42 -0500 Subject: [PATCH 1693/1794] linux-user/hexagon: Use an array for GPRs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Link: https://lore.kernel.org/qemu-devel/023e01dc389c$faf84320$f0e8c960$@gmail.com/ Suggested-by: Taylor Simpson Reviewed-by: Taylor Simpson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Brian Cain --- linux-user/hexagon/signal.c | 79 ++++--------------------------------- 1 file changed, 7 insertions(+), 72 deletions(-) diff --git a/linux-user/hexagon/signal.c b/linux-user/hexagon/signal.c index e5514b2bec17a..183ecfaa19a8b 100644 --- a/linux-user/hexagon/signal.c +++ b/linux-user/hexagon/signal.c @@ -24,14 +24,7 @@ #include "linux-user/trace.h" struct target_user_regs_struct { - abi_ulong r0, r1, r2, r3; - abi_ulong r4, r5, r6, r7; - abi_ulong r8, r9, r10, r11; - abi_ulong r12, r13, r14, r15; - abi_ulong r16, r17, r18, r19; - abi_ulong r20, r21, r22, r23; - abi_ulong r24, r25, r26, r27; - abi_ulong r28, r29, r30, r31; + abi_ulong gpr[32]; abi_ulong sa0; abi_ulong lc0; abi_ulong sa1; @@ -87,38 +80,9 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPUHexagonState *env) { abi_ulong preds = 0; - __put_user(env->gpr[HEX_REG_R00], &sc->sc_regs.r0); - __put_user(env->gpr[HEX_REG_R01], &sc->sc_regs.r1); - __put_user(env->gpr[HEX_REG_R02], &sc->sc_regs.r2); - __put_user(env->gpr[HEX_REG_R03], &sc->sc_regs.r3); - __put_user(env->gpr[HEX_REG_R04], &sc->sc_regs.r4); - __put_user(env->gpr[HEX_REG_R05], &sc->sc_regs.r5); - __put_user(env->gpr[HEX_REG_R06], &sc->sc_regs.r6); - __put_user(env->gpr[HEX_REG_R07], &sc->sc_regs.r7); - __put_user(env->gpr[HEX_REG_R08], &sc->sc_regs.r8); - __put_user(env->gpr[HEX_REG_R09], &sc->sc_regs.r9); - __put_user(env->gpr[HEX_REG_R10], &sc->sc_regs.r10); - __put_user(env->gpr[HEX_REG_R11], &sc->sc_regs.r11); - __put_user(env->gpr[HEX_REG_R12], &sc->sc_regs.r12); - __put_user(env->gpr[HEX_REG_R13], &sc->sc_regs.r13); - __put_user(env->gpr[HEX_REG_R14], &sc->sc_regs.r14); - __put_user(env->gpr[HEX_REG_R15], &sc->sc_regs.r15); - __put_user(env->gpr[HEX_REG_R16], &sc->sc_regs.r16); - __put_user(env->gpr[HEX_REG_R17], &sc->sc_regs.r17); - __put_user(env->gpr[HEX_REG_R18], &sc->sc_regs.r18); - __put_user(env->gpr[HEX_REG_R19], &sc->sc_regs.r19); - __put_user(env->gpr[HEX_REG_R20], &sc->sc_regs.r20); - __put_user(env->gpr[HEX_REG_R21], &sc->sc_regs.r21); - __put_user(env->gpr[HEX_REG_R22], &sc->sc_regs.r22); - __put_user(env->gpr[HEX_REG_R23], &sc->sc_regs.r23); - __put_user(env->gpr[HEX_REG_R24], &sc->sc_regs.r24); - __put_user(env->gpr[HEX_REG_R25], &sc->sc_regs.r25); - __put_user(env->gpr[HEX_REG_R26], &sc->sc_regs.r26); - __put_user(env->gpr[HEX_REG_R27], &sc->sc_regs.r27); - __put_user(env->gpr[HEX_REG_R28], &sc->sc_regs.r28); - __put_user(env->gpr[HEX_REG_R29], &sc->sc_regs.r29); - __put_user(env->gpr[HEX_REG_R30], &sc->sc_regs.r30); - __put_user(env->gpr[HEX_REG_R31], &sc->sc_regs.r31); + for (int i = 0; i < 32; i++) { + __put_user(env->gpr[HEX_REG_R00 + i], &sc->sc_regs.gpr[i]); + } __put_user(env->gpr[HEX_REG_SA0], &sc->sc_regs.sa0); __put_user(env->gpr[HEX_REG_LC0], &sc->sc_regs.lc0); __put_user(env->gpr[HEX_REG_SA1], &sc->sc_regs.sa1); @@ -213,38 +177,9 @@ static void restore_sigcontext(CPUHexagonState *env, { abi_ulong preds; - __get_user(env->gpr[HEX_REG_R00], &sc->sc_regs.r0); - __get_user(env->gpr[HEX_REG_R01], &sc->sc_regs.r1); - __get_user(env->gpr[HEX_REG_R02], &sc->sc_regs.r2); - __get_user(env->gpr[HEX_REG_R03], &sc->sc_regs.r3); - __get_user(env->gpr[HEX_REG_R04], &sc->sc_regs.r4); - __get_user(env->gpr[HEX_REG_R05], &sc->sc_regs.r5); - __get_user(env->gpr[HEX_REG_R06], &sc->sc_regs.r6); - __get_user(env->gpr[HEX_REG_R07], &sc->sc_regs.r7); - __get_user(env->gpr[HEX_REG_R08], &sc->sc_regs.r8); - __get_user(env->gpr[HEX_REG_R09], &sc->sc_regs.r9); - __get_user(env->gpr[HEX_REG_R10], &sc->sc_regs.r10); - __get_user(env->gpr[HEX_REG_R11], &sc->sc_regs.r11); - __get_user(env->gpr[HEX_REG_R12], &sc->sc_regs.r12); - __get_user(env->gpr[HEX_REG_R13], &sc->sc_regs.r13); - __get_user(env->gpr[HEX_REG_R14], &sc->sc_regs.r14); - __get_user(env->gpr[HEX_REG_R15], &sc->sc_regs.r15); - __get_user(env->gpr[HEX_REG_R16], &sc->sc_regs.r16); - __get_user(env->gpr[HEX_REG_R17], &sc->sc_regs.r17); - __get_user(env->gpr[HEX_REG_R18], &sc->sc_regs.r18); - __get_user(env->gpr[HEX_REG_R19], &sc->sc_regs.r19); - __get_user(env->gpr[HEX_REG_R20], &sc->sc_regs.r20); - __get_user(env->gpr[HEX_REG_R21], &sc->sc_regs.r21); - __get_user(env->gpr[HEX_REG_R22], &sc->sc_regs.r22); - __get_user(env->gpr[HEX_REG_R23], &sc->sc_regs.r23); - __get_user(env->gpr[HEX_REG_R24], &sc->sc_regs.r24); - __get_user(env->gpr[HEX_REG_R25], &sc->sc_regs.r25); - __get_user(env->gpr[HEX_REG_R26], &sc->sc_regs.r26); - __get_user(env->gpr[HEX_REG_R27], &sc->sc_regs.r27); - __get_user(env->gpr[HEX_REG_R28], &sc->sc_regs.r28); - __get_user(env->gpr[HEX_REG_R29], &sc->sc_regs.r29); - __get_user(env->gpr[HEX_REG_R30], &sc->sc_regs.r30); - __get_user(env->gpr[HEX_REG_R31], &sc->sc_regs.r31); + for (int i = 0; i < 32; i++) { + __get_user(env->gpr[HEX_REG_R00 + i], &sc->sc_regs.gpr[i]); + } __get_user(env->gpr[HEX_REG_SA0], &sc->sc_regs.sa0); __get_user(env->gpr[HEX_REG_LC0], &sc->sc_regs.lc0); __get_user(env->gpr[HEX_REG_SA1], &sc->sc_regs.sa1); From 6f6edad6c8daae1e7217ba0066b9e4791187c969 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Tue, 7 Oct 2025 20:04:53 -0500 Subject: [PATCH 1694/1794] tests/tcg/hexagon: Add cs{0,1} coverage Cover cs0,1 register corruption in the signal_context test case. lc0, sa0 registers previously omitted from the clobbers list are now captured. Reviewed-by: Anton Johansson Reviewed-by: Taylor Simpson Reviewed-by: Richard Henderson Signed-off-by: Brian Cain --- tests/tcg/hexagon/signal_context.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/tests/tcg/hexagon/signal_context.c b/tests/tcg/hexagon/signal_context.c index 7202fa64b67d7..9de7f6be4fd14 100644 --- a/tests/tcg/hexagon/signal_context.c +++ b/tests/tcg/hexagon/signal_context.c @@ -26,7 +26,11 @@ void sig_user(int sig, siginfo_t *info, void *puc) "p1 = r7\n\t" "p2 = r7\n\t" "p3 = r7\n\t" - : : : "r7", "p0", "p1", "p2", "p3"); + "r6 = #0x12345678\n\t" + "cs0 = r6\n\t" + "r6 = #0x87654321\n\t" + "cs1 = r6\n\t" + : : : "r6", "r7", "p0", "p1", "p2", "p3", "cs0", "cs1"); } int main() @@ -53,7 +57,11 @@ int main() timer_settime(tid, 0, &it, NULL); asm("loop0(1f, %1)\n\t" - "1: r8 = #0xff\n\t" + "1: r9 = #0xdeadbeef\n\t" + " cs0 = r9\n\t" + " r9 = #0xbadc0fee\n\t" + " cs1 = r9\n\t" + " r8 = #0xff\n\t" " p0 = r8\n\t" " p1 = r8\n\t" " p2 = r8\n\t" @@ -74,10 +82,19 @@ int main() " r8 = p3\n\t" " p0 = cmp.eq(r8, #0xff)\n\t" " if (!p0) jump 2b\n\t" + " r8 = cs0\n\t" + " r9 = #0xdeadbeef\n\t" + " p0 = cmp.eq(r8, r9)\n\t" + " if (!p0) jump 2b\n\t" + " r8 = cs1\n\t" + " r9 = #0xbadc0fee\n\t" + " p0 = cmp.eq(r8, r9)\n\t" + " if (!p0) jump 2b\n\t" "4: {}: endloop0\n\t" : : "r"(&err), "r"(i) - : "memory", "r8", "p0", "p1", "p2", "p3"); + : "memory", "r8", "r9", "p0", "p1", "p2", "p3", "cs0", "cs1", "lc0", + "sa0"); puts(err ? "FAIL" : "PASS"); return err; From 0c8a4e8434e3e8fa3193a38413c17cd259ce3457 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Mon, 7 Apr 2025 12:27:01 -0700 Subject: [PATCH 1695/1794] target/hexagon: handle .new values Reviewed-by: Taylor Simpson Reviewed-by: Matheus Tavares Bernardino Signed-off-by: Brian Cain --- target/hexagon/hex_common.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index 758e5fd12dfed..6803908718179 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -350,6 +350,7 @@ def helper_arg(self): f"{self.helper_arg_type()} {self.helper_arg_name()}" ) + # # Every register is either Single or Pair or Hvx # @@ -1070,11 +1071,22 @@ def init_registers(): for reg in new_regs: new_registers[f"{reg.regtype}{reg.regid}"] = reg -def get_register(tag, regtype, regid): - if f"{regtype}{regid}V" in semdict[tag]: - return registers[f"{regtype}{regid}"] - else: - return new_registers[f"{regtype}{regid}"] +def is_new_reg(tag, regid): + if regid[0] in "NO": + return True + return regid[0] == "P" and \ + f"{regid}N" in semdict[tag] and \ + f"{regid}V" not in semdict[tag] + +def get_register(tag, regtype, regid, subtype=""): + regid = f"{regtype}{regid}" + is_new = is_new_reg(tag, regid) + try: + reg = new_registers[regid] if is_new else registers[regid] + except KeyError: + raise Exception(f"Unknown {'new ' if is_new else ''}register {regid}" +\ + f"from '{tag}' with syntax '{semdict[tag]}'") from None + return reg def helper_ret_type(tag, regs): ## If there is a scalar result, it is the return type From 1118972ef58b8e6e5644a70ff9579ec10541a873 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Mon, 7 Apr 2025 12:27:04 -0700 Subject: [PATCH 1696/1794] target/hexagon: s/pkt_has_store/pkt_has_scalar_store To remove any confusion with HVX or other potential store instructions, we'll qualify this context var with "scalar". Reviewed-by: Taylor Simpson Reviewed-by: Matheus Tavares Bernardino Signed-off-by: Brian Cain --- target/hexagon/decode.c | 4 ++-- target/hexagon/gen_helper_funcs.py | 2 +- target/hexagon/genptr.c | 3 ++- target/hexagon/idef-parser/README.rst | 2 +- target/hexagon/idef-parser/parser-helpers.c | 4 ++-- target/hexagon/insn.h | 4 ++-- target/hexagon/macros.h | 8 ++++---- target/hexagon/op_helper.c | 4 ++-- target/hexagon/translate.c | 9 +++++---- 9 files changed, 21 insertions(+), 19 deletions(-) diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index 23deba2426f84..b5ece604505b6 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c @@ -236,9 +236,9 @@ static void decode_set_insn_attr_fields(Packet *pkt) if (GET_ATTRIB(opcode, A_SCALAR_STORE) && !GET_ATTRIB(opcode, A_MEMSIZE_0B)) { if (pkt->insn[i].slot == 0) { - pkt->pkt_has_store_s0 = true; + pkt->pkt_has_scalar_store_s0 = true; } else { - pkt->pkt_has_store_s1 = true; + pkt->pkt_has_scalar_store_s1 = true; } } } diff --git a/target/hexagon/gen_helper_funcs.py b/target/hexagon/gen_helper_funcs.py index c1f806ac4b25b..a9c0e27a801ff 100755 --- a/target/hexagon/gen_helper_funcs.py +++ b/target/hexagon/gen_helper_funcs.py @@ -69,7 +69,7 @@ def gen_helper_function(f, tag, tagregs, tagimms): if hex_common.need_slot(tag): if "A_LOAD" in hex_common.attribdict[tag]: f.write(hex_common.code_fmt(f"""\ - bool pkt_has_store_s1 = slotval & 0x1; + bool pkt_has_scalar_store_s1 = slotval & 0x1; """)) f.write(hex_common.code_fmt(f"""\ uint32_t slot = slotval >> 1; diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 08fc5413de79b..cecaece4ae5f7 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -395,7 +395,8 @@ static inline void gen_store_conditional8(DisasContext *ctx, #ifndef CONFIG_HEXAGON_IDEF_PARSER static TCGv gen_slotval(DisasContext *ctx) { - int slotval = (ctx->pkt->pkt_has_store_s1 & 1) | (ctx->insn->slot << 1); + int slotval = + (ctx->pkt->pkt_has_scalar_store_s1 & 1) | (ctx->insn->slot << 1); return tcg_constant_tl(slotval); } #endif diff --git a/target/hexagon/idef-parser/README.rst b/target/hexagon/idef-parser/README.rst index 7199177ee33e6..235e3debee3c3 100644 --- a/target/hexagon/idef-parser/README.rst +++ b/target/hexagon/idef-parser/README.rst @@ -637,7 +637,7 @@ tinycode for the Hexagon ``add`` instruction :: ---- 00021094 - mov_i32 pkt_has_store_s1,$0x0 + mov_i32 pkt_has_scalar_store_s1,$0x0 add_i32 tmp0,r2,r2 mov_i32 loc2,tmp0 mov_i32 new_r1,loc2 diff --git a/target/hexagon/idef-parser/parser-helpers.c b/target/hexagon/idef-parser/parser-helpers.c index 542af8d0a65b3..1dc52b4e02990 100644 --- a/target/hexagon/idef-parser/parser-helpers.c +++ b/target/hexagon/idef-parser/parser-helpers.c @@ -1725,7 +1725,7 @@ void gen_cancel(Context *c, YYLTYPE *locp) void gen_load_cancel(Context *c, YYLTYPE *locp) { - OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_store_s1) {\n"); + OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_scalar_store_s1) {\n"); OUT(c, locp, "ctx->s1_store_processed = false;\n"); OUT(c, locp, "process_store(ctx, 1);\n"); OUT(c, locp, "}\n"); @@ -1750,7 +1750,7 @@ void gen_load(Context *c, YYLTYPE *locp, HexValue *width, /* Lookup the effective address EA */ find_variable(c, locp, ea, ea); - OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_store_s1) {\n"); + OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_scalar_store_s1) {\n"); OUT(c, locp, "probe_noshuf_load(", ea, ", ", width, ", ctx->mem_idx);\n"); OUT(c, locp, "process_store(ctx, 1);\n"); OUT(c, locp, "}\n"); diff --git a/target/hexagon/insn.h b/target/hexagon/insn.h index 24dcf7fe9f385..5d59430da9e12 100644 --- a/target/hexagon/insn.h +++ b/target/hexagon/insn.h @@ -66,8 +66,8 @@ struct Packet { bool pkt_has_dczeroa; - bool pkt_has_store_s0; - bool pkt_has_store_s1; + bool pkt_has_scalar_store_s0; + bool pkt_has_scalar_store_s1; bool pkt_has_hvx; Insn *vhist_insn; diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index 9ba9be408db4c..088e5961ab7af 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -83,7 +83,7 @@ */ #define CHECK_NOSHUF(VA, SIZE) \ do { \ - if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \ + if (insn->slot == 0 && ctx->pkt->pkt_has_scalar_store_s1) { \ probe_noshuf_load(VA, SIZE, ctx->mem_idx); \ process_store(ctx, 1); \ } \ @@ -94,11 +94,11 @@ TCGLabel *noshuf_label = gen_new_label(); \ tcg_gen_brcondi_tl(TCG_COND_EQ, PRED, 0, noshuf_label); \ GET_EA; \ - if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \ + if (insn->slot == 0 && ctx->pkt->pkt_has_scalar_store_s1) { \ probe_noshuf_load(EA, SIZE, ctx->mem_idx); \ } \ gen_set_label(noshuf_label); \ - if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \ + if (insn->slot == 0 && ctx->pkt->pkt_has_scalar_store_s1) { \ process_store(ctx, 1); \ } \ } while (0) @@ -525,7 +525,7 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #define fLOAD(NUM, SIZE, SIGN, EA, DST) \ do { \ - check_noshuf(env, pkt_has_store_s1, slot, EA, SIZE, GETPC()); \ + check_noshuf(env, pkt_has_scalar_store_s1, slot, EA, SIZE, GETPC()); \ DST = (size##SIZE##SIGN##_t)MEM_LOAD##SIZE(env, EA, GETPC()); \ } while (0) #endif diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 444799d3adec8..e2e80ca7efac3 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -463,11 +463,11 @@ void HELPER(probe_pkt_scalar_hvx_stores)(CPUHexagonState *env, int mask) * If the load is in slot 0 and there is a store in slot1 (that * wasn't cancelled), we have to do the store first. */ -static void check_noshuf(CPUHexagonState *env, bool pkt_has_store_s1, +static void check_noshuf(CPUHexagonState *env, bool pkt_has_scalar_store_s1, uint32_t slot, target_ulong vaddr, int size, uintptr_t ra) { - if (slot == 0 && pkt_has_store_s1 && + if (slot == 0 && pkt_has_scalar_store_s1 && ((env->slot_cancelled & (1 << 1)) == 0)) { probe_read(env, vaddr, size, MMU_USER_IDX, ra); commit_store(env, 1, ra); diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 50766eafe27f3..8fce219c0de48 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -693,11 +693,11 @@ static void process_store_log(DisasContext *ctx) * the memory accesses overlap. */ Packet *pkt = ctx->pkt; - if (pkt->pkt_has_store_s1) { + if (pkt->pkt_has_scalar_store_s1) { g_assert(!pkt->pkt_has_dczeroa); process_store(ctx, 1); } - if (pkt->pkt_has_store_s0) { + if (pkt->pkt_has_scalar_store_s0) { g_assert(!pkt->pkt_has_dczeroa); process_store(ctx, 0); } @@ -822,8 +822,9 @@ static void gen_commit_packet(DisasContext *ctx) * involved in committing the packet. */ Packet *pkt = ctx->pkt; - bool has_store_s0 = pkt->pkt_has_store_s0; - bool has_store_s1 = (pkt->pkt_has_store_s1 && !ctx->s1_store_processed); + bool has_store_s0 = pkt->pkt_has_scalar_store_s0; + bool has_store_s1 = + (pkt->pkt_has_scalar_store_s1 && !ctx->s1_store_processed); bool has_hvx_store = pkt_has_hvx_store(pkt); if (pkt->pkt_has_dczeroa) { /* From e01046a1400e40315d3d4be13b47473ade76b7a7 Mon Sep 17 00:00:00 2001 From: Anton Johansson Date: Wed, 2 Apr 2025 13:42:58 +0200 Subject: [PATCH 1697/1794] target/hexagon: Replace `prepare` script with meson target The purpose of the prepare script is to invoke `cpp` to preprocess input to idef-parser by expanding a few select macros. On macOS `cpp` expands into `clang ... -traditional-cpp` which breaks macro concatenation. Replace `cpp` with `${compiler} -E` and replace the script with a meson custom_target. Signed-off-by: Anton Johansson Reviewed-by: Brian Cain Signed-off-by: Brian Cain --- target/hexagon/idef-parser/prepare | 24 ------------------------ target/hexagon/meson.build | 3 ++- 2 files changed, 2 insertions(+), 25 deletions(-) delete mode 100755 target/hexagon/idef-parser/prepare diff --git a/target/hexagon/idef-parser/prepare b/target/hexagon/idef-parser/prepare deleted file mode 100755 index cb3622d4f8d9e..0000000000000 --- a/target/hexagon/idef-parser/prepare +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -# -# Copyright(c) 2019-2021 rev.ng Labs Srl. All Rights Reserved. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . -# - -set -e -set -o pipefail - -# Run the preprocessor and drop comments -cpp "$@" diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index bb4ebaae816ea..abcf00ca1f69e 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build @@ -280,12 +280,13 @@ if idef_parser_enabled and 'hexagon-linux-user' in target_dirs command: [python, files('gen_idef_parser_funcs.py'), semantics_generated, '@OUTPUT@'], ) + compiler = meson.get_compiler('c').get_id() preprocessed_idef_parser_input_generated = custom_target( 'idef_parser_input.preprocessed.h.inc', output: 'idef_parser_input.preprocessed.h.inc', input: idef_parser_input_generated, depend_files: [idef_parser_dir / 'macros.h.inc'], - command: [idef_parser_dir / 'prepare', '@INPUT@', '-I' + idef_parser_dir, '-o', '@OUTPUT@'], + command: [compiler, '-x', 'c', '-E', '-I', idef_parser_dir, '-o', '@OUTPUT@', '@INPUT@'], ) flex = generator( From f97700e0752753e294db11de8462aef5d8009a89 Mon Sep 17 00:00:00 2001 From: Anton Johansson Date: Wed, 2 Apr 2025 13:42:59 +0200 Subject: [PATCH 1698/1794] target/hexagon: Only indent on linux indent on macOS, installed via homebrew, doesn't support -linux. Only run indent on linux hosts. Signed-off-by: Anton Johansson Reviewed-by: Brian Cain Signed-off-by: Brian Cain --- target/hexagon/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index abcf00ca1f69e..d26787a9b948e 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build @@ -324,7 +324,7 @@ if idef_parser_enabled and 'hexagon-linux-user' in target_dirs ) indent = find_program('indent', required: false) - if indent.found() + if indent.found() and host_os == 'linux' idef_generated_tcg_c = custom_target( 'indent', input: idef_generated_tcg[0], From f8a5ba111596ca788524e7e4d7d31d9ffe8e2a79 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 15 Oct 2025 11:55:33 -0700 Subject: [PATCH 1699/1794] tcg/ppc: Remove dead cases from tcg_target_op_def MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missed some lines when converting to TCGOutOpQemuLdSt*. Fixes: 86fe5c2597c ("tcg: Convert qemu_st{2} to TCGOutOpLdSt{2}") Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/ppc/tcg-target.c.inc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index b8b23d44d5e2d..cd2b68f9e12a3 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -4435,12 +4435,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_st: - return C_O0_I2(r, r); - case INDEX_op_qemu_st2: - return TCG_TARGET_REG_BITS == 64 - ? C_O0_I3(o, m, r) : C_O0_I3(r, r, r); - case INDEX_op_add_vec: case INDEX_op_sub_vec: case INDEX_op_mul_vec: From 5c1ec5a1ee0958068a532ac1e06cc6cee0c75ba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 14 Oct 2025 19:38:57 +0200 Subject: [PATCH 1700/1794] tcg/ppc: Remove support for 32-bit hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 32-bit host support is deprecated since commit 6d701c9bac1 ("meson: Deprecate 32-bit host support"), released as v10.0. The next release being v10.2, we can remove the TCG backend for 32-bit PPC hosts. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-ID: <20251014173900.87497-2-philmd@linaro.org> --- tcg/ppc/tcg-target-has.h | 5 +- tcg/ppc/tcg-target-reg-bits.h | 8 +- tcg/ppc/tcg-target.c.inc | 385 +++++++--------------------------- 3 files changed, 79 insertions(+), 319 deletions(-) diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 81ec5aece7adf..a3711feeae2b6 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -17,12 +17,9 @@ #define have_vsx (cpuinfo & CPUINFO_VSX) /* optional instructions */ -#if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_extr_i64_i32 0 -#endif -#define TCG_TARGET_HAS_qemu_ldst_i128 \ - (TCG_TARGET_REG_BITS == 64 && have_isa_2_07) +#define TCG_TARGET_HAS_qemu_ldst_i128 have_isa_2_07 #define TCG_TARGET_HAS_tst 1 diff --git a/tcg/ppc/tcg-target-reg-bits.h b/tcg/ppc/tcg-target-reg-bits.h index 0efa80e7e07f3..3a15d7bee41ec 100644 --- a/tcg/ppc/tcg-target-reg-bits.h +++ b/tcg/ppc/tcg-target-reg-bits.h @@ -7,10 +7,10 @@ #ifndef TCG_TARGET_REG_BITS_H #define TCG_TARGET_REG_BITS_H -#ifdef _ARCH_PPC64 -# define TCG_TARGET_REG_BITS 64 -#else -# define TCG_TARGET_REG_BITS 32 +#ifndef _ARCH_PPC64 +# error Expecting 64-bit host architecture #endif +#define TCG_TARGET_REG_BITS 64 + #endif diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index cd2b68f9e12a3..3c36b26f25c5c 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -29,35 +29,18 @@ * Apple XCode does not define _CALL_DARWIN. * Clang defines _CALL_ELF (64-bit) but not _CALL_SYSV or _CALL_AIX. */ -#if TCG_TARGET_REG_BITS == 64 -# ifdef _CALL_AIX +#ifdef _CALL_AIX /* ok */ -# elif defined(_CALL_ELF) && _CALL_ELF == 1 +#elif defined(_CALL_ELF) && _CALL_ELF == 1 # define _CALL_AIX -# elif defined(_CALL_ELF) && _CALL_ELF == 2 +#elif defined(_CALL_ELF) && _CALL_ELF == 2 /* ok */ -# else -# error "Unknown ABI" -# endif #else -# if defined(_CALL_SYSV) || defined(_CALL_DARWIN) - /* ok */ -# elif defined(__APPLE__) -# define _CALL_DARWIN -# elif defined(__ELF__) -# define _CALL_SYSV -# else # error "Unknown ABI" -# endif #endif -#if TCG_TARGET_REG_BITS == 64 -# define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_EXTEND -# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL -#else -# define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL -# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_REF -#endif +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_EXTEND +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL #ifdef _CALL_SYSV # define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_EVEN # define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_BY_REF @@ -81,7 +64,7 @@ #define TCG_VEC_TMP2 TCG_REG_V1 #define TCG_REG_TB TCG_REG_R31 -#define USE_REG_TB (TCG_TARGET_REG_BITS == 64 && !have_isa_3_00) +#define USE_REG_TB !have_isa_3_00 /* Shorthand for size of a pointer. Avoid promotion to unsigned. */ #define SZP ((int)sizeof(void *)) @@ -327,8 +310,7 @@ static bool tcg_target_const_match(int64_t sval, int ct, if (uval == (uint32_t)uval && mask_operand(uval, &mb, &me)) { return 1; } - if (TCG_TARGET_REG_BITS == 64 && - mask64_operand(uval << clz64(uval), &mb, &me)) { + if (mask64_operand(uval << clz64(uval), &mb, &me)) { return 1; } return 0; @@ -857,10 +839,8 @@ static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) return true; } switch (type) { - case TCG_TYPE_I64: - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); - /* fallthru */ case TCG_TYPE_I32: + case TCG_TYPE_I64: if (ret < TCG_REG_V0) { if (arg < TCG_REG_V0) { tcg_out32(s, OR | SAB(arg, ret, arg)); @@ -898,7 +878,6 @@ static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) static void tcg_out_rld_rc(TCGContext *s, int op, TCGReg ra, TCGReg rs, int sh, int mb, bool rc) { - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); sh = SH(sh & 0x1f) | (((sh >> 5) & 1) << 1); mb = MB64((mb >> 5) | ((mb << 1) & 0x3f)); tcg_out32(s, op | RA(ra) | RS(rs) | sh | mb | rc); @@ -946,13 +925,11 @@ static void tcg_out_ext16u(TCGContext *s, TCGReg dst, TCGReg src) static void tcg_out_ext32s(TCGContext *s, TCGReg dst, TCGReg src) { - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); tcg_out32(s, EXTSW | RA(dst) | RS(src)); } static void tcg_out_ext32u(TCGContext *s, TCGReg dst, TCGReg src) { - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); tcg_out_rld(s, RLDICL, dst, src, 0, 32); } @@ -968,7 +945,6 @@ static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg dst, TCGReg src) static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rn) { - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); tcg_out_mov(s, TCG_TYPE_I32, rd, rn); } @@ -1037,9 +1013,7 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, tcg_target_long tmp; int shift; - tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); - - if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { + if (type == TCG_TYPE_I32) { arg = (int32_t)arg; } @@ -1076,7 +1050,7 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, /* Load 32-bit immediates with two insns. Note that we've already eliminated bare ADDIS, so we know both insns are required. */ - if (TCG_TARGET_REG_BITS == 32 || arg == (int32_t)arg) { + if (arg == (int32_t)arg) { tcg_out32(s, ADDIS | TAI(ret, 0, arg >> 16)); tcg_out32(s, ORI | SAI(ret, ret, arg)); return; @@ -1227,19 +1201,10 @@ static void tcg_out_dupi_vec(TCGContext *s, TCGType type, unsigned vece, if (have_vsx) { load_insn = type == TCG_TYPE_V64 ? LXSDX : LXVDSX; load_insn |= VRT(ret) | RB(TCG_REG_TMP1); - if (TCG_TARGET_REG_BITS == 64) { - new_pool_label(s, val, rel, s->code_ptr, add); - } else { - new_pool_l2(s, rel, s->code_ptr, add, val >> 32, val); - } + new_pool_label(s, val, rel, s->code_ptr, add); } else { load_insn = LVX | VRT(ret) | RB(TCG_REG_TMP1); - if (TCG_TARGET_REG_BITS == 64) { - new_pool_l2(s, rel, s->code_ptr, add, val, val); - } else { - new_pool_l4(s, rel, s->code_ptr, add, - val >> 32, val, val >> 32, val); - } + new_pool_l2(s, rel, s->code_ptr, add, val, val); } if (USE_REG_TB) { @@ -1351,7 +1316,6 @@ static void tcg_out_andi64(TCGContext *s, TCGReg dst, TCGReg src, uint64_t c) { int mb, me; - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); if (mask64_operand(c, &mb, &me)) { if (mb == 0) { tcg_out_rld(s, RLDICR, dst, src, 0, me); @@ -1543,7 +1507,6 @@ static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, break; case TCG_TYPE_I64: if (ret < TCG_REG_V0) { - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); tcg_out_mem_long(s, LD, LDX, ret, base, offset); break; } @@ -1598,7 +1561,6 @@ static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, break; case TCG_TYPE_I64: if (arg < TCG_REG_V0) { - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); tcg_out_mem_long(s, STD, STDX, arg, base, offset); break; } @@ -1641,7 +1603,7 @@ static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, static void tcg_out_test(TCGContext *s, TCGReg dest, TCGReg arg1, TCGArg arg2, bool const_arg2, TCGType type, bool rc) { - int mb, me; + int mb, me, sh; if (!const_arg2) { tcg_out32(s, AND | SAB(arg1, dest, arg2) | rc); @@ -1664,12 +1626,10 @@ static void tcg_out_test(TCGContext *s, TCGReg dest, TCGReg arg1, TCGArg arg2, tcg_out_rlw_rc(s, RLWINM, dest, arg1, 0, mb, me, rc); return; } - if (TCG_TARGET_REG_BITS == 64) { - int sh = clz64(arg2); - if (mask64_operand(arg2 << sh, &mb, &me)) { - tcg_out_rld_rc(s, RLDICR, dest, arg1, sh, me, rc); - return; - } + sh = clz64(arg2); + if (mask64_operand(arg2 << sh, &mb, &me)) { + tcg_out_rld_rc(s, RLDICR, dest, arg1, sh, me, rc); + return; } /* Constraints should satisfy this. */ g_assert_not_reached(); @@ -1680,8 +1640,6 @@ static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, { uint32_t op; - tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); - /* * Simplify the comparisons below wrt CMPI. * All of the tests are 16-bit, so a 32-bit sign extend always works. @@ -1747,7 +1705,7 @@ static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, static void tcg_out_setcond_eq0(TCGContext *s, TCGType type, TCGReg dst, TCGReg src, bool neg) { - if (neg && (TCG_TARGET_REG_BITS == 32 || type == TCG_TYPE_I64)) { + if (neg && type == TCG_TYPE_I64) { /* * X != 0 implies X + -1 generates a carry. * RT = (~X + X) + CA @@ -1774,7 +1732,7 @@ static void tcg_out_setcond_eq0(TCGContext *s, TCGType type, static void tcg_out_setcond_ne0(TCGContext *s, TCGType type, TCGReg dst, TCGReg src, bool neg) { - if (!neg && (TCG_TARGET_REG_BITS == 32 || type == TCG_TYPE_I64)) { + if (!neg && type == TCG_TYPE_I64) { /* * X != 0 implies X + -1 generates a carry. Extra addition * trickery means: R = X-1 + ~X + C = X-1 + (-X+1) + C = C. @@ -1814,8 +1772,6 @@ static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, int sh; bool inv; - tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); - /* Ignore high bits of a potential constant arg2. */ if (type == TCG_TYPE_I32) { arg2 = (uint32_t)arg2; @@ -2117,109 +2073,6 @@ static void tcg_out_cntxz(TCGContext *s, TCGType type, uint32_t opc, } } -static void tcg_out_cmp2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, - TCGArg bl, bool blconst, TCGArg bh, bool bhconst) -{ - static const struct { uint8_t bit1, bit2; } bits[] = { - [TCG_COND_LT ] = { CR_LT, CR_LT }, - [TCG_COND_LE ] = { CR_LT, CR_GT }, - [TCG_COND_GT ] = { CR_GT, CR_GT }, - [TCG_COND_GE ] = { CR_GT, CR_LT }, - [TCG_COND_LTU] = { CR_LT, CR_LT }, - [TCG_COND_LEU] = { CR_LT, CR_GT }, - [TCG_COND_GTU] = { CR_GT, CR_GT }, - [TCG_COND_GEU] = { CR_GT, CR_LT }, - }; - - TCGCond cond2; - int op, bit1, bit2; - - switch (cond) { - case TCG_COND_EQ: - op = CRAND; - goto do_equality; - case TCG_COND_NE: - op = CRNAND; - do_equality: - tcg_out_cmp(s, cond, al, bl, blconst, 6, TCG_TYPE_I32); - tcg_out_cmp(s, cond, ah, bh, bhconst, 7, TCG_TYPE_I32); - tcg_out32(s, op | BT(0, CR_EQ) | BA(6, CR_EQ) | BB(7, CR_EQ)); - break; - - case TCG_COND_TSTEQ: - case TCG_COND_TSTNE: - if (blconst) { - tcg_out_andi32(s, TCG_REG_R0, al, bl); - } else { - tcg_out32(s, AND | SAB(al, TCG_REG_R0, bl)); - } - if (bhconst) { - tcg_out_andi32(s, TCG_REG_TMP1, ah, bh); - } else { - tcg_out32(s, AND | SAB(ah, TCG_REG_TMP1, bh)); - } - tcg_out32(s, OR | SAB(TCG_REG_R0, TCG_REG_R0, TCG_REG_TMP1) | 1); - break; - - case TCG_COND_LT: - case TCG_COND_LE: - case TCG_COND_GT: - case TCG_COND_GE: - case TCG_COND_LTU: - case TCG_COND_LEU: - case TCG_COND_GTU: - case TCG_COND_GEU: - bit1 = bits[cond].bit1; - bit2 = bits[cond].bit2; - op = (bit1 != bit2 ? CRANDC : CRAND); - cond2 = tcg_unsigned_cond(cond); - - tcg_out_cmp(s, cond, ah, bh, bhconst, 6, TCG_TYPE_I32); - tcg_out_cmp(s, cond2, al, bl, blconst, 7, TCG_TYPE_I32); - tcg_out32(s, op | BT(0, CR_EQ) | BA(6, CR_EQ) | BB(7, bit2)); - tcg_out32(s, CROR | BT(0, CR_EQ) | BA(6, bit1) | BB(0, CR_EQ)); - break; - - default: - g_assert_not_reached(); - } -} - -static void tgen_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg al, TCGReg ah, - TCGArg bl, bool const_bl, - TCGArg bh, bool const_bh) -{ - tcg_out_cmp2(s, cond, al, ah, bl, const_bl, bh, const_bh); - tcg_out32(s, MFOCRF | RT(TCG_REG_R0) | FXM(0)); - tcg_out_rlw(s, RLWINM, ret, TCG_REG_R0, CR_EQ + 0*4 + 1, 31, 31); -} - -#if TCG_TARGET_REG_BITS != 32 -__attribute__((unused)) -#endif -static const TCGOutOpSetcond2 outop_setcond2 = { - .base.static_constraint = C_O1_I4(r, r, r, rU, rC), - .out = tgen_setcond2, -}; - -static void tgen_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, - TCGArg bl, bool const_bl, - TCGArg bh, bool const_bh, TCGLabel *l) -{ - assert(TCG_TARGET_REG_BITS == 32); - tcg_out_cmp2(s, cond, al, ah, bl, const_bl, bh, const_bh); - tcg_out_bc_lab(s, TCG_COND_EQ, l); -} - -#if TCG_TARGET_REG_BITS != 32 -__attribute__((unused)) -#endif -static const TCGOutOpBrcond2 outop_brcond2 = { - .base.static_constraint = C_O0_I4(r, r, rU, rC), - .out = tgen_brcond2, -}; - static void tcg_out_mb(TCGContext *s, unsigned a0) { uint32_t insn; @@ -2438,13 +2291,8 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP2, TCG_AREG0, table_off); /* Extract the page index, shifted into place for tlb index. */ - if (TCG_TARGET_REG_BITS == 32) { - tcg_out_shri32(s, TCG_REG_R0, addr, - TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - } else { - tcg_out_shri64(s, TCG_REG_R0, addr, - TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - } + tcg_out_shri64(s, TCG_REG_R0, addr, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); tcg_out32(s, AND | SAB(TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_R0)); /* @@ -2453,8 +2301,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, * We will ignore the high bits with tcg_out_cmp(..., addr_type). */ if (cmp_off == 0) { - tcg_out32(s, (TCG_TARGET_REG_BITS == 64 ? LDUX : LWZUX) - | TAB(TCG_REG_TMP2, TCG_REG_TMP1, TCG_REG_TMP2)); + tcg_out32(s, LDUX | TAB(TCG_REG_TMP2, TCG_REG_TMP1, TCG_REG_TMP2)); } else { tcg_out32(s, ADD | TAB(TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_TMP2)); tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP2, TCG_REG_TMP1, cmp_off); @@ -2464,51 +2311,36 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, * Load the TLB addend for use on the fast path. * Do this asap to minimize any load use delay. */ - if (TCG_TARGET_REG_BITS == 64 || addr_type == TCG_TYPE_I32) { - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_REG_TMP1, - offsetof(CPUTLBEntry, addend)); - } + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_REG_TMP1, + offsetof(CPUTLBEntry, addend)); /* Clear the non-page, non-alignment bits from the address in R0. */ - if (TCG_TARGET_REG_BITS == 32) { - /* - * We don't support unaligned accesses on 32-bits. - * Preserve the bottom bits and thus trigger a comparison - * failure on unaligned accesses. - */ - if (a_bits < s_bits) { - a_bits = s_bits; - } - tcg_out_rlw(s, RLWINM, TCG_REG_R0, addr, 0, + TCGReg t = addr; + + /* + * If the access is unaligned, we need to make sure we fail if we + * cross a page boundary. The trick is to add the access size-1 + * to the address before masking the low bits. That will make the + * address overflow to the next page if we cross a page boundary, + * which will then force a mismatch of the TLB compare. + */ + if (a_bits < s_bits) { + unsigned a_mask = (1 << a_bits) - 1; + unsigned s_mask = (1 << s_bits) - 1; + tcg_out32(s, ADDI | TAI(TCG_REG_R0, t, s_mask - a_mask)); + t = TCG_REG_R0; + } + + /* Mask the address for the requested alignment. */ + if (addr_type == TCG_TYPE_I32) { + tcg_out_rlw(s, RLWINM, TCG_REG_R0, t, 0, (32 - a_bits) & 31, 31 - TARGET_PAGE_BITS); + } else if (a_bits == 0) { + tcg_out_rld(s, RLDICR, TCG_REG_R0, t, 0, 63 - TARGET_PAGE_BITS); } else { - TCGReg t = addr; - - /* - * If the access is unaligned, we need to make sure we fail if we - * cross a page boundary. The trick is to add the access size-1 - * to the address before masking the low bits. That will make the - * address overflow to the next page if we cross a page boundary, - * which will then force a mismatch of the TLB compare. - */ - if (a_bits < s_bits) { - unsigned a_mask = (1 << a_bits) - 1; - unsigned s_mask = (1 << s_bits) - 1; - tcg_out32(s, ADDI | TAI(TCG_REG_R0, t, s_mask - a_mask)); - t = TCG_REG_R0; - } - - /* Mask the address for the requested alignment. */ - if (addr_type == TCG_TYPE_I32) { - tcg_out_rlw(s, RLWINM, TCG_REG_R0, t, 0, - (32 - a_bits) & 31, 31 - TARGET_PAGE_BITS); - } else if (a_bits == 0) { - tcg_out_rld(s, RLDICR, TCG_REG_R0, t, 0, 63 - TARGET_PAGE_BITS); - } else { - tcg_out_rld(s, RLDICL, TCG_REG_R0, t, - 64 - TARGET_PAGE_BITS, TARGET_PAGE_BITS - a_bits); - tcg_out_rld(s, RLDICL, TCG_REG_R0, TCG_REG_R0, TARGET_PAGE_BITS, 0); - } + tcg_out_rld(s, RLDICL, TCG_REG_R0, t, + 64 - TARGET_PAGE_BITS, TARGET_PAGE_BITS - a_bits); + tcg_out_rld(s, RLDICL, TCG_REG_R0, TCG_REG_R0, TARGET_PAGE_BITS, 0); } /* Full comparison into cr0. */ @@ -2537,7 +2369,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, h->base = guest_base ? TCG_GUEST_BASE_REG : 0; } - if (TCG_TARGET_REG_BITS == 64 && addr_type == TCG_TYPE_I32) { + if (addr_type == TCG_TYPE_I32) { /* Zero-extend the guest address for use in the host address. */ tcg_out_ext32u(s, TCG_REG_TMP2, addr); h->index = TCG_REG_TMP2; @@ -2554,40 +2386,22 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; HostAddress h; + uint32_t insn = qemu_ldx_opc[opc & (MO_BSWAP | MO_SSIZE)]; ldst = prepare_host_addr(s, &h, addr, oi, true); - if (TCG_TARGET_REG_BITS == 32 && (opc & MO_SIZE) == MO_64) { - if (opc & MO_BSWAP) { - tcg_out32(s, ADDI | TAI(TCG_REG_R0, h.index, 4)); - tcg_out32(s, LWBRX | TAB(datalo, h.base, h.index)); - tcg_out32(s, LWBRX | TAB(datahi, h.base, TCG_REG_R0)); - } else if (h.base != 0) { - tcg_out32(s, ADDI | TAI(TCG_REG_R0, h.index, 4)); - tcg_out32(s, LWZX | TAB(datahi, h.base, h.index)); - tcg_out32(s, LWZX | TAB(datalo, h.base, TCG_REG_R0)); - } else if (h.index == datahi) { - tcg_out32(s, LWZ | TAI(datalo, h.index, 4)); - tcg_out32(s, LWZ | TAI(datahi, h.index, 0)); - } else { - tcg_out32(s, LWZ | TAI(datahi, h.index, 0)); - tcg_out32(s, LWZ | TAI(datalo, h.index, 4)); - } + if (!have_isa_2_06 && insn == LDBRX) { + tcg_out32(s, ADDI | TAI(TCG_REG_R0, h.index, 4)); + tcg_out32(s, LWBRX | TAB(datalo, h.base, h.index)); + tcg_out32(s, LWBRX | TAB(TCG_REG_R0, h.base, TCG_REG_R0)); + tcg_out_rld(s, RLDIMI, datalo, TCG_REG_R0, 32, 0); + } else if (insn) { + tcg_out32(s, insn | TAB(datalo, h.base, h.index)); } else { - uint32_t insn = qemu_ldx_opc[opc & (MO_BSWAP | MO_SSIZE)]; - if (!have_isa_2_06 && insn == LDBRX) { - tcg_out32(s, ADDI | TAI(TCG_REG_R0, h.index, 4)); - tcg_out32(s, LWBRX | TAB(datalo, h.base, h.index)); - tcg_out32(s, LWBRX | TAB(TCG_REG_R0, h.base, TCG_REG_R0)); - tcg_out_rld(s, RLDIMI, datalo, TCG_REG_R0, 32, 0); - } else if (insn) { - tcg_out32(s, insn | TAB(datalo, h.base, h.index)); - } else { - insn = qemu_ldx_opc[opc & (MO_SIZE | MO_BSWAP)]; - tcg_out32(s, insn | TAB(datalo, h.base, h.index)); - tcg_out_movext(s, TCG_TYPE_REG, datalo, - TCG_TYPE_REG, opc & MO_SSIZE, datalo); - } + insn = qemu_ldx_opc[opc & (MO_SIZE | MO_BSWAP)]; + tcg_out32(s, insn | TAB(datalo, h.base, h.index)); + tcg_out_movext(s, TCG_TYPE_REG, datalo, + TCG_TYPE_REG, opc & MO_SSIZE, datalo); } if (ldst) { @@ -2604,32 +2418,17 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; HostAddress h; + uint32_t insn = qemu_stx_opc[opc & (MO_BSWAP | MO_SIZE)]; ldst = prepare_host_addr(s, &h, addr, oi, false); - if (TCG_TARGET_REG_BITS == 32 && (opc & MO_SIZE) == MO_64) { - if (opc & MO_BSWAP) { - tcg_out32(s, ADDI | TAI(TCG_REG_R0, h.index, 4)); - tcg_out32(s, STWBRX | SAB(datalo, h.base, h.index)); - tcg_out32(s, STWBRX | SAB(datahi, h.base, TCG_REG_R0)); - } else if (h.base != 0) { - tcg_out32(s, ADDI | TAI(TCG_REG_R0, h.index, 4)); - tcg_out32(s, STWX | SAB(datahi, h.base, h.index)); - tcg_out32(s, STWX | SAB(datalo, h.base, TCG_REG_R0)); - } else { - tcg_out32(s, STW | TAI(datahi, h.index, 0)); - tcg_out32(s, STW | TAI(datalo, h.index, 4)); - } + if (!have_isa_2_06 && insn == STDBRX) { + tcg_out32(s, STWBRX | SAB(datalo, h.base, h.index)); + tcg_out32(s, ADDI | TAI(TCG_REG_TMP2, h.index, 4)); + tcg_out_shri64(s, TCG_REG_R0, datalo, 32); + tcg_out32(s, STWBRX | SAB(TCG_REG_R0, h.base, TCG_REG_TMP2)); } else { - uint32_t insn = qemu_stx_opc[opc & (MO_BSWAP | MO_SIZE)]; - if (!have_isa_2_06 && insn == STDBRX) { - tcg_out32(s, STWBRX | SAB(datalo, h.base, h.index)); - tcg_out32(s, ADDI | TAI(TCG_REG_TMP2, h.index, 4)); - tcg_out_shri64(s, TCG_REG_R0, datalo, 32); - tcg_out32(s, STWBRX | SAB(TCG_REG_R0, h.base, TCG_REG_TMP2)); - } else { - tcg_out32(s, insn | SAB(datalo, h.base, h.index)); - } + tcg_out32(s, insn | SAB(datalo, h.base, h.index)); } if (ldst) { @@ -2709,16 +2508,11 @@ static const TCGOutOpQemuLdSt outop_qemu_ld = { static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, TCGReg datahi, TCGReg addr, MemOpIdx oi) { - if (TCG_TARGET_REG_BITS == 32) { - tcg_out_qemu_ld(s, datalo, datahi, addr, oi, type); - } else { - tcg_out_qemu_ldst_i128(s, datalo, datahi, addr, oi, true); - } + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr, oi, true); } static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { - .base.static_constraint = - TCG_TARGET_REG_BITS == 64 ? C_N1O1_I1(o, m, r) : C_O2_I1(r, r, r), + .base.static_constraint = C_N1O1_I1(o, m, r), .out = tgen_qemu_ld2, }; @@ -2736,16 +2530,11 @@ static const TCGOutOpQemuLdSt outop_qemu_st = { static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, TCGReg datahi, TCGReg addr, MemOpIdx oi) { - if (TCG_TARGET_REG_BITS == 32) { - tcg_out_qemu_st(s, datalo, datahi, addr, oi, type); - } else { - tcg_out_qemu_ldst_i128(s, datalo, datahi, addr, oi, false); - } + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr, oi, false); } static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { - .base.static_constraint = - TCG_TARGET_REG_BITS == 64 ? C_O0_I3(o, m, r) : C_O0_I3(r, r, r), + .base.static_constraint = C_O0_I3(o, m, r), .out = tgen_qemu_st2, }; @@ -2767,16 +2556,11 @@ static void tcg_out_nop_fill(tcg_insn_unit *p, int count) #elif defined(_CALL_DARWIN) # define LINK_AREA_SIZE (6 * SZR) # define LR_OFFSET (2 * SZR) -#elif TCG_TARGET_REG_BITS == 64 +#else # if defined(_CALL_ELF) && _CALL_ELF == 2 # define LINK_AREA_SIZE (4 * SZR) # define LR_OFFSET (1 * SZR) # endif -#else /* TCG_TARGET_REG_BITS == 32 */ -# if defined(_CALL_SYSV) -# define LINK_AREA_SIZE (2 * SZR) -# define LR_OFFSET (1 * SZR) -# endif #endif #ifndef LR_OFFSET # error "Unhandled abi" @@ -3107,7 +2891,6 @@ static void tgen_eqv(TCGContext *s, TCGType type, tcg_out32(s, EQV | SAB(a1, a0, a2)); } -#if TCG_TARGET_REG_BITS == 64 static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) { tcg_out_shri64(s, a0, a1, 32); @@ -3117,7 +2900,6 @@ static const TCGOutOpUnary outop_extrh_i64_i32 = { .base.static_constraint = C_O1_I1(r, r), .out_rr = tgen_extrh_i64_i32, }; -#endif static void tgen_divs(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) @@ -3596,7 +3378,6 @@ static const TCGOutOpBswap outop_bswap32 = { .out_rr = tgen_bswap32, }; -#if TCG_TARGET_REG_BITS == 64 static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg dst, TCGReg src) { TCGReg t0 = dst == src ? TCG_REG_R0 : dst; @@ -3639,7 +3420,6 @@ static const TCGOutOpUnary outop_bswap64 = { .base.static_constraint = C_O1_I1(r, r), .out_rr = tgen_bswap64, }; -#endif /* TCG_TARGET_REG_BITS == 64 */ static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { @@ -3776,7 +3556,6 @@ static const TCGOutOpLoad outop_ld16s = { .out = tgen_ld16s, }; -#if TCG_TARGET_REG_BITS == 64 static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, TCGReg base, ptrdiff_t offset) { @@ -3798,7 +3577,6 @@ static const TCGOutOpLoad outop_ld32s = { .base.static_constraint = C_O1_I1(r, r), .out = tgen_ld32s, }; -#endif static void tgen_st8(TCGContext *s, TCGType type, TCGReg data, TCGReg base, ptrdiff_t offset) @@ -4278,14 +4056,6 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, tcg_out_bitsel_vec(s, a0, a1, a2, args[3]); return; - case INDEX_op_dup2_vec: - assert(TCG_TARGET_REG_BITS == 32); - /* With inputs a1 = xLxx, a2 = xHxx */ - tcg_out32(s, VMRGHW | VRT(a0) | VRA(a2) | VRB(a1)); /* a0 = xxHL */ - tcg_out_vsldoi(s, TCG_VEC_TMP1, a0, a0, 8); /* tmp = HLxx */ - tcg_out_vsldoi(s, a0, a0, TCG_VEC_TMP1, 8); /* a0 = HLHL */ - return; - case INDEX_op_ppc_mrgh_vec: insn = mrgh_op[vece]; break; @@ -4465,7 +4235,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ppc_muleu_vec: case INDEX_op_ppc_mulou_vec: case INDEX_op_ppc_pkum_vec: - case INDEX_op_dup2_vec: return C_O1_I2(v, v, v); case INDEX_op_not_vec: @@ -4543,9 +4312,7 @@ static void tcg_target_init(TCGContext *s) #if defined(_CALL_SYSV) tcg_regset_set_reg(s->reserved_regs, TCG_REG_R2); /* toc pointer */ #endif -#if defined(_CALL_SYSV) || TCG_TARGET_REG_BITS == 64 tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); /* thread pointer */ -#endif tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP1); tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP2); tcg_regset_set_reg(s->reserved_regs, TCG_VEC_TMP1); @@ -4566,11 +4333,7 @@ typedef struct { /* We're expecting a 2 byte uleb128 encoded value. */ QEMU_BUILD_BUG_ON(FRAME_SIZE >= (1 << 14)); -#if TCG_TARGET_REG_BITS == 64 -# define ELF_HOST_MACHINE EM_PPC64 -#else -# define ELF_HOST_MACHINE EM_PPC -#endif +#define ELF_HOST_MACHINE EM_PPC64 static DebugFrame debug_frame = { .cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ From 3a2d5612a7422732b648b46d4b934e2e54622fd6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 17 Oct 2025 14:31:56 +0100 Subject: [PATCH 1701/1794] .gitlab-ci.d/buildtest.yml: Install 'file' for the Coverity job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The coverity-scan upload job has started failing as of 30th September: [ERROR] Cannot find '/usr/bin/file' command, and no 'file' command is found in the PATH Coverity Capture uses this tool to identify the file type of executables. Please ensure '/usr/bin/file' is available, or add the 'file' utility to your PATH. This seems to have broken when we moved our containers from Fedora 40 to 41 -- probably F40 indirectly pulled in 'file' via some other dependency, but in F41 it does not. Explicitly install 'file' for the coverity job, in the same way we already do for curl and wget. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-ID: <20251017133156.926094-1-peter.maydell@linaro.org> --- .gitlab-ci.d/buildtest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 0502094b9aa67..21f6d7e96fedc 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -759,7 +759,7 @@ coverity: - job: amd64-fedora-container optional: true before_script: - - dnf install -y curl wget + - dnf install -y curl wget file script: # would be nice to cancel the job if over quota (https://gitlab.com/gitlab-org/gitlab/-/issues/256089) # for example: From b83d8be7a73d977440cd852620a0142a1e551434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Oct 2025 16:03:46 +0100 Subject: [PATCH 1702/1794] gitlab: use template for ubuntu-24.04-aarch64 jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of the test is pure boilerplate so to save ourselves from repetition move all the main bits into a minimal copy of native_build_job_template but without the caching. We keep all the current allow_fail and configure setups but do take the opportunity to replace the -j`nproc --ignore=40` hack with something that almost, but not quite, saturates the machine its being built on. Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-ID: <20251016150357.876415-2-alex.bennee@linaro.org> Signed-off-by: Alex Bennée --- .../custom-runners/ubuntu-24.04-aarch64.yml | 230 ++++++++---------- 1 file changed, 96 insertions(+), 134 deletions(-) diff --git a/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch64.yml b/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch64.yml index d26c7827f45b8..46db9ae0138c6 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch64.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch64.yml @@ -2,150 +2,112 @@ # setup by the scripts/ci/setup/ubuntu/build-environment.yml task # "Install basic packages to build QEMU on Ubuntu 24.04" +.ubuntu_aarch64_template: + extends: .custom_runner_template + needs: [] + stage: build + tags: + - ubuntu_24.04 + - aarch64 + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + - if: "$AARCH64_RUNNER_AVAILABLE" + before_script: + - source scripts/ci/gitlab-ci-section + - section_start setup "Pre-script setup" + - JOBS=$(expr $(nproc) - 4) + - section_end setup + script: + - mkdir build + - cd build + - section_start configure "Running configure" + - ../configure $CONFIGURE_ARGS || + { cat config.log meson-logs/meson-log.txt && exit 1; } + - section_end configure + - section_start build "Building QEMU" + - make --output-sync -j"$JOBS" + - section_end build + - section_start test "Running tests" + - if test -n "$MAKE_CHECK_ARGS"; + then + make -j"$JOBS" $MAKE_CHECK_ARGS ; + fi + - section_end test + ubuntu-24.04-aarch64-all-linux-static: - extends: .custom_runner_template - needs: [] - stage: build - tags: - - ubuntu_24.04 - - aarch64 - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - - if: "$AARCH64_RUNNER_AVAILABLE" - script: - - mkdir build - - cd build - # Disable -static-pie due to build error with system libc: - # https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/1987438 - - ../configure --enable-debug --static --disable-system --disable-pie - || { cat config.log meson-logs/meson-log.txt; exit 1; } - - make --output-sync -j`nproc --ignore=40` - - make check-tcg - - make --output-sync -j`nproc --ignore=40` check + extends: .ubuntu_aarch64_template + variables: + # Disable -static-pie due to build error with system libc: + # https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/1987438 + CONFIGURE_ARGS: --enable-debug --static --disable-system --disable-pie + MAKE_CHECK_ARGS: check-tcg ubuntu-24.04-aarch64-all: - extends: .custom_runner_template - needs: [] - stage: build - tags: - - ubuntu_24.04 - - aarch64 - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - when: manual - allow_failure: true - - if: "$AARCH64_RUNNER_AVAILABLE" - when: manual - allow_failure: true - script: - - mkdir build - - cd build - - ../configure - || { cat config.log meson-logs/meson-log.txt; exit 1; } - - make --output-sync -j`nproc --ignore=40` - - make --output-sync -j`nproc --ignore=40` check + extends: .ubuntu_aarch64_template + variables: + MAKE_CHECK_ARGS: check + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + when: manual + allow_failure: true + - if: "$AARCH64_RUNNER_AVAILABLE" + when: manual + allow_failure: true ubuntu-24.04-aarch64-without-defaults: - extends: .custom_runner_template - needs: [] - stage: build - tags: - - ubuntu_24.04 - - aarch64 - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - when: manual - allow_failure: true - - if: "$AARCH64_RUNNER_AVAILABLE" - when: manual - allow_failure: true - script: - - mkdir build - - cd build - - ../configure --disable-user --without-default-devices --without-default-features - || { cat config.log meson-logs/meson-log.txt; exit 1; } - - make --output-sync -j`nproc --ignore=40` - - make --output-sync -j`nproc --ignore=40` check + extends: .ubuntu_aarch64_template + variables: + CONFIGURE_ARGS: --disable-user --without-default-devices --without-default-features + MAKE_CHECK_ARGS: check + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + when: manual + allow_failure: true + - if: "$AARCH64_RUNNER_AVAILABLE" + when: manual + allow_failure: true ubuntu-24.04-aarch64-alldbg: - extends: .custom_runner_template - needs: [] - stage: build - tags: - - ubuntu_24.04 - - aarch64 - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - - if: "$AARCH64_RUNNER_AVAILABLE" - script: - - mkdir build - - cd build - - ../configure --enable-debug - || { cat config.log meson-logs/meson-log.txt; exit 1; } - - make clean - - make --output-sync -j`nproc --ignore=40` - - make --output-sync -j`nproc --ignore=40` check + extends: .ubuntu_aarch64_template + variables: + CONFIGURE_ARGS: --enable-debug + MAKE_CHECK_ARGS: check-tcg ubuntu-24.04-aarch64-clang: - extends: .custom_runner_template - needs: [] - stage: build - tags: - - ubuntu_24.04 - - aarch64 - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - when: manual - allow_failure: true - - if: "$AARCH64_RUNNER_AVAILABLE" - when: manual - allow_failure: true - script: - - mkdir build - - cd build - - ../configure --disable-libssh --cc=clang --cxx=clang++ --enable-ubsan - || { cat config.log meson-logs/meson-log.txt; exit 1; } - - make --output-sync -j`nproc --ignore=40` - - make --output-sync -j`nproc --ignore=40` check + extends: .ubuntu_aarch64_template + variables: + CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-ubsan + MAKE_CHECK_ARGS: check + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + when: manual + allow_failure: true + - if: "$AARCH64_RUNNER_AVAILABLE" + when: manual + allow_failure: true ubuntu-24.04-aarch64-tci: - needs: [] - stage: build - tags: - - ubuntu_24.04 - - aarch64 - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - when: manual - allow_failure: true - - if: "$AARCH64_RUNNER_AVAILABLE" - when: manual - allow_failure: true - script: - - mkdir build - - cd build - - ../configure --enable-tcg-interpreter - || { cat config.log meson-logs/meson-log.txt; exit 1; } - - make --output-sync -j`nproc --ignore=40` + extends: .ubuntu_aarch64_template + variables: + CONFIGURE_ARGS: --enable-tcg-interpreter + MAKE_CHECK_ARGS: check + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + when: manual + allow_failure: true + - if: "$AARCH64_RUNNER_AVAILABLE" + when: manual + allow_failure: true ubuntu-24.04-aarch64-notcg: - extends: .custom_runner_template - needs: [] - stage: build - tags: - - ubuntu_24.04 - - aarch64 - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - when: manual - allow_failure: true - - if: "$AARCH64_RUNNER_AVAILABLE" - when: manual - allow_failure: true - script: - - mkdir build - - cd build - - ../configure --disable-tcg --with-devices-aarch64=minimal - || { cat config.log meson-logs/meson-log.txt; exit 1; } - - make --output-sync -j`nproc --ignore=40` - - make --output-sync -j`nproc --ignore=40` check + extends: .ubuntu_aarch64_template + variables: + CONFIGURE_ARGS: --disable-tcg --with-devices-aarch64=minimal + MAKE_CHECK_ARGS: check + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + when: manual + allow_failure: true + - if: "$AARCH64_RUNNER_AVAILABLE" + when: manual + allow_failure: true From 76a7a7158722bb032ab7169f44d3c32cb3442152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Oct 2025 16:03:47 +0100 Subject: [PATCH 1703/1794] gitlab: drop aarch32 runner and associated bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While working out what hoops to jump through to get a full set of aarch32 packages installed on the aarch64 runner it was pointed out 32 bit host support is deprecated. As the extra packages where needed for system emulation (marked deprecated since 8.0!) there didn't seem much point keeping this in. While the full expunging of 32 bit host support will probably be done for 11.0 we can at least reduce the CI burden a bit now. Reviewed-by: Pierrick Bouvier Message-ID: <20251016150357.876415-3-alex.bennee@linaro.org> Signed-off-by: Alex Bennée --- .gitlab-ci.d/custom-runners.yml | 1 - .../custom-runners/ubuntu-24.04-aarch32.yml | 25 ---- scripts/ci/setup/ubuntu/build-environment.yml | 17 --- .../setup/ubuntu/ubuntu-2204-armhf-cross.yml | 127 ------------------ 4 files changed, 170 deletions(-) delete mode 100644 .gitlab-ci.d/custom-runners/ubuntu-24.04-aarch32.yml delete mode 100644 scripts/ci/setup/ubuntu/ubuntu-2204-armhf-cross.yml diff --git a/.gitlab-ci.d/custom-runners.yml b/.gitlab-ci.d/custom-runners.yml index 3eb8216d571a3..142fbf4a242f2 100644 --- a/.gitlab-ci.d/custom-runners.yml +++ b/.gitlab-ci.d/custom-runners.yml @@ -31,4 +31,3 @@ include: - local: '/.gitlab-ci.d/custom-runners/ubuntu-24.04-s390x.yml' - local: '/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch64.yml' - - local: '/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch32.yml' diff --git a/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch32.yml b/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch32.yml deleted file mode 100644 index 75029c9187e83..0000000000000 --- a/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch32.yml +++ /dev/null @@ -1,25 +0,0 @@ -# All ubuntu-24.04 jobs should run successfully in an environment -# setup by the scripts/ci/setup/ubuntu/build-environment.yml task -# "Install basic packages to build QEMU on Ubuntu 24.04" - -ubuntu-24.04-aarch32-all: - extends: .custom_runner_template - needs: [] - stage: build - tags: - - ubuntu_24.04 - - aarch32 - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - when: manual - allow_failure: true - - if: "$AARCH32_RUNNER_AVAILABLE" - when: manual - allow_failure: true - script: - - mkdir build - - cd build - - ../configure --cross-prefix=arm-linux-gnueabihf- - || { cat config.log meson-logs/meson-log.txt; exit 1; } - - make --output-sync -j`nproc --ignore=40` - - make --output-sync -j`nproc --ignore=40` check diff --git a/scripts/ci/setup/ubuntu/build-environment.yml b/scripts/ci/setup/ubuntu/build-environment.yml index 6042750cb4dee..0f8ec5fab04fa 100644 --- a/scripts/ci/setup/ubuntu/build-environment.yml +++ b/scripts/ci/setup/ubuntu/build-environment.yml @@ -47,21 +47,4 @@ - ansible_facts['distribution'] == 'Ubuntu' - ansible_facts['distribution_version'] == '24.04' - - name: Install armhf cross-compile packages to build QEMU on AArch64 Ubuntu 24.04 - package: - name: - - binutils-arm-linux-gnueabihf - - gcc-arm-linux-gnueabihf - - libblkid-dev:armhf - - libc6-dev:armhf - - libffi-dev:armhf - - libglib2.0-dev:armhf - - libmount-dev:armhf - - libpcre2-dev:armhf - - libpixman-1-dev:armhf - - zlib1g-dev:armhf - when: - - ansible_facts['distribution'] == 'Ubuntu' - - ansible_facts['distribution_version'] == '24.04' - - ansible_facts['architecture'] == 'aarch64' diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-armhf-cross.yml b/scripts/ci/setup/ubuntu/ubuntu-2204-armhf-cross.yml deleted file mode 100644 index 0cc34cd10b9a1..0000000000000 --- a/scripts/ci/setup/ubuntu/ubuntu-2204-armhf-cross.yml +++ /dev/null @@ -1,127 +0,0 @@ -# THIS FILE WAS AUTO-GENERATED -# -# $ lcitool variables --cross-arch armv7l ubuntu-2204 qemu -# -# https://gitlab.com/libvirt/libvirt-ci - -packages: - - bash - - bc - - bison - - bsdextrautils - - bzip2 - - ca-certificates - - ccache - - dbus - - debianutils - - diffutils - - exuberant-ctags - - findutils - - flex - - gcc - - gcovr - - gettext - - git - - hostname - - libglib2.0-dev - - libpcre2-dev - - libsndio-dev - - libspice-protocol-dev - - llvm - - locales - - make - - meson - - mtools - - ncat - - ninja-build - - openssh-client - - pkgconf - - python3 - - python3-numpy - - python3-opencv - - python3-pillow - - python3-pip - - python3-sphinx - - python3-sphinx-rtd-theme - - python3-tomli - - python3-venv - - python3-yaml - - rpm2cpio - - sed - - socat - - sparse - - swtpm - - tar - - tesseract-ocr - - tesseract-ocr-eng - - xorriso - - zstd - - gcc-arm-linux-gnueabihf - - libaio-dev:armhf - - libasan6:armhf - - libasound2-dev:armhf - - libattr1-dev:armhf - - libbpf-dev:armhf - - libbrlapi-dev:armhf - - libbz2-dev:armhf - - libc6-dev:armhf - - libcacard-dev:armhf - - libcap-ng-dev:armhf - - libcapstone-dev:armhf - - libcmocka-dev:armhf - - libcurl4-gnutls-dev:armhf - - libdaxctl-dev:armhf - - libdrm-dev:armhf - - libepoxy-dev:armhf - - libfdt-dev:armhf - - libffi-dev:armhf - - libfuse3-dev:armhf - - libgbm-dev:armhf - - libgcrypt20-dev:armhf - - libglib2.0-dev:armhf - - libglusterfs-dev:armhf - - libgnutls28-dev:armhf - - libgtk-3-dev:armhf - - libibumad-dev:armhf - - libibverbs-dev:armhf - - libiscsi-dev:armhf - - libjemalloc-dev:armhf - - libjpeg-turbo8-dev:armhf - - libjson-c-dev:armhf - - liblttng-ust-dev:armhf - - liblzo2-dev:armhf - - libncursesw5-dev:armhf - - libnfs-dev:armhf - - libnuma-dev:armhf - - libpam0g-dev:armhf - - libpipewire-0.3-dev:armhf - - libpixman-1-dev:armhf - - libpng-dev:armhf - - libpulse-dev:armhf - - librbd-dev:armhf - - librdmacm-dev:armhf - - libsasl2-dev:armhf - - libsdl2-dev:armhf - - libsdl2-image-dev:armhf - - libseccomp-dev:armhf - - libselinux1-dev:armhf - - libslirp-dev:armhf - - libsnappy-dev:armhf - - libspice-server-dev:armhf - - libssh-dev:armhf - - libsystemd-dev:armhf - - libtasn1-6-dev:armhf - - libubsan1:armhf - - libudev-dev:armhf - - liburing-dev:armhf - - libusb-1.0-0-dev:armhf - - libusbredirhost-dev:armhf - - libvdeplug-dev:armhf - - libvirglrenderer-dev:armhf - - libvte-2.91-dev:armhf - - libxen-dev:armhf - - libzstd-dev:armhf - - nettle-dev:armhf - - systemtap-sdt-dev:armhf - - zlib1g-dev:armhf - From 77dc9d662f80b9f4eb450df27f60a0a9b3c97785 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 16 Oct 2025 16:03:48 +0100 Subject: [PATCH 1704/1794] tests/tcg/multiarch/linux/linux-test: Don't try to test atime update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The linux-test test includes an attempt to check the utime and stat syscalls by setting the atime and mtime of a file to specific values, and then calling stat() to check that the values read back correctly. Unfortunately this is flaky, as it will fail if some other process (for instance a virus scanner, backup program, etc) gets in and reads the file between the utime() and stat() call, resulting in a host syscall sequence like this: utimensat(AT_FDCWD, "file2", [{tv_sec=1001, tv_nsec=0} /* 1970-01-01T01:16:41+0100 */, {tv_sec=1000, tv_nsec=0} /* 1970-01-01T01:16:40+0100 */], 0) = 0 # successfully set atime to 1001 and mtime to 1000 statx(AT_FDCWD, "file2", AT_STATX_SYNC_AS_STAT|AT_NO_AUTOMOUNT, STATX_BASIC_STATS, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_blksize=4096, stx_attributes=0, stx_nlink=1, stx_uid=32808, stx_gid=32808, stx_mode=S_IFREG|0600, stx_ino=21659016, stx_size=100, stx_blocks=8, stx_attributes_mask=STATX_ATTR_COMPRESSED|STATX_ATTR_IMMUTABLE| STATX_ATTR_APPEND|STATX_ATTR_NODUMP|STATX_ATTR_ENCRYPTED| STATX_ATTR_AUTOMOUNT|STATX_ATTR_MOUNT_ROOT|STATX_ATTR_VERITY| STATX_ATTR_DAX, stx_atime={tv_sec=1760091862, tv_nsec=63509009} /* 2025-10-10T11:24:22.063509009+0100 */, stx_ctime={tv_sec=1760091862, tv_nsec=63509009} /* 2025-10-10T11:24:22.063509009+0100 */, stx_mtime={tv_sec=1000, tv_nsec=0} /* 1970-01-01T01:16:40+0100 */, stx_rdev_major=0, stx_rdev_minor=0, stx_dev_major=252, stx_dev_minor=0, stx_mnt_id=0x1f}) = 0 # but when we statx the file, we get back an mtime of 1000 # but an atime corresponding to when the other process read it and which will cause the test program to fail with the error message "stat time". In theory we could defend against this by e.g. operating on files in a dummy loopback mount filesystem which we mounted as 'noatime', but this isn't worth the hassle. Just drop the check on atime. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-ID: <20251016150357.876415-4-alex.bennee@linaro.org> Signed-off-by: Alex Bennée --- tests/tcg/multiarch/linux/linux-test.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/tcg/multiarch/linux/linux-test.c b/tests/tcg/multiarch/linux/linux-test.c index 64f57cb287eb2..bf6e0fda2626e 100644 --- a/tests/tcg/multiarch/linux/linux-test.c +++ b/tests/tcg/multiarch/linux/linux-test.c @@ -155,9 +155,14 @@ static void test_file(void) error("stat mode"); if ((st.st_mode & 0777) != 0600) error("stat mode2"); - if (st.st_atime != 1001 || - st.st_mtime != 1000) + /* + * Only check mtime, not atime: other processes such as + * virus scanners might race with this test program and get + * in and update the atime, causing random failures. + */ + if (st.st_mtime != 1000) { error("stat time"); + } chk_error(stat(tmpdir, &st)); if (!S_ISDIR(st.st_mode)) From 0c6d6d79a5cbd538c6c0798b1068f37bfbbf9693 Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Thu, 16 Oct 2025 16:03:50 +0100 Subject: [PATCH 1705/1794] ui/gtk-gl-area: Remove extra draw call in refresh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This partially reverts commit 77bf310084dad38b3a2badf01766c659056f1cf2 which causes some guest display corruption when gtk-gl-area is used for GTK rendering (e.g. Wayland Compositor) possibly due to simulataneous accesses on the guest frame buffer by host compositor and the guest. Fixes: 77bf310084 ("ui/gtk: Draw guest frame at refresh cycle") Reported-by: Dmitry Osipenko Reported-by: Alex Bennée Tested-by: Alex Bennée Tested-by: Dmitry Osipenko Reviewed-by: Alex Bennée Reviewed-by: Dmitry Osipenko Cc: Marc-André Lureau Cc: Vivek Kasireddy Signed-off-by: Dongwon Kim Message-Id: <20250214170813.2234754-1-dongwon.kim@intel.com> Message-ID: <20250603110204.838117-12-alex.bennee@linaro.org> Cc: qemu-stable@nongnu.org Message-ID: <20251016150357.876415-6-alex.bennee@linaro.org> Signed-off-by: Alex Bennée --- ui/gtk-gl-area.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index 05fc38096eca3..cd86022d264a2 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -165,7 +165,22 @@ void gd_gl_area_refresh(DisplayChangeListener *dcl) if (vc->gfx.guest_fb.dmabuf && qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { - gd_gl_area_draw(vc); + /* + * gd_egl_refresh() calls gd_egl_draw() if a DMA-BUF draw has already + * been submitted, but this function does not call gd_gl_area_draw() in + * such a case due to display corruption. + * + * Calling gd_gl_area_draw() is necessary to prevent a situation where + * there is a scheduled draw event but it won't happen bacause the window + * is currently in inactive state (minimized or tabified). If draw is not + * done for a long time, gl_block timeout and/or fence timeout (on the + * guest) will happen eventually. + * + * However, it is found that calling gd_gl_area_draw() here causes guest + * display corruption on a Wayland Compositor. The display corruption is + * more serious than the possible fence timeout so gd_gl_area_draw() is + * omitted for now. + */ return; } From 9f714c4b2064d11ca422ab71c973f41ebaea121e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Oct 2025 16:03:51 +0100 Subject: [PATCH 1706/1794] hw/display: add blob map/unmap trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As these events happen dynamically as the guest does various things they are quite handy to trace. Reviewed-by: Pierrick Bouvier Reviewed-by: Akihiko Odaki Message-ID: <20251016150357.876415-7-alex.bennee@linaro.org> Signed-off-by: Alex Bennée --- hw/display/trace-events | 2 ++ hw/display/virtio-gpu-virgl.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/hw/display/trace-events b/hw/display/trace-events index 52786e6e1840b..e323a82cff24b 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -38,6 +38,8 @@ virtio_gpu_cmd_set_scanout_blob(uint32_t id, uint32_t res, uint32_t w, uint32_t virtio_gpu_cmd_res_create_2d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t h) "res 0x%x, fmt 0x%x, w %d, h %d" virtio_gpu_cmd_res_create_3d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t h, uint32_t d) "res 0x%x, fmt 0x%x, w %d, h %d, d %d" virtio_gpu_cmd_res_create_blob(uint32_t res, uint64_t size) "res 0x%x, size %" PRId64 +virtio_gpu_cmd_res_map_blob(uint32_t res, void *vmr, void *mr) "res 0x%x, vmr %p, mr %p" +virtio_gpu_cmd_res_unmap_blob(uint32_t res, void *mr, bool finish_unmapping) "res 0x%x, mr %p, finish_unmapping %d" virtio_gpu_cmd_res_unref(uint32_t res) "res 0x%x" virtio_gpu_cmd_res_back_attach(uint32_t res) "res 0x%x" virtio_gpu_cmd_res_back_detach(uint32_t res) "res 0x%x" diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c index 94ddc01f91c6b..07f6355ad62e1 100644 --- a/hw/display/virtio-gpu-virgl.c +++ b/hw/display/virtio-gpu-virgl.c @@ -134,6 +134,8 @@ virtio_gpu_virgl_map_resource_blob(VirtIOGPU *g, res->mr = mr; + trace_virtio_gpu_cmd_res_map_blob(res->base.resource_id, vmr, mr); + return 0; } @@ -153,6 +155,8 @@ virtio_gpu_virgl_unmap_resource_blob(VirtIOGPU *g, vmr = to_hostmem_region(res->mr); + trace_virtio_gpu_cmd_res_unmap_blob(res->base.resource_id, mr, vmr->finish_unmapping); + /* * Perform async unmapping in 3 steps: * From 8a545a336d577e87b47e39031c17dd89eebd77d7 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Oct 2025 16:03:52 +0100 Subject: [PATCH 1707/1794] contrib/plugins/uftrace_symbols.py: generate debug files to map symbols to source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhance uftrace_symbols.py to generate .dbg files, containing source location for every symbol present in .sym file. It allows to use `uftrace {replay,dump} --srcline` and show origin of functions, connecting trace to original source code. It was first implemented with pyelftools DWARF parser, which was way too slow (~minutes) to get locations for every symbol in the linux kernel. Thus, we use `addr2line` instead, which runs in seconds. As well, there were some bugs with latest pyelftools release, requiring to run master version, which is not installable with pip. Thus, since we now require binutils (addr2line), we can ditch pyelftools based implementation and simply rely on `nm` to get symbols information, which is faster and better. Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251016150357.876415-8-alex.bennee@linaro.org> Signed-off-by: Alex Bennée --- contrib/plugins/uftrace_symbols.py | 120 +++++++++++++++++++---------- 1 file changed, 78 insertions(+), 42 deletions(-) diff --git a/contrib/plugins/uftrace_symbols.py b/contrib/plugins/uftrace_symbols.py index b49e03203c8fc..45fb79c7a58c9 100755 --- a/contrib/plugins/uftrace_symbols.py +++ b/contrib/plugins/uftrace_symbols.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # -# Create symbols and mapping files for uftrace. +# Create symbols, debug and mapping files for uftrace. # # Copyright 2025 Linaro Ltd # Author: Pierrick Bouvier @@ -9,44 +9,71 @@ # SPDX-License-Identifier: GPL-2.0-or-later import argparse -import elftools # pip install pyelftools import os - -from elftools.elf.elffile import ELFFile -from elftools.elf.sections import SymbolTableSection - -def elf_func_symbols(elf): - symbol_tables = [(idx, s) for idx, s in enumerate(elf.iter_sections()) - if isinstance(s, SymbolTableSection)] - symbols = [] - for _, section in symbol_tables: - for _, symbol in enumerate(section.iter_symbols()): - if symbol_size(symbol) == 0: - continue - type = symbol['st_info']['type'] - if type == 'STT_FUNC' or type == 'STT_NOTYPE': - symbols.append(symbol) - symbols.sort(key = lambda x: symbol_addr(x)) +import subprocess + +class Symbol: + def __init__(self, name, addr, size): + self.name = name + # clamp addr to 48 bits, like uftrace entries + self.addr = addr & 0xffffffffffff + self.full_addr = addr + self.size = size + + def set_loc(self, file, line): + self.file = file + self.line = line + +def get_symbols(elf_file): + symbols=[] + try: + out = subprocess.check_output(['nm', '--print-size', elf_file], + stderr=subprocess.STDOUT, + text=True) + except subprocess.CalledProcessError as e: + print(e.output) + raise + out = out.strip().split('\n') + for line in out: + info = line.split(' ') + if len(info) == 3: + # missing size information + continue + addr, size, type, name = info + # add only symbols from .text section + if type.lower() != 't': + continue + addr = int(addr, 16) + size = int(size, 16) + symbols.append(Symbol(name, addr, size)) + symbols.sort(key = lambda x: x.addr) return symbols -def symbol_size(symbol): - return symbol['st_size'] - -def symbol_addr(symbol): - addr = symbol['st_value'] - # clamp addr to 48 bits, like uftrace entries - return addr & 0xffffffffffff - -def symbol_name(symbol): - return symbol.name +def find_symbols_locations(elf_file, symbols): + addresses = '\n'.join([hex(x.full_addr) for x in symbols]) + try: + out = subprocess.check_output(['addr2line', '--exe', elf_file], + stderr=subprocess.STDOUT, + input=addresses, text=True) + except subprocess.CalledProcessError as e: + print(e.output) + raise + out = out.strip().split('\n') + assert len(out) == len(symbols) + for i in range(len(symbols)): + s = symbols[i] + file, line = out[i].split(':') + # addr2line may return 'line (discriminator [0-9]+)' sometimes, + # remove this to keep only line number. + line = line.split(' ')[0] + s.set_loc(file, line) class BinaryFile: def __init__(self, path, map_offset): self.fullpath = os.path.realpath(path) self.map_offset = map_offset - with open(path, 'rb') as f: - self.elf = ELFFile(f) - self.symbols = elf_func_symbols(self.elf) + self.symbols = get_symbols(self.fullpath) + find_symbols_locations(self.fullpath, self.symbols) def path(self): return self.fullpath @@ -56,24 +83,31 @@ def addr_start(self): def addr_end(self): last_sym = self.symbols[-1] - return symbol_addr(last_sym) + symbol_size(last_sym) + self.map_offset + return last_sym.addr + last_sym.size + self.map_offset def generate_symbol_file(self, prefix_symbols): binary_name = os.path.basename(self.fullpath) - sym_file_path = f'./uftrace.data/{binary_name}.sym' + sym_file_path = os.path.join('uftrace.data', f'{binary_name}.sym') print(f'{sym_file_path} ({len(self.symbols)} symbols)') with open(sym_file_path, 'w') as sym_file: # print hexadecimal addresses on 48 bits addrx = "0>12x" for s in self.symbols: - addr = symbol_addr(s) + addr = s.addr addr = f'{addr:{addrx}}' - size = f'{symbol_size(s):{addrx}}' - name = symbol_name(s) + size = f'{s.size:{addrx}}' if prefix_symbols: - name = f'{binary_name}:{name}' + name = f'{binary_name}:{s.name}' print(addr, size, 'T', name, file=sym_file) + def generate_debug_file(self): + binary_name = os.path.basename(self.fullpath) + dbg_file_path = os.path.join('uftrace.data', f'{binary_name}.dbg') + with open(dbg_file_path, 'w') as dbg_file: + for s in self.symbols: + print(f'F: {hex(s.addr)} {s.name}', file=dbg_file) + print(f'L: {s.line} {s.file}', file=dbg_file) + def parse_parameter(p): s = p.split(":") path = s[0] @@ -84,7 +118,7 @@ def parse_parameter(p): offset = s[1] if not offset.startswith('0x'): err = f'offset "{offset}" is not an hexadecimal constant. ' - err += 'It should starts with "0x".' + err += 'It should start with "0x".' raise ValueError(err) offset = int(offset, 16) return path, offset @@ -97,7 +131,7 @@ def is_from_user_mode(map_file_path): return False def generate_map(binaries): - map_file_path = './uftrace.data/sid-0.map' + map_file_path = os.path.join('uftrace.data', 'sid-0.map') if is_from_user_mode(map_file_path): print(f'do not overwrite {map_file_path} generated from qemu-user') @@ -124,7 +158,8 @@ def generate_map(binaries): def main(): parser = argparse.ArgumentParser(description= - 'generate symbol files for uftrace') + 'generate symbol files for uftrace. ' + 'Require binutils (nm and addr2line).') parser.add_argument('elf_file', nargs='+', help='path to an ELF file. ' 'Use /path/to/file:0xdeadbeef to add a mapping offset.') @@ -133,8 +168,8 @@ def main(): action=argparse.BooleanOptionalAction) args = parser.parse_args() - if not os.path.exists('./uftrace.data'): - os.mkdir('./uftrace.data') + if not os.path.exists('uftrace.data'): + os.mkdir('uftrace.data') binaries = [] for file in args.elf_file: @@ -145,6 +180,7 @@ def main(): for b in binaries: b.generate_symbol_file(args.prefix_symbols) + b.generate_debug_file() generate_map(binaries) From 487ce6ce999ca8f41960c19981ae3f01f6209d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Oct 2025 16:03:53 +0100 Subject: [PATCH 1708/1794] docs/system: split VirtIO devices from the rest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In an effort to tidy up our device documentation split the VirtIO docs from the rest of the index and put the index to them at the front of the list. Sort the remaining entries alphabetically and tweak the references appropriately. Add a short preface to the VirtIO section nudging users to use VirtIO unless they specifically want a particular piece of hardware emulation. Reviewed-by: Manos Pitsidianakis Reviewed-by: John Levon Message-ID: <20251016150357.876415-9-alex.bennee@linaro.org> Signed-off-by: Alex Bennée --- docs/system/device-emulation.rst | 18 +++++------ docs/system/devices/vfio-user.rst | 2 +- docs/system/devices/virtio/index.rst | 30 +++++++++++++++++++ .../devices/{ => virtio}/vhost-user-input.rst | 0 .../devices/{ => virtio}/vhost-user-rng.rst | 0 .../devices/{ => virtio}/vhost-user.rst | 0 .../devices/{ => virtio}/virtio-gpu.rst | 0 .../devices/{ => virtio}/virtio-pmem.rst | 0 .../devices/{ => virtio}/virtio-snd.rst | 0 9 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 docs/system/devices/virtio/index.rst rename docs/system/devices/{ => virtio}/vhost-user-input.rst (100%) rename docs/system/devices/{ => virtio}/vhost-user-rng.rst (100%) rename docs/system/devices/{ => virtio}/vhost-user.rst (100%) rename docs/system/devices/{ => virtio}/virtio-gpu.rst (100%) rename docs/system/devices/{ => virtio}/virtio-pmem.rst (100%) rename docs/system/devices/{ => virtio}/virtio-snd.rst (100%) diff --git a/docs/system/device-emulation.rst b/docs/system/device-emulation.rst index 911381643f1c6..db714ad47aa5b 100644 --- a/docs/system/device-emulation.rst +++ b/docs/system/device-emulation.rst @@ -82,22 +82,18 @@ Emulated Devices .. toctree:: :maxdepth: 1 + devices/virtio/index.rst + devices/can.rst + devices/canokey.rst devices/ccid.rst devices/cxl.rst - devices/vfio-user.rst - devices/ivshmem.rst + devices/igb.rst devices/ivshmem-flat.rst + devices/ivshmem.rst devices/keyboard.rst devices/net.rst devices/nvme.rst - devices/usb.rst - devices/vhost-user.rst - devices/virtio-gpu.rst - devices/virtio-pmem.rst - devices/virtio-snd.rst - devices/vhost-user-input.rst - devices/vhost-user-rng.rst - devices/canokey.rst devices/usb-u2f.rst - devices/igb.rst + devices/usb.rst + devices/vfio-user.rst diff --git a/docs/system/devices/vfio-user.rst b/docs/system/devices/vfio-user.rst index b6dcaa5615e53..30c2215f4ea5a 100644 --- a/docs/system/devices/vfio-user.rst +++ b/docs/system/devices/vfio-user.rst @@ -6,7 +6,7 @@ vfio-user QEMU includes a ``vfio-user`` client. The ``vfio-user`` specification allows for implementing (PCI) devices in userspace outside of QEMU; it is similar to -``vhost-user`` in this respect (see :doc:`vhost-user`), but can emulate arbitrary +``vhost-user`` in this respect (see :doc:`virtio/vhost-user`), but can emulate arbitrary PCI devices, not just ``virtio``. Whereas ``vfio`` is handled by the host kernel, ``vfio-user``, while similar in implementation, is handled entirely in userspace. diff --git a/docs/system/devices/virtio/index.rst b/docs/system/devices/virtio/index.rst new file mode 100644 index 0000000000000..fc457ca74c78e --- /dev/null +++ b/docs/system/devices/virtio/index.rst @@ -0,0 +1,30 @@ +VirtIO Devices +============== + +VirtIO devices are paravirtualized devices designed to be efficient to +emulate and virtualize. Unless you are specifically trying to exercise +a driver for some particular hardware they are the recommended device +models to use for virtual machines. + +The `VirtIO specification`_ is an open standard managed by OASIS. It +describes how a *driver* in a guest operating system interacts with +the *device* model provided by QEMU. Multiple Operating Systems +support drivers for VirtIO with Linux perhaps having the widest range +of device types supported. + +The device implementation can either be provided wholly by QEMU, or in +concert with the kernel (known as *vhost*). The device implementation +can also be off-loaded to an external process via :ref:`vhost user +`. + +.. toctree:: + :maxdepth: 1 + + virtio-gpu.rst + virtio-pmem.rst + virtio-snd.rst + vhost-user.rst + vhost-user-input.rst + vhost-user-rng.rst + +.. _VirtIO specification: https://docs.oasis-open.org/virtio/virtio/v1.3/virtio-v1.3.html diff --git a/docs/system/devices/vhost-user-input.rst b/docs/system/devices/virtio/vhost-user-input.rst similarity index 100% rename from docs/system/devices/vhost-user-input.rst rename to docs/system/devices/virtio/vhost-user-input.rst diff --git a/docs/system/devices/vhost-user-rng.rst b/docs/system/devices/virtio/vhost-user-rng.rst similarity index 100% rename from docs/system/devices/vhost-user-rng.rst rename to docs/system/devices/virtio/vhost-user-rng.rst diff --git a/docs/system/devices/vhost-user.rst b/docs/system/devices/virtio/vhost-user.rst similarity index 100% rename from docs/system/devices/vhost-user.rst rename to docs/system/devices/virtio/vhost-user.rst diff --git a/docs/system/devices/virtio-gpu.rst b/docs/system/devices/virtio/virtio-gpu.rst similarity index 100% rename from docs/system/devices/virtio-gpu.rst rename to docs/system/devices/virtio/virtio-gpu.rst diff --git a/docs/system/devices/virtio-pmem.rst b/docs/system/devices/virtio/virtio-pmem.rst similarity index 100% rename from docs/system/devices/virtio-pmem.rst rename to docs/system/devices/virtio/virtio-pmem.rst diff --git a/docs/system/devices/virtio-snd.rst b/docs/system/devices/virtio/virtio-snd.rst similarity index 100% rename from docs/system/devices/virtio-snd.rst rename to docs/system/devices/virtio/virtio-snd.rst From bb8ad154e6dbe29e2dc3d172d75bf87dc7222ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Oct 2025 16:03:54 +0100 Subject: [PATCH 1709/1794] docs/system: unify the naming style for VirtIO devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes the index look a little neater. Reviewed-by: John Levon Message-ID: <20251016150357.876415-10-alex.bennee@linaro.org> Signed-off-by: Alex Bennée --- docs/system/devices/virtio/virtio-gpu.rst | 2 +- docs/system/devices/virtio/virtio-pmem.rst | 6 ++---- docs/system/devices/virtio/virtio-snd.rst | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/system/devices/virtio/virtio-gpu.rst b/docs/system/devices/virtio/virtio-gpu.rst index b7eb0fc0e727a..39d2fd2d21cbe 100644 --- a/docs/system/devices/virtio/virtio-gpu.rst +++ b/docs/system/devices/virtio/virtio-gpu.rst @@ -1,7 +1,7 @@ .. SPDX-License-Identifier: GPL-2.0-or-later -virtio-gpu +VirtIO GPU ========== This document explains the setup and usage of the virtio-gpu device. diff --git a/docs/system/devices/virtio/virtio-pmem.rst b/docs/system/devices/virtio/virtio-pmem.rst index c82ac0673153b..0c24de83ec704 100644 --- a/docs/system/devices/virtio/virtio-pmem.rst +++ b/docs/system/devices/virtio/virtio-pmem.rst @@ -1,7 +1,5 @@ - -=========== -virtio pmem -=========== +VirtIO Persistent Memory +======================== This document explains the setup and usage of the virtio pmem device. The virtio pmem device is a paravirtualized persistent memory device diff --git a/docs/system/devices/virtio/virtio-snd.rst b/docs/system/devices/virtio/virtio-snd.rst index 2a9187fd701a3..3c797f66e039f 100644 --- a/docs/system/devices/virtio/virtio-snd.rst +++ b/docs/system/devices/virtio/virtio-snd.rst @@ -1,4 +1,4 @@ -virtio sound +VirtIO Sound ============ This document explains the setup and usage of the Virtio sound device. From b1401456d561aeb9edd8a4df52f6036a0e8f50b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Oct 2025 16:03:55 +0100 Subject: [PATCH 1710/1794] docs/system: drop vhost-user-rng docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a fairly lightweight document which doesn't add much to the general advice in vhost-user. Update the vhost-user docs to point directly at the rust-vmm repo. Reviewed-by: John Levon Message-ID: <20251016150357.876415-11-alex.bennee@linaro.org> Signed-off-by: Alex Bennée --- docs/system/devices/virtio/index.rst | 1 - docs/system/devices/virtio/vhost-user-rng.rst | 41 ------------------- docs/system/devices/virtio/vhost-user.rst | 2 +- 3 files changed, 1 insertion(+), 43 deletions(-) delete mode 100644 docs/system/devices/virtio/vhost-user-rng.rst diff --git a/docs/system/devices/virtio/index.rst b/docs/system/devices/virtio/index.rst index fc457ca74c78e..e3839e61824e9 100644 --- a/docs/system/devices/virtio/index.rst +++ b/docs/system/devices/virtio/index.rst @@ -25,6 +25,5 @@ can also be off-loaded to an external process via :ref:`vhost user virtio-snd.rst vhost-user.rst vhost-user-input.rst - vhost-user-rng.rst .. _VirtIO specification: https://docs.oasis-open.org/virtio/virtio/v1.3/virtio-v1.3.html diff --git a/docs/system/devices/virtio/vhost-user-rng.rst b/docs/system/devices/virtio/vhost-user-rng.rst deleted file mode 100644 index ead14053264d0..0000000000000 --- a/docs/system/devices/virtio/vhost-user-rng.rst +++ /dev/null @@ -1,41 +0,0 @@ -.. _vhost_user_rng: - -QEMU vhost-user-rng - RNG emulation -=================================== - -Background ----------- - -What follows builds on the material presented in vhost-user.rst - it should -be reviewed before moving forward with the content in this file. - -Description ------------ - -The vhost-user-rng device implementation was designed to work with a random -number generator daemon such as the one found in the vhost-device crate of -the rust-vmm project available on github [1]. - -[1]. https://github.com/rust-vmm/vhost-device - -Examples --------- - -The daemon should be started first: - -:: - - host# vhost-device-rng --socket-path=rng.sock -c 1 -m 512 -p 1000 - -The QEMU invocation needs to create a chardev socket the device can -use to communicate as well as share the guests memory over a memfd. - -:: - - host# qemu-system \ - -chardev socket,path=$(PATH)/rng.sock,id=rng0 \ - -device vhost-user-rng-pci,chardev=rng0 \ - -m 4096 \ - -object memory-backend-file,id=mem,size=4G,mem-path=/dev/shm,share=on \ - -numa node,memdev=mem \ - ... diff --git a/docs/system/devices/virtio/vhost-user.rst b/docs/system/devices/virtio/vhost-user.rst index bddf8df5ed504..9c9a28df380ad 100644 --- a/docs/system/devices/virtio/vhost-user.rst +++ b/docs/system/devices/virtio/vhost-user.rst @@ -45,7 +45,7 @@ platform details for what sort of virtio bus to use. - :ref:`vhost_user_input` * - vhost-user-rng - Entropy driver - - :ref:`vhost_user_rng` + - See https://github.com/rust-vmm/vhost-device * - vhost-user-scmi - System Control and Management Interface - See https://github.com/rust-vmm/vhost-device From faa29110b903fed29ba27bad3d0d8fcc7a8aa0fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Oct 2025 16:03:56 +0100 Subject: [PATCH 1711/1794] docs/system: merge vhost-user-input into vhost-user-contrib MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We might as well group all the contrib submissions together and gently dissuade people from using them in production. Update the references in vhost-user to neatly refer to the storage daemon and the various external rust backends. Reviewed-by: Manos Pitsidianakis Message-ID: <20251016150357.876415-12-alex.bennee@linaro.org> Signed-off-by: Alex Bennée --- docs/system/devices/virtio/index.rst | 2 +- .../devices/virtio/vhost-user-contrib.rst | 87 +++++++++++++++++++ .../devices/virtio/vhost-user-input.rst | 45 ---------- docs/system/devices/virtio/vhost-user.rst | 22 ++--- docs/system/devices/virtio/virtio-gpu.rst | 2 + 5 files changed, 101 insertions(+), 57 deletions(-) create mode 100644 docs/system/devices/virtio/vhost-user-contrib.rst delete mode 100644 docs/system/devices/virtio/vhost-user-input.rst diff --git a/docs/system/devices/virtio/index.rst b/docs/system/devices/virtio/index.rst index e3839e61824e9..c292101ade638 100644 --- a/docs/system/devices/virtio/index.rst +++ b/docs/system/devices/virtio/index.rst @@ -24,6 +24,6 @@ can also be off-loaded to an external process via :ref:`vhost user virtio-pmem.rst virtio-snd.rst vhost-user.rst - vhost-user-input.rst + vhost-user-contrib.rst .. _VirtIO specification: https://docs.oasis-open.org/virtio/virtio/v1.3/virtio-v1.3.html diff --git a/docs/system/devices/virtio/vhost-user-contrib.rst b/docs/system/devices/virtio/vhost-user-contrib.rst new file mode 100644 index 0000000000000..48d04d2ade3fb --- /dev/null +++ b/docs/system/devices/virtio/vhost-user-contrib.rst @@ -0,0 +1,87 @@ +vhost-user daemons in contrib +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +QEMU provides a number of :ref:`vhost_user` daemons in the contrib +directory. They were often written when vhost-user was initially added +to the code base. You should also consider if other vhost-user daemons +such as those from the rust-vmm `vhost-device repository`_ are better +suited for production use. + +.. _vhost-device repository: https://github.com/rust-vmm/vhost-device + +.. _vhost_user_block: + +vhost-user-block - block device +=============================== + +vhost-user-block is a backend for exposing block devices. It can +present a flat file or block device as a simple block device to the +guest. You almost certainly want to use the :ref:`storage-daemon` +instead which supports a wide variety of storage modes and exports a +number of interfaces including vhost-user. + +.. _vhost_user_gpu: + +vhost-user-gpu - gpu device +=========================== + +vhost-user-gpu presents a paravirtualized GPU and display controller. +You probably want to use the internal :ref:`virtio_gpu` implementation +if you want the latest features. There is also a `vhost_device_gpu`_ +daemon as part of the rust-vmm project. + +.. _vhost_device_gpu: https://github.com/rust-vmm/vhost-device/tree/main/vhost-device-gpu + +.. _vhost_user_input: + +vhost-user-input - Input emulation +================================== + +The Virtio input device is a paravirtualized device for input events. + +Description +----------- + +The vhost-user-input device implementation was designed to work with a daemon +polling on input devices and passes input events to the guest. + +QEMU provides a backend implementation in contrib/vhost-user-input. + +Linux kernel support +-------------------- + +Virtio input requires a guest Linux kernel built with the +``CONFIG_VIRTIO_INPUT`` option. + +Examples +-------- + +The backend daemon should be started first: + +:: + + host# vhost-user-input --socket-path=input.sock \ + --evdev-path=/dev/input/event17 + +The QEMU invocation needs to create a chardev socket to communicate with the +backend daemon and access the VirtIO queues with the guest over the +:ref:`shared memory `. + +:: + + host# qemu-system \ + -chardev socket,path=/tmp/input.sock,id=mouse0 \ + -device vhost-user-input-pci,chardev=mouse0 \ + -m 4096 \ + -object memory-backend-file,id=mem,size=4G,mem-path=/dev/shm,share=on \ + -numa node,memdev=mem \ + ... + + +.. _vhost_user_scsi: + +vhost-user-scsi - SCSI controller +================================= + +The vhost-user-scsi daemon can proxy iSCSI devices onto a virtualized +SCSI controller. diff --git a/docs/system/devices/virtio/vhost-user-input.rst b/docs/system/devices/virtio/vhost-user-input.rst deleted file mode 100644 index 118eb78101c6a..0000000000000 --- a/docs/system/devices/virtio/vhost-user-input.rst +++ /dev/null @@ -1,45 +0,0 @@ -.. _vhost_user_input: - -QEMU vhost-user-input - Input emulation -======================================= - -This document describes the setup and usage of the Virtio input device. -The Virtio input device is a paravirtualized device for input events. - -Description ------------ - -The vhost-user-input device implementation was designed to work with a daemon -polling on input devices and passes input events to the guest. - -QEMU provides a backend implementation in contrib/vhost-user-input. - -Linux kernel support --------------------- - -Virtio input requires a guest Linux kernel built with the -``CONFIG_VIRTIO_INPUT`` option. - -Examples --------- - -The backend daemon should be started first: - -:: - - host# vhost-user-input --socket-path=input.sock \ - --evdev-path=/dev/input/event17 - -The QEMU invocation needs to create a chardev socket to communicate with the -backend daemon and access the VirtIO queues with the guest over the -:ref:`shared memory `. - -:: - - host# qemu-system \ - -chardev socket,path=/tmp/input.sock,id=mouse0 \ - -device vhost-user-input-pci,chardev=mouse0 \ - -m 4096 \ - -object memory-backend-file,id=mem,size=4G,mem-path=/dev/shm,share=on \ - -numa node,memdev=mem \ - ... diff --git a/docs/system/devices/virtio/vhost-user.rst b/docs/system/devices/virtio/vhost-user.rst index 9c9a28df380ad..f556a840e996b 100644 --- a/docs/system/devices/virtio/vhost-user.rst +++ b/docs/system/devices/virtio/vhost-user.rst @@ -27,37 +27,37 @@ platform details for what sort of virtio bus to use. - Notes * - vhost-user-blk - Block storage - - See contrib/vhost-user-blk + - :ref:`storage-daemon` * - vhost-user-fs - File based storage driver - - See https://gitlab.com/virtio-fs/virtiofsd + - `virtiofsd `_ * - vhost-user-gpio - Proxy gpio pins to host - - See https://github.com/rust-vmm/vhost-device + - `vhost-device-gpio `_ * - vhost-user-gpu - GPU driver - - See contrib/vhost-user-gpu + - `vhost-device-gpu `_ or :ref:`vhost_user_gpu` * - vhost-user-i2c - Proxy i2c devices to host - - See https://github.com/rust-vmm/vhost-device + - `vhost-device-i2c `_ * - vhost-user-input - Generic input driver - - :ref:`vhost_user_input` + - `vhost-device-input `_ or :ref:`vhost_user_input` * - vhost-user-rng - Entropy driver - - See https://github.com/rust-vmm/vhost-device + - `vhost-device-rng `_ * - vhost-user-scmi - System Control and Management Interface - - See https://github.com/rust-vmm/vhost-device + - `vhost-device-scmi `_ * - vhost-user-snd - Audio device - - See https://github.com/rust-vmm/vhost-device/staging + - `vhost-device-sound `_ * - vhost-user-scsi - SCSI based storage - - See contrib/vhost-user-scsi + - :ref:`vhost_user_scsi` * - vhost-user-vsock - Socket based communication - - See https://github.com/rust-vmm/vhost-device + - `vhost-device-vsock `_ The referenced *daemons* are not exhaustive, any conforming backend implementing the device and using the vhost-user protocol should work. diff --git a/docs/system/devices/virtio/virtio-gpu.rst b/docs/system/devices/virtio/virtio-gpu.rst index 39d2fd2d21cbe..0f4bb304a9b84 100644 --- a/docs/system/devices/virtio/virtio-gpu.rst +++ b/docs/system/devices/virtio/virtio-gpu.rst @@ -1,6 +1,8 @@ .. SPDX-License-Identifier: GPL-2.0-or-later +.. _virtio_gpu: + VirtIO GPU ========== From 1a5319e26797404a5f7738111ba788b593483af6 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 13 Oct 2025 13:21:02 +0200 Subject: [PATCH 1712/1794] ui/pixman: Fix crash in qemu_pixman_shareable_free() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: Bernhard Beschow Fixes: b296b29d3414 (ui/pixman: Consistent error handling in qemu_pixman_shareable_free()) Signed-off-by: Markus Armbruster Message-ID: <20251013112102.2396012-1-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Tested-by: Bernhard Beschow Reviewed-by: Bernhard Beschow --- ui/qemu-pixman.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index e46c6232cfaa9..aea09755b9495 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -291,7 +291,9 @@ qemu_pixman_shareable_free(qemu_pixman_shareable handle, Error *err = NULL; qemu_win32_map_free(ptr, handle, &err); - error_report_err(err); + if (err) { + error_report_err(err); + } #else qemu_memfd_free(ptr, size, handle); #endif From 0ce63280dc6fe9fce1d89922f2d17dcae77827e6 Mon Sep 17 00:00:00 2001 From: Shalini Chellathurai Saroja Date: Thu, 16 Oct 2025 14:17:07 +0200 Subject: [PATCH 1713/1794] qapi/machine-s390x: add QAPI event SCLP_CPI_INFO_AVAILABLE Add QAPI event SCLP_CPI_INFO_AVAILABLE to notify the availability of Control-Program Identification data in QOM. Signed-off-by: Shalini Chellathurai Saroja Suggested-by: Thomas Huth Reviewed-by: Hendrik Brueckner Reviewed-by: Thomas Huth Message-ID: <20251016121708.334133-1-shalini@linux.ibm.com> Signed-off-by: Thomas Huth --- hw/s390x/sclpcpi.c | 4 ++++ qapi/machine-s390x.json | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/hw/s390x/sclpcpi.c b/hw/s390x/sclpcpi.c index 7aa039d510035..68fc1b809bf3d 100644 --- a/hw/s390x/sclpcpi.c +++ b/hw/s390x/sclpcpi.c @@ -54,6 +54,7 @@ #include "hw/s390x/event-facility.h" #include "hw/s390x/ebcdic.h" #include "qapi/qapi-visit-machine.h" +#include "qapi/qapi-events-machine-s390x.h" #include "migration/vmstate.h" typedef struct Data { @@ -106,6 +107,9 @@ static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr) e->timestamp = qemu_clock_get_ns(QEMU_CLOCK_HOST); cpim->ebh.flags = SCLP_EVENT_BUFFER_ACCEPTED; + + qapi_event_send_sclp_cpi_info_available(); + return SCLP_RC_NORMAL_COMPLETION; } diff --git a/qapi/machine-s390x.json b/qapi/machine-s390x.json index 966dbd61d2e0b..8412668b67123 100644 --- a/qapi/machine-s390x.json +++ b/qapi/machine-s390x.json @@ -119,3 +119,24 @@ { 'command': 'query-s390x-cpu-polarization', 'returns': 'CpuPolarizationInfo', 'features': [ 'unstable' ] } + +## +# @SCLP_CPI_INFO_AVAILABLE: +# +# Emitted when the Control-Program Identification data is available +# in the QOM tree. +# +# Features: +# +# @unstable: This event is experimental. +# +# Since: 10.2 +# +# .. qmp-example:: +# +# <- { "event": "SCLP_CPI_INFO_AVAILABLE", +# "timestamp": { "seconds": 1401385907, "microseconds": 422329 } } +## +{ 'event': 'SCLP_CPI_INFO_AVAILABLE', + 'features': [ 'unstable' ] +} From bc436b739c3c8683ef5e8c22391952dcaa95242e Mon Sep 17 00:00:00 2001 From: Shalini Chellathurai Saroja Date: Thu, 16 Oct 2025 14:17:08 +0200 Subject: [PATCH 1714/1794] tests/functional: add tests for SCLP event CPI Add tests for SCLP event type Control-Program Identification. Signed-off-by: Shalini Chellathurai Saroja Suggested-by: Thomas Huth Reviewed-by: Hendrik Brueckner Reviewed-by Thomas Huth Message-ID: <20251016121708.334133-2-shalini@linux.ibm.com> Signed-off-by: Thomas Huth --- tests/functional/s390x/test_ccw_virtio.py | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/functional/s390x/test_ccw_virtio.py b/tests/functional/s390x/test_ccw_virtio.py index 453711aa0f5e4..045533785670a 100755 --- a/tests/functional/s390x/test_ccw_virtio.py +++ b/tests/functional/s390x/test_ccw_virtio.py @@ -15,6 +15,7 @@ import tempfile from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command from qemu_test import exec_command_and_wait_for_pattern from qemu_test import wait_for_console_pattern @@ -270,5 +271,30 @@ def test_s390x_fedora(self): 'while ! (dmesg -c | grep Start.virtcrypto_remove) ; do' ' sleep 1 ; done', 'Start virtcrypto_remove.') + # Test SCLP event Control-Program Identification (CPI) + cpi = '/sys/firmware/cpi/' + sclpcpi = '/machine/sclp/s390-sclp-event-facility/sclpcpi' + self.log.info("Test SCLP event CPI") + exec_command(self, 'echo TESTVM > ' + cpi + 'system_name') + exec_command(self, 'echo LINUX > ' + cpi + 'system_type') + exec_command(self, 'echo TESTPLEX > ' + cpi + 'sysplex_name') + exec_command(self, 'echo 0x001a000000060b00 > ' + cpi + 'system_level') + exec_command_and_wait_for_pattern(self, + 'echo 1 > ' + cpi + 'set', ':/#') + try: + event = self.vm.event_wait('SCLP_CPI_INFO_AVAILABLE') + except TimeoutError: + self.fail('Timed out waiting for the SCLP_CPI_INFO_AVAILABLE event') + ts = self.vm.cmd('qom-get', path=sclpcpi, property='timestamp') + self.assertNotEqual(ts, 0) + name = self.vm.cmd('qom-get', path=sclpcpi, property='system_name') + self.assertEqual(name.strip(), 'TESTVM') + typ = self.vm.cmd('qom-get', path=sclpcpi, property='system_type') + self.assertEqual(typ.strip(), 'LINUX') + sysplex = self.vm.cmd('qom-get', path=sclpcpi, property='sysplex_name') + self.assertEqual(sysplex.strip(), 'TESTPLEX') + level = self.vm.cmd('qom-get', path=sclpcpi, property='system_level') + self.assertEqual(level, 0x001a000000060b00) + if __name__ == '__main__': QemuSystemTest.main() From 6fc2caf39602aa42cc6ca8de24c7f2c568a3b634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 20 Oct 2025 11:49:00 +0200 Subject: [PATCH 1715/1794] hw/s390x/ccw: Remove deprecated s390-ccw-virtio-4.2 machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This machine has been supported for a period of more than 6 years. According to our versioned machine support policy (see commit ce80c4fa6ff "docs: document special exception for machine type deprecation & removal") it can now be removed. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20251020094903.72182-2-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/s390x/s390-virtio-ccw.c | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index ad2c48188a878..64b81345f1e20 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -715,26 +715,6 @@ static void s390_nmi(NMIState *n, int cpu_index, Error **errp) s390_cpu_restart(S390_CPU(cs)); } -static ram_addr_t s390_fixup_ram_size(ram_addr_t sz) -{ - /* same logic as in sclp.c */ - int increment_size = 20; - ram_addr_t newsz; - - while ((sz >> increment_size) > MAX_STORAGE_INCREMENTS) { - increment_size++; - } - newsz = sz >> increment_size << increment_size; - - if (sz != newsz) { - qemu_printf("Ram size %" PRIu64 "MB was fixed up to %" PRIu64 - "MB to match machine restrictions. Consider updating " - "the guest definition.\n", (uint64_t) (sz / MiB), - (uint64_t) (newsz / MiB)); - } - return newsz; -} - static inline bool machine_get_aes_key_wrap(Object *obj, Error **errp) { S390CcwMachineState *ms = S390_CCW_MACHINE(obj); @@ -1165,19 +1145,6 @@ static void ccw_machine_5_0_class_options(MachineClass *mc) } DEFINE_CCW_MACHINE(5, 0); -static void ccw_machine_4_2_instance_options(MachineState *machine) -{ - ccw_machine_5_0_instance_options(machine); -} - -static void ccw_machine_4_2_class_options(MachineClass *mc) -{ - ccw_machine_5_0_class_options(mc); - mc->fixup_ram_size = s390_fixup_ram_size; - compat_props_add(mc->compat_props, hw_compat_4_2, hw_compat_4_2_len); -} -DEFINE_CCW_MACHINE(4, 2); - static void ccw_machine_register_types(void) { type_register_static(&ccw_machine_info); From 9280e54fa664f7820ae046a0e0e139720d8eedb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 20 Oct 2025 11:49:01 +0200 Subject: [PATCH 1716/1794] hw/s390x/ccw: Remove SCLPDevice::increment_size field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SCLPDevice::increment_size field was only used by the s390-ccw-virtio-4.2 machine, which got removed. Remove it as now unused, along with the sclp_memory_init() method. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20251020094903.72182-3-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/s390x/sclp.c | 34 +++------------------------------- include/hw/s390x/sclp.h | 5 +---- 2 files changed, 4 insertions(+), 35 deletions(-) diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 8602a566a4937..c9a9c4bb375a7 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -110,7 +110,7 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) ReadInfo *read_info = (ReadInfo *) sccb; MachineState *machine = MACHINE(qdev_get_machine()); int cpu_count; - int rnsize, rnmax; + int rnmax; int required_len = SCCB_REQ_LEN(ReadInfo, machine->possible_cpus->len); int offset_cpu = s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB) ? offsetof(ReadInfo, entries) : @@ -153,21 +153,14 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) read_info->mha_pow = s390_get_mha_pow(); read_info->hmfai = cpu_to_be32(s390_get_hmfai()); - - rnsize = 1 << (sclp->increment_size - 20); - if (rnsize <= 128) { - read_info->rnsize = rnsize; - } else { - read_info->rnsize = 0; - read_info->rnsize2 = cpu_to_be32(rnsize); - } + read_info->rnsize = 1; /* * We don't support standby memory. maxram_size is used for sizing the * memory device region, which is not exposed through SCLP but through * diag500. */ - rnmax = machine->ram_size >> sclp->increment_size; + rnmax = machine->ram_size >> 20; if (rnmax < 0x10000) { read_info->rnmax = cpu_to_be16(rnmax); } else { @@ -406,25 +399,6 @@ static void sclp_realize(DeviceState *dev, Error **errp) } } -static void sclp_memory_init(SCLPDevice *sclp) -{ - MachineState *machine = MACHINE(qdev_get_machine()); - MachineClass *machine_class = MACHINE_GET_CLASS(qdev_get_machine()); - ram_addr_t initial_mem = machine->ram_size; - int increment_size = 20; - - /* The storage increment size is a multiple of 1M and is a power of 2. - * For some machine types, the number of storage increments must be - * MAX_STORAGE_INCREMENTS or fewer. - * The variable 'increment_size' is an exponent of 2 that can be - * used to calculate the size (in bytes) of an increment. */ - while (machine_class->fixup_ram_size != NULL && - (initial_mem >> increment_size) > MAX_STORAGE_INCREMENTS) { - increment_size++; - } - sclp->increment_size = increment_size; -} - static void sclp_init(Object *obj) { SCLPDevice *sclp = SCLP(obj); @@ -434,8 +408,6 @@ static void sclp_init(Object *obj) object_property_add_child(obj, TYPE_SCLP_EVENT_FACILITY, new); object_unref(new); sclp->event_facility = EVENT_FACILITY(new); - - sclp_memory_init(sclp); } static void sclp_class_init(ObjectClass *oc, const void *data) diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h index d32f6180e0d3e..33f01f85bb168 100644 --- a/include/hw/s390x/sclp.h +++ b/include/hw/s390x/sclp.h @@ -197,12 +197,9 @@ OBJECT_DECLARE_TYPE(SCLPDevice, SCLPDeviceClass, struct SCLPEventFacility; struct SCLPDevice { - /* private */ DeviceState parent_obj; - struct SCLPEventFacility *event_facility; - int increment_size; - /* public */ + struct SCLPEventFacility *event_facility; }; struct SCLPDeviceClass { From 049271a2ea27a0a617537ac8bad6839a67ffaa51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 20 Oct 2025 11:49:02 +0200 Subject: [PATCH 1717/1794] hw/core/machine: Remove MachineClass::fixup_ram_size callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MachineClass::fixup_ram_size callback, which was added in commit 5c30ef937f5 ("vl/s390x: fixup ram sizes for compat machines"), was only used by the s390-ccw-virtio-4.2 machine, which got removed. Remove it as now unused. Reported-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20251020094903.72182-4-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/core/machine.c | 3 --- include/hw/boards.h | 7 ------- 2 files changed, 10 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 681adbb7ac5d7..7aec3916e8093 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -648,9 +648,6 @@ static void machine_set_mem(Object *obj, Visitor *v, const char *name, mem->size = mc->default_ram_size; } mem->size = QEMU_ALIGN_UP(mem->size, 8192); - if (mc->fixup_ram_size) { - mem->size = mc->fixup_ram_size(mem->size); - } if ((ram_addr_t)mem->size != mem->size) { error_setg(errp, "ram size %llu exceeds permitted maximum %llu", (unsigned long long)mem->size, diff --git a/include/hw/boards.h b/include/hw/boards.h index 665b6201214c7..d0a69cd490b46 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -250,12 +250,6 @@ typedef struct { * It also will be used as a way to option into "-m" option support. * If it's not set by board, '-m' will be ignored and generic code will * not create default RAM MemoryRegion. - * @fixup_ram_size: - * Amends user provided ram size (with -m option) using machine - * specific algorithm. To be used by old machine types for compat - * purposes only. - * Applies only to default memory backend, i.e., explicit memory backend - * wasn't used. * @smbios_memory_device_size: * Default size of memory device, * SMBIOS 3.1.0 "7.18 Memory Device (Type 17)" @@ -325,7 +319,6 @@ struct MachineClass { unsigned cpu_index); const CPUArchIdList *(*possible_cpu_arch_ids)(MachineState *machine); int64_t (*get_default_cpu_node_id)(const MachineState *ms, int idx); - ram_addr_t (*fixup_ram_size)(ram_addr_t size); uint64_t smbios_memory_device_size; bool (*create_default_memdev)(MachineState *ms, const char *path, Error **errp); From 8a2b283efa007f705f12784fc10185435bd1852f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 20 Oct 2025 18:17:59 +0200 Subject: [PATCH 1718/1794] gitlab-ci: Decrease the size of the compiler cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Uploading the cache from the runner takes a long time in the MSYS2 job, mostly due to the size of the compiler cache. However, looking at runs with a non-poluted cache, it seems like you can get a build with a 99% hit rate already with ~ 160 MiB cache size, so the compiler cache with 500 MiB certainly contains a lot of stale files. Thus decrease the size of the ccache to a more reasonable value to speed up the MSYS2 job in our CI. While at it, also add a "du -sh" for the build folder to get a better feeling for the amount of object code that is required for the build, and publish the list of files in /var/cache to be able to better analyze what is really clogging our cache here. Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20251020161759.50241-1-thuth@redhat.com> --- .gitlab-ci.d/windows.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index 6e1135d8b8675..5dbdabfbec03c 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -25,6 +25,7 @@ msys2-64bit: expire_in: 7 days paths: - build/meson-logs + - build/cache-log.txt reports: junit: build/meson-logs/*.junit.xml before_script: @@ -94,7 +95,7 @@ msys2-64bit: - $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink - $env:CCACHE_BASEDIR = "$env:CI_PROJECT_DIR" - $env:CCACHE_DIR = "$env:CCACHE_BASEDIR/ccache" - - $env:CCACHE_MAXSIZE = "500M" + - $env:CCACHE_MAXSIZE = "180M" - $env:CCACHE_DEPEND = 1 # cache misses are too expensive with preprocessor mode - $env:CC = "ccache gcc" - mkdir build @@ -103,5 +104,7 @@ msys2-64bit: - ..\msys64\usr\bin\bash -lc "../configure $CONFIGURE_ARGS" - ..\msys64\usr\bin\bash -lc "make -j$env:JOBS" - ..\msys64\usr\bin\bash -lc "make check MTESTARGS='$TEST_ARGS' || { cat meson-logs/testlog.txt; exit 1; } ;" + - ..\msys64\usr\bin\bash -lc "ls -lR /var/cache > cache-log.txt" + - ..\msys64\usr\bin\bash -lc "du -sh ." - ..\msys64\usr\bin\bash -lc "ccache --show-stats" - Write-Output "Finished build at $(Get-Date -Format u)" From d9a8e9d79ade5b82d9ba467fbcb49a9003b88e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Mar 2025 14:21:06 +0100 Subject: [PATCH 1719/1794] hw/virtio/virtio-mem: Convert VIRTIO_MEM_USABLE_EXTENT to runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use target_arch() to check at runtime which target architecture is being run. Note, since TARGET_ARM is defined for TARGET_AARCH64, we check for both ARM & AARCH64 enum values. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Acked-by: David Hildenbrand Reviewed-by: Michael S. Tsirkin Message-Id: <20250502214551.80401-4-philmd@linaro.org> --- hw/virtio/virtio-mem.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 15ba6799f2281..f16445b9347c4 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -15,6 +15,7 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/units.h" +#include "qemu/target-info-qapi.h" #include "system/numa.h" #include "system/system.h" #include "system/ramblock.h" @@ -170,13 +171,20 @@ static bool virtio_mem_has_shared_zeropage(RAMBlock *rb) * necessary (as the section size can change). But it's more likely that the * section size will rather get smaller and not bigger over time. */ -#if defined(TARGET_X86_64) || defined(TARGET_I386) || defined(TARGET_S390X) -#define VIRTIO_MEM_USABLE_EXTENT (2 * (128 * MiB)) -#elif defined(TARGET_ARM) -#define VIRTIO_MEM_USABLE_EXTENT (2 * (512 * MiB)) -#else -#error VIRTIO_MEM_USABLE_EXTENT not defined -#endif +static uint64_t virtio_mem_usable_extent_size(void) +{ + switch (target_arch()) { + case SYS_EMU_TARGET_I386: + case SYS_EMU_TARGET_X86_64: + case SYS_EMU_TARGET_S390X: + return 2 * 128 * MiB; + case SYS_EMU_TARGET_AARCH64: + case SYS_EMU_TARGET_ARM: + return 2 * 512 * MiB; + default: + g_assert_not_reached(); + } +} static bool virtio_mem_is_busy(void) { @@ -699,7 +707,7 @@ static void virtio_mem_resize_usable_region(VirtIOMEM *vmem, bool can_shrink) { uint64_t newsize = MIN(memory_region_size(&vmem->memdev->mr), - requested_size + VIRTIO_MEM_USABLE_EXTENT); + requested_size + virtio_mem_usable_extent_size()); /* The usable region size always has to be multiples of the block size. */ newsize = QEMU_ALIGN_UP(newsize, vmem->block_size); From 0d9468ba523431794b0d11da64a7f67b95fe3ed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 16 Apr 2025 14:29:55 +0200 Subject: [PATCH 1720/1794] hw/virtio/virtio-mem: Convert VIRTIO_MEM_HAS_LEGACY_GUESTS to runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check legacy guests support at runtime: instead of evaluating the VIRTIO_MEM_HAS_LEGACY_GUESTS definition at compile time, call target_arch() to detect which target is being run at runtime. Register virtio_mem_legacy_guests_properties[] at runtime. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Acked-by: David Hildenbrand Reviewed-by: Michael S. Tsirkin Message-Id: <20250502214551.80401-5-philmd@linaro.org> --- hw/virtio/virtio-mem.c | 76 ++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index f16445b9347c4..ae7c13e33cf39 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -34,13 +34,21 @@ static const VMStateDescription vmstate_virtio_mem_device_early; -/* - * We only had legacy x86 guests that did not support - * VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE. Other targets don't have legacy guests. - */ -#if defined(TARGET_X86_64) || defined(TARGET_I386) -#define VIRTIO_MEM_HAS_LEGACY_GUESTS -#endif +static bool virtio_mem_has_legacy_guests(void) +{ + /* + * We only had legacy x86 guests that did not support + * VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE. Other targets don't have + * legacy guests. + */ + switch (target_arch()) { + case SYS_EMU_TARGET_I386: + case SYS_EMU_TARGET_X86_64: + return true; + default: + return false; + } +} /* * Let's not allow blocks smaller than 1 MiB, for example, to keep the tracking @@ -144,7 +152,6 @@ static uint64_t virtio_mem_default_block_size(RAMBlock *rb) return MAX(page_size, VIRTIO_MEM_MIN_BLOCK_SIZE); } -#if defined(VIRTIO_MEM_HAS_LEGACY_GUESTS) static bool virtio_mem_has_shared_zeropage(RAMBlock *rb) { /* @@ -155,7 +162,6 @@ static bool virtio_mem_has_shared_zeropage(RAMBlock *rb) return !qemu_ram_is_shared(rb) && qemu_ram_get_fd(rb) < 0 && qemu_ram_pagesize(rb) == qemu_real_host_page_size(); } -#endif /* VIRTIO_MEM_HAS_LEGACY_GUESTS */ /* * Size the usable region bigger than the requested size if possible. Esp. @@ -983,28 +989,28 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) rb = vmem->memdev->mr.ram_block; page_size = qemu_ram_pagesize(rb); -#if defined(VIRTIO_MEM_HAS_LEGACY_GUESTS) - switch (vmem->unplugged_inaccessible) { - case ON_OFF_AUTO_AUTO: - if (virtio_mem_has_shared_zeropage(rb)) { - vmem->unplugged_inaccessible = ON_OFF_AUTO_OFF; - } else { - vmem->unplugged_inaccessible = ON_OFF_AUTO_ON; - } - break; - case ON_OFF_AUTO_OFF: - if (!virtio_mem_has_shared_zeropage(rb)) { - warn_report("'%s' property set to 'off' with a memdev that does" - " not support the shared zeropage.", - VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP); + if (virtio_mem_has_legacy_guests()) { + switch (vmem->unplugged_inaccessible) { + case ON_OFF_AUTO_AUTO: + if (virtio_mem_has_shared_zeropage(rb)) { + vmem->unplugged_inaccessible = ON_OFF_AUTO_OFF; + } else { + vmem->unplugged_inaccessible = ON_OFF_AUTO_ON; + } + break; + case ON_OFF_AUTO_OFF: + if (!virtio_mem_has_shared_zeropage(rb)) { + warn_report("'%s' property set to 'off' with a memdev that does" + " not support the shared zeropage.", + VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP); + } + break; + default: + break; } - break; - default: - break; + } else { + vmem->unplugged_inaccessible = ON_OFF_AUTO_ON; } -#else /* VIRTIO_MEM_HAS_LEGACY_GUESTS */ - vmem->unplugged_inaccessible = ON_OFF_AUTO_ON; -#endif /* VIRTIO_MEM_HAS_LEGACY_GUESTS */ if (vmem->dynamic_memslots && vmem->unplugged_inaccessible != ON_OFF_AUTO_ON) { @@ -1701,16 +1707,17 @@ static const Property virtio_mem_properties[] = { DEFINE_PROP_BOOL(VIRTIO_MEM_PREALLOC_PROP, VirtIOMEM, prealloc, false), DEFINE_PROP_LINK(VIRTIO_MEM_MEMDEV_PROP, VirtIOMEM, memdev, TYPE_MEMORY_BACKEND, HostMemoryBackend *), -#if defined(VIRTIO_MEM_HAS_LEGACY_GUESTS) - DEFINE_PROP_ON_OFF_AUTO(VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP, VirtIOMEM, - unplugged_inaccessible, ON_OFF_AUTO_ON), -#endif DEFINE_PROP_BOOL(VIRTIO_MEM_EARLY_MIGRATION_PROP, VirtIOMEM, early_migration, true), DEFINE_PROP_BOOL(VIRTIO_MEM_DYNAMIC_MEMSLOTS_PROP, VirtIOMEM, dynamic_memslots, false), }; +static const Property virtio_mem_legacy_guests_properties[] = { + DEFINE_PROP_ON_OFF_AUTO(VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP, VirtIOMEM, + unplugged_inaccessible, ON_OFF_AUTO_ON), +}; + static uint64_t virtio_mem_rdm_get_min_granularity(const RamDiscardManager *rdm, const MemoryRegion *mr) { @@ -1862,6 +1869,9 @@ static void virtio_mem_class_init(ObjectClass *klass, const void *data) RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_CLASS(klass); device_class_set_props(dc, virtio_mem_properties); + if (virtio_mem_has_legacy_guests()) { + device_class_set_props(dc, virtio_mem_legacy_guests_properties); + } dc->vmsd = &vmstate_virtio_mem; set_bit(DEVICE_CATEGORY_MISC, dc->categories); From f1ee11469318a1e5d9da16b6c6c6b51c99b3edf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Mar 2025 14:51:58 +0100 Subject: [PATCH 1721/1794] hw/virtio: Compile virtio-mem.c once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unused "system/ram_addr.h" header. This file doesn't use any target specific definitions anymore, compile it once by moving it to system_virtio_ss[]. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: David Hildenbrand Reviewed-by: Pierrick Bouvier Acked-by: David Hildenbrand Reviewed-by: Michael S. Tsirkin Message-Id: <20250502214551.80401-6-philmd@linaro.org> --- hw/virtio/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index 48b9fedfa569c..affd66887db50 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -57,7 +57,7 @@ specific_virtio_ss.add(when: 'CONFIG_VIRTIO_BALLOON', if_true: files('virtio-bal specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_FS', if_true: files('vhost-user-fs.c')) specific_virtio_ss.add(when: 'CONFIG_VIRTIO_PMEM', if_true: files('virtio-pmem.c')) specific_virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock.c')) -specific_virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c')) +system_virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c')) system_virtio_ss.add(when: 'CONFIG_VIRTIO_NSM', if_true: files('virtio-nsm.c')) system_virtio_ss.add(when: 'CONFIG_VIRTIO_NSM', if_true: [files('cbor-helpers.c'), libcbor]) system_virtio_ss.add(when: 'CONFIG_VHOST_USER_SCMI', if_true: files('vhost-user-scmi.c')) From 62ef928312a981d0e25672f72dd467f4a0c05463 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 4 May 2025 18:01:35 +0200 Subject: [PATCH 1722/1794] hw/pci-host/raven: Simplify direct config access address decoding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use ctz instead of an open coded version and rename function to better show what it does. Signed-off-by: BALATON Zoltan Reviewed-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Message-ID: <68c038fd225463db282d0277d80cb525e0551413.1760795082.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/pci-host/raven.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index f8c0be5d21c35..ee9058e262a22 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -72,16 +72,9 @@ struct PRePPCIState { #define PCI_IO_BASE_ADDR 0x80000000 /* Physical address on main bus */ -static inline uint32_t raven_pci_io_config(hwaddr addr) +static inline uint32_t raven_idsel_to_addr(hwaddr addr) { - int i; - - for (i = 0; i < 11; i++) { - if ((addr & (1 << (11 + i))) != 0) { - break; - } - } - return (addr & 0x7ff) | (i << 11); + return (ctz16(addr >> 11) << 11) | (addr & 0x7ff); } static void raven_pci_io_write(void *opaque, hwaddr addr, @@ -89,7 +82,7 @@ static void raven_pci_io_write(void *opaque, hwaddr addr, { PREPPCIState *s = opaque; PCIHostState *phb = PCI_HOST_BRIDGE(s); - pci_data_write(phb->bus, raven_pci_io_config(addr), val, size); + pci_data_write(phb->bus, raven_idsel_to_addr(addr), val, size); } static uint64_t raven_pci_io_read(void *opaque, hwaddr addr, @@ -97,7 +90,7 @@ static uint64_t raven_pci_io_read(void *opaque, hwaddr addr, { PREPPCIState *s = opaque; PCIHostState *phb = PCI_HOST_BRIDGE(s); - return pci_data_read(phb->bus, raven_pci_io_config(addr), size); + return pci_data_read(phb->bus, raven_idsel_to_addr(addr), size); } static const MemoryRegionOps raven_pci_io_ops = { From 046d2e21fcf6a8b0dca8555da2d5684ca8a4dd82 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 4 May 2025 18:01:36 +0200 Subject: [PATCH 1723/1794] hw/pci-host/raven: Rename direct config access ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename memory io ops implementing PCI configuration direct access to mmcfg which describes better what these are for. Signed-off-by: BALATON Zoltan Reviewed-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Message-ID: <74fcd70106289663ea426161aada78e879995d6c.1760795082.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/pci-host/raven.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index ee9058e262a22..e0ebc81c24714 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -77,25 +77,24 @@ static inline uint32_t raven_idsel_to_addr(hwaddr addr) return (ctz16(addr >> 11) << 11) | (addr & 0x7ff); } -static void raven_pci_io_write(void *opaque, hwaddr addr, - uint64_t val, unsigned int size) +static void raven_mmcfg_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int size) { PREPPCIState *s = opaque; PCIHostState *phb = PCI_HOST_BRIDGE(s); pci_data_write(phb->bus, raven_idsel_to_addr(addr), val, size); } -static uint64_t raven_pci_io_read(void *opaque, hwaddr addr, - unsigned int size) +static uint64_t raven_mmcfg_read(void *opaque, hwaddr addr, unsigned int size) { PREPPCIState *s = opaque; PCIHostState *phb = PCI_HOST_BRIDGE(s); return pci_data_read(phb->bus, raven_idsel_to_addr(addr), size); } -static const MemoryRegionOps raven_pci_io_ops = { - .read = raven_pci_io_read, - .write = raven_pci_io_write, +static const MemoryRegionOps raven_mmcfg_ops = { + .read = raven_mmcfg_read, + .write = raven_mmcfg_write, .endianness = DEVICE_LITTLE_ENDIAN, }; @@ -253,8 +252,8 @@ static void raven_pcihost_realizefn(DeviceState *d, Error **errp) "pci-conf-data", 4); memory_region_add_subregion(&s->pci_io, 0xcfc, &h->data_mem); - memory_region_init_io(&h->mmcfg, OBJECT(s), &raven_pci_io_ops, s, - "pciio", 0x00400000); + memory_region_init_io(&h->mmcfg, OBJECT(s), &raven_mmcfg_ops, s, + "pci-mmcfg", 0x00400000); memory_region_add_subregion(address_space_mem, 0x80800000, &h->mmcfg); memory_region_init_io(&s->pci_intack, OBJECT(s), &raven_intack_ops, s, From dcff29f4cca2090d8454aa8a6ba23511e4758ff4 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 3 Jul 2025 00:09:02 +0200 Subject: [PATCH 1724/1794] hw/pci-host/raven: Use correct parameter in direct access ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of passing unneeded enclosing objects to the config direct access ops that only need the bus we can pass that directly thus simplifying the functions. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-ID: <226e0756661e72a03ba363887730112a58acde85.1760795082.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/pci-host/raven.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index e0ebc81c24714..eacffc86d849a 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -80,16 +80,16 @@ static inline uint32_t raven_idsel_to_addr(hwaddr addr) static void raven_mmcfg_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size) { - PREPPCIState *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - pci_data_write(phb->bus, raven_idsel_to_addr(addr), val, size); + PCIBus *hbus = opaque; + + pci_data_write(hbus, raven_idsel_to_addr(addr), val, size); } static uint64_t raven_mmcfg_read(void *opaque, hwaddr addr, unsigned int size) { - PREPPCIState *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - return pci_data_read(phb->bus, raven_idsel_to_addr(addr), size); + PCIBus *hbus = opaque; + + return pci_data_read(hbus, raven_idsel_to_addr(addr), size); } static const MemoryRegionOps raven_mmcfg_ops = { @@ -252,7 +252,7 @@ static void raven_pcihost_realizefn(DeviceState *d, Error **errp) "pci-conf-data", 4); memory_region_add_subregion(&s->pci_io, 0xcfc, &h->data_mem); - memory_region_init_io(&h->mmcfg, OBJECT(s), &raven_mmcfg_ops, s, + memory_region_init_io(&h->mmcfg, OBJECT(h), &raven_mmcfg_ops, h->bus, "pci-mmcfg", 0x00400000); memory_region_add_subregion(address_space_mem, 0x80800000, &h->mmcfg); From 2c6fab1c143059e59128b805f4e6a6796eedee43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 20 Oct 2025 23:46:21 +0200 Subject: [PATCH 1725/1794] hw/core: Filter machine list available for a particular target binary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Binaries can register a QOM type to filter their machines by filling their TargetInfo::machine_typename field. Commit 28502121be7 ("system/vl: Filter machine list available for a particular target binary") added the filter to machine_help_func() but missed the other places where the machine list must be filtered, such QMP 'query-machines' command used by QTests, and select_machine(). Fix that. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-Id: <20251020220941.65269-2-philmd@linaro.org> --- hw/core/machine-qmp-cmds.c | 4 +++- monitor/qemu-config-qmp.c | 3 ++- system/vl.c | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index 51d5c230f7e07..28dfd3e15bd0a 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -20,6 +20,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/type-helpers.h" #include "qemu/uuid.h" +#include "qemu/target-info.h" #include "qemu/target-info-qapi.h" #include "qom/qom-qobject.h" #include "system/hostmem.h" @@ -94,9 +95,10 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) MachineInfoList *qmp_query_machines(bool has_compat_props, bool compat_props, Error **errp) { - GSList *el, *machines = object_class_get_list(TYPE_MACHINE, false); + GSList *el, *machines; MachineInfoList *mach_list = NULL; + machines = object_class_get_list(target_machine_typename(), false); for (el = machines; el; el = el->next) { MachineClass *mc = el->data; const char *default_cpu_type = machine_class_default_cpu_type(mc); diff --git a/monitor/qemu-config-qmp.c b/monitor/qemu-config-qmp.c index 9a3b183602dea..8bd28fc232880 100644 --- a/monitor/qemu-config-qmp.c +++ b/monitor/qemu-config-qmp.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" +#include "qemu/target-info.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc.h" #include "qobject/qlist.h" @@ -128,7 +129,7 @@ static CommandLineParameterInfoList *query_all_machine_properties(void) ObjectProperty *prop; bool is_new; - machines = object_class_get_list(TYPE_MACHINE, false); + machines = object_class_get_list(target_machine_typename(), false); assert(machines); /* Loop over all machine classes */ diff --git a/system/vl.c b/system/vl.c index 646239e4a69d1..a96063f9901d8 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1672,7 +1672,8 @@ static MachineClass *select_machine(QDict *qdict, Error **errp) { ERRP_GUARD(); const char *machine_type = qdict_get_try_str(qdict, "type"); - g_autoptr(GSList) machines = object_class_get_list(TYPE_MACHINE, false); + g_autoptr(GSList) machines = object_class_get_list(target_machine_typename(), + false); MachineClass *machine_class = NULL; if (machine_type) { From 42a4700bb9c6e9424b3ed12f041209e0770f971c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 4 Apr 2025 00:54:15 +0200 Subject: [PATCH 1726/1794] hw/core/machine: Allow dynamic registration of valid CPU types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add MachineClass::get_valid_cpu_types(), a helper that returns a dynamic list of CPU types. Since the helper takes a MachineState argument, we know the machine is created by the time we call it. Suggested-by: Pierrick Bouvier Reviewed-by: Pierrick Bouvier Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20251020220941.65269-4-philmd@linaro.org> --- hw/core/machine.c | 28 ++++++++++++++++++++++++++++ include/hw/boards.h | 4 ++++ 2 files changed, 32 insertions(+) diff --git a/hw/core/machine.c b/hw/core/machine.c index 7aec3916e8093..cd2f1414a777d 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -1570,6 +1570,8 @@ static bool is_cpu_type_supported(const MachineState *machine, Error **errp) */ if (mc->valid_cpu_types) { assert(mc->valid_cpu_types[0] != NULL); + assert(!mc->get_valid_cpu_types); + for (i = 0; mc->valid_cpu_types[i]; i++) { if (object_class_dynamic_cast(oc, mc->valid_cpu_types[i])) { break; @@ -1596,6 +1598,32 @@ static bool is_cpu_type_supported(const MachineState *machine, Error **errp) error_append_hint(errp, "\n"); } + return false; + } + } else if (mc->get_valid_cpu_types) { + GPtrArray *vct = mc->get_valid_cpu_types(machine); + bool valid = false; + + for (i = 0; i < vct->len; i++) { + if (object_class_dynamic_cast(oc, vct->pdata[i])) { + valid = true; + break; + } + } + + if (!valid) { + g_autofree char *requested = cpu_model_from_type(machine->cpu_type); + + error_setg(errp, "Invalid CPU model: %s", requested); + error_append_hint(errp, "The valid models are: "); + for (i = 0; i < vct->len; i++) { + g_autofree char *model = cpu_model_from_type(vct->pdata[i]); + error_append_hint(errp, "%s%s", + model, i + 1 == vct->len ? "\n" : ", "); + } + } + g_ptr_array_free(vct, true); + if (!valid) { return false; } } diff --git a/include/hw/boards.h b/include/hw/boards.h index d0a69cd490b46..c45272b7414d1 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -253,6 +253,9 @@ typedef struct { * @smbios_memory_device_size: * Default size of memory device, * SMBIOS 3.1.0 "7.18 Memory Device (Type 17)" + * @get_valid_cpu_types: + * Returns a list of valid CPU types for this board. May be NULL + * if not needed. */ struct MachineClass { /*< private >*/ @@ -299,6 +302,7 @@ struct MachineClass { bool ignore_memory_transaction_failures; int numa_mem_align_shift; const char * const *valid_cpu_types; + GPtrArray *(*get_valid_cpu_types)(const MachineState *ms); strList *allowed_dynamic_sysbus_devices; bool auto_enable_numa_with_memhp; bool auto_enable_numa_with_memdev; From 6d06b1e3f6b2f906767e2faa1b781dcef2308bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 22 Apr 2025 12:11:56 +0200 Subject: [PATCH 1727/1794] hw/core: Introduce MachineClass::get_default_cpu_type() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MachineClass::get_default_cpu_type() runs once the machine is created, being able to evaluate runtime checks; it returns the machine default CPU type. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20251020221508.67413-7-philmd@linaro.org> --- hw/core/machine.c | 10 ++++++++++ include/hw/boards.h | 6 ++++++ system/vl.c | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index cd2f1414a777d..cd63803000c40 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -1556,6 +1556,16 @@ const char *machine_class_default_cpu_type(MachineClass *mc) return mc->default_cpu_type; } +const char *machine_default_cpu_type(const MachineState *ms) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + + if (mc->get_default_cpu_type) { + return mc->get_default_cpu_type(ms); + } + return machine_class_default_cpu_type(mc); +} + static bool is_cpu_type_supported(const MachineState *machine, Error **errp) { MachineClass *mc = MACHINE_GET_CLASS(machine); diff --git a/include/hw/boards.h b/include/hw/boards.h index c45272b7414d1..014007920dd85 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -25,6 +25,11 @@ OBJECT_DECLARE_TYPE(MachineState, MachineClass, MACHINE) extern MachineState *current_machine; +/** + * machine_default_cpu_type: Return the machine default CPU type. + * @ms: Machine state + */ +const char *machine_default_cpu_type(const MachineState *ms); /** * machine_class_default_cpu_type: Return the machine default CPU type. * @mc: Machine class @@ -303,6 +308,7 @@ struct MachineClass { int numa_mem_align_shift; const char * const *valid_cpu_types; GPtrArray *(*get_valid_cpu_types)(const MachineState *ms); + const char *(*get_default_cpu_type)(const MachineState *ms); strList *allowed_dynamic_sysbus_devices; bool auto_enable_numa_with_memhp; bool auto_enable_numa_with_memdev; diff --git a/system/vl.c b/system/vl.c index a96063f9901d8..fd98ea52d9ce0 100644 --- a/system/vl.c +++ b/system/vl.c @@ -3817,7 +3817,7 @@ void qemu_init(int argc, char **argv) migration_object_init(); /* parse features once if machine provides default cpu_type */ - current_machine->cpu_type = machine_class_default_cpu_type(machine_class); + current_machine->cpu_type = machine_default_cpu_type(current_machine); if (cpu_option) { current_machine->cpu_type = parse_cpu_option(cpu_option); } From 0f212b0d12cc35d4600d08d4a337f4b6c243f07b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 20 Oct 2025 16:50:03 +0200 Subject: [PATCH 1728/1794] hw/boards: Move DEFINE_MACHINE() definition closer to its doc string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code movement to have the DEFINE_MACHINE() definition follow its usage documentation comment. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-Id: <20251020220941.65269-3-philmd@linaro.org> --- include/hw/boards.h | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/include/hw/boards.h b/include/hw/boards.h index 014007920dd85..a214e3322ad0e 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -506,6 +506,23 @@ struct MachineState { * DEFINE_VIRT_MACHINE_IMPL(false, major, minor, micro, _, tag) */ +#define DEFINE_MACHINE(namestr, machine_initfn) \ + static void machine_initfn##_class_init(ObjectClass *oc, const void *data) \ + { \ + MachineClass *mc = MACHINE_CLASS(oc); \ + machine_initfn(mc); \ + } \ + static const TypeInfo machine_initfn##_typeinfo = { \ + .name = MACHINE_TYPE_NAME(namestr), \ + .parent = TYPE_MACHINE, \ + .class_init = machine_initfn##_class_init, \ + }; \ + static void machine_initfn##_register_types(void) \ + { \ + type_register_static(&machine_initfn##_typeinfo); \ + } \ + type_init(machine_initfn##_register_types) + /* * Helper for dispatching different macros based on how * many __VA_ARGS__ are passed. Supports 1 to 5 variadic @@ -765,23 +782,6 @@ struct MachineState { } \ } while (0) -#define DEFINE_MACHINE(namestr, machine_initfn) \ - static void machine_initfn##_class_init(ObjectClass *oc, const void *data) \ - { \ - MachineClass *mc = MACHINE_CLASS(oc); \ - machine_initfn(mc); \ - } \ - static const TypeInfo machine_initfn##_typeinfo = { \ - .name = MACHINE_TYPE_NAME(namestr), \ - .parent = TYPE_MACHINE, \ - .class_init = machine_initfn##_class_init, \ - }; \ - static void machine_initfn##_register_types(void) \ - { \ - type_register_static(&machine_initfn##_typeinfo); \ - } \ - type_init(machine_initfn##_register_types) - extern GlobalProperty hw_compat_10_1[]; extern const size_t hw_compat_10_1_len; From ce837a4fd65dedf04abb3fac7f69c26c69dfbe9a Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 2 May 2025 01:20:33 +0200 Subject: [PATCH 1729/1794] hw/boards: Extend DEFINE_MACHINE macro to cover more use cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a more general DEFINE_MACHINE_EXTENDED macro and define simpler versions with less parameters based on that. This is inspired by how the OBJECT_DEFINE macros do this in a similar way to allow using the shortened definition in more complex cases too. Signed-off-by: BALATON Zoltan Message-ID: Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier --- include/hw/boards.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/include/hw/boards.h b/include/hw/boards.h index a214e3322ad0e..b2964cf0556a2 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -506,7 +506,8 @@ struct MachineState { * DEFINE_VIRT_MACHINE_IMPL(false, major, minor, micro, _, tag) */ -#define DEFINE_MACHINE(namestr, machine_initfn) \ +#define DEFINE_MACHINE_EXTENDED(namestr, PARENT_NAME, InstanceName, \ + machine_initfn, ABSTRACT, ...) \ static void machine_initfn##_class_init(ObjectClass *oc, const void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ @@ -514,8 +515,11 @@ struct MachineState { } \ static const TypeInfo machine_initfn##_typeinfo = { \ .name = MACHINE_TYPE_NAME(namestr), \ - .parent = TYPE_MACHINE, \ + .parent = TYPE_##PARENT_NAME, \ .class_init = machine_initfn##_class_init, \ + .instance_size = sizeof(InstanceName), \ + .abstract = ABSTRACT, \ + .interfaces = (const InterfaceInfo[]) { __VA_ARGS__ }, \ }; \ static void machine_initfn##_register_types(void) \ { \ @@ -523,6 +527,14 @@ struct MachineState { } \ type_init(machine_initfn##_register_types) +#define DEFINE_MACHINE(namestr, machine_initfn) \ + DEFINE_MACHINE_EXTENDED(namestr, MACHINE, MachineState, machine_initfn, \ + false, { }) + +#define DEFINE_MACHINE_WITH_INTERFACES(namestr, machine_initfn, ...) \ + DEFINE_MACHINE_EXTENDED(namestr, MACHINE, MachineState, machine_initfn, \ + false, __VA_ARGS__) + /* * Helper for dispatching different macros based on how * many __VA_ARGS__ are passed. Supports 1 to 5 variadic From a4d934ea079563a38cc6ea4a556da3fb14a2b753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 24 Apr 2025 15:10:49 +0200 Subject: [PATCH 1730/1794] hw/boards: Introduce DEFINE_MACHINE_WITH_INTERFACE_ARRAY() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DEFINE_MACHINE_WITH_INTERFACE_ARRAY() is similar to DEFINE_MACHINE_WITH_INTERFACES() but allows to pass an InterfaceInfo[] pointer. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-Id: <20251020220941.65269-5-philmd@linaro.org> --- include/hw/boards.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/include/hw/boards.h b/include/hw/boards.h index b2964cf0556a2..a48ed4f86a353 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -507,7 +507,7 @@ struct MachineState { */ #define DEFINE_MACHINE_EXTENDED(namestr, PARENT_NAME, InstanceName, \ - machine_initfn, ABSTRACT, ...) \ + machine_initfn, ABSTRACT, ifaces...) \ static void machine_initfn##_class_init(ObjectClass *oc, const void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ @@ -519,7 +519,7 @@ struct MachineState { .class_init = machine_initfn##_class_init, \ .instance_size = sizeof(InstanceName), \ .abstract = ABSTRACT, \ - .interfaces = (const InterfaceInfo[]) { __VA_ARGS__ }, \ + .interfaces = ifaces, \ }; \ static void machine_initfn##_register_types(void) \ { \ @@ -529,11 +529,15 @@ struct MachineState { #define DEFINE_MACHINE(namestr, machine_initfn) \ DEFINE_MACHINE_EXTENDED(namestr, MACHINE, MachineState, machine_initfn, \ - false, { }) + false, NULL) -#define DEFINE_MACHINE_WITH_INTERFACES(namestr, machine_initfn, ...) \ +#define DEFINE_MACHINE_WITH_INTERFACE_ARRAY(namestr, machine_initfn, ifaces...)\ DEFINE_MACHINE_EXTENDED(namestr, MACHINE, MachineState, machine_initfn, \ - false, __VA_ARGS__) + false, ifaces) + +#define DEFINE_MACHINE_WITH_INTERFACES(namestr, machine_initfn, ...) \ + DEFINE_MACHINE_WITH_INTERFACE_ARRAY(namestr, machine_initfn, \ + (const InterfaceInfo[]) { __VA_ARGS__ }) /* * Helper for dispatching different macros based on how From 40eed74cc9deaa4343b31e49a6fbf7f037e8d841 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Wed, 8 Oct 2025 14:25:02 +0200 Subject: [PATCH 1731/1794] hw/i2c/smbus_eeprom: Add minimum write recovery time for DDR2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is needed for newer u-boot-sam460ex versions to pass the DRAM setup. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251008122502.9DA8956F301@zero.eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/i2c/smbus_eeprom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/i2c/smbus_eeprom.c b/hw/i2c/smbus_eeprom.c index 0a1088fbb0ab3..26e211b31ad58 100644 --- a/hw/i2c/smbus_eeprom.c +++ b/hw/i2c/smbus_eeprom.c @@ -288,6 +288,7 @@ uint8_t *spd_data_generate(enum sdram_type type, ram_addr_t ram_size) spd[33] = 8; /* addr/cmd hold time */ spd[34] = 20; /* data input setup time */ spd[35] = 8; /* data input hold time */ + spd[36] = (type == DDR2 ? 13 << 2 : 0); /* min. write recovery time */ /* checksum */ for (i = 0; i < 63; i++) { From 270b28681e369082dc17f58eae2871b75d3e8e6c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 15 Oct 2025 13:12:43 +0200 Subject: [PATCH 1732/1794] hw/ppc/e500: Check for compatible CPU type instead of aborting ungracefully MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using the ppce500 machine with an embedded CPU type that has the right MMU model, but is not part of the e500 CPU family, QEMU currently aborts ungracefully: $ ./qemu-system-ppc -machine ppce500 -cpu e200z5 -nographic qemu-system-ppc: ../qemu/hw/core/gpio.c:108: qdev_get_gpio_in_named: Assertion `n >= 0 && n < gpio_list->num_in' failed. Aborted (core dumped) The ppce500 machine expects a CPU with certain GPIO interrupt pins, so let's replace the coarse check for the MMU_BOOKE206 model with a more precise check that only allows CPUs from the e500 family. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3162 Signed-off-by: Thomas Huth Acked-by: Bernhard Beschow Reviewed-by: Harsh Prateek Bora Reviewed-by: BALATON Zoltan Message-ID: <20251015111243.1585018-1-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/ppc/e500.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 723c97fad2e16..3d69428f31c0d 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -20,6 +20,7 @@ #include "qemu/guest-random.h" #include "exec/target_page.h" #include "qapi/error.h" +#include "cpu-models.h" #include "e500.h" #include "e500-ccsr.h" #include "net/net.h" @@ -942,9 +943,8 @@ void ppce500_init(MachineState *machine) env = &cpu->env; cs = CPU(cpu); - if (env->mmu_model != POWERPC_MMU_BOOKE206) { - error_report("MMU model %i not supported by this machine", - env->mmu_model); + if (!(POWERPC_CPU_GET_CLASS(cpu)->svr & POWERPC_SVR_E500)) { + error_report("This machine needs a CPU from the e500 family"); exit(1); } From 6095c7dd3b6750eaac7eee7a6327ae68af19097e Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Thu, 16 Oct 2025 14:48:10 +0200 Subject: [PATCH 1733/1794] hw/openrisc/openrisc_sim: Avoid buffer overflow build error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves this build breakage (which is actually a false-positive) ../hw/openrisc/openrisc_sim.c: In function ‘openrisc_sim_init’: ../hw/openrisc/openrisc_sim.c:284:45: error: ‘__builtin___snprintf_chk’ output may be truncated before the last format character [-Werror=format-truncation=] snprintf(alias, sizeof(alias), "serial%d", uart_idx); ^ In file included from /usr/include/stdio.h:964:0, from /data/qemu/include/qemu/osdep.h:114, from ../hw/openrisc/openrisc_sim.c:21: /usr/include/bits/stdio2.h:54:10: note: ‘__builtin___snprintf_chk’ output between 8 and 9 bytes into a destination of size 8 return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ __glibc_objsize (__s), __fmt, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ __va_arg_pack ()); ~~~~~~~~~~~~~~~~~ by using a modern, more robust allocation pattern. Suggested-by: Peter Maydell Signed-off-by: Jan Kiszka Reviewed-by: Peter Maydell Message-ID: <298bd904-1ee9-439e-8220-7a24e0952861@siemens.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/openrisc/openrisc_sim.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index 880c8ebbb8b85..b7d9cdd900731 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -247,10 +247,10 @@ static void openrisc_sim_serial_init(Or1ksimState *state, hwaddr base, OpenRISCCPU *cpus[], int irq_pin, int uart_idx) { + g_autofree char *alias = g_strdup_printf("serial%d", uart_idx); void *fdt = state->fdt; char *nodename; qemu_irq serial_irq; - char alias[sizeof("serial0")]; int i; if (num_cpus > 1) { @@ -281,7 +281,6 @@ static void openrisc_sim_serial_init(Or1ksimState *state, hwaddr base, /* The /chosen node is created during fdt creation. */ qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename); } - snprintf(alias, sizeof(alias), "serial%d", uart_idx); qemu_fdt_setprop_string(fdt, "/aliases", alias, nodename); g_free(nodename); From f74ce602264d05f3b17de6d431f887fa0f0d5932 Mon Sep 17 00:00:00 2001 From: Roger Pau Monne Date: Fri, 17 Oct 2025 17:51:36 +0200 Subject: [PATCH 1734/1794] hw/xen: pass PCI domain to xc_physdev_map_pirq_msi() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's currently impossible for passthrough devices on segment different than 0 to work correctly, as the PCI domain is not provided to xc_physdev_map_pirq_msi(), and hence it's unconditionally assumed that all devices are on segment 0. Adjust the call to xc_physdev_map_pirq_msi() to pass the PCI domain in the high 16bits of the bus parameter. On versions of Xen where this is not supported the passed segment will be ignored and assume to be 0, no worse than the current state. Signed-off-by: Roger Pau Monné Reviewed-by: Frediano Ziglio Reviewed-by: Anthony PERARD Reviewed-by: Edgar E. Iglesias Message-ID: <20251017155136.16540-1-roger.pau@citrix.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/xen/xen_pt_msi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c index e9ba17317abaf..df15ccf0d030e 100644 --- a/hw/xen/xen_pt_msi.c +++ b/hw/xen/xen_pt_msi.c @@ -138,6 +138,7 @@ static int msi_msix_setup(XenPCIPassthroughState *s, rc = xc_physdev_map_pirq_msi(xen_xc, xen_domid, XEN_PT_AUTO_ASSIGN, ppirq, PCI_DEVFN(s->real_device.dev, s->real_device.func), + ((uint32_t)s->real_device.domain << 16) | s->real_device.bus, msix_entry, table_base); if (rc) { From f66752961a5750298229b86adccd821e7785bfdf Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 17 Oct 2025 18:18:00 +0200 Subject: [PATCH 1735/1794] hw/core/register: remove the REGISTER device type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The REGISTER class (RegisterInfo struct) is currently a QOM type inheriting from DEVICE. This class has no real purpose: - the qdev API is not used, - according to the comment preceding it, the object_initialize call is here to zero-initialize the struct. However all the effective struct attributes are then initialized explicitly. - the object is never parented. This commits drops the REGISTER QOM type completely, leaving the RegisterInfo struct as a bare C struct. The register_register_types function is left empty here because it is reused in the next commit. Reviewed-by: Alistair Francis Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Signed-off-by: Luc Michel Message-ID: <20251017161809.235740-2-luc.michel@amd.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/register.c | 18 ------------------ hw/net/can/xlnx-versal-canfd.c | 2 -- include/hw/register.h | 7 ------- 3 files changed, 27 deletions(-) diff --git a/hw/core/register.c b/hw/core/register.c index 3340df70b06e7..2553cb15aba9e 100644 --- a/hw/core/register.c +++ b/hw/core/register.c @@ -258,9 +258,6 @@ static RegisterInfoArray *register_init_block(DeviceState *owner, int index = rae[i].addr / data_size; RegisterInfo *r = &ri[index]; - /* Init the register, this will zero it. */ - object_initialize((void *)r, sizeof(*r), TYPE_REGISTER); - /* Set the properties of the register */ r->data = data + data_size * index; r->data_size = data_size; @@ -318,24 +315,9 @@ void register_finalize_block(RegisterInfoArray *r_array) g_free(r_array); } -static void register_class_init(ObjectClass *oc, const void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - /* Reason: needs to be wired up to work */ - dc->user_creatable = false; -} - -static const TypeInfo register_info = { - .name = TYPE_REGISTER, - .parent = TYPE_DEVICE, - .class_init = register_class_init, - .instance_size = sizeof(RegisterInfo), -}; static void register_register_types(void) { - type_register_static(®ister_info); } type_init(register_register_types) diff --git a/hw/net/can/xlnx-versal-canfd.c b/hw/net/can/xlnx-versal-canfd.c index 343348660b5a2..99bbdd7d3fe01 100644 --- a/hw/net/can/xlnx-versal-canfd.c +++ b/hw/net/can/xlnx-versal-canfd.c @@ -1868,8 +1868,6 @@ static int canfd_populate_regarray(XlnxVersalCANFDState *s, int index = rae[i].addr / 4; RegisterInfo *r = &s->reg_info[index]; - object_initialize(r, sizeof(*r), TYPE_REGISTER); - *r = (RegisterInfo) { .data = &s->regs[index], .data_size = sizeof(uint32_t), diff --git a/include/hw/register.h b/include/hw/register.h index a913c52aee5be..4d13ea183c700 100644 --- a/include/hw/register.h +++ b/include/hw/register.h @@ -75,10 +75,6 @@ struct RegisterAccessInfo { */ struct RegisterInfo { - /* */ - DeviceState parent_obj; - - /* */ void *data; int data_size; @@ -87,9 +83,6 @@ struct RegisterInfo { void *opaque; }; -#define TYPE_REGISTER "qemu-register" -DECLARE_INSTANCE_CHECKER(RegisterInfo, REGISTER, - TYPE_REGISTER) /** * This structure is used to group all of the individual registers which are From 5c6367bc1c8850f74812eeaaf87cff9911be58de Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 17 Oct 2025 18:18:01 +0200 Subject: [PATCH 1736/1794] hw/core/register: add the REGISTER_ARRAY type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the REGISTER_ARRAY QOM type. This type reuses the existing RegisterInfoArray struct. When `register_init_block' is called, it creates a REGISTER_ARRAY object and parents it to the calling device. This way it gets finalized when the device is. The memory region is parented to the REGISTER_ARRAY object to ensure correct finalizing order. The finalize function of the REGISTER_ARRAY type performs the necessary cleaning that used to be done by `register_finalize_block'. The latter is left empty and will be removed when all the register API users have been refactored. Reviewed-by: Alistair Francis Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Signed-off-by: Luc Michel Message-ID: <20251017161809.235740-3-luc.michel@amd.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/register.c | 26 ++++++++++++++++++++++---- include/hw/register.h | 4 ++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/hw/core/register.c b/hw/core/register.c index 2553cb15aba9e..1612ad174f9f9 100644 --- a/hw/core/register.c +++ b/hw/core/register.c @@ -245,10 +245,16 @@ static RegisterInfoArray *register_init_block(DeviceState *owner, size_t data_size_bits) { const char *device_prefix = object_get_typename(OBJECT(owner)); - RegisterInfoArray *r_array = g_new0(RegisterInfoArray, 1); + Object *obj; + RegisterInfoArray *r_array; int data_size = data_size_bits >> 3; int i; + obj = object_new(TYPE_REGISTER_ARRAY); + object_property_add_child(OBJECT(owner), "reg-array[*]", obj); + object_unref(obj); + + r_array = REGISTER_ARRAY(obj); r_array->r = g_new0(RegisterInfo *, num); r_array->num_elements = num; r_array->debug = debug_enabled; @@ -267,7 +273,7 @@ static RegisterInfoArray *register_init_block(DeviceState *owner, r_array->r[i] = r; } - memory_region_init_io(&r_array->mem, OBJECT(owner), ops, r_array, + memory_region_init_io(&r_array->mem, OBJECT(r_array), ops, r_array, device_prefix, memory_size); return r_array; @@ -309,15 +315,27 @@ RegisterInfoArray *register_init_block64(DeviceState *owner, data, ops, debug_enabled, memory_size, 64); } -void register_finalize_block(RegisterInfoArray *r_array) +static void register_array_finalize(Object *obj) { + RegisterInfoArray *r_array = REGISTER_ARRAY(obj); + g_free(r_array->r); - g_free(r_array); } +void register_finalize_block(RegisterInfoArray *r_array) +{ +} + +static const TypeInfo register_array_info = { + .name = TYPE_REGISTER_ARRAY, + .parent = TYPE_OBJECT, + .instance_size = sizeof(RegisterInfoArray), + .instance_finalize = register_array_finalize, +}; static void register_register_types(void) { + type_register_static(®ister_array_info); } type_init(register_register_types) diff --git a/include/hw/register.h b/include/hw/register.h index 4d13ea183c700..65c82600e06fb 100644 --- a/include/hw/register.h +++ b/include/hw/register.h @@ -83,6 +83,8 @@ struct RegisterInfo { void *opaque; }; +#define TYPE_REGISTER_ARRAY "qemu-register-array" +OBJECT_DECLARE_SIMPLE_TYPE(RegisterInfoArray, REGISTER_ARRAY) /** * This structure is used to group all of the individual registers which are @@ -96,6 +98,8 @@ struct RegisterInfo { */ struct RegisterInfoArray { + Object parent_obj; + MemoryRegion mem; int num_elements; From f423f7ebac82136515f3e3957a26f2482ff04ed9 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 17 Oct 2025 18:18:02 +0200 Subject: [PATCH 1737/1794] hw/core/register: remove the calls to `register_finalize_block' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is now a no-op. The register array is parented to the device and get finalized when the device is. Drop all the calls to `register_finalize_block'. Drop the RegisterInfoArray reference when it is not used elsewhere in the device. Reviewed-by: Alistair Francis Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Signed-off-by: Luc Michel Message-ID: <20251017161809.235740-4-luc.michel@amd.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/misc/xlnx-versal-crl.c | 38 +++++++++++--------------- hw/misc/xlnx-versal-trng.c | 1 - hw/misc/xlnx-versal-xramc.c | 12 ++------ hw/misc/xlnx-zynqmp-apu-ctrl.c | 12 ++------ hw/misc/xlnx-zynqmp-crf.c | 12 ++------ hw/nvram/xlnx-bbram.c | 13 ++------- hw/nvram/xlnx-versal-efuse-ctrl.c | 1 - hw/nvram/xlnx-zynqmp-efuse.c | 8 ------ include/hw/misc/xlnx-versal-crl.h | 1 - include/hw/misc/xlnx-versal-xramc.h | 1 - include/hw/misc/xlnx-zynqmp-apu-ctrl.h | 1 - include/hw/misc/xlnx-zynqmp-crf.h | 1 - include/hw/nvram/xlnx-bbram.h | 1 - 13 files changed, 28 insertions(+), 74 deletions(-) diff --git a/hw/misc/xlnx-versal-crl.c b/hw/misc/xlnx-versal-crl.c index 10e6af002bae3..5987f32c7165d 100644 --- a/hw/misc/xlnx-versal-crl.c +++ b/hw/misc/xlnx-versal-crl.c @@ -634,17 +634,17 @@ static void versal_crl_init(Object *obj) XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj); XlnxVersalCRLBase *xvcb = XLNX_VERSAL_CRL_BASE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; int i; - xvcb->reg_array = - register_init_block32(DEVICE(obj), crl_regs_info, - ARRAY_SIZE(crl_regs_info), - s->regs_info, s->regs, - &crl_ops, - XLNX_VERSAL_CRL_ERR_DEBUG, - CRL_R_MAX * 4); + reg_array = register_init_block32(DEVICE(obj), crl_regs_info, + ARRAY_SIZE(crl_regs_info), + s->regs_info, s->regs, + &crl_ops, + XLNX_VERSAL_CRL_ERR_DEBUG, + CRL_R_MAX * 4); xvcb->regs = s->regs; - sysbus_init_mmio(sbd, &xvcb->reg_array->mem); + sysbus_init_mmio(sbd, ®_array->mem); sysbus_init_irq(sbd, &s->irq); for (i = 0; i < ARRAY_SIZE(s->cfg.rpu); ++i) { @@ -688,17 +688,18 @@ static void versal2_crl_init(Object *obj) XlnxVersal2CRL *s = XLNX_VERSAL2_CRL(obj); XlnxVersalCRLBase *xvcb = XLNX_VERSAL_CRL_BASE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; size_t i; - xvcb->reg_array = register_init_block32(DEVICE(obj), versal2_crl_regs_info, - ARRAY_SIZE(versal2_crl_regs_info), - s->regs_info, s->regs, - &crl_ops, - XLNX_VERSAL_CRL_ERR_DEBUG, - VERSAL2_CRL_R_MAX * 4); + reg_array = register_init_block32(DEVICE(obj), versal2_crl_regs_info, + ARRAY_SIZE(versal2_crl_regs_info), + s->regs_info, s->regs, + &crl_ops, + XLNX_VERSAL_CRL_ERR_DEBUG, + VERSAL2_CRL_R_MAX * 4); xvcb->regs = s->regs; - sysbus_init_mmio(sbd, &xvcb->reg_array->mem); + sysbus_init_mmio(sbd, ®_array->mem); for (i = 0; i < ARRAY_SIZE(s->cfg.rpu); ++i) { object_property_add_link(obj, "rpu[*]", TYPE_ARM_CPU, @@ -750,12 +751,6 @@ static void versal2_crl_init(Object *obj) } } -static void crl_finalize(Object *obj) -{ - XlnxVersalCRLBase *s = XLNX_VERSAL_CRL_BASE(obj); - register_finalize_block(s->reg_array); -} - static const VMStateDescription vmstate_versal_crl = { .name = TYPE_XLNX_VERSAL_CRL, .version_id = 1, @@ -804,7 +799,6 @@ static const TypeInfo crl_base_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(XlnxVersalCRLBase), .class_size = sizeof(XlnxVersalCRLBaseClass), - .instance_finalize = crl_finalize, .abstract = true, }; diff --git a/hw/misc/xlnx-versal-trng.c b/hw/misc/xlnx-versal-trng.c index f34dd3ef35257..2b573a45bdb26 100644 --- a/hw/misc/xlnx-versal-trng.c +++ b/hw/misc/xlnx-versal-trng.c @@ -627,7 +627,6 @@ static void trng_finalize(Object *obj) { XlnxVersalTRng *s = XLNX_VERSAL_TRNG(obj); - register_finalize_block(s->reg_array); g_rand_free(s->prng); s->prng = NULL; } diff --git a/hw/misc/xlnx-versal-xramc.c b/hw/misc/xlnx-versal-xramc.c index 07370b80c0d91..d90f3e87c74ca 100644 --- a/hw/misc/xlnx-versal-xramc.c +++ b/hw/misc/xlnx-versal-xramc.c @@ -190,24 +190,19 @@ static void xram_ctrl_init(Object *obj) { XlnxXramCtrl *s = XLNX_XRAM_CTRL(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; - s->reg_array = + reg_array = register_init_block32(DEVICE(obj), xram_ctrl_regs_info, ARRAY_SIZE(xram_ctrl_regs_info), s->regs_info, s->regs, &xram_ctrl_ops, XLNX_XRAM_CTRL_ERR_DEBUG, XRAM_CTRL_R_MAX * 4); - sysbus_init_mmio(sbd, &s->reg_array->mem); + sysbus_init_mmio(sbd, ®_array->mem); sysbus_init_irq(sbd, &s->irq); } -static void xram_ctrl_finalize(Object *obj) -{ - XlnxXramCtrl *s = XLNX_XRAM_CTRL(obj); - register_finalize_block(s->reg_array); -} - static const VMStateDescription vmstate_xram_ctrl = { .name = TYPE_XLNX_XRAM_CTRL, .version_id = 1, @@ -241,7 +236,6 @@ static const TypeInfo xram_ctrl_info = { .instance_size = sizeof(XlnxXramCtrl), .class_init = xram_ctrl_class_init, .instance_init = xram_ctrl_init, - .instance_finalize = xram_ctrl_finalize, }; static void xram_ctrl_register_types(void) diff --git a/hw/misc/xlnx-zynqmp-apu-ctrl.c b/hw/misc/xlnx-zynqmp-apu-ctrl.c index e85da32d99c22..08777496d5624 100644 --- a/hw/misc/xlnx-zynqmp-apu-ctrl.c +++ b/hw/misc/xlnx-zynqmp-apu-ctrl.c @@ -179,16 +179,17 @@ static void zynqmp_apu_handle_wfi(void *opaque, int irq, int level) static void zynqmp_apu_init(Object *obj) { XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj); + RegisterInfoArray *reg_array; int i; - s->reg_array = + reg_array = register_init_block32(DEVICE(obj), zynqmp_apu_regs_info, ARRAY_SIZE(zynqmp_apu_regs_info), s->regs_info, s->regs, &zynqmp_apu_ops, XILINX_ZYNQMP_APU_ERR_DEBUG, APU_R_MAX * 4); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->reg_array->mem); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), ®_array->mem); sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq_imr); for (i = 0; i < APU_MAX_CPU; ++i) { @@ -208,12 +209,6 @@ static void zynqmp_apu_init(Object *obj) qdev_init_gpio_in_named(DEVICE(obj), zynqmp_apu_handle_wfi, "wfi_in", 4); } -static void zynqmp_apu_finalize(Object *obj) -{ - XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj); - register_finalize_block(s->reg_array); -} - static const VMStateDescription vmstate_zynqmp_apu = { .name = TYPE_XLNX_ZYNQMP_APU_CTRL, .version_id = 1, @@ -241,7 +236,6 @@ static const TypeInfo zynqmp_apu_info = { .instance_size = sizeof(XlnxZynqMPAPUCtrl), .class_init = zynqmp_apu_class_init, .instance_init = zynqmp_apu_init, - .instance_finalize = zynqmp_apu_finalize, }; static void zynqmp_apu_register_types(void) diff --git a/hw/misc/xlnx-zynqmp-crf.c b/hw/misc/xlnx-zynqmp-crf.c index cccca0e814e22..d9c1bd50e4ff1 100644 --- a/hw/misc/xlnx-zynqmp-crf.c +++ b/hw/misc/xlnx-zynqmp-crf.c @@ -211,24 +211,19 @@ static void crf_init(Object *obj) { XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; - s->reg_array = + reg_array = register_init_block32(DEVICE(obj), crf_regs_info, ARRAY_SIZE(crf_regs_info), s->regs_info, s->regs, &crf_ops, XLNX_ZYNQMP_CRF_ERR_DEBUG, CRF_R_MAX * 4); - sysbus_init_mmio(sbd, &s->reg_array->mem); + sysbus_init_mmio(sbd, ®_array->mem); sysbus_init_irq(sbd, &s->irq_ir); } -static void crf_finalize(Object *obj) -{ - XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj); - register_finalize_block(s->reg_array); -} - static const VMStateDescription vmstate_crf = { .name = TYPE_XLNX_ZYNQMP_CRF, .version_id = 1, @@ -255,7 +250,6 @@ static const TypeInfo crf_info = { .instance_size = sizeof(XlnxZynqMPCRF), .class_init = crf_class_init, .instance_init = crf_init, - .instance_finalize = crf_finalize, }; static void crf_register_types(void) diff --git a/hw/nvram/xlnx-bbram.c b/hw/nvram/xlnx-bbram.c index 5702bb3f31087..22aefbc240d51 100644 --- a/hw/nvram/xlnx-bbram.c +++ b/hw/nvram/xlnx-bbram.c @@ -456,8 +456,9 @@ static void bbram_ctrl_init(Object *obj) { XlnxBBRam *s = XLNX_BBRAM(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; - s->reg_array = + reg_array = register_init_block32(DEVICE(obj), bbram_ctrl_regs_info, ARRAY_SIZE(bbram_ctrl_regs_info), s->regs_info, s->regs, @@ -465,17 +466,10 @@ static void bbram_ctrl_init(Object *obj) XLNX_BBRAM_ERR_DEBUG, R_MAX * 4); - sysbus_init_mmio(sbd, &s->reg_array->mem); + sysbus_init_mmio(sbd, ®_array->mem); sysbus_init_irq(sbd, &s->irq_bbram); } -static void bbram_ctrl_finalize(Object *obj) -{ - XlnxBBRam *s = XLNX_BBRAM(obj); - - register_finalize_block(s->reg_array); -} - static void bbram_prop_set_drive(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -542,7 +536,6 @@ static const TypeInfo bbram_ctrl_info = { .instance_size = sizeof(XlnxBBRam), .class_init = bbram_ctrl_class_init, .instance_init = bbram_ctrl_init, - .instance_finalize = bbram_ctrl_finalize, }; static void bbram_ctrl_register_types(void) diff --git a/hw/nvram/xlnx-versal-efuse-ctrl.c b/hw/nvram/xlnx-versal-efuse-ctrl.c index 90962198008e7..6f17f32a0c327 100644 --- a/hw/nvram/xlnx-versal-efuse-ctrl.c +++ b/hw/nvram/xlnx-versal-efuse-ctrl.c @@ -728,7 +728,6 @@ static void efuse_ctrl_finalize(Object *obj) { XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(obj); - register_finalize_block(s->reg_array); g_free(s->extra_pg0_lock_spec); } diff --git a/hw/nvram/xlnx-zynqmp-efuse.c b/hw/nvram/xlnx-zynqmp-efuse.c index 5a218c32e84c9..ce35bb0cc1f72 100644 --- a/hw/nvram/xlnx-zynqmp-efuse.c +++ b/hw/nvram/xlnx-zynqmp-efuse.c @@ -816,13 +816,6 @@ static void zynqmp_efuse_init(Object *obj) sysbus_init_irq(sbd, &s->irq); } -static void zynqmp_efuse_finalize(Object *obj) -{ - XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(obj); - - register_finalize_block(s->reg_array); -} - static const VMStateDescription vmstate_efuse = { .name = TYPE_XLNX_ZYNQMP_EFUSE, .version_id = 1, @@ -857,7 +850,6 @@ static const TypeInfo efuse_info = { .instance_size = sizeof(XlnxZynqMPEFuse), .class_init = zynqmp_efuse_class_init, .instance_init = zynqmp_efuse_init, - .instance_finalize = zynqmp_efuse_finalize, }; static void efuse_register_types(void) diff --git a/include/hw/misc/xlnx-versal-crl.h b/include/hw/misc/xlnx-versal-crl.h index f6b8694ebeae2..49ed500acde7b 100644 --- a/include/hw/misc/xlnx-versal-crl.h +++ b/include/hw/misc/xlnx-versal-crl.h @@ -533,7 +533,6 @@ REG32(VERSAL2_RST_OCM, 0x3d8) struct XlnxVersalCRLBase { SysBusDevice parent_obj; - RegisterInfoArray *reg_array; uint32_t *regs; }; diff --git a/include/hw/misc/xlnx-versal-xramc.h b/include/hw/misc/xlnx-versal-xramc.h index d3d1862676fc1..35e4e8b91ddf0 100644 --- a/include/hw/misc/xlnx-versal-xramc.h +++ b/include/hw/misc/xlnx-versal-xramc.h @@ -90,7 +90,6 @@ typedef struct XlnxXramCtrl { unsigned int encoded_size; } cfg; - RegisterInfoArray *reg_array; uint32_t regs[XRAM_CTRL_R_MAX]; RegisterInfo regs_info[XRAM_CTRL_R_MAX]; } XlnxXramCtrl; diff --git a/include/hw/misc/xlnx-zynqmp-apu-ctrl.h b/include/hw/misc/xlnx-zynqmp-apu-ctrl.h index c3bf3c1583bb2..fbfe34aa7e5ea 100644 --- a/include/hw/misc/xlnx-zynqmp-apu-ctrl.h +++ b/include/hw/misc/xlnx-zynqmp-apu-ctrl.h @@ -85,7 +85,6 @@ struct XlnxZynqMPAPUCtrl { uint8_t cpu_pwrdwn_req; uint8_t cpu_in_wfi; - RegisterInfoArray *reg_array; uint32_t regs[APU_R_MAX]; RegisterInfo regs_info[APU_R_MAX]; }; diff --git a/include/hw/misc/xlnx-zynqmp-crf.h b/include/hw/misc/xlnx-zynqmp-crf.h index 02ef0bdeeee56..c746ae10397a8 100644 --- a/include/hw/misc/xlnx-zynqmp-crf.h +++ b/include/hw/misc/xlnx-zynqmp-crf.h @@ -203,7 +203,6 @@ struct XlnxZynqMPCRF { MemoryRegion iomem; qemu_irq irq_ir; - RegisterInfoArray *reg_array; uint32_t regs[CRF_R_MAX]; RegisterInfo regs_info[CRF_R_MAX]; }; diff --git a/include/hw/nvram/xlnx-bbram.h b/include/hw/nvram/xlnx-bbram.h index 58acbe9f51b8c..af90900bfc64d 100644 --- a/include/hw/nvram/xlnx-bbram.h +++ b/include/hw/nvram/xlnx-bbram.h @@ -47,7 +47,6 @@ struct XlnxBBRam { bool bbram8_wo; bool blk_ro; - RegisterInfoArray *reg_array; uint32_t regs[RMAX_XLNX_BBRAM]; RegisterInfo regs_info[RMAX_XLNX_BBRAM]; }; From 2fcb0f1f28a03bdf61259f4856b980517d1cc9d7 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 17 Oct 2025 18:18:03 +0200 Subject: [PATCH 1738/1794] hw/core/register: remove the `register_finalize_block' function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is now unused. Drop it. Reviewed-by: Alistair Francis Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Signed-off-by: Luc Michel Message-ID: <20251017161809.235740-5-luc.michel@amd.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/register.c | 4 ---- include/hw/register.h | 14 -------------- 2 files changed, 18 deletions(-) diff --git a/hw/core/register.c b/hw/core/register.c index 1612ad174f9f9..81316d485979d 100644 --- a/hw/core/register.c +++ b/hw/core/register.c @@ -322,10 +322,6 @@ static void register_array_finalize(Object *obj) g_free(r_array->r); } -void register_finalize_block(RegisterInfoArray *r_array) -{ -} - static const TypeInfo register_array_info = { .name = TYPE_REGISTER_ARRAY, .parent = TYPE_OBJECT, diff --git a/include/hw/register.h b/include/hw/register.h index 65c82600e06fb..7b0f4c8b7a663 100644 --- a/include/hw/register.h +++ b/include/hw/register.h @@ -209,18 +209,4 @@ RegisterInfoArray *register_init_block64(DeviceState *owner, bool debug_enabled, uint64_t memory_size); -/** - * This function should be called to cleanup the registers that were initialized - * when calling register_init_block32(). This function should only be called - * from the device's instance_finalize function. - * - * Any memory operations that the device performed that require cleanup (such - * as creating subregions) need to be called before calling this function. - * - * @r_array: A structure containing all of the registers, as returned by - * register_init_block32() - */ - -void register_finalize_block(RegisterInfoArray *r_array); - #endif From b7477d714005e1ed5784f6c066a4c43d55c010e5 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 17 Oct 2025 18:18:04 +0200 Subject: [PATCH 1739/1794] hw/net/can/xlnx-versal-canfd: refactor the banked registers logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CANFD device has several groups of registers: - the main control registers from 0x0 to 0xec - several banks of multiple registers. The number of banks is either hardcoded, or configurable using QOM properties: - Tx registers - Filter registers - Tx events registers - Rx0 registers - Rx1 registers As of now, all the registers are handled using the register API. The banked register logic results in a convoluted code to correctly allocate the register descriptors for the register API. This code bypasses the standard register API creation function (register_init_block). The resulting code leaks memory when the device is finalized. This commit introduces decoding logic for the banked registers. Those registers are quite simple in practice. Accessing them triggers no side-effect (only the filter registers need a check to catch guest invalid behaviour). Starting from the Tx events registers, they are all read-only. The main device memory region is changed to an I/O one, calling the new decoding logic when accessed. The register API memory region still overlaps all of it so for now the introduced code has no effect. The next commit will remove the register API usage for banked registers. Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Signed-off-by: Luc Michel Message-ID: <20251017161809.235740-6-luc.michel@amd.com> [PMD: Have canfd_decode_reg_bank() take optional @idx, types fixups] Signed-off-by: Philippe Mathieu-Daudé --- hw/net/can/xlnx-versal-canfd.c | 150 ++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 4 deletions(-) diff --git a/hw/net/can/xlnx-versal-canfd.c b/hw/net/can/xlnx-versal-canfd.c index 99bbdd7d3fe01..04f9a6d57d7e0 100644 --- a/hw/net/can/xlnx-versal-canfd.c +++ b/hw/net/can/xlnx-versal-canfd.c @@ -1410,6 +1410,22 @@ static uint64_t canfd_srr_pre_write(RegisterInfo *reg, uint64_t val64) return s->regs[R_SOFTWARE_RESET_REGISTER]; } +static void filter_reg_write(XlnxVersalCANFDState *s, hwaddr addr, + unsigned bank_idx, uint32_t val) +{ + size_t reg_idx = addr / sizeof(uint32_t); + + if (!(s->regs[R_ACCEPTANCE_FILTER_CONTROL_REGISTER] & (1 << bank_idx))) { + s->regs[reg_idx] = val; + } else { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter register 0x%" + HWADDR_PRIx " changed while filter %u enabled\n", + path, addr, bank_idx + 1); + } +} + static uint64_t filter_mask(RegisterInfo *reg, uint64_t val64) { XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); @@ -1763,9 +1779,135 @@ static void xlnx_versal_canfd_ptimer_cb(void *opaque) /* No action required on the timer rollover. */ } +static bool canfd_decode_reg_bank(XlnxVersalCANFDState *s, hwaddr addr, + hwaddr first_reg, hwaddr last_reg, + size_t num_banks, unsigned *idx, + hwaddr *offset) +{ + hwaddr base = addr - first_reg; + hwaddr span = last_reg - first_reg + sizeof(uint32_t); + unsigned index = base / span; + + if (index >= num_banks) { + return false; + } + if (idx) { + *idx = index; + } + + *offset = base % span; + *offset += first_reg; + + return true; +} + +/* + * Decode the given addr into a (idx, offset) pair: + * - idx is the bank index of the register at addr, + * - offset is the register offset relative to bank 0 + * + * @return true is the decoding succeded, false otherwise + */ +static bool canfd_decode_addr(XlnxVersalCANFDState *s, hwaddr addr, + unsigned *idx, hwaddr *offset) +{ + if (addr <= A_RX_FIFO_WATERMARK_REGISTER) { + /* from 0x0 to 0xec. Handled by the register API */ + g_assert_not_reached(); + } else if (addr < A_TB_ID_REGISTER) { + /* no register in this gap */ + return false; + } else if (addr < A_AFMR_REGISTER) { + /* TX registers */ + return canfd_decode_reg_bank(s, addr, + A_TB_ID_REGISTER, A_TB_DW15_REGISTER, + s->cfg.tx_fifo, idx, offset); + } else if (addr < A_TXE_FIFO_TB_ID_REGISTER) { + /* Filter registers */ + return canfd_decode_reg_bank(s, addr, + A_AFMR_REGISTER, A_AFIR_REGISTER, + 32, idx, offset); + } else if (addr < A_RB_ID_REGISTER) { + /* TX event registers */ + return canfd_decode_reg_bank(s, addr, + A_TXE_FIFO_TB_ID_REGISTER, + A_TXE_FIFO_TB_DLC_REGISTER, + 32, idx, offset); + } else if (addr < A_RB_ID_REGISTER_1) { + /* RX0 registers */ + return canfd_decode_reg_bank(s, addr, + A_RB_ID_REGISTER, + A_RB_DW15_REGISTER, + s->cfg.rx0_fifo, idx, offset); + } else if (addr <= A_RB_DW15_REGISTER_1) { + /* RX1 registers */ + return canfd_decode_reg_bank(s, addr, + A_RB_ID_REGISTER_1, + A_RB_DW15_REGISTER_1, + s->cfg.rx1_fifo, idx, offset); + } + + /* decode error */ + return false; +} + +static uint64_t canfd_read(void *opaque, hwaddr addr, unsigned size) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(opaque); + hwaddr reg_offset; + uint64_t ret; + + if (!canfd_decode_addr(s, addr, NULL, ®_offset)) { + qemu_log_mask(LOG_GUEST_ERROR, TYPE_XILINX_CANFD + ": read to unknown register at address 0x%" + HWADDR_PRIx "\n", addr); + return 0; + } + + switch (reg_offset) { + default: + ret = s->regs[addr / sizeof(uint32_t)]; + } + + return ret; +} + +static void canfd_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(opaque); + unsigned bank_idx; + hwaddr reg_offset; + + if (!canfd_decode_addr(s, addr, &bank_idx, ®_offset)) { + qemu_log_mask(LOG_GUEST_ERROR, TYPE_XILINX_CANFD + ": write to unknown register at address 0x%" + HWADDR_PRIx "\n", addr); + return; + } + + if (addr >= A_TXE_FIFO_TB_ID_REGISTER) { + /* All registers from TX event regs to the end are read-only */ + qemu_log_mask(LOG_GUEST_ERROR, TYPE_XILINX_CANFD + ": write to read-only register at 0x%" HWADDR_PRIx "\n", + addr); + return; + } + + switch (reg_offset) { + case A_AFMR_REGISTER: + case A_AFIR_REGISTER: + filter_reg_write(s, addr, bank_idx, value); + break; + + default: + s->regs[addr / sizeof(uint32_t)] = value; + } +} + static const MemoryRegionOps canfd_ops = { - .read = register_read_memory, - .write = register_write_memory, + .read = canfd_read, + .write = canfd_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, @@ -2018,8 +2160,8 @@ static void canfd_init(Object *obj) { XlnxVersalCANFDState *s = XILINX_CANFD(obj); - memory_region_init(&s->iomem, obj, TYPE_XILINX_CANFD, - XLNX_VERSAL_CANFD_R_MAX * 4); + memory_region_init_io(&s->iomem, obj, &canfd_ops, s, TYPE_XILINX_CANFD, + XLNX_VERSAL_CANFD_R_MAX * 4); } static const VMStateDescription vmstate_canfd = { From c11200c71575cb38f78548743555c2797d14bf96 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 17 Oct 2025 18:18:05 +0200 Subject: [PATCH 1740/1794] hw/net/can/xlnx-versal-canfd: remove register API usage for banked regs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we have a simple decoding logic for all the banked registers, remove the register API usage for them. This restricts the register API usage to only the base registers (from 0x0 to 0xec). This also removes all the custom code that was creating register descriptors for the register API and was leading to memory leaks when the device was finalized. Reviewed-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Signed-off-by: Luc Michel Message-ID: <20251017161809.235740-7-luc.michel@amd.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/can/xlnx-versal-canfd.c | 303 ++--------------------------- include/hw/net/xlnx-versal-canfd.h | 8 - 2 files changed, 15 insertions(+), 296 deletions(-) diff --git a/hw/net/can/xlnx-versal-canfd.c b/hw/net/can/xlnx-versal-canfd.c index 04f9a6d57d7e0..5735639b85a68 100644 --- a/hw/net/can/xlnx-versal-canfd.c +++ b/hw/net/can/xlnx-versal-canfd.c @@ -1426,46 +1426,6 @@ static void filter_reg_write(XlnxVersalCANFDState *s, hwaddr addr, } } -static uint64_t filter_mask(RegisterInfo *reg, uint64_t val64) -{ - XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); - uint32_t reg_idx = (reg->access->addr) / 4; - uint32_t val = val64; - uint32_t filter_offset = (reg_idx - R_AFMR_REGISTER) / 2; - - if (!(s->regs[R_ACCEPTANCE_FILTER_CONTROL_REGISTER] & - (1 << filter_offset))) { - s->regs[reg_idx] = val; - } else { - g_autofree char *path = object_get_canonical_path(OBJECT(s)); - - qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter %d not enabled\n", - path, filter_offset + 1); - } - - return s->regs[reg_idx]; -} - -static uint64_t filter_id(RegisterInfo *reg, uint64_t val64) -{ - XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); - hwaddr reg_idx = (reg->access->addr) / 4; - uint32_t val = val64; - uint32_t filter_offset = (reg_idx - R_AFIR_REGISTER) / 2; - - if (!(s->regs[R_ACCEPTANCE_FILTER_CONTROL_REGISTER] & - (1 << filter_offset))) { - s->regs[reg_idx] = val; - } else { - g_autofree char *path = object_get_canonical_path(OBJECT(s)); - - qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter %d not enabled\n", - path, filter_offset + 1); - } - - return s->regs[reg_idx]; -} - static uint64_t canfd_tx_fifo_status_prew(RegisterInfo *reg, uint64_t val64) { XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); @@ -1591,125 +1551,6 @@ static uint64_t canfd_write_check_prew(RegisterInfo *reg, uint64_t val64) return 0; } -static const RegisterAccessInfo canfd_tx_regs[] = { - { .name = "TB_ID_REGISTER", .addr = A_TB_ID_REGISTER, - },{ .name = "TB0_DLC_REGISTER", .addr = A_TB0_DLC_REGISTER, - },{ .name = "TB_DW0_REGISTER", .addr = A_TB_DW0_REGISTER, - },{ .name = "TB_DW1_REGISTER", .addr = A_TB_DW1_REGISTER, - },{ .name = "TB_DW2_REGISTER", .addr = A_TB_DW2_REGISTER, - },{ .name = "TB_DW3_REGISTER", .addr = A_TB_DW3_REGISTER, - },{ .name = "TB_DW4_REGISTER", .addr = A_TB_DW4_REGISTER, - },{ .name = "TB_DW5_REGISTER", .addr = A_TB_DW5_REGISTER, - },{ .name = "TB_DW6_REGISTER", .addr = A_TB_DW6_REGISTER, - },{ .name = "TB_DW7_REGISTER", .addr = A_TB_DW7_REGISTER, - },{ .name = "TB_DW8_REGISTER", .addr = A_TB_DW8_REGISTER, - },{ .name = "TB_DW9_REGISTER", .addr = A_TB_DW9_REGISTER, - },{ .name = "TB_DW10_REGISTER", .addr = A_TB_DW10_REGISTER, - },{ .name = "TB_DW11_REGISTER", .addr = A_TB_DW11_REGISTER, - },{ .name = "TB_DW12_REGISTER", .addr = A_TB_DW12_REGISTER, - },{ .name = "TB_DW13_REGISTER", .addr = A_TB_DW13_REGISTER, - },{ .name = "TB_DW14_REGISTER", .addr = A_TB_DW14_REGISTER, - },{ .name = "TB_DW15_REGISTER", .addr = A_TB_DW15_REGISTER, - } -}; - -static const RegisterAccessInfo canfd_rx0_regs[] = { - { .name = "RB_ID_REGISTER", .addr = A_RB_ID_REGISTER, - .ro = 0xffffffff, - },{ .name = "RB_DLC_REGISTER", .addr = A_RB_DLC_REGISTER, - .ro = 0xfe1fffff, - },{ .name = "RB_DW0_REGISTER", .addr = A_RB_DW0_REGISTER, - .ro = 0xffffffff, - },{ .name = "RB_DW1_REGISTER", .addr = A_RB_DW1_REGISTER, - .ro = 0xffffffff, - },{ .name = "RB_DW2_REGISTER", .addr = A_RB_DW2_REGISTER, - .ro = 0xffffffff, - },{ .name = "RB_DW3_REGISTER", .addr = A_RB_DW3_REGISTER, - .ro = 0xffffffff, - },{ .name = "RB_DW4_REGISTER", .addr = A_RB_DW4_REGISTER, - .ro = 0xffffffff, - },{ .name = "RB_DW5_REGISTER", .addr = A_RB_DW5_REGISTER, - .ro = 0xffffffff, - },{ .name = "RB_DW6_REGISTER", .addr = A_RB_DW6_REGISTER, - .ro = 0xffffffff, - },{ .name = "RB_DW7_REGISTER", .addr = A_RB_DW7_REGISTER, - .ro = 0xffffffff, - },{ .name = "RB_DW8_REGISTER", .addr = A_RB_DW8_REGISTER, - .ro = 0xffffffff, - },{ .name = "RB_DW9_REGISTER", .addr = A_RB_DW9_REGISTER, - .ro = 0xffffffff, - },{ .name = "RB_DW10_REGISTER", .addr = A_RB_DW10_REGISTER, - .ro = 0xffffffff, - },{ .name = "RB_DW11_REGISTER", .addr = A_RB_DW11_REGISTER, - .ro = 0xffffffff, - },{ .name = "RB_DW12_REGISTER", .addr = A_RB_DW12_REGISTER, - .ro = 0xffffffff, - },{ .name = "RB_DW13_REGISTER", .addr = A_RB_DW13_REGISTER, - .ro = 0xffffffff, - },{ .name = "RB_DW14_REGISTER", .addr = A_RB_DW14_REGISTER, - .ro = 0xffffffff, - },{ .name = "RB_DW15_REGISTER", .addr = A_RB_DW15_REGISTER, - .ro = 0xffffffff, - } -}; - -static const RegisterAccessInfo canfd_rx1_regs[] = { - { .name = "RB_ID_REGISTER_1", .addr = A_RB_ID_REGISTER_1, - .ro = 0xffffffff, - },{ .name = "RB_DLC_REGISTER_1", .addr = A_RB_DLC_REGISTER_1, - .ro = 0xfe1fffff, - },{ .name = "RB0_DW0_REGISTER_1", .addr = A_RB0_DW0_REGISTER_1, - .ro = 0xffffffff, - },{ .name = "RB_DW1_REGISTER_1", .addr = A_RB_DW1_REGISTER_1, - .ro = 0xffffffff, - },{ .name = "RB_DW2_REGISTER_1", .addr = A_RB_DW2_REGISTER_1, - .ro = 0xffffffff, - },{ .name = "RB_DW3_REGISTER_1", .addr = A_RB_DW3_REGISTER_1, - .ro = 0xffffffff, - },{ .name = "RB_DW4_REGISTER_1", .addr = A_RB_DW4_REGISTER_1, - .ro = 0xffffffff, - },{ .name = "RB_DW5_REGISTER_1", .addr = A_RB_DW5_REGISTER_1, - .ro = 0xffffffff, - },{ .name = "RB_DW6_REGISTER_1", .addr = A_RB_DW6_REGISTER_1, - .ro = 0xffffffff, - },{ .name = "RB_DW7_REGISTER_1", .addr = A_RB_DW7_REGISTER_1, - .ro = 0xffffffff, - },{ .name = "RB_DW8_REGISTER_1", .addr = A_RB_DW8_REGISTER_1, - .ro = 0xffffffff, - },{ .name = "RB_DW9_REGISTER_1", .addr = A_RB_DW9_REGISTER_1, - .ro = 0xffffffff, - },{ .name = "RB_DW10_REGISTER_1", .addr = A_RB_DW10_REGISTER_1, - .ro = 0xffffffff, - },{ .name = "RB_DW11_REGISTER_1", .addr = A_RB_DW11_REGISTER_1, - .ro = 0xffffffff, - },{ .name = "RB_DW12_REGISTER_1", .addr = A_RB_DW12_REGISTER_1, - .ro = 0xffffffff, - },{ .name = "RB_DW13_REGISTER_1", .addr = A_RB_DW13_REGISTER_1, - .ro = 0xffffffff, - },{ .name = "RB_DW14_REGISTER_1", .addr = A_RB_DW14_REGISTER_1, - .ro = 0xffffffff, - },{ .name = "RB_DW15_REGISTER_1", .addr = A_RB_DW15_REGISTER_1, - .ro = 0xffffffff, - } -}; - -/* Acceptance filter registers. */ -static const RegisterAccessInfo canfd_af_regs[] = { - { .name = "AFMR_REGISTER", .addr = A_AFMR_REGISTER, - .pre_write = filter_mask, - },{ .name = "AFIR_REGISTER", .addr = A_AFIR_REGISTER, - .pre_write = filter_id, - } -}; - -static const RegisterAccessInfo canfd_txe_regs[] = { - { .name = "TXE_FIFO_TB_ID_REGISTER", .addr = A_TXE_FIFO_TB_ID_REGISTER, - .ro = 0xffffffff, - },{ .name = "TXE_FIFO_TB_DLC_REGISTER", .addr = A_TXE_FIFO_TB_DLC_REGISTER, - .ro = 0xffffffff, - } -}; - static const RegisterAccessInfo canfd_regs_info[] = { { .name = "SOFTWARE_RESET_REGISTER", .addr = A_SOFTWARE_RESET_REGISTER, .pre_write = canfd_srr_pre_write, @@ -1915,6 +1756,16 @@ static const MemoryRegionOps canfd_ops = { }, }; +static const MemoryRegionOps canfd_regs_ops = { + .read = register_read_memory, + .write = register_write_memory, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + static void canfd_reset(DeviceState *dev) { XlnxVersalCANFDState *s = XILINX_CANFD(dev); @@ -1994,140 +1845,16 @@ static int xlnx_canfd_connect_to_bus(XlnxVersalCANFDState *s, return can_bus_insert_client(bus, &s->bus_client); } -#define NUM_REG_PER_AF ARRAY_SIZE(canfd_af_regs) -#define NUM_AF 32 -#define NUM_REG_PER_TXE ARRAY_SIZE(canfd_txe_regs) -#define NUM_TXE 32 - -static int canfd_populate_regarray(XlnxVersalCANFDState *s, - RegisterInfoArray *r_array, int pos, - const RegisterAccessInfo *rae, - int num_rae) -{ - int i; - - for (i = 0; i < num_rae; i++) { - int index = rae[i].addr / 4; - RegisterInfo *r = &s->reg_info[index]; - - *r = (RegisterInfo) { - .data = &s->regs[index], - .data_size = sizeof(uint32_t), - .access = &rae[i], - .opaque = OBJECT(s), - }; - - r_array->r[i + pos] = r; - } - return i + pos; -} - -static void canfd_create_rai(RegisterAccessInfo *rai_array, - const RegisterAccessInfo *canfd_regs, - int template_rai_array_sz, - int num_template_to_copy) -{ - int i; - int reg_num; - - for (reg_num = 0; reg_num < num_template_to_copy; reg_num++) { - int pos = reg_num * template_rai_array_sz; - - memcpy(rai_array + pos, canfd_regs, - template_rai_array_sz * sizeof(RegisterAccessInfo)); - - for (i = 0; i < template_rai_array_sz; i++) { - const char *name = canfd_regs[i].name; - uint64_t addr = canfd_regs[i].addr; - rai_array[i + pos].name = g_strdup_printf("%s%d", name, reg_num); - rai_array[i + pos].addr = addr + pos * 4; - } - } -} - -static RegisterInfoArray *canfd_create_regarray(XlnxVersalCANFDState *s) -{ - const char *device_prefix = object_get_typename(OBJECT(s)); - uint64_t memory_size = XLNX_VERSAL_CANFD_R_MAX * 4; - int num_regs; - int pos = 0; - RegisterInfoArray *r_array; - - num_regs = ARRAY_SIZE(canfd_regs_info) + - s->cfg.tx_fifo * NUM_REGS_PER_MSG_SPACE + - s->cfg.rx0_fifo * NUM_REGS_PER_MSG_SPACE + - NUM_AF * NUM_REG_PER_AF + - NUM_TXE * NUM_REG_PER_TXE; - - s->tx_regs = g_new0(RegisterAccessInfo, - s->cfg.tx_fifo * ARRAY_SIZE(canfd_tx_regs)); - - canfd_create_rai(s->tx_regs, canfd_tx_regs, - ARRAY_SIZE(canfd_tx_regs), s->cfg.tx_fifo); - - s->rx0_regs = g_new0(RegisterAccessInfo, - s->cfg.rx0_fifo * ARRAY_SIZE(canfd_rx0_regs)); - - canfd_create_rai(s->rx0_regs, canfd_rx0_regs, - ARRAY_SIZE(canfd_rx0_regs), s->cfg.rx0_fifo); - - s->af_regs = g_new0(RegisterAccessInfo, - NUM_AF * ARRAY_SIZE(canfd_af_regs)); - - canfd_create_rai(s->af_regs, canfd_af_regs, - ARRAY_SIZE(canfd_af_regs), NUM_AF); - - s->txe_regs = g_new0(RegisterAccessInfo, - NUM_TXE * ARRAY_SIZE(canfd_txe_regs)); - - canfd_create_rai(s->txe_regs, canfd_txe_regs, - ARRAY_SIZE(canfd_txe_regs), NUM_TXE); - - if (s->cfg.enable_rx_fifo1) { - num_regs += s->cfg.rx1_fifo * NUM_REGS_PER_MSG_SPACE; - - s->rx1_regs = g_new0(RegisterAccessInfo, - s->cfg.rx1_fifo * ARRAY_SIZE(canfd_rx1_regs)); - - canfd_create_rai(s->rx1_regs, canfd_rx1_regs, - ARRAY_SIZE(canfd_rx1_regs), s->cfg.rx1_fifo); - } - - r_array = g_new0(RegisterInfoArray, 1); - r_array->r = g_new0(RegisterInfo * , num_regs); - r_array->num_elements = num_regs; - r_array->prefix = device_prefix; - - pos = canfd_populate_regarray(s, r_array, pos, - canfd_regs_info, - ARRAY_SIZE(canfd_regs_info)); - pos = canfd_populate_regarray(s, r_array, pos, - s->tx_regs, s->cfg.tx_fifo * - NUM_REGS_PER_MSG_SPACE); - pos = canfd_populate_regarray(s, r_array, pos, - s->rx0_regs, s->cfg.rx0_fifo * - NUM_REGS_PER_MSG_SPACE); - if (s->cfg.enable_rx_fifo1) { - pos = canfd_populate_regarray(s, r_array, pos, - s->rx1_regs, s->cfg.rx1_fifo * - NUM_REGS_PER_MSG_SPACE); - } - pos = canfd_populate_regarray(s, r_array, pos, - s->af_regs, NUM_AF * NUM_REG_PER_AF); - pos = canfd_populate_regarray(s, r_array, pos, - s->txe_regs, NUM_TXE * NUM_REG_PER_TXE); - - memory_region_init_io(&r_array->mem, OBJECT(s), &canfd_ops, r_array, - device_prefix, memory_size); - return r_array; -} - static void canfd_realize(DeviceState *dev, Error **errp) { XlnxVersalCANFDState *s = XILINX_CANFD(dev); RegisterInfoArray *reg_array; - reg_array = canfd_create_regarray(s); + reg_array = register_init_block32(dev, canfd_regs_info, + ARRAY_SIZE(canfd_regs_info), s->reg_info, + s->regs, &canfd_regs_ops, false, + A_RX_FIFO_WATERMARK_REGISTER + + sizeof(uint32_t)); memory_region_add_subregion(&s->iomem, 0x00, ®_array->mem); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq_canfd_int); diff --git a/include/hw/net/xlnx-versal-canfd.h b/include/hw/net/xlnx-versal-canfd.h index ad3104dd13f19..396f90d6dc100 100644 --- a/include/hw/net/xlnx-versal-canfd.h +++ b/include/hw/net/xlnx-versal-canfd.h @@ -54,14 +54,6 @@ typedef struct XlnxVersalCANFDState { qemu_irq irq_addr_err; RegisterInfo reg_info[XLNX_VERSAL_CANFD_R_MAX]; - RegisterAccessInfo *tx_regs; - RegisterAccessInfo *rx0_regs; - RegisterAccessInfo *rx1_regs; - RegisterAccessInfo *af_regs; - RegisterAccessInfo *txe_regs; - RegisterAccessInfo *rx_mailbox_regs; - RegisterAccessInfo *af_mask_regs_mailbox; - uint32_t regs[XLNX_VERSAL_CANFD_R_MAX]; ptimer_state *canfd_timer; From 5e7ea00c5fdcb05d189fb0a5cb8ab9f520303ee8 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 18 Oct 2025 16:04:59 +0200 Subject: [PATCH 1741/1794] hw/ppc/prep: Always create prep-systemio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prep-systemio device models the system control ports of the 40p machine which is not an optional pluggable device but part of the system so it should not be disabled by -nodefaults but always created. Additionally remove some line breaks to make lines related to one device appear in one block for logical separation from other devices. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-ID: Signed-off-by: Philippe Mathieu-Daudé --- hw/ppc/prep.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 982e40e53e1a6..c730cb3429e66 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -308,6 +308,13 @@ static void ibm_40p_init(MachineState *machine) sysbus_connect_irq(pcihost, 0, qdev_get_gpio_in(i82378_dev, 15)); isa_bus = ISA_BUS(qdev_get_child_bus(i82378_dev, "isa.0")); + /* system control ports */ + isa_dev = isa_new("prep-systemio"); + dev = DEVICE(isa_dev); + qdev_prop_set_uint32(dev, "ibm-planar-id", 0xfc); + qdev_prop_set_uint32(dev, "equipment", 0xc0); + isa_realize_and_unref(isa_dev, isa_bus, &error_fatal); + /* Memory controller */ isa_dev = isa_new("rs6000-mc"); dev = DEVICE(isa_dev); @@ -333,7 +340,6 @@ static void ibm_40p_init(MachineState *machine) dev = DEVICE(isa_dev); qdev_prop_set_uint32(dev, "iobase", 0x830); qdev_prop_set_uint32(dev, "irq", 10); - if (machine->audiodev) { qdev_prop_set_string(dev, "audiodev", machine->audiodev); } @@ -344,14 +350,7 @@ static void ibm_40p_init(MachineState *machine) qdev_prop_set_uint32(dev, "config", 12); isa_realize_and_unref(isa_dev, isa_bus, &error_fatal); - isa_dev = isa_new("prep-systemio"); - dev = DEVICE(isa_dev); - qdev_prop_set_uint32(dev, "ibm-planar-id", 0xfc); - qdev_prop_set_uint32(dev, "equipment", 0xc0); - isa_realize_and_unref(isa_dev, isa_bus, &error_fatal); - - dev = DEVICE(pci_create_simple(pci_bus, PCI_DEVFN(1, 0), - "lsi53c810")); + dev = DEVICE(pci_create_simple(pci_bus, PCI_DEVFN(1, 0), "lsi53c810")); lsi53c8xx_handle_legacy_cmdline(dev); qdev_connect_gpio_out(dev, 0, qdev_get_gpio_in(i82378_dev, 13)); From be2f66e07aab5998f55d5e6e4de0a69a069759ed Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 19 Oct 2025 23:02:54 +0200 Subject: [PATCH 1742/1794] hw/timer/i8254: Add I/O trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allows to see how the guest interacts with the device. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251019210303.104718-2-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/timer/i8254.c | 6 ++++++ hw/timer/trace-events | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/hw/timer/i8254.c b/hw/timer/i8254.c index 4b25c487f7971..7033ebf50da40 100644 --- a/hw/timer/i8254.c +++ b/hw/timer/i8254.c @@ -29,6 +29,7 @@ #include "hw/timer/i8254.h" #include "hw/timer/i8254_internal.h" #include "qom/object.h" +#include "trace.h" //#define DEBUG_PIT @@ -130,6 +131,8 @@ static void pit_ioport_write(void *opaque, hwaddr addr, int channel, access; PITChannelState *s; + trace_pit_ioport_write(addr, val); + addr &= 3; if (addr == 3) { channel = val >> 6; @@ -248,6 +251,9 @@ static uint64_t pit_ioport_read(void *opaque, hwaddr addr, break; } } + + trace_pit_ioport_read(addr, ret); + return ret; } diff --git a/hw/timer/trace-events b/hw/timer/trace-events index c5b6db49f5879..2bb51f95ea821 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -49,6 +49,10 @@ cmsdk_apb_dualtimer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK A cmsdk_apb_dualtimer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_dualtimer_reset(void) "CMSDK APB dualtimer: reset" +# i8254.c +pit_ioport_read(uint8_t addr, uint32_t value) "[0x%" PRIx8 "] -> 0x%" PRIx32 +pit_ioport_write(uint8_t addr, uint32_t value) "[0x%" PRIx8 "] <- 0x%" PRIx32 + # imx_gpt.c imx_gpt_set_freq(uint32_t clksrc, uint32_t freq) "Setting clksrc %u to %u Hz" imx_gpt_read(const char *name, uint64_t value) "%s -> 0x%08" PRIx64 From 6b670b1b9616ac113392eb438642cbf6e854e3eb Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 19 Oct 2025 23:02:55 +0200 Subject: [PATCH 1743/1794] hw/audio/pcspk: Add I/O trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allows to see how the guest interacts with the device. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251019210303.104718-3-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/audio/pcspk.c | 10 +++++++++- hw/audio/trace-events | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index a419161b5b17e..f8020593b0b3c 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -34,6 +34,7 @@ #include "hw/audio/pcspk.h" #include "qapi/error.h" #include "qom/object.h" +#include "trace.h" #define PCSPK_BUF_LEN 1792 #define PCSPK_SAMPLE_RATE 32000 @@ -137,13 +138,18 @@ static uint64_t pcspk_io_read(void *opaque, hwaddr addr, { PCSpkState *s = opaque; PITChannelInfo ch; + uint8_t val; pit_get_channel_info(s->pit, 2, &ch); s->dummy_refresh_clock ^= (1 << 4); - return ch.gate | (s->data_on << 1) | s->dummy_refresh_clock | + val = ch.gate | (s->data_on << 1) | s->dummy_refresh_clock | (ch.out << 5); + + trace_pcspk_io_read(s->iobase, val); + + return val; } static void pcspk_io_write(void *opaque, hwaddr addr, uint64_t val, @@ -152,6 +158,8 @@ static void pcspk_io_write(void *opaque, hwaddr addr, uint64_t val, PCSpkState *s = opaque; const int gate = val & 1; + trace_pcspk_io_write(s->iobase, val); + s->data_on = (val >> 1) & 1; pit_set_gate(s->pit, 2, gate); if (s->voice) { diff --git a/hw/audio/trace-events b/hw/audio/trace-events index b8ef5727678f6..30f5921545308 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -23,6 +23,10 @@ hda_audio_format(const char *stream, int chan, const char *fmt, int freq) "st %s hda_audio_adjust(const char *stream, int pos) "st %s, pos %d" hda_audio_overrun(const char *stream) "st %s" +# pcspk.c +pcspk_io_read(uint16_t addr, uint8_t val) "[0x%"PRIx16"] -> 0x%"PRIx8 +pcspk_io_write(uint16_t addr, uint8_t val) "[0x%"PRIx16"] <- 0x%"PRIx8 + #via-ac97.c via_ac97_codec_write(uint8_t addr, uint16_t val) "0x%x <- 0x%x" via_ac97_sgd_fetch(uint32_t curr, uint32_t addr, char stop, char eol, char flag, uint32_t len) "curr=0x%x addr=0x%x %c%c%c len=%d" From 4847c5701a33d519067c06054243434e489e337c Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 19 Oct 2025 23:02:56 +0200 Subject: [PATCH 1744/1794] hw/rtc/mc146818rtc: Convert CMOS_DPRINTF() into trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251019210303.104718-4-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/rtc/mc146818rtc.c | 14 +++----------- hw/rtc/trace-events | 4 ++++ 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index f9f5cf396f007..61e9c0bf99f23 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -43,16 +43,10 @@ #include "qapi/error.h" #include "qapi/qapi-events-misc.h" #include "qapi/visitor.h" +#include "trace.h" -//#define DEBUG_CMOS //#define DEBUG_COALESCED -#ifdef DEBUG_CMOS -# define CMOS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) -#else -# define CMOS_DPRINTF(format, ...) do { } while (0) -#endif - #ifdef DEBUG_COALESCED # define DPRINTF_C(format, ...) printf(format, ## __VA_ARGS__) #else @@ -439,8 +433,7 @@ static void cmos_ioport_write(void *opaque, hwaddr addr, if ((addr & 1) == 0) { s->cmos_index = data & 0x7f; } else { - CMOS_DPRINTF("cmos: write index=0x%02x val=0x%02" PRIx64 "\n", - s->cmos_index, data); + trace_mc146818_rtc_ioport_write(s->cmos_index, data); switch(s->cmos_index) { case RTC_SECONDS_ALARM: case RTC_MINUTES_ALARM: @@ -726,8 +719,7 @@ static uint64_t cmos_ioport_read(void *opaque, hwaddr addr, ret = s->cmos_data[s->cmos_index]; break; } - CMOS_DPRINTF("cmos: read index=0x%02x val=0x%02x\n", - s->cmos_index, ret); + trace_mc146818_rtc_ioport_read(s->cmos_index, ret); return ret; } } diff --git a/hw/rtc/trace-events b/hw/rtc/trace-events index b9f2852d35fca..d2f36217cb835 100644 --- a/hw/rtc/trace-events +++ b/hw/rtc/trace-events @@ -32,6 +32,10 @@ m48txx_nvram_io_write(uint64_t addr, uint64_t value) "io write addr:0x%04" PRIx6 m48txx_nvram_mem_read(uint32_t addr, uint32_t value) "mem read addr:0x%04x value:0x%02x" m48txx_nvram_mem_write(uint32_t addr, uint32_t value) "mem write addr:0x%04x value:0x%02x" +# mc146818rtc.c +mc146818_rtc_ioport_read(uint8_t addr, uint8_t value) "[0x%02" PRIx8 "] -> 0x%02" PRIx8 +mc146818_rtc_ioport_write(uint8_t addr, uint8_t value) "[0x%02" PRIx8 "] <- 0x%02" PRIx8 + # goldfish_rtc.c goldfish_rtc_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 goldfish_rtc_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 From 3006fa8d26fe8b8d9b344c9e983513cd59477cb7 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 19 Oct 2025 23:02:57 +0200 Subject: [PATCH 1745/1794] hw/rtc/mc146818rtc: Use ARRAY_SIZE macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoids the error-prone repetition of the array size. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251019210303.104718-5-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/rtc/mc146818rtc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index 61e9c0bf99f23..5a89062b4c598 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -726,13 +726,14 @@ static uint64_t cmos_ioport_read(void *opaque, hwaddr addr, void mc146818rtc_set_cmos_data(MC146818RtcState *s, int addr, int val) { - if (addr >= 0 && addr <= 127) + if (addr >= 0 && addr < ARRAY_SIZE(s->cmos_data)) { s->cmos_data[addr] = val; + } } int mc146818rtc_get_cmos_data(MC146818RtcState *s, int addr) { - assert(addr >= 0 && addr <= 127); + assert(addr >= 0 && addr < ARRAY_SIZE(s->cmos_data)); return s->cmos_data[addr]; } From f3c0c9da662b22d62532c25b7c561dd9ce6b8def Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 19 Oct 2025 23:02:58 +0200 Subject: [PATCH 1746/1794] hw/rtc/mc146818rtc: Assert correct usage of mc146818rtc_set_cmos_data() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The offset is never controlled by the guest, so any misuse constitutes a programming error and shouldn't be silently ignored. Fix this by using assert(). Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251019210303.104718-6-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/rtc/mc146818rtc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index 5a89062b4c598..8631386b9f092 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -726,9 +726,8 @@ static uint64_t cmos_ioport_read(void *opaque, hwaddr addr, void mc146818rtc_set_cmos_data(MC146818RtcState *s, int addr, int val) { - if (addr >= 0 && addr < ARRAY_SIZE(s->cmos_data)) { - s->cmos_data[addr] = val; - } + assert(addr >= 0 && addr < ARRAY_SIZE(s->cmos_data)); + s->cmos_data[addr] = val; } int mc146818rtc_get_cmos_data(MC146818RtcState *s, int addr) From dd012373a591ea52e3b9105cd5f8ba69d2303103 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 19 Oct 2025 23:02:59 +0200 Subject: [PATCH 1747/1794] hw/ide/ide-internal: Move dma_buf_commit() into ide "namespace" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The identifier suggests that it is a generic DMA function while it is tied to IDE. Fix this by adding an "ide_" prefix. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251019210303.104718-7-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/ide/ahci.c | 8 ++++---- hw/ide/core.c | 10 +++++----- hw/ide/ide-internal.h | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 1303c21cb70c5..14bc66fb7fad6 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1417,7 +1417,7 @@ static void ahci_pio_transfer(const IDEDMA *dma) } /* Update number of transferred bytes, destroy sglist */ - dma_buf_commit(s, size); + ide_dma_buf_commit(s, size); out: /* declare that we processed everything */ @@ -1482,8 +1482,8 @@ static int32_t ahci_dma_prepare_buf(const IDEDMA *dma, int32_t limit) /** * Updates the command header with a bytes-read value. - * Called via dma_buf_commit, for both DMA and PIO paths. - * sglist destruction is handled within dma_buf_commit. + * Called via ide_dma_buf_commit, for both DMA and PIO paths. + * sglist destruction is handled within ide_dma_buf_commit. */ static void ahci_commit_buf(const IDEDMA *dma, uint32_t tx_bytes) { @@ -1511,7 +1511,7 @@ static int ahci_dma_rw_buf(const IDEDMA *dma, bool is_write) } /* free sglist, update byte count */ - dma_buf_commit(s, l); + ide_dma_buf_commit(s, l); s->io_buffer_index += l; trace_ahci_dma_rw_buf(ad->hba, ad->port_no, l); diff --git a/hw/ide/core.c b/hw/ide/core.c index b14983ec54faf..8c380abf7c162 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -827,7 +827,7 @@ static void ide_sector_read(IDEState *s) ide_sector_read_cb, s); } -void dma_buf_commit(IDEState *s, uint32_t tx_bytes) +void ide_dma_buf_commit(IDEState *s, uint32_t tx_bytes) { if (s->bus->dma->ops->commit_buf) { s->bus->dma->ops->commit_buf(s->bus->dma, tx_bytes); @@ -848,7 +848,7 @@ void ide_set_inactive(IDEState *s, bool more) void ide_dma_error(IDEState *s) { - dma_buf_commit(s, 0); + ide_dma_buf_commit(s, 0); ide_abort_command(s); ide_set_inactive(s, false); ide_bus_set_irq(s->bus); @@ -893,7 +893,7 @@ static void ide_dma_cb(void *opaque, int ret) if (ret < 0) { if (ide_handle_rw_error(s, -ret, ide_dma_cmd_to_retry(s->dma_cmd))) { s->bus->dma->aiocb = NULL; - dma_buf_commit(s, 0); + ide_dma_buf_commit(s, 0); return; } } @@ -912,7 +912,7 @@ static void ide_dma_cb(void *opaque, int ret) sector_num = ide_get_sector(s); if (n > 0) { assert(n * 512 == s->sg.size); - dma_buf_commit(s, s->sg.size); + ide_dma_buf_commit(s, s->sg.size); sector_num += n; ide_set_sector(s, sector_num); s->nsector -= n; @@ -944,7 +944,7 @@ static void ide_dma_cb(void *opaque, int ret) * Reset the Active bit and don't raise the interrupt. */ s->status = READY_STAT | SEEK_STAT; - dma_buf_commit(s, 0); + ide_dma_buf_commit(s, 0); goto eot; } diff --git a/hw/ide/ide-internal.h b/hw/ide/ide-internal.h index 0d64805da20c0..281d07c9d58f0 100644 --- a/hw/ide/ide-internal.h +++ b/hw/ide/ide-internal.h @@ -398,7 +398,7 @@ int64_t ide_get_sector(IDEState *s); void ide_set_sector(IDEState *s, int64_t sector_num); void ide_start_dma(IDEState *s, BlockCompletionFunc *cb); -void dma_buf_commit(IDEState *s, uint32_t tx_bytes); +void ide_dma_buf_commit(IDEState *s, uint32_t tx_bytes); void ide_dma_error(IDEState *s); void ide_abort_command(IDEState *s); From 2fd15a24ca3b88531d66efb203d1f7f39d8519f5 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 19 Oct 2025 23:03:00 +0200 Subject: [PATCH 1748/1794] hw/i386/apic: Prefer APICCommonState over DeviceState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makes the APIC API more type-safe by resolving quite a few APIC_COMMON downcasts. Like PICCommonState, the APICCommonState is now a public typedef while staying an abstract datatype. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251019210303.104718-8-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/kvm/apic.c | 3 +- hw/i386/vapic.c | 2 +- hw/i386/x86-cpu.c | 2 +- hw/intc/apic.c | 97 +++++++++++++------------------- hw/intc/apic_common.c | 56 +++++++----------- include/hw/i386/apic.h | 33 +++++------ include/hw/i386/apic_internal.h | 7 +-- target/i386/cpu-apic.c | 18 +++--- target/i386/cpu-dump.c | 2 +- target/i386/cpu.c | 2 +- target/i386/cpu.h | 5 +- target/i386/kvm/kvm.c | 2 +- target/i386/kvm/kvm_i386.h | 2 +- target/i386/whpx/whpx-apic.c | 3 +- target/i386/whpx/whpx-internal.h | 4 +- 15 files changed, 98 insertions(+), 140 deletions(-) diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c index 1be9bfe36e911..82355f04631a8 100644 --- a/hw/i386/kvm/apic.c +++ b/hw/i386/kvm/apic.c @@ -60,9 +60,8 @@ static void kvm_put_apic_state(APICCommonState *s, struct kvm_lapic_state *kapic kvm_apic_set_reg(kapic, 0x3e, s->divide_conf); } -void kvm_get_apic_state(DeviceState *dev, struct kvm_lapic_state *kapic) +void kvm_get_apic_state(APICCommonState *s, struct kvm_lapic_state *kapic) { - APICCommonState *s = APIC_COMMON(dev); int i, v; if (kvm_has_x2apic_api() && s->apicbase & MSR_IA32_APICBASE_EXTD) { diff --git a/hw/i386/vapic.c b/hw/i386/vapic.c index 0c1c92c479349..f1089f0a7c826 100644 --- a/hw/i386/vapic.c +++ b/hw/i386/vapic.c @@ -490,7 +490,7 @@ void vapic_report_tpr_access(DeviceState *dev, CPUState *cs, target_ulong ip, } typedef struct VAPICEnableTPRReporting { - DeviceState *apic; + APICCommonState *apic; bool enable; } VAPICEnableTPRReporting; diff --git a/hw/i386/x86-cpu.c b/hw/i386/x86-cpu.c index c876e6709e038..1a86a853d5ffc 100644 --- a/hw/i386/x86-cpu.c +++ b/hw/i386/x86-cpu.c @@ -86,7 +86,7 @@ int cpu_get_pic_interrupt(CPUX86State *env) return intno; } -DeviceState *cpu_get_current_apic(void) +APICCommonState *cpu_get_current_apic(void) { if (current_cpu) { X86CPU *cpu = X86_CPU(current_cpu); diff --git a/hw/intc/apic.c b/hw/intc/apic.c index c7680338563a6..cb35c80c75b79 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -181,10 +181,8 @@ static void apic_local_deliver(APICCommonState *s, int vector) } } -void apic_deliver_pic_intr(DeviceState *dev, int level) +void apic_deliver_pic_intr(APICCommonState *s, int level) { - APICCommonState *s = APIC(dev); - if (level) { apic_local_deliver(s, APIC_LVT_LINT0); } else { @@ -301,10 +299,8 @@ static void apic_deliver_irq(uint32_t dest, uint8_t dest_mode, apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, trigger_mode); } -bool is_x2apic_mode(DeviceState *dev) +bool is_x2apic_mode(APICCommonState *s) { - APICCommonState *s = APIC(dev); - return s->apicbase & MSR_IA32_APICBASE_EXTD; } @@ -388,15 +384,12 @@ static void apic_set_tpr(APICCommonState *s, uint8_t val) } } -int apic_get_highest_priority_irr(DeviceState *dev) +int apic_get_highest_priority_irr(APICCommonState *s) { - APICCommonState *s; - - if (!dev) { + if (!s) { /* no interrupts */ return -1; } - s = APIC_COMMON(dev); return get_highest_priority_int(s->irr); } @@ -458,22 +451,19 @@ static int apic_irq_pending(APICCommonState *s) static void apic_update_irq(APICCommonState *s) { CPUState *cpu; - DeviceState *dev = (DeviceState *)s; cpu = CPU(s->cpu); if (!qemu_cpu_is_self(cpu)) { cpu_interrupt(cpu, CPU_INTERRUPT_POLL); } else if (apic_irq_pending(s) > 0) { cpu_interrupt(cpu, CPU_INTERRUPT_HARD); - } else if (!apic_accept_pic_intr(dev) || !pic_get_output(isa_pic)) { + } else if (!apic_accept_pic_intr(s) || !pic_get_output(isa_pic)) { cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD); } } -void apic_poll_irq(DeviceState *dev) +void apic_poll_irq(APICCommonState *s) { - APICCommonState *s = APIC(dev); - apic_sync_vapic(s, SYNC_FROM_VAPIC); apic_update_irq(s); } @@ -516,7 +506,7 @@ static void apic_eoi(APICCommonState *s) static bool apic_match_dest(APICCommonState *apic, uint32_t dest) { - if (is_x2apic_mode(&apic->parent_obj)) { + if (is_x2apic_mode(apic)) { return apic->initial_apic_id == dest; } else { return apic->id == (uint8_t)dest; @@ -550,7 +540,7 @@ static void apic_get_broadcast_bitmask(uint32_t *deliver_bitmask, for (i = 0; i < max_apics; i++) { apic_iter = local_apics[i]; if (apic_iter) { - bool apic_in_x2apic = is_x2apic_mode(&apic_iter->parent_obj); + bool apic_in_x2apic = is_x2apic_mode(apic_iter); if (is_x2apic_broadcast && apic_in_x2apic) { apic_set_bit(deliver_bitmask, i); @@ -642,27 +632,24 @@ static void apic_startup(APICCommonState *s, int vector_num) cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_SIPI); } -void apic_sipi(DeviceState *dev) +void apic_sipi(APICCommonState *s) { - APICCommonState *s = APIC(dev); - if (!s->wait_for_sipi) return; cpu_x86_load_seg_cache_sipi(s->cpu, s->sipi_vector); s->wait_for_sipi = 0; } -static void apic_deliver(DeviceState *dev, uint32_t dest, uint8_t dest_mode, +static void apic_deliver(APICCommonState *s, uint32_t dest, uint8_t dest_mode, uint8_t delivery_mode, uint8_t vector_num, uint8_t trigger_mode, uint8_t dest_shorthand) { - APICCommonState *s = APIC(dev); APICCommonState *apic_iter; uint32_t deliver_bitmask_size = max_apic_words * sizeof(uint32_t); g_autofree uint32_t *deliver_bitmask = g_new(uint32_t, max_apic_words); uint32_t current_apic_id; - if (is_x2apic_mode(dev)) { + if (is_x2apic_mode(s)) { current_apic_id = s->initial_apic_id; } else { current_apic_id = s->id; @@ -709,18 +696,15 @@ static void apic_deliver(DeviceState *dev, uint32_t dest, uint8_t dest_mode, static bool apic_check_pic(APICCommonState *s) { - DeviceState *dev = (DeviceState *)s; - - if (!apic_accept_pic_intr(dev) || !pic_get_output(isa_pic)) { + if (!apic_accept_pic_intr(s) || !pic_get_output(isa_pic)) { return false; } - apic_deliver_pic_intr(dev, 1); + apic_deliver_pic_intr(s, 1); return true; } -int apic_get_interrupt(DeviceState *dev) +int apic_get_interrupt(APICCommonState *s) { - APICCommonState *s = APIC(dev); int intno; /* if the APIC is installed or enabled, we let the 8259 handle the @@ -752,9 +736,8 @@ int apic_get_interrupt(DeviceState *dev) return intno; } -int apic_accept_pic_intr(DeviceState *dev) +int apic_accept_pic_intr(APICCommonState *s) { - APICCommonState *s = APIC(dev); uint32_t lvt0; if (!s) @@ -788,20 +771,18 @@ static void apic_timer(void *opaque) static int apic_register_read(int index, uint64_t *value) { - DeviceState *dev; APICCommonState *s; uint32_t val; int ret = 0; - dev = cpu_get_current_apic(); - if (!dev) { + s = cpu_get_current_apic(); + if (!s) { return -1; } - s = APIC(dev); switch(index) { case 0x02: /* id */ - if (is_x2apic_mode(dev)) { + if (is_x2apic_mode(s)) { val = s->initial_apic_id; } else { val = s->id << 24; @@ -828,14 +809,14 @@ static int apic_register_read(int index, uint64_t *value) val = 0; break; case 0x0d: - if (is_x2apic_mode(dev)) { + if (is_x2apic_mode(s)) { val = s->extended_log_dest; } else { val = s->log_dest << 24; } break; case 0x0e: - if (is_x2apic_mode(dev)) { + if (is_x2apic_mode(s)) { val = 0; ret = -1; } else { @@ -902,14 +883,14 @@ static uint64_t apic_mem_read(void *opaque, hwaddr addr, unsigned size) int apic_msr_read(int index, uint64_t *val) { - DeviceState *dev; + APICCommonState *s; - dev = cpu_get_current_apic(); - if (!dev) { + s = cpu_get_current_apic(); + if (!s) { return -1; } - if (!is_x2apic_mode(dev)) { + if (!is_x2apic_mode(s)) { return -1; } @@ -943,20 +924,18 @@ static void apic_send_msi(MSIMessage *msi) static int apic_register_write(int index, uint64_t val) { - DeviceState *dev; APICCommonState *s; - dev = cpu_get_current_apic(); - if (!dev) { + s = cpu_get_current_apic(); + if (!s) { return -1; } - s = APIC(dev); trace_apic_register_write(index, val); switch(index) { case 0x02: - if (is_x2apic_mode(dev)) { + if (is_x2apic_mode(s)) { return -1; } @@ -979,14 +958,14 @@ static int apic_register_write(int index, uint64_t val) apic_eoi(s); break; case 0x0d: - if (is_x2apic_mode(dev)) { + if (is_x2apic_mode(s)) { return -1; } s->log_dest = val >> 24; break; case 0x0e: - if (is_x2apic_mode(dev)) { + if (is_x2apic_mode(s)) { return -1; } @@ -1005,20 +984,20 @@ static int apic_register_write(int index, uint64_t val) uint32_t dest; s->icr[0] = val; - if (is_x2apic_mode(dev)) { + if (is_x2apic_mode(s)) { s->icr[1] = val >> 32; dest = s->icr[1]; } else { dest = (s->icr[1] >> 24) & 0xff; } - apic_deliver(dev, dest, (s->icr[0] >> 11) & 1, + apic_deliver(s, dest, (s->icr[0] >> 11) & 1, (s->icr[0] >> 8) & 7, (s->icr[0] & 0xff), (s->icr[0] >> 15) & 1, (s->icr[0] >> 18) & 3); break; } case 0x31: - if (is_x2apic_mode(dev)) { + if (is_x2apic_mode(s)) { return -1; } @@ -1053,7 +1032,7 @@ static int apic_register_write(int index, uint64_t val) case 0x3f: { int vector = val & 0xff; - if (!is_x2apic_mode(dev)) { + if (!is_x2apic_mode(s)) { return -1; } @@ -1063,7 +1042,7 @@ static int apic_register_write(int index, uint64_t val) * - Trigger mode: 0 (Edge) * - Delivery mode: 0 (Fixed) */ - apic_deliver(dev, 0, 0, APIC_DM_FIXED, vector, 0, 1); + apic_deliver(s, 0, 0, APIC_DM_FIXED, vector, 0, 1); break; } @@ -1102,14 +1081,14 @@ static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val, int apic_msr_write(int index, uint64_t val) { - DeviceState *dev; + APICCommonState *s; - dev = cpu_get_current_apic(); - if (!dev) { + s = cpu_get_current_apic(); + if (!s) { return -1; } - if (!is_x2apic_mode(dev)) { + if (!is_x2apic_mode(s)) { return -1; } diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 394fe020134f4..ec9e978b0b40f 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -35,12 +35,11 @@ bool apic_report_tpr_access; -int cpu_set_apic_base(DeviceState *dev, uint64_t val) +int cpu_set_apic_base(APICCommonState *s, uint64_t val) { trace_cpu_set_apic_base(val); - if (dev) { - APICCommonState *s = APIC_COMMON(dev); + if (s) { APICCommonClass *info = APIC_COMMON_GET_CLASS(s); /* Reset possibly modified xAPIC ID */ s->id = s->initial_apic_id; @@ -50,10 +49,9 @@ int cpu_set_apic_base(DeviceState *dev, uint64_t val) return 0; } -uint64_t cpu_get_apic_base(DeviceState *dev) +uint64_t cpu_get_apic_base(APICCommonState *s) { - if (dev) { - APICCommonState *s = APIC_COMMON(dev); + if (s) { trace_cpu_get_apic_base((uint64_t)s->apicbase); return s->apicbase; } else { @@ -62,52 +60,43 @@ uint64_t cpu_get_apic_base(DeviceState *dev) } } -bool cpu_is_apic_enabled(DeviceState *dev) +bool cpu_is_apic_enabled(APICCommonState *s) { - APICCommonState *s; - - if (!dev) { + if (!s) { return false; } - s = APIC_COMMON(dev); - return s->apicbase & MSR_IA32_APICBASE_ENABLE; } -void cpu_set_apic_tpr(DeviceState *dev, uint8_t val) +void cpu_set_apic_tpr(APICCommonState *s, uint8_t val) { - APICCommonState *s; APICCommonClass *info; - if (!dev) { + if (!s) { return; } - s = APIC_COMMON(dev); info = APIC_COMMON_GET_CLASS(s); info->set_tpr(s, val); } -uint8_t cpu_get_apic_tpr(DeviceState *dev) +uint8_t cpu_get_apic_tpr(APICCommonState *s) { - APICCommonState *s; APICCommonClass *info; - if (!dev) { + if (!s) { return 0; } - s = APIC_COMMON(dev); info = APIC_COMMON_GET_CLASS(s); return info->get_tpr(s); } -void apic_enable_tpr_access_reporting(DeviceState *dev, bool enable) +void apic_enable_tpr_access_reporting(APICCommonState *s, bool enable) { - APICCommonState *s = APIC_COMMON(dev); APICCommonClass *info = APIC_COMMON_GET_CLASS(s); apic_report_tpr_access = enable; @@ -116,26 +105,22 @@ void apic_enable_tpr_access_reporting(DeviceState *dev, bool enable) } } -void apic_enable_vapic(DeviceState *dev, hwaddr paddr) +void apic_enable_vapic(APICCommonState *s, hwaddr paddr) { - APICCommonState *s = APIC_COMMON(dev); APICCommonClass *info = APIC_COMMON_GET_CLASS(s); s->vapic_paddr = paddr; info->vapic_base_update(s); } -void apic_handle_tpr_access_report(DeviceState *dev, target_ulong ip, +void apic_handle_tpr_access_report(APICCommonState *s, target_ulong ip, TPRAccess access) { - APICCommonState *s = APIC_COMMON(dev); - vapic_report_tpr_access(s->vapic, CPU(s->cpu), ip, access); } -void apic_deliver_nmi(DeviceState *dev) +void apic_deliver_nmi(APICCommonState *s) { - APICCommonState *s = APIC_COMMON(dev); APICCommonClass *info = APIC_COMMON_GET_CLASS(s); info->external_nmi(s); @@ -193,16 +178,14 @@ uint32_t apic_get_current_count(APICCommonState *s) return val; } -void apic_init_reset(DeviceState *dev) +void apic_init_reset(APICCommonState *s) { - APICCommonState *s; APICCommonClass *info; int i; - if (!dev) { + if (!s) { return; } - s = APIC_COMMON(dev); s->tpr = 0; s->spurious_vec = 0xff; s->log_dest = 0; @@ -233,13 +216,12 @@ void apic_init_reset(DeviceState *dev) } } -void apic_designate_bsp(DeviceState *dev, bool bsp) +void apic_designate_bsp(APICCommonState *s, bool bsp) { - if (dev == NULL) { + if (s == NULL) { return; } - APICCommonState *s = APIC_COMMON(dev); if (bsp) { s->apicbase |= MSR_IA32_APICBASE_BSP; } else { @@ -262,7 +244,7 @@ static void apic_reset_common(DeviceState *dev) s->vapic_paddr = 0; info->vapic_base_update(s); - apic_init_reset(dev); + apic_init_reset(s); } static const VMStateDescription vmstate_apic_common; diff --git a/include/hw/i386/apic.h b/include/hw/i386/apic.h index eb606d60760f4..871f14288854f 100644 --- a/include/hw/i386/apic.h +++ b/include/hw/i386/apic.h @@ -1,28 +1,29 @@ #ifndef APIC_H #define APIC_H +typedef struct APICCommonState APICCommonState; /* apic.c */ void apic_set_max_apic_id(uint32_t max_apic_id); -int apic_accept_pic_intr(DeviceState *s); -void apic_deliver_pic_intr(DeviceState *s, int level); -void apic_deliver_nmi(DeviceState *d); -int apic_get_interrupt(DeviceState *s); -int cpu_set_apic_base(DeviceState *s, uint64_t val); -uint64_t cpu_get_apic_base(DeviceState *s); -bool cpu_is_apic_enabled(DeviceState *s); -void cpu_set_apic_tpr(DeviceState *s, uint8_t val); -uint8_t cpu_get_apic_tpr(DeviceState *s); -void apic_init_reset(DeviceState *s); -void apic_sipi(DeviceState *s); -void apic_poll_irq(DeviceState *d); -void apic_designate_bsp(DeviceState *d, bool bsp); -int apic_get_highest_priority_irr(DeviceState *dev); +int apic_accept_pic_intr(APICCommonState *s); +void apic_deliver_pic_intr(APICCommonState *s, int level); +void apic_deliver_nmi(APICCommonState *s); +int apic_get_interrupt(APICCommonState *s); +int cpu_set_apic_base(APICCommonState *s, uint64_t val); +uint64_t cpu_get_apic_base(APICCommonState *s); +bool cpu_is_apic_enabled(APICCommonState *s); +void cpu_set_apic_tpr(APICCommonState *s, uint8_t val); +uint8_t cpu_get_apic_tpr(APICCommonState *s); +void apic_init_reset(APICCommonState *s); +void apic_sipi(APICCommonState *s); +void apic_poll_irq(APICCommonState *s); +void apic_designate_bsp(APICCommonState *s, bool bsp); +int apic_get_highest_priority_irr(APICCommonState *s); int apic_msr_read(int index, uint64_t *val); int apic_msr_write(int index, uint64_t val); -bool is_x2apic_mode(DeviceState *d); +bool is_x2apic_mode(APICCommonState *s); /* pc.c */ -DeviceState *cpu_get_current_apic(void); +APICCommonState *cpu_get_current_apic(void); #endif diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h index 429278da61876..4a62fdceb4ea1 100644 --- a/include/hw/i386/apic_internal.h +++ b/include/hw/i386/apic_internal.h @@ -22,6 +22,7 @@ #define QEMU_APIC_INTERNAL_H #include "cpu.h" +#include "hw/i386/apic.h" #include "system/memory.h" #include "qemu/timer.h" #include "target/i386/cpu-qom.h" @@ -125,8 +126,6 @@ #define VAPIC_ENABLE_BIT 0 #define VAPIC_ENABLE_MASK (1 << VAPIC_ENABLE_BIT) -typedef struct APICCommonState APICCommonState; - #define TYPE_APIC_COMMON "apic-common" typedef struct APICCommonClass APICCommonClass; DECLARE_OBJ_CHECKERS(APICCommonState, APICCommonClass, @@ -203,8 +202,8 @@ typedef struct VAPICState { extern bool apic_report_tpr_access; bool apic_next_timer(APICCommonState *s, int64_t current_time); -void apic_enable_tpr_access_reporting(DeviceState *d, bool enable); -void apic_enable_vapic(DeviceState *d, hwaddr paddr); +void apic_enable_tpr_access_reporting(APICCommonState *s, bool enable); +void apic_enable_vapic(APICCommonState *s, hwaddr paddr); void vapic_report_tpr_access(DeviceState *dev, CPUState *cpu, target_ulong ip, TPRAccess access); diff --git a/target/i386/cpu-apic.c b/target/i386/cpu-apic.c index 242a05fdbe9c1..564c1288e47d0 100644 --- a/target/i386/cpu-apic.c +++ b/target/i386/cpu-apic.c @@ -41,34 +41,31 @@ APICCommonClass *apic_get_class(Error **errp) void x86_cpu_apic_create(X86CPU *cpu, Error **errp) { - APICCommonState *apic; APICCommonClass *apic_class = apic_get_class(errp); if (!apic_class) { return; } - cpu->apic_state = DEVICE(object_new_with_class(OBJECT_CLASS(apic_class))); + cpu->apic_state = APIC_COMMON(object_new_with_class(OBJECT_CLASS(apic_class))); object_property_add_child(OBJECT(cpu), "lapic", OBJECT(cpu->apic_state)); object_unref(OBJECT(cpu->apic_state)); /* TODO: convert to link<> */ - apic = APIC_COMMON(cpu->apic_state); - apic->cpu = cpu; - apic->apicbase = APIC_DEFAULT_ADDRESS | MSR_IA32_APICBASE_ENABLE; + cpu->apic_state->cpu = cpu; + cpu->apic_state->apicbase = APIC_DEFAULT_ADDRESS | MSR_IA32_APICBASE_ENABLE; /* * apic_common_set_id needs to check if the CPU has x2APIC - * feature in case APIC ID >= 255, so we need to set apic->cpu + * feature in case APIC ID >= 255, so we need to set cpu->apic_state->cpu * before setting APIC ID */ - qdev_prop_set_uint32(cpu->apic_state, "id", cpu->apic_id); + qdev_prop_set_uint32(DEVICE(cpu->apic_state), "id", cpu->apic_id); } void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) { - APICCommonState *apic; static bool apic_mmio_map_once; if (cpu->apic_state == NULL) { @@ -77,12 +74,11 @@ void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) qdev_realize(DEVICE(cpu->apic_state), NULL, errp); /* Map APIC MMIO area */ - apic = APIC_COMMON(cpu->apic_state); if (!apic_mmio_map_once) { memory_region_add_subregion_overlap(get_system_memory(), - apic->apicbase & + cpu->apic_state->apicbase & MSR_IA32_APICBASE_BASE, - &apic->io_memory, + &cpu->apic_state->io_memory, 0x1000); apic_mmio_map_once = true; } diff --git a/target/i386/cpu-dump.c b/target/i386/cpu-dump.c index a72ed93bd2f34..67bf31e0caafb 100644 --- a/target/i386/cpu-dump.c +++ b/target/i386/cpu-dump.c @@ -291,7 +291,7 @@ static void dump_apic_interrupt(const char *name, uint32_t *ireg_tab, void x86_cpu_dump_local_apic_state(CPUState *cs, int flags) { X86CPU *cpu = X86_CPU(cs); - APICCommonState *s = APIC_COMMON(cpu->apic_state); + APICCommonState *s = cpu->apic_state; if (!s) { qemu_printf("local apic state not available\n"); return; diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 455caff6b2305..0a66e1fec939e 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8789,7 +8789,7 @@ void x86_cpu_after_reset(X86CPU *cpu) } if (cpu->apic_state) { - device_cold_reset(cpu->apic_state); + device_cold_reset(DEVICE(cpu->apic_state)); } #endif } diff --git a/target/i386/cpu.h b/target/i386/cpu.h index ce948861a7659..d0da9bfe58ce1 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -27,6 +27,7 @@ #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "exec/memop.h" +#include "hw/i386/apic.h" #include "hw/i386/topology.h" #include "qapi/qapi-types-common.h" #include "qemu/cpu-float.h" @@ -2349,7 +2350,7 @@ struct ArchCPU { /* in order to simplify APIC support, we leave this pointer to the user */ - struct DeviceState *apic_state; + APICCommonState *apic_state; struct MemoryRegion *cpu_as_root, *cpu_as_mem, *smram; Notifier machine_done; @@ -2830,7 +2831,7 @@ bool cpu_svm_has_intercept(CPUX86State *env, uint32_t type); /* apic.c */ void cpu_report_tpr_access(CPUX86State *env, TPRAccess access); -void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip, +void apic_handle_tpr_access_report(APICCommonState *s, target_ulong ip, TPRAccess access); /* Special values for X86CPUVersion: */ diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 309f043373cd6..f7a6ef650af73 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5029,7 +5029,7 @@ static int kvm_get_mp_state(X86CPU *cpu) static int kvm_get_apic(X86CPU *cpu) { - DeviceState *apic = cpu->apic_state; + APICCommonState *apic = cpu->apic_state; struct kvm_lapic_state kapic; int ret; diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index 5f83e8850a2b7..5c908fdd6a584 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -56,7 +56,7 @@ bool kvm_has_adjust_clock_stable(void); bool kvm_has_exception_payload(void); void kvm_synchronize_all_tsc(void); -void kvm_get_apic_state(DeviceState *d, struct kvm_lapic_state *kapic); +void kvm_get_apic_state(APICCommonState *s, struct kvm_lapic_state *kapic); void kvm_put_apicbase(X86CPU *cpu, uint64_t value); bool kvm_has_x2apic_api(void); diff --git a/target/i386/whpx/whpx-apic.c b/target/i386/whpx/whpx-apic.c index e1ef6d4e6d4df..afcb25843b515 100644 --- a/target/i386/whpx/whpx-apic.c +++ b/target/i386/whpx/whpx-apic.c @@ -151,9 +151,8 @@ static void whpx_apic_put(CPUState *cs, run_on_cpu_data data) } } -void whpx_apic_get(DeviceState *dev) +void whpx_apic_get(APICCommonState *s) { - APICCommonState *s = APIC_COMMON(dev); CPUState *cpu = CPU(s->cpu); struct whpx_lapic_state kapic; diff --git a/target/i386/whpx/whpx-internal.h b/target/i386/whpx/whpx-internal.h index 6633e9c4ca31f..2dcad1f56502c 100644 --- a/target/i386/whpx/whpx-internal.h +++ b/target/i386/whpx/whpx-internal.h @@ -5,6 +5,8 @@ #include #include +#include "hw/i386/apic.h" + typedef enum WhpxBreakpointState { WHPX_BP_CLEARED = 0, WHPX_BP_SET_PENDING, @@ -44,7 +46,7 @@ struct whpx_state { }; extern struct whpx_state whpx_global; -void whpx_apic_get(DeviceState *s); +void whpx_apic_get(APICCommonState *s); #define WHV_E_UNKNOWN_CAPABILITY 0x80370300L From 337eece9c0a4b84cd2e34ed94b55efbad9f37a6b Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 19 Oct 2025 23:03:01 +0200 Subject: [PATCH 1749/1794] hw/i386/apic: Ensure own APIC use in apic_msr_{read,write} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoids the `current_cpu` global and seems more robust by not "forgetting" the own APIC and then re-determining it by cpu_get_current_apic() which uses the global. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251019210303.104718-9-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/apic.c | 10 ++-------- include/hw/i386/apic.h | 4 ++-- target/i386/hvf/hvf.c | 4 ++-- target/i386/tcg/system/misc_helper.c | 4 ++-- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/hw/intc/apic.c b/hw/intc/apic.c index cb35c80c75b79..ba0eda39217d1 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -881,11 +881,8 @@ static uint64_t apic_mem_read(void *opaque, hwaddr addr, unsigned size) return val; } -int apic_msr_read(int index, uint64_t *val) +int apic_msr_read(APICCommonState *s, int index, uint64_t *val) { - APICCommonState *s; - - s = cpu_get_current_apic(); if (!s) { return -1; } @@ -1079,11 +1076,8 @@ static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val, apic_register_write(index, val); } -int apic_msr_write(int index, uint64_t val) +int apic_msr_write(APICCommonState *s, int index, uint64_t val) { - APICCommonState *s; - - s = cpu_get_current_apic(); if (!s) { return -1; } diff --git a/include/hw/i386/apic.h b/include/hw/i386/apic.h index 871f14288854f..6a0933f401cb7 100644 --- a/include/hw/i386/apic.h +++ b/include/hw/i386/apic.h @@ -19,8 +19,8 @@ void apic_sipi(APICCommonState *s); void apic_poll_irq(APICCommonState *s); void apic_designate_bsp(APICCommonState *s, bool bsp); int apic_get_highest_priority_irr(APICCommonState *s); -int apic_msr_read(int index, uint64_t *val); -int apic_msr_write(int index, uint64_t val); +int apic_msr_read(APICCommonState *s, int index, uint64_t *val); +int apic_msr_write(APICCommonState *s, int index, uint64_t val); bool is_x2apic_mode(APICCommonState *s); /* pc.c */ diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 8445cadecec3c..33f723a76a771 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -527,7 +527,7 @@ void hvf_simulate_rdmsr(CPUState *cs) int ret; int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; - ret = apic_msr_read(index, &val); + ret = apic_msr_read(cpu->apic_state, index, &val); if (ret < 0) { x86_emul_raise_exception(env, EXCP0D_GPF, 0); } @@ -638,7 +638,7 @@ void hvf_simulate_wrmsr(CPUState *cs) int ret; int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; - ret = apic_msr_write(index, data); + ret = apic_msr_write(cpu->apic_state, index, data); if (ret < 0) { x86_emul_raise_exception(env, EXCP0D_GPF, 0); } diff --git a/target/i386/tcg/system/misc_helper.c b/target/i386/tcg/system/misc_helper.c index 9c3f5cc99b356..0c32424d36ada 100644 --- a/target/i386/tcg/system/misc_helper.c +++ b/target/i386/tcg/system/misc_helper.c @@ -299,7 +299,7 @@ void helper_wrmsr(CPUX86State *env) int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; bql_lock(); - ret = apic_msr_write(index, val); + ret = apic_msr_write(env_archcpu(env)->apic_state, index, val); bql_unlock(); if (ret < 0) { goto error; @@ -477,7 +477,7 @@ void helper_rdmsr(CPUX86State *env) int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; bql_lock(); - ret = apic_msr_read(index, &val); + ret = apic_msr_read(x86_cpu->apic_state, index, &val); bql_unlock(); if (ret < 0) { raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); From e2bb6f999f44f7b5835df4a52d83fc2d7b349a14 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 19 Oct 2025 23:03:02 +0200 Subject: [PATCH 1750/1794] hw/intc/apic: Pass APICCommonState to apic_register_{read,write} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As per the previous patch, the APIC instance is already available in apic_msr_{read,write}, so it can be passed along. It turns out that the call to cpu_get_current_apic() is only required in apic_mem_{read,write}, so it has been moved there. Longer term, cpu_get_current_apic() could be removed entirely if apic_mem_{read,write} is tied to a CPU's local address space. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251019210303.104718-10-shentey@gmail.com> [PMD: Move return after apic_send_msi() in apic_mem_write()] Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/apic.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/hw/intc/apic.c b/hw/intc/apic.c index ba0eda39217d1..aad253af15822 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -769,17 +769,11 @@ static void apic_timer(void *opaque) apic_timer_update(s, s->next_time); } -static int apic_register_read(int index, uint64_t *value) +static int apic_register_read(APICCommonState *s, int index, uint64_t *value) { - APICCommonState *s; uint32_t val; int ret = 0; - s = cpu_get_current_apic(); - if (!s) { - return -1; - } - switch(index) { case 0x02: /* id */ if (is_x2apic_mode(s)) { @@ -868,6 +862,7 @@ static int apic_register_read(int index, uint64_t *value) static uint64_t apic_mem_read(void *opaque, hwaddr addr, unsigned size) { + APICCommonState *s = cpu_get_current_apic(); uint64_t val; int index; @@ -875,8 +870,12 @@ static uint64_t apic_mem_read(void *opaque, hwaddr addr, unsigned size) return 0; } + if (!s) { + return -1; + } + index = (addr >> 4) & 0xff; - apic_register_read(index, &val); + apic_register_read(s, index, &val); return val; } @@ -891,7 +890,7 @@ int apic_msr_read(APICCommonState *s, int index, uint64_t *val) return -1; } - return apic_register_read(index, val); + return apic_register_read(s, index, val); } static void apic_send_msi(MSIMessage *msi) @@ -919,15 +918,8 @@ static void apic_send_msi(MSIMessage *msi) apic_deliver_irq(dest, dest_mode, delivery, vector, trigger_mode); } -static int apic_register_write(int index, uint64_t val) +static int apic_register_write(APICCommonState *s, int index, uint64_t val) { - APICCommonState *s; - - s = cpu_get_current_apic(); - if (!s) { - return -1; - } - trace_apic_register_write(index, val); switch(index) { @@ -1054,6 +1046,7 @@ static int apic_register_write(int index, uint64_t val) static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { + APICCommonState *s = cpu_get_current_apic(); int index = (addr >> 4) & 0xff; if (size < 4) { @@ -1073,7 +1066,11 @@ static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val, return; } - apic_register_write(index, val); + if (!s) { + return; + } + + apic_register_write(s, index, val); } int apic_msr_write(APICCommonState *s, int index, uint64_t val) @@ -1086,7 +1083,7 @@ int apic_msr_write(APICCommonState *s, int index, uint64_t val) return -1; } - return apic_register_write(index, val); + return apic_register_write(s, index, val); } static void apic_pre_save(APICCommonState *s) From 5a78db7f8099274550bdacdc1fc24943567ac615 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Sun, 28 Sep 2025 04:54:27 -0400 Subject: [PATCH 1751/1794] vfio/container: Remap only populated parts in a section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If there are multiple containers and unmap-all fails for some of them, we need to remap vaddr for the other containers for which unmap-all succeeded. When ram discard is enabled, we should only remap populated parts in a section instead of the whole section. Fixes: eba1f657cbb1 ("vfio/container: recover from unmap-all-vaddr failure") Signed-off-by: Zhenzhong Duan Reviewed-by: Steven Sistare Reviewed-by: David Hildenbrand Link: https://lore.kernel.org/qemu-devel/20250928085432.40107-2-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr-legacy.c | 20 +++++++++++++++----- hw/vfio/listener.c | 4 ++-- include/hw/vfio/vfio-cpr.h | 2 +- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index 80af7469d06b2..b4581e8f85932 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -228,22 +228,32 @@ void vfio_cpr_giommu_remap(VFIOContainer *bcontainer, memory_region_iommu_replay(giommu->iommu_mr, &giommu->n); } +static int vfio_cpr_rdm_remap(MemoryRegionSection *section, void *opaque) +{ + RamDiscardListener *rdl = opaque; + + return rdl->notify_populate(rdl, section); +} + /* * In old QEMU, VFIO_DMA_UNMAP_FLAG_VADDR may fail on some mapping after * succeeding for others, so the latter have lost their vaddr. Call this - * to restore vaddr for a section with a RamDiscardManager. + * to restore vaddr for populated parts in a section with a RamDiscardManager. * - * The ram discard listener already exists. Call its populate function + * The ram discard listener already exists. Call its replay_populated function * directly, which calls vfio_legacy_cpr_dma_map. */ -bool vfio_cpr_ram_discard_register_listener(VFIOContainer *bcontainer, - MemoryRegionSection *section) +bool vfio_cpr_ram_discard_replay_populated(VFIOContainer *bcontainer, + MemoryRegionSection *section) { + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); VFIORamDiscardListener *vrdl = vfio_find_ram_discard_listener(bcontainer, section); g_assert(vrdl); - return vrdl->listener.notify_populate(&vrdl->listener, section) == 0; + return ram_discard_manager_replay_populated(rdm, section, + vfio_cpr_rdm_remap, + &vrdl->listener) == 0; } int vfio_cpr_group_get_device_fd(int d, const char *name) diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index c6bb58f5209a4..1b6e5065a3267 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -577,8 +577,8 @@ void vfio_container_region_add(VFIOContainer *bcontainer, if (!vfio_ram_discard_register_listener(bcontainer, section, &err)) { goto fail; } - } else if (!vfio_cpr_ram_discard_register_listener(bcontainer, - section)) { + } else if (!vfio_cpr_ram_discard_replay_populated(bcontainer, + section)) { error_setg(&err, "vfio_cpr_ram_discard_register_listener for %s failed", memory_region_name(section->mr)); diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index 81f4e24e229ef..4606da500a796 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -68,7 +68,7 @@ bool vfio_cpr_container_match(struct VFIOLegacyContainer *container, void vfio_cpr_giommu_remap(struct VFIOContainer *bcontainer, MemoryRegionSection *section); -bool vfio_cpr_ram_discard_register_listener( +bool vfio_cpr_ram_discard_replay_populated( struct VFIOContainer *bcontainer, MemoryRegionSection *section); void vfio_cpr_save_vector_fd(struct VFIOPCIDevice *vdev, const char *name, From 94230948960e56cb47e835266c7cd8df46da03a4 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Sun, 28 Sep 2025 04:54:28 -0400 Subject: [PATCH 1752/1794] vfio/cpr-legacy: drop an erroneous assert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_legacy_cpr_dma_map() is not only used in post_load on destination but also error recovery path on source side. Assert it for destination is wrong. Fixes: 7e9f21411302 ("vfio/container: restore DMA vaddr") Signed-off-by: Zhenzhong Duan Reviewed-by: Steve Sistare Link: https://lore.kernel.org/qemu-devel/20250928085432.40107-3-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr-legacy.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index b4581e8f85932..7184c9399128e 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -52,8 +52,6 @@ static int vfio_legacy_cpr_dma_map(const VFIOContainer *bcontainer, .size = size, }; - g_assert(cpr_is_incoming()); - if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map)) { return -errno; } From d59db04aed750ba4fc56f79cae99814334ec8285 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Sun, 28 Sep 2025 04:54:29 -0400 Subject: [PATCH 1753/1794] vfio/iommufd: Set cpr.ioas_id on source side for CPR transfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On source side, if there are more than one VFIO devices and they attach to same container, only the first device sets cpr.ioas_id, the others are bypassed. We should set it for each device, or else only first device works. Fixes: 4296ee07455e ("vfio/iommufd: reconstruct device") Signed-off-by: Zhenzhong Duan Reviewed-by: Steve Sistare Link: https://lore.kernel.org/qemu-devel/20250928085432.40107-4-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 68470d552eccc..63a16d4dc1795 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -602,7 +602,6 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, container->be = vbasedev->iommufd; container->ioas_id = ioas_id; QLIST_INIT(&container->hwpt_list); - vbasedev->cpr.ioas_id = ioas_id; bcontainer = VFIO_IOMMU(container); vfio_address_space_insert(space, bcontainer); @@ -636,6 +635,8 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, bcontainer->initialized = true; found_container: + vbasedev->cpr.ioas_id = container->ioas_id; + ret = ioctl(devfd, VFIO_DEVICE_GET_INFO, &dev_info); if (ret) { error_setg_errno(errp, errno, "error getting device info"); From 8bf49fff0dfbb065ad65daa48d2e1a63ad2fd552 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Sun, 28 Sep 2025 04:54:30 -0400 Subject: [PATCH 1754/1794] vfio/iommufd: Restore vbasedev's reference to hwpt after CPR transfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After CPR transfer, if there are more than one VFIO devices, device is not added to hwpt->device_list and its reference to hwpt isn't restored on destination. We still need to call iommufd_cdev_attach_container() to restore it after a matching container is found, or else SIGSEV triggers. Fixes: 4296ee07455e ("vfio/iommufd: reconstruct device") Signed-off-by: Zhenzhong Duan Reviewed-by: Steve Sistare Link: https://lore.kernel.org/qemu-devel/20250928085432.40107-5-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 63a16d4dc1795..24a99efa87f4d 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -560,10 +560,9 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, continue; } - if (!cpr_is_incoming()) { + if (!cpr_is_incoming() || + (vbasedev->cpr.ioas_id == container->ioas_id)) { res = iommufd_cdev_attach_container(vbasedev, container, &err); - } else if (vbasedev->cpr.ioas_id == container->ioas_id) { - res = true; } else { continue; } From 725ec898039dfed0a669bf78b556529b51e248e8 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Sun, 28 Sep 2025 04:54:31 -0400 Subject: [PATCH 1755/1794] accel/kvm: Fix an erroneous check on coalesced_mmio_ring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to KVM uAPI, coalesced mmio page is KVM_COALESCED_MMIO_PAGE_OFFSET offset from kvm_run pages. For x86 it's 2 pages offset, for arm it's 1 page offset currently. We shouldn't presume it's hardcoded 1 page or else coalesced_mmio_ring will not be cleared in do_kvm_destroy_vcpu() in x86. Fixes: 7ed0919119b0 ("migration: close kvm after cpr") Cc: Markus Armbruster Signed-off-by: Zhenzhong Duan Reviewed-by: Steve Sistare Link: https://lore.kernel.org/qemu-devel/20250928085432.40107-6-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- accel/kvm/kvm-all.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 56031925c4e9e..f9254ae654667 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -525,7 +525,8 @@ static int do_kvm_destroy_vcpu(CPUState *cpu) } /* If I am the CPU that created coalesced_mmio_ring, then discard it */ - if (s->coalesced_mmio_ring == (void *)cpu->kvm_run + PAGE_SIZE) { + if (s->coalesced_mmio_ring == + (void *)cpu->kvm_run + s->coalesced_mmio * PAGE_SIZE) { s->coalesced_mmio_ring = NULL; } From 962bcf0911e7f3601da0f07ba7da9824cb6a5ba5 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 9 Oct 2025 00:01:32 -0400 Subject: [PATCH 1756/1794] vfio/container: Support unmap all in one ioctl() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VFIO type1 kernel uAPI supports unmapping whole address space in one call since commit c19650995374 ("vfio/type1: implement unmap all"). Use the unmap_all variant whenever it's supported in kernel. Opportunistically pass VFIOLegacyContainer pointer in low level function vfio_legacy_dma_unmap_one(). Co-developed-by: John Levon Signed-off-by: John Levon Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251009040134.334251-2-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/container-legacy.c | 38 ++++++++++++++----------- include/hw/vfio/vfio-container-legacy.h | 1 + 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/hw/vfio/container-legacy.c b/hw/vfio/container-legacy.c index a3615d7b5dba8..8e9639603e08b 100644 --- a/hw/vfio/container-legacy.c +++ b/hw/vfio/container-legacy.c @@ -121,14 +121,14 @@ static int vfio_dma_unmap_bitmap(const VFIOLegacyContainer *container, return ret; } -static int vfio_legacy_dma_unmap_one(const VFIOContainer *bcontainer, +static int vfio_legacy_dma_unmap_one(const VFIOLegacyContainer *container, hwaddr iova, uint64_t size, - IOMMUTLBEntry *iotlb) + uint32_t flags, IOMMUTLBEntry *iotlb) { - const VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); + const VFIOContainer *bcontainer = VFIO_IOMMU(container); struct vfio_iommu_type1_dma_unmap unmap = { .argsz = sizeof(unmap), - .flags = 0, + .flags = flags, .iova = iova, .size = size, }; @@ -170,25 +170,28 @@ static int vfio_legacy_dma_unmap(const VFIOContainer *bcontainer, hwaddr iova, uint64_t size, IOMMUTLBEntry *iotlb, bool unmap_all) { + const VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); + uint32_t flags = 0; int ret; if (unmap_all) { - /* The unmap ioctl doesn't accept a full 64-bit span. */ - Int128 llsize = int128_rshift(int128_2_64(), 1); - - ret = vfio_legacy_dma_unmap_one(bcontainer, 0, int128_get64(llsize), - iotlb); + if (container->unmap_all_supported) { + flags = VFIO_DMA_UNMAP_FLAG_ALL; + } else { + /* The unmap ioctl doesn't accept a full 64-bit span. */ + Int128 llsize = int128_rshift(int128_2_64(), 1); + size = int128_get64(llsize); + + ret = vfio_legacy_dma_unmap_one(container, 0, size, flags, iotlb); + if (ret) { + return ret; + } - if (ret == 0) { - ret = vfio_legacy_dma_unmap_one(bcontainer, int128_get64(llsize), - int128_get64(llsize), iotlb); + iova = size; } - - } else { - ret = vfio_legacy_dma_unmap_one(bcontainer, iova, size, iotlb); } - return ret; + return vfio_legacy_dma_unmap_one(container, iova, size, flags, iotlb); } static int vfio_legacy_dma_map(const VFIOContainer *bcontainer, hwaddr iova, @@ -519,6 +522,9 @@ static bool vfio_legacy_setup(VFIOContainer *bcontainer, Error **errp) vfio_get_info_iova_range(info, bcontainer); + ret = ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UNMAP_ALL); + container->unmap_all_supported = !!ret; + vfio_get_iommu_info_migration(container, info); return true; } diff --git a/include/hw/vfio/vfio-container-legacy.h b/include/hw/vfio/vfio-container-legacy.h index 74a72df018b50..ffd594e80d64e 100644 --- a/include/hw/vfio/vfio-container-legacy.h +++ b/include/hw/vfio/vfio-container-legacy.h @@ -30,6 +30,7 @@ struct VFIOLegacyContainer { int fd; /* /dev/vfio/vfio, empowered by the attached groups */ unsigned iommu_type; + bool unmap_all_supported; QLIST_HEAD(, VFIOGroup) group_list; VFIOContainerCPR cpr; }; From b30823e5619ed5658d33e43abe1308195edb3e8b Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 9 Oct 2025 00:01:33 -0400 Subject: [PATCH 1757/1794] vfio/iommufd: Support unmap all in one ioctl() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IOMMUFD kernel uAPI supports unmapping whole address space in one call with [iova, size] set to [0, UINT64_MAX], this can simplify iommufd_cdev_unmap() a bit. See iommufd_ioas_unmap() in kernel for details. Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251009040134.334251-3-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 24a99efa87f4d..fc9cd9d22ff2d 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -62,21 +62,8 @@ static int iommufd_cdev_unmap(const VFIOContainer *bcontainer, { const VFIOIOMMUFDContainer *container = VFIO_IOMMU_IOMMUFD(bcontainer); - /* unmap in halves */ if (unmap_all) { - Int128 llsize = int128_rshift(int128_2_64(), 1); - int ret; - - ret = iommufd_backend_unmap_dma(container->be, container->ioas_id, - 0, int128_get64(llsize)); - - if (ret == 0) { - ret = iommufd_backend_unmap_dma(container->be, container->ioas_id, - int128_get64(llsize), - int128_get64(llsize)); - } - - return ret; + size = UINT64_MAX; } /* TODO: Handle dma_unmap_bitmap with iotlb args (migration) */ From 271fec6f18492630df2e1b4599ba2de6eb1d0668 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 9 Oct 2025 00:01:34 -0400 Subject: [PATCH 1758/1794] vfio/listener: Add an assertion for unmap_all MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the maximum of iommu address space is 64bit. So when a maximum iommu memory section is deleted, it's in scope [0, 2^64). Add a assertion for that. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251009040134.334251-4-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/listener.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index 1b6e5065a3267..2d7d3a464577e 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -715,6 +715,7 @@ static void vfio_listener_region_del(MemoryListener *listener, bool unmap_all = false; if (int128_eq(llsize, int128_2_64())) { + assert(!iova); unmap_all = true; llsize = int128_zero(); } From 1118a4207b0c5287d942a18b1a446c2e29baea3a Mon Sep 17 00:00:00 2001 From: John Levon Date: Thu, 9 Oct 2025 16:02:06 +0200 Subject: [PATCH 1759/1794] docs/system/devices/vfio-user: fix formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The example QEMU argument was not rendering properly, as it was not indented. Signed-off-by: John Levon Fixes: c688cc165b ("docs: add vfio-user documentation") Reviewed-by: Peter Maydell Link: https://lore.kernel.org/qemu-devel/20251009140206.386249-1-john.levon@nutanix.com Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- docs/system/devices/vfio-user.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/system/devices/vfio-user.rst b/docs/system/devices/vfio-user.rst index 30c2215f4ea5a..e10a6d0822993 100644 --- a/docs/system/devices/vfio-user.rst +++ b/docs/system/devices/vfio-user.rst @@ -20,7 +20,7 @@ Presuming a suitable ``vfio-user`` server has opened a socket at .. code-block:: console --device '{"driver": "vfio-user-pci","socket": {"path": "/tmp/vfio-user.sock", "type": "unix"}}' + --device '{"driver": "vfio-user-pci","socket": {"path": "/tmp/vfio-user.sock", "type": "unix"}}' See `libvfio-user `_ for further information. From 7ea3fd2b6c2543c60fb309265fa445fa8fdcc1ea Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 13 Oct 2025 09:35:35 -0600 Subject: [PATCH 1760/1794] MAINTAINERS: Update Alex Williamson's email address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch to a personal email account as I'll be leaving Red Hat soon. Signed-off-by: Alex Williamson Signed-off-by: Alex Williamson Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013153543.3091169-1-alex.williamson@redhat.com Signed-off-by: Cédric Le Goater --- .mailmap | 1 + MAINTAINERS | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.mailmap b/.mailmap index 15bec72470ead..7f817d9f425c0 100644 --- a/.mailmap +++ b/.mailmap @@ -74,6 +74,7 @@ Aleksandar Markovic Aleksandar Rikalo Aleksandar Rikalo +Alex Williamson Alexander Graf Ani Sinha Anthony Liguori Anthony Liguori diff --git a/MAINTAINERS b/MAINTAINERS index 667acd933c7fe..7a0809d82a365 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2285,7 +2285,7 @@ S: Maintained F: hw/usb/dev-serial.c VFIO -M: Alex Williamson +M: Alex Williamson M: Cédric Le Goater S: Supported F: hw/vfio/* @@ -2297,7 +2297,7 @@ F: migration/vfio-stub.c F: tests/functional/aarch64/test_device_passthrough.py vfio-igd -M: Alex Williamson +M: Alex Williamson M: Cédric Le Goater M: Tomita Moeko S: Supported From aaca725884b57c9245528a0afb3f32e078543faf Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 14 Oct 2025 17:12:26 +0200 Subject: [PATCH 1761/1794] vfio: rename field to "num_initial_regions" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We set VFIODevice::num_regions at initialization time, and do not otherwise refresh it. As it is valid in theory for a VFIO device to later increase the number of supported regions, rename the field to "num_initial_regions" to better reflect its semantics. Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20251014151227.2298892-2-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 2 +- hw/vfio/ccw.c | 4 ++-- hw/vfio/device.c | 12 ++++++------ hw/vfio/iommufd.c | 3 ++- hw/vfio/pci.c | 4 ++-- include/hw/vfio/vfio-device.h | 2 +- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index 0609a7dc25428..64ef35b320942 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -134,7 +134,7 @@ static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev, VFIOUserFDs fds = { 0, 1, fd}; int ret; - if (info->index > vbasedev->num_regions) { + if (info->index > vbasedev->num_initial_regions) { return -EINVAL; } diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 9560b8d851b6b..4d9588e7aa1c0 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -484,9 +484,9 @@ static bool vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) * We always expect at least the I/O region to be present. We also * may have a variable number of regions governed by capabilities. */ - if (vdev->num_regions < VFIO_CCW_CONFIG_REGION_INDEX + 1) { + if (vdev->num_initial_regions < VFIO_CCW_CONFIG_REGION_INDEX + 1) { error_setg(errp, "vfio: too few regions (%u), expected at least %u", - vdev->num_regions, VFIO_CCW_CONFIG_REGION_INDEX + 1); + vdev->num_initial_regions, VFIO_CCW_CONFIG_REGION_INDEX + 1); return false; } diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 64f8750389479..52079f4cf5bda 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -257,7 +257,7 @@ int vfio_device_get_region_info_type(VFIODevice *vbasedev, uint32_t type, { int i; - for (i = 0; i < vbasedev->num_regions; i++) { + for (i = 0; i < vbasedev->num_initial_regions; i++) { struct vfio_info_cap_header *hdr; struct vfio_region_info_cap_type *cap_type; @@ -466,7 +466,7 @@ void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainer *bcontainer, int i; vbasedev->num_irqs = info->num_irqs; - vbasedev->num_regions = info->num_regions; + vbasedev->num_initial_regions = info->num_regions; vbasedev->flags = info->flags; vbasedev->reset_works = !!(info->flags & VFIO_DEVICE_FLAGS_RESET); @@ -476,10 +476,10 @@ void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainer *bcontainer, QLIST_INSERT_HEAD(&vfio_device_list, vbasedev, global_next); vbasedev->reginfo = g_new0(struct vfio_region_info *, - vbasedev->num_regions); + vbasedev->num_initial_regions); if (vbasedev->use_region_fds) { - vbasedev->region_fds = g_new0(int, vbasedev->num_regions); - for (i = 0; i < vbasedev->num_regions; i++) { + vbasedev->region_fds = g_new0(int, vbasedev->num_initial_regions); + for (i = 0; i < vbasedev->num_initial_regions; i++) { vbasedev->region_fds[i] = -1; } } @@ -489,7 +489,7 @@ void vfio_device_unprepare(VFIODevice *vbasedev) { int i; - for (i = 0; i < vbasedev->num_regions; i++) { + for (i = 0; i < vbasedev->num_initial_regions; i++) { g_free(vbasedev->reginfo[i]); if (vbasedev->region_fds != NULL && vbasedev->region_fds[i] != -1) { close(vbasedev->region_fds[i]); diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index fc9cd9d22ff2d..bb5775aa711a6 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -650,7 +650,8 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, vfio_iommufd_cpr_register_device(vbasedev); trace_iommufd_cdev_device_info(vbasedev->name, devfd, vbasedev->num_irqs, - vbasedev->num_regions, vbasedev->flags); + vbasedev->num_initial_regions, + vbasedev->flags); return true; err_listener_register: diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 06b06afc2b43d..8b8bc5a42186d 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2975,9 +2975,9 @@ bool vfio_pci_populate_device(VFIOPCIDevice *vdev, Error **errp) return false; } - if (vbasedev->num_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1) { + if (vbasedev->num_initial_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1) { error_setg(errp, "unexpected number of io regions %u", - vbasedev->num_regions); + vbasedev->num_initial_regions); return false; } diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index 7e9aed6d3cd42..0fe6c60ba2d65 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -74,7 +74,7 @@ typedef struct VFIODevice { VFIODeviceOps *ops; VFIODeviceIOOps *io_ops; unsigned int num_irqs; - unsigned int num_regions; + unsigned int num_initial_regions; unsigned int flags; VFIOMigration *migration; Error *migration_blocker; From ecbe424a63c9f860a901d6a4a75724b046abd796 Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 14 Oct 2025 17:12:27 +0200 Subject: [PATCH 1762/1794] vfio: only check region info cache for initial regions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is semantically valid for a VFIO device to increase the number of regions after initialization. In this case, we'd attempt to check for cached region info past the size of the ->reginfo array. Check for the region index and skip the cache in these cases. This also works around some VGPU use cases which appear to be a bug, where VFIO_DEVICE_QUERY_GFX_PLANE returns a region index beyond the reported ->num_regions. Fixes: 95cdb024 ("vfio: add region info cache") Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20251014151227.2298892-3-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/device.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 52079f4cf5bda..8b63e765acbac 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -205,10 +205,19 @@ int vfio_device_get_region_info(VFIODevice *vbasedev, int index, int fd = -1; int ret; - /* check cache */ - if (vbasedev->reginfo[index] != NULL) { - *info = vbasedev->reginfo[index]; - return 0; + /* + * We only set up the region info cache for the initial number of regions. + * + * Since a VFIO device may later increase the number of regions then use + * such regions with an index past ->num_initial_regions, don't attempt to + * use the info cache in those cases. + */ + if (index < vbasedev->num_initial_regions) { + /* check cache */ + if (vbasedev->reginfo[index] != NULL) { + *info = vbasedev->reginfo[index]; + return 0; + } } *info = g_malloc0(argsz); @@ -236,10 +245,12 @@ int vfio_device_get_region_info(VFIODevice *vbasedev, int index, goto retry; } - /* fill cache */ - vbasedev->reginfo[index] = *info; - if (vbasedev->region_fds != NULL) { - vbasedev->region_fds[index] = fd; + if (index < vbasedev->num_initial_regions) { + /* fill cache */ + vbasedev->reginfo[index] = *info; + if (vbasedev->region_fds != NULL) { + vbasedev->region_fds[index] = fd; + } } return 0; From 274e199ba07160d61bb0675070649725aa122f78 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 15 Oct 2025 14:21:56 +0800 Subject: [PATCH 1763/1794] hw/arm/aspeed_ast27x0-ssp: Add SDRAM region and fix naming and size to 512MB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, the SSP memory was incorrectly modeled as "SRAM" with a 32 MB size. This change introduces a new sdram field in AspeedCoprocessorState and updates the realization logic accordingly. Rename from SRAM to SDRAM and correct size from 32MB to 512MB to match hardware. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251015062210.3128710-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-ssp.c | 20 ++++++++++---------- include/hw/arm/aspeed_coprocessor.h | 1 + 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c index 936c7c72e8f74..9f3a1933a0665 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -16,10 +16,10 @@ #include "hw/arm/aspeed_soc.h" #include "hw/arm/aspeed_coprocessor.h" -#define AST2700_SSP_RAM_SIZE (32 * MiB) +#define AST2700_SSP_SDRAM_SIZE (512 * MiB) static const hwaddr aspeed_soc_ast27x0ssp_memmap[] = { - [ASPEED_DEV_SRAM] = 0x00000000, + [ASPEED_DEV_SDRAM] = 0x00000000, [ASPEED_DEV_INTC] = 0x72100000, [ASPEED_DEV_SCU] = 0x72C02000, [ASPEED_DEV_SCUIO] = 0x74C02000, @@ -165,7 +165,7 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) AspeedCoprocessorState *s = ASPEED_COPROCESSOR(dev_soc); AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); DeviceState *armv7m; - g_autofree char *sram_name = NULL; + g_autofree char *sdram_name = NULL; int uart; int i; @@ -184,16 +184,16 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) OBJECT(s->memory), &error_abort); sysbus_realize(SYS_BUS_DEVICE(&a->armv7m), &error_abort); - sram_name = g_strdup_printf("aspeed.dram.%d", - CPU(a->armv7m.cpu)->cpu_index); - - if (!memory_region_init_ram(&s->sram, OBJECT(s), sram_name, - AST2700_SSP_RAM_SIZE, errp)) { + /* SDRAM */ + sdram_name = g_strdup_printf("aspeed.sdram.%d", + CPU(a->armv7m.cpu)->cpu_index); + if (!memory_region_init_ram(&s->sdram, OBJECT(s), sdram_name, + AST2700_SSP_SDRAM_SIZE, errp)) { return; } memory_region_add_subregion(s->memory, - sc->memmap[ASPEED_DEV_SRAM], - &s->sram); + sc->memmap[ASPEED_DEV_SDRAM], + &s->sdram); /* SCU */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { diff --git a/include/hw/arm/aspeed_coprocessor.h b/include/hw/arm/aspeed_coprocessor.h index d77655d65911a..0c7168a89c15a 100644 --- a/include/hw/arm/aspeed_coprocessor.h +++ b/include/hw/arm/aspeed_coprocessor.h @@ -16,6 +16,7 @@ struct AspeedCoprocessorState { DeviceState parent; MemoryRegion *memory; + MemoryRegion sdram; MemoryRegion sram; Clock *sysclk; From 4e7232f268a583a7b37ffea2cf41471e3b1cf97c Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 15 Oct 2025 14:21:57 +0800 Subject: [PATCH 1764/1794] hw/arm/aspeed_ast27x0-tsp: Add SDRAM region and fix naming and size to 512MB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, the TSP memory was incorrectly modeled as "SRAM" with a 32 MB size. Rename from SRAM to SDRAM and correct size from 32MB to 512MB to match hardware. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251015062210.3128710-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-tsp.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index 9318f8c86c519..caeb3392a8e6e 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -16,10 +16,10 @@ #include "hw/arm/aspeed_soc.h" #include "hw/arm/aspeed_coprocessor.h" -#define AST2700_TSP_RAM_SIZE (32 * MiB) +#define AST2700_TSP_SDRAM_SIZE (512 * MiB) static const hwaddr aspeed_soc_ast27x0tsp_memmap[] = { - [ASPEED_DEV_SRAM] = 0x00000000, + [ASPEED_DEV_SDRAM] = 0x00000000, [ASPEED_DEV_INTC] = 0x72100000, [ASPEED_DEV_SCU] = 0x72C02000, [ASPEED_DEV_SCUIO] = 0x74C02000, @@ -165,7 +165,7 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) AspeedCoprocessorState *s = ASPEED_COPROCESSOR(dev_soc); AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); DeviceState *armv7m; - g_autofree char *sram_name = NULL; + g_autofree char *sdram_name = NULL; int uart; int i; @@ -184,16 +184,16 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) OBJECT(s->memory), &error_abort); sysbus_realize(SYS_BUS_DEVICE(&a->armv7m), &error_abort); - sram_name = g_strdup_printf("aspeed.dram.%d", - CPU(a->armv7m.cpu)->cpu_index); - - if (!memory_region_init_ram(&s->sram, OBJECT(s), sram_name, - AST2700_TSP_RAM_SIZE, errp)) { + /* SDRAM */ + sdram_name = g_strdup_printf("aspeed.sdram.%d", + CPU(a->armv7m.cpu)->cpu_index); + if (!memory_region_init_ram(&s->sdram, OBJECT(s), sdram_name, + AST2700_TSP_SDRAM_SIZE, errp)) { return; } memory_region_add_subregion(s->memory, - sc->memmap[ASPEED_DEV_SRAM], - &s->sram); + sc->memmap[ASPEED_DEV_SDRAM], + &s->sdram); /* SCU */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { From 00d65b454187089fbcbd64ba0d756b6883039531 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 15 Oct 2025 14:21:58 +0800 Subject: [PATCH 1765/1794] hw/arm/ast27x0: Add SRAM link and alias mapping for SSP coprocessor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 has a 128KB SRAM, physically mapped at 0x10000000–0x1001FFFF for the PSP (CA35) processor. The SSP coprocessor shares this same SRAM but accesses it through a different address window at 0x70000000–0x7001FFFF. To model this shared-memory behavior in QEMU, this commit introduces a linked SRAM property and alias mapping between the PSP and SSP subsystems. Changes include: - Add a "MemoryRegion *sram" link and "MemoryRegion sram_alias" to AspeedCoprocessorState. - Register the new "sram" property in aspeed_coprocessor_common.c. - In aspeed_ast27x0-fc.c, connect the SSP coprocessor’s "sram" link to the PSP’s SRAM region. - In aspeed_ast27x0-ssp.c, create an alias mapping for SRAM at 0x70000000 – 0x7001FFFF in the SSP’s memory map. This ensures that the SSP can correctly access the shared SRAM contents through its own address space while maintaining a consistent physical backing region. It also guarantees that the SRAM is realized before the SSP device, ensuring successful alias setup. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251015062210.3128710-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 4 ++++ hw/arm/aspeed_ast27x0-ssp.c | 7 +++++++ hw/arm/aspeed_coprocessor_common.c | 2 ++ include/hw/arm/aspeed_coprocessor.h | 3 ++- 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index a61ecff3909b8..25e668a648fa6 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -118,6 +118,8 @@ static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) AspeedCoprocessorState *soc; AspeedCoprocessorClass *sc; Ast2700FCState *s = AST2700A1FC(machine); + AspeedSoCState *psp = ASPEED_SOC(&s->ca35); + s->ssp_sysclk = clock_new(OBJECT(s), "SSP_SYSCLK"); clock_set_hz(s->ssp_sysclk, 200000000ULL); @@ -134,6 +136,8 @@ static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) sc = ASPEED_COPROCESSOR_GET_CLASS(soc); aspeed_soc_uart_set_chr(soc->uart, ASPEED_DEV_UART4, sc->uarts_base, sc->uarts_num, serial_hd(1)); + object_property_set_link(OBJECT(&s->ssp), "sram", + OBJECT(&psp->sram), &error_abort); if (!qdev_realize(DEVICE(&s->ssp), NULL, errp)) { return false; } diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c index 9f3a1933a0665..66c4ef6d1b748 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -20,6 +20,7 @@ static const hwaddr aspeed_soc_ast27x0ssp_memmap[] = { [ASPEED_DEV_SDRAM] = 0x00000000, + [ASPEED_DEV_SRAM] = 0x70000000, [ASPEED_DEV_INTC] = 0x72100000, [ASPEED_DEV_SCU] = 0x72C02000, [ASPEED_DEV_SCUIO] = 0x74C02000, @@ -195,6 +196,12 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) sc->memmap[ASPEED_DEV_SDRAM], &s->sdram); + /* SRAM */ + memory_region_init_alias(&s->sram_alias, OBJECT(s), "sram.alias", + s->sram, 0, memory_region_size(s->sram)); + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SRAM], + &s->sram_alias); + /* SCU */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { return; diff --git a/hw/arm/aspeed_coprocessor_common.c b/hw/arm/aspeed_coprocessor_common.c index 8a94b44f07f23..8322ad5eb544a 100644 --- a/hw/arm/aspeed_coprocessor_common.c +++ b/hw/arm/aspeed_coprocessor_common.c @@ -25,6 +25,8 @@ static void aspeed_coprocessor_realize(DeviceState *dev, Error **errp) static const Property aspeed_coprocessor_properties[] = { DEFINE_PROP_LINK("memory", AspeedCoprocessorState, memory, TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_LINK("sram", AspeedCoprocessorState, sram, TYPE_MEMORY_REGION, + MemoryRegion *), }; static void aspeed_coprocessor_class_init(ObjectClass *oc, const void *data) diff --git a/include/hw/arm/aspeed_coprocessor.h b/include/hw/arm/aspeed_coprocessor.h index 0c7168a89c15a..d9a5f517d7c78 100644 --- a/include/hw/arm/aspeed_coprocessor.h +++ b/include/hw/arm/aspeed_coprocessor.h @@ -17,7 +17,8 @@ struct AspeedCoprocessorState { MemoryRegion *memory; MemoryRegion sdram; - MemoryRegion sram; + MemoryRegion *sram; + MemoryRegion sram_alias; Clock *sysclk; AspeedSCUState scu; From 2c9078b8d2e31af6c33d530f9edb4ba6387b77bd Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 15 Oct 2025 14:21:59 +0800 Subject: [PATCH 1766/1794] hw/arm/ast27x0: Add SRAM link and alias mapping for TSP coprocessor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 has a 128KB SRAM, physically mapped at 0x10000000–0x1001FFFF for the PSP (CA35) processor. The TSP coprocessor shares this same SRAM but accesses it through a different address window at 0x70000000–0x7001FFFF. To model this shared-memory behavior in QEMU, this commit introduces a linked SRAM property and alias mapping between the PSP and TSP subsystems. Changes include: - Add the SRAM alias mapping at 0x70000000 in aspeed_ast27x0-tsp.c. - In aspeed_ast27x0-fc.c, connect the TSP coprocessor’s "sram" link to the PSP’s SRAM region. - Ensure the alias region is initialized during TSP SoC realization so the TSP can correctly access shared SRAM through its own address space. This ensures that the TSP and PSP share the same physical SRAM backing. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251015062210.3128710-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 4 ++++ hw/arm/aspeed_ast27x0-tsp.c | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index 25e668a648fa6..dfac7d1e17b84 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -150,6 +150,8 @@ static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) AspeedCoprocessorState *soc; AspeedCoprocessorClass *sc; Ast2700FCState *s = AST2700A1FC(machine); + AspeedSoCState *psp = ASPEED_SOC(&s->ca35); + s->tsp_sysclk = clock_new(OBJECT(s), "TSP_SYSCLK"); clock_set_hz(s->tsp_sysclk, 200000000ULL); @@ -166,6 +168,8 @@ static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) sc = ASPEED_COPROCESSOR_GET_CLASS(soc); aspeed_soc_uart_set_chr(soc->uart, ASPEED_DEV_UART7, sc->uarts_base, sc->uarts_num, serial_hd(2)); + object_property_set_link(OBJECT(&s->tsp), "sram", + OBJECT(&psp->sram), &error_abort); if (!qdev_realize(DEVICE(&s->tsp), NULL, errp)) { return false; } diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index caeb3392a8e6e..56b68adf541ba 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -20,6 +20,7 @@ static const hwaddr aspeed_soc_ast27x0tsp_memmap[] = { [ASPEED_DEV_SDRAM] = 0x00000000, + [ASPEED_DEV_SRAM] = 0x70000000, [ASPEED_DEV_INTC] = 0x72100000, [ASPEED_DEV_SCU] = 0x72C02000, [ASPEED_DEV_SCUIO] = 0x74C02000, @@ -195,6 +196,12 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) sc->memmap[ASPEED_DEV_SDRAM], &s->sdram); + /* SRAM */ + memory_region_init_alias(&s->sram_alias, OBJECT(s), "sram.alias", + s->sram, 0, memory_region_size(s->sram)); + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SRAM], + &s->sram_alias); + /* SCU */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { return; From 362e54b10ddd76ac9989900f53b7acede09ca86f Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 15 Oct 2025 14:22:00 +0800 Subject: [PATCH 1767/1794] hw/arm/ast27x0: Share single SCU instance across PSP, SSP, and TSP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 has a single SCU hardware block, memory-mapped at 0x12C02000–0x12C03FFF from the perspective of the main CA35 processor (PSP). The SSP and TSP coprocessors access this same SCU block at different addresses: 0x72C02000–0x72C03FFF. Previously, each subsystem (PSP, SSP, and TSP) instantiated its own SCU device, resulting in three independent SCU instances in the QEMU model. In real hardware, however, only a single SCU exists and is shared among all processors. This commit reworks the SCU model to correctly reflect the hardware behavior by allowing SSP and TSP to reference the PSP’s SCU instance. The following changes are introduced: - Add a scu property to AspeedCoprocessorState for linking the coprocessor to the PSP’s SCU instance. - Replace per-coprocessor SCU instantiation with a shared SCU link. - Add "MemoryRegion scu_alias" to model address remapping for SSP and TSP. - Create SCU alias regions in both SSP and TSP coprocessors and map them at 0x72C02000 to mirror the PSP’s SCU registers. - Ensure the SCU device in PSP is realized before SSP/TSP alias setup. With this change, PSP, SSP, and TSP now share a consistent SCU state, matching the single-SCU hardware design of AST2700. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251015062210.3128710-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 4 ++++ hw/arm/aspeed_ast27x0-ssp.c | 13 +++++-------- hw/arm/aspeed_ast27x0-tsp.c | 13 +++++-------- hw/arm/aspeed_coprocessor_common.c | 2 ++ include/hw/arm/aspeed_coprocessor.h | 4 ++-- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index dfac7d1e17b84..ba43a4620760c 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -138,6 +138,8 @@ static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) sc->uarts_num, serial_hd(1)); object_property_set_link(OBJECT(&s->ssp), "sram", OBJECT(&psp->sram), &error_abort); + object_property_set_link(OBJECT(&s->ssp), "scu", + OBJECT(&psp->scu), &error_abort); if (!qdev_realize(DEVICE(&s->ssp), NULL, errp)) { return false; } @@ -170,6 +172,8 @@ static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) sc->uarts_num, serial_hd(2)); object_property_set_link(OBJECT(&s->tsp), "sram", OBJECT(&psp->sram), &error_abort); + object_property_set_link(OBJECT(&s->tsp), "scu", + OBJECT(&psp->scu), &error_abort); if (!qdev_realize(DEVICE(&s->tsp), NULL, errp)) { return false; } diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c index 66c4ef6d1b748..577a3379c6c02 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -137,9 +137,7 @@ static void aspeed_soc_ast27x0ssp_init(Object *obj) int i; object_initialize_child(obj, "armv7m", &a->armv7m, TYPE_ARMV7M); - object_initialize_child(obj, "scu", &s->scu, TYPE_ASPEED_2700_SCU); s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); - qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", sc->silicon_rev); for (i = 0; i < sc->uarts_num; i++) { object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_SERIAL_MM); @@ -203,11 +201,11 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) &s->sram_alias); /* SCU */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { - return; - } - aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->scu), 0, - sc->memmap[ASPEED_DEV_SCU]); + memory_region_init_alias(&s->scu_alias, OBJECT(s), "scu.alias", + &s->scu->iomem, 0, + memory_region_size(&s->scu->iomem)); + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SCU], + &s->scu_alias); /* INTC */ if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[0]), errp)) { @@ -285,7 +283,6 @@ static void aspeed_soc_ast27x0ssp_class_init(ObjectClass *klass, dc->realize = aspeed_soc_ast27x0ssp_realize; sc->valid_cpu_types = valid_cpu_types; - sc->silicon_rev = AST2700_A1_SILICON_REV; sc->uarts_num = 13; sc->uarts_base = ASPEED_DEV_UART0; sc->irqmap = aspeed_soc_ast27x0ssp_irqmap; diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index 56b68adf541ba..a7c141678fc28 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -137,9 +137,7 @@ static void aspeed_soc_ast27x0tsp_init(Object *obj) int i; object_initialize_child(obj, "armv7m", &a->armv7m, TYPE_ARMV7M); - object_initialize_child(obj, "scu", &s->scu, TYPE_ASPEED_2700_SCU); s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); - qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", sc->silicon_rev); for (i = 0; i < sc->uarts_num; i++) { object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_SERIAL_MM); @@ -203,11 +201,11 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) &s->sram_alias); /* SCU */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { - return; - } - aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->scu), 0, - sc->memmap[ASPEED_DEV_SCU]); + memory_region_init_alias(&s->scu_alias, OBJECT(s), "scu.alias", + &s->scu->iomem, 0, + memory_region_size(&s->scu->iomem)); + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SCU], + &s->scu_alias); /* INTC */ if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[0]), errp)) { @@ -285,7 +283,6 @@ static void aspeed_soc_ast27x0tsp_class_init(ObjectClass *klass, dc->realize = aspeed_soc_ast27x0tsp_realize; sc->valid_cpu_types = valid_cpu_types; - sc->silicon_rev = AST2700_A1_SILICON_REV; sc->uarts_num = 13; sc->uarts_base = ASPEED_DEV_UART0; sc->irqmap = aspeed_soc_ast27x0tsp_irqmap; diff --git a/hw/arm/aspeed_coprocessor_common.c b/hw/arm/aspeed_coprocessor_common.c index 8322ad5eb544a..14e26bbe234ed 100644 --- a/hw/arm/aspeed_coprocessor_common.c +++ b/hw/arm/aspeed_coprocessor_common.c @@ -27,6 +27,8 @@ static const Property aspeed_coprocessor_properties[] = { TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_LINK("sram", AspeedCoprocessorState, sram, TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_LINK("scu", AspeedCoprocessorState, scu, TYPE_ASPEED_SCU, + AspeedSCUState *), }; static void aspeed_coprocessor_class_init(ObjectClass *oc, const void *data) diff --git a/include/hw/arm/aspeed_coprocessor.h b/include/hw/arm/aspeed_coprocessor.h index d9a5f517d7c78..c1f2c549c323f 100644 --- a/include/hw/arm/aspeed_coprocessor.h +++ b/include/hw/arm/aspeed_coprocessor.h @@ -19,9 +19,10 @@ struct AspeedCoprocessorState { MemoryRegion sdram; MemoryRegion *sram; MemoryRegion sram_alias; + MemoryRegion scu_alias; Clock *sysclk; - AspeedSCUState scu; + AspeedSCUState *scu; AspeedSCUState scuio; AspeedTimerCtrlState timerctrl; SerialMM uart[ASPEED_UARTS_NUM]; @@ -36,7 +37,6 @@ struct AspeedCoprocessorClass { /** valid_cpu_types: NULL terminated array of a single CPU type. */ const char * const *valid_cpu_types; - uint32_t silicon_rev; const hwaddr *memmap; const int *irqmap; int uarts_base; From 10e377246d37bc0bfeaed4d9a700975ae9bc5282 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 15 Oct 2025 14:22:01 +0800 Subject: [PATCH 1768/1794] hw/arm/ast27x0: Share single UART set across PSP, SSP, and TSP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the original model, each subsystem (PSP, SSP, and TSP) created its own set of 13 UART devices, resulting in a total of 39 UART instances. However, on real AST2700 hardware, there is only one set of 13 UARTs shared among all processors. This commit reworks the UART handling to correctly model the shared hardware design. The PSP now creates the full set of 13 UART instances, while the SSP and TSP link to the corresponding shared UART device through object properties. Changes include: - Add "DEFINE_PROP_LINK("uart", ...)" and "DEFINE_PROP_INT32("uart-dev", ...)" to allow each coprocessor to reference a specific shared UART instance. - Modify SSP to link to PSP’s UART4, and TSP to link to PSP’s UART7. - Introduce "uart_alias" to remap the UART’s MMIO region into the coprocessor’s memory space. - Redirect the UART interrupt to the coprocessor’s NVIC, replacing the default routing to the PSP’s GIC. With this change, only one set of 13 UART devices is instantiated by the PSP, while the SSP and TSP reuse them via aliasing and shared interrupt routing, matching the real AST2700 hardware behavior. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251015062210.3128710-7-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 24 ++++++++++++------------ hw/arm/aspeed_ast27x0-ssp.c | 29 ++++++++++++----------------- hw/arm/aspeed_ast27x0-tsp.c | 29 ++++++++++++----------------- hw/arm/aspeed_coprocessor_common.c | 3 +++ include/hw/arm/aspeed_coprocessor.h | 6 +++--- 5 files changed, 42 insertions(+), 49 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index ba43a4620760c..99af87dbd7bd5 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -93,6 +93,10 @@ static bool ast2700fc_ca35_init(MachineState *machine, Error **errp) AST2700FC_HW_STRAP2, &error_abort); aspeed_soc_uart_set_chr(soc->uart, ASPEED_DEV_UART12, sc->uarts_base, sc->uarts_num, serial_hd(0)); + aspeed_soc_uart_set_chr(soc->uart, ASPEED_DEV_UART4, sc->uarts_base, + sc->uarts_num, serial_hd(1)); + aspeed_soc_uart_set_chr(soc->uart, ASPEED_DEV_UART7, sc->uarts_base, + sc->uarts_num, serial_hd(2)); if (!qdev_realize(DEVICE(&s->ca35), NULL, errp)) { return false; } @@ -115,8 +119,6 @@ static bool ast2700fc_ca35_init(MachineState *machine, Error **errp) static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) { - AspeedCoprocessorState *soc; - AspeedCoprocessorClass *sc; Ast2700FCState *s = AST2700A1FC(machine); AspeedSoCState *psp = ASPEED_SOC(&s->ca35); @@ -132,10 +134,10 @@ static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) object_property_set_link(OBJECT(&s->ssp), "memory", OBJECT(&s->ssp_memory), &error_abort); - soc = ASPEED_COPROCESSOR(&s->ssp); - sc = ASPEED_COPROCESSOR_GET_CLASS(soc); - aspeed_soc_uart_set_chr(soc->uart, ASPEED_DEV_UART4, sc->uarts_base, - sc->uarts_num, serial_hd(1)); + object_property_set_link(OBJECT(&s->ssp), "uart", + OBJECT(&psp->uart[4]), &error_abort); + object_property_set_int(OBJECT(&s->ssp), "uart-dev", ASPEED_DEV_UART4, + &error_abort); object_property_set_link(OBJECT(&s->ssp), "sram", OBJECT(&psp->sram), &error_abort); object_property_set_link(OBJECT(&s->ssp), "scu", @@ -149,8 +151,6 @@ static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) { - AspeedCoprocessorState *soc; - AspeedCoprocessorClass *sc; Ast2700FCState *s = AST2700A1FC(machine); AspeedSoCState *psp = ASPEED_SOC(&s->ca35); @@ -166,10 +166,10 @@ static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) object_property_set_link(OBJECT(&s->tsp), "memory", OBJECT(&s->tsp_memory), &error_abort); - soc = ASPEED_COPROCESSOR(&s->tsp); - sc = ASPEED_COPROCESSOR_GET_CLASS(soc); - aspeed_soc_uart_set_chr(soc->uart, ASPEED_DEV_UART7, sc->uarts_base, - sc->uarts_num, serial_hd(2)); + object_property_set_link(OBJECT(&s->tsp), "uart", + OBJECT(&psp->uart[7]), &error_abort); + object_property_set_int(OBJECT(&s->tsp), "uart-dev", ASPEED_DEV_UART7, + &error_abort); object_property_set_link(OBJECT(&s->tsp), "sram", OBJECT(&psp->sram), &error_abort); object_property_set_link(OBJECT(&s->tsp), "scu", diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c index 577a3379c6c02..d12a9b8459c23 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -133,16 +133,10 @@ static void aspeed_soc_ast27x0ssp_init(Object *obj) { Aspeed27x0CoprocessorState *a = ASPEED27X0SSP_COPROCESSOR(obj); AspeedCoprocessorState *s = ASPEED_COPROCESSOR(obj); - AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); - int i; object_initialize_child(obj, "armv7m", &a->armv7m, TYPE_ARMV7M); s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); - for (i = 0; i < sc->uarts_num; i++) { - object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_SERIAL_MM); - } - object_initialize_child(obj, "intc0", &a->intc[0], TYPE_ASPEED_2700SSP_INTC); object_initialize_child(obj, "intc1", &a->intc[1], @@ -165,7 +159,6 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); DeviceState *armv7m; g_autofree char *sdram_name = NULL; - int uart; int i; if (!clock_has_source(s->sysclk)) { @@ -244,15 +237,19 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc[1]), i, qdev_get_gpio_in(DEVICE(&a->intc[0].orgates[0]), i)); } + /* UART */ - for (i = 0, uart = sc->uarts_base; i < sc->uarts_num; i++, uart++) { - if (!aspeed_soc_uart_realize(s->memory, &s->uart[i], - sc->memmap[uart], errp)) { - return; - } - sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, - aspeed_soc_ast27x0ssp_get_irq(s, uart)); - } + memory_region_init_alias(&s->uart_alias, OBJECT(s), "uart.alias", + &s->uart->serial.io, 0, + memory_region_size(&s->uart->serial.io)); + memory_region_add_subregion(s->memory, sc->memmap[s->uart_dev], + &s->uart_alias); + /* + * Redirect the UART interrupt to the NVIC, replacing the default routing + * to the PSP's GIC. + */ + sysbus_connect_irq(SYS_BUS_DEVICE(s->uart), 0, + aspeed_soc_ast27x0ssp_get_irq(s, s->uart_dev)); aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->timerctrl), "aspeed.timerctrl", @@ -283,8 +280,6 @@ static void aspeed_soc_ast27x0ssp_class_init(ObjectClass *klass, dc->realize = aspeed_soc_ast27x0ssp_realize; sc->valid_cpu_types = valid_cpu_types; - sc->uarts_num = 13; - sc->uarts_base = ASPEED_DEV_UART0; sc->irqmap = aspeed_soc_ast27x0ssp_irqmap; sc->memmap = aspeed_soc_ast27x0ssp_memmap; } diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index a7c141678fc28..5b75e14206399 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -133,16 +133,10 @@ static void aspeed_soc_ast27x0tsp_init(Object *obj) { Aspeed27x0CoprocessorState *a = ASPEED27X0TSP_COPROCESSOR(obj); AspeedCoprocessorState *s = ASPEED_COPROCESSOR(obj); - AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); - int i; object_initialize_child(obj, "armv7m", &a->armv7m, TYPE_ARMV7M); s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); - for (i = 0; i < sc->uarts_num; i++) { - object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_SERIAL_MM); - } - object_initialize_child(obj, "intc0", &a->intc[0], TYPE_ASPEED_2700TSP_INTC); object_initialize_child(obj, "intc1", &a->intc[1], @@ -165,7 +159,6 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) AspeedCoprocessorClass *sc = ASPEED_COPROCESSOR_GET_CLASS(s); DeviceState *armv7m; g_autofree char *sdram_name = NULL; - int uart; int i; if (!clock_has_source(s->sysclk)) { @@ -244,15 +237,19 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc[1]), i, qdev_get_gpio_in(DEVICE(&a->intc[0].orgates[0]), i)); } + /* UART */ - for (i = 0, uart = sc->uarts_base; i < sc->uarts_num; i++, uart++) { - if (!aspeed_soc_uart_realize(s->memory, &s->uart[i], - sc->memmap[uart], errp)) { - return; - } - sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, - aspeed_soc_ast27x0tsp_get_irq(s, uart)); - } + memory_region_init_alias(&s->uart_alias, OBJECT(s), "uart.alias", + &s->uart->serial.io, 0, + memory_region_size(&s->uart->serial.io)); + memory_region_add_subregion(s->memory, sc->memmap[s->uart_dev], + &s->uart_alias); + /* + * Redirect the UART interrupt to the NVIC, replacing the default routing + * to the PSP's GIC. + */ + sysbus_connect_irq(SYS_BUS_DEVICE(s->uart), 0, + aspeed_soc_ast27x0tsp_get_irq(s, s->uart_dev)); aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->timerctrl), "aspeed.timerctrl", @@ -283,8 +280,6 @@ static void aspeed_soc_ast27x0tsp_class_init(ObjectClass *klass, dc->realize = aspeed_soc_ast27x0tsp_realize; sc->valid_cpu_types = valid_cpu_types; - sc->uarts_num = 13; - sc->uarts_base = ASPEED_DEV_UART0; sc->irqmap = aspeed_soc_ast27x0tsp_irqmap; sc->memmap = aspeed_soc_ast27x0tsp_memmap; } diff --git a/hw/arm/aspeed_coprocessor_common.c b/hw/arm/aspeed_coprocessor_common.c index 14e26bbe234ed..f037d5b573fd4 100644 --- a/hw/arm/aspeed_coprocessor_common.c +++ b/hw/arm/aspeed_coprocessor_common.c @@ -29,6 +29,9 @@ static const Property aspeed_coprocessor_properties[] = { MemoryRegion *), DEFINE_PROP_LINK("scu", AspeedCoprocessorState, scu, TYPE_ASPEED_SCU, AspeedSCUState *), + DEFINE_PROP_LINK("uart", AspeedCoprocessorState, uart, TYPE_SERIAL_MM, + SerialMM *), + DEFINE_PROP_INT32("uart-dev", AspeedCoprocessorState, uart_dev, 0), }; static void aspeed_coprocessor_class_init(ObjectClass *oc, const void *data) diff --git a/include/hw/arm/aspeed_coprocessor.h b/include/hw/arm/aspeed_coprocessor.h index c1f2c549c323f..4a50f688ecdcd 100644 --- a/include/hw/arm/aspeed_coprocessor.h +++ b/include/hw/arm/aspeed_coprocessor.h @@ -19,13 +19,15 @@ struct AspeedCoprocessorState { MemoryRegion sdram; MemoryRegion *sram; MemoryRegion sram_alias; + MemoryRegion uart_alias; MemoryRegion scu_alias; Clock *sysclk; AspeedSCUState *scu; AspeedSCUState scuio; AspeedTimerCtrlState timerctrl; - SerialMM uart[ASPEED_UARTS_NUM]; + SerialMM *uart; + int uart_dev; }; #define TYPE_ASPEED_COPROCESSOR "aspeed-coprocessor" @@ -39,8 +41,6 @@ struct AspeedCoprocessorClass { const char * const *valid_cpu_types; const hwaddr *memmap; const int *irqmap; - int uarts_base; - int uarts_num; }; struct Aspeed27x0CoprocessorState { From 71c01f57879129bc62b7cb53cd0a13bd70d79582 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 15 Oct 2025 14:22:02 +0800 Subject: [PATCH 1769/1794] hw/arm/aspeed_ast27x0-fc: Map FMC0 flash contents into CA35 boot ROM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces a dedicated ca35_boot_rom memory region and copies the FMC0 flash data into it. The motivation is to support the upcoming vbootrom. The vbootrom replaces the existing BOOTMCU (RISC-V 32 SPL) flow, which currently reads the "image-bmc" from FMC_CS0 and loads the following components into DRAM: - Trusted Firmware-A - OP-TEE OS - u-boot-nodtb.bin - u-boot.dtb After loading, BOOTMCU releases the CA35 reset so that CA35 can start executing Trusted Firmware-A. The vbootrom follows the same sequence: CA35 fetches "image-bmc" from FMC0 flash at the SPI boot ROM base address (0x100000000), parses the FIT image, loads each component into its designated DRAM location, and then jumps to Trusted Firmware-A. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251015062210.3128710-8-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index 99af87dbd7bd5..99c0b3415990f 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -35,6 +35,7 @@ struct Ast2700FCState { MemoryRegion ca35_memory; MemoryRegion ca35_dram; + MemoryRegion ca35_boot_rom; MemoryRegion ssp_memory; MemoryRegion tsp_memory; @@ -44,8 +45,6 @@ struct Ast2700FCState { Aspeed27x0SoCState ca35; Aspeed27x0CoprocessorState ssp; Aspeed27x0CoprocessorState tsp; - - bool mmio_exec; }; #define AST2700FC_BMC_RAM_SIZE (1 * GiB) @@ -61,6 +60,9 @@ static bool ast2700fc_ca35_init(MachineState *machine, Error **errp) Ast2700FCState *s = AST2700A1FC(machine); AspeedSoCState *soc; AspeedSoCClass *sc; + BlockBackend *fmc0 = NULL; + DeviceState *dev = NULL; + uint64_t rom_size; object_initialize_child(OBJECT(s), "ca35", &s->ca35, "ast2700-a1"); soc = ASPEED_SOC(&s->ca35); @@ -112,6 +114,14 @@ static bool ast2700fc_ca35_init(MachineState *machine, Error **errp) ast2700fc_board_info.ram_size = machine->ram_size; ast2700fc_board_info.loader_start = sc->memmap[ASPEED_DEV_SDRAM]; + dev = ssi_get_cs(soc->fmc.spi, 0); + fmc0 = dev ? m25p80_get_blk(dev) : NULL; + + if (fmc0) { + rom_size = memory_region_size(&soc->spi_boot); + aspeed_install_boot_rom(soc, fmc0, &s->ca35_boot_rom, rom_size); + } + arm_load_kernel(ARM_CPU(first_cpu), machine, &ast2700fc_board_info); return true; From 02bf7686e87979325f7af6f3ab88766853b881ef Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 15 Oct 2025 14:22:03 +0800 Subject: [PATCH 1770/1794] hw/arm/aspeed_ast27x0-fc: Add VBOOTROM support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces support for loading a vbootrom image into the dedicated vbootrom memory region in the AST2700 Full Core machine. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251015062210.3128710-9-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index 99c0b3415990f..7be2e849274df 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -60,6 +60,7 @@ static bool ast2700fc_ca35_init(MachineState *machine, Error **errp) Ast2700FCState *s = AST2700A1FC(machine); AspeedSoCState *soc; AspeedSoCClass *sc; + const char *bios_name = NULL; BlockBackend *fmc0 = NULL; DeviceState *dev = NULL; uint64_t rom_size; @@ -122,6 +123,10 @@ static bool ast2700fc_ca35_init(MachineState *machine, Error **errp) aspeed_install_boot_rom(soc, fmc0, &s->ca35_boot_rom, rom_size); } + /* VBOOTROM */ + bios_name = machine->firmware ?: VBOOTROM_FILE_NAME; + aspeed_load_vbootrom(soc, bios_name, errp); + arm_load_kernel(ARM_CPU(first_cpu), machine, &ast2700fc_board_info); return true; From 37acdd737a2696a9c2d8e165fb67e2e497b4e331 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 15 Oct 2025 14:22:04 +0800 Subject: [PATCH 1771/1794] tests/functional/aarch64/ast2700fc: Update test ASPEED SDK v09.08 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251015062210.3128710-10-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/aarch64/test_aspeed_ast2700fc.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/functional/aarch64/test_aspeed_ast2700fc.py b/tests/functional/aarch64/test_aspeed_ast2700fc.py index 28b66614d970c..bcce0c8d4e533 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700fc.py +++ b/tests/functional/aarch64/test_aspeed_ast2700fc.py @@ -36,9 +36,9 @@ def verify_openbmc_boot_and_login(self, name): exec_command_and_wait_for_pattern(self, 'root', 'Password:') exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') - ASSET_SDK_V906_AST2700 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2700-default-obmc.tar.gz', - 'f1d53e0be8a404ecce3e105f72bc50fa4e090ad13160ffa91b10a6e0233a9dc6') + ASSET_SDK_V908_AST2700 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.08/ast2700-default-obmc.tar.gz', + 'eac3dc409b7ea3cd4b03d4792d3cebd469792ad893cb51e1d15f0fc20bd1e2cd') def do_ast2700_i2c_test(self): exec_command_and_wait_for_pattern(self, @@ -66,7 +66,7 @@ def do_ast2700fc_ssp_test(self): self.vm.set_console(console_index=1) self.vm.launch() - exec_command_and_wait_for_pattern(self, '\012', 'ssp:~$') + exec_command_and_wait_for_pattern(self, '\012', 'ssp_tsp:~$') exec_command_and_wait_for_pattern(self, 'version', 'Zephyr version 3.7.1') exec_command_and_wait_for_pattern(self, 'md 72c02000 1', @@ -133,10 +133,10 @@ def start_ast2700fc_test(self, name): self.do_test_aarch64_aspeed_sdk_start( self.scratch_file(name, 'image-bmc')) - def test_aarch64_ast2700fc_sdk_v09_06(self): + def test_aarch64_ast2700fc_sdk_v09_08(self): self.set_machine('ast2700fc') - self.archive_extract(self.ASSET_SDK_V906_AST2700) + self.archive_extract(self.ASSET_SDK_V908_AST2700) self.start_ast2700fc_test('ast2700-default') self.verify_openbmc_boot_and_login('ast2700-default') self.do_ast2700_i2c_test() From f001f5d95aeed29032029c3ab1dca7a50af922bc Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 15 Oct 2025 14:22:05 +0800 Subject: [PATCH 1772/1794] tests/functional/aarch64/ast2700fc: Add eth2 network interface check in PCIe test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhance the AST2700 functional PCIe test to verify the network interface configuration for eth2. This adds an additional command to check the IP address assignment on eth2 to ensure network functionality is correctly initialized in the test environment. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251015062210.3128710-11-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/aarch64/test_aspeed_ast2700fc.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/functional/aarch64/test_aspeed_ast2700fc.py b/tests/functional/aarch64/test_aspeed_ast2700fc.py index bcce0c8d4e533..843647e6c539c 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700fc.py +++ b/tests/functional/aarch64/test_aspeed_ast2700fc.py @@ -60,6 +60,9 @@ def do_ast2700_pcie_test(self): 'lspci -s 0002:01:00.0', '0002:01:00.0 Ethernet controller: ' 'Intel Corporation 82574L Gigabit Network Connection') + exec_command_and_wait_for_pattern(self, + 'ip addr show dev eth2', + 'inet 10.0.2.15/24') def do_ast2700fc_ssp_test(self): self.vm.shutdown() @@ -135,6 +138,7 @@ def start_ast2700fc_test(self, name): def test_aarch64_ast2700fc_sdk_v09_08(self): self.set_machine('ast2700fc') + self.require_netdev('user') self.archive_extract(self.ASSET_SDK_V908_AST2700) self.start_ast2700fc_test('ast2700-default') From 7c77c48e824cd4a1e58b17b747bfb3e2e09a79a2 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 15 Oct 2025 14:22:06 +0800 Subject: [PATCH 1773/1794] tests/functional/aarch64/ast2700fc: Move coprocessor image loading to common function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes duplicate code in start_ast2700fc_test() and prepares for reuse in upcoming VBOOTROM tests. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251015062210.3128710-12-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- .../aarch64/test_aspeed_ast2700fc.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/functional/aarch64/test_aspeed_ast2700fc.py b/tests/functional/aarch64/test_aspeed_ast2700fc.py index 843647e6c539c..9ab3d3269b2ad 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700fc.py +++ b/tests/functional/aarch64/test_aspeed_ast2700fc.py @@ -36,6 +36,17 @@ def verify_openbmc_boot_and_login(self, name): exec_command_and_wait_for_pattern(self, 'root', 'Password:') exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') + def load_ast2700fc_coprocessor(self, name): + load_elf_list = { + 'ssp': self.scratch_file(name, 'zephyr-aspeed-ssp.elf'), + 'tsp': self.scratch_file(name, 'zephyr-aspeed-tsp.elf') + } + + for cpu_num, key in enumerate(load_elf_list, start=4): + file = load_elf_list[key] + self.vm.add_args('-device', + f'loader,file={file},cpu-num={cpu_num}') + ASSET_SDK_V908_AST2700 = Asset( 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.08/ast2700-default-obmc.tar.gz', 'eac3dc409b7ea3cd4b03d4792d3cebd469792ad893cb51e1d15f0fc20bd1e2cd') @@ -123,16 +134,7 @@ def start_ast2700fc_test(self, name): self.vm.add_args('-device', f'loader,addr=0x430000000,cpu-num={i}') - load_elf_list = { - 'ssp': self.scratch_file(name, 'zephyr-aspeed-ssp.elf'), - 'tsp': self.scratch_file(name, 'zephyr-aspeed-tsp.elf') - } - - for cpu_num, key in enumerate(load_elf_list, start=4): - file = load_elf_list[key] - self.vm.add_args('-device', - f'loader,file={file},cpu-num={cpu_num}') - + self.load_ast2700fc_coprocessor(name) self.do_test_aarch64_aspeed_sdk_start( self.scratch_file(name, 'image-bmc')) From d187d120d17c6b6de3b6432243dcc706ec144658 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 15 Oct 2025 14:22:07 +0800 Subject: [PATCH 1774/1794] tests/functional/aarch64/ast2700fc: Add vbootrom test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add start_ast2700fc_test_vbootrom() which boots the ast2700fc machine with -bios ast27x0_bootrom.bin and reuses the coprocessor loader. Add test_aarch64_ast2700fc_sdk_vbootrom_v09_08() to test the vbootrom with ast2700fc machine. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251015062210.3128710-13-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/aarch64/test_aspeed_ast2700fc.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/functional/aarch64/test_aspeed_ast2700fc.py b/tests/functional/aarch64/test_aspeed_ast2700fc.py index 9ab3d3269b2ad..8dbc8f234f9c5 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700fc.py +++ b/tests/functional/aarch64/test_aspeed_ast2700fc.py @@ -138,6 +138,12 @@ def start_ast2700fc_test(self, name): self.do_test_aarch64_aspeed_sdk_start( self.scratch_file(name, 'image-bmc')) + def start_ast2700fc_test_vbootrom(self, name): + self.vm.add_args('-bios', 'ast27x0_bootrom.bin') + self.load_ast2700fc_coprocessor(name) + self.do_test_aarch64_aspeed_sdk_start( + self.scratch_file(name, 'image-bmc')) + def test_aarch64_ast2700fc_sdk_v09_08(self): self.set_machine('ast2700fc') self.require_netdev('user') @@ -150,5 +156,14 @@ def test_aarch64_ast2700fc_sdk_v09_08(self): self.do_ast2700fc_ssp_test() self.do_ast2700fc_tsp_test() + def test_aarch64_ast2700fc_sdk_vbootrom_v09_08(self): + self.set_machine('ast2700fc') + + self.archive_extract(self.ASSET_SDK_V908_AST2700) + self.start_ast2700fc_test_vbootrom('ast2700-default') + self.verify_openbmc_boot_and_login('ast2700-default') + self.do_ast2700fc_ssp_test() + self.do_ast2700fc_tsp_test() + if __name__ == '__main__': QemuSystemTest.main() From 42fa4bd01a90baea260833d565922fec5d28f768 Mon Sep 17 00:00:00 2001 From: Felix Wu Date: Wed, 15 Oct 2025 01:18:25 +0000 Subject: [PATCH 1775/1794] hw/gpio: Add property for ASPEED GPIO in 32 bits basis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added 32 bits property for ASPEED GPIO. Previously it can only be access in bitwise manner. The changes to qobject is to index gpios with array indices on top of accessing with registers. This allows for easier gpio access, especially in tests with complex behaviors that requires large number of gpios at a time, like fault injection and networking behaviors. Indexing multiple gpios at once allows qmp/side band client to no longer hardcode and populate register names and manipulate them faster. Signed-off-by: Felix Wu Reviewed-by: Andrew Jeffery Link: https://lore.kernel.org/qemu-devel/20251015011830.1688468-2-lixiaoyan@google.com [ clg: wrapped commit log lines ] Signed-off-by: Cédric Le Goater --- hw/gpio/aspeed_gpio.c | 57 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c index 609a556908f94..2d78bf9515c40 100644 --- a/hw/gpio/aspeed_gpio.c +++ b/hw/gpio/aspeed_gpio.c @@ -1308,6 +1308,57 @@ static void aspeed_gpio_2700_write(void *opaque, hwaddr offset, } /* Setup functions */ +static void aspeed_gpio_set_set(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + uint32_t set_val = 0; + AspeedGPIOState *s = ASPEED_GPIO(obj); + AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s); + int set_idx = 0; + + if (!visit_type_uint32(v, name, &set_val, errp)) { + return; + } + + if (sscanf(name, "gpio-set[%d]", &set_idx) != 1) { + error_setg(errp, "%s: error reading %s", __func__, name); + return; + } + + if (set_idx >= agc->nr_gpio_sets || set_idx < 0) { + error_setg(errp, "%s: invalid set_idx %s", __func__, name); + return; + } + + aspeed_gpio_update(s, &s->sets[set_idx], set_val, + ~s->sets[set_idx].direction); +} + +static void aspeed_gpio_get_set(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + uint32_t set_val = 0; + AspeedGPIOState *s = ASPEED_GPIO(obj); + AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s); + int set_idx = 0; + + if (sscanf(name, "gpio-set[%d]", &set_idx) != 1) { + error_setg(errp, "%s: error reading %s", __func__, name); + return; + } + + if (set_idx >= agc->nr_gpio_sets || set_idx < 0) { + error_setg(errp, "%s: invalid set_idx %s", __func__, name); + return; + } + + set_val = s->sets[set_idx].data_value; + visit_type_uint32(v, name, &set_val, errp); +} + +/****************** Setup functions ******************/ static const GPIOSetProperties ast2400_set_props[ASPEED_GPIO_MAX_NR_SETS] = { [0] = {0xffffffff, 0xffffffff, {"A", "B", "C", "D"} }, [1] = {0xffffffff, 0xffffffff, {"E", "F", "G", "H"} }, @@ -1435,6 +1486,12 @@ static void aspeed_gpio_init(Object *obj) g_free(name); } } + + for (int i = 0; i < agc->nr_gpio_sets; i++) { + char *name = g_strdup_printf("gpio-set[%d]", i); + object_property_add(obj, name, "uint32", aspeed_gpio_get_set, + aspeed_gpio_set_set, NULL, NULL); + } } static const VMStateDescription vmstate_gpio_regs = { From 1306584925d1e5d8c4a7699a9f3fa125a10e4996 Mon Sep 17 00:00:00 2001 From: Felix Wu Date: Wed, 15 Oct 2025 01:18:26 +0000 Subject: [PATCH 1776/1794] tests/qtest: Add qtest for for ASPEED GPIO gpio-set property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added qtests to test gpio-set property for ASPEED. - Added function to get uint in qdict. Signed-off-by: Felix Wu Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251015011830.1688468-3-lixiaoyan@google.com Signed-off-by: Cédric Le Goater --- include/qobject/qdict.h | 1 + qobject/qdict.c | 13 ++++ tests/qtest/aspeed_gpio-test.c | 105 ++++++++++++++++++++++++++++++--- 3 files changed, 110 insertions(+), 9 deletions(-) diff --git a/include/qobject/qdict.h b/include/qobject/qdict.h index 903e6e5462fb2..861996f08dd6a 100644 --- a/include/qobject/qdict.h +++ b/include/qobject/qdict.h @@ -57,6 +57,7 @@ void qdict_put_str(QDict *qdict, const char *key, const char *value); double qdict_get_double(const QDict *qdict, const char *key); int64_t qdict_get_int(const QDict *qdict, const char *key); +uint64_t qdict_get_uint(const QDict *qdict, const char *key); bool qdict_get_bool(const QDict *qdict, const char *key); QList *qdict_get_qlist(const QDict *qdict, const char *key); QDict *qdict_get_qdict(const QDict *qdict, const char *key); diff --git a/qobject/qdict.c b/qobject/qdict.c index a90ac9ae2f89e..0dafe6d421793 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -209,6 +209,19 @@ int64_t qdict_get_int(const QDict *qdict, const char *key) return qnum_get_int(qobject_to(QNum, qdict_get(qdict, key))); } +/** + * qdict_get_uint(): Get an unsigned integer mapped by 'key' + * + * This function assumes that 'key' exists and it stores a + * QNum representable as uint. + * + * Return unsigned integer mapped by 'key'. + */ +uint64_t qdict_get_uint(const QDict *qdict, const char *key) +{ + return qnum_get_uint(qobject_to(QNum, qdict_get(qdict, key))); +} + /** * qdict_get_bool(): Get a bool mapped by 'key' * diff --git a/tests/qtest/aspeed_gpio-test.c b/tests/qtest/aspeed_gpio-test.c index 12675d4cbba93..c2f9ca2298a7f 100644 --- a/tests/qtest/aspeed_gpio-test.c +++ b/tests/qtest/aspeed_gpio-test.c @@ -27,28 +27,115 @@ #include "qemu/timer.h" #include "qobject/qdict.h" #include "libqtest-single.h" +#include "qemu/typedefs.h" #define AST2600_GPIO_BASE 0x1E780000 #define GPIO_ABCD_DATA_VALUE 0x000 #define GPIO_ABCD_DIRECTION 0x004 +static uint32_t qtest_qom_get_uint32(QTestState *s, const char *path, + const char *property) +{ + QDict *r; + + uint32_t res; + r = qtest_qmp(s, "{ 'execute': 'qom-get', 'arguments': " + "{ 'path': %s, 'property': %s } }", path, property); + res = qdict_get_uint(r, "return"); + qobject_unref(r); + + return res; +} + +static void qtest_qom_set_uint32(QTestState *s, const char *path, + const char *property, uint32_t value) +{ + QDict *r; + + r = qtest_qmp(s, "{ 'execute': 'qom-set', 'arguments': " + "{ 'path': %s, 'property': %s, 'value': %" PRIu32 " } }", + path, property, value); + qobject_unref(r); +} + +static const char *resp_get_error(QDict *r, const char* error_key) +{ + QDict *qdict; + + g_assert(r); + + qdict = qdict_get_qdict(r, "error"); + if (qdict) { + return qdict_get_str(qdict, error_key); + } + + return NULL; +} + +static bool qtest_qom_check_error(QTestState *s, const char *path, + const char *property, const char *error_msg, + const char *error_msg_key) +{ + QDict *r; + bool b; + + r = qtest_qmp(s, "{ 'execute': 'qom-get', 'arguments': " + "{ 'path': %s, 'property': %s } }", path, property); + b = g_str_equal(resp_get_error(r, error_msg_key), error_msg); + qobject_unref(r); + + return b; +} + static void test_set_colocated_pins(const void *data) { QTestState *s = (QTestState *)data; - + const char path[] = "/machine/soc/gpio"; /* * gpioV4-7 occupy bits within a single 32-bit value, so we want to make * sure that modifying one doesn't affect the other. */ - qtest_qom_set_bool(s, "/machine/soc/gpio", "gpioV4", true); - qtest_qom_set_bool(s, "/machine/soc/gpio", "gpioV5", false); - qtest_qom_set_bool(s, "/machine/soc/gpio", "gpioV6", true); - qtest_qom_set_bool(s, "/machine/soc/gpio", "gpioV7", false); - g_assert(qtest_qom_get_bool(s, "/machine/soc/gpio", "gpioV4")); - g_assert(!qtest_qom_get_bool(s, "/machine/soc/gpio", "gpioV5")); - g_assert(qtest_qom_get_bool(s, "/machine/soc/gpio", "gpioV6")); - g_assert(!qtest_qom_get_bool(s, "/machine/soc/gpio", "gpioV7")); + qtest_qom_set_bool(s, path, "gpioV4", true); + qtest_qom_set_bool(s, path, "gpioV5", false); + qtest_qom_set_bool(s, path, "gpioV6", true); + qtest_qom_set_bool(s, path, "gpioV7", false); + g_assert(qtest_qom_get_bool(s, path, "gpioV4")); + g_assert(!qtest_qom_get_bool(s, path, "gpioV5")); + g_assert(qtest_qom_get_bool(s, path, "gpioV6")); + g_assert(!qtest_qom_get_bool(s, path, "gpioV7")); + + /* + * Testing the gpio-set[%d] properties, using individual gpio boolean + * properties to do cross check. + * We use gpioR4-7 for test, Setting them to be 0b1010. + */ + qtest_qom_set_uint32(s, path, "gpio-set[4]", 0x0); + g_assert(qtest_qom_get_uint32(s, path, "gpio-set[4]") == 0x0); + qtest_qom_set_uint32(s, path, "gpio-set[4]", 0xa000); + g_assert(qtest_qom_get_uint32(s, path, "gpio-set[4]") == 0xa000); + + g_assert(!qtest_qom_get_bool(s, path, "gpioR4")); + g_assert(qtest_qom_get_bool(s, path, "gpioR5")); + g_assert(!qtest_qom_get_bool(s, path, "gpioR6")); + g_assert(qtest_qom_get_bool(s, path, "gpioR7")); + + /* + * Testing the invalid indexing, the response info should contain following + * info: + * {key: "class", value: "GenericError"} + * + * For pins, it should follow "gpio%2[A-Z]%1d" or "gpio%3[18A-E]%1d" format. + */ + const char error_msg[] = "GenericError"; + const char error_msg_key[] = "class"; + + g_assert(qtest_qom_check_error(s, path, "gpioR+1", error_msg, + error_msg_key)); + g_assert(qtest_qom_check_error(s, path, "gpio-set[99]", error_msg, + error_msg_key)); + g_assert(qtest_qom_check_error(s, path, "gpio-set[-3]", error_msg, + error_msg_key)); } static void test_set_input_pins(const void *data) From fa42484ee1553aa8a8b97aa933315f7e0b9dba2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 16 Oct 2025 23:24:37 +0200 Subject: [PATCH 1777/1794] hw/arm/aspeed: ast2600-evb: Use w25q512jv flash model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ast2600-evb machine model is using the "mx66u51235f" flash model, which has issues with recent Linux kernels (6.15+) when reading SFDP data. Change the flash model to "w25q512jv", which is the model present on some ast2600a3 EVB board and is known to work correctly with recent kernels. Adjust the corresponding qtest to reflect the new JEDEC ID of the w25q512jv flash. Reviewed-by: Jamin Lin Link: https://lore.kernel.org/qemu-devel/20251016212437.1046135-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 4 ++-- tests/qtest/aspeed_smc-test.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 21ee62f75044d..1bc9e534ba49b 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1592,8 +1592,8 @@ static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, amc->soc_name = "ast2600-a3"; amc->hw_strap1 = AST2600_EVB_HW_STRAP1; amc->hw_strap2 = AST2600_EVB_HW_STRAP2; - amc->fmc_model = "mx66u51235f"; - amc->spi_model = "mx66u51235f"; + amc->fmc_model = "w25q512jv"; + amc->spi_model = "w25q512jv"; amc->num_cs = 1; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON | ASPEED_MAC2_ON | ASPEED_MAC3_ON; diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index 52a00e6f0a7e1..50a87e6250016 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -134,10 +134,10 @@ static void test_ast2600_evb(AspeedSMCTestData *data) "-drive file=%s,format=raw,if=mtd", data->tmp_path); - /* fmc cs0 with mx66u51235f flash */ + /* fmc cs0 with w25q512jv flash */ data->flash_base = 0x20000000; data->spi_base = 0x1E620000; - data->jedec_id = 0xc2253a; + data->jedec_id = 0xef4020; data->cs = 0; data->node = "/machine/soc/fmc/ssi.0/child[0]"; /* beyond 16MB */ From d7bd42a740d0e8887540d7b450d0bdb2d6ba31ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Oct 2025 13:04:27 +0200 Subject: [PATCH 1778/1794] hw/arm/aspeed: Remove ast2700fc self-aliasing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove pointless alias to the very same machine: $ qemu-system-aarch64 -M help | fgrep ast2700fc ast2700fc ast2700 full core support (alias of ast2700fc) ast2700fc ast2700 full core support Fixes: a74faf35efc ("hw/arm: Introduce ASPEED AST2700 A1 full core machine") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251021110427.93991-1-philmd@linaro.org Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index 7be2e849274df..580ac5f7a1249 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -207,7 +207,6 @@ static void ast2700fc_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->alias = "ast2700fc"; mc->desc = "ast2700 full core support"; mc->init = ast2700fc_init; mc->no_floppy = 1; From 0305ee82754aba1f312fb1fc6ad613219f697db7 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 19 Oct 2025 23:03:03 +0200 Subject: [PATCH 1779/1794] tests/qtest/ds1338: Reuse from_bcd() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit from_bcd() is a public API function which can be unit-tested. Reuse it to avoid code duplication. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Fabiano Rosas Message-ID: <20251019210303.104718-11-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- tests/qtest/ds1338-test.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/qtest/ds1338-test.c b/tests/qtest/ds1338-test.c index d12424d27f676..b8d0e65ec44c6 100644 --- a/tests/qtest/ds1338-test.c +++ b/tests/qtest/ds1338-test.c @@ -18,16 +18,12 @@ */ #include "qemu/osdep.h" +#include "qemu/bcd.h" #include "libqtest.h" #include "libqos/i2c.h" #define DS1338_ADDR 0x68 -static inline uint8_t bcd2bin(uint8_t x) -{ - return ((x) & 0x0f) + ((x) >> 4) * 10; -} - static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) { QI2CDevice *i2cdev = (QI2CDevice *)obj; @@ -39,9 +35,9 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) i2c_read_block(i2cdev, 0, resp, sizeof(resp)); /* check retrieved time against local time */ - g_assert_cmpuint(bcd2bin(resp[4]), == , tm_ptr->tm_mday); - g_assert_cmpuint(bcd2bin(resp[5]), == , 1 + tm_ptr->tm_mon); - g_assert_cmpuint(2000 + bcd2bin(resp[6]), == , 1900 + tm_ptr->tm_year); + g_assert_cmpuint(from_bcd(resp[4]), == , tm_ptr->tm_mday); + g_assert_cmpuint(from_bcd(resp[5]), == , 1 + tm_ptr->tm_mon); + g_assert_cmpuint(2000 + from_bcd(resp[6]), == , 1900 + tm_ptr->tm_year); } static void ds1338_register_nodes(void) From 047b721c60f592bdd6abbc69ed2f63316e061fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 21 Oct 2025 13:02:32 +0400 Subject: [PATCH 1780/1794] hw/audio: improve error reports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The -audiodev argument is 'model=..', use same terminology. Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251021090317.425409-2-marcandre.lureau@redhat.com> [PMD: Fixed checkpatch.pl issues] Signed-off-by: Philippe Mathieu-Daudé --- hw/audio/soundhw.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/hw/audio/soundhw.c b/hw/audio/soundhw.c index d18fd9fa05153..2a41e108c02fb 100644 --- a/hw/audio/soundhw.c +++ b/hw/audio/soundhw.c @@ -70,13 +70,13 @@ void show_valid_soundhw(void) struct soundhw *c; if (soundhw_count) { - printf("Valid sound card names (comma separated):\n"); - for (c = soundhw; c->name; ++c) { - printf ("%-11s %s\n", c->name, c->descr); - } + printf("Valid audio device model names:\n"); + for (c = soundhw; c->name; ++c) { + printf("%-11s %s\n", c->name, c->descr); + } } else { - printf("Machine has no user-selectable audio hardware " - "(it may or may not have always-present audio hardware).\n"); + printf("Machine has no user-selectable audio hardware " + "(it may or may not have always-present audio hardware).\n"); } } @@ -88,7 +88,7 @@ void select_soundhw(const char *name, const char *audiodev) struct soundhw *c; if (selected) { - error_report("only one -soundhw option is allowed"); + error_report("only one -audio option is allowed"); exit(1); } @@ -101,7 +101,7 @@ void select_soundhw(const char *name, const char *audiodev) } if (!c->name) { - error_report("Unknown sound card name `%s'", name); + error_report("Unknown audio device model `%s'", name); show_valid_soundhw(); exit(1); } @@ -140,4 +140,3 @@ void soundhw_init(void) c->init_pci(pci_bus, audiodev_id); } } - From 74d3a119ef642477a548360476c6b566a0fe6744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 21 Oct 2025 13:02:33 +0400 Subject: [PATCH 1781/1794] hw/audio: rename model list function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251021090317.425409-3-marcandre.lureau@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/audio/soundhw.c | 4 ++-- include/hw/audio/soundhw.h | 2 +- system/vl.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/audio/soundhw.c b/hw/audio/soundhw.c index 2a41e108c02fb..63a685563520e 100644 --- a/hw/audio/soundhw.c +++ b/hw/audio/soundhw.c @@ -65,7 +65,7 @@ void deprecated_register_soundhw(const char *name, const char *descr, soundhw_count++; } -void show_valid_soundhw(void) +void audio_print_available_models(void) { struct soundhw *c; @@ -102,7 +102,7 @@ void select_soundhw(const char *name, const char *audiodev) if (!c->name) { error_report("Unknown audio device model `%s'", name); - show_valid_soundhw(); + audio_print_available_models(); exit(1); } } diff --git a/include/hw/audio/soundhw.h b/include/hw/audio/soundhw.h index 474c5ff94e584..83b30110834fb 100644 --- a/include/hw/audio/soundhw.h +++ b/include/hw/audio/soundhw.h @@ -7,7 +7,7 @@ void deprecated_register_soundhw(const char *name, const char *descr, int isa, const char *typename); void soundhw_init(void); -void show_valid_soundhw(void); +void audio_print_available_models(void); void select_soundhw(const char *name, const char *audiodev); #endif diff --git a/system/vl.c b/system/vl.c index fd98ea52d9ce0..17bbc092c8777 100644 --- a/system/vl.c +++ b/system/vl.c @@ -3078,7 +3078,7 @@ void qemu_init(int argc, char **argv) model = g_strdup(qdict_get_str(dict, "model")); qdict_del(dict, "model"); if (is_help_option(model)) { - show_valid_soundhw(); + audio_print_available_models(); exit(0); } } From 0347f9e0e1caaaf6fb6e7f53b5f90ced6a6ec5e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 21 Oct 2025 13:02:34 +0400 Subject: [PATCH 1782/1794] hw/audio: remove global pcspk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is no longer used since commit 6033b9ecd4 ("pc: remove -soundhw pcspk") Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251021090317.425409-4-marcandre.lureau@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/audio/pcspk.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index f8020593b0b3c..610a5b216266f 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -61,7 +61,6 @@ struct PCSpkState { }; static const char *s_spk = "pcspk"; -static PCSpkState *pcspk_state; static inline void generate_samples(PCSpkState *s) { @@ -200,8 +199,6 @@ static void pcspk_realizefn(DeviceState *dev, Error **errp) if (s->card.state && AUD_register_card(s_spk, &s->card, errp)) { pcspk_audio_init(s); } - - pcspk_state = s; } static bool migrate_needed(void *opaque) @@ -237,7 +234,6 @@ static void pcspk_class_initfn(ObjectClass *klass, const void *data) set_bit(DEVICE_CATEGORY_SOUND, dc->categories); dc->vmsd = &vmstate_spk; device_class_set_props(dc, pcspk_properties); - /* Reason: realize sets global pcspk_state */ /* Reason: pit object link */ dc->user_creatable = false; } From 0c35f5bf86813f085e8a5053a90ebac0bc33e3ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 21 Oct 2025 13:02:35 +0400 Subject: [PATCH 1783/1794] hw/pcspk: use explicitly the required PIT types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251021090317.425409-5-marcandre.lureau@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/audio/pcspk.c | 2 +- hw/timer/i8254_common.c | 6 ++---- include/hw/timer/i8254.h | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index 610a5b216266f..9cf78ff55fa57 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -51,7 +51,7 @@ struct PCSpkState { uint8_t sample_buf[PCSPK_BUF_LEN]; QEMUSoundCard card; SWVoiceOut *voice; - void *pit; + PITCommonState *pit; unsigned int pit_count; unsigned int samples; unsigned int play_pos; diff --git a/hw/timer/i8254_common.c b/hw/timer/i8254_common.c index ad091594cdeeb..419d4cd6e572e 100644 --- a/hw/timer/i8254_common.c +++ b/hw/timer/i8254_common.c @@ -32,9 +32,8 @@ #include "migration/vmstate.h" /* val must be 0 or 1 */ -void pit_set_gate(ISADevice *dev, int channel, int val) +void pit_set_gate(PITCommonState *pit, int channel, int val) { - PITCommonState *pit = PIT_COMMON(dev); PITChannelState *s = &pit->channels[channel]; PITCommonClass *c = PIT_COMMON_GET_CLASS(pit); @@ -139,9 +138,8 @@ void pit_get_channel_info_common(PITCommonState *s, PITChannelState *sc, info->out = pit_get_out(sc, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); } -void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info) +void pit_get_channel_info(PITCommonState *pit, int channel, PITChannelInfo *info) { - PITCommonState *pit = PIT_COMMON(dev); PITChannelState *s = &pit->channels[channel]; PITCommonClass *c = PIT_COMMON_GET_CLASS(pit); diff --git a/include/hw/timer/i8254.h b/include/hw/timer/i8254.h index 8402caad30733..f7148d92865f8 100644 --- a/include/hw/timer/i8254.h +++ b/include/hw/timer/i8254.h @@ -75,7 +75,7 @@ static inline ISADevice *kvm_pit_init(ISABus *bus, int base) return d; } -void pit_set_gate(ISADevice *dev, int channel, int val); -void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info); +void pit_set_gate(PITCommonState *pit, int channel, int val); +void pit_get_channel_info(PITCommonState *pit, int channel, PITChannelInfo *info); #endif /* HW_I8254_H */ From 64a013e18bbd02c26a7ca2b2c4c1a14d77058749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 21 Oct 2025 13:02:36 +0400 Subject: [PATCH 1784/1794] hw/pcspk: make 'pit' a class property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should be functionally equivalent. (for some reason, the device property was convert to an object instance property in commit 873b4d3f0571) Signed-off-by: Marc-André Lureau Message-ID: <20251021090317.425409-6-marcandre.lureau@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé --- hw/audio/pcspk.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index 9cf78ff55fa57..7b716c08efc95 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -182,11 +182,6 @@ static void pcspk_initfn(Object *obj) PCSpkState *s = PC_SPEAKER(obj); memory_region_init_io(&s->ioport, OBJECT(s), &pcspk_io_ops, s, "pcspk", 1); - - object_property_add_link(obj, "pit", TYPE_PIT_COMMON, - (Object **)&s->pit, - qdev_prop_allow_set_link_before_realize, - 0); } static void pcspk_realizefn(DeviceState *dev, Error **errp) @@ -224,6 +219,7 @@ static const Property pcspk_properties[] = { DEFINE_AUDIO_PROPERTIES(PCSpkState, card), DEFINE_PROP_UINT32("iobase", PCSpkState, iobase, 0x61), DEFINE_PROP_BOOL("migrate", PCSpkState, migrate, true), + DEFINE_PROP_LINK("pit", PCSpkState, pit, TYPE_PIT_COMMON, PITCommonState *), }; static void pcspk_class_initfn(ObjectClass *klass, const void *data) From 840c2c4ed9645fe729321a53d76c5abd3fecc67b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 21 Oct 2025 13:02:37 +0400 Subject: [PATCH 1785/1794] hw/pcspk: check the "pit" is set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't let the user create a "isa-pcspk" via -device yet (in theory, we could, and fallback on a lookup PIT), but we can add some safety checks that the property was correctly set nonetheless. Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251021090317.425409-7-marcandre.lureau@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/audio/pcspk.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index 7b716c08efc95..54fcf632e6a67 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -189,6 +189,11 @@ static void pcspk_realizefn(DeviceState *dev, Error **errp) ISADevice *isadev = ISA_DEVICE(dev); PCSpkState *s = PC_SPEAKER(dev); + if (!s->pit) { + error_setg(errp, "pcspk: No \"pit\" set or available"); + return; + } + isa_register_ioport(isadev, &s->ioport, s->iobase); if (s->card.state && AUD_register_card(s_spk, &s->card, errp)) { From 55634cdf115f31ec26b66d622e6ea50354805b16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 21 Oct 2025 13:03:02 +0400 Subject: [PATCH 1786/1794] hw/audio: replace AUD_log() usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AUD_log() is just printf(stderr, "prefix: "..), we can use error_report() or warn_report() appropriately instead. Ideally it should be converted to traces, but there are many places to convert, this is left for another day. Avoid bit-rot by using conditionals. The patch could be splitted if necessary. Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251021090317.425409-32-marcandre.lureau@redhat.com> [PMD: Fixed checkpatch.pl issues] Signed-off-by: Philippe Mathieu-Daudé --- hw/audio/ac97.c | 124 +++++++++++++++++++++++---------------------- hw/audio/adlib.c | 24 ++++----- hw/audio/cs4231a.c | 44 ++++++++-------- hw/audio/es1370.c | 24 ++++----- hw/audio/gus.c | 25 ++++----- hw/audio/pcspk.c | 2 +- hw/audio/sb16.c | 115 +++++++++++++++++++++-------------------- 7 files changed, 180 insertions(+), 178 deletions(-) diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index eb7a847080da9..828333b66af40 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -26,6 +26,7 @@ #include "qemu/module.h" #include "system/dma.h" #include "qom/object.h" +#include "qemu/error-report.h" #include "ac97.h" #define SOFT_VOLUME @@ -141,11 +142,12 @@ enum { BUP_LAST = 2 }; -#ifdef DEBUG_AC97 -#define dolog(...) AUD_log("ac97", __VA_ARGS__) -#else -#define dolog(...) -#endif +#define DEBUG_AC97 0 +#define dolog(fmt, ...) do { \ + if (DEBUG_AC97) { \ + error_report("ac97: " fmt, ##__VA_ARGS__); \ + } \ + } while (0) #define MKREGS(prefix, start) \ enum { \ @@ -190,7 +192,7 @@ static void fetch_bd(AC97LinkState *s, AC97BusMasterRegs *r) r->bd.addr = le32_to_cpu(*(uint32_t *) &b[0]) & ~3; r->bd.ctl_len = le32_to_cpu(*(uint32_t *) &b[4]); r->picb = r->bd.ctl_len & 0xffff; - dolog("bd %2d addr=0x%x ctl=0x%06x len=0x%x(%d bytes)\n", + dolog("bd %2d addr=0x%x ctl=0x%06x len=0x%x(%d bytes)", r->civ, r->bd.addr, r->bd.ctl_len >> 16, r->bd.ctl_len & 0xffff, (r->bd.ctl_len & 0xffff) << 1); } @@ -222,7 +224,7 @@ static void update_sr(AC97LinkState *s, AC97BusMasterRegs *r, uint32_t new_sr) r->sr = new_sr; - dolog("IOC%d LVB%d sr=0x%x event=%d level=%d\n", + dolog("IOC%d LVB%d sr=0x%x event=%d level=%d", r->sr & SR_BCIS, r->sr & SR_LVBCI, r->sr, event, level); if (!event) { @@ -231,11 +233,11 @@ static void update_sr(AC97LinkState *s, AC97BusMasterRegs *r, uint32_t new_sr) if (level) { s->glob_sta |= masks[r - s->bm_regs]; - dolog("set irq level=1\n"); + dolog("set irq level=1"); pci_irq_assert(&s->dev); } else { s->glob_sta &= ~masks[r - s->bm_regs]; - dolog("set irq level=0\n"); + dolog("set irq level=0"); pci_irq_deassert(&s->dev); } } @@ -256,14 +258,14 @@ static void voice_set_active(AC97LinkState *s, int bm_index, int on) break; default: - AUD_log("ac97", "invalid bm_index(%d) in voice_set_active", bm_index); + error_report("ac97: invalid bm_index(%d) in voice_set_active", bm_index); break; } } static void reset_bm_regs(AC97LinkState *s, AC97BusMasterRegs *r) { - dolog("reset_bm_regs\n"); + dolog("reset_bm_regs"); r->bdbar = 0; r->civ = 0; r->lvi = 0; @@ -281,7 +283,7 @@ static void reset_bm_regs(AC97LinkState *s, AC97BusMasterRegs *r) static void mixer_store(AC97LinkState *s, uint32_t i, uint16_t v) { if (i + 2 > sizeof(s->mixer_data)) { - dolog("mixer_store: index %d out of bounds %zd\n", + dolog("mixer_store: index %d out of bounds %zd", i, sizeof(s->mixer_data)); return; } @@ -295,7 +297,7 @@ static uint16_t mixer_load(AC97LinkState *s, uint32_t i) uint16_t val = 0xffff; if (i + 2 > sizeof(s->mixer_data)) { - dolog("mixer_load: index %d out of bounds %zd\n", + dolog("mixer_load: index %d out of bounds %zd", i, sizeof(s->mixer_data)); } else { val = s->mixer_data[i + 0] | (s->mixer_data[i + 1] << 8); @@ -460,7 +462,7 @@ static void mixer_reset(AC97LinkState *s) { uint8_t active[LAST_INDEX]; - dolog("mixer_reset\n"); + dolog("mixer_reset"); memset(s->mixer_data, 0, sizeof(s->mixer_data)); memset(active, 0, sizeof(active)); mixer_store(s, AC97_Reset, 0x0000); /* 6940 */ @@ -508,7 +510,7 @@ static void mixer_reset(AC97LinkState *s) static uint32_t nam_readb(void *opaque, uint32_t addr) { AC97LinkState *s = opaque; - dolog("U nam readb 0x%x\n", addr); + dolog("U nam readb 0x%x", addr); s->cas = 0; return ~0U; } @@ -523,7 +525,7 @@ static uint32_t nam_readw(void *opaque, uint32_t addr) static uint32_t nam_readl(void *opaque, uint32_t addr) { AC97LinkState *s = opaque; - dolog("U nam readl 0x%x\n", addr); + dolog("U nam readl 0x%x", addr); s->cas = 0; return ~0U; } @@ -535,7 +537,7 @@ static uint32_t nam_readl(void *opaque, uint32_t addr) static void nam_writeb(void *opaque, uint32_t addr, uint32_t val) { AC97LinkState *s = opaque; - dolog("U nam writeb 0x%x <- 0x%x\n", addr, val); + dolog("U nam writeb 0x%x <- 0x%x", addr, val); s->cas = 0; } @@ -563,10 +565,10 @@ static void nam_writew(void *opaque, uint32_t addr, uint32_t val) break; case AC97_Vendor_ID1: case AC97_Vendor_ID2: - dolog("Attempt to write vendor ID to 0x%x\n", val); + dolog("Attempt to write vendor ID to 0x%x", val); break; case AC97_Extended_Audio_ID: - dolog("Attempt to write extended audio ID to 0x%x\n", val); + dolog("Attempt to write extended audio ID to 0x%x", val); break; case AC97_Extended_Audio_Ctrl_Stat: if (!(val & EACS_VRA)) { @@ -579,36 +581,36 @@ static void nam_writew(void *opaque, uint32_t addr, uint32_t val) mixer_store(s, AC97_MIC_ADC_Rate, 0xbb80); open_voice(s, MC_INDEX, 48000); } - dolog("Setting extended audio control to 0x%x\n", val); + dolog("Setting extended audio control to 0x%x", val); mixer_store(s, AC97_Extended_Audio_Ctrl_Stat, val); break; case AC97_PCM_Front_DAC_Rate: if (mixer_load(s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) { mixer_store(s, addr, val); - dolog("Set front DAC rate to %d\n", val); + dolog("Set front DAC rate to %d", val); open_voice(s, PO_INDEX, val); } else { - dolog("Attempt to set front DAC rate to %d, but VRA is not set\n", + dolog("Attempt to set front DAC rate to %d, but VRA is not set", val); } break; case AC97_MIC_ADC_Rate: if (mixer_load(s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRM) { mixer_store(s, addr, val); - dolog("Set MIC ADC rate to %d\n", val); + dolog("Set MIC ADC rate to %d", val); open_voice(s, MC_INDEX, val); } else { - dolog("Attempt to set MIC ADC rate to %d, but VRM is not set\n", + dolog("Attempt to set MIC ADC rate to %d, but VRM is not set", val); } break; case AC97_PCM_LR_ADC_Rate: if (mixer_load(s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) { mixer_store(s, addr, val); - dolog("Set front LR ADC rate to %d\n", val); + dolog("Set front LR ADC rate to %d", val); open_voice(s, PI_INDEX, val); } else { - dolog("Attempt to set LR ADC rate to %d, but VRA is not set\n", + dolog("Attempt to set LR ADC rate to %d, but VRA is not set", val); } break; @@ -630,7 +632,7 @@ static void nam_writew(void *opaque, uint32_t addr, uint32_t val) /* None of the features in these regs are emulated, so they are RO */ break; default: - dolog("U nam writew 0x%x <- 0x%x\n", addr, val); + dolog("U nam writew 0x%x <- 0x%x", addr, val); mixer_store(s, addr, val); break; } @@ -639,7 +641,7 @@ static void nam_writew(void *opaque, uint32_t addr, uint32_t val) static void nam_writel(void *opaque, uint32_t addr, uint32_t val) { AC97LinkState *s = opaque; - dolog("U nam writel 0x%x <- 0x%x\n", addr, val); + dolog("U nam writel 0x%x <- 0x%x", addr, val); s->cas = 0; } @@ -655,7 +657,7 @@ static uint32_t nabm_readb(void *opaque, uint32_t addr) switch (addr) { case CAS: - dolog("CAS %d\n", s->cas); + dolog("CAS %d", s->cas); val = s->cas; s->cas = 1; break; @@ -664,38 +666,38 @@ static uint32_t nabm_readb(void *opaque, uint32_t addr) case MC_CIV: r = &s->bm_regs[GET_BM(addr)]; val = r->civ; - dolog("CIV[%d] -> 0x%x\n", GET_BM(addr), val); + dolog("CIV[%d] -> 0x%x", GET_BM(addr), val); break; case PI_LVI: case PO_LVI: case MC_LVI: r = &s->bm_regs[GET_BM(addr)]; val = r->lvi; - dolog("LVI[%d] -> 0x%x\n", GET_BM(addr), val); + dolog("LVI[%d] -> 0x%x", GET_BM(addr), val); break; case PI_PIV: case PO_PIV: case MC_PIV: r = &s->bm_regs[GET_BM(addr)]; val = r->piv; - dolog("PIV[%d] -> 0x%x\n", GET_BM(addr), val); + dolog("PIV[%d] -> 0x%x", GET_BM(addr), val); break; case PI_CR: case PO_CR: case MC_CR: r = &s->bm_regs[GET_BM(addr)]; val = r->cr; - dolog("CR[%d] -> 0x%x\n", GET_BM(addr), val); + dolog("CR[%d] -> 0x%x", GET_BM(addr), val); break; case PI_SR: case PO_SR: case MC_SR: r = &s->bm_regs[GET_BM(addr)]; val = r->sr & 0xff; - dolog("SRb[%d] -> 0x%x\n", GET_BM(addr), val); + dolog("SRb[%d] -> 0x%x", GET_BM(addr), val); break; default: - dolog("U nabm readb 0x%x -> 0x%x\n", addr, val); + dolog("U nabm readb 0x%x -> 0x%x", addr, val); break; } return val; @@ -713,17 +715,17 @@ static uint32_t nabm_readw(void *opaque, uint32_t addr) case MC_SR: r = &s->bm_regs[GET_BM(addr)]; val = r->sr; - dolog("SR[%d] -> 0x%x\n", GET_BM(addr), val); + dolog("SR[%d] -> 0x%x", GET_BM(addr), val); break; case PI_PICB: case PO_PICB: case MC_PICB: r = &s->bm_regs[GET_BM(addr)]; val = r->picb; - dolog("PICB[%d] -> 0x%x\n", GET_BM(addr), val); + dolog("PICB[%d] -> 0x%x", GET_BM(addr), val); break; default: - dolog("U nabm readw 0x%x -> 0x%x\n", addr, val); + dolog("U nabm readw 0x%x -> 0x%x", addr, val); break; } return val; @@ -741,14 +743,14 @@ static uint32_t nabm_readl(void *opaque, uint32_t addr) case MC_BDBAR: r = &s->bm_regs[GET_BM(addr)]; val = r->bdbar; - dolog("BMADDR[%d] -> 0x%x\n", GET_BM(addr), val); + dolog("BMADDR[%d] -> 0x%x", GET_BM(addr), val); break; case PI_CIV: case PO_CIV: case MC_CIV: r = &s->bm_regs[GET_BM(addr)]; val = r->civ | (r->lvi << 8) | (r->sr << 16); - dolog("CIV LVI SR[%d] -> 0x%x, 0x%x, 0x%x\n", GET_BM(addr), + dolog("CIV LVI SR[%d] -> 0x%x, 0x%x, 0x%x", GET_BM(addr), r->civ, r->lvi, r->sr); break; case PI_PICB: @@ -756,19 +758,19 @@ static uint32_t nabm_readl(void *opaque, uint32_t addr) case MC_PICB: r = &s->bm_regs[GET_BM(addr)]; val = r->picb | (r->piv << 16) | (r->cr << 24); - dolog("PICB PIV CR[%d] -> 0x%x 0x%x 0x%x 0x%x\n", GET_BM(addr), + dolog("PICB PIV CR[%d] -> 0x%x 0x%x 0x%x 0x%x", GET_BM(addr), val, r->picb, r->piv, r->cr); break; case GLOB_CNT: val = s->glob_cnt; - dolog("glob_cnt -> 0x%x\n", val); + dolog("glob_cnt -> 0x%x", val); break; case GLOB_STA: val = s->glob_sta | GS_S0CR; - dolog("glob_sta -> 0x%x\n", val); + dolog("glob_sta -> 0x%x", val); break; default: - dolog("U nabm readl 0x%x -> 0x%x\n", addr, val); + dolog("U nabm readl 0x%x -> 0x%x", addr, val); break; } return val; @@ -795,7 +797,7 @@ static void nabm_writeb(void *opaque, uint32_t addr, uint32_t val) fetch_bd(s, r); } r->lvi = val % 32; - dolog("LVI[%d] <- 0x%x\n", GET_BM(addr), val); + dolog("LVI[%d] <- 0x%x", GET_BM(addr), val); break; case PI_CR: case PO_CR: @@ -816,7 +818,7 @@ static void nabm_writeb(void *opaque, uint32_t addr, uint32_t val) voice_set_active(s, r - s->bm_regs, 1); } } - dolog("CR[%d] <- 0x%x (cr 0x%x)\n", GET_BM(addr), val, r->cr); + dolog("CR[%d] <- 0x%x (cr 0x%x)", GET_BM(addr), val, r->cr); break; case PI_SR: case PO_SR: @@ -824,10 +826,10 @@ static void nabm_writeb(void *opaque, uint32_t addr, uint32_t val) r = &s->bm_regs[GET_BM(addr)]; r->sr |= val & ~(SR_RO_MASK | SR_WCLEAR_MASK); update_sr(s, r, r->sr & ~(val & SR_WCLEAR_MASK)); - dolog("SR[%d] <- 0x%x (sr 0x%x)\n", GET_BM(addr), val, r->sr); + dolog("SR[%d] <- 0x%x (sr 0x%x)", GET_BM(addr), val, r->sr); break; default: - dolog("U nabm writeb 0x%x <- 0x%x\n", addr, val); + dolog("U nabm writeb 0x%x <- 0x%x", addr, val); break; } } @@ -844,10 +846,10 @@ static void nabm_writew(void *opaque, uint32_t addr, uint32_t val) r = &s->bm_regs[GET_BM(addr)]; r->sr |= val & ~(SR_RO_MASK | SR_WCLEAR_MASK); update_sr(s, r, r->sr & ~(val & SR_WCLEAR_MASK)); - dolog("SR[%d] <- 0x%x (sr 0x%x)\n", GET_BM(addr), val, r->sr); + dolog("SR[%d] <- 0x%x (sr 0x%x)", GET_BM(addr), val, r->sr); break; default: - dolog("U nabm writew 0x%x <- 0x%x\n", addr, val); + dolog("U nabm writew 0x%x <- 0x%x", addr, val); break; } } @@ -863,22 +865,22 @@ static void nabm_writel(void *opaque, uint32_t addr, uint32_t val) case MC_BDBAR: r = &s->bm_regs[GET_BM(addr)]; r->bdbar = val & ~3; - dolog("BDBAR[%d] <- 0x%x (bdbar 0x%x)\n", GET_BM(addr), val, r->bdbar); + dolog("BDBAR[%d] <- 0x%x (bdbar 0x%x)", GET_BM(addr), val, r->bdbar); break; case GLOB_CNT: /* TODO: Handle WR or CR being set (warm/cold reset requests) */ if (!(val & (GC_WR | GC_CR))) { s->glob_cnt = val & GC_VALID_MASK; } - dolog("glob_cnt <- 0x%x (glob_cnt 0x%x)\n", val, s->glob_cnt); + dolog("glob_cnt <- 0x%x (glob_cnt 0x%x)", val, s->glob_cnt); break; case GLOB_STA: s->glob_sta &= ~(val & GS_WCLEAR_MASK); s->glob_sta |= (val & ~(GS_WCLEAR_MASK | GS_RO_MASK)) & GS_VALID_MASK; - dolog("glob_sta <- 0x%x (glob_sta 0x%x)\n", val, s->glob_sta); + dolog("glob_sta <- 0x%x (glob_sta 0x%x)", val, s->glob_sta); break; default: - dolog("U nabm writel 0x%x <- 0x%x\n", addr, val); + dolog("U nabm writel 0x%x <- 0x%x", addr, val); break; } } @@ -903,7 +905,7 @@ static int write_audio(AC97LinkState *s, AC97BusMasterRegs *r, to_copy = MIN(temp, sizeof(tmpbuf)); pci_dma_read(&s->dev, addr, tmpbuf, to_copy); copied = AUD_write(s->voice_po, tmpbuf, to_copy); - dolog("write_audio max=%x to_copy=%x copied=%x\n", + dolog("write_audio max=%x to_copy=%x copied=%x", max, to_copy, copied); if (!copied) { *stop = 1; @@ -916,7 +918,7 @@ static int write_audio(AC97LinkState *s, AC97BusMasterRegs *r, if (!temp) { if (to_copy < 4) { - dolog("whoops\n"); + dolog("whoops"); s->last_samp = 0; } else { s->last_samp = *(uint32_t *)&tmpbuf[to_copy - 4]; @@ -929,7 +931,7 @@ static int write_audio(AC97LinkState *s, AC97BusMasterRegs *r, static void write_bup(AC97LinkState *s, int elapsed) { - dolog("write_bup\n"); + dolog("write_bup"); if (!(s->bup_flag & BUP_SET)) { if (s->bup_flag & BUP_LAST) { int i; @@ -997,7 +999,7 @@ static void transfer_audio(AC97LinkState *s, int index, int elapsed) int stop = 0; if (s->invalid_freq[index]) { - AUD_log("ac97", "attempt to use voice %d with invalid frequency %d\n", + error_report("ac97: attempt to use voice %d with invalid frequency %d", index, s->invalid_freq[index]); return; } @@ -1017,12 +1019,12 @@ static void transfer_audio(AC97LinkState *s, int index, int elapsed) int temp; if (!r->bd_valid) { - dolog("invalid bd\n"); + dolog("invalid bd"); fetch_bd(s, r); } if (!r->picb) { - dolog("fresh bd %d is empty 0x%x 0x%x\n", + dolog("fresh bd %d is empty 0x%x 0x%x", r->civ, r->bd.addr, r->bd.ctl_len); if (r->civ == r->lvi) { r->sr |= SR_DCH; /* CELV? */ @@ -1059,7 +1061,7 @@ static void transfer_audio(AC97LinkState *s, int index, int elapsed) } if (r->civ == r->lvi) { - dolog("Underrun civ (%d) == lvi (%d)\n", r->civ, r->lvi); + dolog("Underrun civ (%d) == lvi (%d)", r->civ, r->lvi); new_sr |= SR_LVBCI | SR_DCH | SR_CELV; stop = 1; diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c index 1f29a7e319df9..772435f04cd55 100644 --- a/hw/audio/adlib.c +++ b/hw/audio/adlib.c @@ -29,24 +29,24 @@ #include "audio/audio.h" #include "hw/isa/isa.h" #include "hw/qdev-properties.h" +#include "qemu/error-report.h" #include "qom/object.h" -//#define DEBUG +#define DEBUG 0 #define ADLIB_KILL_TIMERS 1 #define ADLIB_DESC "Yamaha YM3812 (OPL2)" -#ifdef DEBUG +#if DEBUG #include "qemu/timer.h" #endif -#define dolog(...) AUD_log ("adlib", __VA_ARGS__) -#ifdef DEBUG -#define ldebug(...) dolog (__VA_ARGS__) -#else -#define ldebug(...) -#endif +#define ldebug(fmt, ...) do { \ + if (DEBUG) { \ + error_report("adlib: " fmt, ##__VA_ARGS__); \ + } \ + } while (0) #include "fmopl.h" #define SHIFT 1 @@ -64,7 +64,7 @@ struct AdlibState { int enabled; int active; int bufpos; -#ifdef DEBUG +#if DEBUG int64_t exp[2]; #endif int16_t *mixbuf; @@ -92,7 +92,7 @@ static void adlib_kill_timers (AdlibState *s) delta = AUD_get_elapsed_usec_out (s->voice, &s->ats); ldebug ( - "delta = %f dexp = %f expired => %d\n", + "delta = %f dexp = %f expired => %d", delta / 1000000.0, s->dexp[i] / 1000000.0, delta >= s->dexp[i] @@ -131,7 +131,7 @@ static void timer_handler (void *opaque, int c, double interval_Sec) { AdlibState *s = opaque; unsigned n = c & 1; -#ifdef DEBUG +#if DEBUG double interval; int64_t exp; #endif @@ -142,7 +142,7 @@ static void timer_handler (void *opaque, int c, double interval_Sec) } s->ticking[n] = 1; -#ifdef DEBUG +#if DEBUG interval = NANOSECONDS_PER_SECOND * interval_Sec; exp = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + interval; s->exp[n] = exp; diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c index 6dfff202ff924..7931fcfec8171 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -30,7 +30,7 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "qemu/timer.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "qom/object.h" @@ -43,21 +43,21 @@ More... */ -/* #define DEBUG */ +#define DEBUG 0 /* #define DEBUG_XLAW */ static struct { int aci_counter; } conf = {1}; -#ifdef DEBUG -#define dolog(...) AUD_log ("cs4231a", __VA_ARGS__) -#else -#define dolog(...) -#endif +#define dolog(fmt, ...) do { \ + if (DEBUG) { \ + error_report("cs4231a: " fmt, ##__VA_ARGS__); \ + } \ + } while (0) -#define lwarn(...) AUD_log ("cs4231a", "warning: " __VA_ARGS__) -#define lerr(...) AUD_log ("cs4231a", "error: " __VA_ARGS__) +#define lwarn(fmt, ...) warn_report("cs4231a: " fmt, ##__VA_ARGS__) +#define lerr(fmt, ...) error_report("cs4231a: " fmt, ##__VA_ARGS__) #define CS_REGS 16 #define CS_DREGS 32 @@ -284,7 +284,7 @@ static void cs_reset_voices (CSState *s, uint32_t val) as.freq = freqs[xtal][(val >> 1) & 7]; if (as.freq == -1) { - lerr ("unsupported frequency (val=%#x)\n", val); + lerr("unsupported frequency (val=0x%x)", val); goto error; } @@ -319,11 +319,11 @@ static void cs_reset_voices (CSState *s, uint32_t val) case 7: case 4: - lerr ("attempt to use reserved format value (%#x)\n", val); + lerr("attempt to use reserved format value (0x%x)", val); goto error; case 5: - lerr ("ADPCM 4 bit IMA compatible format is not supported\n"); + lerr("ADPCM 4 bit IMA compatible format is not supported"); goto error; } @@ -393,7 +393,7 @@ static uint64_t cs_read (void *opaque, hwaddr addr, unsigned size) ret = s->regs[saddr]; break; } - dolog ("read %d:%d -> %d\n", saddr, iaddr, ret); + dolog("read %d:%d -> %d", saddr, iaddr, ret); return ret; } @@ -425,7 +425,7 @@ static void cs_write (void *opaque, hwaddr addr, case RESERVED: case RESERVED_2: case RESERVED_3: - lwarn ("attempt to write %#x to reserved indirect register %d\n", + lwarn("attempt to write 0x%x to reserved indirect register %d", val, iaddr); break; @@ -439,7 +439,7 @@ static void cs_write (void *opaque, hwaddr addr, cs_reset_voices (s, val); } else { - lwarn ("[P]MCE(%#x, %#x) is not set, val=%#x\n", + lwarn("[P]MCE(0x%x, 0x%x) is not set, val=0x%x", s->regs[Index_Address], s->dregs[Alternate_Feature_Status], val); @@ -453,7 +453,7 @@ static void cs_write (void *opaque, hwaddr addr, val &= ~(1 << 5); /* D5 is reserved */ s->dregs[iaddr] = val; if (val & PPIO) { - lwarn ("PIO is not supported (%#x)\n", val); + lwarn("PIO is not supported (0x%x)", val); break; } if (val & PEN) { @@ -472,11 +472,11 @@ static void cs_write (void *opaque, hwaddr addr, break; case Error_Status_And_Initialization: - lwarn ("attempt to write to read only register %d\n", iaddr); + lwarn("attempt to write to read only register %d", iaddr); break; case MODE_And_ID: - dolog ("val=%#x\n", val); + dolog("val=0x%x", val); if (val & MODE2) s->dregs[iaddr] |= MODE2; else @@ -485,7 +485,7 @@ static void cs_write (void *opaque, hwaddr addr, case Alternate_Feature_Enable_I: if (val & TE) - lerr ("timer is not yet supported\n"); + lerr("timer is not yet supported"); s->dregs[iaddr] = val; break; @@ -499,7 +499,7 @@ static void cs_write (void *opaque, hwaddr addr, break; case Version_Chip_ID: - lwarn ("write to Version_Chip_ID register %#x\n", val); + lwarn("write to Version_Chip_ID register 0x%x", val); s->dregs[iaddr] = val; break; @@ -507,7 +507,7 @@ static void cs_write (void *opaque, hwaddr addr, s->dregs[iaddr] = val; break; } - dolog ("written value %#x to indirect register %d\n", val, iaddr); + dolog("written value 0x%x to indirect register %d", val, iaddr); break; case Status: @@ -519,7 +519,7 @@ static void cs_write (void *opaque, hwaddr addr, break; case PIO_Data: - lwarn ("attempt to write value %#x to PIO register\n", val); + lwarn("attempt to write value 0x%x to PIO register", val); break; } } diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index a6a32a6348bc0..6b0da0746ecba 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -32,7 +32,7 @@ #include "migration/vmstate.h" #include "qemu/cutils.h" #include "qemu/module.h" -#include "system/dma.h" +#include "qemu/error-report.h" #include "qom/object.h" #include "trace.h" @@ -190,7 +190,7 @@ static void print_ctl(uint32_t val) a(CDC_EN); a(SERR_DIS); #undef a - AUD_log("es1370", "ctl - PCLKDIV %d(DAC2 freq %d), freq %d,%s\n", + error_report("es1370: ctl - PCLKDIV %d(DAC2 freq %d), freq %d,%s", (val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV, DAC2_DIVTOSR((val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), dac1_samplerate[(val & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], @@ -226,7 +226,7 @@ static void print_sctl(uint32_t val) } #undef b #undef a - AUD_log("es1370", + error_report("es1370: " "%s p2_end_inc %d, p2_st_inc %d," " r1_fmt %s, p2_fmt %s, p1_fmt %s\n", buf, @@ -238,10 +238,10 @@ static void print_sctl(uint32_t val) } } -#define lwarn(...) \ +#define lwarn(fmt, ...) \ do { \ if (VERBOSE_ES1370) { \ - AUD_log("es1370: warning", __VA_ARGS__); \ + error_report("es1370: " fmt, ##__VA_ARGS__); \ } \ } while (0) @@ -502,10 +502,10 @@ static void es1370_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) break; case ES1370_REG_PHANTOM_FRAMECNT: - lwarn("writing to phantom frame count 0x%" PRIx64 "\n", val); + lwarn("writing to phantom frame count 0x%" PRIx64, val); break; case ES1370_REG_PHANTOM_FRAMEADR: - lwarn("writing to phantom frame address 0x%" PRIx64 "\n", val); + lwarn("writing to phantom frame address 0x%" PRIx64, val); break; case ES1370_REG_ADC_FRAMECNT: @@ -522,7 +522,7 @@ static void es1370_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) break; default: - lwarn("writel 0x%" PRIx64 " <- 0x%" PRIx64 "\n", addr, val); + lwarn("writel 0x%" PRIx64 " <- 0x%" PRIx64, addr, val); break; } } @@ -586,16 +586,16 @@ static uint64_t es1370_read(void *opaque, hwaddr addr, unsigned size) case ES1370_REG_PHANTOM_FRAMECNT: val = ~0U; - lwarn("reading from phantom frame count\n"); + lwarn("reading from phantom frame count"); break; case ES1370_REG_PHANTOM_FRAMEADR: val = ~0U; - lwarn("reading from phantom frame address\n"); + lwarn("reading from phantom frame address"); break; default: val = ~0U; - lwarn("readl 0x%" PRIx64 " -> 0x%x\n", addr, val); + lwarn("readl 0x%" PRIx64 " -> 0x%x", addr, val); break; } return val; @@ -677,7 +677,7 @@ static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel, * when the sample count reaches zero) or 1 for stop mode (set * interrupt and stop recording). */ - AUD_log ("es1370: warning", "non looping mode\n"); + warn_report("es1370: non looping mode"); } else { d->frame_cnt = size; diff --git a/hw/audio/gus.c b/hw/audio/gus.c index c36df0240fe23..91d07e0f81ccb 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -32,15 +32,16 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "gusemu.h" -#include "gustate.h" +#include "qemu/error-report.h" #include "qom/object.h" -#define dolog(...) AUD_log ("audio", __VA_ARGS__) -#ifdef DEBUG -#define ldebug(...) dolog (__VA_ARGS__) -#else -#define ldebug(...) -#endif +#define DEBUG 0 + +#define ldebug(fmt, ...) do { \ + if (DEBUG) { \ + error_report("gus: " fmt, ##__VA_ARGS__); \ + } \ + } while (0) #define TYPE_GUS "gus" OBJECT_DECLARE_SIMPLE_TYPE(GUSState, GUS) @@ -154,14 +155,14 @@ int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n) /* qemu_irq_lower (s->pic); */ qemu_irq_raise (s->pic); s->irqs += n; - ldebug ("irqrequest %d %d %d\n", hwirq, n, s->irqs); + ldebug("irqrequest %d %d %d", hwirq, n, s->irqs); return n; } void GUS_irqclear (GUSEmuState *emu, int hwirq) { GUSState *s = emu->opaque; - ldebug ("irqclear %d %d\n", hwirq, s->irqs); + ldebug("irqclear %d %d", hwirq, s->irqs); qemu_irq_lower (s->pic); s->irqs -= 1; #ifdef IRQ_STORM @@ -175,7 +176,7 @@ void GUS_dmarequest (GUSEmuState *emu) { GUSState *s = emu->opaque; IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); - ldebug ("dma request %d\n", der->gusdma); + ldebug("dma request %d", s->emu.gusdma); k->hold_DREQ(s->isa_dma, s->emu.gusdma); } @@ -186,13 +187,13 @@ static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) QEMU_UNINITIALIZED char tmpbuf[4096]; int pos = dma_pos, mode, left = dma_len - dma_pos; - ldebug ("read DMA %#x %d\n", dma_pos, dma_len); + ldebug("read DMA 0x%x %d", dma_pos, dma_len); mode = k->has_autoinitialization(s->isa_dma, s->emu.gusdma); while (left) { int to_copy = MIN ((size_t) left, sizeof (tmpbuf)); int copied; - ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos); + ldebug("left=%d to_copy=%d pos=%d", left, to_copy, pos); copied = k->read_memory(s->isa_dma, nchan, tmpbuf, pos, to_copy); gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied); left -= copied; diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index 54fcf632e6a67..a719912872acc 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -125,7 +125,7 @@ static int pcspk_audio_init(PCSpkState *s) s->voice = AUD_open_out(&s->card, s->voice, s_spk, s, pcspk_callback, &as); if (!s->voice) { - AUD_log(s_spk, "Could not open voice\n"); + error_report("pcspk: Could not open voice"); return -1; } diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index bac64118fef91..03c82f2777ec6 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -30,22 +30,21 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qemu/timer.h" +#include "qemu/error-report.h" #include "qemu/host-utils.h" #include "qemu/log.h" #include "qemu/module.h" #include "qapi/error.h" #include "qom/object.h" -#define dolog(...) AUD_log ("sb16", __VA_ARGS__) - -/* #define DEBUG */ +#define DEBUG 0 /* #define DEBUG_SB16_MOST */ -#ifdef DEBUG -#define ldebug(...) dolog (__VA_ARGS__) -#else -#define ldebug(...) -#endif +#define ldebug(fmt, ...) do { \ + if (DEBUG) { \ + error_report("sb16: " fmt, ##__VA_ARGS__); \ + } \ + } while (0) static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992."; @@ -157,7 +156,7 @@ static int irq_of_magic (int magic) #if 0 static void log_dsp (SB16State *dsp) { - ldebug ("%s:%s:%d:%s:dmasize=%d:freq=%d:const=%d:speaker=%d\n", + ldebug("%s:%s:%d:%s:dmasize=%d:freq=%d:const=%d:speaker=%d", dsp->fmt_stereo ? "Stereo" : "Mono", dsp->fmt_signed ? "Signed" : "Unsigned", dsp->fmt_bits, @@ -182,7 +181,7 @@ static void control (SB16State *s, int hold) IsaDmaClass *k = ISADMA_GET_CLASS(isa_dma); s->dma_running = hold; - ldebug ("hold %d high %d dma %d\n", hold, s->use_hdma, dma); + ldebug("hold %d high %d dma %d", hold, s->use_hdma, dma); if (hold) { k->hold_DREQ(isa_dma, dma); @@ -289,8 +288,8 @@ static void dma_cmd8 (SB16State *s, int mask, int dma_len) " alignment %d\n", s->block_size, s->align + 1); } - ldebug ("freq %d, stereo %d, sign %d, bits %d, " - "dma %d, auto %d, fifo %d, high %d\n", + ldebug("freq %d, stereo %d, sign %d, bits %d, " + "dma %d, auto %d, fifo %d, high %d", s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits, s->block_size, s->dma_auto, s->fifo, s->highspeed); @@ -337,8 +336,8 @@ static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len) s->block_size <<= s->fmt_stereo; } - ldebug ("freq %d, stereo %d, sign %d, bits %d, " - "dma %d, auto %d, fifo %d, high %d\n", + ldebug("freq %d, stereo %d, sign %d, bits %d, " + "dma %d, auto %d, fifo %d, high %d", s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits, s->block_size, s->dma_auto, s->fifo, s->highspeed); @@ -395,7 +394,7 @@ static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len) static inline void dsp_out_data (SB16State *s, uint8_t val) { - ldebug ("outdata %#x\n", val); + ldebug("outdata 0x%x", val); if ((size_t) s->out_data_len < sizeof (s->out_data)) { s->out_data[s->out_data_len++] = val; } @@ -407,18 +406,18 @@ static inline uint8_t dsp_get_data (SB16State *s) return s->in2_data[--s->in_index]; } else { - dolog ("buffer underflow\n"); + warn_report("sb16: buffer underflow"); return 0; } } static void command (SB16State *s, uint8_t cmd) { - ldebug ("command %#x\n", cmd); + ldebug("command 0x%x", cmd); if (cmd > 0xaf && cmd < 0xd0) { if (cmd & 8) { - qemu_log_mask(LOG_UNIMP, "ADC not yet supported (command %#x)\n", + qemu_log_mask(LOG_UNIMP, "ADC not yet supported (command 0x%x)\n", cmd); } @@ -427,7 +426,7 @@ static void command (SB16State *s, uint8_t cmd) case 12: break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%#x wrong bits\n", cmd); + qemu_log_mask(LOG_GUEST_ERROR, "0x%x wrong bits\n", cmd); } s->needed_bytes = 3; } @@ -645,13 +644,13 @@ static void command (SB16State *s, uint8_t cmd) goto warn; default: - qemu_log_mask(LOG_UNIMP, "Unrecognized command %#x\n", cmd); + qemu_log_mask(LOG_UNIMP, "Unrecognized command 0x%x\n", cmd); break; } } if (!s->needed_bytes) { - ldebug ("\n"); + ldebug("!needed_bytes"); } exit: @@ -664,7 +663,7 @@ static void command (SB16State *s, uint8_t cmd) return; warn: - qemu_log_mask(LOG_UNIMP, "warning: command %#x,%d is not truly understood" + qemu_log_mask(LOG_UNIMP, "warning: command 0x%x,%d is not truly understood" " yet\n", cmd, s->needed_bytes); goto exit; @@ -687,7 +686,7 @@ static uint16_t dsp_get_hilo (SB16State *s) static void complete (SB16State *s) { int d0, d1, d2; - ldebug ("complete command %#x, in_index %d, needed_bytes %d\n", + ldebug("complete command 0x%x, in_index %d, needed_bytes %d", s->cmd, s->in_index, s->needed_bytes); if (s->cmd > 0xaf && s->cmd < 0xd0) { @@ -696,11 +695,11 @@ static void complete (SB16State *s) d0 = dsp_get_data (s); if (s->cmd & 8) { - dolog ("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", + warn_report("sb16: ADC params cmd = 0x%x d0 = %d, d1 = %d, d2 = %d", s->cmd, d0, d1, d2); } else { - ldebug ("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", + ldebug("cmd = 0x%x d0 = %d, d1 = %d, d2 = %d", s->cmd, d0, d1, d2); dma_cmd (s, s->cmd, d0, d1 + (d2 << 8)); } @@ -711,13 +710,13 @@ static void complete (SB16State *s) s->csp_mode = dsp_get_data (s); s->csp_reg83r = 0; s->csp_reg83w = 0; - ldebug ("CSP command 0x04: mode=%#x\n", s->csp_mode); + ldebug("CSP command 0x04: mode=0x%x", s->csp_mode); break; case 0x05: s->csp_param = dsp_get_data (s); s->csp_value = dsp_get_data (s); - ldebug ("CSP command 0x05: param=%#x value=%#x\n", + ldebug("CSP command 0x05: param=0x%x value=0x%x", s->csp_param, s->csp_value); break; @@ -725,9 +724,9 @@ static void complete (SB16State *s) case 0x0e: d0 = dsp_get_data (s); d1 = dsp_get_data (s); - ldebug ("write CSP register %d <- %#x\n", d1, d0); + ldebug("write CSP register %d <- 0x%x", d1, d0); if (d1 == 0x83) { - ldebug ("0x83[%d] <- %#x\n", s->csp_reg83r, d0); + ldebug("0x83[%d] <- 0x%x", s->csp_reg83r, d0); s->csp_reg83[s->csp_reg83r % 4] = d0; s->csp_reg83r += 1; } @@ -738,10 +737,10 @@ static void complete (SB16State *s) case 0x0f: d0 = dsp_get_data (s); - ldebug ("read CSP register %#x -> %#x, mode=%#x\n", + ldebug("read CSP register 0x%x -> 0x%x, mode=0x%x", d0, s->csp_regs[d0], s->csp_mode); if (d0 == 0x83) { - ldebug ("0x83[%d] -> %#x\n", + ldebug("0x83[%d] -> 0x%x", s->csp_reg83w, s->csp_reg83[s->csp_reg83w % 4]); dsp_out_data (s, s->csp_reg83[s->csp_reg83w % 4]); @@ -754,7 +753,7 @@ static void complete (SB16State *s) case 0x10: d0 = dsp_get_data (s); - dolog ("cmd 0x10 d0=%#x\n", d0); + warn_report("sb16: cmd 0x10 d0=0x%x", d0); break; case 0x14: @@ -763,7 +762,7 @@ static void complete (SB16State *s) case 0x40: s->time_const = dsp_get_data (s); - ldebug ("set time const %d\n", s->time_const); + ldebug("set time const %d", s->time_const); break; case 0x41: @@ -776,12 +775,12 @@ static void complete (SB16State *s) * http://homepages.cae.wisc.edu/~brodskye/sb16doc/sb16doc.html#SamplingRate */ s->freq = restrict_sampling_rate(dsp_get_hilo(s)); - ldebug ("set freq %d\n", s->freq); + ldebug("set freq %d", s->freq); break; case 0x48: s->block_size = dsp_get_lohi (s) + 1; - ldebug ("set dma block len %d\n", s->block_size); + ldebug("set dma block len %d", s->block_size); break; case 0x74: @@ -811,21 +810,21 @@ static void complete (SB16State *s) ); } } - ldebug ("mix silence %d %d %" PRId64 "\n", samples, bytes, ticks); + ldebug("mix silence %d %d %" PRId64, samples, bytes, ticks); } break; case 0xe0: d0 = dsp_get_data (s); s->out_data_len = 0; - ldebug ("E0 data = %#x\n", d0); + ldebug("E0 data = 0x%x", d0); dsp_out_data (s, ~d0); break; case 0xe2: -#ifdef DEBUG +#if DEBUG d0 = dsp_get_data (s); - dolog ("E2 = %#x\n", d0); + warn_report("sb16: E2 = 0x%x", d0); #endif break; @@ -835,7 +834,7 @@ static void complete (SB16State *s) case 0xf9: d0 = dsp_get_data (s); - ldebug ("command 0xf9 with %#x\n", d0); + ldebug("command 0xf9 with 0x%x", d0); switch (d0) { case 0x0e: dsp_out_data (s, 0xff); @@ -856,13 +855,13 @@ static void complete (SB16State *s) break; default: - qemu_log_mask(LOG_UNIMP, "complete: unrecognized command %#x\n", + qemu_log_mask(LOG_UNIMP, "complete: unrecognized command 0x%x\n", s->cmd); return; } } - ldebug ("\n"); + ldebug(""); s->cmd = -1; } @@ -926,7 +925,7 @@ static void dsp_write(void *opaque, uint32_t nport, uint32_t val) iport = nport - s->port; - ldebug ("write %#x <- %#x\n", nport, val); + ldebug("write 0x%x <- 0x%x", nport, val); switch (iport) { case 0x06: switch (val) { @@ -976,7 +975,7 @@ static void dsp_write(void *opaque, uint32_t nport, uint32_t val) } else { if (s->in_index == sizeof (s->in2_data)) { - dolog ("in data overrun\n"); + warn_report("sb16: in data overrun"); } else { s->in2_data[s->in_index++] = val; @@ -992,7 +991,7 @@ static void dsp_write(void *opaque, uint32_t nport, uint32_t val) break; default: - ldebug ("(nport=%#x, val=%#x)\n", nport, val); + ldebug("(nport=0x%x, val=0x%x)", nport, val); break; } } @@ -1016,7 +1015,7 @@ static uint32_t dsp_read(void *opaque, uint32_t nport) } else { if (s->cmd != -1) { - dolog ("empty output buffer for command %#x\n", + warn_report("sb16: empty output buffer for command 0x%x", s->cmd); } retval = s->last_read_byte; @@ -1029,7 +1028,7 @@ static uint32_t dsp_read(void *opaque, uint32_t nport) break; case 0x0d: /* timer interrupt clear */ - /* dolog ("timer interrupt clear\n"); */ + /* warn_report("sb16: timer interrupt clear"); */ retval = 0; break; @@ -1056,13 +1055,13 @@ static uint32_t dsp_read(void *opaque, uint32_t nport) } if (!ack) { - ldebug ("read %#x -> %#x\n", nport, retval); + ldebug("read 0x%x -> 0x%x", nport, retval); } return retval; error: - dolog ("warning: dsp_read %#x error\n", nport); + warn_report("sb16: dsp_read 0x%x error", nport); return 0xff; } @@ -1108,7 +1107,7 @@ static void mixer_write_datab(void *opaque, uint32_t nport, uint32_t val) SB16State *s = opaque; (void) nport; - ldebug ("mixer_write [%#x] <- %#x\n", s->mixer_nreg, val); + ldebug("mixer_write [0x%x] <- 0x%x", s->mixer_nreg, val); switch (s->mixer_nreg) { case 0x00: @@ -1118,7 +1117,7 @@ static void mixer_write_datab(void *opaque, uint32_t nport, uint32_t val) case 0x80: { int irq = irq_of_magic (val); - ldebug ("setting irq to %d (val=%#x)\n", irq, val); + ldebug("setting irq to %d (val=0x%x)", irq, val); if (irq > 0) { s->irq = irq; } @@ -1133,7 +1132,7 @@ static void mixer_write_datab(void *opaque, uint32_t nport, uint32_t val) hdma = ctz32 (val & 0xf0); if (dma != s->dma || hdma != s->hdma) { qemu_log_mask(LOG_GUEST_ERROR, "attempt to change DMA 8bit" - " %d(%d), 16bit %d(%d) (val=%#x)\n", dma, s->dma, + " %d(%d), 16bit %d(%d) (val=0x%x)\n", dma, s->dma, hdma, s->hdma, val); } #if 0 @@ -1145,12 +1144,12 @@ static void mixer_write_datab(void *opaque, uint32_t nport, uint32_t val) case 0x82: qemu_log_mask(LOG_GUEST_ERROR, "attempt to write into IRQ status" - " register (val=%#x)\n", val); + " register (val=0x%x)\n", val); return; default: if (s->mixer_nreg >= 0x80) { - ldebug ("attempt to write mixer[%#x] <- %#x\n", s->mixer_nreg, val); + ldebug("attempt to write mixer[0x%x] <- 0x%x", s->mixer_nreg, val); } break; } @@ -1165,11 +1164,11 @@ static uint32_t mixer_read(void *opaque, uint32_t nport) (void) nport; #ifndef DEBUG_SB16_MOST if (s->mixer_nreg != 0x82) { - ldebug ("mixer_read[%#x] -> %#x\n", + ldebug("mixer_read[0x%x] -> 0x%x", s->mixer_nreg, s->mixer_regs[s->mixer_nreg]); } #else - ldebug ("mixer_read[%#x] -> %#x\n", + ldebug("mixer_read[0x%x] -> 0x%x", s->mixer_nreg, s->mixer_regs[s->mixer_nreg]); #endif return s->mixer_regs[s->mixer_nreg]; @@ -1241,7 +1240,7 @@ static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) till = s->left_till_irq; #ifdef DEBUG_SB16_MOST - dolog ("pos:%06d %d till:%d len:%d\n", + warn_report("sb16: pos:%06d %d till:%d len:%d", dma_pos, free, till, dma_len); #endif @@ -1265,7 +1264,7 @@ static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) } #ifdef DEBUG_SB16_MOST - ldebug ("pos %5d free %5d size %5d till % 5d copy %5d written %5d size %5d\n", + ldebug("pos %5d free %5d size %5d till % 5d copy %5d written %5d size %5d", dma_pos, free, dma_len, s->left_till_irq, copy, written, s->block_size); #endif From 344c79b8ff700559dec0f871b30f09b25f0aca45 Mon Sep 17 00:00:00 2001 From: Vishal Chourasia Date: Tue, 21 Oct 2025 16:24:44 +0530 Subject: [PATCH 1787/1794] hw/ppc/spapr: Rename resize_hpt_err to errp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename resize_hpt_err to standard errp naming convention. Signed-off-by: Vishal Chourasia Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251021105442.1474602-9-vishalc@linux.ibm.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/ppc/spapr.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 97ab6bebd25c0..e0a2e5a984d64 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2817,7 +2817,7 @@ static void spapr_machine_init(MachineState *machine) int i; MemoryRegion *sysmem = get_system_memory(); long load_limit, fw_size; - Error *resize_hpt_err = NULL; + Error *errp = NULL; NICInfo *nd; if (!filename) { @@ -2845,7 +2845,7 @@ static void spapr_machine_init(MachineState *machine) /* Determine capabilities to run with */ spapr_caps_init(spapr); - kvmppc_check_papr_resize_hpt(&resize_hpt_err); + kvmppc_check_papr_resize_hpt(&errp); if (spapr->resize_hpt == SPAPR_RESIZE_HPT_DEFAULT) { /* * If the user explicitly requested a mode we should either @@ -2853,10 +2853,10 @@ static void spapr_machine_init(MachineState *machine) * it's not set explicitly, we reset our mode to something * that works */ - if (resize_hpt_err) { + if (errp) { spapr->resize_hpt = SPAPR_RESIZE_HPT_DISABLED; - error_free(resize_hpt_err); - resize_hpt_err = NULL; + error_free(errp); + errp = NULL; } else { spapr->resize_hpt = smc->resize_hpt_default; } @@ -2864,14 +2864,14 @@ static void spapr_machine_init(MachineState *machine) assert(spapr->resize_hpt != SPAPR_RESIZE_HPT_DEFAULT); - if ((spapr->resize_hpt != SPAPR_RESIZE_HPT_DISABLED) && resize_hpt_err) { + if ((spapr->resize_hpt != SPAPR_RESIZE_HPT_DISABLED) && errp) { /* * User requested HPT resize, but this host can't supply it. Bail out */ - error_report_err(resize_hpt_err); + error_report_err(errp); exit(1); } - error_free(resize_hpt_err); + error_free(errp); spapr->rma_size = spapr_rma_size(spapr, &error_fatal); From 253f902d845b4ce2877b4b446c026be0dd0ea7f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 13 May 2025 12:33:29 +0100 Subject: [PATCH 1788/1794] qemu/target-info: Include missing 'qapi-types-common.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When adding the TargetInfo::@endianness field in commit a37aec2e7d8, we neglected to include the "qapi-types-common.h" header to get the EndianMode enum definition. Fix that. Fixes: a37aec2e7d8 ("qemu/target-info: Add target_endian_mode()") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-Id: <20251020220941.65269-10-philmd@linaro.org> --- include/qemu/target-info-impl.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/qemu/target-info-impl.h b/include/qemu/target-info-impl.h index 17887f64e26a4..e446585bf538f 100644 --- a/include/qemu/target-info-impl.h +++ b/include/qemu/target-info-impl.h @@ -9,6 +9,7 @@ #ifndef QEMU_TARGET_INFO_IMPL_H #define QEMU_TARGET_INFO_IMPL_H +#include "qapi/qapi-types-common.h" #include "qapi/qapi-types-machine.h" typedef struct TargetInfo { From 7d82e9b56c6fad65d993a31d990fdc5792926692 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 20 Oct 2025 16:04:24 +0200 Subject: [PATCH 1789/1794] MAINTAINERS: Add missing machine name in the Alpha section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without a machine name here, get_maintainers.pl uses the "-----..." separator for describing what the maintainer is taking care of: $ scripts/get_maintainer.pl -f hw/alpha/dp264.c Richard Henderson (maintainer:--------------) qemu-devel@nongnu.org (open list:All patches CC here) Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251020140425.45003-1-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 667acd933c7fe..d4e7fd965b0e6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -671,6 +671,7 @@ F: tests/docker/dockerfiles/emsdk-wasm32-cross.docker Alpha Machines -------------- +Clipper M: Richard Henderson S: Maintained F: hw/alpha/ From ba4d0a0184cb364df98fe66d885b79427ebd0e7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 21 Oct 2025 13:02:38 +0400 Subject: [PATCH 1790/1794] docs: update -soundhw -> -device list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (note: I wonder if pcspk was really an option when -soundhw was available, since it was not user-creatable) Signed-off-by: Marc-André Lureau Message-ID: <20251021090317.425409-8-marcandre.lureau@redhat.com> Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé --- docs/qdev-device-use.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/qdev-device-use.txt b/docs/qdev-device-use.txt index c98c86d828021..043ae4611400d 100644 --- a/docs/qdev-device-use.txt +++ b/docs/qdev-device-use.txt @@ -324,8 +324,10 @@ Map from -soundhw sound card name to -device: gus -device gus,iobase=IOADDR,irq=IRQ,dma=DMA,freq=F hda -device intel-hda,msi=MSI -device hda-duplex sb16 -device sb16,iobase=IOADDR,irq=IRQ,dma=DMA,dma16=DMA16,version=V - adlib not yet available with -device - pcspk not yet available with -device + adlib -device adlib,iobase=IOADDR,freq=F + + pcspk Not available with -device, + but audiodev can be set with -machine pcspk-audiodev= For PCI devices, you can add bus=PCI-BUS,addr=DEVFN to control the PCI device address, as usual. From 274d9060664b1754a3d349af718d006ed2e70b2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Oct 2025 15:11:07 +0200 Subject: [PATCH 1791/1794] docs: Update mentions of removed '-soundhw' command line option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `-soundhw` CLI was removed in commit 039a68373c4 ("introduce -audio as a replacement for -soundhw"). Remove outdated comments and update the document mentioning the old usage. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20251021131825.99390-2-philmd@linaro.org> --- docs/qdev-device-use.txt | 4 ++-- system/qdev-monitor.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/qdev-device-use.txt b/docs/qdev-device-use.txt index 043ae4611400d..fb420da2a9e1a 100644 --- a/docs/qdev-device-use.txt +++ b/docs/qdev-device-use.txt @@ -311,9 +311,9 @@ constraints. Host and guest part of audio devices have always been separate. -The old way to define guest audio devices is -soundhw C1,... +The old way to define guest audio devices was -soundhw C1,... -The new way is to define each guest audio device separately with +The current way is to define each guest audio device separately with -device. Map from -soundhw sound card name to -device: diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 2ac92d0a076cb..ec4a2394ceb31 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -73,9 +73,9 @@ typedef struct QDevAlias /* Please keep this table sorted by typename. */ static const QDevAlias qdev_alias_table[] = { - { "AC97", "ac97" }, /* -soundhw name */ + { "AC97", "ac97" }, { "e1000", "e1000-82540em" }, - { "ES1370", "es1370" }, /* -soundhw name */ + { "ES1370", "es1370" }, { "ich9-ahci", "ahci" }, { "lsi53c895a", "lsi" }, { "virtio-9p-device", "virtio-9p", QEMU_ARCH_VIRTIO_MMIO }, From 1b2fda59164ea4e487d9eb1e34f6a885f67d6ee9 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 17 Oct 2025 13:50:03 +0200 Subject: [PATCH 1792/1794] hw/uefi: add query-firmware-log monitor command Starting with the edk2-stable202508 tag OVMF (and ArmVirt too) have optional support for logging to a memory buffer. There is guest side support -- for example in linux kernels v6.17+ -- to read that buffer. But that might not helpful if your guest stops booting early enough that guest tooling can not be used yet. So host side support to read that log buffer is a useful thing to have. This patch implements the query-firmware-log qmp monitor command to read the firmware log. Reviewed-by: Markus Armbruster Signed-off-by: Gerd Hoffmann Message-ID: <20251017115006.2696991-2-kraxel@redhat.com> --- hw/uefi/meson.build | 2 +- hw/uefi/ovmf-log.c | 233 +++++++++++++++++++++++++++++++++++++ qapi/machine.json | 24 ++++ tests/qtest/qmp-cmd-test.c | 2 + 4 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 hw/uefi/ovmf-log.c diff --git a/hw/uefi/meson.build b/hw/uefi/meson.build index 91eb95f89e6dc..c8f38dfae2476 100644 --- a/hw/uefi/meson.build +++ b/hw/uefi/meson.build @@ -1,4 +1,4 @@ -system_ss.add(files('hardware-info.c')) +system_ss.add(files('hardware-info.c', 'ovmf-log.c')) uefi_vars_ss = ss.source_set() if (config_all_devices.has_key('CONFIG_UEFI_VARS')) diff --git a/hw/uefi/ovmf-log.c b/hw/uefi/ovmf-log.c new file mode 100644 index 0000000000000..0d4bd503a06a8 --- /dev/null +++ b/hw/uefi/ovmf-log.c @@ -0,0 +1,233 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * print ovmf debug log + * + * see OvmfPkg/Library/MemDebugLogLib/ in edk2 + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/target-info-qapi.h" +#include "hw/boards.h" +#include "hw/i386/x86.h" +#include "hw/arm/virt.h" +#include "system/dma.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "qapi/type-helpers.h" +#include "qapi/qapi-commands-machine.h" + + +/* ----------------------------------------------------------------------- */ +/* copy from edk2 */ + +#define MEM_DEBUG_LOG_MAGIC1 0x3167646d666d766f /* "ovmfmdg1" */ +#define MEM_DEBUG_LOG_MAGIC2 0x3267646d666d766f /* "ovmfmdg2" */ + +/* + * Mem Debug Log buffer header. + * The Log buffer is circular. Only the most + * recent messages are retained. Older messages + * will be discarded if the buffer overflows. + * The Debug Log starts just after the header. + */ +typedef struct { + /* + * Magic values + * These fields are used by tools to locate the buffer in + * memory. These MUST be the first two fields of the structure. + * Use a 128 bit Magic to vastly reduce the possibility of + * a collision with random data in memory. + */ + uint64_t Magic1; + uint64_t Magic2; + /* + * Header Size + * This MUST be the third field of the structure + */ + uint64_t HeaderSize; + /* + * Debug log size (minus header) + */ + uint64_t DebugLogSize; + /* + * edk2 uses this for locking access. + */ + uint64_t MemDebugLogLock; + /* + * Debug log head offset + */ + uint64_t DebugLogHeadOffset; + /* + * Debug log tail offset + */ + uint64_t DebugLogTailOffset; + /* + * Flag to indicate if the buffer wrapped and was thus truncated. + */ + uint64_t Truncated; + /* + * Firmware Build Version (PcdFirmwareVersionString) + */ + char FirmwareVersion[128]; +} MEM_DEBUG_LOG_HDR; + + +/* ----------------------------------------------------------------------- */ +/* qemu monitor command */ + +typedef struct { + uint64_t magic1; + uint64_t magic2; +} MemDebugLogMagic; + +/* find log buffer in guest memory by searching for the magic cookie */ +static dma_addr_t find_ovmf_log_range(dma_addr_t start, dma_addr_t end) +{ + static const MemDebugLogMagic magic = { + .magic1 = MEM_DEBUG_LOG_MAGIC1, + .magic2 = MEM_DEBUG_LOG_MAGIC2, + }; + MemDebugLogMagic check; + dma_addr_t step = 4 * KiB; + dma_addr_t offset; + + for (offset = start; offset < end; offset += step) { + if (dma_memory_read(&address_space_memory, offset, + &check, sizeof(check), + MEMTXATTRS_UNSPECIFIED)) { + /* dma error -> stop searching */ + break; + } + if (memcmp(&magic, &check, sizeof(check)) == 0) { + return offset; + } + } + return (dma_addr_t)-1; +} + +static dma_addr_t find_ovmf_log(void) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + dma_addr_t start, end, offset; + + if (target_arch() == SYS_EMU_TARGET_X86_64 && + object_dynamic_cast(OBJECT(ms), TYPE_X86_MACHINE)) { + X86MachineState *x86ms = X86_MACHINE(ms); + + /* early log buffer, static allocation in memfd, sec + early pei */ + offset = find_ovmf_log_range(0x800000, 0x900000); + if (offset != -1) { + return offset; + } + + /* + * normal log buffer, dynamically allocated close to end of low memory, + * late pei + dxe phase + */ + end = x86ms->below_4g_mem_size; + start = end - MIN(end, 128 * MiB); + return find_ovmf_log_range(start, end); + } + + if (target_arch() == SYS_EMU_TARGET_AARCH64 && + object_dynamic_cast(OBJECT(ms), TYPE_VIRT_MACHINE)) { + VirtMachineState *vms = VIRT_MACHINE(ms); + + /* edk2 ArmVirt firmware allocations are in the first 128 MB */ + start = vms->memmap[VIRT_MEM].base; + end = start + 128 * MiB; + return find_ovmf_log_range(start, end); + } + + return (dma_addr_t)-1; +} + +static void handle_ovmf_log_range(GString *out, + dma_addr_t start, + dma_addr_t end, + Error **errp) +{ + if (start > end) { + return; + } + + size_t len = end - start; + g_string_set_size(out, out->len + len); + if (dma_memory_read(&address_space_memory, start, + out->str + (out->len - len), + len, MEMTXATTRS_UNSPECIFIED)) { + error_setg(errp, "can not read firmware log buffer contents"); + return; + } +} + +FirmwareLog *qmp_query_firmware_log(Error **errp) +{ + MEM_DEBUG_LOG_HDR header; + dma_addr_t offset, base; + FirmwareLog *ret; + g_autoptr(GString) log = g_string_new(""); + + offset = find_ovmf_log(); + if (offset == -1) { + error_setg(errp, "firmware log buffer not found"); + return NULL; + } + + if (dma_memory_read(&address_space_memory, offset, + &header, sizeof(header), + MEMTXATTRS_UNSPECIFIED)) { + error_setg(errp, "can not read firmware log buffer header"); + return NULL; + } + + if (header.DebugLogSize > MiB) { + /* default size is 128k (32 pages), allow up to 1M */ + error_setg(errp, "firmware log: log buffer is too big"); + return NULL; + } + + if (header.DebugLogHeadOffset > header.DebugLogSize || + header.DebugLogTailOffset > header.DebugLogSize) { + error_setg(errp, "firmware log buffer header is invalid"); + return NULL; + } + + base = offset + header.HeaderSize; + if (header.DebugLogHeadOffset > header.DebugLogTailOffset) { + /* wrap around */ + handle_ovmf_log_range(log, + base + header.DebugLogHeadOffset, + base + header.DebugLogSize, + errp); + if (*errp) { + return NULL; + } + handle_ovmf_log_range(log, + base + 0, + base + header.DebugLogTailOffset, + errp); + if (*errp) { + return NULL; + } + } else { + handle_ovmf_log_range(log, + base + header.DebugLogHeadOffset, + base + header.DebugLogTailOffset, + errp); + if (*errp) { + return NULL; + } + } + + ret = g_new0(FirmwareLog, 1); + if (header.FirmwareVersion[0] != '\0') { + ret->version = g_strndup(header.FirmwareVersion, + sizeof(header.FirmwareVersion)); + } + ret->log = g_base64_encode((const guchar *)log->str, log->len); + return ret; +} diff --git a/qapi/machine.json b/qapi/machine.json index 038eab281c78c..96133e5c71cfb 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1839,6 +1839,30 @@ 'returns': 'HumanReadableText', 'features': [ 'unstable' ]} +## +# @FirmwareLog: +# +# @version: Firmware version. +# +# @log: Firmware debug log, in base64 encoding. First and last log +# line might be incomplete. +# +# Since: 10.2 +## +{ 'struct': 'FirmwareLog', + 'data': { '*version': 'str', + 'log': 'str' } } + +## +# @query-firmware-log: +# +# Find firmware memory log buffer in guest memory, return content. +# +# Since: 10.2 +## +{ 'command': 'query-firmware-log', + 'returns': 'FirmwareLog' } + ## # @dump-skeys: # diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index cf718761861de..279a8f5614e96 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -52,6 +52,8 @@ static int query_error_class(const char *cmd) /* Only valid with accel=tcg */ { "x-query-jit", ERROR_CLASS_GENERIC_ERROR }, { "xen-event-list", ERROR_CLASS_GENERIC_ERROR }, + /* requires firmware with memory buffer logging support */ + { "query-firmware-log", ERROR_CLASS_GENERIC_ERROR }, { NULL, -1 } }; int i; From c8aa8120313fc7770900de13b657dacb7dc77a95 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 17 Oct 2025 13:50:04 +0200 Subject: [PATCH 1793/1794] hw/uefi: add 'info firmware-log' hmp monitor command. This adds the hmp variant of the query-firmware-log qmp command. Reviewed-by: Markus Armbruster Signed-off-by: Gerd Hoffmann Message-ID: <20251017115006.2696991-3-kraxel@redhat.com> --- hmp-commands-info.hx | 13 +++++++++++++ hw/uefi/ovmf-log.c | 27 +++++++++++++++++++++++++++ include/monitor/hmp.h | 1 + 3 files changed, 41 insertions(+) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 25b4aed51f56d..33cf740bbc1b6 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -995,3 +995,16 @@ SRST ``info cryptodev`` Show the crypto devices. ERST + + { + .name = "firmware-log", + .args_type = "", + .params = "", + .help = "show the firmware (ovmf) debug log", + .cmd = hmp_info_firmware_log, + }, + +SRST + ``info firmware-log`` + Show the firmware (ovmf) debug log. +ERST diff --git a/hw/uefi/ovmf-log.c b/hw/uefi/ovmf-log.c index 0d4bd503a06a8..fe8acbd192361 100644 --- a/hw/uefi/ovmf-log.c +++ b/hw/uefi/ovmf-log.c @@ -231,3 +231,30 @@ FirmwareLog *qmp_query_firmware_log(Error **errp) ret->log = g_base64_encode((const guchar *)log->str, log->len); return ret; } + +void hmp_info_firmware_log(Monitor *mon, const QDict *qdict) +{ + g_autofree gchar *log_esc = NULL; + g_autofree guchar *log_out = NULL; + Error *err = NULL; + FirmwareLog *log; + gsize log_len; + + log = qmp_query_firmware_log(&err); + if (err) { + hmp_handle_error(mon, err); + return; + } + + g_assert(log != NULL); + g_assert(log->log != NULL); + + if (log->version) { + g_autofree gchar *esc = g_strescape(log->version, NULL); + monitor_printf(mon, "[ firmware version: %s ]\n", esc); + } + + log_out = g_base64_decode(log->log, &log_len); + log_esc = g_strescape((gchar *)log_out, "\r\n"); + monitor_printf(mon, "%s\n", log_esc); +} diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index 897dfaa2b6d93..83721b5ffc6de 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -179,5 +179,6 @@ void hmp_boot_set(Monitor *mon, const QDict *qdict); void hmp_info_mtree(Monitor *mon, const QDict *qdict); void hmp_info_cryptodev(Monitor *mon, const QDict *qdict); void hmp_dumpdtb(Monitor *mon, const QDict *qdict); +void hmp_info_firmware_log(Monitor *mon, const QDict *qdict); #endif From c6c6d854447a7821288e01857d0f7fb28b82cf44 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 17 Oct 2025 13:50:05 +0200 Subject: [PATCH 1794/1794] hw/uefi/ovmf-log: add maxsize parameter Allow limiting the amount of log output sent. Allow up to 1 MiB. In case the guest log buffer is larger than 1 MiB limit the output instead of throwing an error. Acked-by: Markus Armbruster Signed-off-by: Gerd Hoffmann Message-ID: <20251017115006.2696991-4-kraxel@redhat.com> --- hmp-commands-info.hx | 4 ++-- hw/uefi/ovmf-log.c | 42 ++++++++++++++++++++++++++++++++++-------- qapi/machine.json | 5 +++++ 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 33cf740bbc1b6..2a7f5810d7060 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -998,8 +998,8 @@ ERST { .name = "firmware-log", - .args_type = "", - .params = "", + .args_type = "max-size:o?", + .params = "[max-size]", .help = "show the firmware (ovmf) debug log", .cmd = hmp_info_firmware_log, }, diff --git a/hw/uefi/ovmf-log.c b/hw/uefi/ovmf-log.c index fe8acbd192361..98ebb0209491c 100644 --- a/hw/uefi/ovmf-log.c +++ b/hw/uefi/ovmf-log.c @@ -18,6 +18,7 @@ #include "qapi/error.h" #include "qapi/type-helpers.h" #include "qapi/qapi-commands-machine.h" +#include "qobject/qdict.h" /* ----------------------------------------------------------------------- */ @@ -164,7 +165,8 @@ static void handle_ovmf_log_range(GString *out, } } -FirmwareLog *qmp_query_firmware_log(Error **errp) +FirmwareLog *qmp_query_firmware_log(bool have_max_size, uint64_t max_size, + Error **errp) { MEM_DEBUG_LOG_HDR header; dma_addr_t offset, base; @@ -184,18 +186,40 @@ FirmwareLog *qmp_query_firmware_log(Error **errp) return NULL; } - if (header.DebugLogSize > MiB) { - /* default size is 128k (32 pages), allow up to 1M */ - error_setg(errp, "firmware log: log buffer is too big"); - return NULL; - } - if (header.DebugLogHeadOffset > header.DebugLogSize || header.DebugLogTailOffset > header.DebugLogSize) { error_setg(errp, "firmware log buffer header is invalid"); return NULL; } + if (have_max_size) { + if (max_size > MiB) { + error_setg(errp, "parameter 'max-size' exceeds 1MiB"); + return NULL; + } + } else { + max_size = MiB; + } + + /* adjust header.DebugLogHeadOffset so we return at most maxsize bytes */ + if (header.DebugLogHeadOffset > header.DebugLogTailOffset) { + /* wrap around */ + if (header.DebugLogTailOffset > max_size) { + header.DebugLogHeadOffset = header.DebugLogTailOffset - max_size; + } else { + uint64_t max_chunk = max_size - header.DebugLogTailOffset; + if (header.DebugLogSize > max_chunk && + header.DebugLogHeadOffset < header.DebugLogSize - max_chunk) { + header.DebugLogHeadOffset = header.DebugLogSize - max_chunk; + } + } + } else { + if (header.DebugLogTailOffset > max_size && + header.DebugLogHeadOffset < header.DebugLogTailOffset - max_size) { + header.DebugLogHeadOffset = header.DebugLogTailOffset - max_size; + } + } + base = offset + header.HeaderSize; if (header.DebugLogHeadOffset > header.DebugLogTailOffset) { /* wrap around */ @@ -239,8 +263,10 @@ void hmp_info_firmware_log(Monitor *mon, const QDict *qdict) Error *err = NULL; FirmwareLog *log; gsize log_len; + int64_t maxsize; - log = qmp_query_firmware_log(&err); + maxsize = qdict_get_try_int(qdict, "max-size", -1); + log = qmp_query_firmware_log(maxsize != -1, (uint64_t)maxsize, &err); if (err) { hmp_handle_error(mon, err); return; diff --git a/qapi/machine.json b/qapi/machine.json index 96133e5c71cfb..c6dc6fe69b5cd 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1858,9 +1858,14 @@ # # Find firmware memory log buffer in guest memory, return content. # +# @max-size: limit the amount of log data returned. Up to 1 MiB of +# log data is allowed. In case the amount of log data is +# larger than @max-size the tail of the log is returned. +# # Since: 10.2 ## { 'command': 'query-firmware-log', + 'data': { '*max-size': 'size' }, 'returns': 'FirmwareLog' } ##